diff options
author | Peter Harris <git@peter.is-a-geek.org> | 2010-12-22 16:55:07 -0500 |
---|---|---|
committer | Peter Harris <git@peter.is-a-geek.org> | 2010-12-22 16:55:07 -0500 |
commit | 49b9a7f64aad4eb47f2a9671c539546c43f2c785 (patch) | |
tree | 2ee9f7aba42e67ef74f9dcbe9f4d2cc2f497aa51 | |
parent | 32b2f8870f0aeda7433df99db7317a8054a7f251 (diff) |
Basic functionality complete
test.go functions as expected if you specify the full display as
tcp/localhost:0 and the server has xauth disabled.
-rw-r--r-- | test.go | 68 | ||||
-rw-r--r-- | xgob.go | 186 |
2 files changed, 236 insertions, 18 deletions
@@ -1,9 +1,75 @@ package main import ( + "fmt" "xgob" ) +func toUint32 (buf []byte) uint32 { + rv := uint32(buf[0]) << 24 + rv |= uint32(buf[1]) << 16 + rv |= uint32(buf[2]) << 8 + rv |= uint32(buf[3]) + return rv +} + +func decodeGif (in chan interface{}, out chan uint32, err chan xgob.Error) { + r := <- in + switch reply := r.(type) { + case xgob.Reply: + focus := toUint32(reply.Remainder[:4]) + out <- focus + case xgob.Error: + err <- reply + out <- 0x80000000 + } +} + func main () { - xgob.Connect("") + c, _ := xgob.Connect("") + + error := make(chan xgob.Error) + + var gifRequest [8]byte + gifRequest[0] = 43 /* X_GetInputFocus */ + gifRequest[3] = 1 /* request length */ + + done := make(chan uint32) + + cookie1 := c.WriteRequest(gifRequest[:4]) + go decodeGif(cookie1, done, error) + + gifRequest[3] = 2 // bad length + cookie2 := c.WriteRequest(gifRequest[:]) + go decodeGif(cookie2, done, error) + + c.Flush() + + if c.HasError() { + return + } + + /* Wait for both replies, process events */ + var i int + for i < 2 { + select { + case win := <- done: + if win < 0x80000000 { + fmt.Printf("Window with focus: 0x%X", win) + fmt.Println() + } + i++ + case e := <- error: + fmt.Printf("Error of type %d", e.Error) + fmt.Println() + fmt.Printf(" request %d.%d", e.Major, e.Minor) + fmt.Println() + fmt.Printf(" sequence %d", e.Sequence) + fmt.Println() + fmt.Printf(" value 0x%X (%d)", e.BadValue, e.BadValue) + fmt.Println() + case event := <- c.Event: + println("Unexpected event", event.EventType) + } + } } @@ -8,23 +8,49 @@ import ( "os" "strconv" "strings" + "sync" ) const X_TCP_PORT = 6000 +type ConnectionBlock struct { + Major, Minor uint16 + todo []byte +} + +type Event struct { + EventType uint8 + Sent bool + Remainder [31]byte +} + type Error struct { + Sequence uint64 + BadValue uint32 + Minor uint16 + Major uint8 + Error uint8 } -type ConnectionBlock struct { - major, minor uint16 - todo []byte +type Reply struct { + Sequence uint64 + Length uint32 + Remainder []byte + Aux byte } type Connection struct { - conn io.ReadWriteCloser - Setup ConnectionBlock + Setup ConnectionBlock + Event chan Event + conn io.ReadWriteCloser + reply map[uint64]chan interface {} + replyMutex sync.Mutex + lastRequestWritten uint64 + lastSequenceRead uint64 } +const events_before_readblock = 10000 + func parse_display (name string) (host string, protocol string, display int, screen int) { if len(name) == 0 { name = os.Getenv("DISPLAY") @@ -165,22 +191,46 @@ func getUint16(in *bufio.Reader) uint16 { return rv } +func getUint32(in *bufio.Reader) uint32 { + var rv uint32 + i, _ := in.ReadByte() + rv = uint32(i) << 24 + i, _ = in.ReadByte() + rv |= uint32(i) << 16 + i, _ = in.ReadByte() + rv |= uint32(i) << 8 + i, _ = in.ReadByte() + rv |= uint32(i) + return rv +} + +func skipBytes (in *bufio.Reader, skip uint) { + for i := uint(0); i < skip; i++ { + _, _ = in.ReadByte() + } +} + +func readAll(in *bufio.Reader, buf []byte) { + var read int + len := len(buf) + for read < len { + input, err := in.Read(buf[read:]) + if err != nil { + return + } + read += input + } +} + func (c *Connection) read_setup(in *bufio.Reader) bool { status, _ := in.ReadByte() reason_len, _ := in.ReadByte() - c.Setup.major = getUint16(in) - c.Setup.minor = getUint16(in) + c.Setup.Major = getUint16(in) + c.Setup.Minor = getUint16(in) len := getUint16(in) c.Setup.todo = make([]byte, len * 4) - var read int - for read < int(len) { - in, err := in.Read(c.Setup.todo[read:]) - if err != nil { - return false - } - read += in - } + readAll(in, c.Setup.todo) switch status { case 0: @@ -198,14 +248,83 @@ func (c *Connection) read_setup(in *bufio.Reader) bool { return false } +func (c *Connection) fullSeq(seq uint16) uint64 { + var fs uint64 + fs = (c.lastSequenceRead &^ 0xFFFF) | uint64(seq) + if fs < c.lastSequenceRead { + fs += 0x10000 + } + return fs +} + +func (c *Connection) postReply (seq uint64, reply interface{}) { + c.replyMutex.Lock() + defer c.replyMutex.Unlock() + + if c.reply[seq] == nil { + println("Error: Unexpected sequence", seq) + return + } + + for i := c.lastSequenceRead; i < seq; i++ { + c.reply[i] <- nil + c.reply[i] = nil, false + } + c.reply[seq] <- reply + c.lastSequenceRead = seq +} + +func (c *Connection) read (in *bufio.Reader) { + for { + t, err := in.ReadByte() + if err != nil { + c.conn = nil + return + } + + switch t { + case 0: + var e Error + e.Error, _ = in.ReadByte() + e.Sequence = c.fullSeq(getUint16(in)) + e.BadValue = getUint32(in) + e.Minor = getUint16(in) + e.Major, _ = in.ReadByte() + skipBytes(in, 20) + c.postReply(e.Sequence, e) + case 1: + var r Reply + r.Aux, _ = in.ReadByte() + r.Sequence = c.fullSeq(getUint16(in)) + r.Length = getUint32(in) + r.Remainder = make([]byte, r.Length * 4 + 24) + readAll(in, r.Remainder) + c.postReply(r.Sequence, r) + default: + var e Event + e.EventType = t + if t & 0x80 != 0 { + e.EventType = t & 0x7F + e.Sent = true + } + readAll(in, e.Remainder[:]) + c.Event <- e + // TODO GenericEvent + } + } +} + func (c *Connection)connect_auth(name string, data string) { c.write_setup(name, data) in := bufio.NewReader(c.conn) if !c.read_setup(in) { - c.conn.Close() - c.conn = nil + c.Disconnect() return } + c.Event = make (chan Event, events_before_readblock) + c.reply = make (map [uint64]chan interface{}) + c.lastSequenceRead = 1 // Not really, but this prevents writing to a nil channel + go c.read(in) } func Connect (dpy string) (conn *Connection, screen int) { @@ -227,4 +346,37 @@ func Connect (dpy string) (conn *Connection, screen int) { func (c *Connection) Disconnect () { c.conn.Close() + c.conn = nil +} + +func (c *Connection) WriteMultiRequest (req []byte, expected int) chan interface{} { + rv := make(chan interface {}, expected) + + if c == nil || c.conn == nil { + return rv + } + + c.replyMutex.Lock() + defer c.replyMutex.Unlock() + + c.lastRequestWritten++ + c.reply[c.lastRequestWritten] = rv + + c.conn.Write(req) + return rv +} + +func (c *Connection) WriteRequest (req []byte) chan interface{} { + return c.WriteMultiRequest(req, 1) +} + +func (c *Connection) Flush () { + // TODO buffer WriteRequest +} + +func (c *Connection) HasError () bool { + if c == nil || c.conn == nil { + return true + } + return false } |