chore(repo): baseline du fork input-remapper (upstream v2.2.1)
Le dépôt ne contenait que README.md ; ajout de l'intégralité du code fonctionnel du fork comme socle stable de la branche de release. L'état runtime d'orchestration (.ideai/) est exclu du suivi. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
BIN
readme/architecture.png
Normal file
|
After Width: | Height: | Size: 251 KiB |
176
readme/capabilities.md
Normal file
@ -0,0 +1,176 @@
|
||||
# Capabilities
|
||||
|
||||
A list of example capabilities for reference.
|
||||
|
||||
- [Gamepads](#Gamepads)
|
||||
- [Graphics Tablets](#Graphics-tablets)
|
||||
- [Touchpads](#Touchpads)
|
||||
|
||||
Feel free to extend this list with more devices that are not keyboards
|
||||
and not mice.
|
||||
|
||||
```bash
|
||||
sudo python3
|
||||
```
|
||||
|
||||
```py
|
||||
import evdev
|
||||
evdev.InputDevice('/dev/input/event12').capabilities(verbose=True)
|
||||
```
|
||||
|
||||
## Gamepads
|
||||
|
||||
#### Microsoft X-Box 360 pad
|
||||
|
||||
```py
|
||||
{
|
||||
('EV_SYN', 0): [('SYN_REPORT', 0), ('SYN_CONFIG', 1), ('SYN_DROPPED', 3), ('?', 21)],
|
||||
('EV_KEY', 1): [
|
||||
(['BTN_A', 'BTN_GAMEPAD', 'BTN_SOUTH'], 304),
|
||||
(['BTN_B', 'BTN_EAST'], 305), (['BTN_NORTH', 'BTN_X'], 307),
|
||||
(['BTN_WEST', 'BTN_Y'], 308), ('BTN_TL', 310), ('BTN_TR', 311),
|
||||
('BTN_SELECT', 314), ('BTN_START', 315), ('BTN_MODE', 316),
|
||||
('BTN_THUMBL', 317), ('BTN_THUMBR', 318)
|
||||
],
|
||||
('EV_ABS', 3): [
|
||||
(('ABS_X', 0), AbsInfo(value=1476, min=-32768, max=32767, fuzz=16, flat=128, resolution=0)),
|
||||
(('ABS_Y', 1), AbsInfo(value=366, min=-32768, max=32767, fuzz=16, flat=128, resolution=0)),
|
||||
(('ABS_Z', 2), AbsInfo(value=0, min=0, max=255, fuzz=0, flat=0, resolution=0)),
|
||||
(('ABS_RX', 3), AbsInfo(value=-2950, min=-32768, max=32767, fuzz=16, flat=128, resolution=0)),
|
||||
(('ABS_RY', 4), AbsInfo(value=1973, min=-32768, max=32767, fuzz=16, flat=128, resolution=0)),
|
||||
(('ABS_RZ', 5), AbsInfo(value=0, min=0, max=255, fuzz=0, flat=0, resolution=0)),
|
||||
(('ABS_HAT0X', 16), AbsInfo(value=0, min=-1, max=1, fuzz=0, flat=0, resolution=0)),
|
||||
(('ABS_HAT0Y', 17), AbsInfo(value=0, min=-1, max=1, fuzz=0, flat=0, resolution=0))
|
||||
],
|
||||
('EV_FF', 21): [
|
||||
(['FF_EFFECT_MIN', 'FF_RUMBLE'], 80), ('FF_PERIODIC', 81),
|
||||
(['FF_SQUARE', 'FF_WAVEFORM_MIN'], 88), ('FF_TRIANGLE', 89),
|
||||
('FF_SINE', 90), (['FF_GAIN', 'FF_MAX_EFFECTS'], 96)
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## Graphics tablets
|
||||
|
||||
#### Wacom Intuos 5 M
|
||||
|
||||
Pen
|
||||
|
||||
```py
|
||||
{
|
||||
('EV_SYN', 0): [
|
||||
('SYN_REPORT', 0), ('SYN_CONFIG', 1),
|
||||
('SYN_MT_REPORT', 2), ('SYN_DROPPED', 3), ('?', 4)
|
||||
],
|
||||
('EV_KEY', 1): [
|
||||
(['BTN_LEFT', 'BTN_MOUSE'], 272), ('BTN_RIGHT', 273), ('BTN_MIDDLE', 274),
|
||||
('BTN_SIDE', 275), ('BTN_EXTRA', 276), (['BTN_DIGI', 'BTN_TOOL_PEN'], 320),
|
||||
('BTN_TOOL_RUBBER', 321), ('BTN_TOOL_BRUSH', 322), ('BTN_TOOL_PENCIL', 323),
|
||||
('BTN_TOOL_AIRBRUSH', 324), ('BTN_TOOL_MOUSE', 326), ('BTN_TOOL_LENS', 327),
|
||||
('BTN_TOUCH', 330), ('BTN_STYLUS', 331), ('BTN_STYLUS2', 332)
|
||||
],
|
||||
('EV_REL', 2): [('REL_WHEEL', 8)],
|
||||
('EV_ABS', 3): [
|
||||
(('ABS_X', 0), AbsInfo(value=0, min=0, max=44704, fuzz=4, flat=0, resolution=200)),
|
||||
(('ABS_Y', 1), AbsInfo(value=0, min=0, max=27940, fuzz=4, flat=0, resolution=200)),
|
||||
(('ABS_Z', 2), AbsInfo(value=0, min=-900, max=899, fuzz=0, flat=0, resolution=287)),
|
||||
(('ABS_RZ', 5), AbsInfo(value=0, min=-900, max=899, fuzz=0, flat=0, resolution=287)),
|
||||
(('ABS_THROTTLE', 6), AbsInfo(value=0, min=-1023, max=1023, fuzz=0, flat=0, resolution=0)),
|
||||
(('ABS_WHEEL', 8), AbsInfo(value=0, min=0, max=1023, fuzz=0, flat=0, resolution=0)),
|
||||
(('ABS_PRESSURE', 24), AbsInfo(value=0, min=0, max=2047, fuzz=0, flat=0, resolution=0)),
|
||||
(('ABS_DISTANCE', 25), AbsInfo(value=0, min=0, max=63, fuzz=1, flat=0, resolution=0)),
|
||||
(('ABS_TILT_X', 26), AbsInfo(value=0, min=-64, max=63, fuzz=1, flat=0, resolution=57)),
|
||||
(('ABS_TILT_Y', 27), AbsInfo(value=0, min=-64, max=63, fuzz=1, flat=0, resolution=57)),
|
||||
(('ABS_MISC', 40), AbsInfo(value=0, min=0, max=0, fuzz=0, flat=0, resolution=0))
|
||||
],
|
||||
('EV_MSC', 4): [('MSC_SERIAL', 0)]
|
||||
}
|
||||
```
|
||||
|
||||
Pad
|
||||
|
||||
```py
|
||||
{
|
||||
('EV_SYN', 0): [('SYN_REPORT', 0), ('SYN_CONFIG', 1), ('SYN_DROPPED', 3)],
|
||||
('EV_KEY', 1): [
|
||||
(['BTN_0', 'BTN_MISC'], 256), ('BTN_1', 257), ('BTN_2', 258),
|
||||
('BTN_3', 259), ('BTN_4', 260), ('BTN_5', 261), ('BTN_6', 262),
|
||||
('BTN_7', 263), ('BTN_8', 264), ('BTN_STYLUS', 331)],
|
||||
('EV_ABS', 3): [
|
||||
(('ABS_X', 0), AbsInfo(value=0, min=0, max=1, fuzz=0, flat=0, resolution=0)),
|
||||
(('ABS_Y', 1), AbsInfo(value=0, min=0, max=1, fuzz=0, flat=0, resolution=0)),
|
||||
(('ABS_WHEEL', 8), AbsInfo(value=0, min=0, max=71, fuzz=0, flat=0, resolution=0)),
|
||||
(('ABS_MISC', 40), AbsInfo(value=0, min=0, max=0, fuzz=0, flat=0, resolution=0))
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
#### 10 inch PenTablet
|
||||
|
||||
```py
|
||||
{
|
||||
('EV_SYN', 0): [('SYN_REPORT', 0), ('SYN_CONFIG', 1), ('SYN_DROPPED', 3), ('?', 4)],
|
||||
('EV_KEY', 1): [(['BTN_DIGI', 'BTN_TOOL_PEN'], 320), ('BTN_TOUCH', 330), ('BTN_STYLUS', 331)],
|
||||
('EV_ABS', 3): [
|
||||
(('ABS_X', 0), AbsInfo(value=41927, min=0, max=50794, fuzz=0, flat=0, resolution=200)),
|
||||
(('ABS_Y', 1), AbsInfo(value=11518, min=0, max=30474, fuzz=0, flat=0, resolution=200)),
|
||||
(('ABS_PRESSURE', 24), AbsInfo(value=0, min=0, max=8191, fuzz=0, flat=0, resolution=0)),
|
||||
(('ABS_TILT_X', 26), AbsInfo(value=0, min=-127, max=127, fuzz=0, flat=0, resolution=0)),
|
||||
(('ABS_TILT_Y', 27), AbsInfo(value=0, min=-127, max=127, fuzz=0, flat=0, resolution=0))
|
||||
],
|
||||
('EV_MSC', 4): [('MSC_SCAN', 4)]
|
||||
}
|
||||
```
|
||||
|
||||
10 inch PenTablet Mouse
|
||||
|
||||
```py
|
||||
{
|
||||
('EV_SYN', 0): [
|
||||
('SYN_REPORT', 0), ('SYN_CONFIG', 1), ('SYN_MT_REPORT', 2),
|
||||
('SYN_DROPPED', 3), ('?', 4)
|
||||
],
|
||||
('EV_KEY', 1): [
|
||||
(['BTN_LEFT', 'BTN_MOUSE'], 272), ('BTN_RIGHT', 273),
|
||||
('BTN_MIDDLE', 274), ('BTN_SIDE', 275), ('BTN_EXTRA', 276),
|
||||
('BTN_TOUCH', 330)
|
||||
],
|
||||
('EV_REL', 2): [
|
||||
('REL_X', 0), ('REL_Y', 1), ('REL_HWHEEL', 6), ('REL_WHEEL', 8),
|
||||
('REL_WHEEL_HI_RES', 11), ('REL_HWHEEL_HI_RES', 12)
|
||||
],
|
||||
('EV_ABS', 3): [
|
||||
(('ABS_X', 0), AbsInfo(value=0, min=0, max=32767, fuzz=0, flat=0, resolution=0)),
|
||||
(('ABS_Y', 1), AbsInfo(value=0, min=0, max=32767, fuzz=0, flat=0, resolution=0)),
|
||||
(('ABS_PRESSURE', 24), AbsInfo(value=0, min=0, max=2047, fuzz=0, flat=0, resolution=0))
|
||||
],
|
||||
('EV_MSC', 4): [('MSC_SCAN', 4)]
|
||||
}
|
||||
```
|
||||
|
||||
## Touchpads
|
||||
|
||||
#### ThinkPad E590 SynPS/2 Synaptics TouchPad
|
||||
|
||||
```py
|
||||
{
|
||||
('EV_SYN', 0): [('SYN_REPORT', 0), ('SYN_CONFIG', 1), ('SYN_DROPPED', 3)],
|
||||
('EV_KEY', 1): [
|
||||
(['BTN_LEFT', 'BTN_MOUSE'], 272), ('BTN_TOOL_FINGER', 325),
|
||||
('BTN_TOOL_QUINTTAP', 328), ('BTN_TOUCH', 330),
|
||||
('BTN_TOOL_DOUBLETAP', 333), ('BTN_TOOL_TRIPLETAP', 334),
|
||||
('BTN_TOOL_QUADTAP', 335)
|
||||
],
|
||||
('EV_ABS', 3): [
|
||||
(('ABS_X', 0), AbsInfo(value=3111, min=1266, max=5678, fuzz=0, flat=0, resolution=0)),
|
||||
(('ABS_Y', 1), AbsInfo(value=2120, min=1162, max=4694, fuzz=0, flat=0, resolution=0)),
|
||||
(('ABS_PRESSURE', 24), AbsInfo(value=0, min=0, max=255, fuzz=0, flat=0, resolution=0)),
|
||||
(('ABS_TOOL_WIDTH', 28), AbsInfo(value=0, min=0, max=15, fuzz=0, flat=0, resolution=0)),
|
||||
(('ABS_MT_SLOT', 47), AbsInfo(value=0, min=0, max=1, fuzz=0, flat=0, resolution=0)),
|
||||
(('ABS_MT_POSITION_X', 53), AbsInfo(value=0, min=1266, max=5678, fuzz=0, flat=0, resolution=0)),
|
||||
(('ABS_MT_POSITION_Y', 54), AbsInfo(value=0, min=1162, max=4694, fuzz=0, flat=0, resolution=0)),
|
||||
(('ABS_MT_TRACKING_ID', 57), AbsInfo(value=0, min=0, max=65535, fuzz=0, flat=0, resolution=0)),
|
||||
(('ABS_MT_PRESSURE', 58), AbsInfo(value=0, min=0, max=255, fuzz=0, flat=0, resolution=0))
|
||||
]
|
||||
}
|
||||
```
|
||||
23
readme/coverage.svg
Normal file
@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="99" height="20">
|
||||
<linearGradient id="b" x2="0" y2="100%">
|
||||
<stop offset="0" stop-color="#bbb" stop-opacity=".1"/>
|
||||
<stop offset="1" stop-opacity=".1"/>
|
||||
</linearGradient>
|
||||
<mask id="anybadge_1">
|
||||
<rect width="99" height="20" rx="3" fill="#fff"/>
|
||||
</mask>
|
||||
<g mask="url(#anybadge_1)">
|
||||
<path fill="#555" d="M0 0h65v20H0z"/>
|
||||
<path fill="#4C1" d="M65 0h34v20H65z"/>
|
||||
<path fill="url(#b)" d="M0 0h99v20H0z"/>
|
||||
</g>
|
||||
<g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11">
|
||||
<text x="33.5" y="15" fill="#010101" fill-opacity=".3">coverage</text>
|
||||
<text x="32.5" y="14">coverage</text>
|
||||
</g>
|
||||
<g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11">
|
||||
<text x="83.0" y="15" fill="#010101" fill-opacity=".3">88%</text>
|
||||
<text x="82.0" y="14">88%</text>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.0 KiB |
156
readme/development.md
Normal file
@ -0,0 +1,156 @@
|
||||
Development
|
||||
===========
|
||||
|
||||
Contributions are very welcome, I will gladly review and discuss any merge requests.
|
||||
If you have questions about the code and architecture, feel free to
|
||||
[open an issue](https://github.com/sezanzeb/input-remapper/issues).
|
||||
This file should give an overview about some internals of input-remapper.
|
||||
|
||||
All pull requests will at some point require unittests (see below for more info).
|
||||
The code coverage may only be improved, not decreased. It also has to be mostly
|
||||
compliant with pylint.
|
||||
|
||||
Running
|
||||
-------
|
||||
|
||||
To quickly restart input-remapper without pkexec prompts, you can use
|
||||
|
||||
```bash
|
||||
sudo pkill -f input-remapper && sudo input-remapper-reader-service -d & sudo input-remapper-service -d & input-remapper-gtk -d
|
||||
```
|
||||
|
||||
Linting
|
||||
-------
|
||||
|
||||
```bash
|
||||
mypy inputremapper # find typing issues
|
||||
black . # auto-format all code in-place
|
||||
pip install pylint-pydantic --user # https://github.com/fcfangcc/pylint-pydantic
|
||||
pylint inputremapper # get a code quality rating from pylint
|
||||
```
|
||||
|
||||
Pylint gives lots of great advice on how to write better python code and even detects
|
||||
errors. Mypy checks for typing errors. Use black to format it.
|
||||
|
||||
Automated tests
|
||||
---------------
|
||||
|
||||
You should be able to use your IDEs built in python unittest features to run tests.
|
||||
But you can also run them from your console:
|
||||
|
||||
```bash
|
||||
pip install psutil # https://github.com/giampaolo/psutil
|
||||
sudo pkill -f input-remapper
|
||||
python3 -m unittest discover -s ./tests/
|
||||
python3 -m unittest tests/unit/test_daemon.py
|
||||
python3 -m unittest tests.unit.test_ipc.TestPipe -k "test_pipe" -f
|
||||
# See `python -m unittest -h` for more.
|
||||
```
|
||||
|
||||
Don't use your computer during integration tests to avoid interacting with the gui,
|
||||
which might make tests fail.
|
||||
|
||||
To read events for manual testing, `evtest` is very helpful.
|
||||
Add `-d` to `input-remapper-gtk` to get debug output.
|
||||
|
||||
Writing Tests
|
||||
-------------
|
||||
|
||||
Tests are in https://github.com/sezanzeb/input-remapper/tree/main/tests
|
||||
|
||||
Make sure to use the `@test_setup` decorator. Look for other tests that did something
|
||||
vaguely similar for inspiration. For example, copy one of the macro test files
|
||||
and modify it if you write a new macro.
|
||||
|
||||
If you have difficulty running your tests locally, github will run them for you when
|
||||
you create a new pull request.
|
||||
|
||||
Scripts
|
||||
-------
|
||||
|
||||
To automate some of the development tasks, you can use the
|
||||
[setup.sh](/scripts/setup.sh) script. The script avoids using `pip` for installation.
|
||||
Instead, it uses either your local `python3` in your virtual env, or using
|
||||
`/usr/bin/python3` explicitly. For more information run
|
||||
|
||||
```
|
||||
scripts/setup.sh help
|
||||
```
|
||||
|
||||
Advice
|
||||
------
|
||||
|
||||
Do not use GTKs `foreach` methods, because when the function fails it just freezes up
|
||||
completely. Use `get_children()` and iterate over it with regular python `for` loops.
|
||||
Use `gtk_iteration()` in tests when interacting with GTK methods to trigger events to
|
||||
be emitted.
|
||||
|
||||
Do not do `from evdev import list_devices; list_devices()`, and instead do
|
||||
`import evdev; evdev.list_devices()`. The first variant cannot be easily patched in
|
||||
tests (there are ways, but as far as I can tell it has to be configured individually
|
||||
for each source-file/module). The second option allows for patches to be defiend in
|
||||
one central places. Importing `KEY_*`, `BTN_*`, etc. constants via `from evdev` is
|
||||
fine.
|
||||
|
||||
Releasing
|
||||
---------
|
||||
|
||||
ssh/login into a debian/ubuntu environment
|
||||
|
||||
```bash
|
||||
scripts/build-deb.sh
|
||||
```
|
||||
|
||||
This will generate `dist/input-remapper-2.2.1.deb`
|
||||
|
||||
Badges
|
||||
------
|
||||
|
||||
```bash
|
||||
# https://github.com/nedbat/coveragepy https://github.com/giampaolo/psutil
|
||||
pip install coverage anybadge pylint psutil
|
||||
sudo pkill -f input-remapper
|
||||
# Make sure input-remapper is uninstalled, then install it editable (without sudo
|
||||
# should be fine), so that the path for the coverage collection is correct.
|
||||
# Use `find /usr/ -iname "*inputremapper*"` to check if it is uninstalled.
|
||||
pip install -e .
|
||||
./scripts/badges.sh
|
||||
```
|
||||
|
||||
New badges, if needed, will be created in `readme/` and they just need to be commited.
|
||||
|
||||
Translations
|
||||
------------
|
||||
|
||||
To regenerate the `po/input-remapper.pot` file, run
|
||||
|
||||
```bash
|
||||
xgettext -k --keyword=translatable --sort-output -o po/input-remapper.pot data/input-remapper.glade
|
||||
xgettext --keyword=_ -L Python --sort-output -jo po/input-remapper.pot inputremapper/configs/mapping.py inputremapper/gui/*.py inputremapper/gui/components/*.py
|
||||
```
|
||||
|
||||
This is the template file that you can copy to fill in the translations. Also create a
|
||||
corresponding symlink, like `ln -s it_IT.po it.po`, because some environments expect
|
||||
different names, apparently. See https://github.com/sezanzeb/input-remapper/tree/main/po
|
||||
for examples.
|
||||
|
||||
Architecture
|
||||
------------
|
||||
|
||||
There is a miro board describing input-remappers architecture:
|
||||
|
||||
https://miro.com/app/board/uXjVPLa8ilM=/?share_link_id=272180986764
|
||||
|
||||

|
||||
|
||||
Resources
|
||||
---------
|
||||
|
||||
- [Guidelines for device capabilities](https://www.kernel.org/doc/Documentation/input/event-codes.txt)
|
||||
- [PyGObject API Reference](https://lazka.github.io/pgi-docs/)
|
||||
- [python-evdev](https://python-evdev.readthedocs.io/en/stable/)
|
||||
- [Python Unix Domain Sockets](https://pymotw.com/2/socket/uds.html)
|
||||
- [GNOME HIG](https://developer.gnome.org/hig/stable/)
|
||||
- [GtkSource Example](https://github.com/wolfthefallen/py-GtkSourceCompletion-example)
|
||||
- [linux/input-event-codes.h](https://github.com/torvalds/linux/blob/master/include/uapi/linux/input-event-codes.h)
|
||||
- [Screenshot Guidelines](https://www.freedesktop.org/software/appstream/docs/chap-Quickstart.html)
|
||||
190
readme/examples.md
Normal file
@ -0,0 +1,190 @@
|
||||
# Examples
|
||||
|
||||
Examples for particular devices and/or use cases:
|
||||
|
||||
## Event Names
|
||||
|
||||
- Alphanumeric `a` to `z` and `0` to `9`
|
||||
- Modifiers `Alt_L` `Control_L` `Control_R` `Shift_L` `Shift_R`
|
||||
- Mouse buttons `BTN_LEFT` `BTN_RIGHT` `BTN_MIDDLE` `BTN_SIDE` ...
|
||||
- Multimedia keys `KEY_NEXTSONG` `KEY_PLAYPAUSE` `XF86AudioMicMute` ...
|
||||
|
||||
Mouse movements have to be performed by macros. See below.
|
||||
|
||||
## Short Macro Examples
|
||||
|
||||
- `key(BTN_LEFT)` a single mouse-click
|
||||
- `key(1).key(2)` 1, 2
|
||||
- `wheel(down, 10)` `wheel(up, 10)` Scroll while the input is pressed.
|
||||
- `mouse(left, 5)` `mouse(right, 2)` `mouse(up, 1)` `mouse(down, 3)` Move the cursor while the input is pressed.
|
||||
- `repeat(3, key(a).w(500))` a, a, a with 500ms pause
|
||||
- `modify(Control_L, key(a).key(x))` CTRL + a, CTRL + x
|
||||
- `key(1).hold(key(2)).key(3)` writes 1 2 2 ... 2 2 3 while the key is pressed
|
||||
- `event(EV_REL, REL_X, 10)` moves the mouse cursor 10px to the right
|
||||
- `mouse(right, 4)` which keeps moving the mouse while pressed
|
||||
- `wheel(down, 1)` keeps scrolling down while held
|
||||
- `set(foo, 1)` set ["foo"](https://en.wikipedia.org/wiki/Metasyntactic_variable) to 1
|
||||
- `if_eq($foo, 1, key(x), key(y))` if "foo" is 1, write x, otherwise y
|
||||
- `hold()` does nothing as long as your key is held down
|
||||
- `hold_keys(a)` holds down "a" as long as the key is pressed, just like a regular non-macro mapping
|
||||
- `if_tap(key(a), key(b))` writes a if the key is tapped, otherwise b
|
||||
- `if_tap(key(a), key(b), 1000)` writes a if the key is released within a second, otherwise b
|
||||
- `if_single(key(a), key(b))` writes b if another key is pressed, or a if the key is released
|
||||
and no other key was pressed in the meantime.
|
||||
- `if_tap(if_tap(key(a), key(b)), key(c))` "a" if tapped twice, "b" if tapped once and "c" if
|
||||
held down long enough
|
||||
- `key_up(a).wait(1000).key_down(a)` keeps a pressed for one second
|
||||
- `hold_keys(Control_L, a)` holds down those two keys
|
||||
- `key(BTN_LEFT).wait(100).key(BTN_LEFT)` a double-click
|
||||
|
||||
## Double Tap
|
||||
|
||||
```
|
||||
if_tap(
|
||||
if_tap(
|
||||
key(a),
|
||||
key(c)
|
||||
),
|
||||
key(b)
|
||||
)
|
||||
```
|
||||
|
||||
- press twice: a
|
||||
- press and hold: b
|
||||
- press and release: c
|
||||
|
||||
## Combinations Spanning Multiple Devices
|
||||
|
||||
For regular combinations on only single devices it is not required to
|
||||
configure macros. See [readme/usage.md](usage.md#combinations).
|
||||
|
||||
**Keyboard** `space` `set(foo, 1).hold_keys(space).set(foo, 0)`
|
||||
|
||||
**Mouse** `middle` `if_eq($foo, 1, hold_keys(a), hold_keys(BTN_MIDDLE))`
|
||||
|
||||
Apply both presets. If you press space on your keyboard, it will write a
|
||||
space exactly like it used to. If you hold down space and press the middle
|
||||
button of your mouse, it will write "a" instead. If you just press the
|
||||
middle button of your mouse it behaves like a regular middle mouse button.
|
||||
|
||||
**Explanation**
|
||||
|
||||
`hold_keys(space)` makes your key work exactly like if it was mapped to "space".
|
||||
It will inject a key-down event if you press it, does nothing as long you
|
||||
hold your key down, and injects a key-up event after releasing.
|
||||
`set(foo, 1).set(foo, 0)` sets "foo" to 1 and then sets "foo" to 0.
|
||||
`set` and `if_eq` work on shared memory, so all injections will see your
|
||||
variables. Combine both to get a key that works like a normal key, but that also
|
||||
works as a modifier for other keys of other devices. `if_eq($foo, 1, ..., ...)`
|
||||
runs the first param if foo is 1, or the second one if foo is not 1.
|
||||
|
||||
|
||||
## Scroll and Click on a Keyboard
|
||||
|
||||
Seldom used PrintScreen, ScrollLock and Pause keys on keyboards with TKL (ten key
|
||||
less) layout are easily accessible by the right hand thanks to the missing
|
||||
numeric block, so they can be mapped to mouse scroll and click events:
|
||||
|
||||
- Print: `wheel(up, 1)`
|
||||
- Pause: `wheel(down, 1)`
|
||||
- Scroll Lock: `BTN_LEFT`
|
||||
- Menu: `BTN_RIGHT`
|
||||
- F12: `KEY_LEFTCTRL + w`
|
||||
|
||||
In contrast to libinput's `ScrollMethod` `button` which requires the scroll
|
||||
button to belong to the same (mouse) device, clicking and scrolling events mapped
|
||||
to a keyboard key can fully cooperate with events from a real mouse, e.g.
|
||||
drag'n'drop by holding a (mapped) keyboard key and moving the cursor by mouse.
|
||||
|
||||
Mapping the scrolling to a keyboard key is also useful for trackballs without
|
||||
a scroll ring.
|
||||
|
||||
In contrast to a real scroll wheel, holding a key which has mouse wheel event
|
||||
mapped produces linear auto-repeat, without any acceleration. Using a PageDown
|
||||
key for fast scrolling requires only a small adjustment of the right hand
|
||||
position.
|
||||
|
||||
## Scroll on a 3-Button Mouse
|
||||
|
||||
Cheap 3-button mouse without a scroll wheel can scroll using the middle button:
|
||||
|
||||
- Button MIDDLE: `wheel(down, 1)`
|
||||
|
||||
## Click on Lower Buttons of Trackball
|
||||
|
||||
Trackball with 4 buttons (e.g. Kensington Wireless Expert Mouse) with lower 2
|
||||
buttons by default assigned to middle and side button can be remapped to provide
|
||||
left and right click on both the upper and lower pairs of buttons to avoid
|
||||
readjusting a hand after moving the cursor down:
|
||||
|
||||
- Button MIDDLE: BTN_LEFT
|
||||
- Button SIDE: BTN_RIGHT
|
||||
|
||||
## Scroll on Foot Pedals
|
||||
|
||||
While Kinesis Savant Elite 2 foot pedals can be programmed to emit key press or
|
||||
mouse click events, they cannot emit scroll events themselves. Using the pedals
|
||||
for scrolling while standing at a standing desk is possible thanks to remapping:
|
||||
|
||||
- Button LEFT: `wheel(up, 1)`
|
||||
- Button RIGHT: `wheel(down, 1)`
|
||||
|
||||
## Gamepads
|
||||
|
||||
Joystick movements will be translated to mouse movements, while the second
|
||||
joystick acts as a mouse wheel. You can swap this in the user interface.
|
||||
All buttons, triggers and D-Pads can be mapped to keycodes and macros.
|
||||
|
||||
The D-Pad can be mapped to W, A, S, D for example, to run around in games,
|
||||
while the joystick turns the view (depending on the game).
|
||||
|
||||
Tested with the XBOX 360 Gamepad. On Ubuntu, gamepads worked better in
|
||||
Wayland than with X11.
|
||||
|
||||
## Sequence of Keys with Modifiers
|
||||
|
||||
Alt+TAB, Enter, Alt+TAB:
|
||||
|
||||
```
|
||||
modify(Alt_L, key(tab)).wait(250).
|
||||
key(KP_Enter).key(key_UP).wait(150).
|
||||
modify(Alt_L, key(tab))
|
||||
```
|
||||
|
||||
## Home Row Mods
|
||||
|
||||
See https://precondition.github.io/home-row-mods#home-row-mods-order
|
||||
|
||||
- a: `mod_tap(a, Super_L)`
|
||||
- s: `mod_tap(s, Alt_L)`
|
||||
- d: `mod_tap(d, Shift_L)`
|
||||
- f: `mod_tap(f, Control_L)`
|
||||
|
||||
## Emitting Unavailable Symbols
|
||||
|
||||
For example Japanese letters without overwriting any existing key
|
||||
of your system-layout. Only works in X11.
|
||||
|
||||
```
|
||||
xmodmap -pke > keyboard_layout
|
||||
mousepad keyboard_layout &
|
||||
```
|
||||
|
||||
Find a code that is not mapped to anything, for example `keycode 93 = `,
|
||||
and map it like `keycode 93 = kana_YA`. See [this gist](https://gist.github.com/sezanzeb/e29bae637b8a799ccf2490b8537487df)
|
||||
for available symbols.
|
||||
|
||||
```
|
||||
xmodmap keyboard_layout
|
||||
input-remapper-gtk
|
||||
```
|
||||
|
||||
"kana_YA" should be in the dropdown of available symbols now. Map it
|
||||
to a key and press apply. Now run
|
||||
|
||||
```
|
||||
xmodmap keyboard_layout
|
||||
```
|
||||
|
||||
again for the injection to use that xmodmap as well. It should be possible
|
||||
to write "ヤ" now when pressing the key.
|
||||
431
readme/macros.md
Normal file
@ -0,0 +1,431 @@
|
||||
# Macros
|
||||
|
||||
input-remapper comes with an optional custom macro language with support for cross-device
|
||||
variables, conditions and named parameters.
|
||||
|
||||
Syntax errors are shown in the UI on save. Each `key` function adds a short delay of 10ms
|
||||
between key-down, key-up and at the end. See [usage.md](usage.md#configuration-files)
|
||||
for more info.
|
||||
|
||||
Macros are written into the same text field, that would usually contain the output symbol.
|
||||
|
||||
Bear in mind that anti-cheat software might detect macros in games.
|
||||
|
||||
### key
|
||||
|
||||
> Acts like a pressed key. All names that are available in regular mappings can be used
|
||||
> here.
|
||||
>
|
||||
> ```ts
|
||||
> key(symbol: str)
|
||||
> ```
|
||||
>
|
||||
> Examples:
|
||||
>
|
||||
> ```ts
|
||||
> key(symbol=KEY_A)
|
||||
> key(b).key(space)
|
||||
> ```
|
||||
|
||||
### key_down and key_up
|
||||
|
||||
> Inject the press/down/1 and release/up/0 events individually with those macros.
|
||||
>
|
||||
> ```ts
|
||||
> key_down(symbol: str)
|
||||
> key_up(symbol: str)
|
||||
> ```
|
||||
>
|
||||
> Examples:
|
||||
>
|
||||
> ```ts
|
||||
> key_down(KEY_A)
|
||||
> key_up(KEY_B)
|
||||
> ```
|
||||
|
||||
### wait
|
||||
|
||||
> Waits in milliseconds before continuing the macro. If the max_time argument is
|
||||
> provided, it will randomize the time between time and max_time.
|
||||
>
|
||||
> ```ts
|
||||
> wait(time: int, max_time: int | None)
|
||||
> ```
|
||||
>
|
||||
> Examples:
|
||||
>
|
||||
> ```ts
|
||||
> wait(time=100)
|
||||
> wait(500)
|
||||
> wait(10, 1000)
|
||||
> ```
|
||||
|
||||
### repeat
|
||||
|
||||
> Repeats the execution of the second parameter a few times
|
||||
>
|
||||
> ```ts
|
||||
> repeat(repeats: int, macro: Macro)
|
||||
> ```
|
||||
>
|
||||
> Examples:
|
||||
>
|
||||
> ```ts
|
||||
> repeat(1, key(KEY_A))
|
||||
> repeat(repeats=2, key(space))
|
||||
> ```
|
||||
|
||||
### toggle
|
||||
|
||||
> Repeats the execution of the parameter until activated again
|
||||
>
|
||||
> ```ts
|
||||
> toggle(macro: Macro)
|
||||
> ```
|
||||
>
|
||||
> Examples:
|
||||
>
|
||||
> ```ts
|
||||
> toggle(key(KEY_A))
|
||||
> ```
|
||||
|
||||
### modify
|
||||
|
||||
> Holds a modifier while executing the second parameter
|
||||
>
|
||||
> ```ts
|
||||
> modify(modifier: str, macro: Macro)
|
||||
> ```
|
||||
>
|
||||
> Examples:
|
||||
>
|
||||
> ```ts
|
||||
> modify(Control_L, key(a).key(x))
|
||||
> ```
|
||||
|
||||
### mod_tap
|
||||
|
||||
> If an input is held down long enough, then it turns into a modifier for all keys
|
||||
> that came and come afterwards.
|
||||
>
|
||||
> You can use this to create home row mods for example.
|
||||
>
|
||||
> Behaves similar to the Mod-Tap feature of QMK.
|
||||
>
|
||||
> ```ts
|
||||
> mod_tap(
|
||||
> default: str,
|
||||
> modifier: str,
|
||||
> tapping_term: int
|
||||
> )
|
||||
> ```
|
||||
>
|
||||
> Examples:
|
||||
>
|
||||
> ```ts
|
||||
> mod_tap(a, Shift_L)
|
||||
> mod_tap(s, Control_L, 300)
|
||||
> ```
|
||||
|
||||
### hold_keys
|
||||
|
||||
> Holds down all the provided symbols like a combination, and releases them when the
|
||||
> actual key on your keyboard is released.
|
||||
>
|
||||
> An arbitrary number of symbols can be provided.
|
||||
>
|
||||
> When provided with a single key, it will behave just like a regular keyboard key.
|
||||
>
|
||||
> ```ts
|
||||
> hold_keys(*symbols: str)
|
||||
> ```
|
||||
>
|
||||
> Examples:
|
||||
>
|
||||
> ```ts
|
||||
> hold_keys(KEY_B)
|
||||
> hold_keys(KEY_LEFTCTRL, KEY_A)
|
||||
> hold_keys(Control_L, Alt_L, Delete)
|
||||
> set(foo, KEY_A).hold_keys($foo)
|
||||
> ```
|
||||
|
||||
### hold
|
||||
|
||||
> Runs the child macro repeatedly as long as the input is pressed.
|
||||
>
|
||||
> ```ts
|
||||
> hold(macro: Macro)
|
||||
> ```
|
||||
>
|
||||
> Examples:
|
||||
>
|
||||
> ```ts
|
||||
> hold(key(space))
|
||||
> ```
|
||||
|
||||
### mouse
|
||||
|
||||
> Moves the mouse cursor
|
||||
>
|
||||
> If the fractional `acceleration` value is provided then the cursor will accelerate
|
||||
> from zero to a maximum speed of `speed`.
|
||||
>
|
||||
> ```ts
|
||||
> mouse(
|
||||
> direction: up | down | left | right,
|
||||
> speed: int,
|
||||
> acceleration: int | float
|
||||
> )
|
||||
> ```
|
||||
>
|
||||
> Examples:
|
||||
>
|
||||
> ```ts
|
||||
> mouse(up, 1)
|
||||
> mouse(left, 2)
|
||||
> mouse(down, 10, 0.05)
|
||||
> ```
|
||||
|
||||
### mouse_xy
|
||||
|
||||
> Moves the mouse cursor in both x and y direction.
|
||||
>
|
||||
> If the fractional `acceleration` value is provided then the cursor will accelerate
|
||||
> from zero to the maximum specified x and y speeds.
|
||||
>
|
||||
> ```ts
|
||||
> mouse(
|
||||
> x: int | float,
|
||||
> y: int | float,
|
||||
> acceleration: int | float
|
||||
> )
|
||||
> ```
|
||||
>
|
||||
> Examples:
|
||||
>
|
||||
> ```ts
|
||||
> mouse_xy(x=10, y=20)
|
||||
> mouse_xy(-5, -1, 0.01)
|
||||
> mouse_xy(x=10, acceleration=0.05)
|
||||
> ```
|
||||
|
||||
### wheel
|
||||
|
||||
> Injects scroll wheel events
|
||||
>
|
||||
> ```ts
|
||||
> wheel(
|
||||
> direction: up | down | left | right,
|
||||
> speed: int
|
||||
> )
|
||||
> ```
|
||||
>
|
||||
> Examples:
|
||||
>
|
||||
> ```ts
|
||||
> mouse(up, 10)
|
||||
> mouse(left, 20)
|
||||
> ```
|
||||
|
||||
### event
|
||||
|
||||
> Writes an event. Examples for `type`, `code` and `value` can be found via the
|
||||
> `sudo evtest` command. Also check out [input-event-codes.h](https://github.com/torvalds/linux/blob/master/include/uapi/linux/input-event-codes.h).
|
||||
> `EV_KEY` for keys, `EV_REL` for mouse movements, `EV_ABS` for gamepad events among
|
||||
> others.
|
||||
>
|
||||
> ```ts
|
||||
> event(
|
||||
> type: str | int,
|
||||
> code: str | int,
|
||||
> value: int
|
||||
> )
|
||||
> ```
|
||||
>
|
||||
> Examples:
|
||||
>
|
||||
> ```ts
|
||||
> event(EV_KEY, KEY_A, 1)
|
||||
> event(EV_REL, REL_X, -10)
|
||||
> event(2, 8, 1)
|
||||
> ```
|
||||
|
||||
### set
|
||||
|
||||
> Set a variable to a value. This variable and its value is available in all injection
|
||||
> processes.
|
||||
>
|
||||
> Variables can be used in function arguments by adding a `$` in front of their name:
|
||||
> `repeat($foo, key(KEY_A))`
|
||||
>
|
||||
> Their values are available for other injections/devices as well, so you can make them
|
||||
> interact with each other. In other words, using `set` on a keyboard and `if_eq` with
|
||||
> the previously used variable name on a mouse will work.
|
||||
>
|
||||
> ```ts
|
||||
> set(variable: str, value: str | int)
|
||||
> ```
|
||||
>
|
||||
> Examples:
|
||||
>
|
||||
> ```ts
|
||||
> set(foo, 1)
|
||||
> set(foo, "qux")
|
||||
> ```
|
||||
|
||||
### add
|
||||
|
||||
> Adds a number fo a variable.
|
||||
>
|
||||
> ```ts
|
||||
> add(variable: str, value: int)
|
||||
> ```
|
||||
>
|
||||
> Examples:
|
||||
>
|
||||
> ```ts
|
||||
> set(a, 1).add(a, 2).if_eq($a, 3, key(x), key(y))
|
||||
> ```
|
||||
|
||||
### if_eq
|
||||
|
||||
> Compare two values and run different macros depending on the outcome.
|
||||
>
|
||||
> ```ts
|
||||
> if_eq(
|
||||
> value_1: str | int,
|
||||
> value_2: str | int,
|
||||
> then: Macro | None,
|
||||
> else: Macro | None
|
||||
> )
|
||||
> ```
|
||||
>
|
||||
> Examples:
|
||||
>
|
||||
> ```ts
|
||||
> set(a, 1).if_eq($a, 1, key(KEY_A), key(KEY_B))
|
||||
> set(a, 1).set(b, 1).if_eq($a, $b, else=key(KEY_B).key(KEY_C))
|
||||
> set(a, "foo").if_eq("foo", $a, key(KEY_A))
|
||||
> set(a, 1).if_eq($a, 1, None, key(KEY_B))
|
||||
> ```
|
||||
|
||||
### if_capslock
|
||||
|
||||
> Run the first macro if your capslock is on, otherwise the second.
|
||||
>
|
||||
> ```ts
|
||||
> if_capslock(
|
||||
> then: Macro | None,
|
||||
> else: Macro | None
|
||||
> )
|
||||
> ```
|
||||
>
|
||||
> Examples:
|
||||
>
|
||||
> ```ts
|
||||
> if_capslock(
|
||||
> then=hold_keys(KEY_3),
|
||||
> else=hold_keys(KEY_4)
|
||||
> )
|
||||
> ```
|
||||
|
||||
### if_numlock
|
||||
|
||||
> Run the first macro if your numlock is on, otherwise the second.
|
||||
>
|
||||
> ```ts
|
||||
> if_numlock(
|
||||
> then: Macro | None,
|
||||
> else: Macro | None
|
||||
> )
|
||||
> ```
|
||||
>
|
||||
> Examples:
|
||||
>
|
||||
> ```ts
|
||||
> if_numlock(hold_keys(KEY_3), hold_keys(KEY_4))
|
||||
> ```
|
||||
|
||||
### if_tap
|
||||
|
||||
> If the key is tapped quickly, run the `then` macro, otherwise the
|
||||
> second. The third param is the optional time in milliseconds and defaults to
|
||||
> 300ms
|
||||
>
|
||||
> ```ts
|
||||
> if_tap(
|
||||
> then: Macro | None,
|
||||
> else: Macro | None,
|
||||
> timeout: int
|
||||
> )
|
||||
> ```
|
||||
>
|
||||
> Examples:
|
||||
>
|
||||
> ```ts
|
||||
> if_tap(key(KEY_A), key(KEY_B), timeout=500)
|
||||
> if_tap(then=key(KEY_A), else=key(KEY_B))
|
||||
> ```
|
||||
|
||||
### if_single
|
||||
|
||||
> If the key that is mapped to the macro is pressed and released, run the `then` macro.
|
||||
>
|
||||
> If another key is pressed while the triggering key is held down, run the `else` macro.
|
||||
>
|
||||
> If a timeout number is provided, the macro will run `else` if no event arrives for
|
||||
> more than the configured number in milliseconds.
|
||||
>
|
||||
> ```ts
|
||||
> if_single(
|
||||
> then: Macro | None,
|
||||
> else: Macro | None,
|
||||
> timeout: int | None
|
||||
> )
|
||||
> ```
|
||||
>
|
||||
> Examples:
|
||||
>
|
||||
> ```ts
|
||||
> if_single(key(KEY_A), key(KEY_B))
|
||||
> if_single(None, key(KEY_B))
|
||||
> if_single(then=key(KEY_A), else=key(KEY_B))
|
||||
> if_single(key(KEY_A), key(KEY_B), timeout=1000)
|
||||
> ```
|
||||
|
||||
### parallel
|
||||
|
||||
> Run all provided macros in parallel.
|
||||
>
|
||||
> ```ts
|
||||
> parallel(*macros: Macro)
|
||||
> ```
|
||||
>
|
||||
> Examples:
|
||||
>
|
||||
> ```ts
|
||||
> parallel(
|
||||
> mouse(up, 10),
|
||||
> hold_keys(a),
|
||||
> wheel(down, 10)
|
||||
> )
|
||||
> ```
|
||||
|
||||
## Syntax
|
||||
|
||||
Multiple functions are chained using `.`.
|
||||
|
||||
Unlike other programming languages, `qux(bar())` would not run `bar` and then
|
||||
`qux`. Instead, `cux` can decide to run `bar` during runtime depending on various
|
||||
other factors. Like `repeat` is running its parameter multiple times.
|
||||
|
||||
Whitespaces, newlines and tabs don't have any meaning and are removed when the macro
|
||||
gets compiled, unless you wrap your strings in "quotes".
|
||||
|
||||
Similar to python, arguments can be either positional or keyword arguments.
|
||||
`key(symbol=KEY_A)` is the same as `key(KEY_A)`.
|
||||
|
||||
Using `$` resolves a variable during runtime. For example `set(a, $1)` and
|
||||
`if_eq($a, 1, key(KEY_A), key(KEY_B))`.
|
||||
|
||||
Comments can be written with '#', like `key(KEY_A) # write an "a"`
|
||||
BIN
readme/plus.png
Normal file
|
After Width: | Height: | Size: 12 KiB |
23
readme/pylint.svg
Normal file
@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="80" height="20">
|
||||
<linearGradient id="b" x2="0" y2="100%">
|
||||
<stop offset="0" stop-color="#bbb" stop-opacity=".1"/>
|
||||
<stop offset="1" stop-opacity=".1"/>
|
||||
</linearGradient>
|
||||
<mask id="anybadge_1">
|
||||
<rect width="80" height="20" rx="3" fill="#fff"/>
|
||||
</mask>
|
||||
<g mask="url(#anybadge_1)">
|
||||
<path fill="#555" d="M0 0h44v20H0z"/>
|
||||
<path fill="#4C1" d="M44 0h36v20H44z"/>
|
||||
<path fill="url(#b)" d="M0 0h80v20H0z"/>
|
||||
</g>
|
||||
<g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11">
|
||||
<text x="23.0" y="15" fill="#010101" fill-opacity=".3">pylint</text>
|
||||
<text x="22.0" y="14">pylint</text>
|
||||
</g>
|
||||
<g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11">
|
||||
<text x="63.0" y="15" fill="#010101" fill-opacity=".3">9.21</text>
|
||||
<text x="62.0" y="14">9.21</text>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.0 KiB |
BIN
readme/screenshot.png
Normal file
|
After Width: | Height: | Size: 57 KiB |
BIN
readme/screenshot_2.png
Normal file
|
After Width: | Height: | Size: 64 KiB |
355
readme/usage.md
Normal file
@ -0,0 +1,355 @@
|
||||
# Usage
|
||||
|
||||
Look into your applications menu and search for **Input Remapper** to open the UI.
|
||||
You should be prompted for your sudo password as special permissions are needed to read
|
||||
events from `/dev/input/` files. You can also start it via `input-remapper-gtk`.
|
||||
|
||||
First, select your device (like your keyboard) on the first page, then create a new
|
||||
preset on the second page, and add a mapping. Then you can already edit your inputs,
|
||||
as shown in the screenshots below.
|
||||
|
||||
<p align="center">
|
||||
<img src="usage_1.png"/>
|
||||
<img src="usage_2.png"/>
|
||||
</p>
|
||||
|
||||
In the "Output" textbox on the right, type the key to which you would like to map this input.
|
||||
More information about the possible mappings can be found in
|
||||
[examples.md](./examples.md) and [below](#key-names). You can also write your macro
|
||||
into the "Output" textbox. If you hit enter, it will switch to a multiline-editor with
|
||||
line-numbers.
|
||||
|
||||
Changes are saved automatically. Press the "Apply" button to activate (inject) the
|
||||
mapping you created.
|
||||
|
||||
If you later want to modify the Input of your mapping you need to use the
|
||||
"Stop" button, so that the application can read your original input.
|
||||
It would otherwise be invisible since the daemon maps it independently of the GUI.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
If your key is hanging due to a macro, unplug your device, and then plug it back in.
|
||||
This should reset the key.
|
||||
|
||||
If stuff doesn't work, check the output of `input-remapper-gtk -d` and feel free
|
||||
to [open up an issue here](https://github.com/sezanzeb/input-remapper/issues/new).
|
||||
Make sure to not post any debug logs that were generated while you entered
|
||||
private information with your device. Debug logs are quite verbose.
|
||||
|
||||
If input-remapper or your presets prevents your input device from working
|
||||
at all due to autoload, please try to unplug and plug it in twice.
|
||||
No injection should be running anymore.
|
||||
|
||||
## Combinations
|
||||
|
||||
You can use combinations of different inputs to trigger a mapping: While you record
|
||||
the input (`Record` - Button) press multiple keys and/or move axis at once.
|
||||
The mapping will be triggered as soon as all the recorded inputs are pressed.
|
||||
|
||||
If you use an axis an input you can modify the threshold at which the mapping is
|
||||
activated in the advanced input configuration, which can be opened by clicking
|
||||
on the `Advanced` button.
|
||||
|
||||
A mapping with an input combination is only injected once all combination keys
|
||||
are pressed. This means all the input keys you press before the combination is complete
|
||||
will be injected unmodified. In some cases this can be desirable, in others not.
|
||||
|
||||
*Option 1*: In the advanced input configuration there is the `Release Input` toggle.
|
||||
This will release all inputs which are part of the combination before the mapping is
|
||||
injected. Consider a mapping `Shift+1 -> a` this will inject a lowercase `a` if the
|
||||
toggle is on and an uppercase `A` if it is off. The exact behaviour if the toggle is off
|
||||
is dependent on keys (are modifiers involved?), the order in which they are pressed and
|
||||
on your environment (X11/Wayland). By default the toggle is on.
|
||||
|
||||
*Option 2*: Disable the keys that are part of the combination individually. So with
|
||||
a mapping of `Super+1 -> a`, you could additionally map `Super` to `disable`. Now
|
||||
`Super` won't do anything anymore, and therefore pressing the combination won't have
|
||||
any side effects anymore.
|
||||
|
||||
## Writing Combinations
|
||||
|
||||
You can write `Control_L + a` as mapping, which will inject those two
|
||||
keycodes into your system on a single key press. An arbitrary number of
|
||||
names can be chained using ` + `.
|
||||
|
||||
<p align="center">
|
||||
<img src="plus.png"/>
|
||||
</p>
|
||||
|
||||
## UI Shortcuts
|
||||
|
||||
- `ctrl` + `del` stops the injection (only works while the gui is in focus)
|
||||
- `ctrl` + `q` closes the application
|
||||
- `ctrl` + `r` refreshes the device list
|
||||
|
||||
## Key Names
|
||||
|
||||
Check the autocompletion of the GUI for possible values. You can also
|
||||
obtain a complete list of possiblities using `input-remapper-control --symbol-names`.
|
||||
|
||||
Input-remapper only recognizes symbol names, but not the symbols themselves. So for
|
||||
example, input-remapper might (depending on the system layout) know what a `minus` is, but
|
||||
it doesn't know `-`.
|
||||
|
||||
Key names that start with `KEY_` are keyboard layout independent constants that might
|
||||
not result in the expected output. For example using `KEY_Y` would result in "z"
|
||||
if the layout of the environment is set to german. Using `y` on the other hand would
|
||||
correctly result in "y" to be written.
|
||||
|
||||
It is also possible to map a key to `disable` to stop it from doing anything.
|
||||
|
||||
## Limitations
|
||||
|
||||
**If your fingers can't type it on your keyboard, input-remapper can't inject it.**
|
||||
|
||||
The available symbols depend on the environments keyboard layout, and only those that
|
||||
don't require a combination to be pressed can be used without workarounds (so most
|
||||
special characters need some extra steps to use them). Furthermore, if your configured
|
||||
keyboard layout doesn't support the special character at all (not even via a
|
||||
combination), then it also won't be possible for input-remapper to map that character at
|
||||
all.
|
||||
|
||||
For example, mapping a key to an exclamation mark is not possible if the keyboard
|
||||
layout is set to german. However, it is possible to mimic the combination that would
|
||||
be required to write it, by writing `Shift_L + 1` into the mapping.
|
||||
|
||||
This is because input-remapper creates a new virtual keyboard and injects numeric keycodes,
|
||||
and it won't be able to inject anything a usb keyboard wouldn't been able to. This has
|
||||
the benefit of being compatible to all display servers, but means the environment will
|
||||
ultimately decide which character to write.
|
||||
|
||||
## Analog Axis
|
||||
|
||||
It is possible to map analog inputs to analog outputs. E.g. use a gamepad as a mouse.
|
||||
For this you need to create a mapping and record the input axis. Then click on
|
||||
`Advanced` and select `Use as Analog`. Make sure to select a target
|
||||
which supports analog axis and switch to the `Analog Axis` tab.
|
||||
There you can select an output axis and use the different sliders to configure the
|
||||
sensitivity, non-linearity and other parameters as you like.
|
||||
|
||||
It is also possible to use an analog output with an input combination.
|
||||
This will result in the analog axis to be only injected if the combination is pressed
|
||||
|
||||
## Wheels
|
||||
|
||||
When mapping wheels, you need to be aware that there are both `WHEEL` and `WHEEL_HI_RES`
|
||||
events. This can cause your wheel to scroll, despite being mapped to something.
|
||||
By fiddling around with the advanced settings when editing one of your inputs, you can
|
||||
map the "Hi Res" inputs to `disable`.
|
||||
|
||||
# External tools
|
||||
|
||||
Repositories listed here are made by input-remappers users. Feel free to extend. Beware,
|
||||
that I can't review their code, so use them at your own risk (just like everything).
|
||||
|
||||
- input-remapper-xautopresets: https://github.com/DreadPirateLynx/input-remapper-xautopresets
|
||||
|
||||
# Advanced
|
||||
|
||||
## Configuration Files
|
||||
|
||||
If you don't have a graphical user interface, you'll need to edit the
|
||||
configuration files. All configuration files need to be valid json files, otherwise the
|
||||
parser refuses to work.
|
||||
|
||||
The default configuration is stored at `~/.config/input-remapper-2/config.json`,
|
||||
which doesn't include any mappings, but rather other parameters that
|
||||
are interesting for injections. The config might look something like this:
|
||||
|
||||
```json
|
||||
{
|
||||
"version": "2.2.1",
|
||||
"autoload": {
|
||||
"USB Optical Mouse": "preset name"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`preset name` refers to `~/.config/input-remapper/presets/device name/preset name.json`.
|
||||
The device name can be found with `sudo input-remapper-control --list-devices`.
|
||||
|
||||
### Preset
|
||||
|
||||
The preset files are a collection of mappings.
|
||||
Here is an example configuration for preset "a" for the "gamepad" device:
|
||||
`~/.config/input-remapper-2/presets/gamepad/a.json`
|
||||
|
||||
```json
|
||||
[
|
||||
{
|
||||
"input_combination": [
|
||||
{"type": 1, "code": 307}
|
||||
],
|
||||
"target_uinput": "keyboard",
|
||||
"output_symbol": "key(2).key(3)",
|
||||
"macro_key_sleep_ms": 100
|
||||
},
|
||||
{
|
||||
"input_combination": [
|
||||
{"type": 1, "code": 315, "origin_hash": "07f543a6d19f00769e7300c2b1033b7a"},
|
||||
{"type": 3, "code": 1, "analog_threshold": 10}
|
||||
],
|
||||
"target_uinput": "keyboard",
|
||||
"output_symbol": "1"
|
||||
},
|
||||
{
|
||||
"input_combination": [
|
||||
{"type": 3, "code": 1}
|
||||
],
|
||||
"target_uinput": "mouse",
|
||||
"output_type": 2,
|
||||
"output_code": 1,
|
||||
"gain": 0.5
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
This preset consists of three mappings.
|
||||
|
||||
* The first maps the key event with code 307 to a macro and sets the time between
|
||||
injected events of macros to 100 ms. The macro injects its events to the virtual keyboard.
|
||||
* The second mapping is a combination of a key event with the code 315 and a
|
||||
analog input of the axis 1 (y-Axis).
|
||||
* The third maps the y-Axis of a joystick to the y-Axis on the virtual mouse.
|
||||
|
||||
### Mapping
|
||||
|
||||
As shown above, the mapping is part of the preset. It consists of the input-combination,
|
||||
which is a list of input-configurations and the mapping parameters.
|
||||
|
||||
```
|
||||
{
|
||||
"input_combination": [
|
||||
<InputConfig 1>,
|
||||
<InputConfig 2>
|
||||
]
|
||||
<parameter 1>: <value1>,
|
||||
<parameter 2>: <value2>
|
||||
}
|
||||
```
|
||||
|
||||
#### Input Combination and Configuration
|
||||
|
||||
The input-combination is a list of one or more input configurations. To trigger a
|
||||
mapping, all input configurations must trigger.
|
||||
|
||||
A input configuration is a dictionary with some or all of the following parameters:
|
||||
|
||||
| Parameter | Default | Type | Description |
|
||||
|------------------|---------|------------------------|---------------------------------------------------------------------|
|
||||
| type | - | int | Input Event Type |
|
||||
| code | - | int | Input Evnet Code |
|
||||
| origin_hash | None | hex (string formatted) | A unique identifier for the device which emits the described event. |
|
||||
| analog_threshold | None | int | The threshold above which a input axis triggers the mapping. |
|
||||
|
||||
##### type, code
|
||||
|
||||
The `type` and `code` parameters are always needed. Use the program `evtest` to find
|
||||
Available types and codes. See also the [evdev documentation](https://www.kernel.org/doc/html/latest/input/event-codes.html#input-event-codes)
|
||||
|
||||
##### origin_hash
|
||||
|
||||
The origin_hash is an internally computed hash. It is used associate the input with a
|
||||
specific `/dev/input/eventXX` device. This is useful when a single pyhsical device
|
||||
creates multiple `/dev/input/eventXX` devices wihth similar capabilities.
|
||||
See also: [Issue#435](https://github.com/sezanzeb/input-remapper/issues/435)
|
||||
|
||||
##### analog_threshold
|
||||
|
||||
Setting the `analog_threshold` to zero or omitting it means that the input will be
|
||||
mapped to an axis. There can only be one axis input with a threshold of 0 in a mapping.
|
||||
If the `type` is 1 (EV_KEY) the `analog_threshold` has no effect.
|
||||
|
||||
The `analog_threshold` is needend when the input is a analog axis which should be
|
||||
treated as a key input. If the event type is `3 (EV_ABS)` (as in: map a joystick axis to
|
||||
a key or macro) the threshold can be between `-100 [%]` and `100 [%]`. The mapping will
|
||||
be triggered once the joystick reaches the position described by the value.
|
||||
|
||||
If the event type is `2 (EV_REL)` (as in: map a relative axis (e.g. mouse wheel) to a
|
||||
key or macro) the threshold can be anything. The mapping will be triggered once the
|
||||
speed and direction of the axis is higher than described by the threshold.
|
||||
|
||||
#### Mapping Parameters
|
||||
|
||||
The following table contains all possible parameters and their default values:
|
||||
|
||||
| Parameter | Default | Type | Description |
|
||||
|--------------------------|---------|-----------------|-------------------------------------------------------------------------------------------------------------------------|
|
||||
| input_combination | | list | see [above](#input-combination-and-configuration) |
|
||||
| target_uinput | | string | The UInput to which the mapped event will be sent |
|
||||
| output_symbol | | string | The symbol or macro string if applicable |
|
||||
| output_type | | int | The event type of the mapped event |
|
||||
| output_code | | int | The event code of the mapped event |
|
||||
| release_combination_keys | true | bool | If release events will be sent to the forwarded device as soon as a combination triggers see also #229 |
|
||||
| **Macro settings** | | | |
|
||||
| macro_key_sleep_ms | 0 | positive int | |
|
||||
| **Axis settings** | | | |
|
||||
| deadzone | 0.1 | float ∈ (0, 1) | The deadzone of the input axis |
|
||||
| gain | 1.0 | float | Scale factor when mapping an axis to an axis |
|
||||
| expo | 0 | float ∈ (-1, 1) | Non liniarity factor see also [GeoGebra](https://www.geogebra.org/calculator/mkdqueky) |
|
||||
| **EV_REL output** | | | |
|
||||
| rel_rate | 60 | positive int | The frequency `[Hz]` at which `EV_REL` events get generated (also effects mouse macro) |
|
||||
| **EV_REL as input** | | | |
|
||||
| rel_to_abs_input_cutoff | 2 | positive float | The value relative to a predefined base-speed, at which `EV_REL` input (cursor and wheel) is considered at its maximum. |
|
||||
| release_timeout | 0.05 | positive float | The time `[s]` until a relative axis is considered stationary if no new events arrive |
|
||||
|
||||
|
||||
## CLI
|
||||
|
||||
**input-remapper-control**
|
||||
|
||||
`--command` requires the service to be running. You can start it via
|
||||
`systemctl start input-remapper` or `sudo input-remapper-service` if it isn't already
|
||||
running (or without sudo if your user has the appropriate permissions).
|
||||
|
||||
Examples:
|
||||
|
||||
| Description | Command |
|
||||
|---------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------|
|
||||
| Load all configured presets for all devices | `input-remapper-control --command autoload` |
|
||||
| If you are running as root user, provide information about the whereabouts of the input-remapper config | `input-remapper-control --command autoload --config-dir "~/.config/input-remapper/"` |
|
||||
| List available device names for the `--device` parameter | `sudo input-remapper-control --list-devices` |
|
||||
| Stop injecting | `input-remapper-control --command stop --device "Razer Razer Naga Trinity"` |
|
||||
| Load `~/.config/input-remapper/presets/Razer Razer Naga Trinity/a.json` | `input-remapper-control --command start --device "Razer Razer Naga Trinity" --preset "a"` |
|
||||
| Loads the configured preset for whatever device is using this /dev path | `/bin/input-remapper-control --command autoload --device /dev/input/event5` |
|
||||
| Make the input-remapper-service process exit | `/bin/input-remapper-control --command quit` |
|
||||
|
||||
**systemctl**
|
||||
|
||||
Stopping the service will stop all ongoing injections
|
||||
|
||||
```bash
|
||||
sudo systemctl stop input-remapper
|
||||
sudo systemctl start input-remapper
|
||||
systemctl status input-remapper
|
||||
```
|
||||
|
||||
## Testing your Installation
|
||||
|
||||
The following commands can be used to make sure it works:
|
||||
|
||||
```bash
|
||||
sudo input-remapper-service &
|
||||
input-remapper-control --command hello
|
||||
```
|
||||
|
||||
should print `Daemon answered with "hello"`. And
|
||||
|
||||
```bash
|
||||
sudo input-remapper-control --list-devices
|
||||
```
|
||||
|
||||
should print `Found "...", ...`. If anything looks wrong, feel free to [create
|
||||
an issue](https://github.com/sezanzeb/input-remapper/issues/new).
|
||||
|
||||
## Migrating beta configs to version 2
|
||||
|
||||
By default, Input Remapper will not migrate configurations from the beta.
|
||||
If you want to use those you will need to copy them manually.
|
||||
|
||||
```bash
|
||||
rm ~/.config/input-remapper-2 -r
|
||||
cp ~/.config/input-remapper/beta_1.6.0-beta ~/.config/input-remapper-2 -r
|
||||
```
|
||||
|
||||
Then start input-remapper
|
||||
BIN
readme/usage_1.png
Normal file
|
After Width: | Height: | Size: 18 KiB |
BIN
readme/usage_2.png
Normal file
|
After Width: | Height: | Size: 14 KiB |