// Copyright 2009 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // The XGB package implements the X11 core protocol. // It is based on XCB: http://xcb.freedesktop.org/ package xgb import ( "fmt"; "io"; "net"; "os"; "strconv"; "strings"; ) // A Conn represents a connection to an X server. // Only one goroutine should use a Conn's methods at a time. type Conn struct { conn net.Conn; nextId Id; nextCookie Cookie; replies map[Cookie][]byte; events queue; err os.Error; defaultScreen int; scratch [32]byte; Setup SetupInfo; } // Id is used for all X identifiers, such as windows, pixmaps, and GCs. type Id uint32 // Cookies are the sequence numbers used to pair replies up with their requests type Cookie uint16 type Keysym uint32 type Timestamp uint32 // Event is an interface that can contain any of the events returned by the server. // Use a type assertion switch to extract the Event structs. type Event interface{} // Error contains protocol errors returned to us by the X server. type Error struct { Detail uint8; Major uint8; Minor uint16; Cookie Cookie; Id Id; } func (e *Error) String() string { return fmt.Sprintf("Bad%s (major=%d minor=%d cookie=%d id=0x%x)", errorNames[e.Detail], e.Major, e.Minor, e.Cookie, e.Id) } // NewID generates a new unused ID for use with requests like CreateWindow. func (c *Conn) NewId() Id { id := c.nextId; // TODO: handle ID overflow c.nextId++; return id; } // Pad a length to align on 4 bytes. func pad(n int) int { return (n + 3) & ^3 } func put16(buf []byte, v uint16) { buf[0] = byte(v); buf[1] = byte(v >> 8); } func put32(buf []byte, v uint32) { buf[0] = byte(v); buf[1] = byte(v >> 8); buf[2] = byte(v >> 16); buf[3] = byte(v >> 24); } func get16(buf []byte) uint16 { v := uint16(buf[0]); v |= uint16(buf[1]) << 8; return v; } func get32(buf []byte) uint32 { v := uint32(buf[0]); v |= uint32(buf[1]) << 8; v |= uint32(buf[2]) << 16; v |= uint32(buf[3]) << 32; return v; } // Voodoo to count the number of bits set in a value list mask. func popCount(mask0 int) int { mask := uint32(mask0); n := 0; for i := uint32(0); i < 32; i++ { if mask&(1< 0 { copy(q.data, q.data[q.a:q.b]); q.a, q.b = 0, q.b-q.a; } else { newData := make([][]byte, (len(q.data)*3)/2); copy(newData, q.data); q.data = newData; } } q.data[q.b] = item; q.b++; } func (q *queue) dequeue() []byte { if q.a < q.b { item := q.data[q.a]; q.a++; return item; } return nil; } // sendRequest sends a request to the server and return its associated sequence number, or cookie. // It is only used to send the fixed length portion of the request, sendBytes and friends are used // to send any additional variable length data. func (c *Conn) sendRequest(buf []byte) Cookie { if _, err := c.conn.Write(buf); err != nil { fmt.Fprintf(os.Stderr, "x protocol write error: %s\n", err); c.err = err; } cookie := c.nextCookie; c.nextCookie++; return cookie; } // sendPadding sends enough bytes to align to a 4-byte border. // It is used to pad the variable length data that is used with some requests. func (c *Conn) sendPadding(n int) { x := pad(n) - n; if x > 0 { _, err := c.conn.Write(c.scratch[0:x]); if err != nil { fmt.Fprintf(os.Stderr, "x protocol write error: %s\n", err); c.err = err; } } } // sendBytes sends a byte slice as variable length data after the fixed portion of a request, // along with any necessary padding. func (c *Conn) sendBytes(buf []byte) { if _, err := c.conn.Write(buf); err != nil { fmt.Fprintf(os.Stderr, "x protocol write error: %s\n", err); c.err = err; } c.sendPadding(len(buf)); } func (c *Conn) sendString(str string) { c.sendBytes(strings.Bytes(str)) } // sendUInt32s sends a list of 32-bit integers as variable length data. func (c *Conn) sendUInt32List(list []uint32) { buf := make([]byte, len(list)*4); for i := 0; i < len(list); i++ { put32(buf[i*4:], list[i]) } c.sendBytes(buf); } func (c *Conn) sendIdList(list []Id, length int) { buf := make([]byte, length*4); for i := 0; i < length; i++ { put32(buf[i*4:], uint32(list[i])) } c.sendBytes(buf); } func (c *Conn) sendKeysymList(list []Keysym, length int) { buf := make([]byte, length*4); for i := 0; i < length; i++ { put32(buf[i*4:], uint32(list[i])) } c.sendBytes(buf); } // readNextReply reads and processes the next server reply. // If it is a protocol error then it is returned as an Error. // Events are pushed onto the event queue and replies to requests // are stashed away in a map indexed by the sequence number. func (c *Conn) readNextReply() os.Error { buf := make([]byte, 32); if _, err := io.ReadFull(c.conn, buf); err != nil { fmt.Fprintf(os.Stderr, "x protocol read error: %s\n", err); return err; } switch buf[0] { case 0: err := &Error{ Detail: buf[1], Cookie: Cookie(get16(buf[2:])), Id: Id(get32(buf[4:])), Minor: get16(buf[8:]), Major: buf[10], }; fmt.Fprintf(os.Stderr, "x protocol error: %s\n", err); return err; case 1: seq := Cookie(get16(buf[2:])); size := get32(buf[4:]); if size > 0 { bigbuf := make([]byte, 32+size*4, 32+size*4); copy(bigbuf[0:32], buf); if _, err := io.ReadFull(c.conn, bigbuf[32:]); err != nil { fmt.Fprintf(os.Stderr, "x protocol read error: %s\n", err); return err; } c.replies[seq] = bigbuf; } else { c.replies[seq] = buf } default: c.events.queue(buf) } return nil; } // waitForReply looks for a reply in the map indexed by sequence number. // If the reply is not in the map it will block while reading replies from the server // until the reply is found or an error occurs. func (c *Conn) waitForReply(cookie Cookie) ([]byte, os.Error) { for { if reply, ok := c.replies[cookie]; ok { c.replies[cookie] = reply, false; return reply, nil; } if err := c.readNextReply(); err != nil { return nil, err } } panic("unreachable"); } // WaitForEvent returns the next event from the server. // It will block until an event is available. func (c *Conn) WaitForEvent() (Event, os.Error) { for { if reply := c.events.dequeue(); reply != nil { return parseEvent(reply) } if err := c.readNextReply(); err != nil { return nil, err } } panic("unreachable"); } // PollForEvent returns the next event from the server if one is available in the internal queue. // It will not read from the connection, so you must call WaitForEvent to receive new events. // Only use this function to empty the queue without blocking. func (c *Conn) PollForEvent() (Event, os.Error) { if reply := c.events.dequeue(); reply != nil { return parseEvent(reply) } return nil, nil; } // Dial connects to the X server given in the 'display' string. // The display string is typically taken from os.Getenv("DISPLAY"). func Dial(display string) (*Conn, os.Error) { var err os.Error; c := new(Conn); if display[0] == '/' { c.conn, err = net.Dial("unix", "", display); if err != nil { fmt.Printf("cannot connect: %v\n", err); return nil, err; } } else { parts := strings.Split(display, ":", 2); host := parts[0]; port := 0; if len(parts) > 1 { parts = strings.Split(parts[1], ".", 2); port, _ = strconv.Atoi(parts[0]); if len(parts) > 1 { c.defaultScreen, _ = strconv.Atoi(parts[1]) } } display = fmt.Sprintf("%s:%d", host, port+6000); c.conn, err = net.Dial("tcp", "", display); if err != nil { fmt.Printf("cannot connect: %v\n", err); return nil, err; } } // TODO: get these from .Xauthority var authName, authData []byte; buf := make([]byte, 12+pad(len(authName))+pad(len(authData))); buf[0] = 'l'; buf[1] = 0; put16(buf[2:], 11); put16(buf[4:], 0); put16(buf[6:], uint16(len(authName))); put16(buf[8:], uint16(len(authData))); put16(buf[10:], 0); copy(buf[12:], authName); copy(buf[12+pad(len(authName)):], authData); if _, err = c.conn.Write(buf); err != nil { return nil, err } head := make([]byte, 8); if _, err = io.ReadFull(c.conn, head[0:8]); err != nil { return nil, err } code := head[0]; reasonLen := head[1]; major := get16(head[2:]); minor := get16(head[4:]); dataLen := get16(head[6:]); if major != 11 || minor != 0 { return nil, os.NewError(fmt.Sprintf("x protocol version mismatch: %d.%d", major, minor)) } buf = make([]byte, int(dataLen)*4+8, int(dataLen)*4+8); copy(buf, head); if _, err = io.ReadFull(c.conn, buf[8:]); err != nil { return nil, err } if code == 0 { reason := buf[8 : 8+reasonLen]; return nil, os.NewError(fmt.Sprintf("x protocol authentication refused: %s", string(reason))); } getSetupInfo(buf, &c.Setup); if c.defaultScreen >= len(c.Setup.Roots) { c.defaultScreen = 0 } c.nextId = Id(c.Setup.ResourceIdBase); c.nextCookie = 1; c.replies = make(map[Cookie][]byte); c.events = queue{make([][]byte, 100), 0, 0}; return c, nil; } // Close closes the connection to the X server. 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] } // ClientMessageData holds the data from a client message, // duplicated in three forms because Go doesn't have unions. type ClientMessageData struct { Data8 [20]byte; Data16 [10]uint16; Data32 [5]uint32; } func getClientMessageData(b []byte, v *ClientMessageData) int { copy(&v.Data8, b); for i := 0; i < 10; i++ { v.Data16[i] = get16(b[i*2:]) } for i := 0; i < 5; i++ { v.Data32[i] = get32(b[i*4:]) } return 20; }