summaryrefslogtreecommitdiff
path: root/xgob.go
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 /xgob.go
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.
Diffstat (limited to 'xgob.go')
-rw-r--r--xgob.go186
1 files changed, 169 insertions, 17 deletions
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
}