github.com/robotn/xgb@v0.0.0-20190912153532-2cb92d044934/xgb.go (about)

     1  package xgb
     2  
     3  import (
     4  	"errors"
     5  	"io"
     6  	"log"
     7  	"net"
     8  	"os"
     9  	"sync"
    10  )
    11  
    12  var (
    13  	// Where to log error-messages. Defaults to stderr.
    14  	// To disable logging, just set this to log.New(ioutil.Discard, "", 0)
    15  	Logger = log.New(os.Stderr, "XGB: ", log.Lshortfile)
    16  )
    17  
    18  const (
    19  	// cookieBuffer represents the queue size of cookies existing at any
    20  	// point in time. The size of the buffer is really only important when
    21  	// there are many requests without replies made in sequence. Once the
    22  	// buffer fills, a round trip request is made to clear the buffer.
    23  	cookieBuffer = 1000
    24  
    25  	// xidBuffer represents the queue size of the xid channel.
    26  	// I don't think this value matters much, since xid generation is not
    27  	// that expensive.
    28  	xidBuffer = 5
    29  
    30  	// seqBuffer represents the queue size of the sequence number channel.
    31  	// I don't think this value matters much, since sequence number generation
    32  	// is not that expensive.
    33  	seqBuffer = 5
    34  
    35  	// reqBuffer represents the queue size of the number of requests that
    36  	// can be made until new ones block. This value seems OK.
    37  	reqBuffer = 100
    38  
    39  	// eventBuffer represents the queue size of the number of events or errors
    40  	// that can be loaded off the wire and not grabbed with WaitForEvent
    41  	// until reading an event blocks. This value should be big enough to handle
    42  	// bursts of events.
    43  	eventBuffer = 5000
    44  )
    45  
    46  // A Conn represents a connection to an X server.
    47  type Conn struct {
    48  	host          string
    49  	conn          net.Conn
    50  	display       string
    51  	DisplayNumber int
    52  	DefaultScreen int
    53  	SetupBytes    []byte
    54  
    55  	setupResourceIdBase uint32
    56  	setupResourceIdMask uint32
    57  
    58  	eventChan  chan eventOrError
    59  	cookieChan chan *Cookie
    60  	xidChan    chan xid
    61  	seqChan    chan uint16
    62  	reqChan    chan *request
    63  	closing    chan chan struct{}
    64  
    65  	// ExtLock is a lock used whenever new extensions are initialized.
    66  	// It should not be used. It is exported for use in the extension
    67  	// sub-packages.
    68  	ExtLock sync.RWMutex
    69  
    70  	// Extensions is a map from extension name to major opcode. It should
    71  	// not be used. It is exported for use in the extension sub-packages.
    72  	Extensions map[string]byte
    73  }
    74  
    75  // NewConn creates a new connection instance. It initializes locks, data
    76  // structures, and performs the initial handshake. (The code for the handshake
    77  // has been relegated to conn.go.)
    78  func NewConn() (*Conn, error) {
    79  	return NewConnDisplay("")
    80  }
    81  
    82  // NewConnDisplay is just like NewConn, but allows a specific DISPLAY
    83  // string to be used.
    84  // If 'display' is empty it will be taken from os.Getenv("DISPLAY").
    85  //
    86  // Examples:
    87  //	NewConn(":1") -> net.Dial("unix", "", "/tmp/.X11-unix/X1")
    88  //	NewConn("/tmp/launch-12/:0") -> net.Dial("unix", "", "/tmp/launch-12/:0")
    89  //	NewConn("hostname:2.1") -> net.Dial("tcp", "", "hostname:6002")
    90  //	NewConn("tcp/hostname:1.0") -> net.Dial("tcp", "", "hostname:6001")
    91  func NewConnDisplay(display string) (*Conn, error) {
    92  	conn := &Conn{}
    93  
    94  	// First connect. This reads authority, checks DISPLAY environment
    95  	// variable, and loads the initial Setup info.
    96  	err := conn.connect(display)
    97  	if err != nil {
    98  		return nil, err
    99  	}
   100  
   101  	return postNewConn(conn)
   102  }
   103  
   104  // NewConnDisplay is just like NewConn, but allows a specific net.Conn
   105  // to be used.
   106  func NewConnNet(netConn net.Conn) (*Conn, error) {
   107  	conn := &Conn{}
   108  
   109  	// First connect. This reads authority, checks DISPLAY environment
   110  	// variable, and loads the initial Setup info.
   111  	err := conn.connectNet(netConn)
   112  
   113  	if err != nil {
   114  		return nil, err
   115  	}
   116  
   117  	return postNewConn(conn)
   118  }
   119  
   120  func postNewConn(conn *Conn) (*Conn, error) {
   121  	conn.Extensions = make(map[string]byte)
   122  
   123  	conn.cookieChan = make(chan *Cookie, cookieBuffer)
   124  	conn.xidChan = make(chan xid, xidBuffer)
   125  	conn.seqChan = make(chan uint16, seqBuffer)
   126  	conn.reqChan = make(chan *request, reqBuffer)
   127  	conn.eventChan = make(chan eventOrError, eventBuffer)
   128  	conn.closing = make(chan chan struct{}, 1)
   129  
   130  	go conn.generateXIds()
   131  	go conn.generateSeqIds()
   132  	go conn.sendRequests()
   133  	go conn.readResponses()
   134  
   135  	return conn, nil
   136  }
   137  
   138  // Close gracefully closes the connection to the X server.
   139  func (c *Conn) Close() {
   140  	close(c.reqChan)
   141  }
   142  
   143  // Event is an interface that can contain any of the events returned by the
   144  // server. Use a type assertion switch to extract the Event structs.
   145  type Event interface {
   146  	Bytes() []byte
   147  	String() string
   148  }
   149  
   150  // NewEventFun is the type of function use to construct events from raw bytes.
   151  // It should not be used. It is exported for use in the extension sub-packages.
   152  type NewEventFun func(buf []byte) Event
   153  
   154  // NewEventFuncs is a map from event numbers to functions that create
   155  // the corresponding event. It should not be used. It is exported for use
   156  // in the extension sub-packages.
   157  var NewEventFuncs = make(map[int]NewEventFun)
   158  
   159  // NewExtEventFuncs is a temporary map that stores event constructor functions
   160  // for each extension. When an extension is initialized, each event for that
   161  // extension is added to the 'NewEventFuncs' map. It should not be used. It is
   162  // exported for use in the extension sub-packages.
   163  var NewExtEventFuncs = make(map[string]map[int]NewEventFun)
   164  
   165  // Error is an interface that can contain any of the errors returned by
   166  // the server. Use a type assertion switch to extract the Error structs.
   167  type Error interface {
   168  	SequenceId() uint16
   169  	BadId() uint32
   170  	Error() string
   171  }
   172  
   173  // NewErrorFun is the type of function use to construct errors from raw bytes.
   174  // It should not be used. It is exported for use in the extension sub-packages.
   175  type NewErrorFun func(buf []byte) Error
   176  
   177  // NewErrorFuncs is a map from error numbers to functions that create
   178  // the corresponding error. It should not be used. It is exported for use in
   179  // the extension sub-packages.
   180  var NewErrorFuncs = make(map[int]NewErrorFun)
   181  
   182  // NewExtErrorFuncs is a temporary map that stores error constructor functions
   183  // for each extension. When an extension is initialized, each error for that
   184  // extension is added to the 'NewErrorFuncs' map. It should not be used. It is
   185  // exported for use in the extension sub-packages.
   186  var NewExtErrorFuncs = make(map[string]map[int]NewErrorFun)
   187  
   188  // eventOrError corresponds to values that can be either an event or an
   189  // error.
   190  type eventOrError interface{}
   191  
   192  // NewId generates a new unused ID for use with requests like CreateWindow.
   193  // If no new ids can be generated, the id returned is 0 and error is non-nil.
   194  // This shouldn't be used directly, and is exported for use in the extension
   195  // sub-packages.
   196  // If you need identifiers, use the appropriate constructor.
   197  // e.g., For a window id, use xproto.NewWindowId. For
   198  // a new pixmap id, use xproto.NewPixmapId. And so on.
   199  func (c *Conn) NewId() (uint32, error) {
   200  	xid := <-c.xidChan
   201  	if xid.err != nil {
   202  		return 0, xid.err
   203  	}
   204  	return xid.id, nil
   205  }
   206  
   207  // xid encapsulates a resource identifier being sent over the Conn.xidChan
   208  // channel. If no new resource id can be generated, id is set to 0 and a
   209  // non-nil error is set in xid.err.
   210  type xid struct {
   211  	id  uint32
   212  	err error
   213  }
   214  
   215  // generateXids sends new Ids down the channel for NewId to use.
   216  // generateXids should be run in its own goroutine.
   217  // This needs to be updated to use the XC Misc extension once we run out of
   218  // new ids.
   219  // Thanks to libxcb/src/xcb_xid.c. This code is greatly inspired by it.
   220  func (conn *Conn) generateXIds() {
   221  	defer close(conn.xidChan)
   222  
   223  	// This requires some explanation. From the horse's mouth:
   224  	// "The resource-id-mask contains a single contiguous set of bits (at least
   225  	// 18).  The client allocates resource IDs for types WINDOW, PIXMAP,
   226  	// CURSOR, FONT, GCONTEXT, and COLORMAP by choosing a value with only some
   227  	// subset of these bits set and ORing it with resource-id-base. Only values
   228  	// constructed in this way can be used to name newly created resources over
   229  	// this connection."
   230  	// So for example (using 8 bit integers), the mask might look like:
   231  	// 00111000
   232  	// So that valid values would be 00101000, 00110000, 00001000, and so on.
   233  	// Thus, the idea is to increment it by the place of the last least
   234  	// significant '1'. In this case, that value would be 00001000. To get
   235  	// that value, we can AND the original mask with its two's complement:
   236  	// 00111000 & 11001000 = 00001000.
   237  	// And we use that value to increment the last resource id to get a new one.
   238  	// (And then, of course, we OR it with resource-id-base.)
   239  	inc := conn.setupResourceIdMask & -conn.setupResourceIdMask
   240  	max := conn.setupResourceIdMask
   241  	last := uint32(0)
   242  	for {
   243  		// TODO: Use the XC Misc extension to look for released ids.
   244  		if last > 0 && last >= max-inc+1 {
   245  			conn.xidChan <- xid{
   246  				id: 0,
   247  				err: errors.New("There are no more available resource" +
   248  					"identifiers."),
   249  			}
   250  		}
   251  
   252  		last += inc
   253  		conn.xidChan <- xid{
   254  			id:  last | conn.setupResourceIdBase,
   255  			err: nil,
   256  		}
   257  	}
   258  }
   259  
   260  // newSeqId fetches the next sequence id from the Conn.seqChan channel.
   261  func (c *Conn) newSequenceId() uint16 {
   262  	return <-c.seqChan
   263  }
   264  
   265  // generateSeqIds returns new sequence ids. It is meant to be run in its
   266  // own goroutine.
   267  // A sequence id is generated for *every* request. It's the identifier used
   268  // to match up replies with requests.
   269  // Since sequence ids can only be 16 bit integers we start over at zero when it
   270  // comes time to wrap.
   271  // N.B. As long as the cookie buffer is less than 2^16, there are no limitations
   272  // on the number (or kind) of requests made in sequence.
   273  func (c *Conn) generateSeqIds() {
   274  	defer close(c.seqChan)
   275  
   276  	seqid := uint16(1)
   277  	for {
   278  		c.seqChan <- seqid
   279  		if seqid == uint16((1<<16)-1) {
   280  			seqid = 0
   281  		} else {
   282  			seqid++
   283  		}
   284  	}
   285  }
   286  
   287  // request encapsulates a buffer of raw bytes (containing the request data)
   288  // and a cookie, which when combined represents a single request.
   289  // The cookie is used to match up the reply/error.
   290  type request struct {
   291  	buf    []byte
   292  	cookie *Cookie
   293  
   294  	// seq is closed when the request (cookie) has been sequenced by the Conn.
   295  	seq chan struct{}
   296  }
   297  
   298  // NewRequest takes the bytes and a cookie of a particular request, constructs
   299  // a request type, and sends it over the Conn.reqChan channel.
   300  // Note that the sequence number is added to the cookie after it is sent
   301  // over the request channel, but before it is sent to X.
   302  //
   303  // Note that you may safely use NewRequest to send arbitrary byte requests
   304  // to X. The resulting cookie can be used just like any normal cookie and
   305  // abides by the same rules, except that for replies, you'll get back the
   306  // raw byte data. This may be useful for performance critical sections where
   307  // every allocation counts, since all X requests in XGB allocate a new byte
   308  // slice. In contrast, NewRequest allocates one small request struct and
   309  // nothing else. (Except when the cookie buffer is full and has to be flushed.)
   310  //
   311  // If you're using NewRequest manually, you'll need to use NewCookie to create
   312  // a new cookie.
   313  //
   314  // In all likelihood, you should be able to copy and paste with some minor
   315  // edits the generated code for the request you want to issue.
   316  func (c *Conn) NewRequest(buf []byte, cookie *Cookie) {
   317  	seq := make(chan struct{})
   318  	c.reqChan <- &request{buf: buf, cookie: cookie, seq: seq}
   319  	<-seq
   320  }
   321  
   322  // sendRequests is run as a single goroutine that takes requests and writes
   323  // the bytes to the wire and adds the cookie to the cookie queue.
   324  // It is meant to be run as its own goroutine.
   325  func (c *Conn) sendRequests() {
   326  	defer close(c.cookieChan)
   327  
   328  	for req := range c.reqChan {
   329  		// ho there! if the cookie channel is nearly full, force a round
   330  		// trip to clear out the cookie buffer.
   331  		// Note that we circumvent the request channel, because we're *in*
   332  		// the request channel.
   333  		if len(c.cookieChan) == cookieBuffer-1 {
   334  			if err := c.noop(); err != nil {
   335  				// Shut everything down.
   336  				break
   337  			}
   338  		}
   339  		req.cookie.Sequence = c.newSequenceId()
   340  		c.cookieChan <- req.cookie
   341  		c.writeBuffer(req.buf)
   342  		close(req.seq)
   343  	}
   344  	response := make(chan struct{})
   345  	c.closing <- response
   346  	c.noop() // Flush the response reading goroutine, ignore error.
   347  	<-response
   348  	c.conn.Close()
   349  }
   350  
   351  // noop circumvents the usual request sending goroutines and forces a round
   352  // trip request manually.
   353  func (c *Conn) noop() error {
   354  	cookie := c.NewCookie(true, true)
   355  	cookie.Sequence = c.newSequenceId()
   356  	c.cookieChan <- cookie
   357  	if err := c.writeBuffer(c.getInputFocusRequest()); err != nil {
   358  		return err
   359  	}
   360  	cookie.Reply() // wait for the buffer to clear
   361  	return nil
   362  }
   363  
   364  // writeBuffer is a convenience function for writing a byte slice to the wire.
   365  func (c *Conn) writeBuffer(buf []byte) error {
   366  	if _, err := c.conn.Write(buf); err != nil {
   367  		Logger.Printf("A write error is unrecoverable: %s", err)
   368  		return err
   369  	} else {
   370  		return nil
   371  	}
   372  }
   373  
   374  // readResponses is a goroutine that reads events, errors and
   375  // replies off the wire.
   376  // When an event is read, it is always added to the event channel.
   377  // When an error is read, if it corresponds to an existing checked cookie,
   378  // it is sent to that cookie's error channel. Otherwise it is added to the
   379  // event channel.
   380  // When a reply is read, it is added to the corresponding cookie's reply
   381  // channel. (It is an error if no such cookie exists in this case.)
   382  // Finally, cookies that came "before" this reply are always cleaned up.
   383  func (c *Conn) readResponses() {
   384  	defer close(c.eventChan)
   385  
   386  	var (
   387  		err        Error
   388  		seq        uint16
   389  		replyBytes []byte
   390  	)
   391  
   392  	for {
   393  		select {
   394  		case respond := <-c.closing:
   395  			respond <- struct{}{}
   396  			return
   397  		default:
   398  		}
   399  
   400  		buf := make([]byte, 32)
   401  		err, seq = nil, 0
   402  		if _, err := io.ReadFull(c.conn, buf); err != nil {
   403  			Logger.Printf("A read error is unrecoverable: %s", err)
   404  			c.eventChan <- err
   405  			c.Close()
   406  			continue
   407  		}
   408  		switch buf[0] {
   409  		case 0: // This is an error
   410  			// Use the constructor function for this error (that is auto
   411  			// generated) by looking it up by the error number.
   412  			newErrFun, ok := NewErrorFuncs[int(buf[1])]
   413  			if !ok {
   414  				Logger.Printf("BUG: Could not find error constructor function "+
   415  					"for error with number %d.", buf[1])
   416  				continue
   417  			}
   418  			err = newErrFun(buf)
   419  			seq = err.SequenceId()
   420  
   421  			// This error is either sent to the event channel or a specific
   422  			// cookie's error channel below.
   423  		case 1: // This is a reply
   424  			seq = Get16(buf[2:])
   425  
   426  			// check to see if this reply has more bytes to be read
   427  			size := Get32(buf[4:])
   428  			if size > 0 {
   429  				byteCount := 32 + size*4
   430  				biggerBuf := make([]byte, byteCount)
   431  				copy(biggerBuf[:32], buf)
   432  				if _, err := io.ReadFull(c.conn, biggerBuf[32:]); err != nil {
   433  					Logger.Printf("A read error is unrecoverable: %s", err)
   434  					c.eventChan <- err
   435  					c.Close()
   436  					continue
   437  				}
   438  				replyBytes = biggerBuf
   439  			} else {
   440  				replyBytes = buf
   441  			}
   442  
   443  			// This reply is sent to its corresponding cookie below.
   444  		default: // This is an event
   445  			// Use the constructor function for this event (like for errors,
   446  			// and is also auto generated) by looking it up by the event number.
   447  			// Note that we AND the event number with 127 so that we ignore
   448  			// the most significant bit (which is set when it was sent from
   449  			// a SendEvent request).
   450  			evNum := int(buf[0] & 127)
   451  			newEventFun, ok := NewEventFuncs[evNum]
   452  			if !ok {
   453  				Logger.Printf("BUG: Could not find event construct function "+
   454  					"for event with number %d.", evNum)
   455  				continue
   456  			}
   457  			c.eventChan <- newEventFun(buf)
   458  			continue
   459  		}
   460  
   461  		// At this point, we have a sequence number and we're either
   462  		// processing an error or a reply, which are both responses to
   463  		// requests. So all we have to do is find the cookie corresponding
   464  		// to this error/reply, and send the appropriate data to it.
   465  		// In doing so, we make sure that any cookies that came before it
   466  		// are marked as successful if they are void and checked.
   467  		// If there's a cookie that requires a reply that is before this
   468  		// reply, then something is wrong.
   469  		for cookie := range c.cookieChan {
   470  			// This is the cookie we're looking for. Process and break.
   471  			if cookie.Sequence == seq {
   472  				if err != nil { // this is an error to a request
   473  					// synchronous processing
   474  					if cookie.errorChan != nil {
   475  						cookie.errorChan <- err
   476  					} else { // asynchronous processing
   477  						c.eventChan <- err
   478  						// if this is an unchecked reply, ping the cookie too
   479  						if cookie.pingChan != nil {
   480  							cookie.pingChan <- true
   481  						}
   482  					}
   483  				} else { // this is a reply
   484  					if cookie.replyChan == nil {
   485  						Logger.Printf("Reply with sequence id %d does not "+
   486  							"have a cookie with a valid reply channel.", seq)
   487  						continue
   488  					} else {
   489  						cookie.replyChan <- replyBytes
   490  					}
   491  				}
   492  				break
   493  			}
   494  
   495  			switch {
   496  			// Checked requests with replies
   497  			case cookie.replyChan != nil && cookie.errorChan != nil:
   498  				Logger.Printf("Found cookie with sequence id %d that is "+
   499  					"expecting a reply but will never get it. Currently "+
   500  					"on sequence number %d", cookie.Sequence, seq)
   501  			// Unchecked requests with replies
   502  			case cookie.replyChan != nil && cookie.pingChan != nil:
   503  				Logger.Printf("Found cookie with sequence id %d that is "+
   504  					"expecting a reply (and not an error) but will never "+
   505  					"get it. Currently on sequence number %d",
   506  					cookie.Sequence, seq)
   507  			// Checked requests without replies
   508  			case cookie.pingChan != nil && cookie.errorChan != nil:
   509  				cookie.pingChan <- true
   510  				// Unchecked requests without replies don't have any channels,
   511  				// so we can't do anything with them except let them pass by.
   512  			}
   513  		}
   514  	}
   515  }
   516  
   517  // processEventOrError takes an eventOrError, type switches on it,
   518  // and returns it in Go idiomatic style.
   519  func processEventOrError(everr eventOrError) (Event, Error) {
   520  	switch ee := everr.(type) {
   521  	case Event:
   522  		return ee, nil
   523  	case Error:
   524  		return nil, ee
   525  	default:
   526  		Logger.Printf("Invalid event/error type: %T", everr)
   527  		return nil, nil
   528  	}
   529  }
   530  
   531  // WaitForEvent returns the next event from the server.
   532  // It will block until an event is available.
   533  // WaitForEvent returns either an Event or an Error. (Returning both
   534  // is a bug.) Note than an Error here is an X error and not an XGB error. That
   535  // is, X errors are sometimes completely expected (and you may want to ignore
   536  // them in some cases).
   537  //
   538  // If both the event and error are nil, then the connection has been closed.
   539  func (c *Conn) WaitForEvent() (Event, Error) {
   540  	return processEventOrError(<-c.eventChan)
   541  }
   542  
   543  // PollForEvent returns the next event from the server if one is available in
   544  // the internal queue without blocking. Note that unlike WaitForEvent, both
   545  // Event and Error could be nil. Indeed, they are both nil when the event queue
   546  // is empty.
   547  func (c *Conn) PollForEvent() (Event, Error) {
   548  	select {
   549  	case everr := <-c.eventChan:
   550  		return processEventOrError(everr)
   551  	default:
   552  		return nil, nil
   553  	}
   554  }