overhaul to use xkbcommon
This commit is contained in:
parent
01fcc71667
commit
f8d6461da0
10 changed files with 812 additions and 720 deletions
|
@ -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
|
|
@ -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"
|
||||
|
|
23
README.md
23
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
|
||||
|
||||
|
|
|
@ -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
|
199
dotool.go
199
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,14 +354,19 @@ 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)
|
||||
}
|
||||
}
|
||||
time.Sleep(typedelay)
|
||||
}
|
||||
} else if s, ok := cutCmd(text, "typedelay"); ok {
|
||||
|
|
|
@ -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"
|
|
@ -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
|
||||
|
|
145
xkb/xkb.go
Normal file
145
xkb/xkb.go
Normal file
|
@ -0,0 +1,145 @@
|
|||
package xkb
|
||||
|
||||
//#cgo pkg-config: xkbcommon
|
||||
//#cgo LDFLAGS: -ldl
|
||||
//
|
||||
//#include <stdlib.h>
|
||||
//#include <xkbcommon/xkbcommon-compose.h>
|
||||
//#include <xkbcommon/xkbcommon.h>
|
||||
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
|
||||
}
|
42
xkeys.bash
42
xkeys.bash
|
@ -1,42 +0,0 @@
|
|||
#!/bin/bash
|
||||
|
||||
align() {
|
||||
sed '/\<KeyRo\>/ d
|
||||
/\<KeyKpjpcomma\>/ d
|
||||
/\<KeyMacro\>/ d
|
||||
/\<KeyYen\>/ d
|
||||
/\<KeySetup\>/ d
|
||||
/\<KeyDeletefile\>/ d
|
||||
/\<KeyClosecd\>/ d
|
||||
/\<KeyEjectclosecd\>/ d
|
||||
/\<KeyIso\>/ d
|
||||
/\<KeyMove\>/ d
|
||||
/\<KeyEdit\>/ d
|
||||
/\<KeyAlterase\>/ d
|
||||
/\<KeyUnknown\>/ d
|
||||
/\<KeyMicmute\>/ 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 '}'
|
Loading…
Reference in a new issue