zorldo

Goofing around with Ebiten
git clone git://bsandro.tech/zorldo
Log | Files | Refs | README

xgb.go (20209B)


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