github.com/BurntSushi/xgb@v0.0.0-20210121224620-deaf085860bc/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 }