]> Cypherpunks repositories - gostls13.git/commitdiff
exp/draw/x11: implement the mapping from keycodes to keysyms.
authorNigel Tao <nigeltao@golang.org>
Tue, 29 Jun 2010 00:54:41 +0000 (10:54 +1000)
committerNigel Tao <nigeltao@golang.org>
Tue, 29 Jun 2010 00:54:41 +0000 (10:54 +1000)
R=rsc
CC=golang-dev, rog
https://golang.org/cl/1739042

src/pkg/exp/draw/x11/conn.go

index 3579a24c38e31f9f094057082070c6794b01f39c..979ce2b7d95d44893f61e2b5c86d5158fe010e5e 100644 (file)
@@ -29,6 +29,11 @@ const (
        windowWidth  = 800
 )
 
+const (
+       keymapLo = 8
+       keymapHi = 255
+)
+
 type conn struct {
        // TODO(nigeltao): Figure out which goroutine should be responsible for closing c,
        // or if there is a race condition if one goroutine calls c.Close whilst another one
@@ -136,6 +141,13 @@ func (c *conn) QuitChan() <-chan bool { return c.quit }
 
 // pumper runs in its own goroutine, reading X events and demuxing them over the kbd / mouse / resize / quit chans.
 func (c *conn) pumper() {
+       var (
+               keymap            [256][]int
+               keysymsPerKeycode int
+       )
+       defer close(c.flush)
+       // TODO(nigeltao): Is this the right place for defer c.c.Close()?
+       // TODO(nigeltao): Should we explicitly defer close our kbd/mouse/resize/quit chans?
        for {
                // X events are always 32 bytes long.
                _, err := io.ReadFull(c.r, c.buf[0:32])
@@ -144,15 +156,49 @@ func (c *conn) pumper() {
                        // TODO(nigeltao): should we do c.quit<-true? Should c.quit be a buffered channel?
                        // Or is c.quit only for non-exceptional closing (e.g. when the window manager destroys
                        // our window), and not for e.g. an I/O error?
-                       break
+                       os.Stderr.Write([]byte(err.String()))
+                       return
                }
                switch c.buf[0] {
+               case 0x01: // Reply from a request (e.g. GetKeyboardMapping).
+                       cookie := int(c.buf[3])<<8 | int(c.buf[2])
+                       if cookie != 1 {
+                               // We issued only one request (GetKeyboardMapping) with a cookie of 1,
+                               // so we shouldn't get any other reply from the X server.
+                               os.Stderr.Write([]byte("exp/draw/x11: unexpected cookie\n"))
+                               return
+                       }
+                       keysymsPerKeycode = int(c.buf[1])
+                       b := make([]int, 256*keysymsPerKeycode)
+                       for i := range keymap {
+                               keymap[i] = b[i*keysymsPerKeycode : (i+1)*keysymsPerKeycode]
+                       }
+                       for i := keymapLo; i <= keymapHi; i++ {
+                               m := keymap[i]
+                               for j := range m {
+                                       u, err := readU32LE(c.r, c.buf[0:4])
+                                       if err != nil {
+                                               os.Stderr.Write([]byte(err.String()))
+                                               return
+                                       }
+                                       m[j] = int(u)
+                               }
+                       }
                case 0x02, 0x03: // Key press, key release.
-                       // BUG(nigeltao): Keycode to keysym mapping is not implemented.
-
-                       // The keycode is in c.buf[1], but as keymaps aren't implemented yet, we'll use the
-                       // space character as a placeholder.
-                       keysym := int(' ')
+                       // X Keyboard Encoding is documented at http://tronche.com/gui/x/xlib/input/keyboard-encoding.html
+                       // TODO(nigeltao): Do we need to implement the "MODE SWITCH / group modifier" feature
+                       // or is that some no-longer-used X construct?
+                       if keysymsPerKeycode < 2 {
+                               // Either we haven't yet received the GetKeyboardMapping reply or
+                               // the X server has sent one that's too short.
+                               continue
+                       }
+                       keycode := int(c.buf[1])
+                       shift := int(c.buf[28]) & 0x01
+                       keysym := keymap[keycode][shift]
+                       if keysym == 0 {
+                               keysym = keymap[keycode][0]
+                       }
                        // TODO(nigeltao): Should we send KeyboardChan ints for Shift/Ctrl/Alt? Should Shift-A send
                        // the same int down the channel as the sent on just the A key?
                        // TODO(nigeltao): How should IME events (e.g. key presses that should generate CJK text) work? Or
@@ -194,9 +240,6 @@ func (c *conn) pumper() {
                        // What about EnterNotify (0x07) and LeaveNotify (0x08)?
                }
        }
-       close(c.flush)
-       // TODO(nigeltao): Is this the right place for c.c.Close()?
-       // TODO(nigeltao): Should we explicitly close our kbd/mouse/resize/quit chans?
 }
 
 // connect connects to the X server given by the full X11 display name (e.g.
@@ -537,29 +580,33 @@ func NewWindowDisplay(display string) (draw.Context, os.Error) {
        }
 
        // Now that we're connected, show a window, via three X protocol messages.
-       // First, create a graphics context (GC).
-       setU32LE(c.buf[0:4], 0x00060037) // 0x37 is the CreateGC opcode, and the message is 6 x 4 bytes long.
-       setU32LE(c.buf[4:8], uint32(c.gc))
-       setU32LE(c.buf[8:12], uint32(c.root))
-       setU32LE(c.buf[12:16], 0x00010004) // Bit 2 is XCB_GC_FOREGROUND, bit 16 is XCB_GC_GRAPHICS_EXPOSURES.
-       setU32LE(c.buf[16:20], 0x00000000) // The Foreground is black.
-       setU32LE(c.buf[20:24], 0x00000000) // GraphicsExposures' value is unused.
-       // Second, create the window.
-       setU32LE(c.buf[24:28], 0x000a0001) // 0x01 is the CreateWindow opcode, and the message is 10 x 4 bytes long.
-       setU32LE(c.buf[28:32], uint32(c.window))
-       setU32LE(c.buf[32:36], uint32(c.root))
-       setU32LE(c.buf[36:40], 0x00000000) // Initial (x, y) is (0, 0).
-       setU32LE(c.buf[40:44], windowHeight<<16|windowWidth)
-       setU32LE(c.buf[44:48], 0x00010000) // Border width is 0, XCB_WINDOW_CLASS_INPUT_OUTPUT is 1.
-       setU32LE(c.buf[48:52], uint32(c.visual))
-       setU32LE(c.buf[52:56], 0x00000802) // Bit 1 is XCB_CW_BACK_PIXEL, bit 11 is XCB_CW_EVENT_MASK.
-       setU32LE(c.buf[56:60], 0x00000000) // The Back-Pixel is black.
-       setU32LE(c.buf[60:64], 0x0000804f) // Key/button press and release, pointer motion, and expose event masks.
-       // Third, map the window.
-       setU32LE(c.buf[64:68], 0x00020008) // 0x08 is the MapWindow opcode, and the message is 2 x 4 bytes long.
-       setU32LE(c.buf[68:72], uint32(c.window))
+       // First, issue a GetKeyboardMapping request. This is the first request, and
+       // will be associated with a cookie of 1.
+       setU32LE(c.buf[0:4], 0x00020065) // 0x65 is the GetKeyboardMapping opcode, and the message is 2 x 4 bytes long.
+       setU32LE(c.buf[4:8], uint32((keymapHi-keymapLo+1)<<8|keymapLo))
+       // Second, create a graphics context (GC).
+       setU32LE(c.buf[8:12], 0x00060037) // 0x37 is the CreateGC opcode, and the message is 6 x 4 bytes long.
+       setU32LE(c.buf[12:16], uint32(c.gc))
+       setU32LE(c.buf[16:20], uint32(c.root))
+       setU32LE(c.buf[20:24], 0x00010004) // Bit 2 is XCB_GC_FOREGROUND, bit 16 is XCB_GC_GRAPHICS_EXPOSURES.
+       setU32LE(c.buf[24:28], 0x00000000) // The Foreground is black.
+       setU32LE(c.buf[28:32], 0x00000000) // GraphicsExposures' value is unused.
+       // Third, create the window.
+       setU32LE(c.buf[32:36], 0x000a0001) // 0x01 is the CreateWindow opcode, and the message is 10 x 4 bytes long.
+       setU32LE(c.buf[36:40], uint32(c.window))
+       setU32LE(c.buf[40:44], uint32(c.root))
+       setU32LE(c.buf[44:48], 0x00000000) // Initial (x, y) is (0, 0).
+       setU32LE(c.buf[48:52], windowHeight<<16|windowWidth)
+       setU32LE(c.buf[52:56], 0x00010000) // Border width is 0, XCB_WINDOW_CLASS_INPUT_OUTPUT is 1.
+       setU32LE(c.buf[56:60], uint32(c.visual))
+       setU32LE(c.buf[60:64], 0x00000802) // Bit 1 is XCB_CW_BACK_PIXEL, bit 11 is XCB_CW_EVENT_MASK.
+       setU32LE(c.buf[64:68], 0x00000000) // The Back-Pixel is black.
+       setU32LE(c.buf[68:72], 0x0000804f) // Key/button press and release, pointer motion, and expose event masks.
+       // Fourth, map the window.
+       setU32LE(c.buf[72:76], 0x00020008) // 0x08 is the MapWindow opcode, and the message is 2 x 4 bytes long.
+       setU32LE(c.buf[76:80], uint32(c.window))
        // Write the bytes.
-       _, err = c.w.Write(c.buf[0:72])
+       _, err = c.w.Write(c.buf[0:80])
        if err != nil {
                return nil, err
        }