go-tg-screenshot-bot
608 строк · 19.7 Кб
1package xgb
2
3import (
4"errors"
5"io"
6"log"
7"net"
8"os"
9"sync"
10)
11
12var (
13// Where to log error-messages. Defaults to stderr.
14// To disable logging, just set this to log.New(ioutil.Discard, "", 0)
15Logger = log.New(os.Stderr, "XGB: ", log.Lshortfile)
16)
17
18const (
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.
23cookieBuffer = 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.
28xidBuffer = 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.
33seqBuffer = 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.
37reqBuffer = 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.
43eventBuffer = 5000
44)
45
46// A Conn represents a connection to an X server.
47type Conn struct {
48host string
49conn net.Conn
50display string
51DisplayNumber int
52DefaultScreen int
53SetupBytes []byte
54
55setupResourceIdBase uint32
56setupResourceIdMask uint32
57
58eventChan chan eventOrError
59cookieChan chan *Cookie
60xidChan chan xid
61seqChan chan uint16
62reqChan chan *request
63doneSend chan struct{}
64doneRead 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.
69ExtLock 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.
73Extensions 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.
81func NewConn() (*Conn, error) {
82return 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")
94func NewConnDisplay(display string) (*Conn, error) {
95c := &Conn{}
96
97// First connect. This reads authority, checks DISPLAY environment
98// variable, and loads the initial Setup info.
99err := c.connect(display)
100if err != nil {
101return nil, err
102}
103
104return postNewConn(c)
105}
106
107// NewConnNet is just like NewConn (see closing instructions), but allows a specific net.Conn
108// to be used.
109func NewConnNet(netConn net.Conn) (*Conn, error) {
110c := &Conn{}
111
112// First connect. This reads authority, checks DISPLAY environment
113// variable, and loads the initial Setup info.
114err := c.connectNet(netConn)
115
116if err != nil {
117return nil, err
118}
119
120return postNewConn(c)
121}
122
123func postNewConn(c *Conn) (*Conn, error) {
124c.Extensions = make(map[string]byte)
125
126c.cookieChan = make(chan *Cookie, cookieBuffer)
127c.xidChan = make(chan xid, xidBuffer)
128c.seqChan = make(chan uint16, seqBuffer)
129c.reqChan = make(chan *request, reqBuffer)
130c.eventChan = make(chan eventOrError, eventBuffer)
131c.doneSend = make(chan struct{})
132c.doneRead = make(chan struct{})
133
134go c.generateXIds()
135go c.generateSeqIds()
136go c.sendRequests()
137go c.readResponses()
138
139return 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)
144func (c *Conn) Close() {
145select {
146case c.reqChan <- nil:
147case <-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.
153type Event interface {
154Bytes() []byte
155String() 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.
160type 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.
165var 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.
171var 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.
175type Error interface {
176SequenceId() uint16
177BadId() uint32
178Error() 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.
183type 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.
188var 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.
194var NewExtErrorFuncs = make(map[string]map[int]NewErrorFun)
195
196// eventOrError corresponds to values that can be either an event or an
197// error.
198type 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.
208func (c *Conn) NewId() (uint32, error) {
209xid, ok := <-c.xidChan
210if !ok {
211return 0, io.EOF
212}
213if xid.err != nil {
214return 0, xid.err
215}
216return 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.
222type xid struct {
223id uint32
224err 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.
232func (c *Conn) generateXIds() {
233defer 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.)
251inc := c.setupResourceIdMask & -c.setupResourceIdMask
252max := c.setupResourceIdMask
253last := uint32(0)
254for {
255id := xid{}
256if last > 0 && last >= max-inc+1 {
257// TODO: Use the XC Misc extension to look for released ids.
258id = xid{
259id: 0,
260err: errors.New("There are no more available resource identifiers."),
261}
262} else {
263last += inc
264id = xid{
265id: last | c.setupResourceIdBase,
266err: nil,
267}
268}
269
270select {
271case c.xidChan <- id:
272case <-c.doneSend:
273// c.sendRequests is down and since this id is used by requests, we don't need this goroutine running anymore.
274return
275}
276}
277}
278
279// newSeqId fetches the next sequence id from the Conn.seqChan channel.
280func (c *Conn) newSequenceId() uint16 {
281return <-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.
292func (c *Conn) generateSeqIds() {
293defer close(c.seqChan)
294
295seqid := uint16(1)
296for {
297select {
298case c.seqChan <- seqid:
299if seqid == uint16((1<<16)-1) {
300seqid = 0
301} else {
302seqid++
303}
304case <-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.
306return
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.
314type request struct {
315buf []byte
316cookie *Cookie
317
318// seq is closed when the request (cookie) has been sequenced by the Conn.
319seq 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.
340func (c *Conn) NewRequest(buf []byte, cookie *Cookie) {
341seq := make(chan struct{})
342select {
343case c.reqChan <- &request{buf: buf, cookie: cookie, seq: seq}:
344// request is in buffer
345// wait until request is processed or connection is closed
346select {
347case <-seq:
348// request was successfully sent to X server
349case <-c.doneSend:
350// c.sendRequests is down, your request was not handled
351}
352case <-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.
360func (c *Conn) sendRequests() {
361defer close(c.cookieChan)
362defer c.conn.Close()
363defer close(c.doneSend)
364
365for {
366select {
367case req := <-c.reqChan:
368if req == nil {
369// a request by c.Close() to gracefully exit
370// Flush the response reading goroutine.
371if err := c.noop(); err != nil {
372c.conn.Close()
373<-c.doneRead
374}
375return
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.
381if len(c.cookieChan) == cookieBuffer-1 {
382if err := c.noop(); err != nil {
383// Shut everything down.
384c.conn.Close()
385<-c.doneRead
386return
387}
388}
389req.cookie.Sequence = c.newSequenceId()
390c.cookieChan <- req.cookie
391if err := c.writeBuffer(req.buf); err != nil {
392c.conn.Close()
393<-c.doneRead
394return
395}
396close(req.seq)
397case <-c.doneRead:
398return
399}
400}
401}
402
403// noop circumvents the usual request sending goroutines and forces a round
404// trip request manually.
405func (c *Conn) noop() error {
406cookie := c.NewCookie(true, true)
407cookie.Sequence = c.newSequenceId()
408c.cookieChan <- cookie
409if err := c.writeBuffer(c.getInputFocusRequest()); err != nil {
410return err
411}
412cookie.Reply() // wait for the buffer to clear
413return nil
414}
415
416// writeBuffer is a convenience function for writing a byte slice to the wire.
417func (c *Conn) writeBuffer(buf []byte) error {
418if _, err := c.conn.Write(buf); err != nil {
419Logger.Printf("A write error is unrecoverable: %s", err)
420return err
421}
422return 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.
434func (c *Conn) readResponses() {
435defer close(c.eventChan)
436defer c.conn.Close()
437defer close(c.doneRead)
438
439var (
440err Error
441seq uint16
442replyBytes []byte
443)
444
445for {
446buf := make([]byte, 32)
447err, seq = nil, 0
448if _, err := io.ReadFull(c.conn, buf); err != nil {
449select {
450case <-c.doneSend:
451// gracefully closing
452return
453default:
454}
455Logger.Printf("A read error is unrecoverable: %s", err)
456c.eventChan <- err
457return
458}
459switch buf[0] {
460case 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.
463newErrFun, ok := NewErrorFuncs[int(buf[1])]
464if !ok {
465Logger.Printf("BUG: Could not find error constructor function "+
466"for error with number %d.", buf[1])
467continue
468}
469err = newErrFun(buf)
470seq = err.SequenceId()
471
472// This error is either sent to the event channel or a specific
473// cookie's error channel below.
474case 1: // This is a reply
475seq = Get16(buf[2:])
476
477// check to see if this reply has more bytes to be read
478size := Get32(buf[4:])
479if size > 0 {
480byteCount := 32 + size*4
481biggerBuf := make([]byte, byteCount)
482copy(biggerBuf[:32], buf)
483if _, err := io.ReadFull(c.conn, biggerBuf[32:]); err != nil {
484Logger.Printf("A read error is unrecoverable: %s", err)
485c.eventChan <- err
486return
487}
488replyBytes = biggerBuf
489} else {
490replyBytes = buf
491}
492
493// This reply is sent to its corresponding cookie below.
494default: // 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).
500evNum := int(buf[0] & 127)
501newEventFun, ok := NewEventFuncs[evNum]
502if !ok {
503Logger.Printf("BUG: Could not find event construct function "+
504"for event with number %d.", evNum)
505continue
506}
507c.eventChan <- newEventFun(buf)
508continue
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.
519for cookie := range c.cookieChan {
520// This is the cookie we're looking for. Process and break.
521if cookie.Sequence == seq {
522if err != nil { // this is an error to a request
523// synchronous processing
524if cookie.errorChan != nil {
525cookie.errorChan <- err
526} else { // asynchronous processing
527c.eventChan <- err
528// if this is an unchecked reply, ping the cookie too
529if cookie.pingChan != nil {
530cookie.pingChan <- true
531}
532}
533} else { // this is a reply
534if cookie.replyChan == nil {
535Logger.Printf("Reply with sequence id %d does not "+
536"have a cookie with a valid reply channel.", seq)
537continue
538} else {
539cookie.replyChan <- replyBytes
540}
541}
542break
543}
544
545switch {
546// Checked requests with replies
547case cookie.replyChan != nil && cookie.errorChan != nil:
548Logger.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
552case cookie.replyChan != nil && cookie.pingChan != nil:
553Logger.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",
556cookie.Sequence, seq)
557// Checked requests without replies
558case cookie.pingChan != nil && cookie.errorChan != nil:
559cookie.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.
569func processEventOrError(everr eventOrError) (Event, Error) {
570switch ee := everr.(type) {
571case Event:
572return ee, nil
573case Error:
574return nil, ee
575case error:
576// c.conn read error
577case nil:
578// c.eventChan is closed
579default:
580Logger.Printf("Invalid event/error type: %T", everr)
581}
582return 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.
593func (c *Conn) WaitForEvent() (Event, Error) {
594return 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.
601func (c *Conn) PollForEvent() (Event, Error) {
602select {
603case everr := <-c.eventChan:
604return processEventOrError(everr)
605default:
606return nil, nil
607}
608}
609