Files
gKeyMapper/README.md

251 lines
8.5 KiB
Markdown
Raw Normal View History

2026-10-16 22:11:52 +02:00
# gKeyMapper
GTK4 application (C++) for remapping the G keys **G1–G9** of a Logitech **G910**
to other keys or commands (Linux Mint / Ubuntu).
2026-10-17 03:53:51 +02:00
The GUI is complete. A background daemon (`gkeymapper-daemon`) that reads the
G-keys from the keyboard is in progress — see `docs/daemon-plan.md`.
2026-10-16 22:11:52 +02:00
Documentation and source comments are written in English.
## Requirements
- Linux Mint / Ubuntu
- g++ (C++17)
- pkg-config
- gettext (for translations, `msgfmt`)
- GTK 4 development files
2026-10-17 03:53:51 +02:00
- libusb 1.0 development files (for the daemon)
- libxkbcommon development files (for the daemon's layout handling)
2026-10-16 22:11:52 +02:00
### Install dependencies
```bash
sudo apt update
2026-10-17 03:53:51 +02:00
sudo apt install -y build-essential gettext libgtk-4-dev libusb-1.0-0-dev libxkbcommon-dev
2026-10-16 22:11:52 +02:00
```
2026-10-17 03:53:51 +02:00
Afterwards `pkg-config --modversion gtk4` and
`pkg-config --modversion libusb-1.0` should print versions.
2026-10-16 22:11:52 +02:00
## Setting up the project
Change to the project directory:
```bash
cd /home/trager/Nextcloud/Projekte/C++/gKeyMapper
```
(Alternatively clone the repository or copy the project folder to your own location.)
## Build
```bash
make
```
2026-10-17 03:53:51 +02:00
This produces the `gkeymapper` GUI binary, the `gkeymapper-daemon` binary and
the `.mo` translations under `locale/`. The daemon can also be built on its own
with `make daemon`.
2026-10-16 22:11:52 +02:00
## Rebuild translations
```bash
make translations
```
## Run
Normal:
```bash
./gkeymapper
```
With explicit language files (useful for testing from the build directory):
```bash
LANG=de_DE.UTF-8 GKEYMAPPER_LOCALEDIR=$PWD/locale ./gkeymapper
```
Supported languages: German, English, Spanish, French.
2026-10-17 03:53:51 +02:00
## Running the daemon
The daemon reads G1–G9 from the keyboard and replays the configured action
(`key` through a virtual keyboard, `command` by launching the program):
```bash
./gkeymapper-daemon # uses ~/.config/gKeyMapper/mappings.conf
./gkeymapper-daemon /path/to.conf # or an explicit file
```
- It reaches the keyboard through libusb (raw USB HID, vendor interface 1) and
creates a virtual keyboard via `/dev/uinput`. Both need access for your user;
`make install` ships a udev rule (`TAG+="uaccess"`) for that. On Linux Mint the
device already has such an ACL, otherwise install the rule or add your user to
the `input`/`uinput` group.
- While it runs, the kernel driver is detached from the vendor interface, so the
media keys (mute / volume / play) may stop working until it exits — it
reattaches the driver on shutdown.
- The mappings file is **watched**: save in the GUI and the daemon picks the
change up by itself (within about half a second), no restart and no signal.
`SIGHUP`/`SIGUSR1` reload it on demand, which is also the fallback on systems
without inotify:
```bash
kill -HUP $(pgrep -x gkeymapper-daem)
```
Note: the kernel truncates the process name to 15 characters, so
`pgrep -x gkeymapper-daemon` does **not** match.
- A failed reload keeps the previous mappings, so a transient read error never
silently disables every key.
- Key names are layout-independent: `key:ctrl+z` means the key that produces
`z` **on your layout**. The layout is read from `/etc/default/keyboard`
(`XKBLAYOUT`); without it, US key positions are used. Because the name is
symbolic, the same entry keeps its meaning if the layout changes (as long as
the layout has that key); a name the layout does not provide is skipped.
- `altgr+...` presses AltGr (level 3), e.g. `key:altgr+7` types `{` on a German
layout.
- `command:...` is run through the shell, so arguments work
(`command:/usr/bin/firefox --new-window`); a plain program path is also fine.
Stop it with Ctrl+C or `kill`; it reattaches the kernel driver on the way out.
2026-10-16 22:11:52 +02:00
## Install (optional)
```bash
sudo make install
```
2026-10-17 03:53:51 +02:00
Installs, under `PREFIX` (default `/usr/local`):
| Path | What |
|---|---|
| `bin/gkeymapper`, `bin/gkeymapper-daemon` | the GUI and the daemon |
| `share/locale/*/LC_MESSAGES/gkeymapper.mo` | translations |
| `share/gkeymapper/assets/`, `share/applications/`, `share/icons/` | icon, `.desktop` file, icons |
| `lib/udev/rules.d/60-gkeymapper.rules` | lets your user reach the keyboard and `/dev/uinput` |
| `lib/modules-load.d/gkeymapper.conf` | loads the `uinput` module at boot |
| `lib/systemd/user/gkeymapper-daemon.service` | systemd user unit (binary path filled in from `PREFIX`) |
Then enable the daemon for your session — it then starts automatically at login:
```bash
systemctl --user daemon-reload
systemctl --user enable --now gkeymapper-daemon
systemctl --user status gkeymapper-daemon
```
It is a **user** service on purpose: it needs your session for `/dev/uinput` and
so that a `command` action inherits `DISPLAY`/`WAYLAND_DISPLAY`. The unit does
not run as root. `make install` reloads the udev rules for you; on a fresh
machine replug the keyboard (or reboot) once so the ACL applies.
2026-10-16 22:11:52 +02:00
## Clean
```bash
make clean
```
Removes the binary and the generated `locale/` directory.
2026-10-17 02:42:09 +02:00
## Capturing key sequences
For rows of type **Key**, the action button opens a capture dialog:
- Press keys one after another; each press becomes one step.
- Modifier keys (Ctrl, Alt, Shift, Super) held during a press are recorded
as part of that step.
2026-10-17 03:53:51 +02:00
- Each step stores the **base key** (the unshifted key), so `Shift`+ß is
recorded as `shift+ssharp` and not as the shifted symbol `question`.
- AltGr (level 3) is recorded as the `altgr` modifier plus the base key, so
`AltGr`+7 becomes `altgr+7`.
2026-10-17 02:42:09 +02:00
- The live preview shows the sequence. `Clear` resets it, `Done` writes it
into the row, `Cancel` discards it.
The sequence uses the same format as
[g910-gkey-macro-support](https://github.com/JSubelj/g910-gkey-macro-support):
modifiers are joined with `+`, steps are separated with `,`
2026-10-17 03:53:51 +02:00
(e.g. `ctrl+c,ctrl+v` or `shift+f4,super+t`). The modifier order is
`ctrl`, `alt`, `shift`, `super`, `altgr`; parsing accepts any order.
2026-10-17 02:42:09 +02:00
## Configuration format
`Save mappings` writes `~/.config/gKeyMapper/mappings.conf`:
```
# gKeyMapper mappings (G1..G9)
G1=key:ctrl+c,ctrl+v
G2=command:/usr/bin/firefox
G3=disabled:
```
The same format is used by Export/Import. `key` holds a captured shortcut
2026-10-17 03:53:51 +02:00
sequence, `command` a program path or command line, `disabled` ignores the
G key. These map to the `shortcut` / `run` / `nothing` hotkey types of the
reference project; the native C++ daemon (`gkeymapper-daemon`) reads this file
and replays the actions — no Python involved.
`Save mappings` writes atomically (temporary file + `rename`), so the daemon
never sees a half-written file and the watch reloads exactly once per save.
2026-10-17 02:42:09 +02:00
2026-10-16 22:11:52 +02:00
## Project structure
```
2026-10-17 02:57:21 +02:00
src/main.cpp # app actions + GUI
2026-10-17 03:53:51 +02:00
src/keyrow.h # KeyRow (one per G key)
src/mappings.{h,cpp} # GUI glue: config file <-> row widgets
2026-10-17 02:57:21 +02:00
src/capturedialog.{h,cpp} # key-capture dialog
2026-10-17 03:53:51 +02:00
shared/mapping.{h,cpp} # mappings format + shortcut parsing (GUI & daemon)
shared/keys.{h,cpp} # canonical key names -> Linux key codes
shared/keymap.{h,cpp} # layout-aware key lookups (xkbcommon), GUI + daemon
daemon/main.cpp # event loop: read G-keys, run the action
daemon/device_tables.{h,cpp} # per-model raw HID report tables (G910)
daemon/g910_device.{h,cpp} # libusb access to the vendor interface
daemon/uinput_keyboard.{h,cpp}# virtual keyboard for "key" mappings
daemon/action.{h,cpp} # dispatch "key" / "command" / "disabled"
daemon/config_watch.{h,cpp} # inotify watch on mappings.conf (auto-reload)
data/60-gkeymapper.rules # udev rule (uaccess for device + /dev/uinput)
data/gkeymapper-uinput.conf # modules-load.d fragment for uinput
data/gkeymapper-daemon.service# systemd user unit (installed by make install)
docs/daemon-plan.md # daemon plan (reference: g910-gkey-macro-support)
2026-10-16 22:11:52 +02:00
po/ # Translations (pot/po/mo targets)
Makefile # Build via pkg-config + gettext
.vscode/tasks.json # VS Code tasks for make
README.md
```
## Troubleshooting
### `Package gtk4 was not found`
The GTK 4 development files are missing. Install them:
```bash
sudo apt install -y libgtk-4-dev
```
Then verify:
```bash
pkg-config --modversion gtk4
```
### `pkg-config` cannot find `.pc` files (wrong search path)
In some environments `PKG_CONFIG_PATH` must be set explicitly. Standard path on
Ubuntu/Mint:
```bash
export PKG_CONFIG_PATH=/usr/lib/x86_64-linux-gnu/pkgconfig:/usr/share/pkgconfig:/usr/lib/pkgconfig
```
## Planned next steps
2026-10-17 03:53:51 +02:00
- Daemon ↔ GUI channel (D-Bus): reload on save and show the daemon status.
- Support the media keys and the M1–M3 / MR keys of the G910.
- udev rule and systemd user service so the daemon can autostart.
- `make install` for the daemon binary, the udev rule and the service file.