Compare commits
23 commits
Author | SHA1 | Date | |
---|---|---|---|
|
945a7daede | ||
|
7c0a53ef94 | ||
|
5bc814cb39 | ||
|
b5812c001d | ||
|
ba67589517 | ||
|
5404859248 | ||
|
ba251d1afd | ||
|
7688cc321c | ||
|
39689f9b34 | ||
|
a169e2e131 | ||
|
f47908ebf2 | ||
|
240298271c | ||
|
a580b0944b | ||
|
a0049f668b | ||
|
ec566eb9ff | ||
|
d5a6aeef74 | ||
|
29f30ad52b | ||
|
f0e9e7102d | ||
|
d0b10e88bc | ||
|
e939562ee4 | ||
|
3845810f7c | ||
|
d1e4b66dd1 | ||
|
ee4a1cd95d |
12 changed files with 467 additions and 172 deletions
|
@ -1,2 +1,2 @@
|
||||||
# This allows users in group input to use dotool without root permissions.
|
# This allows users in group input to use dotool without root permissions.
|
||||||
KERNEL=="uinput", GROUP="input", MODE="0660", OPTIONS+="static_node=uinput"
|
KERNEL=="uinput", GROUP="input", MODE="0620", OPTIONS+="static_node=uinput"
|
||||||
|
|
28
CHANGELOG.md
28
CHANGELOG.md
|
@ -2,14 +2,38 @@
|
||||||
|
|
||||||
Notable changes to dotool will be documented in this file.
|
Notable changes to dotool will be documented in this file.
|
||||||
|
|
||||||
|
## [1.5](https://git.sr.ht/~geb/dotool/refs/1.5)
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- The selecting of the fewest modifiers for simulating keys.
|
||||||
|
|
||||||
|
## [1.4](https://git.sr.ht/~geb/dotool/refs/1.4)
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- A manpage, requiring scdoc.
|
||||||
|
- Heuristic support for dead keys.
|
||||||
|
- Support for altgr.
|
||||||
|
- $DOTOOL_KEYBOARD_NAME to set the virtual keyboard's name.
|
||||||
|
- More verbose --list-keys output.
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- Replaced ./install.sh with ./build.sh.
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- Now prefers the fewest modifiers for simulating keys. (UPDATE: see 1.5)
|
||||||
|
|
||||||
## [1.3](https://git.sr.ht/~geb/dotool/refs/1.3)
|
## [1.3](https://git.sr.ht/~geb/dotool/refs/1.3)
|
||||||
|
|
||||||
## Added
|
### Added
|
||||||
|
|
||||||
- Support for keyboard layouts.
|
- Support for keyboard layouts.
|
||||||
- hwheel for horizontal scrolling.
|
- hwheel for horizontal scrolling.
|
||||||
|
|
||||||
## Changed
|
### Changed
|
||||||
|
|
||||||
- Now depends on the xkbcommon library.
|
- Now depends on the xkbcommon library.
|
||||||
- XKB key names are now case-sensitive.
|
- XKB key names are now case-sensitive.
|
||||||
|
|
65
README.md
65
README.md
|
@ -1,65 +1,42 @@
|
||||||
# dotool
|
# dotool
|
||||||
|
|
||||||
dotool reads commands from stdin and simulates keyboard and pointer events.
|
dotool reads actions from stdin and simulates keyboard/mouse input using
|
||||||
It works everywhere on Linux, including in X11, Wayland and TTYs.
|
Linux's uinput module. It works systemwide and supports keyboard layouts.
|
||||||
|
|
||||||
|
## Install From Packages
|
||||||
|
|
||||||
|
Packages of dotool are available on:
|
||||||
|
|
||||||
|
- [Alpine](https://pkgs.alpinelinux.org/packages?name=dotool)
|
||||||
|
- [Arch (AUR)](https://aur.archlinux.org/packages?SeB=n&K=dotool)
|
||||||
|
- [Nix](https://search.nixos.org/packages?channel=unstable&type=packages&query=dotool)
|
||||||
|
- [Void](https://voidlinux.org/packages/?q=dotool)
|
||||||
|
|
||||||
|
and potentially other platforms.
|
||||||
|
|
||||||
## Install From Source
|
## Install From Source
|
||||||
|
|
||||||
With `go` and `libxkbcommon-dev` installed, run:
|
With `go`, `libxkbcommon-dev` and `scdoc` installed, run:
|
||||||
|
|
||||||
sudo ./install.sh
|
./build.sh && sudo ./build.sh install
|
||||||
|
|
||||||
## Permission
|
And to trigger the udev rule, run:
|
||||||
|
|
||||||
dotool requires permission to `/dev/uinput` to create the virtual input
|
sudo udevadm control --reload && sudo udevadm trigger
|
||||||
devices, and a udev rule grants this to users in group input.
|
|
||||||
|
|
||||||
You could try:
|
|
||||||
|
|
||||||
echo type hello | dotool
|
|
||||||
|
|
||||||
and if need be, you can run:
|
|
||||||
|
|
||||||
sudo groupadd -f input
|
|
||||||
sudo usermod -a -G input $USER
|
|
||||||
|
|
||||||
and re-login and trigger the udev rule or just reboot.
|
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
See `dotool --help`, but this greets the world:
|
See the [manpage](doc/dotool.1.scd).
|
||||||
|
|
||||||
echo 'type Sup, Lads!' | dotool
|
|
||||||
|
|
||||||
and this screams for three seconds:
|
|
||||||
|
|
||||||
{ echo keydown A; sleep 3; echo key H shift+1; } | 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`.
|
|
||||||
|
|
||||||
dotoold &
|
|
||||||
echo type super | dotoolc
|
|
||||||
echo type speedy | dotoolc
|
|
||||||
|
|
||||||
## Keyboard Layouts
|
|
||||||
|
|
||||||
dotool will type 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
|
## Numen and Contact
|
||||||
|
|
||||||
dotool was written for [Numen](https://numenvoice.org), which has a
|
dotool was written for [Numen](https://numenvoice.org), which has a
|
||||||
[chat on Matrix](https://matrix.to/#/#numen:matrix.org) you're welcome to join.
|
[chat on Matrix](https://matrix.to/#/#numen:matrix.org) you're welcome to join.
|
||||||
|
|
||||||
You can also send questions, thoughts or patches by composing an email to
|
You can also send questions or patches by composing an email to
|
||||||
[~geb/public-inbox@lists.sr.ht](https://lists.sr.ht/~geb/public-inbox).
|
[~geb/numen@lists.sr.ht](https://lists.sr.ht/~geb/numen).
|
||||||
|
|
||||||
## Support Me
|
## Support My Work 👀
|
||||||
|
|
||||||
[Thank you!](https://liberapay.com/geb)
|
[Thank you!](https://liberapay.com/geb)
|
||||||
|
|
||||||
|
|
22
build.sh
Executable file
22
build.sh
Executable file
|
@ -0,0 +1,22 @@
|
||||||
|
#!/bin/sh
|
||||||
|
# ./build.sh ['install']
|
||||||
|
: "${DOTOOL_VERSION=$(git describe --long --abbrev=12 --tags --dirty 2>/dev/null || echo 1.5)}"
|
||||||
|
: "${DOTOOL_DESTDIR=}"
|
||||||
|
: "${DOTOOL_BINDIR=usr/local/bin}"
|
||||||
|
: "${DOTOOL_UDEV_RULES_DIR=etc/udev/rules.d}"
|
||||||
|
|
||||||
|
if [ "$*" != '' ] && [ "$*" != install ]; then
|
||||||
|
echo bad usage
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! [ "$1" ]; then
|
||||||
|
go build -ldflags "-X main.Version=$DOTOOL_VERSION" || exit
|
||||||
|
echo Built Successfully.
|
||||||
|
else
|
||||||
|
install -Dm755 dotool dotoolc dotoold -t "$DOTOOL_DESTDIR/$DOTOOL_BINDIR" || exit
|
||||||
|
install -Dm644 80-dotool.rules -t "$DOTOOL_DESTDIR/$DOTOOL_UDEV_RULES_DIR" || exit
|
||||||
|
mkdir -p "$DOTOOL_DESTDIR/usr/share/man/man1" || exit
|
||||||
|
scdoc < doc/dotool.1.scd > "$DOTOOL_DESTDIR/usr/share/man/man1/dotool.1" || exit
|
||||||
|
echo Installed Successfully.
|
||||||
|
fi
|
132
doc/dotool.1.scd
Normal file
132
doc/dotool.1.scd
Normal file
|
@ -0,0 +1,132 @@
|
||||||
|
dotool(1)
|
||||||
|
|
||||||
|
# NAME
|
||||||
|
|
||||||
|
*dotool* - uinput tool
|
||||||
|
|
||||||
|
# SYNOPSIS
|
||||||
|
|
||||||
|
*dotool* < _actions_
|
||||||
|
|
||||||
|
# DESCRIPTION
|
||||||
|
|
||||||
|
*dotool* reads actions from stdin and simulates keyboard/mouse input using
|
||||||
|
Linux's uinput module.
|
||||||
|
|
||||||
|
# PERMISSION
|
||||||
|
|
||||||
|
*dotool* requires write permission to */dev/uinput*, which is granted to
|
||||||
|
users in group *input* by a udev rule.
|
||||||
|
|
||||||
|
You can test:
|
||||||
|
|
||||||
|
*echo type hello | dotool*
|
||||||
|
|
||||||
|
and if need be, you could add your user to group *input* with:
|
||||||
|
|
||||||
|
*groupadd -f input*++
|
||||||
|
*usermod -a -G input $USER*
|
||||||
|
|
||||||
|
and then it's foolproof to reboot to make the group and rule effective.
|
||||||
|
|
||||||
|
# KEYBOARD LAYOUTS
|
||||||
|
|
||||||
|
*dotool* may type gobbledygook if it's simulating keycodes for a different
|
||||||
|
keyboard layout than your environment is expecting.
|
||||||
|
|
||||||
|
You can specify the layout with the environment variables *DOTOOL_XKB_LAYOUT*
|
||||||
|
and *DOTOOL_XKB_VARIANT*. For example:
|
||||||
|
|
||||||
|
*echo type azerty | DOTOOL_XKB_LAYOUT=fr dotool*
|
||||||
|
|
||||||
|
You can also specify the name to give the virtual keyboard with the environment
|
||||||
|
variable *DOTOOL_KEYBOARD_NAME*, which can be useful making rules for your
|
||||||
|
environment.
|
||||||
|
|
||||||
|
Currently the *type* action has only heuristic support for dead keys.
|
||||||
|
|
||||||
|
# OPTIONS
|
||||||
|
|
||||||
|
*-h*, *--help*
|
||||||
|
Print help and exit.
|
||||||
|
|
||||||
|
*--list-keys*
|
||||||
|
Print the possible Linux keys and exit.
|
||||||
|
|
||||||
|
*--list-x-keys*
|
||||||
|
Print the possible XKB keys and exit.
|
||||||
|
|
||||||
|
*--version*
|
||||||
|
Print the version and exit.
|
||||||
|
|
||||||
|
# ACTIONS
|
||||||
|
|
||||||
|
*key* _CHORD_...++
|
||||||
|
*keydown* _CHORD_...++
|
||||||
|
*keyup* _CHORD_...
|
||||||
|
Press and/or release each _CHORD_. A _CHORD_ is a key or a key with
|
||||||
|
modifiers, such as *a*, *shift+a* or *ctrl+shift+a*.
|
||||||
|
|
||||||
|
The supported modifiers are *super*, *altgr*, *ctrl*, *alt* and *shift*.
|
||||||
|
|
||||||
|
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. This example types *!!!* with the *us* layout:
|
||||||
|
|
||||||
|
*echo key shift+1 x:exclam shift+k:2 | dotool*
|
||||||
|
|
||||||
|
*type* _TEXT_
|
||||||
|
Type _TEXT_.
|
||||||
|
|
||||||
|
*click* *left*/*middle*/*right*++
|
||||||
|
*buttondown* *left*/*middle*/*right*++
|
||||||
|
*buttonup* *left*/*middle*/*right*
|
||||||
|
Press and/or release a mouse button.
|
||||||
|
|
||||||
|
*wheel* _AMOUNT_++
|
||||||
|
*hwheel* _AMOUNT_
|
||||||
|
Scroll a vertical/horizontal mouse wheel by a positive or negative
|
||||||
|
_AMOUNT_.
|
||||||
|
|
||||||
|
*mouseto* _X_ _Y_
|
||||||
|
Jump the cursor to the position _X_ _Y_, where _X_ and _Y_ are
|
||||||
|
percentages between 0.0 and 1.0.
|
||||||
|
|
||||||
|
*mousemove* _X_ _Y_
|
||||||
|
Move the cursor relative to its current position.
|
||||||
|
|
||||||
|
*keydelay* _MILLISECONDS_++
|
||||||
|
*keyhold* _MILLISECONDS_++
|
||||||
|
*typedelay* _MILLISECONDS_++
|
||||||
|
*typehold* _MILLISECONDS_
|
||||||
|
Set the delay between/holding each key with the *key*\* actions/*type*
|
||||||
|
action.
|
||||||
|
|
||||||
|
The default *keydelay* and *typedelay* is 2ms, and the default
|
||||||
|
*keyhold* and *typehold* is 8ms.
|
||||||
|
|
||||||
|
# LONG-RUNNING INSTANCE
|
||||||
|
|
||||||
|
Each instance of *dotool* has an initial delay registering the virtual
|
||||||
|
devices, but you can keep writing actions to a long-running instance. The
|
||||||
|
daemon and client, *dotoold* and *dotoolc*, let you do this with a pipe
|
||||||
|
behind the scenes, for example:
|
||||||
|
|
||||||
|
*dotoold &*++
|
||||||
|
*echo type super | dotoolc*++
|
||||||
|
*echo type speedy | dotoolc*
|
||||||
|
|
||||||
|
# EXAMPLES
|
||||||
|
|
||||||
|
This greets the world:
|
||||||
|
|
||||||
|
*echo type hi | dotool*
|
||||||
|
|
||||||
|
This screams for roughly three seconds:
|
||||||
|
|
||||||
|
*{ echo keydown A; sleep 3; echo key H shift+1; } | dotool*
|
||||||
|
|
||||||
|
# AUTHOR
|
||||||
|
|
||||||
|
John Gebbie
|
178
dotool.go
178
dotool.go
|
@ -18,9 +18,9 @@ import (
|
||||||
var Version string
|
var Version string
|
||||||
|
|
||||||
func usage() {
|
func usage() {
|
||||||
fmt.Println(`dotool reads commands from stdin and simulates keyboard and pointer events.
|
fmt.Println(`dotool reads actions from stdin and simulates input using uinput.
|
||||||
|
|
||||||
The commands are:
|
The supported actions are:
|
||||||
key CHORD...
|
key CHORD...
|
||||||
keydown CHORD...
|
keydown CHORD...
|
||||||
keyup CHORD...
|
keyup CHORD...
|
||||||
|
@ -28,62 +28,20 @@ The commands are:
|
||||||
click left/middle/right
|
click left/middle/right
|
||||||
buttondown left/middle/right
|
buttondown left/middle/right
|
||||||
buttonup left/middle/right
|
buttonup left/middle/right
|
||||||
wheel AMOUNT (a positive AMOUNT is up, a negative is down)
|
wheel AMOUNT
|
||||||
hwheel AMOUNT (a positive AMOUNT is right, a negative is left)
|
hwheel AMOUNT
|
||||||
mouseto X Y (where X and Y are percentages between 0.0 and 1.0)
|
mouseto X Y (where X and Y are percentages between 0.0 and 1.0)
|
||||||
mousemove X Y (where X and Y are the number of pixels to move)
|
mousemove X Y (where X and Y are amounts to move)
|
||||||
keydelay MILLISECONDS (default: 2)
|
keydelay MILLISECONDS
|
||||||
keyhold MILLISECONDS (default: 8)
|
keyhold MILLISECONDS
|
||||||
typedelay MILLISECONDS (default: 2)
|
typedelay MILLISECONDS
|
||||||
typehold MILLISECONDS (default: 8)
|
typehold MILLISECONDS
|
||||||
|
|
||||||
|
--list-keys Print the possible Linux keys and exit.
|
||||||
|
--list-x-keys Print the possible XKB keys and exit.
|
||||||
|
--version Print the version and exit.
|
||||||
|
|
||||||
dotool is installed with a udev rule to allow users in group input to run
|
See 'man dotool' for the documentation.`)
|
||||||
it without root permissions.
|
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
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 type 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 possible Linux keys and exit.
|
|
||||||
|
|
||||||
--list-x-keys
|
|
||||||
Print the possible XKB keys and exit.
|
|
||||||
|
|
||||||
--version
|
|
||||||
Print the version and exit.`)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func fatal(a ...any) {
|
func fatal(a ...any) {
|
||||||
|
@ -104,6 +62,8 @@ func log(err error) {
|
||||||
type Chord struct {
|
type Chord struct {
|
||||||
Super, AltGr, Ctrl, Alt, Shift bool
|
Super, AltGr, Ctrl, Alt, Shift bool
|
||||||
Key int
|
Key int
|
||||||
|
|
||||||
|
level uint32 // just for initKeys
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseChord(keymap *xkb.Keymap, chord string) (Chord, error) {
|
func parseChord(keymap *xkb.Keymap, chord string) (Chord, error) {
|
||||||
|
@ -138,6 +98,8 @@ func parseChord(keymap *xkb.Keymap, chord string) (Chord, error) {
|
||||||
switch strings.ToLower(keys[i]) {
|
switch strings.ToLower(keys[i]) {
|
||||||
case "super":
|
case "super":
|
||||||
c.Super = true
|
c.Super = true
|
||||||
|
case "altgr":
|
||||||
|
c.AltGr = true
|
||||||
case "ctrl", "control":
|
case "ctrl", "control":
|
||||||
c.Ctrl = true
|
c.Ctrl = true
|
||||||
case "alt":
|
case "alt":
|
||||||
|
@ -152,55 +114,80 @@ func parseChord(keymap *xkb.Keymap, chord string) (Chord, error) {
|
||||||
return c, nil
|
return c, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Chord) KeyDown(kb uinput.Keyboard) {
|
func (c Chord) KeyDown(kb uinput.Keyboard) {
|
||||||
if c.Super {
|
if c.Super {
|
||||||
log(kb.KeyDown(uinput.KeyLeftmeta))
|
log(kb.KeyDown(super))
|
||||||
}
|
}
|
||||||
if c.AltGr {
|
if c.AltGr {
|
||||||
log(kb.KeyDown(84))
|
log(kb.KeyDown(altgr))
|
||||||
}
|
}
|
||||||
if c.Ctrl {
|
if c.Ctrl {
|
||||||
log(kb.KeyDown(uinput.KeyLeftctrl))
|
log(kb.KeyDown(ctrl))
|
||||||
}
|
}
|
||||||
if c.Alt {
|
if c.Alt {
|
||||||
log(kb.KeyDown(uinput.KeyLeftalt))
|
log(kb.KeyDown(alt))
|
||||||
}
|
}
|
||||||
if c.Shift {
|
if c.Shift {
|
||||||
log(kb.KeyDown(uinput.KeyLeftshift))
|
log(kb.KeyDown(shift))
|
||||||
}
|
}
|
||||||
log(kb.KeyDown(c.Key))
|
log(kb.KeyDown(c.Key))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Chord) KeyUp(kb uinput.Keyboard) {
|
func (c Chord) KeyUp(kb uinput.Keyboard) {
|
||||||
if c.Super {
|
if c.Super {
|
||||||
log(kb.KeyUp(uinput.KeyLeftmeta))
|
log(kb.KeyUp(super))
|
||||||
}
|
}
|
||||||
if c.AltGr {
|
if c.AltGr {
|
||||||
log(kb.KeyUp(84))
|
log(kb.KeyUp(altgr))
|
||||||
}
|
}
|
||||||
if c.Ctrl {
|
if c.Ctrl {
|
||||||
log(kb.KeyUp(uinput.KeyLeftctrl))
|
log(kb.KeyUp(ctrl))
|
||||||
}
|
}
|
||||||
if c.Alt {
|
if c.Alt {
|
||||||
log(kb.KeyUp(uinput.KeyLeftalt))
|
log(kb.KeyUp(alt))
|
||||||
}
|
}
|
||||||
if c.Shift {
|
if c.Shift {
|
||||||
log(kb.KeyUp(uinput.KeyLeftshift))
|
log(kb.KeyUp(shift))
|
||||||
}
|
}
|
||||||
log(kb.KeyUp(c.Key))
|
log(kb.KeyUp(c.Key))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Chord) String() string {
|
||||||
|
var sb strings.Builder
|
||||||
|
if c.Super {
|
||||||
|
sb.WriteString("super+")
|
||||||
|
}
|
||||||
|
if c.AltGr {
|
||||||
|
sb.WriteString("altgr+")
|
||||||
|
}
|
||||||
|
if c.Ctrl {
|
||||||
|
sb.WriteString("ctrl+")
|
||||||
|
}
|
||||||
|
if c.Alt {
|
||||||
|
sb.WriteString("alt+")
|
||||||
|
}
|
||||||
|
if c.Shift {
|
||||||
|
sb.WriteString("shift+")
|
||||||
|
}
|
||||||
|
sb.WriteString("k:")
|
||||||
|
sb.WriteString(strconv.Itoa(c.Key))
|
||||||
|
return sb.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
func listKeys(keymap *xkb.Keymap, keys map[string]Chord) {
|
func listKeys(keymap *xkb.Keymap, keys map[string]Chord) {
|
||||||
|
var margin int
|
||||||
for code := 1; code < 256; code++ {
|
for code := 1; code < 256; code++ {
|
||||||
for name, chord := range keys {
|
for name := range keys {
|
||||||
if chord.Key == code && (chord == Chord{Key: code}) {
|
if len(name) > margin {
|
||||||
fmt.Println(name, code)
|
margin = len(name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
for code := 1; code < 256; code++ {
|
||||||
for name, chord := range keys {
|
for name, chord := range keys {
|
||||||
if chord.Key == code && (chord != Chord{Key: code}) {
|
if chord.Key == code {
|
||||||
fmt.Println(name, code)
|
fmt.Printf("%-*s %s\n", margin, name, chord.String())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -245,43 +232,47 @@ func main() {
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
optset := opt.NewOptionSet()
|
o := opt.NewOptSet()
|
||||||
|
|
||||||
optset.FlagFunc("h", func() error {
|
o.FlagFunc("h", func() error {
|
||||||
usage()
|
usage()
|
||||||
os.Exit(0)
|
os.Exit(0)
|
||||||
panic("unreachable")
|
panic("unreachable")
|
||||||
})
|
})
|
||||||
optset.Alias("h", "help")
|
o.Alias("h", "help")
|
||||||
|
|
||||||
optset.FlagFunc("list-keys", func() error {
|
o.FlagFunc("list-keys", func() error {
|
||||||
listKeys(keymap, LinuxKeys)
|
listKeys(keymap, LinuxKeys)
|
||||||
os.Exit(0)
|
os.Exit(0)
|
||||||
panic("unreachable")
|
panic("unreachable")
|
||||||
})
|
})
|
||||||
|
|
||||||
optset.FlagFunc("list-x-keys", func() error {
|
o.FlagFunc("list-x-keys", func() error {
|
||||||
listKeys(keymap, XKeys)
|
listKeys(keymap, XKeys)
|
||||||
os.Exit(0)
|
os.Exit(0)
|
||||||
panic("unreachable")
|
panic("unreachable")
|
||||||
})
|
})
|
||||||
|
|
||||||
optset.FlagFunc("version", func() error {
|
o.FlagFunc("version", func() error {
|
||||||
fmt.Println(Version)
|
fmt.Println(Version)
|
||||||
os.Exit(0)
|
os.Exit(0)
|
||||||
panic("unreachable")
|
panic("unreachable")
|
||||||
})
|
})
|
||||||
|
|
||||||
err := optset.Parse(true, os.Args[1:])
|
err := o.Parse(true, os.Args[1:])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fatal(err.Error())
|
fatal(err.Error())
|
||||||
}
|
}
|
||||||
if len(optset.Args()) > 0 {
|
if len(o.Args()) > 0 {
|
||||||
fatal("there should be no arguments, commands are read from stdin")
|
fatal("there should be no arguments, commands are read from stdin")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
keyboard, err := uinput.CreateKeyboard("/dev/uinput", []byte("dotool keyboard"))
|
keyboardName := []byte(os.Getenv("DOTOOL_KEYBOARD_NAME"))
|
||||||
|
if len(keyboardName) == 0 {
|
||||||
|
keyboardName = []byte("dotool keyboard")
|
||||||
|
}
|
||||||
|
keyboard, err := uinput.CreateKeyboard("/dev/uinput", keyboardName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fatal(err.Error())
|
fatal(err.Error())
|
||||||
}
|
}
|
||||||
|
@ -358,18 +349,23 @@ func main() {
|
||||||
}
|
}
|
||||||
} else if s, ok := cutWord(text, "type"); ok {
|
} else if s, ok := cutWord(text, "type"); ok {
|
||||||
for _, r := range s {
|
for _, r := range s {
|
||||||
if sym := xkb.Utf32ToKeysym(uint32(r)); sym == 0 {
|
sym := xkb.Utf32ToKeysym(uint32(r))
|
||||||
|
if sym == 0 {
|
||||||
warn("invalid character: " + string(r))
|
warn("invalid character: " + string(r))
|
||||||
|
} else if c := getChord(keymap, sym); c.Key != 0 {
|
||||||
|
c.KeyDown(keyboard)
|
||||||
|
time.Sleep(typehold)
|
||||||
|
c.KeyUp(keyboard)
|
||||||
|
} else if c1, c2 := getDeadChords(keymap, r); c1.Key != 0 {
|
||||||
|
c1.KeyDown(keyboard)
|
||||||
|
time.Sleep(typehold)
|
||||||
|
c1.KeyUp(keyboard)
|
||||||
|
time.Sleep(typedelay)
|
||||||
|
c2.KeyDown(keyboard)
|
||||||
|
time.Sleep(typehold)
|
||||||
|
c2.KeyUp(keyboard)
|
||||||
} else {
|
} else {
|
||||||
chord := getChord(keymap, sym)
|
warn("impossible character for layout: " + string(r))
|
||||||
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)
|
time.Sleep(typedelay)
|
||||||
}
|
}
|
||||||
|
|
17
dotoold
17
dotoold
|
@ -1,10 +1,15 @@
|
||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
if [ $# != 0 ]; then
|
for a; do
|
||||||
echo 'dotoold runs dotool reading from a pipe for dotoolc to write to. dotoold
|
case "$a" in
|
||||||
|
-h|--help)
|
||||||
|
echo 'dotoold runs dotool reading from a pipe for dotoolc to write to. dotoold
|
||||||
will exit immediately if the pipe is already being read. The path used
|
will exit immediately if the pipe is already being read. The path used
|
||||||
for the pipe is $DOTOOL_PIPE else /tmp/dotool-pipe.' >&2
|
for the pipe is $DOTOOL_PIPE else /tmp/dotool-pipe.' >&2
|
||||||
[ "$1" = -h ] || [ "$1" = --help ]; exit
|
exit
|
||||||
fi
|
;;
|
||||||
|
--) break;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
fifo_being_read(){
|
fifo_being_read(){
|
||||||
[ -p "$1" ] && /bin/echo 1<>"$1" >"$1"
|
[ -p "$1" ] && /bin/echo 1<>"$1" >"$1"
|
||||||
|
@ -20,5 +25,5 @@ fi
|
||||||
rm -f -- "$p" || exit 1
|
rm -f -- "$p" || exit 1
|
||||||
trap 'rm -f -- "$p"; pkill -P $$; trap - EXIT; exit' EXIT INT TERM HUP
|
trap 'rm -f -- "$p"; pkill -P $$; trap - EXIT; exit' EXIT INT TERM HUP
|
||||||
mkfifo -m 660 "$p" || exit 1
|
mkfifo -m 660 "$p" || exit 1
|
||||||
dotool <> "$p" &
|
dotool "$@" <> "$p" &
|
||||||
wait
|
wait $!
|
||||||
|
|
4
go.mod
4
go.mod
|
@ -3,6 +3,6 @@ module git.sr.ht/~geb/dotool
|
||||||
go 1.19
|
go 1.19
|
||||||
|
|
||||||
require (
|
require (
|
||||||
git.sr.ht/~geb/opt v0.0.0-20221207200434-dad26d091d71
|
git.sr.ht/~geb/opt v0.0.0-20230911153257-e72225a1933c
|
||||||
github.com/bendahl/uinput v1.6.0
|
github.com/bendahl/uinput v1.7.0
|
||||||
)
|
)
|
||||||
|
|
8
go.sum
8
go.sum
|
@ -1,4 +1,4 @@
|
||||||
git.sr.ht/~geb/opt v0.0.0-20221207200434-dad26d091d71 h1:jh3Ite7R1ZvdLt6j52e4njO2SS/z5dWOLXllw7inalc=
|
git.sr.ht/~geb/opt v0.0.0-20230911153257-e72225a1933c h1:gIC1gnCgoasPHks1x6MB+bgDmIWMxKc5HIJPJrsV5Ck=
|
||||||
git.sr.ht/~geb/opt v0.0.0-20221207200434-dad26d091d71/go.mod h1:S6h1g8P7DyG7i7YIHZ5IpYbC6lzZB9DYIEl8PyXOmsg=
|
git.sr.ht/~geb/opt v0.0.0-20230911153257-e72225a1933c/go.mod h1:S6h1g8P7DyG7i7YIHZ5IpYbC6lzZB9DYIEl8PyXOmsg=
|
||||||
github.com/bendahl/uinput v1.6.0 h1:fM6r3OSC17rHh758mizKjSBuqi+XinhiGd4N3pWvZiI=
|
github.com/bendahl/uinput v1.7.0 h1:nA4fm8Wu8UYNOPykIZm66nkWEyvxzfmJ8YC02PM40jg=
|
||||||
github.com/bendahl/uinput v1.6.0/go.mod h1:Np7w3DINc9wB83p12fTAM3DPPhFnAKP0WTXRqCQJ6Z8=
|
github.com/bendahl/uinput v1.7.0/go.mod h1:Np7w3DINc9wB83p12fTAM3DPPhFnAKP0WTXRqCQJ6Z8=
|
||||||
|
|
17
install.sh
17
install.sh
|
@ -1,17 +0,0 @@
|
||||||
#!/bin/sh
|
|
||||||
# ./install.sh [DESTDIR] [BINDIR]
|
|
||||||
: "${DOTOOL_VERSION=$(git describe --long --abbrev=12 --tags --dirty 2>/dev/null || echo 1.3)}"
|
|
||||||
|
|
||||||
go build -ldflags "-X main.Version=$DOTOOL_VERSION" || exit
|
|
||||||
mkdir -p "$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
|
|
||||||
|
|
||||||
# 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
|
|
||||||
udevadm trigger
|
|
165
keys.go
165
keys.go
|
@ -3,8 +3,11 @@ package main
|
||||||
import (
|
import (
|
||||||
"git.sr.ht/~geb/dotool/xkb"
|
"git.sr.ht/~geb/dotool/xkb"
|
||||||
"github.com/bendahl/uinput"
|
"github.com/bendahl/uinput"
|
||||||
|
"unicode"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var super, altgr, ctrl, alt, shift int
|
||||||
|
|
||||||
var LinuxKeys = map[string]Chord{
|
var LinuxKeys = map[string]Chord{
|
||||||
// Linux Only
|
// Linux Only
|
||||||
"micmute": Chord{Key: uinput.KeyMicmute},
|
"micmute": Chord{Key: uinput.KeyMicmute},
|
||||||
|
@ -481,11 +484,137 @@ var linuxXSyms = map[string]uint32{
|
||||||
|
|
||||||
var XKeys = map[string]Chord{}
|
var XKeys = map[string]Chord{}
|
||||||
|
|
||||||
func newChord(keymap *xkb.Keymap, mask, code uint32) Chord{
|
type DeadKeyResult struct {
|
||||||
|
DeadKey, Key string
|
||||||
|
Result rune
|
||||||
|
}
|
||||||
|
|
||||||
|
// this relies on a conventional keymap
|
||||||
|
// this isn't exhaustive (e.g. acircumflexacute)
|
||||||
|
var DeadKeyResults = []DeadKeyResult{
|
||||||
|
{"dead_abovedot", "B", 'Ḃ'},
|
||||||
|
{"dead_abovedot", "C", 'Ċ'},
|
||||||
|
{"dead_abovedot", "D", 'Ḋ'},
|
||||||
|
{"dead_abovedot", "E", 'Ė'},
|
||||||
|
{"dead_abovedot", "F", 'Ḟ'},
|
||||||
|
{"dead_abovedot", "G", 'Ġ'},
|
||||||
|
{"dead_abovedot", "I", 'İ'},
|
||||||
|
{"dead_abovedot", "M", 'Ṁ'},
|
||||||
|
{"dead_abovedot", "P", 'Ṗ'},
|
||||||
|
{"dead_abovedot", "S", 'Ṡ'},
|
||||||
|
{"dead_abovedot", "T", 'Ṫ'},
|
||||||
|
{"dead_abovedot", "X", 'Ẋ'},
|
||||||
|
{"dead_abovedot", "Z", 'Ż'},
|
||||||
|
{"dead_acute", "A", 'Á'},
|
||||||
|
{"dead_acute", "C", 'Ć'},
|
||||||
|
{"dead_acute", "E", 'É'},
|
||||||
|
{"dead_acute", "I", 'Í'},
|
||||||
|
{"dead_acute", "L", 'Ĺ'},
|
||||||
|
{"dead_acute", "N", 'Ń'},
|
||||||
|
{"dead_acute", "O", 'Ó'},
|
||||||
|
{"dead_acute", "R", 'Ŕ'},
|
||||||
|
{"dead_acute", "S", 'Ś'},
|
||||||
|
{"dead_acute", "U", 'Ú'},
|
||||||
|
{"dead_acute", "W", 'Ẃ'},
|
||||||
|
{"dead_acute", "Y", 'Ý'},
|
||||||
|
{"dead_acute", "Z", 'Ź'},
|
||||||
|
{"dead_belowdot", "A", 'Ạ'},
|
||||||
|
{"dead_belowdot", "E", 'Ẹ'},
|
||||||
|
{"dead_belowdot", "I", 'Ị'},
|
||||||
|
{"dead_belowdot", "L", 'Ḷ'},
|
||||||
|
{"dead_belowdot", "O", 'Ọ'},
|
||||||
|
{"dead_belowdot", "U", 'Ụ'},
|
||||||
|
{"dead_belowdot", "Y", 'Ỵ'},
|
||||||
|
{"dead_breve", "A", 'Ă'},
|
||||||
|
{"dead_breve", "G", 'Ğ'},
|
||||||
|
{"dead_breve", "I", 'Ĭ'},
|
||||||
|
{"dead_breve", "U", 'Ŭ'},
|
||||||
|
{"dead_caron", "C", 'Č'},
|
||||||
|
{"dead_caron", "D", 'Ď'},
|
||||||
|
{"dead_caron", "E", 'Ě'},
|
||||||
|
{"dead_caron", "G", 'Ǧ'},
|
||||||
|
{"dead_caron", "L", 'Ľ'},
|
||||||
|
{"dead_caron", "N", 'Ň'},
|
||||||
|
{"dead_caron", "O", 'Ǒ'},
|
||||||
|
{"dead_caron", "R", 'Ř'},
|
||||||
|
{"dead_caron", "S", 'Š'},
|
||||||
|
{"dead_caron", "T", 'Ť'},
|
||||||
|
{"dead_caron", "Z", 'Ž'},
|
||||||
|
{"dead_cedilla", "C", 'Ç'},
|
||||||
|
{"dead_cedilla", "G", 'Ģ'},
|
||||||
|
{"dead_cedilla", "K", 'Ķ'},
|
||||||
|
{"dead_cedilla", "L", 'Ļ'},
|
||||||
|
{"dead_cedilla", "N", 'Ņ'},
|
||||||
|
{"dead_cedilla", "R", 'Ŗ'},
|
||||||
|
{"dead_cedilla", "S", 'Ş'},
|
||||||
|
{"dead_cedilla", "T", 'Ţ'},
|
||||||
|
{"dead_circumflex", "A", 'Â'},
|
||||||
|
{"dead_circumflex", "C", 'Ĉ'},
|
||||||
|
{"dead_circumflex", "E", 'Ê'},
|
||||||
|
{"dead_circumflex", "G", 'Ĝ'},
|
||||||
|
{"dead_circumflex", "H", 'Ĥ'},
|
||||||
|
{"dead_circumflex", "I", 'Î'},
|
||||||
|
{"dead_circumflex", "J", 'Ĵ'},
|
||||||
|
{"dead_circumflex", "O", 'Ô'},
|
||||||
|
{"dead_circumflex", "S", 'Ŝ'},
|
||||||
|
{"dead_circumflex", "U", 'Û'},
|
||||||
|
{"dead_circumflex", "W", 'Ŵ'},
|
||||||
|
{"dead_circumflex", "Y", 'Ŷ'},
|
||||||
|
{"dead_diaeresis", "A", 'Ä'},
|
||||||
|
{"dead_diaeresis", "E", 'Ë'},
|
||||||
|
{"dead_diaeresis", "I", 'Ï'},
|
||||||
|
{"dead_diaeresis", "O", 'Ö'},
|
||||||
|
{"dead_diaeresis", "U", 'Ü'},
|
||||||
|
{"dead_diaeresis", "W", 'Ẅ'},
|
||||||
|
{"dead_diaeresis", "Y", 'Ÿ'},
|
||||||
|
{"dead_doubleacute", "O", 'Ő'},
|
||||||
|
{"dead_doubleacute", "U", 'Ű'},
|
||||||
|
{"dead_grave", "A", 'À'},
|
||||||
|
{"dead_grave", "E", 'È'},
|
||||||
|
{"dead_grave", "I", 'Ì'},
|
||||||
|
{"dead_grave", "O", 'Ò'},
|
||||||
|
{"dead_grave", "U", 'Ù'},
|
||||||
|
{"dead_grave", "W", 'Ẁ'},
|
||||||
|
{"dead_grave", "Y", 'Ỳ'},
|
||||||
|
{"dead_hook", "A", 'Ả'},
|
||||||
|
{"dead_hook", "E", 'Ẻ'},
|
||||||
|
{"dead_hook", "I", 'Ỉ'},
|
||||||
|
{"dead_hook", "O", 'Ỏ'},
|
||||||
|
{"dead_hook", "U", 'Ủ'},
|
||||||
|
{"dead_hook", "Y", 'Ỷ'},
|
||||||
|
{"dead_horn", "O", 'Ơ'},
|
||||||
|
{"dead_horn", "T", 'Þ'},
|
||||||
|
{"dead_horn", "U", 'Ư'},
|
||||||
|
{"dead_macron", "A", 'Ā'},
|
||||||
|
{"dead_macron", "E", 'Ē'},
|
||||||
|
{"dead_macron", "I", 'Ī'},
|
||||||
|
{"dead_macron", "O", 'Ō'},
|
||||||
|
{"dead_macron", "U", 'Ū'},
|
||||||
|
{"dead_ogonek", "A", 'Ą'},
|
||||||
|
{"dead_ogonek", "E", 'Ę'},
|
||||||
|
{"dead_ogonek", "I", 'Į'},
|
||||||
|
{"dead_ogonek", "U", 'Ų'},
|
||||||
|
{"dead_stroke", "D", 'Đ'},
|
||||||
|
{"dead_stroke", "H", 'Ħ'},
|
||||||
|
{"dead_stroke", "L", 'Ł'},
|
||||||
|
{"dead_stroke", "Z", 'Ƶ'},
|
||||||
|
{"dead_tilde", "A", 'Ã'},
|
||||||
|
{"dead_tilde", "E", 'Ẽ'},
|
||||||
|
{"dead_tilde", "I", 'Ĩ'},
|
||||||
|
{"dead_tilde", "N", 'Ñ'},
|
||||||
|
{"dead_tilde", "O", 'Õ'},
|
||||||
|
{"dead_tilde", "U", 'Ũ'},
|
||||||
|
{"dead_tilde", "Y", 'Ỹ'},
|
||||||
|
}
|
||||||
|
|
||||||
|
func newChord(keymap *xkb.Keymap, code, level uint32) Chord {
|
||||||
altGrMask := uint32(1) << keymap.ModGetIndex("Mod5")
|
altGrMask := uint32(1) << keymap.ModGetIndex("Mod5")
|
||||||
ctrlMask := uint32(1) << keymap.ModGetIndex(xkb.ModNameCtrl)
|
ctrlMask := uint32(1) << keymap.ModGetIndex(xkb.ModNameCtrl)
|
||||||
altMask := uint32(1) << keymap.ModGetIndex(xkb.ModNameAlt)
|
altMask := uint32(1) << keymap.ModGetIndex(xkb.ModNameAlt)
|
||||||
shiftMask := uint32(1) << keymap.ModGetIndex(xkb.ModNameShift)
|
shiftMask := uint32(1) << keymap.ModGetIndex(xkb.ModNameShift)
|
||||||
|
|
||||||
|
// TODO support layouts other than 0
|
||||||
|
mask := keymap.KeyGetMod(code, 0, level)
|
||||||
return Chord{
|
return Chord{
|
||||||
false,
|
false,
|
||||||
(mask & altGrMask) != 0,
|
(mask & altGrMask) != 0,
|
||||||
|
@ -493,6 +622,7 @@ func newChord(keymap *xkb.Keymap, mask, code uint32) Chord{
|
||||||
(mask & altMask) != 0,
|
(mask & altMask) != 0,
|
||||||
(mask & shiftMask) != 0,
|
(mask & shiftMask) != 0,
|
||||||
int(code) - 8,
|
int(code) - 8,
|
||||||
|
level,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -502,16 +632,26 @@ func initKeys(keymap *xkb.Keymap) {
|
||||||
numLevels := keymap.NumLevelsForKey(code, 0)
|
numLevels := keymap.NumLevelsForKey(code, 0)
|
||||||
for level := uint32(0); level < numLevels; level++ {
|
for level := uint32(0); level < numLevels; level++ {
|
||||||
for _, sym := range keymap.KeyGetSymsByLevel(code, 0, level) {
|
for _, sym := range keymap.KeyGetSymsByLevel(code, 0, level) {
|
||||||
chord := newChord(keymap, keymap.KeyGetMod(code, 0, level), code)
|
chord := newChord(keymap, code, level)
|
||||||
for name, s := range linuxXSyms {
|
for name, s := range linuxXSyms {
|
||||||
if s == sym {
|
if s == sym {
|
||||||
LinuxKeys[name] = chord
|
if l, ok := LinuxKeys[name]; !ok || level < l.level {
|
||||||
|
LinuxKeys[name] = chord
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
XKeys[xkb.KeysymGetName(sym)] = chord
|
name := xkb.KeysymGetName(sym)
|
||||||
|
if x, ok := XKeys[name]; !ok || level < x.level {
|
||||||
|
XKeys[name] = chord
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
super = XKeys["Super_L"].Key
|
||||||
|
altgr = XKeys["ISO_Level3_Shift"].Key
|
||||||
|
ctrl = XKeys["Control_L"].Key
|
||||||
|
alt = XKeys["Alt_L"].Key
|
||||||
|
shift = XKeys["Shift_L"].Key
|
||||||
}
|
}
|
||||||
|
|
||||||
func getChord(keymap *xkb.Keymap, keysym uint32) Chord {
|
func getChord(keymap *xkb.Keymap, keysym uint32) Chord {
|
||||||
|
@ -521,10 +661,25 @@ func getChord(keymap *xkb.Keymap, keysym uint32) Chord {
|
||||||
for level := uint32(0); level < numLevels; level++ {
|
for level := uint32(0); level < numLevels; level++ {
|
||||||
for _, sym := range keymap.KeyGetSymsByLevel(code, 0, level) {
|
for _, sym := range keymap.KeyGetSymsByLevel(code, 0, level) {
|
||||||
if sym == keysym {
|
if sym == keysym {
|
||||||
return newChord(keymap, keymap.KeyGetMod(code, 0, level), code)
|
return newChord(keymap, code, level)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return Chord{}
|
return Chord{}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getDeadChords(keymap *xkb.Keymap, result rune) (Chord, Chord) {
|
||||||
|
r := unicode.ToUpper(result)
|
||||||
|
for _, d := range DeadKeyResults {
|
||||||
|
if d.Result == r {
|
||||||
|
deadKey := XKeys[d.DeadKey]
|
||||||
|
key := XKeys[d.Key]
|
||||||
|
if deadKey.Key != 0 && key.Key != 0 {
|
||||||
|
key.Shift = d.Result == result
|
||||||
|
return deadKey, key
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Chord{}, Chord{}
|
||||||
|
}
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
rm -vf /usr/local/bin/dotool /usr/local/bin/dotoolc /usr/local/bin/dotoold
|
rm -vf /usr/local/bin/dotool /usr/local/bin/dotoolc /usr/local/bin/dotoold
|
||||||
rm -vf /etc/udev/rules.d/80-dotool.rules
|
rm -vf /etc/udev/rules.d/80-dotool.rules
|
||||||
|
rm -vf /usr/share/man/man1/dotool.1
|
||||||
|
|
Loading…
Add table
Reference in a new issue