summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPeter Harris <git@peter.is-a-geek.org>2010-12-22 16:55:07 -0500
committerPeter Harris <git@peter.is-a-geek.org>2010-12-22 16:55:07 -0500
commit49b9a7f64aad4eb47f2a9671c539546c43f2c785 (patch)
tree2ee9f7aba42e67ef74f9dcbe9f4d2cc2f497aa51
parent32b2f8870f0aeda7433df99db7317a8054a7f251 (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.go68
-rw-r--r--xgob.go186
2 files changed, 236 insertions, 18 deletions
diff --git a/test.go b/test.go
index 03436bd..a0b7c22 100644
--- a/test.go
+++ b/test.go
@@ -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)
+ }
+ }
}
diff --git a/xgob.go b/xgob.go
index c9ae8e6..fc76de7 100644
--- a/xgob.go
+++ b/xgob.go
@@ -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
}