Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code | Sign in
(373)

Side by Side Diff: src/pkg/xgb/xgb.go

Issue 162053: A first stab at porting the XCB X11 protocol bindings to go.
Patch Set: code review 162053: A first stab at porting the XCB X11 protocol bindings to go. Created 15 years, 11 months ago
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments. Please Sign in to add in-line comments.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2009 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
4
5 // The XGB package implements the X11 core protocol.
6 // It is based on XCB: http://xcb.freedesktop.org/
7 package xgb
8
9 import "fmt"
nigeltao 2009/11/30 13:02:33 Russ -- is it conventional to have only one "impor
rsc 2009/11/30 17:16:21 Good catch - this import list should be factored.
10 import "os"
11 import "io"
12 import "net"
13 import "strings"
14 import "strconv"
15
16 // The connection to the server. This struct is not reentrant, so please
rsc 2009/11/30 02:01:11 Documentation comments should begin with the name
tor 2009/11/30 13:48:46 Done.
17 // only call its methods from within one goroutine.
18 type Conn struct {
19 sock net.Conn;
rsc 2009/11/30 02:01:11 please call this conn (on some systems it might no
tor 2009/11/30 13:48:46 Done.
20 nextId uint32;
21 nextCookie uint16;
22 replies map[uint16][]byte;
nigeltao 2009/11/30 13:02:33 Russ -- would it be worth having type seqNum uint1
tor 2009/11/30 13:48:46 I decided against using too many type aliases, esp
rsc 2009/11/30 17:16:21 This seems like a good compromise: it's always con
23 events queue;
24 error os.Error;
rsc 2009/11/30 02:01:11 typically err no error.
tor 2009/11/30 13:48:46 I've been writing error and okay for five years in
rsc 2009/11/30 17:16:21 I certainly understand the consistency in your oth
25 defaultScreen int;
26 Setup SetupInfo;
27 }
28
29 // An interface that can contain any of the events returned by the server.
30 // Use a type assertion switch to extract the Event structs.
31 type Event interface{}
32
33 // Generate a new unused ID for use with requests like CreateWindow.
nigeltao 2009/11/30 13:02:33 Can you add a TODO comment to check for ID overflo
tor 2009/11/30 13:48:46 What should the behavior be when we run out?
rsc 2009/11/30 17:16:21 That's why it's a TODO, I guess. ;-) // TODO(tor,
34 func (c *Conn) NewId() uint32 {
35 id := c.nextId;
36 c.nextId++;
37 return id;
38 }
39
40 // Pad a length to align on 4 bytes.
41 func pad(n int) int { return (n + 3) & ^3 }
42
43 func put8(buf []byte, offset int, v uint8) { buf[offset] = v }
rsc 2009/11/30 02:01:11 Instead of passing offset explicitly, you could ju
tor 2009/11/30 13:48:46 Good idea, but I could also make the auto generate
44 func get8(buf []byte, offset int) uint8 { return buf[offset] }
45
46 func put16(buf []byte, offset int, v uint16) {
47 buf[offset] = byte(v);
48 buf[offset+1] = byte(v >> 8);
49 }
50
51 func put32(buf []byte, offset int, v uint32) {
52 buf[offset] = byte(v);
53 buf[offset+1] = byte(v >> 8);
54 buf[offset+2] = byte(v >> 16);
55 buf[offset+3] = byte(v >> 24);
56 }
57
58 func get16(buf []byte, offset int) uint16 {
59 v := uint16(buf[offset]);
60 v |= uint16(buf[offset+1]) << 8;
61 return v;
62 }
63
64 func get32(buf []byte, offset int) uint32 {
65 v := uint32(buf[offset]);
66 v |= uint32(buf[offset+1]) << 8;
67 v |= uint32(buf[offset+2]) << 16;
68 v |= uint32(buf[offset+3]) << 32;
69 return v;
70 }
71
72 // Voodoo to count the number of bits set in a value list mask.
73 func popCount(mask0 int) int {
74 mask := uint32(mask0);
75 y := (mask >> 1) & 033333333333;
76 y = mask - y - ((y >> 1) & 033333333333);
77 y = ((y + (y >> 3)) & 030707070707) % 077;
rsc 2009/11/30 02:01:11 The % 077 trick might have made sense on a PDP-10,
tor 2009/11/30 13:48:46 Done.
78 return int(y);
79 }
80
81 // A simple queue used to stow away events.
82 type queue struct {
83 data [][]byte;
84 a, b int;
85 }
86
87 func (q *queue) queue(item []byte) {
88 if q.b == len(q.data) {
89 if q.a > 0 {
90 for i := q.a; i < q.b; i++ {
rsc 2009/11/30 02:01:11 If you update to the latest tree (cd $GOROOT/src;
tor 2009/11/30 13:48:46 Or I could just have a and b wrap around len(data)
91 q.data[i-q.a] = q.data[i]
92 }
93 q.a, q.b = 0, q.b-q.a;
94 } else {
95 newData := make([][]byte, (len(q.data)*3)/2);
96 for i := q.a; i < q.b; i++ {
rsc 2009/11/30 02:01:11 copy(newData, q.data);
tor 2009/11/30 13:48:46 Done.
97 newData[i] = q.data[i]
98 }
99 q.data = newData;
100 }
101 }
102 q.data[q.b] = item;
103 q.b++;
104 }
105
106 func (q *queue) unqueue() []byte {
rsc 2009/11/30 02:01:11 the verb is usually dequeue.
tor 2009/11/30 13:48:46 Done.
107 if q.a < q.b {
108 item := q.data[q.a];
109 q.a++;
110 return item;
111 }
112 return nil;
113 }
114
115 // Send a request to the server and return its associated sequence number, or co okie.
rsc 2009/11/30 02:01:11 // sendRequest sends ... and returns ...
tor 2009/11/30 13:48:46 Done.
116 // This will only send the fixed length portion of the request.
117 func (c *Conn) sendRequest(buf []byte) uint16 {
118 _, error := c.sock.Write(buf);
119 if error != nil {
120 fmt.Fprintf(os.Stderr, "x protocol write error: %s\n", error);
nigeltao 2009/11/30 13:02:33 Nit: I think the "X" in "X protocol" should be cap
121 c.error = error;
122 }
123 cookie := c.nextCookie;
124 c.nextCookie++;
125 return cookie;
126 }
127
128 // Send enough bytes to align to a 4-byte border.
rsc 2009/11/30 02:01:11 // sendPadding sends .... // It is used to pad ...
tor 2009/11/30 13:48:46 Done.
129 // Used to pad the variable length data that is used with some requests.
130 func (c *Conn) sendPadding(n int) {
131 var three [3]byte;
rsc 2009/11/30 02:01:11 This allocates each time sendPadding is called. It
tor 2009/11/30 13:48:46 Ow. I just assumed that since it was a constant si
rsc 2009/11/30 17:16:21 If you take its address and the compiler isn't sur
132 x := pad(n) - n;
133 if x > 0 {
134 _, error := c.sock.Write(three[0:x]);
rsc 2009/11/30 02:01:11 Typically called err not error. (etc.)
tor 2009/11/30 13:48:46 Done.
135 if error != nil {
136 fmt.Fprintf(os.Stderr, "x protocol write error: %s\n", e rror);
137 c.error = error;
138 }
139 }
140 }
141
142 // Send a byte slice as variable length data after the fixed portion of a reques t.
143 // Automatically pads.
144 func (c *Conn) sendBytes(buf []byte) {
145 _, error := c.sock.Write(buf);
146 if error != nil {
147 fmt.Fprintf(os.Stderr, "x protocol write error: %s\n", error);
148 c.error = error;
149 }
150 c.sendPadding(len(buf));
151 }
152
153 func (c *Conn) sendString(str string) { c.sendBytes(strings.Bytes(str)) }
154
155 // Send a list of 32-bit integers as variable length data.
156 func (c *Conn) sendUInt32s(list []uint32) {
157 buf := make([]byte, len(list)*4);
158 for i := 0; i < len(list); i++ {
159 put32(buf, i*4, list[i])
160 }
161 c.sendBytes(buf);
162 }
163
164 // Read the next server reply. If it is a protocol error then return it as an er ror.
165 // Push events onto the event queue and stash away replies to requests in a map
166 // indexed by the sequence number.
167 func (c *Conn) readNextReply() os.Error {
nigeltao 2009/11/30 13:02:33 Since this method reads both replies and events, p
168 buf := make([]byte, 32);
169 _, error := io.ReadFull(c.sock, buf);
170 if error != nil {
171 fmt.Fprintf(os.Stderr, "x protocol read error: %s\n", error);
172 return error;
173 }
174
175 if buf[0] == 0 {
rsc 2009/11/30 02:01:11 switch buf[0] {
tor 2009/11/30 13:48:46 Done.
176 detail := buf[1];
177 cookie := get16(buf, 2);
178 id := get32(buf, 4);
179 minor := get16(buf, 8);
180 major := buf[10];
181 str := fmt.Sprintf("Bad%s (major=%d minor=%d cookie=%d id=0x%x)" ,
182 errorNames[detail], major, minor, cookie, id);
183 fmt.Fprintf(os.Stderr, "x protocol error: %s\n", str);
184 return os.NewError(str);
rsc 2009/11/30 02:01:11 More idiomatic Go would be to put this information
tor 2009/11/30 13:48:46 Done.
185 }
186
187 if buf[0] == 1 {
188 seq := get16(buf, 2);
189 size := get32(buf, 4);
190 if size > 0 {
191 bigbuf := make([]byte, 32+size*4, 32+size*4);
192 copy(bigbuf[0:32], buf);
rsc 2009/11/30 02:01:11 I see you found copy. On the next line you can dr
tor 2009/11/30 13:48:46 Done.
193 _, error = io.ReadFull(c.sock, bigbuf[32:len(bigbuf)]);
194 if error != nil {
195 fmt.Fprintf(os.Stderr, "x protocol read error: % s\n", error);
196 return error;
197 }
198 c.replies[seq] = bigbuf;
199 } else {
200 c.replies[seq] = buf
201 }
202 }
203
204 if buf[0] > 1 {
205 c.events.queue(buf)
206 }
207
208 return nil;
209 }
210
211 // Looks for a reply in the map indexed by sequence number.
212 // Will block while reading replies from the server until it is found.
213 func (c *Conn) waitForReply(cookie uint16) ([]byte, os.Error) {
214 for {
215 reply, exists := c.replies[cookie];
rsc 2009/11/30 02:01:11 exists is fine, but this is typically ok, and merg
tor 2009/11/30 13:48:46 I generally dislike putting more than one concept/
rsc 2009/11/30 17:16:21 I'm sympathetic to that but it's still a common Go
216 if exists {
217 c.replies[cookie] = reply, false;
218 return reply, nil;
219 }
220 error := c.readNextReply();
221 if error != nil {
222 return nil, error
223 }
224 }
225 panic("unreachable");
226 }
227
228 // Return the next event from the server.
229 // Block until an event is available.
230 func (c *Conn) WaitForEvent() (Event, os.Error) {
231 for {
232 reply := c.events.unqueue();
233 if reply != nil {
234 return parseEvent(reply), nil
rsc 2009/11/30 02:01:11 It seems like if parseEvent fails, it should retur
tor 2009/11/30 13:48:46 Done.
235 }
236 error := c.readNextReply();
237 if error != nil {
238 return nil, error
239 }
240 }
241 panic("unreachable");
242 }
243
244 // Return the next event from the server if one is available in the internal que ue.
245 // This function will not read from the socket, so you must call WaitForEvent to receive new events.
246 // Only use this function to empty the queue without blocking,
247 // for instance when processing Expose events.
248 func (c *Conn) PollForEvent() Event {
249 reply := c.events.unqueue();
250 if reply != nil {
251 return parseEvent(reply)
252 }
253 return nil;
254 }
255
256 // Return the Screen info for the default screen (0 or the one given in the DISP LAY environment variable).
rsc 2009/11/30 02:01:11 This mentions the DISPLAY environment variable but
tor 2009/11/30 13:48:46 Done.
257 func (c *Conn) DefaultScreen() *ScreenInfo { return &c.Setup.Roots[c.defaul tScreen] }
258
259 // Connect to the X server given in the 'display' string.
nigeltao 2009/11/30 13:02:33 Is there an example main.go program to show me how
tor 2009/11/30 13:48:46 See my darcs repo for an example program: http://
260 func Dial(display string) (*Conn, os.Error) {
261 var error os.Error;
262
263 c := new(Conn);
264
265 if display[0] == '/' {
266 c.sock, error = net.Dial("unix", "", display);
267 if error != nil {
268 fmt.Printf("cannot connect: %v\n", error);
269 return nil, error;
270 }
271 } else {
272 parts := strings.Split(display, ":", 2);
273 host := parts[0];
274 port := 0;
275 if len(parts) > 1 {
276 parts = strings.Split(parts[1], ".", 2);
277 port, _ = strconv.Atoi(parts[0]);
278 if len(parts) > 1 {
279 c.defaultScreen, _ = strconv.Atoi(parts[1])
280 }
281 }
282 display = fmt.Sprintf("%s:%d", host, port+6000);
283 c.sock, error = net.Dial("tcp", "", display);
284 if error != nil {
285 fmt.Printf("cannot connect: %v\n", error);
286 return nil, error;
287 }
288 }
289
290 var authName, authData []byte;
rsc 2009/11/30 02:01:11 // TODO: Fill these in?
tor 2009/11/30 13:48:46 Indeed. It's one of the things I haven't done yet,
291 var buf []byte;
nigeltao 2009/11/30 13:02:33 I would fold this line with the next one using the
tor 2009/11/30 13:48:46 I put it separately because I reuse the variable f
rsc 2009/11/30 17:16:21 You can still use := though. It's pretty unusual t
292
293 buf = make([]byte, 12+pad(len(authName))+pad(len(authData)));
294 buf[0] = 'l';
295 buf[1] = 0;
296 put16(buf, 2, 11);
297 put16(buf, 4, 0);
298 put16(buf, 6, uint16(len(authName)));
299 put16(buf, 8, uint16(len(authData)));
300 put16(buf, 10, 0);
301 copy(buf[12:], authName);
302 copy(buf[12+pad(len(authName)):], authData);
303 _, error = c.sock.Write(buf);
304 if error != nil {
305 return nil, error
306 }
307
308 head := make([]byte, 8);
309 _, error = io.ReadFull(c.sock, head[0:8]);
310 if error != nil {
311 return nil, error
312 }
313 code := head[0];
314 reasonLen := head[1];
315 major := get16(head, 2);
316 minor := get16(head, 4);
317 dataLen := get16(head, 6);
318
319 if major != 11 || minor != 0 {
320 return nil, os.NewError(fmt.Sprintf("x protocol version mismatch : %d.%d", major, minor))
321 }
322
323 buf = make([]byte, int(dataLen)*4+8, int(dataLen)*4+8);
324 copy(buf, head);
325 _, error = io.ReadFull(c.sock, buf[8:]);
326 if error != nil {
327 return nil, error
328 }
329
330 if code == 0 {
331 reason := buf[8 : 8+reasonLen];
332 return nil, os.NewError(fmt.Sprintf("x protocol authentication r efused: %s", string(reason)));
333 }
334
335 parseSetupInfo(buf, &c.Setup);
336
337 if c.defaultScreen >= len(c.Setup.Roots) {
338 c.defaultScreen = 0
339 }
340
341 c.nextId = c.Setup.ResourceIdBase;
342 c.nextCookie = 1;
343 c.replies = make(map[uint16][]byte);
344 c.events = queue{make([][]byte, 100), 0, 0};
345 return c, nil;
346 }
347
348 // Close the connection.
349 func (c *Conn) Close() { c.sock.Close() }
350
351 // Go doesn't have unions so we duplicate the data in all
352 // three formats in ClientMessageData instead.
353 type ClientMessageData struct /*union */ {
nigeltao 2009/11/30 13:02:33 I'd drop the /*union*/, since you've already made
tor 2009/11/30 13:48:46 Done.
354 Data8 [20]byte;
355 Data16 [10]uint16;
356 Data32 [5]uint32;
357 }
358
359 func parseClientMessageData(b []byte, v *ClientMessageData) int {
360 copy(&v.Data8, b);
361 for i := 0; i < 10; i++ {
362 v.Data16[i] = get16(b, i*2)
363 }
364 for i := 0; i < 5; i++ {
365 v.Data32[i] = get32(b, i*4)
366 }
367 return 20;
368 }
OLDNEW

Powered by Google App Engine
RSS Feeds Recent Issues | This issue
This is Rietveld f62528b