aboutsummaryrefslogtreecommitdiff
path: root/nexgb/xgb.go
diff options
context:
space:
mode:
Diffstat (limited to 'nexgb/xgb.go')
-rw-r--r--nexgb/xgb.go102
1 files changed, 79 insertions, 23 deletions
diff --git a/nexgb/xgb.go b/nexgb/xgb.go
index b453427..26c0138 100644
--- a/nexgb/xgb.go
+++ b/nexgb/xgb.go
@@ -17,6 +17,11 @@ import (
)
const (
+ // cookieBuffer represents the queue size of cookies existing at any
+ // point in time. The size of the buffer is really only important when
+ // there are many requests without replies made in sequence. Once the
+ // buffer fills, a round trip request is made to clear the buffer.
+ cookieBuffer = 1000
readBuffer = 100
writeBuffer = 100
)
@@ -32,7 +37,7 @@ type Conn struct {
extensions map[string]byte
eventChan chan eventOrError
- cookieChan chan cookie
+ cookieChan chan *cookie
xidChan chan xid
seqChan chan uint16
reqChan chan *request
@@ -68,7 +73,7 @@ func NewConnDisplay(display string) (*Conn, error) {
conn.extensions = make(map[string]byte)
- conn.cookieChan = make(chan cookie, 100)
+ conn.cookieChan = make(chan *cookie, cookieBuffer)
conn.xidChan = make(chan xid, 5)
conn.seqChan = make(chan uint16, 20)
conn.reqChan = make(chan *request, 100)
@@ -87,6 +92,12 @@ func (c *Conn) Close() {
c.conn.Close()
}
+// DefaultScreen returns the Screen info for the default screen, which is
+// 0 or the one given in the display argument to Dial.
+func (c *Conn) DefaultScreen() *ScreenInfo {
+ return &c.Setup.Roots[c.defaultScreen]
+}
+
// Id is used for all X identifiers, such as windows, pixmaps, and GCs.
type Id uint32
@@ -95,6 +106,7 @@ type Id uint32
type Event interface {
ImplementsEvent()
Bytes() []byte
+ String() string
}
// newEventFuncs is a map from event numbers to functions that create
@@ -188,7 +200,8 @@ func (c *Conn) newSequenceId() uint16 {
// to match up replies with requests.
// Since sequence ids can only be 16 bit integers we start over at zero when it
// comes time to wrap.
-// FIXME: 65,536 requests without replies cannot be made in a single sequence.
+// N.B. As long as the cookie buffer is less than 2^16, there are no limitations
+// on the number (or kind) of requests made in sequence.
func (c *Conn) generateSeqIds() {
seqid := uint16(1)
for {
@@ -206,13 +219,14 @@ func (c *Conn) generateSeqIds() {
// The cookie is used to match up the reply/error.
type request struct {
buf []byte
- cookie cookie
+ cookie *cookie
}
// newRequest takes the bytes an a cookie, constructs a request type,
-// and sends it over the Conn.reqChan channel. It then returns the cookie
-// (for convenience).
-func (c *Conn) newRequest(buf []byte, cookie cookie) {
+// and sends it over the Conn.reqChan channel.
+// Note that the sequence number is added to the cookie after it is sent
+// over the request channel.
+func (c *Conn) newRequest(buf []byte, cookie *cookie) {
c.reqChan <- &request{buf: buf, cookie: cookie}
}
@@ -220,15 +234,38 @@ func (c *Conn) newRequest(buf []byte, cookie cookie) {
// the bytes to the wire and adds the cookie to the cookie queue.
func (c *Conn) sendRequests() {
for req := range c.reqChan {
+ // ho there! if the cookie channel is nearly full, force a round
+ // trip to clear out the cookie buffer.
+ // Note that we circumvent the request channel, because we're *in*
+ // the request channel.
+ if len(c.cookieChan) == cookieBuffer - 1 {
+ cookie := c.newCookie(true, true)
+ cookie.Sequence = c.newSequenceId()
+ c.cookieChan <- cookie
+ if !c.writeBuffer(c.getInputFocusRequest()) {
+ return
+ }
+ GetInputFocusCookie{cookie}.Reply() // wait for the buffer to clear
+ }
+
+ req.cookie.Sequence = c.newSequenceId()
c.cookieChan <- req.cookie
- if _, err := c.conn.Write(req.buf); err != nil {
- fmt.Fprintf(os.Stderr, "x protocol write error: %s\n", err)
- close(c.reqChan)
+ if !c.writeBuffer(req.buf) {
return
}
}
}
+// writeBuffer is a convenience function for writing a byte slice to the wire.
+func (c *Conn) writeBuffer(buf []byte) bool {
+ if _, err := c.conn.Write(buf); err != nil {
+ fmt.Fprintf(os.Stderr, "x protocol write error: %s\n", err)
+ close(c.reqChan)
+ return false
+ }
+ return true
+}
+
// readResponses is a goroutine that reads events, errors and
// replies off the wire.
// When an event is read, it is always added to the event channel.
@@ -260,7 +297,15 @@ func (c *Conn) readResponses() {
case 0: // This is an error
// Use the constructor function for this error (that is auto
// generated) by looking it up by the error number.
- err = newErrorFuncs[int(buf[1])](buf)
+ newErrFun, ok := newErrorFuncs[int(buf[1])]
+ if !ok {
+ fmt.Fprintf(os.Stderr,
+ "BUG: " +
+ "Could not find error constructor function for error " +
+ "with number %d.", buf[1])
+ continue
+ }
+ err = newErrFun(buf)
seq = err.SequenceId()
// This error is either sent to the event channel or a specific
@@ -291,22 +336,23 @@ func (c *Conn) readResponses() {
// Note that we AND the event number with 127 so that we ignore
// the most significant bit (which is set when it was sent from
// a SendEvent request).
- event = newEventFuncs[int(buf[0] & 127)](buf)
- // seq = event.SequenceId() // 0 for KeymapNotify
+ evNum := int(buf[0] & 127)
+ newEventFun, ok := newEventFuncs[evNum]
+ if !ok {
+ fmt.Fprintf(os.Stderr,
+ "BUG: " +
+ "Could not find event constructor function for event " +
+ "with number %d.", evNum)
+ continue
+ }
+
+ event = newEventFun(buf)
// Put the event into the queue.
c.eventChan <- event
// No more processing for events.
continue
-
- // If this was a KeymapNotify event, then we don't do any more
- // processing since we don't have any sequence id.
- // if event != nil {
- // if _, ok := event.(KeymapNotifyEvent); ok {
- // continue
- // }
- // }
}
// At this point, we have a sequence number and we're either
@@ -326,12 +372,17 @@ func (c *Conn) readResponses() {
cookie.errorChan <- err
} else { // asynchronous processing
c.eventChan <- err
+ // if this is an unchecked reply, ping the cookie too
+ if cookie.pingChan != nil {
+ cookie.pingChan <- true
+ }
}
} else { // this is a reply
if cookie.replyChan == nil {
fmt.Fprintf(os.Stderr,
"Reply with sequence id %d does not have a " +
"cookie with a valid reply channel.\n", seq)
+ continue
} else {
cookie.replyChan <- replyBytes
}
@@ -344,10 +395,14 @@ func (c *Conn) readResponses() {
case cookie.replyChan != nil && cookie.errorChan != nil:
fmt.Fprintf(os.Stderr,
"Found cookie with sequence id %d that is expecting a " +
- "reply but will never get it.\n", cookie.Sequence)
+ "reply but will never get it. Currently on sequence " +
+ "number %d\n", cookie.Sequence, seq)
// Unchecked requests with replies
case cookie.replyChan != nil && cookie.pingChan != nil:
- cookie.pingChan <- true
+ fmt.Fprintf(os.Stderr,
+ "Found cookie with sequence id %d that is expecting a " +
+ "reply (and not an error) but will never get it. " +
+ "Currently on sequence number %d\n", cookie.Sequence, seq)
// Checked requests without replies
case cookie.pingChan != nil && cookie.errorChan != nil:
cookie.pingChan <- true
@@ -368,6 +423,7 @@ func processEventOrError(everr eventOrError) (Event, Error) {
return nil, ee
default:
fmt.Fprintf(os.Stderr, "Invalid event/error type: %T\n", everr)
+ return nil, nil
}
panic("unreachable")
}