diff --git a/50-dotool.conf b/50-dotool.conf deleted file mode 100644 index 97fc42d..0000000 --- a/50-dotool.conf +++ /dev/null @@ -1,7 +0,0 @@ -# This can stop dotool typing guff if you are using a non-us keyboard layout. -Section "InputClass" - Identifier "dotool keyboard" - MatchDriver "libinput" - MatchProduct "dotool keyboard" - Option "XkbLayout" "us" -EndSection diff --git a/80-dotool.rules b/80-dotool.rules index 0b235e9..6f3d49e 100644 --- a/80-dotool.rules +++ b/80-dotool.rules @@ -1,6 +1,2 @@ # This allows users in group input to use dotool without root permissions. KERNEL=="uinput", GROUP="input", MODE="0660", OPTIONS+="static_node=uinput" - -# This can stop dotool typing guff if you are using a non-us keyboard layout, -# but it only seems to affect X and 50-dotool.conf achieves that better. -SUBSYSTEM=="input", ACTION=="add|change", ATTRS{name}=="dotool keyboard", ENV{XKBLAYOUT}="us" diff --git a/README.md b/README.md index 9725d65..6cac656 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,11 @@ # dotool -dotool reads commands from stdin and simulates keyboard and mouse events. +dotool reads commands from stdin and simulates keyboard and pointer events. It works everywhere on Linux, including in X11, Wayland and TTYs. ## Install From Source -With `go` installed, run: +With `go` and `libxkbcommon-dev` installed, run: sudo ./install.sh @@ -35,13 +35,22 @@ and this screams for three seconds: { echo keydown A; sleep 3; echo key H shift+1; } | dotool -Each instance takes about half a second to register the virtual input devices, -but you can keep writing commands to one instance or use the daemon and client, -`dotoold` and `dotoolc`: +There is an initial delay registering the virtual devices, but you can +keep writing commands to the same instance or use the daemon and client, +`dotoold` and `dotoolc`. dotoold & - echo 'type super' | dotoolc - echo 'type speedy' | dotoolc + echo type super | dotoolc + echo type speedy | dotoolc + +## Keyboard Layouts + +dotool will produce gobbledygook if your environment has assigned it a +different keyboard layout than it's simulating keycodes for. You can +match them up with the environment variables `DOTOOL_XKB_LAYOUT` and +`DOTOOL_XKB_VARIANT`. + + echo type azerty | DOTOOL_XKB_LAYOUT=fr dotool ## Numen and Contact diff --git a/_install.sh b/_install.sh deleted file mode 100755 index 773d17a..0000000 --- a/_install.sh +++ /dev/null @@ -1,8 +0,0 @@ -#!/bin/sh -# ./_install.sh [DESTDIR] [BINDIR] -mkdir -p "$1/${2:-usr/local/bin}" || exit -cp -v dotoolc dotoold "$1/${2:-usr/local/bin}" || exit -mkdir -p "$1/usr/share/X11/xorg.conf.d" || exit -cp -v 50-dotool.conf "$1/usr/share/X11/xorg.conf.d" || exit -mkdir -p "$1/etc/sway/config.d" || exit -cp -v dotool.sway "$1/etc/sway/config.d/dotool" || exit diff --git a/dotool.go b/dotool.go index 2c337c4..ff28ac2 100644 --- a/dotool.go +++ b/dotool.go @@ -4,6 +4,7 @@ import ( "bufio" "errors" "fmt" + "git.sr.ht/~geb/dotool/xkb" "git.sr.ht/~geb/opt" "github.com/bendahl/uinput" "math" @@ -17,7 +18,7 @@ import ( var Version string func usage() { - fmt.Fprintln(os.Stderr, `dotool reads commands from stdin and simulates keyboard and pointer events. + fmt.Println(`dotool reads commands from stdin and simulates keyboard and pointer events. The commands are: key CHORD... @@ -35,26 +36,51 @@ The commands are: typedelay MILLISECONDS (default: 2) typehold MILLISECONDS (default: 8) -Example: echo "key h i shift+1" | dotool dotool is installed with a udev rule to allow users in group input to run -it without root permissions. You can make it effective without rebooting by -running: sudo udevadm trigger +it without root permissions. -The keys are those used by Linux, but can also be specified using X11 names -prefixed with x: like x:exclam, as well as their Linux keycode like k:30. -They are case insensitive, except uppercase character keys also simulate shift. +You can add yourself to group input by running: + + sudo groupadd -f input + sudo usermod -a -G input $USER + +It's foolproof to reboot to make the rule effective. + + +Keys can be specified by Linux names, XKB names prefixed with x:, or Linux +keycodes prefixed with k:. The Linux names are case-insensitive, except +uppercase character keys also simulate shift. The modifiers are: super, ctrl, alt and shift. -The daemon and client, dotoold and dotoolc, can used to keep a persistent -virtual device for a quicker initial response. + echo key shift+1 x:exclam shift+k:2 | dotool + + +There is an initial delay registering the virtual devices, but you can keep +writing commands to the same instance or use the daemon and client, dotoold +and dotoolc. + + { echo keydown A; sleep 3; echo key H shift+1; } | dotool + + dotoold & + echo type super | dotoolc + echo type speedy | dotoolc + + +dotool will produce gobbledygook if your environment has assigned it a +different keyboard layout than it's simulating keycodes for. You can +match them up with the environment variables DOTOOL_XKB_LAYOUT and +DOTOOL_XKB_VARIANT. + + echo type azerty | DOTOOL_XKB_LAYOUT=fr dotool + --list-keys - Print the supported Linux keys and their keycodes. + Print the possible Linux keys and exit. --list-x-keys - Print the supported X11 keys and their Linux keycodes. + Print the possible XKB keys and exit. --version Print the version and exit. @@ -79,16 +105,38 @@ func log(err error) { } type Chord struct { - Super bool - Ctrl bool - Alt bool - Shift bool + Super, AltGr, Ctrl, Alt, Shift bool Key int } -func parseChord(chord string) (Chord, error) { +func parseChord(keymap *xkb.Keymap, chord string) (Chord, error) { var c Chord keys := strings.Split(chord, "+") + + k := keys[len(keys)-1] + if strings.HasPrefix(k, "k:") { + code, err := strconv.Atoi(k[2:]) + if err != nil { + return c, errors.New("invalid keycode: " + k[2:]) + } + c.Key = code + } else if strings.HasPrefix(k, "x:") { + var ok bool + c, ok = XKeys[k[2:]] + if !ok { + return c, errors.New("impossible XKB key for layout: " + k[2:]) + } + } else { + var ok bool + c, ok = LinuxKeys[strings.ToLower(k)] + if !ok { + return c, errors.New("impossible key for layout: " + k) + } + if len(k) == 1 && unicode.IsUpper(rune(k[0])) { + c.Shift = true + } + } + for i := 0; i < len(keys) - 1; i++ { switch strings.ToLower(keys[i]) { case "super": @@ -104,34 +152,6 @@ func parseChord(chord string) (Chord, error) { } } - k := keys[len(keys)-1] - if strings.HasPrefix(k, "k:") { - code, err := strconv.Atoi(k[2:]) - if err != nil { - return c, errors.New("invalid keycode: " + k[2:]) - } - c.Key = code - } else if strings.HasPrefix(k, "x:") { - if code, ok := xKeysShifted[strings.ToLower(k[2:])]; ok { - if len(k[2:]) > 1 || unicode.IsUpper(rune(k[2])) { - c.Shift = true - } - c.Key = code - } else if code, ok := xKeysNormal[strings.ToLower(k[2:])]; ok { - c.Key = code - } else { - return c, errors.New("unknown X11 key: " + k[2:]) - } - } else { - code, ok := linuxKeys[strings.ToLower(k)] - if len(k) == 1 && unicode.IsUpper(rune(k[0])) { - c.Shift = true - } - if !ok { - return c, errors.New("unknown key: " + k) - } - c.Key = code - } return c, nil } @@ -139,6 +159,9 @@ func (c *Chord) KeyDown(kb uinput.Keyboard) { if c.Super { log(kb.KeyDown(uinput.KeyLeftmeta)) } + if c.AltGr { + log(kb.KeyDown(84)) + } if c.Ctrl { log(kb.KeyDown(uinput.KeyLeftctrl)) } @@ -155,6 +178,9 @@ func (c *Chord) KeyUp(kb uinput.Keyboard) { if c.Super { log(kb.KeyUp(uinput.KeyLeftmeta)) } + if c.AltGr { + log(kb.KeyUp(84)) + } if c.Ctrl { log(kb.KeyUp(uinput.KeyLeftctrl)) } @@ -168,6 +194,21 @@ func (c *Chord) KeyUp(kb uinput.Keyboard) { } +func listKeys(keymap *xkb.Keymap, keys map[string]Chord) { + for code := 1; code < 256; code++ { + for name, chord := range keys { + if chord.Key == code && (chord == Chord{Key: code}) { + fmt.Println(name, code) + } + } + for name, chord := range keys { + if chord.Key == code && (chord != Chord{Key: code}) { + fmt.Println(name, code) + } + } + } +} + func cutCmd(s, cmd string) (string, bool) { if strings.HasPrefix(s, cmd + " ") || strings.HasPrefix(s, cmd + "\t") { return s[len(cmd)+1:], true @@ -176,6 +217,29 @@ func cutCmd(s, cmd string) (string, bool) { } func main() { + var keymap *xkb.Keymap + { + names := xkb.RuleNames{ + Rules: "", + Model: "", + Layout: os.Getenv("DOTOOL_XKB_LAYOUT"), + Variant: os.Getenv("DOTOOL_XKB_VARIANT"), + Options: "", + } + + ctx := xkb.ContextNew(xkb.ContextNoFlags) + defer ctx.Unref() + + keymap = ctx.KeymapNewFromNames(&names, xkb.KeymapCompileNoFlags) + defer keymap.Unref() + + if keymap == nil { + fatal("failed to compile keymap") + } + + initKeys(keymap, names != xkb.RuleNames{}) + } + { optset := opt.NewOptionSet() @@ -187,37 +251,13 @@ func main() { optset.Alias("h", "help") optset.BoolFunc("list-keys", func(bool) error { - for i := 1; i < 249; i++ { - for name, code := range linuxKeys { - if code == i { - fmt.Printf("%s %d\n", name, code) - break - } - } - } + listKeys(keymap, LinuxKeys) os.Exit(0) panic("unreachable") }) optset.BoolFunc("list-x-keys", func(bool) error { - for i := 1; i < 249; i++ { - for name, code := range xKeysNormal { - if code == i { - fmt.Print(name) - for name, code := range xKeysShifted { - if code == i { - if len(name) == 1 { - name = strings.ToUpper(name) - } - fmt.Printf(" %s", name) - break - } - } - fmt.Printf(" %d\n", code) - break - } - } - } + listKeys(keymap, XKeys) os.Exit(0) panic("unreachable") }) @@ -269,7 +309,7 @@ func main() { } if s, ok := cutCmd(text, "key"); ok { for _, field := range strings.Fields(s) { - if chord, err := parseChord(field); err == nil { + if chord, err := parseChord(keymap, field); err == nil { chord.KeyDown(keyboard) time.Sleep(keyhold) chord.KeyUp(keyboard) @@ -280,7 +320,7 @@ func main() { } } else if s, ok := cutCmd(text, "keydown"); ok { for _, field := range strings.Fields(s) { - if chord, err := parseChord(field); err == nil { + if chord, err := parseChord(keymap, field); err == nil { chord.KeyDown(keyboard) } else { warn(err.Error()) @@ -289,7 +329,7 @@ func main() { } } else if s, ok := cutCmd(text, "keyup"); ok { for _, field := range strings.Fields(s) { - if chord, err := parseChord(field); err == nil { + if chord, err := parseChord(keymap, field); err == nil { chord.KeyUp(keyboard) } else { warn(err.Error()) @@ -314,13 +354,18 @@ func main() { } } else if s, ok := cutCmd(text, "type"); ok { for _, r := range s { - if chord, ok := runeChords[unicode.ToLower(r)]; ok { - if unicode.IsUpper(r) { - chord.Shift = true + if sym := xkb.Utf32ToKeysym(uint32(r)); sym == 0 { + warn("invalid character: " + string(r)) + } else { + chord := getChord(keymap, sym) + if chord.Key == 0 { + warn("impossible character for layout: " + string(r)) + time.Sleep(typehold) + } else { + chord.KeyDown(keyboard) + time.Sleep(typehold) + chord.KeyUp(keyboard) } - chord.KeyDown(keyboard) - time.Sleep(typehold) - chord.KeyUp(keyboard) } time.Sleep(typedelay) } diff --git a/dotool.sway b/dotool.sway deleted file mode 100644 index 6e7e6ba..0000000 --- a/dotool.sway +++ /dev/null @@ -1,2 +0,0 @@ -# This can stop dotool typing guff if you are using a non-us keyboard layout. -input "18193:2069:dotool_keyboard" xkb_layout "us" diff --git a/install.sh b/install.sh index 93c531d..28582ab 100755 --- a/install.sh +++ b/install.sh @@ -1,12 +1,16 @@ #!/bin/sh # ./install.sh [DESTDIR] [BINDIR] : "${DOTOOL_VERSION=$(git describe --long --abbrev=12 --tags --dirty 2>/dev/null || echo 1.2)}" + go build -ldflags "-X main.Version=$DOTOOL_VERSION" || exit mkdir -p "$1/${2:-usr/local/bin}" || exit -cp -v dotool "$1/${2:-usr/local/bin}" || exit +cp -v dotool dotoolc dotoold "$1/${2:-usr/local/bin}" || exit mkdir -p "$1/etc/udev/rules.d" || exit cp -v 80-dotool.rules "$1/etc/udev/rules.d" || exit -./_install.sh "$1" "$2" || exit + +# Remove files from before the keyboard layout approach changed +rm -f "$1/usr/share/X11/xorg.conf.d/50-dotool.conf" +rm -f "$1/etc/sway/config.d/dotool" # Make the new/updated udev rule effective udevadm control --reload diff --git a/keys.go b/keys.go index 931baa0..7187627 100644 --- a/keys.go +++ b/keys.go @@ -1,579 +1,531 @@ package main -import "github.com/bendahl/uinput" +import ( + "git.sr.ht/~geb/dotool/xkb" + "github.com/bendahl/uinput" +) -var runeChords = map[rune]Chord{ - 'a': Chord{Key: uinput.KeyA}, - 'b': Chord{Key: uinput.KeyB}, - 'c': Chord{Key: uinput.KeyC}, - 'd': Chord{Key: uinput.KeyD}, - 'e': Chord{Key: uinput.KeyE}, - 'f': Chord{Key: uinput.KeyF}, - 'g': Chord{Key: uinput.KeyG}, - 'h': Chord{Key: uinput.KeyH}, - 'i': Chord{Key: uinput.KeyI}, - 'j': Chord{Key: uinput.KeyJ}, - 'k': Chord{Key: uinput.KeyK}, - 'l': Chord{Key: uinput.KeyL}, - 'm': Chord{Key: uinput.KeyM}, - 'n': Chord{Key: uinput.KeyN}, - 'o': Chord{Key: uinput.KeyO}, - 'p': Chord{Key: uinput.KeyP}, - 'q': Chord{Key: uinput.KeyQ}, - 'r': Chord{Key: uinput.KeyR}, - 's': Chord{Key: uinput.KeyS}, - 't': Chord{Key: uinput.KeyT}, - 'u': Chord{Key: uinput.KeyU}, - 'v': Chord{Key: uinput.KeyV}, - 'w': Chord{Key: uinput.KeyW}, - 'x': Chord{Key: uinput.KeyX}, - 'y': Chord{Key: uinput.KeyY}, - 'z': Chord{Key: uinput.KeyZ}, - '1': Chord{Key: uinput.Key1}, - '!': Chord{Key: uinput.Key1, Shift: true}, - '2': Chord{Key: uinput.Key2}, - '@': Chord{Key: uinput.Key2, Shift: true}, - '3': Chord{Key: uinput.Key3}, - '#': Chord{Key: uinput.Key3, Shift: true}, - '4': Chord{Key: uinput.Key4}, - '$': Chord{Key: uinput.Key4, Shift: true}, - '5': Chord{Key: uinput.Key5}, - '%': Chord{Key: uinput.Key5, Shift: true}, - '6': Chord{Key: uinput.Key6}, - '^': Chord{Key: uinput.Key6, Shift: true}, - '7': Chord{Key: uinput.Key7}, - '&': Chord{Key: uinput.Key7, Shift: true}, - '8': Chord{Key: uinput.Key8}, - '*': Chord{Key: uinput.Key8, Shift: true}, - '9': Chord{Key: uinput.Key9}, - '(': Chord{Key: uinput.Key9, Shift: true}, - '0': Chord{Key: uinput.Key0}, - ')': Chord{Key: uinput.Key0, Shift: true}, - '`': Chord{Key: uinput.KeyGrave}, - '~': Chord{Key: uinput.KeyGrave, Shift: true}, - '-': Chord{Key: uinput.KeyMinus}, - '_': Chord{Key: uinput.KeyMinus, Shift: true}, - '=': Chord{Key: uinput.KeyEqual}, - '+': Chord{Key: uinput.KeyEqual, Shift: true}, - '[': Chord{Key: uinput.KeyLeftbrace}, - '{': Chord{Key: uinput.KeyLeftbrace, Shift: true}, - '}': Chord{Key: uinput.KeyRightbrace}, - ']': Chord{Key: uinput.KeyRightbrace, Shift: true}, - '\\': Chord{Key: uinput.KeyBackslash}, - '|': Chord{Key: uinput.KeyBackslash, Shift: true}, - ';': Chord{Key: uinput.KeySemicolon}, - ':': Chord{Key: uinput.KeySemicolon, Shift: true}, - '\'':Chord{Key: uinput.KeyApostrophe}, - '"': Chord{Key: uinput.KeyApostrophe, Shift: true}, - ',': Chord{Key: uinput.KeyComma}, - '<': Chord{Key: uinput.KeyComma, Shift: true}, - '.': Chord{Key: uinput.KeyDot}, - '>': Chord{Key: uinput.KeyDot, Shift: true}, - '/': Chord{Key: uinput.KeySlash}, - '?': Chord{Key: uinput.KeySlash, Shift: true}, - ' ': Chord{Key: uinput.KeySpace}, - '\t': Chord{Key: uinput.KeyTab}, +var LinuxKeys = map[string]Chord{ + // Linux Only + "micmute": Chord{Key: uinput.KeyMicmute}, + "unknown": Chord{Key: uinput.KeyUnknown}, + "alterase": Chord{Key: uinput.KeyAlterase}, + "question": Chord{Key: uinput.KeyQuestion}, + "hp": Chord{Key: uinput.KeyHp}, + "bassboost": Chord{Key: uinput.KeyBassboost}, + "play": Chord{Key: uinput.KeyPlay}, + "close": Chord{Key: uinput.KeyClose}, + "edit": Chord{Key: uinput.KeyEdit}, + "move": Chord{Key: uinput.KeyMove}, + "iso": Chord{Key: uinput.KeyIso}, + "closecd": Chord{Key: uinput.KeyClosecd}, + "deletefile": Chord{Key: uinput.KeyDeletefile}, + "setup": Chord{Key: uinput.KeySetup}, + "yen": Chord{Key: uinput.KeyYen}, + "macro": Chord{Key: uinput.KeyMacro}, + "kpjpcomma": Chord{Key: uinput.KeyKpjpcomma}, + "ro": Chord{Key: uinput.KeyRo}, + "zenkakuhankaku": Chord{Key: uinput.KeyZenkakuhankaku}, + + "esc": Chord{Key: uinput.KeyEsc}, + "1": Chord{Key: uinput.Key1}, + "2": Chord{Key: uinput.Key2}, + "3": Chord{Key: uinput.Key3}, + "4": Chord{Key: uinput.Key4}, + "5": Chord{Key: uinput.Key5}, + "6": Chord{Key: uinput.Key6}, + "7": Chord{Key: uinput.Key7}, + "8": Chord{Key: uinput.Key8}, + "9": Chord{Key: uinput.Key9}, + "0": Chord{Key: uinput.Key0}, + "minus": Chord{Key: uinput.KeyMinus}, + "equal": Chord{Key: uinput.KeyEqual}, + "backspace": Chord{Key: uinput.KeyBackspace}, + "tab": Chord{Key: uinput.KeyTab}, + "q": Chord{Key: uinput.KeyQ}, + "w": Chord{Key: uinput.KeyW}, + "e": Chord{Key: uinput.KeyE}, + "r": Chord{Key: uinput.KeyR}, + "t": Chord{Key: uinput.KeyT}, + "y": Chord{Key: uinput.KeyY}, + "u": Chord{Key: uinput.KeyU}, + "i": Chord{Key: uinput.KeyI}, + "o": Chord{Key: uinput.KeyO}, + "p": Chord{Key: uinput.KeyP}, + "leftbrace": Chord{Key: uinput.KeyLeftbrace}, + "rightbrace": Chord{Key: uinput.KeyRightbrace}, + "enter": Chord{Key: uinput.KeyEnter}, + "leftctrl": Chord{Key: uinput.KeyLeftctrl}, + "a": Chord{Key: uinput.KeyA}, + "s": Chord{Key: uinput.KeyS}, + "d": Chord{Key: uinput.KeyD}, + "f": Chord{Key: uinput.KeyF}, + "g": Chord{Key: uinput.KeyG}, + "h": Chord{Key: uinput.KeyH}, + "j": Chord{Key: uinput.KeyJ}, + "k": Chord{Key: uinput.KeyK}, + "l": Chord{Key: uinput.KeyL}, + "semicolon": Chord{Key: uinput.KeySemicolon}, + "apostrophe": Chord{Key: uinput.KeyApostrophe}, + "grave": Chord{Key: uinput.KeyGrave}, + "leftshift": Chord{Key: uinput.KeyLeftshift}, + "backslash": Chord{Key: uinput.KeyBackslash}, + "z": Chord{Key: uinput.KeyZ}, + "x": Chord{Key: uinput.KeyX}, + "c": Chord{Key: uinput.KeyC}, + "v": Chord{Key: uinput.KeyV}, + "b": Chord{Key: uinput.KeyB}, + "n": Chord{Key: uinput.KeyN}, + "m": Chord{Key: uinput.KeyM}, + "comma": Chord{Key: uinput.KeyComma}, + "dot": Chord{Key: uinput.KeyDot}, + "slash": Chord{Key: uinput.KeySlash}, + "rightshift": Chord{Key: uinput.KeyRightshift}, + "kpasterisk": Chord{Key: uinput.KeyKpasterisk}, + "leftalt": Chord{Key: uinput.KeyLeftalt}, + "space": Chord{Key: uinput.KeySpace}, + "capslock": Chord{Key: uinput.KeyCapslock}, + "f1": Chord{Key: uinput.KeyF1}, + "f2": Chord{Key: uinput.KeyF2}, + "f3": Chord{Key: uinput.KeyF3}, + "f4": Chord{Key: uinput.KeyF4}, + "f5": Chord{Key: uinput.KeyF5}, + "f6": Chord{Key: uinput.KeyF6}, + "f7": Chord{Key: uinput.KeyF7}, + "f8": Chord{Key: uinput.KeyF8}, + "f9": Chord{Key: uinput.KeyF9}, + "f10": Chord{Key: uinput.KeyF10}, + "numlock": Chord{Key: uinput.KeyNumlock}, + "scrolllock": Chord{Key: uinput.KeyScrolllock}, + "kp7": Chord{Key: uinput.KeyKp7}, + "kp8": Chord{Key: uinput.KeyKp8}, + "kp9": Chord{Key: uinput.KeyKp9}, + "kpminus": Chord{Key: uinput.KeyKpminus}, + "kp4": Chord{Key: uinput.KeyKp4}, + "kp5": Chord{Key: uinput.KeyKp5}, + "kp6": Chord{Key: uinput.KeyKp6}, + "kpplus": Chord{Key: uinput.KeyKpplus}, + "kp1": Chord{Key: uinput.KeyKp1}, + "kp2": Chord{Key: uinput.KeyKp2}, + "kp3": Chord{Key: uinput.KeyKp3}, + "kp0": Chord{Key: uinput.KeyKp0}, + "kpdot": Chord{Key: uinput.KeyKpdot}, + "102nd": Chord{Key: uinput.Key102Nd}, + "f11": Chord{Key: uinput.KeyF11}, + "f12": Chord{Key: uinput.KeyF12}, + "katakana": Chord{Key: uinput.KeyKatakana}, + "hiragana": Chord{Key: uinput.KeyHiragana}, + "henkan": Chord{Key: uinput.KeyHenkan}, + "katakanahiragana": Chord{Key: uinput.KeyKatakanahiragana}, + "muhenkan": Chord{Key: uinput.KeyMuhenkan}, + "kpenter": Chord{Key: uinput.KeyKpenter}, + "rightctrl": Chord{Key: uinput.KeyRightctrl}, + "kpslash": Chord{Key: uinput.KeyKpslash}, + "sysrq": Chord{Key: uinput.KeySysrq}, + "rightalt": Chord{Key: uinput.KeyRightalt}, + "linefeed": Chord{Key: uinput.KeyLinefeed}, + "home": Chord{Key: uinput.KeyHome}, + "up": Chord{Key: uinput.KeyUp}, + "pageup": Chord{Key: uinput.KeyPageup}, + "left": Chord{Key: uinput.KeyLeft}, + "right": Chord{Key: uinput.KeyRight}, + "end": Chord{Key: uinput.KeyEnd}, + "down": Chord{Key: uinput.KeyDown}, + "pagedown": Chord{Key: uinput.KeyPagedown}, + "insert": Chord{Key: uinput.KeyInsert}, + "delete": Chord{Key: uinput.KeyDelete}, + "mute": Chord{Key: uinput.KeyMute}, + "volumedown": Chord{Key: uinput.KeyVolumedown}, + "volumeup": Chord{Key: uinput.KeyVolumeup}, + "power": Chord{Key: uinput.KeyPower}, + "kpequal": Chord{Key: uinput.KeyKpequal}, + "kpplusminus": Chord{Key: uinput.KeyKpplusminus}, + "pause": Chord{Key: uinput.KeyPause}, + "scale": Chord{Key: uinput.KeyScale}, + "kpcomma": Chord{Key: uinput.KeyKpcomma}, + "hangeul": Chord{Key: uinput.KeyHangeul}, + "hanja": Chord{Key: uinput.KeyHanja}, + "leftmeta": Chord{Key: uinput.KeyLeftmeta}, + "rightmeta": Chord{Key: uinput.KeyRightmeta}, + "compose": Chord{Key: uinput.KeyCompose}, + "stop": Chord{Key: uinput.KeyStop}, + "again": Chord{Key: uinput.KeyAgain}, + "props": Chord{Key: uinput.KeyProps}, + "undo": Chord{Key: uinput.KeyUndo}, + "front": Chord{Key: uinput.KeyFront}, + "copy": Chord{Key: uinput.KeyCopy}, + "open": Chord{Key: uinput.KeyOpen}, + "paste": Chord{Key: uinput.KeyPaste}, + "find": Chord{Key: uinput.KeyFind}, + "cut": Chord{Key: uinput.KeyCut}, + "help": Chord{Key: uinput.KeyHelp}, + "menu": Chord{Key: uinput.KeyMenu}, + "calc": Chord{Key: uinput.KeyCalc}, + "sleep": Chord{Key: uinput.KeySleep}, + "wakeup": Chord{Key: uinput.KeyWakeup}, + "file": Chord{Key: uinput.KeyFile}, + "sendfile": Chord{Key: uinput.KeySendfile}, + "xfer": Chord{Key: uinput.KeyXfer}, + "prog1": Chord{Key: uinput.KeyProg1}, + "prog2": Chord{Key: uinput.KeyProg2}, + "www": Chord{Key: uinput.KeyWww}, + "msdos": Chord{Key: uinput.KeyMsdos}, + "coffee": Chord{Key: uinput.KeyCoffee}, + "direction": Chord{Key: uinput.KeyDirection}, + "cyclewindows": Chord{Key: uinput.KeyCyclewindows}, + "mail": Chord{Key: uinput.KeyMail}, + "bookmarks": Chord{Key: uinput.KeyBookmarks}, + "computer": Chord{Key: uinput.KeyComputer}, + "back": Chord{Key: uinput.KeyBack}, + "forward": Chord{Key: uinput.KeyForward}, + "ejectcd": Chord{Key: uinput.KeyEjectcd}, + "ejectclosecd": Chord{Key: uinput.KeyEjectclosecd}, + "nextsong": Chord{Key: uinput.KeyNextsong}, + "playpause": Chord{Key: uinput.KeyPlaypause}, + "previoussong": Chord{Key: uinput.KeyPrevioussong}, + "stopcd": Chord{Key: uinput.KeyStopcd}, + "record": Chord{Key: uinput.KeyRecord}, + "rewind": Chord{Key: uinput.KeyRewind}, + "phone": Chord{Key: uinput.KeyPhone}, + "config": Chord{Key: uinput.KeyConfig}, + "homepage": Chord{Key: uinput.KeyHomepage}, + "refresh": Chord{Key: uinput.KeyRefresh}, + "exit": Chord{Key: uinput.KeyExit}, + "scrollup": Chord{Key: uinput.KeyScrollup}, + "scrolldown": Chord{Key: uinput.KeyScrolldown}, + "kpleftparen": Chord{Key: uinput.KeyKpleftparen}, + "kprightparen": Chord{Key: uinput.KeyKprightparen}, + "new": Chord{Key: uinput.KeyNew}, + "redo": Chord{Key: uinput.KeyRedo}, + "f13": Chord{Key: uinput.KeyF13}, + "f14": Chord{Key: uinput.KeyF14}, + "f15": Chord{Key: uinput.KeyF15}, + "f16": Chord{Key: uinput.KeyF16}, + "f17": Chord{Key: uinput.KeyF17}, + "f18": Chord{Key: uinput.KeyF18}, + "f19": Chord{Key: uinput.KeyF19}, + "f20": Chord{Key: uinput.KeyF20}, + "f21": Chord{Key: uinput.KeyF21}, + "f22": Chord{Key: uinput.KeyF22}, + "f23": Chord{Key: uinput.KeyF23}, + "f24": Chord{Key: uinput.KeyF24}, + "playcd": Chord{Key: uinput.KeyPlaycd}, + "pausecd": Chord{Key: uinput.KeyPausecd}, + "prog3": Chord{Key: uinput.KeyProg3}, + "prog4": Chord{Key: uinput.KeyProg4}, + "dashboard": Chord{Key: uinput.KeyDashboard}, + "suspend": Chord{Key: uinput.KeySuspend}, + "fastforward": Chord{Key: uinput.KeyFastforward}, + "print": Chord{Key: uinput.KeyPrint}, + "camera": Chord{Key: uinput.KeyCamera}, + "sound": Chord{Key: uinput.KeySound}, + "email": Chord{Key: uinput.KeyEmail}, + "chat": Chord{Key: uinput.KeyChat}, + "search": Chord{Key: uinput.KeySearch}, + "connect": Chord{Key: uinput.KeyConnect}, + "finance": Chord{Key: uinput.KeyFinance}, + "sport": Chord{Key: uinput.KeySport}, + "shop": Chord{Key: uinput.KeyShop}, + "cancel": Chord{Key: uinput.KeyCancel}, + "brightnessdown": Chord{Key: uinput.KeyBrightnessdown}, + "brightnessup": Chord{Key: uinput.KeyBrightnessup}, + "media": Chord{Key: uinput.KeyMedia}, + "switchvideomode": Chord{Key: uinput.KeySwitchvideomode}, + "kbdillumtoggle": Chord{Key: uinput.KeyKbdillumtoggle}, + "kbdillumdown": Chord{Key: uinput.KeyKbdillumdown}, + "kbdillumup": Chord{Key: uinput.KeyKbdillumup}, + "send": Chord{Key: uinput.KeySend}, + "reply": Chord{Key: uinput.KeyReply}, + "forwardmail": Chord{Key: uinput.KeyForwardmail}, + "save": Chord{Key: uinput.KeySave}, + "documents": Chord{Key: uinput.KeyDocuments}, + "battery": Chord{Key: uinput.KeyBattery}, + "bluetooth": Chord{Key: uinput.KeyBluetooth}, + "wlan": Chord{Key: uinput.KeyWlan}, + "uwb": Chord{Key: uinput.KeyUwb}, + "videonext": Chord{Key: uinput.KeyVideoNext}, + "videoprev": Chord{Key: uinput.KeyVideoPrev}, + "brightnesscycle": Chord{Key: uinput.KeyBrightnessCycle}, + "brightnesszero": Chord{Key: uinput.KeyBrightnessZero}, + "displayoff": Chord{Key: uinput.KeyDisplayOff}, + "wimax": Chord{Key: uinput.KeyWimax}, + "rfkill": Chord{Key: uinput.KeyRfkill}, } -// generated with: -// go doc uinput.keyesc | sed '/Key/ !d; s/Key\(\S*\).*/"\L\1\E": uinput.Key\1,/' -var linuxKeys = map[string]int{ - "esc": uinput.KeyEsc, - "1": uinput.Key1, - "2": uinput.Key2, - "3": uinput.Key3, - "4": uinput.Key4, - "5": uinput.Key5, - "6": uinput.Key6, - "7": uinput.Key7, - "8": uinput.Key8, - "9": uinput.Key9, - "0": uinput.Key0, - "minus": uinput.KeyMinus, - "equal": uinput.KeyEqual, - "backspace": uinput.KeyBackspace, - "tab": uinput.KeyTab, - "q": uinput.KeyQ, - "w": uinput.KeyW, - "e": uinput.KeyE, - "r": uinput.KeyR, - "t": uinput.KeyT, - "y": uinput.KeyY, - "u": uinput.KeyU, - "i": uinput.KeyI, - "o": uinput.KeyO, - "p": uinput.KeyP, - "leftbrace": uinput.KeyLeftbrace, - "rightbrace": uinput.KeyRightbrace, - "enter": uinput.KeyEnter, - "leftctrl": uinput.KeyLeftctrl, - "a": uinput.KeyA, - "s": uinput.KeyS, - "d": uinput.KeyD, - "f": uinput.KeyF, - "g": uinput.KeyG, - "h": uinput.KeyH, - "j": uinput.KeyJ, - "k": uinput.KeyK, - "l": uinput.KeyL, - "semicolon": uinput.KeySemicolon, - "apostrophe": uinput.KeyApostrophe, - "grave": uinput.KeyGrave, - "leftshift": uinput.KeyLeftshift, - "backslash": uinput.KeyBackslash, - "z": uinput.KeyZ, - "x": uinput.KeyX, - "c": uinput.KeyC, - "v": uinput.KeyV, - "b": uinput.KeyB, - "n": uinput.KeyN, - "m": uinput.KeyM, - "comma": uinput.KeyComma, - "dot": uinput.KeyDot, - "slash": uinput.KeySlash, - "rightshift": uinput.KeyRightshift, - "kpasterisk": uinput.KeyKpasterisk, - "leftalt": uinput.KeyLeftalt, - "space": uinput.KeySpace, - "capslock": uinput.KeyCapslock, - "f1": uinput.KeyF1, - "f2": uinput.KeyF2, - "f3": uinput.KeyF3, - "f4": uinput.KeyF4, - "f5": uinput.KeyF5, - "f6": uinput.KeyF6, - "f7": uinput.KeyF7, - "f8": uinput.KeyF8, - "f9": uinput.KeyF9, - "f10": uinput.KeyF10, - "numlock": uinput.KeyNumlock, - "scrolllock": uinput.KeyScrolllock, - "kp7": uinput.KeyKp7, - "kp8": uinput.KeyKp8, - "kp9": uinput.KeyKp9, - "kpminus": uinput.KeyKpminus, - "kp4": uinput.KeyKp4, - "kp5": uinput.KeyKp5, - "kp6": uinput.KeyKp6, - "kpplus": uinput.KeyKpplus, - "kp1": uinput.KeyKp1, - "kp2": uinput.KeyKp2, - "kp3": uinput.KeyKp3, - "kp0": uinput.KeyKp0, - "kpdot": uinput.KeyKpdot, - "zenkakuhankaku": uinput.KeyZenkakuhankaku, - "102nd": uinput.Key102Nd, - "f11": uinput.KeyF11, - "f12": uinput.KeyF12, - "ro": uinput.KeyRo, - "katakana": uinput.KeyKatakana, - "hiragana": uinput.KeyHiragana, - "henkan": uinput.KeyHenkan, - "katakanahiragana": uinput.KeyKatakanahiragana, - "muhenkan": uinput.KeyMuhenkan, - "kpjpcomma": uinput.KeyKpjpcomma, - "kpenter": uinput.KeyKpenter, - "rightctrl": uinput.KeyRightctrl, - "kpslash": uinput.KeyKpslash, - "sysrq": uinput.KeySysrq, - "rightalt": uinput.KeyRightalt, - "linefeed": uinput.KeyLinefeed, - "home": uinput.KeyHome, - "up": uinput.KeyUp, - "pageup": uinput.KeyPageup, - "left": uinput.KeyLeft, - "right": uinput.KeyRight, - "end": uinput.KeyEnd, - "down": uinput.KeyDown, - "pagedown": uinput.KeyPagedown, - "insert": uinput.KeyInsert, - "delete": uinput.KeyDelete, - "macro": uinput.KeyMacro, - "mute": uinput.KeyMute, - "volumedown": uinput.KeyVolumedown, - "volumeup": uinput.KeyVolumeup, - "power": uinput.KeyPower, - "kpequal": uinput.KeyKpequal, - "kpplusminus": uinput.KeyKpplusminus, - "pause": uinput.KeyPause, - "scale": uinput.KeyScale, - "kpcomma": uinput.KeyKpcomma, - "hangeul": uinput.KeyHangeul, - "hanja": uinput.KeyHanja, - "yen": uinput.KeyYen, - "leftmeta": uinput.KeyLeftmeta, - "rightmeta": uinput.KeyRightmeta, - "compose": uinput.KeyCompose, - "stop": uinput.KeyStop, - "again": uinput.KeyAgain, - "props": uinput.KeyProps, - "undo": uinput.KeyUndo, - "front": uinput.KeyFront, - "copy": uinput.KeyCopy, - "open": uinput.KeyOpen, - "paste": uinput.KeyPaste, - "find": uinput.KeyFind, - "cut": uinput.KeyCut, - "help": uinput.KeyHelp, - "menu": uinput.KeyMenu, - "calc": uinput.KeyCalc, - "setup": uinput.KeySetup, - "sleep": uinput.KeySleep, - "wakeup": uinput.KeyWakeup, - "file": uinput.KeyFile, - "sendfile": uinput.KeySendfile, - "deletefile": uinput.KeyDeletefile, - "xfer": uinput.KeyXfer, - "prog1": uinput.KeyProg1, - "prog2": uinput.KeyProg2, - "www": uinput.KeyWww, - "msdos": uinput.KeyMsdos, - "coffee": uinput.KeyCoffee, - "direction": uinput.KeyDirection, - "cyclewindows": uinput.KeyCyclewindows, - "mail": uinput.KeyMail, - "bookmarks": uinput.KeyBookmarks, - "computer": uinput.KeyComputer, - "back": uinput.KeyBack, - "forward": uinput.KeyForward, - "closecd": uinput.KeyClosecd, - "ejectcd": uinput.KeyEjectcd, - "ejectclosecd": uinput.KeyEjectclosecd, - "nextsong": uinput.KeyNextsong, - "playpause": uinput.KeyPlaypause, - "previoussong": uinput.KeyPrevioussong, - "stopcd": uinput.KeyStopcd, - "record": uinput.KeyRecord, - "rewind": uinput.KeyRewind, - "phone": uinput.KeyPhone, - "iso": uinput.KeyIso, - "config": uinput.KeyConfig, - "homepage": uinput.KeyHomepage, - "refresh": uinput.KeyRefresh, - "exit": uinput.KeyExit, - "move": uinput.KeyMove, - "edit": uinput.KeyEdit, - "scrollup": uinput.KeyScrollup, - "scrolldown": uinput.KeyScrolldown, - "kpleftparen": uinput.KeyKpleftparen, - "kprightparen": uinput.KeyKprightparen, - "new": uinput.KeyNew, - "redo": uinput.KeyRedo, - "f13": uinput.KeyF13, - "f14": uinput.KeyF14, - "f15": uinput.KeyF15, - "f16": uinput.KeyF16, - "f17": uinput.KeyF17, - "f18": uinput.KeyF18, - "f19": uinput.KeyF19, - "f20": uinput.KeyF20, - "f21": uinput.KeyF21, - "f22": uinput.KeyF22, - "f23": uinput.KeyF23, - "f24": uinput.KeyF24, - "playcd": uinput.KeyPlaycd, - "pausecd": uinput.KeyPausecd, - "prog3": uinput.KeyProg3, - "prog4": uinput.KeyProg4, - "dashboard": uinput.KeyDashboard, - "suspend": uinput.KeySuspend, - "close": uinput.KeyClose, - "play": uinput.KeyPlay, - "fastforward": uinput.KeyFastforward, - "bassboost": uinput.KeyBassboost, - "print": uinput.KeyPrint, - "hp": uinput.KeyHp, - "camera": uinput.KeyCamera, - "sound": uinput.KeySound, - "question": uinput.KeyQuestion, - "email": uinput.KeyEmail, - "chat": uinput.KeyChat, - "search": uinput.KeySearch, - "connect": uinput.KeyConnect, - "finance": uinput.KeyFinance, - "sport": uinput.KeySport, - "shop": uinput.KeyShop, - "alterase": uinput.KeyAlterase, - "cancel": uinput.KeyCancel, - "brightnessdown": uinput.KeyBrightnessdown, - "brightnessup": uinput.KeyBrightnessup, - "media": uinput.KeyMedia, - "switchvideomode": uinput.KeySwitchvideomode, - "kbdillumtoggle": uinput.KeyKbdillumtoggle, - "kbdillumdown": uinput.KeyKbdillumdown, - "kbdillumup": uinput.KeyKbdillumup, - "send": uinput.KeySend, - "reply": uinput.KeyReply, - "forwardmail": uinput.KeyForwardmail, - "save": uinput.KeySave, - "documents": uinput.KeyDocuments, - "battery": uinput.KeyBattery, - "bluetooth": uinput.KeyBluetooth, - "wlan": uinput.KeyWlan, - "uwb": uinput.KeyUwb, - "unknown": uinput.KeyUnknown, - "videonext": uinput.KeyVideoNext, - "videoprev": uinput.KeyVideoPrev, - "brightnesscycle": uinput.KeyBrightnessCycle, - "brightnesszero": uinput.KeyBrightnessZero, - "displayoff": uinput.KeyDisplayOff, - "wimax": uinput.KeyWimax, - "rfkill": uinput.KeyRfkill, - "micmute": uinput.KeyMicmute, +var linuxXSyms = map[string]uint32{ + "esc": 0xff1b, + "1": 0x31, + "2": 0x32, + "3": 0x33, + "4": 0x34, + "5": 0x35, + "6": 0x36, + "7": 0x37, + "8": 0x38, + "9": 0x39, + "0": 0x30, + "minus": 0x2d, + "equal": 0x3d, + "backspace": 0xff08, + "tab": 0xff09, + "q": 0x71, + "w": 0x77, + "e": 0x65, + "r": 0x72, + "t": 0x74, + "y": 0x79, + "u": 0x75, + "i": 0x69, + "o": 0x6f, + "p": 0x70, + "leftbrace": 0x5b, + "rightbrace": 0x5d, + "enter": 0xff0d, + "leftctrl": 0xffe3, + "a": 0x61, + "s": 0x73, + "d": 0x64, + "f": 0x66, + "g": 0x67, + "h": 0x68, + "j": 0x6a, + "k": 0x6b, + "l": 0x6c, + "semicolon": 0x3b, + "apostrophe": 0x27, + "grave": 0x60, + "leftshift": 0xffe1, + "backslash": 0x5c, + "z": 0x7a, + "x": 0x78, + "c": 0x63, + "v": 0x76, + "b": 0x62, + "n": 0x6e, + "m": 0x6d, + "comma": 0x2c, + "dot": 0x2e, + "slash": 0x2f, + "rightshift": 0xffe2, + "kpasterisk": 0xffaa, + "leftalt": 0xffe9, + "space": 0x20, + "capslock": 0xffe5, + "f1": 0xffbe, + "f2": 0xffbf, + "f3": 0xffc0, + "f4": 0xffc1, + "f5": 0xffc2, + "f6": 0xffc3, + "f7": 0xffc4, + "f8": 0xffc5, + "f9": 0xffc6, + "f10": 0xffc7, + "numlock": 0xff7f, + "scrolllock": 0xff14, + "kp7": 0xff95, + "kp8": 0xff97, + "kp9": 0xff9a, + "kpminus": 0xffad, + "kp4": 0xff96, + "kp5": 0xff9d, + "kp6": 0xff98, + "kpplus": 0xffab, + "kp1": 0xff9c, + "kp2": 0xff99, + "kp3": 0xff9b, + "kp0": 0xff9e, + "kpdot": 0xff9f, + "102nd": 0x3c, + "f11": 0xffc8, + "f12": 0xffc9, + "katakana": 0xff26, + "hiragana": 0xff25, + "henkan": 0xff23, + "katakanahiragana": 0xff27, + "muhenkan": 0xff22, + "kpenter": 0xff8d, + "rightctrl": 0xffe4, + "kpslash": 0xffaf, + "sysrq": 0xff61, + "rightalt": 0xffea, + "linefeed": 0xff0a, + "home": 0xff50, + "up": 0xff52, + "pageup": 0xff55, + "left": 0xff51, + "right": 0xff53, + "end": 0xff57, + "down": 0xff54, + "pagedown": 0xff56, + "insert": 0xff63, + "delete": 0xffff, + "mute": 0x1008ff12, + "volumedown": 0x1008ff11, + "volumeup": 0x1008ff13, + "power": 0x1008ff2a, + "kpequal": 0xffbd, + "kpplusminus": 0xb1, + "pause": 0xff13, + "scale": 0x1008ff4a, + "kpcomma": 0xffae, + "hangeul": 0xff31, + "hanja": 0xff34, + "leftmeta": 0xffeb, + "rightmeta": 0xffec, + "compose": 0xff67, + "stop": 0xff69, + "again": 0xff66, + "props": 0x1005ff70, + "undo": 0xff65, + "front": 0x1005ff71, + "copy": 0x1008ff57, + "open": 0x1008ff6b, + "paste": 0x1008ff6d, + "find": 0xff68, + "cut": 0x1008ff58, + "help": 0xff6a, + "menu": 0x1008ff65, + "calc": 0x1008ff1d, + "sleep": 0x1008ff2f, + "wakeup": 0x1008ff2b, + "file": 0x1008ff5d, + "sendfile": 0x1008ff7b, + "xfer": 0x1008ff8a, + "prog1": 0x1008ff41, + "prog2": 0x1008ff42, + "www": 0x1008ff2e, + "msdos": 0x1008ff5a, + "coffee": 0x1008ff2d, + "direction": 0x1008ff74, + "cyclewindows": 0x1008ff7f, + "mail": 0x1008ff19, + "bookmarks": 0x1008ff30, + "computer": 0x1008ff33, + "back": 0x1008ff26, + "forward": 0x1008ff27, + "ejectcd": 0x1008ff2c, + "ejectclosecd": 0x1008ff2c, + "nextsong": 0x1008ff17, + "playpause": 0x1008ff14, + "previoussong": 0x1008ff16, + "stopcd": 0x1008ff15, + "record": 0x1008ff1c, + "rewind": 0x1008ff3e, + "phone": 0x1008ff6e, + "config": 0x1008ff81, + "homepage": 0x1008ff18, + "refresh": 0x1008ff73, + "exit": 0x1008ff56, + "scrollup": 0x1008ff78, + "scrolldown": 0x1008ff79, + "kpleftparen": 0x28, + "kprightparen": 0x29, + "new": 0x1008ff68, + "redo": 0xff66, + "f13": 0xffca, + "f14": 0xffcb, + "f15": 0xffcc, + "f16": 0xffcd, + "f17": 0xffce, + "f18": 0xffcf, + "f19": 0xffd0, + "f20": 0xffd1, + "f21": 0xffd2, + "f22": 0xffd3, + "f23": 0xffd4, + "f24": 0xffd5, + "playcd": 0x1008ff14, + "pausecd": 0x1008ff31, + "prog3": 0x1008ff43, + "prog4": 0x1008ff44, + "dashboard": 0x1008ff4b, + "suspend": 0x1008ffa7, + "fastforward": 0x1008ff97, + "print": 0xff61, + "camera": 0x1008ff8f, + "sound": 0x1008ffb6, + "email": 0x1008ff19, + "chat": 0x1008ff8e, + "search": 0x1008ff1b, + "connect": 0x1008ff5f, + "finance": 0x1008ff3c, + "sport": 0x1008ff5e, + "shop": 0x1008ff36, + "cancel": 0xff69, + "brightnessdown": 0x1008ff03, + "brightnessup": 0x1008ff02, + "media": 0x1008ff32, + "switchvideomode": 0x1008ff59, + "kbdillumtoggle": 0x1008ff04, + "kbdillumdown": 0x1008ff06, + "kbdillumup": 0x1008ff05, + "send": 0x1008ff7b, + "reply": 0x1008ff72, + "forwardmail": 0x1008ff90, + "save": 0x1008ff77, + "documents": 0x1008ff5b, + "battery": 0x1008ff93, + "bluetooth": 0x1008ff94, + "wlan": 0x1008ff95, + "uwb": 0x1008ff96, + "videonext": 0x1008fe22, + "videoprev": 0x1008fe23, + "brightnesscycle": 0x1008ff07, + "brightnesszero": 0x100810f4, + "displayoff": 0x100810f5, + "wimax": 0x1008ffb4, + "rfkill": 0x1008ffb5, } -// generated by ./xkeys.bash -var xKeysNormal = map[string]int{ - "escape": uinput.KeyEsc, - "1": uinput.Key1, - "2": uinput.Key2, - "3": uinput.Key3, - "4": uinput.Key4, - "5": uinput.Key5, - "6": uinput.Key6, - "7": uinput.Key7, - "8": uinput.Key8, - "9": uinput.Key9, - "0": uinput.Key0, - "minus": uinput.KeyMinus, - "equal": uinput.KeyEqual, - "backspace": uinput.KeyBackspace, - "tab": uinput.KeyTab, - "q": uinput.KeyQ, - "w": uinput.KeyW, - "e": uinput.KeyE, - "r": uinput.KeyR, - "t": uinput.KeyT, - "y": uinput.KeyY, - "u": uinput.KeyU, - "i": uinput.KeyI, - "o": uinput.KeyO, - "p": uinput.KeyP, - "bracketleft": uinput.KeyLeftbrace, - "bracketright": uinput.KeyRightbrace, - "return": uinput.KeyEnter, - "control_l": uinput.KeyLeftctrl, - "a": uinput.KeyA, - "s": uinput.KeyS, - "d": uinput.KeyD, - "f": uinput.KeyF, - "g": uinput.KeyG, - "h": uinput.KeyH, - "j": uinput.KeyJ, - "k": uinput.KeyK, - "l": uinput.KeyL, - "semicolon": uinput.KeySemicolon, - "apostrophe": uinput.KeyApostrophe, - "grave": uinput.KeyGrave, - "shift_l": uinput.KeyLeftshift, - "backslash": uinput.KeyBackslash, - "z": uinput.KeyZ, - "x": uinput.KeyX, - "c": uinput.KeyC, - "v": uinput.KeyV, - "b": uinput.KeyB, - "n": uinput.KeyN, - "m": uinput.KeyM, - "comma": uinput.KeyComma, - "period": uinput.KeyDot, - "slash": uinput.KeySlash, - "shift_r": uinput.KeyRightshift, - "kp_multiply": uinput.KeyKpasterisk, - "alt_l": uinput.KeyLeftalt, - "space": uinput.KeySpace, - "caps_lock": uinput.KeyCapslock, - "f1": uinput.KeyF1, - "f2": uinput.KeyF2, - "f3": uinput.KeyF3, - "f4": uinput.KeyF4, - "f5": uinput.KeyF5, - "f6": uinput.KeyF6, - "f7": uinput.KeyF7, - "f8": uinput.KeyF8, - "f9": uinput.KeyF9, - "f10": uinput.KeyF10, - "num_lock": uinput.KeyNumlock, - "scroll_lock": uinput.KeyScrolllock, - "kp_home": uinput.KeyKp7, - "kp_up": uinput.KeyKp8, - "kp_prior": uinput.KeyKp9, - "kp_subtract": uinput.KeyKpminus, - "kp_left": uinput.KeyKp4, - "kp_begin": uinput.KeyKp5, - "kp_right": uinput.KeyKp6, - "kp_add": uinput.KeyKpplus, - "kp_end": uinput.KeyKp1, - "kp_down": uinput.KeyKp2, - "kp_next": uinput.KeyKp3, - "kp_insert": uinput.KeyKp0, - "kp_delete": uinput.KeyKpdot, - "iso_level3_shift": uinput.KeyZenkakuhankaku, - "f11": uinput.KeyF11, - "f12": uinput.KeyF12, - "katakana": uinput.KeyKatakana, - "hiragana": uinput.KeyHiragana, - "henkan_mode": uinput.KeyHenkan, - "hiragana_katakana": uinput.KeyKatakanahiragana, - "muhenkan": uinput.KeyMuhenkan, - "kp_enter": uinput.KeyKpenter, - "control_r": uinput.KeyRightctrl, - "kp_divide": uinput.KeyKpslash, - "alt_r": uinput.KeyRightalt, - "linefeed": uinput.KeyLinefeed, - "home": uinput.KeyHome, - "up": uinput.KeyUp, - "prior": uinput.KeyPageup, - "left": uinput.KeyLeft, - "right": uinput.KeyRight, - "end": uinput.KeyEnd, - "down": uinput.KeyDown, - "next": uinput.KeyPagedown, - "insert": uinput.KeyInsert, - "delete": uinput.KeyDelete, - "xf86audiomute": uinput.KeyMute, - "xf86audiolowervolume": uinput.KeyVolumedown, - "xf86audioraisevolume": uinput.KeyVolumeup, - "xf86poweroff": uinput.KeyPower, - "kp_equal": uinput.KeyKpequal, - "plusminus": uinput.KeyKpplusminus, - "pause": uinput.KeyPause, - "xf86launcha": uinput.KeyScale, - "kp_decimal": uinput.KeyKpcomma, - "hangul": uinput.KeyHangeul, - "hangul_hanja": uinput.KeyHanja, - "super_l": uinput.KeyLeftmeta, - "super_r": uinput.KeyRightmeta, - "menu": uinput.KeyCompose, - "redo": uinput.KeyAgain, - "sunprops": uinput.KeyProps, - "undo": uinput.KeyUndo, - "sunfront": uinput.KeyFront, - "xf86copy": uinput.KeyCopy, - "xf86open": uinput.KeyOpen, - "xf86paste": uinput.KeyPaste, - "find": uinput.KeyFind, - "xf86cut": uinput.KeyCut, - "help": uinput.KeyHelp, - "xf86menukb": uinput.KeyMenu, - "xf86calculator": uinput.KeyCalc, - "xf86sleep": uinput.KeySleep, - "xf86wakeup": uinput.KeyWakeup, - "xf86explorer": uinput.KeyFile, - "xf86xfer": uinput.KeyXfer, - "xf86launch1": uinput.KeyProg1, - "xf86launch2": uinput.KeyProg2, - "xf86www": uinput.KeyWww, - "xf86dos": uinput.KeyMsdos, - "xf86screensaver": uinput.KeyCoffee, - "xf86rotatewindows": uinput.KeyDirection, - "xf86taskpane": uinput.KeyCyclewindows, - "xf86mail": uinput.KeyMail, - "xf86favorites": uinput.KeyBookmarks, - "xf86mycomputer": uinput.KeyComputer, - "xf86back": uinput.KeyBack, - "xf86forward": uinput.KeyForward, - "xf86eject": uinput.KeyEjectcd, - "xf86audionext": uinput.KeyNextsong, - "xf86audioplay": uinput.KeyPlaypause, - "xf86audioprev": uinput.KeyPrevioussong, - "xf86audiostop": uinput.KeyStopcd, - "xf86audiorecord": uinput.KeyRecord, - "xf86audiorewind": uinput.KeyRewind, - "xf86phone": uinput.KeyPhone, - "xf86messenger": uinput.KeyChat, - "xf86search": uinput.KeySearch, - "xf86go": uinput.KeyConnect, - "xf86finance": uinput.KeyFinance, - "xf86game": uinput.KeySport, - "xf86shop": uinput.KeyShop, - "cancel": uinput.KeyCancel, - "xf86monbrightnessdown": uinput.KeyBrightnessdown, - "xf86monbrightnessup": uinput.KeyBrightnessup, - "xf86audiomedia": uinput.KeyMedia, - "xf86display": uinput.KeySwitchvideomode, - "xf86kbdlightonoff": uinput.KeyKbdillumtoggle, - "xf86kbdbrightnessdown": uinput.KeyKbdillumdown, - "xf86kbdbrightnessup": uinput.KeyKbdillumup, - "xf86send": uinput.KeySend, - "xf86reply": uinput.KeyReply, - "xf86mailforward": uinput.KeyForwardmail, - "xf86save": uinput.KeySave, - "xf86documents": uinput.KeyDocuments, - "xf86battery": uinput.KeyBattery, - "xf86bluetooth": uinput.KeyBluetooth, - "xf86wlan": uinput.KeyWlan, - "xf86uwb": uinput.KeyUwb, - "xf86next_vmode": uinput.KeyVideoNext, - "xf86prev_vmode": uinput.KeyVideoPrev, - "xf86monbrightnesscycle": uinput.KeyBrightnessCycle, - "xf86brightnessauto": uinput.KeyBrightnessZero, - "xf86displayoff": uinput.KeyDisplayOff, - "xf86wwan": uinput.KeyWimax, - "xf86rfkill": uinput.KeyRfkill, - "xf86webcam": uinput.KeyCamera, - "print": uinput.KeyPrint, +var XKeys = map[string]Chord{} + +func newChord(keymap *xkb.Keymap, mask, code uint32) Chord{ + altGrMask := uint32(1) << keymap.ModGetIndex("Mod5") + ctrlMask := uint32(1) << keymap.ModGetIndex(xkb.ModNameCtrl) + altMask := uint32(1) << keymap.ModGetIndex(xkb.ModNameAlt) + shiftMask := uint32(1) << keymap.ModGetIndex(xkb.ModNameShift) + return Chord{ + false, + (mask & altGrMask) != 0, + (mask & ctrlMask) != 0, + (mask & altMask) != 0, + (mask & shiftMask) != 0, + int(code) - 8, + } } -var xKeysShifted = map[string]int{ - "exclam": uinput.Key1, - "at": uinput.Key2, - "numbersign": uinput.Key3, - "dollar": uinput.Key4, - "percent": uinput.Key5, - "asciicircum": uinput.Key6, - "ampersand": uinput.Key7, - "asterisk": uinput.Key8, - "parenleft": uinput.Key9, - "parenright": uinput.Key0, - "underscore": uinput.KeyMinus, - "plus": uinput.KeyEqual, - "iso_left_tab": uinput.KeyTab, - "q": uinput.KeyQ, - "w": uinput.KeyW, - "e": uinput.KeyE, - "r": uinput.KeyR, - "t": uinput.KeyT, - "y": uinput.KeyY, - "u": uinput.KeyU, - "i": uinput.KeyI, - "o": uinput.KeyO, - "p": uinput.KeyP, - "braceleft": uinput.KeyLeftbrace, - "braceright": uinput.KeyRightbrace, - "a": uinput.KeyA, - "s": uinput.KeyS, - "d": uinput.KeyD, - "f": uinput.KeyF, - "g": uinput.KeyG, - "h": uinput.KeyH, - "j": uinput.KeyJ, - "k": uinput.KeyK, - "l": uinput.KeyL, - "colon": uinput.KeySemicolon, - "quotedbl": uinput.KeyApostrophe, - "asciitilde": uinput.KeyGrave, - "bar": uinput.KeyBackslash, - "z": uinput.KeyZ, - "x": uinput.KeyX, - "c": uinput.KeyC, - "v": uinput.KeyV, - "b": uinput.KeyB, - "n": uinput.KeyN, - "m": uinput.KeyM, - "less": uinput.KeyComma, - "greater": uinput.KeyDot, - "question": uinput.KeySlash, - "kp_7": uinput.KeyKp7, - "kp_8": uinput.KeyKp8, - "kp_9": uinput.KeyKp9, - "kp_4": uinput.KeyKp4, - "kp_5": uinput.KeyKp5, - "kp_6": uinput.KeyKp6, - "kp_1": uinput.KeyKp1, - "kp_2": uinput.KeyKp2, - "kp_3": uinput.KeyKp3, - "kp_0": uinput.KeyKp0, - "sys_req": uinput.KeySysrq, - "break": uinput.KeyPause, - "xf86audiopause": uinput.KeyPrevioussong, +func initKeys(keymap *xkb.Keymap, linux bool) { + maxCode := keymap.MaxKeycode() + for code := keymap.MinKeycode(); code <= maxCode && code < 256; code++ { + numLevels := keymap.NumLevelsForKey(code, 0) + for level := uint32(0); level < numLevels; level++ { + for _, sym := range keymap.KeyGetSymsByLevel(code, 0, level) { + chord := newChord(keymap, keymap.KeyGetMod(code, 0, level), code) + if linux { + for name, s := range linuxXSyms { + if s == sym { + LinuxKeys[name] = chord + } + } + } + XKeys[xkb.KeysymGetName(sym)] = chord + } + } + } +} + +func getChord(keymap *xkb.Keymap, keysym uint32) Chord { + maxCode := keymap.MaxKeycode() + for code := keymap.MinKeycode(); code <= maxCode && code < 256; code++ { + numLevels := keymap.NumLevelsForKey(code, 0) + for level := uint32(0); level < numLevels; level++ { + for _, sym := range keymap.KeyGetSymsByLevel(code, 0, level) { + if sym == keysym { + return newChord(keymap, keymap.KeyGetMod(code, 0, level), code) + } + } + } + } + return Chord{} } diff --git a/xkb/xkb.go b/xkb/xkb.go new file mode 100644 index 0000000..4bd8ba7 --- /dev/null +++ b/xkb/xkb.go @@ -0,0 +1,145 @@ +package xkb + +//#cgo pkg-config: xkbcommon +//#cgo LDFLAGS: -ldl +// +//#include +//#include +//#include +import "C" +import "unsafe" + +const ( + // ModNameShift is the name of Shift Modifier + ModNameShift = "Shift" + // ModNameCaps is the name of Caps Lock Modifier + ModNameCaps = "Lock" + // ModNameCtrl is the name of Control Modifier + ModNameCtrl = "Control" + // ModNameAlt is the name of Alt Modifier + ModNameAlt = "Mod1" + // ModNameNum is the name of Num Lock Modifier + ModNameNum = "Mod2" + // ModNameLogo is the name of Logo Modifier + ModNameLogo = "Mod4" + // LedNameCaps is the name of Caps Lock Led + LedNameCaps = "Caps Lock" + // LedNameNum is the name of Num Lock Led + LedNameNum = "Num Lock" + // LedNameScroll is the name of Scroll Lock Led + LedNameScroll = "Scroll Lock" +) + +func KeysymGetName(keysym uint32) string { + s := "................................................................" + cs := C.CString(s) + defer C.free(unsafe.Pointer(cs)) + _ = C.xkb_keysym_get_name(C.uint(keysym), cs, C.size_t(len(s))) + return C.GoString(cs) +} + +func KeysymToUtf32(keysym uint32) uint32 { + return uint32(C.xkb_keysym_to_utf32(C.uint(keysym))) +} + +func Utf32ToKeysym(ucs uint32) uint32 { + return uint32(C.xkb_utf32_to_keysym(C.uint(ucs))) +} + +type KeymapCompileFlags int + +const ( + KeymapCompileNoFlags KeymapCompileFlags = C.XKB_KEYMAP_COMPILE_NO_FLAGS +) + +type RuleNames struct { + Rules, Model, Layout, Variant, Options string +} + +func (rn *RuleNames) toC() *C.struct_xkb_rule_names { + if rn == nil { + return nil + } + return &C.struct_xkb_rule_names{ + rules: C.CString(rn.Rules), + model: C.CString(rn.Model), + layout: C.CString(rn.Layout), + variant: C.CString(rn.Variant), + options: C.CString(rn.Options), + } +} + +const ContextNoFlags = 0 + +type Context struct { + p *C.struct_xkb_context +} + +func ContextNew(flags uint32) (ctx *Context) { + ctx = new(Context) + ctx.p = C.xkb_context_new(flags) + return ctx +} + +func (ctx *Context) KeymapNewFromNames(rules *RuleNames, flags KeymapCompileFlags) *Keymap { + km := C.xkb_keymap_new_from_names(ctx.p, rules.toC(), C.enum_xkb_keymap_compile_flags(flags)) + if km == nil { + return nil + } + return &Keymap{km} +} + +func (ctx *Context) Unref() { + C.xkb_context_unref(ctx.p) + ctx.p = nil +} + +type Keymap struct { + p *C.struct_xkb_keymap +} + +func (km *Keymap) KeyGetMod(key, layout, level uint32) uint32 { + var mask C.uint + C.xkb_keymap_key_get_mods_for_level(km.p, C.uint(key), C.uint(layout), C.uint(level), &mask, 1) + return uint32(mask) +} + +func (km *Keymap) KeyGetSymsByLevel(key, layout, level uint32) []uint32 { + var syms *C.xkb_keysym_t + n := int(C.xkb_keymap_key_get_syms_by_level(km.p, C.uint(key), C.uint(layout), C.uint(level), &syms)) + if n == 0 || syms == nil { + return nil + } + data := (*[1 << 30]C.xkb_keysym_t)(unsafe.Pointer(syms))[:n:n] + s := make([]uint32, n) + for i := 0; i < n; i++ { + s[i] = uint32(data[i]) + } + return s +} + +func (km *Keymap) MaxKeycode() uint32 { + return uint32(C.xkb_keymap_max_keycode(km.p)) +} + +func (km *Keymap) MinKeycode() uint32 { + return uint32(C.xkb_keymap_min_keycode(km.p)) +} + +func (km *Keymap) ModGetIndex(mod string) uint { + return uint(C.xkb_keymap_mod_get_index(km.p, C.CString(mod))) +} + +func (km *Keymap) NumLevelsForKey(key, layout uint32) uint32 { + return uint32(C.xkb_keymap_num_levels_for_key(km.p, C.uint(key), C.uint(layout))) +} + +func (km *Keymap) Unref() { + C.xkb_keymap_unref(km.p) + km.p = nil +} + +func keymapUnref(km *Keymap) { + C.xkb_keymap_unref(km.p) + km.p = nil +} diff --git a/xkeys.bash b/xkeys.bash deleted file mode 100755 index 53b1be7..0000000 --- a/xkeys.bash +++ /dev/null @@ -1,42 +0,0 @@ -#!/bin/bash - -align() { - sed '/\/ d - /\/ d - /\/ d - /\/ d - /\/ d - /\/ d - /\/ d - /\/ d - /\/ d - /\/ d - /\/ d - /\/ d - /\/ d - /\/ d' -} - -normal="$({ - paste -d ' ' <(xmodmap -pke | sed '1 d; s/.*= /"/; /.*=/ d; s/ .*/":/' | sed '/^"XF86Eject"/ { N; s/.*\n// }') \ - <(go doc uinput.keyesc | sed '/Key/ !d; s/^\s*/uinput./; s/ .*/,/' | align) | - # Skip really non-matching section, we echo some of them below - sed '/^"XF86Tools"/,/^"XF86AudioPreset"/ d' | - # Remove duplicate keys - sed '/^"XF86Mail":.*Email/ d; /^"Cancel":.*Stop/ d; /^"XF86Send":.*file/ d; /^"Print":.*Sysrq/ d; /Key102Nd,$/ d' - - echo '"XF86WebCam": uinput.KeyCamera,' - echo '"Print": uinput.KeyPrint,' -} | sed 's/^".*"/\L&/; s/^/\t/')" - -printf %s\\n "var xKeysNormal = map[string]int{ -$normal -} -" - -echo 'var xKeysShifted = map[string]int{' -{ - paste -d ' ' <(xmodmap -pke | sed '1 d; s/.*= /"/; /.*=/ d; s/\S* /"/; s/ .*/":/' | sed '/^"XF86Eject"/ { N; s/.*\n// }') \ - <(go doc uinput.keyesc | sed '/Key/ !d; s/^\s*/uinput./; s/ .*/,/' | align) | sed '/^"NoSymbol"/ d; /^\S*_[LR]"/ d; /Key102Nd,$/ d' -} | sed 's/^".*"/\L&/; s/^/\t/' | awk 'NR == FNR {if (length($1) > 4) a[$1]; next} !($1 in a)' <(printf %s\\n "$normal") - -echo '}'