func initKeyboardMapping() { const ( keyLo = 8 keyHi = 255 ) km, err := xp.GetKeyboardMapping(xConn, keyLo, keyHi-keyLo+1).Reply() if err != nil { log.Fatal(err) } if km == nil { log.Fatal("couldn't get keyboard mapping") } n := int(km.KeysymsPerKeycode) if n < 2 { log.Fatalf("too few keysyms per keycode: %d", n) } for i := keyLo; i <= keyHi; i++ { keysyms[i][0] = km.Keysyms[(i-keyLo)*n+0] keysyms[i][1] = km.Keysyms[(i-keyLo)*n+1] } toGrabs := []xp.Keysym{wmKeysym} if doAudioActions { toGrabs = append(toGrabs, xkAudioLowerVolume, xkAudioMute, xkAudioRaiseVolume) } for _, toGrab := range toGrabs { keycode := xp.Keycode(0) for i := keyLo; i <= keyHi; i++ { if keysyms[i][0] == toGrab || keysyms[i][1] == toGrab { keycode = xp.Keycode(i) } } if keycode == 0 { if toGrab != wmKeysym { continue } log.Fatalf("could not find the window manager key %s", keysymString(toGrab)) } if err := xp.GrabKeyChecked(xConn, false, rootXWin, xp.ModMaskAny, keycode, xp.GrabModeAsync, xp.GrabModeAsync).Check(); err != nil { log.Fatal(err) } } // Disable Caps Lock if it is the wmKeysym. if wmKeysym == xkCapsLock { // On Ubuntu 12.04, disabling Caps Lock involved the equivalent of // `xmodmap -e "clear lock"`. On Ubuntu 14.04, XKB has replaced xmodmap, // possibly because this facilitates per-window keyboard layouts, so the // equivalent of `xmodmap -e "clear lock"` doesn't work. As of October // 2014, github.com/BurntSushi/xgb doesn't support XKB, so we exec the // setxkbmap program instead of speaking the X11 protocol directly to // disable Caps Lock. if err := exec.Command("setxkbmap", "-option", "caps:none").Run(); err != nil { log.Fatalf("setxkbmap failed: %v", err) } } }
func findKeycode(keysym xp.Keysym) (keycode xp.Keycode, shift bool) { for i, k := range keysyms { if k[0] == keysym { return xp.Keycode(i), false } if k[1] == keysym { return xp.Keycode(i), true } } return 0, false }
// Given a keysym, find the keycode mapped to it in the current X environment. // keybind.Initialize MUST have been called before using this function. func keycodeGet(xu *xgbutil.XUtil, keysym xproto.Keysym) xproto.Keycode { min, max := minMaxKeycodeGet(xu) keyMap := KeyMapGet(xu) if keyMap == nil { panic("keybind.Initialize must be called before using the keybind " + "package.") } var c byte for kc := int(min); kc <= int(max); kc++ { for c = 0; c < keyMap.KeysymsPerKeycode; c++ { if keysym == KeysymGet(xu, xproto.Keycode(kc), c) { return xproto.Keycode(kc) } } } return 0 }
func ByteToString(k *Keyboard, b1 byte, b2 byte) string { if b1 != 0 { return k.KeycodeToString(xproto.Keycode(b1)) } if b2 != 0 { return fmt.Sprintf("button%d", b2) } return "" }
func (k *Keyboard) Keycodes(keysym xproto.Keysym) []xproto.Keycode { var c byte var keycode xproto.Keycode keycodes := make([]xproto.Keycode, 0) set := make(map[xproto.Keycode]bool, 0) for kc := int(k.Min); kc <= int(k.Max); kc++ { keycode = xproto.Keycode(kc) for c = 0; c < k.KeysymsPerKeycode; c++ { if keysym == k.Keysymget(keycode, c) && !set[keycode] { keycodes = append(keycodes, keycode) set[keycode] = true } } } return keycodes }
// StrToKeycode is a wrapper around keycodeGet meant to make our search // a bit more flexible if needed. (i.e., case-insensitive) func StrToKeycode(xu *xgbutil.XUtil, str string) xproto.Keycode { // Do some fancy case stuff before we give up. sym, ok := keysyms[str] if !ok { sym, ok = keysyms[strings.Title(str)] } if !ok { sym, ok = keysyms[strings.ToLower(str)] } if !ok { sym, ok = keysyms[strings.ToUpper(str)] } // If we don't know what 'str' is, return 0. // There will probably be a bad access. We should do better than that... if !ok { return xproto.Keycode(0) } return keycodeGet(xu, sym) }
// ParseString takes a string of the format '[Mod[-Mod[...]]]-KEY', // i.e., 'Mod4-j', and returns a modifiers/keycode combo. // An error is returned if the string is malformed, or if no valid KEY can // be found. // Valid values of KEY should include almost anything returned by pressing // keys with the 'xev' program. Alternatively, you may reference the keys // of the 'keysyms' map defined in keybind/keysymdef.go. func ParseString(xu *xgbutil.XUtil, s string) (uint16, xproto.Keycode, error) { mods, kc := uint16(0), xproto.Keycode(0) for _, part := range strings.Split(s, "-") { switch strings.ToLower(part) { case "shift": mods |= xproto.ModMaskShift case "lock": mods |= xproto.ModMaskLock case "control": mods |= xproto.ModMaskControl case "mod1": mods |= xproto.ModMask1 case "mod2": mods |= xproto.ModMask2 case "mod3": mods |= xproto.ModMask3 case "mod4": mods |= xproto.ModMask4 case "mod5": mods |= xproto.ModMask5 case "any": mods |= xproto.ModMaskAny default: // a key code! if kc == 0 { // only accept the first keycode we see kc = StrToKeycode(xu, part) } } } if kc == 0 { return 0, 0, fmt.Errorf("Could not find a valid keycode in the "+ "string '%s'. Key binding failed.", s) } return mods, kc, nil }
// Given a keysym, find all keycodes mapped to it in the current X environment. // keybind.Initialize MUST have been called before using this function. func keycodesGet(xu *xgbutil.XUtil, keysym xproto.Keysym) []xproto.Keycode { min, max := minMaxKeycodeGet(xu) keyMap := KeyMapGet(xu) if keyMap == nil { panic("keybind.Initialize must be called before using the keybind " + "package.") } var c byte var keycode xproto.Keycode keycodes := make([]xproto.Keycode, 0) set := make(map[xproto.Keycode]bool, 0) for kc := int(min); kc <= int(max); kc++ { keycode = xproto.Keycode(kc) for c = 0; c < keyMap.KeysymsPerKeycode; c++ { if keysym == KeysymGet(xu, keycode, c) && !set[keycode] { keycodes = append(keycodes, keycode) set[keycode] = true } } } return keycodes }
// updateMaps runs in response to MappingNotify events. // It is responsible for making sure our view of the world's keyboard // and modifier maps is correct. (Pointer mappings should be handled in // a similar callback in the mousebind package.) func updateMaps(xu *xgbutil.XUtil, e xevent.MappingNotifyEvent) { keyMap, modMap := MapsGet(xu) // Hold up... If this is MappingKeyboard, then we may have some keycode // changes. This is GROSS. We basically need to go through each keycode // in the map, look up the keysym using the new map and use that keysym // to look up the keycode in our current map. If the current keycode // does not equal the old keycode, then we log the change in a map of // old keycode -> new keycode. // Once the map is constructed, we look through all of our keybindings // and updated appropriately. *puke* // I am only somewhat confident that this is correct. if e.Request == xproto.MappingKeyboard { changes := make(map[xproto.Keycode]xproto.Keycode, 0) xuKeyMap := &xgbutil.KeyboardMapping{keyMap} min, max := minMaxKeycodeGet(xu) // let's not do too much allocation in our loop, shall we? var newSym, oldSym xproto.Keysym var oldKc xproto.Keycode var column byte // wrap 'int(..)' around bytes min and max to avoid overflow. Hideous. for newKc := int(min); newKc <= int(max); newKc++ { for column = 0; byte(column) < keyMap.KeysymsPerKeycode; column++ { // use new key map newSym = KeysymGetWithMap(xu, xuKeyMap, xproto.Keycode(newKc), column) // uses old key map oldKc = keycodeGet(xu, newSym) oldSym = KeysymGet(xu, xproto.Keycode(newKc), column) // If the old and new keysyms are the same, ignore! // Also ignore if either keysym is VoidSymbol if oldSym == newSym || oldSym == 0 || newSym == 0 { continue } // these should match if there are NO changes if oldKc != xproto.Keycode(newKc) { changes[oldKc] = xproto.Keycode(newKc) } } } // Now use 'changes' to do some regrabbing // Loop through all key bindings and check if we have any affected // key codes. (Note that each key binding may be associated with // multiple callbacks.) // We must ungrab everything first, in case two keys are being swapped. for _, key := range keyKeys(xu) { if _, ok := changes[key.Code]; ok { Ungrab(xu, key.Win, key.Mod, key.Code) } } // Okay, now grab. for _, key := range keyKeys(xu) { if newKc, ok := changes[key.Code]; ok { Grab(xu, key.Win, key.Mod, newKc) updateKeyBindKey(xu, key, newKc) } } } // We don't have to do something with MappingModifier like we do with // MappingKeyboard. This is due to us requiring that key strings use // modifier names built into X. (i.e., the names seen in the output of // `xmodmap`.) This means that the modifier mappings happen on the X server // side, so we don't *typically* have to care what key is actually being // pressed to trigger a modifier. (There are some exceptional cases, and // when that happens, we simply query on-demand which keys are modifiers. // See the RunKey{Press,Release}Callbacks functions in keybind/callback.go // for the deets.) // Finally update our view of the mappings. KeyMapSet(xu, keyMap) ModMapSet(xu, modMap) }
func (w *Window) handleEvents() { var noX int32 = 1<<31 - 1 noX++ var lastX, lastY int32 = noX, 0 var button wde.Button downKeys := map[string]bool{} for { e, err := w.conn.WaitForEvent() if err != nil { fmt.Fprintln(os.Stderr, "[go.wde X error] ", err) continue } switch e := e.(type) { case xproto.ButtonPressEvent: button = button | buttonForDetail(e.Detail) var bpe wde.MouseDownEvent bpe.Which = buttonForDetail(e.Detail) bpe.Where.X = int(e.EventX) bpe.Where.Y = int(e.EventY) lastX = int32(e.EventX) lastY = int32(e.EventY) w.events <- bpe case xproto.ButtonReleaseEvent: button = button & ^buttonForDetail(e.Detail) var bue wde.MouseUpEvent bue.Which = buttonForDetail(e.Detail) bue.Where.X = int(e.EventX) bue.Where.Y = int(e.EventY) lastX = int32(e.EventX) lastY = int32(e.EventY) w.events <- bue case xproto.LeaveNotifyEvent: var wee wde.MouseExitedEvent wee.Where.X = int(e.EventX) wee.Where.Y = int(e.EventY) if lastX != noX { wee.From.X = int(lastX) wee.From.Y = int(lastY) } else { wee.From.X = wee.Where.X wee.From.Y = wee.Where.Y } lastX = int32(e.EventX) lastY = int32(e.EventY) w.events <- wee case xproto.EnterNotifyEvent: var wee wde.MouseEnteredEvent wee.Where.X = int(e.EventX) wee.Where.Y = int(e.EventY) if lastX != noX { wee.From.X = int(lastX) wee.From.Y = int(lastY) } else { wee.From.X = wee.Where.X wee.From.Y = wee.Where.Y } lastX = int32(e.EventX) lastY = int32(e.EventY) w.events <- wee case xproto.MotionNotifyEvent: var mme wde.MouseMovedEvent mme.Where.X = int(e.EventX) mme.Where.Y = int(e.EventY) if lastX != noX { mme.From.X = int(lastX) mme.From.Y = int(lastY) } else { mme.From.X = mme.Where.X mme.From.Y = mme.Where.Y } lastX = int32(e.EventX) lastY = int32(e.EventY) if button == 0 { w.events <- mme } else { var mde wde.MouseDraggedEvent mde.MouseMovedEvent = mme mde.Which = button w.events <- mde } case xproto.KeyPressEvent: var ke wde.KeyEvent code := keybind.LookupString(w.xu, e.State, e.Detail) ke.Key = keyForCode(code) w.events <- wde.KeyDownEvent(ke) downKeys[ke.Key] = true kpe := wde.KeyTypedEvent{ KeyEvent: ke, Glyph: letterForCode(code), Chord: wde.ConstructChord(downKeys), } w.events <- kpe case xproto.KeyReleaseEvent: var ke wde.KeyUpEvent ke.Key = keyForCode(keybind.LookupString(w.xu, e.State, e.Detail)) delete(downKeys, ke.Key) w.events <- ke case xproto.KeymapNotifyEvent: newDownKeys := make(map[string]bool) for i := 0; i < len(e.Keys); i++ { mask := e.Keys[i] for j := 0; j < 8; j++ { if mask&(1<<uint(j)) != 0 { keycode := xproto.Keycode(8*(i+1) + j) key := keyForCode(keybind.LookupString(w.xu, 0, keycode)) newDownKeys[key] = true } } } /* remove keys that are no longer pressed */ for key := range downKeys { if _, ok := newDownKeys[key]; !ok { var ke wde.KeyUpEvent ke.Key = key delete(downKeys, key) w.events <- ke } } /* add keys that are newly pressed */ for key := range newDownKeys { if _, ok := downKeys[key]; !ok { var ke wde.KeyDownEvent ke.Key = key downKeys[key] = true w.events <- ke } } case xproto.ConfigureNotifyEvent: var re wde.ResizeEvent re.Width = int(e.Width) re.Height = int(e.Height) if re.Width != w.width || re.Height != w.height { w.width, w.height = re.Width, re.Height w.bufferLck.Lock() w.buffer.Destroy() w.buffer = xgraphics.New(w.xu, image.Rect(0, 0, re.Width, re.Height)) w.bufferLck.Unlock() w.events <- re } case xproto.ClientMessageEvent: if icccm.IsDeleteProtocol(w.xu, xevent.ClientMessageEvent{&e}) { w.events <- wde.CloseEvent{} } case xproto.DestroyNotifyEvent: case xproto.ReparentNotifyEvent: case xproto.MapNotifyEvent: case xproto.UnmapNotifyEvent: case xproto.PropertyNotifyEvent: default: fmt.Fprintf(os.Stderr, "unhandled event: type %T\n%+v\n", e, e) } } close(w.events) }