2020-12-19 15:09:24 +00:00
|
|
|
# Development
|
|
|
|
|
2020-12-19 15:12:55 +00:00
|
|
|
Contributions are very welcome, I will gladly review and discuss any merge
|
2021-03-21 18:15:20 +00:00
|
|
|
requests. If you have questions about the code and architecture, feel free
|
2022-01-01 12:00:49 +00:00
|
|
|
to [open an issue](https://github.com/sezanzeb/input-remapper/issues). This
|
|
|
|
file should give an overview about some internals of input-remapper.
|
2020-12-19 15:12:55 +00:00
|
|
|
|
2021-08-22 12:00:08 +00:00
|
|
|
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.
|
2021-08-04 08:44:48 +00:00
|
|
|
|
2020-12-19 15:09:24 +00:00
|
|
|
## Roadmap
|
|
|
|
|
|
|
|
- [x] show a dropdown to select valid devices
|
|
|
|
- [x] creating presets per device
|
|
|
|
- [x] renaming presets
|
|
|
|
- [x] show a mapping table
|
|
|
|
- [x] make that list extend itself automatically
|
|
|
|
- [x] read keycodes with evdev
|
|
|
|
- [x] inject the mapping
|
|
|
|
- [x] keep the system defaults for unmapped buttons
|
|
|
|
- [x] button to stop mapping and using system defaults
|
|
|
|
- [x] highlight changes and alert before discarding unsaved changes
|
|
|
|
- [x] automatically load presets on login for plugged in devices
|
|
|
|
- [x] make sure it works on wayland
|
|
|
|
- [x] support timed macros, maybe using some sort of syntax
|
|
|
|
- [x] add to the AUR, provide .deb file
|
|
|
|
- [x] basic support for gamepads as keyboard and mouse combi
|
|
|
|
- [x] executing a macro forever while holding down the key using `h`
|
|
|
|
- [x] mapping D-Pad directions as buttons
|
2020-12-19 23:41:28 +00:00
|
|
|
- [x] configure joystick purpose and speed via the GUI
|
2020-12-24 00:26:34 +00:00
|
|
|
- [x] support for non-GUI TTY environments with a command to stop and start
|
|
|
|
- [x] start the daemon in such a way to not require usermod
|
2020-12-31 20:46:57 +00:00
|
|
|
- [x] mapping a combined button press to a key
|
2021-01-01 13:17:41 +00:00
|
|
|
- [x] add "disable" as mapping option
|
2021-01-01 21:20:33 +00:00
|
|
|
- [x] mapping joystick directions as buttons, making it act like a D-Pad
|
2021-01-05 18:33:47 +00:00
|
|
|
- [x] mapping mouse wheel events to buttons
|
2021-02-07 14:00:36 +00:00
|
|
|
- [x] automatically load presets when devices get plugged in after login (udev)
|
2021-02-12 20:43:40 +00:00
|
|
|
- [x] map keys using a `modifier + modifier + ... + key` syntax
|
2021-02-18 19:38:14 +00:00
|
|
|
- [x] inject in an additional device instead to avoid clashing capabilities
|
2021-03-21 18:15:20 +00:00
|
|
|
- [x] don't run any GUI code as root for improved wayland compatibility
|
2021-08-22 12:00:08 +00:00
|
|
|
- [ ] macro editor with easier to read function names
|
2021-04-08 16:19:55 +00:00
|
|
|
- [ ] plugin support
|
2021-08-18 13:01:40 +00:00
|
|
|
- [x] getting it into the official debian repo
|
2020-12-19 15:09:24 +00:00
|
|
|
|
|
|
|
## Tests
|
|
|
|
|
|
|
|
```bash
|
2021-01-04 19:50:05 +00:00
|
|
|
sudo pip install coverage
|
2022-01-01 12:00:49 +00:00
|
|
|
pylint inputremapper --extension-pkg-whitelist=evdev
|
|
|
|
sudo pkill -f input-remapper
|
2020-12-19 15:09:24 +00:00
|
|
|
sudo pip install . && coverage run tests/test.py
|
|
|
|
coverage combine && coverage report -m
|
|
|
|
```
|
|
|
|
|
2022-01-01 12:00:49 +00:00
|
|
|
To read events, `evtest` is very helpful. Add `-d` to `input-remapper-gtk`
|
2020-12-19 15:12:55 +00:00
|
|
|
to get debug output.
|
|
|
|
|
2021-02-13 13:11:49 +00:00
|
|
|
Single tests can be executed via
|
|
|
|
|
|
|
|
```bash
|
|
|
|
python3 tests/test.py test_paths.TestPaths.test_mkdir
|
|
|
|
```
|
|
|
|
|
2021-04-02 10:16:34 +00:00
|
|
|
Don't use your computer during integration tests to avoid interacting
|
|
|
|
with the gui, which might make tests fail.
|
|
|
|
|
2021-08-22 12:00:08 +00:00
|
|
|
## Writing Tests
|
|
|
|
|
2022-01-01 12:00:49 +00:00
|
|
|
Tests are in https://github.com/sezanzeb/input-remapper/tree/main/tests
|
2021-08-22 12:00:08 +00:00
|
|
|
|
2022-01-01 12:00:49 +00:00
|
|
|
https://github.com/sezanzeb/input-remapper/blob/main/tests/test.py patches some modules and runs tests. The tests need
|
2021-08-22 12:00:08 +00:00
|
|
|
patches because every environment that runs them will be different. By using patches they all look the same to the
|
|
|
|
individual tests. Some patches also allow to make some handy assertions, like the `write_history` of `UInput`.
|
|
|
|
|
|
|
|
Test files are usually named after the module they are in.
|
|
|
|
|
|
|
|
In the tearDown functions, usually one of `quick_cleanup` or `cleanup` should be called. This avoids making a test
|
|
|
|
fail that comes after your new test, because some state variables might still be modified by yours.
|
|
|
|
|
2020-12-19 15:12:55 +00:00
|
|
|
## Releasing
|
|
|
|
|
2021-01-05 18:33:47 +00:00
|
|
|
ssh/login into a debian/ubuntu environment
|
2020-12-19 15:12:55 +00:00
|
|
|
|
|
|
|
```bash
|
|
|
|
./scripts/build.sh
|
|
|
|
```
|
|
|
|
|
2022-01-10 19:20:16 +00:00
|
|
|
This will generate `input-remapper/deb/input-remapper-1.3.0.deb`
|
2020-12-19 15:12:55 +00:00
|
|
|
|
|
|
|
## Badges
|
|
|
|
|
|
|
|
```bash
|
|
|
|
sudo pip install git+https://github.com/jongracecox/anybadge
|
|
|
|
./scripts/badges.sh
|
|
|
|
```
|
|
|
|
|
2020-12-24 00:26:34 +00:00
|
|
|
New badges, if needed, will be created in `readme/` and they
|
2020-12-19 15:12:55 +00:00
|
|
|
just need to be commited.
|
2020-12-24 00:26:34 +00:00
|
|
|
|
|
|
|
## Files
|
|
|
|
|
|
|
|
**gui**
|
|
|
|
|
2022-01-01 12:00:49 +00:00
|
|
|
- `bin/input-remapper-gtk` the executable that starts the gui. It also sends
|
2021-01-23 16:12:20 +00:00
|
|
|
messages to the service via dbus if certain buttons are clicked.
|
2022-01-01 12:00:49 +00:00
|
|
|
- `bin/input-remapper-helper` provides information to the gui that requires
|
2021-03-21 18:15:20 +00:00
|
|
|
root rights. Is stopped when the gui closes.
|
2022-01-01 12:00:49 +00:00
|
|
|
- `data/input-remapper.policy` configures pkexec. By using auth_admin_keep
|
2021-03-21 18:15:20 +00:00
|
|
|
the user is not asked multiple times for each task that needs elevated
|
|
|
|
rights. This is done instead of granting the whole application root rights
|
|
|
|
because it is [considered problematic](https://wiki.archlinux.org/index.php/Running_GUI_applications_as_root).
|
2022-01-01 12:00:49 +00:00
|
|
|
- `data/input-remapper.desktop` is the entry in the start menu
|
2020-12-24 00:26:34 +00:00
|
|
|
|
|
|
|
**cli**
|
|
|
|
|
2022-01-01 12:00:49 +00:00
|
|
|
- `bin/input-remapper-control` is an executable to send messages to the service
|
2021-01-23 16:12:20 +00:00
|
|
|
via dbus. It can be used to start and stop injection without a GUI.
|
2021-03-21 18:15:20 +00:00
|
|
|
The gui also uses it to run the service (if not already running) and
|
|
|
|
helper, because by using one single command for both the polkit rules file
|
|
|
|
remembers not to ask for a password again.
|
2020-12-24 00:26:34 +00:00
|
|
|
|
2021-02-07 14:00:36 +00:00
|
|
|
**service**
|
2020-12-24 00:26:34 +00:00
|
|
|
|
2022-01-01 12:00:49 +00:00
|
|
|
- `bin/input-remapper-service` executable that starts listening for
|
2021-02-07 14:00:36 +00:00
|
|
|
commands via dbus and runs the injector when needed. It shouldn't matter how
|
|
|
|
it is started as long as it manages to start without throwing errors. It
|
|
|
|
usually needs root rights.
|
2022-01-01 12:00:49 +00:00
|
|
|
- `data/input-remapper.service` starts input-remapper-service automatically on boot
|
2021-01-23 16:12:20 +00:00
|
|
|
on distros using systemd.
|
2022-01-01 12:00:49 +00:00
|
|
|
- `data/inputremapper.Control.conf` is needed to connect to dbus services started
|
2021-01-23 16:12:20 +00:00
|
|
|
by systemd from other applications.
|
2020-12-24 00:26:34 +00:00
|
|
|
|
2021-02-07 14:00:36 +00:00
|
|
|
**autoload**
|
2020-12-24 00:26:34 +00:00
|
|
|
|
2022-01-01 12:00:49 +00:00
|
|
|
- `data/input-remapper-autoload.desktop` executes on login and tells the systemd
|
2021-01-23 16:12:20 +00:00
|
|
|
service to stop injecting (possibly the presets of another user) and to
|
|
|
|
inject the users autoloaded presets instead (if any are configured)
|
2022-01-01 12:00:49 +00:00
|
|
|
- `data/input-remapper.rules` udev rule that sends a message to the service to
|
2021-02-07 14:00:36 +00:00
|
|
|
start injecting for new devices when they are seen for the first time.
|
|
|
|
|
|
|
|
**Example system startup**
|
|
|
|
|
2022-01-01 12:00:49 +00:00
|
|
|
1. systemd loads `input-remapper.service` on boot
|
|
|
|
2. on login, `input-remapper-autoload.desktop` is executed, which has knowledge
|
2021-02-07 14:00:36 +00:00
|
|
|
of the current user und doesn't run as root
|
|
|
|
2.1 it sends the users config directory to the service
|
|
|
|
2.2 it makes the service stop all ongoing injectings
|
|
|
|
2.3 it tells the service to start loading all of the configured presets
|
2022-01-01 12:00:49 +00:00
|
|
|
3. a bluetooth device gets connected, so udev runs `input-remapper.rules` which
|
2021-02-07 14:00:36 +00:00
|
|
|
tells the service to start injecting for that device if it has a preset
|
|
|
|
assigned. Works because step 2 told the service about the current users
|
|
|
|
config.
|
|
|
|
|
2022-01-01 12:00:49 +00:00
|
|
|
Communication to the service always happens via `input-remapper-control`
|
2020-12-26 15:45:29 +00:00
|
|
|
|
2021-03-21 18:15:20 +00:00
|
|
|
## Permissions
|
|
|
|
|
|
|
|
**gui**
|
|
|
|
|
|
|
|
The gui process starts without root rights. It makes sure the daemon and
|
|
|
|
helper are running via pkexec.
|
|
|
|
|
|
|
|
**daemon**
|
|
|
|
|
|
|
|
The daemon exists to keep injections alive beyond the lifetime of the
|
|
|
|
user interface. Runs via root. Communicates via dbus. Either started
|
|
|
|
via systemd or pkexec.
|
|
|
|
|
|
|
|
**helper**
|
|
|
|
|
|
|
|
The helper provides information to the user interface like events and
|
|
|
|
devices. Communicates via pipes. It should not exceed the lifetime of
|
|
|
|
the user interface because it exposes all the input events. Starts via
|
|
|
|
pkexec.
|
|
|
|
|
2021-02-16 23:03:45 +00:00
|
|
|
## Unsupported Devices
|
|
|
|
|
|
|
|
Either open up an issue or debug it yourself and make a pull request.
|
|
|
|
|
|
|
|
You will need to work with the devices capabilities. You can get those using
|
|
|
|
|
|
|
|
```
|
|
|
|
sudo evtest
|
|
|
|
```
|
|
|
|
|
|
|
|
**It tries or doesn't try to map ABS_X/ABS_Y**
|
|
|
|
|
|
|
|
Is the device a gamepad? Does the GUI show joystick configurations?
|
2021-02-18 19:38:14 +00:00
|
|
|
|
2021-02-16 23:03:45 +00:00
|
|
|
- if yes, no: adjust `is_gamepad` to loosen up the constraints
|
|
|
|
- if no, yes: adjust `is_gamepad` to tighten up the constraints
|
2021-02-18 19:38:14 +00:00
|
|
|
|
2021-02-16 23:03:45 +00:00
|
|
|
Try to do it in such a way that other devices won't break. Also see
|
|
|
|
readme/capabilities.md
|
|
|
|
|
|
|
|
**It won't offer mapping a button**
|
|
|
|
|
2021-03-21 18:15:20 +00:00
|
|
|
If `sudo evtest` shows an event for the button, try to
|
|
|
|
modify `should_map_as_btn`. If not, the button cannot be mapped.
|
2021-02-16 23:03:45 +00:00
|
|
|
|
2021-03-05 16:20:53 +00:00
|
|
|
## How it works
|
|
|
|
|
2021-08-22 12:00:08 +00:00
|
|
|
It uses evdev. The links below point to the 1.0.0 release, line numbers might have changed in the current main.
|
2021-03-05 16:20:53 +00:00
|
|
|
|
2021-03-21 18:15:20 +00:00
|
|
|
1. It grabs a device (e.g. /dev/input/event3), so that the key events won't
|
|
|
|
reach X11/Wayland anymore
|
2022-01-01 12:00:49 +00:00
|
|
|
[source](https://github.com/sezanzeb/input-remapper/blob/1.0.0/inputremapper/injection/injector.py#L197)
|
2021-03-21 18:15:20 +00:00
|
|
|
2. Reads the events from it (`evtest` can do it, you can also do
|
|
|
|
`cat /dev/input/event3` which yields binary stuff)
|
2022-01-01 12:00:49 +00:00
|
|
|
[source](https://github.com/sezanzeb/input-remapper/blob/1.0.0/inputremapper/injection/injector.py#L443)
|
2021-03-21 18:15:20 +00:00
|
|
|
3. Looks up the mapping if that event maps to anything
|
2022-01-01 12:00:49 +00:00
|
|
|
[source](https://github.com/sezanzeb/input-remapper/blob/1.0.0/inputremapper/injection/keycode_mapper.py#L434)
|
|
|
|
4. Injects the output event in a new device that input-remapper created (another
|
2021-03-21 18:15:20 +00:00
|
|
|
new path in /dev/input, device name is suffixed by "mapped")
|
2022-01-01 12:00:49 +00:00
|
|
|
[source](https://github.com/sezanzeb/input-remapper/blob/1.0.0/inputremapper/injection/keycode_mapper.py#L242),
|
|
|
|
[new device](https://github.com/sezanzeb/input-remapper/blob/1.0.0/inputremapper/injection/injector.py#L356)
|
2021-03-21 18:15:20 +00:00
|
|
|
5. Forwards any events that should not be mapped to anything in another new
|
|
|
|
device (device name is suffixed by "forwarded")
|
2022-01-01 12:00:49 +00:00
|
|
|
[source](https://github.com/sezanzeb/input-remapper/blob/1.0.0/inputremapper/injection/keycode_mapper.py#L247),
|
|
|
|
[new device](https://github.com/sezanzeb/input-remapper/blob/1.0.0/inputremapper/injection/injector.py#L367)
|
2021-03-05 16:20:53 +00:00
|
|
|
|
|
|
|
This stuff is going on as a daemon in the background
|
|
|
|
|
2021-08-18 13:01:40 +00:00
|
|
|
## How combinations are injected
|
|
|
|
|
2021-08-04 08:44:48 +00:00
|
|
|
Here is an example how combinations are injected:
|
|
|
|
|
|
|
|
```
|
|
|
|
a -> x
|
|
|
|
a + b -> y
|
|
|
|
```
|
|
|
|
|
2022-01-01 12:00:49 +00:00
|
|
|
1. the `a` button is pressed with your finger, `a 1` arrives via evdev in input-remapper
|
|
|
|
2. input-remapper maps it to `x 1` and injects it
|
|
|
|
3. `b` is pressed with your finger, `b 1` arrives via evdev in input-remapper
|
|
|
|
4. input-remapper sees a triggered combination and maps it to `y 1` and injects it
|
|
|
|
5. `b` is released, `b 0` arrives at input-remapper
|
|
|
|
6. input-remapper remembered that it was the trigger for a combination and maps that release to `y 0` and injects it
|
|
|
|
7. the `a` button is released, `a 0` arrives at input-remapper
|
|
|
|
8. input-remapper maps that release to `x 0` and injects it
|
2021-08-04 08:44:48 +00:00
|
|
|
|
2021-08-18 13:01:40 +00:00
|
|
|
## Multiple sources, single UInput
|
|
|
|
|
2022-01-01 12:00:49 +00:00
|
|
|
https://github.com/sezanzeb/input-remapper/blob/1.0.0/inputremapper/injection/injector.py
|
2021-08-18 13:01:40 +00:00
|
|
|
|
2022-01-01 12:00:49 +00:00
|
|
|
This "Injector" process is the only process that injects if input-remapper is used for a single device.
|
2021-08-18 13:01:40 +00:00
|
|
|
|
|
|
|
Inside `run` of that process there is an iteration of `for source in sources:`,
|
|
|
|
which runs an event loop for each possible source for events.
|
|
|
|
Each event loop has convenient access to the "context" to read some globals.
|
|
|
|
|
|
|
|
Consider this typical example of device capabilities:
|
|
|
|
|
|
|
|
- "BrandXY Mouse" -> EV_REL, BTN_LEFT, ...
|
|
|
|
- "BrandXY Mouse" -> KEY_1, KEY_2
|
|
|
|
|
|
|
|
There are two devices called "BrandXY Mouse", and they report different events.
|
2022-01-01 12:00:49 +00:00
|
|
|
Input-remapper creates a single uinput to inject all mapped events to. For example
|
2021-08-18 13:01:40 +00:00
|
|
|
|
|
|
|
- BTN_LEFT -> a
|
|
|
|
- KEY_2 -> b
|
|
|
|
|
|
|
|
so you end up with a new device with the following capabilities
|
|
|
|
|
2022-01-01 12:00:49 +00:00
|
|
|
"input-remapper BrandXY Mouse mapped" -> KEY_A, KEY_B
|
2021-08-18 13:01:40 +00:00
|
|
|
|
2022-01-01 12:00:49 +00:00
|
|
|
while input-remapper reads from multiple InputDevices it injects the mapped letters into a single UInput.
|
2021-08-18 13:01:40 +00:00
|
|
|
|
2020-12-26 15:45:29 +00:00
|
|
|
## Resources
|
|
|
|
|
2021-02-13 16:09:35 +00:00
|
|
|
- [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/)
|
2021-03-21 18:15:20 +00:00
|
|
|
- [Python Unix Domain Sockets](https://pymotw.com/2/socket/uds.html)
|
2021-03-21 19:47:36 +00:00
|
|
|
- [GNOME HIG](https://developer.gnome.org/hig/stable/)
|