Deamon management
This commit is contained in:
125
README.md
125
README.md
@@ -3,7 +3,8 @@
|
||||
GTK4 application (C++) for remapping the G keys **G1–G9** of a Logitech **G910**
|
||||
to other keys or commands (Linux Mint / Ubuntu).
|
||||
|
||||
The current focus is on the **GUI** only (no actual key capture/command execution yet).
|
||||
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`.
|
||||
|
||||
Documentation and source comments are written in English.
|
||||
|
||||
@@ -14,15 +15,18 @@ Documentation and source comments are written in English.
|
||||
- pkg-config
|
||||
- gettext (for translations, `msgfmt`)
|
||||
- GTK 4 development files
|
||||
- libusb 1.0 development files (for the daemon)
|
||||
- libxkbcommon development files (for the daemon's layout handling)
|
||||
|
||||
### Install dependencies
|
||||
|
||||
```bash
|
||||
sudo apt update
|
||||
sudo apt install -y build-essential gettext libgtk-4-dev
|
||||
sudo apt install -y build-essential gettext libgtk-4-dev libusb-1.0-0-dev libxkbcommon-dev
|
||||
```
|
||||
|
||||
Afterwards `pkg-config --modversion gtk4` should print a version (e.g. `4.6.x`).
|
||||
Afterwards `pkg-config --modversion gtk4` and
|
||||
`pkg-config --modversion libusb-1.0` should print versions.
|
||||
|
||||
## Setting up the project
|
||||
|
||||
@@ -40,7 +44,9 @@ cd /home/trager/Nextcloud/Projekte/C++/gKeyMapper
|
||||
make
|
||||
```
|
||||
|
||||
This produces the `gkeymapper` binary and the `.mo` translations under `locale/`.
|
||||
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`.
|
||||
|
||||
## Rebuild translations
|
||||
|
||||
@@ -64,14 +70,78 @@ LANG=de_DE.UTF-8 GKEYMAPPER_LOCALEDIR=$PWD/locale ./gkeymapper
|
||||
|
||||
Supported languages: German, English, Spanish, French.
|
||||
|
||||
## 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.
|
||||
|
||||
## Install (optional)
|
||||
|
||||
```bash
|
||||
sudo make install
|
||||
```
|
||||
|
||||
Installs `gkeymapper` to `/usr/local/bin` and the `.mo` files to
|
||||
`/usr/local/share/locale`.
|
||||
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.
|
||||
|
||||
## Clean
|
||||
|
||||
@@ -88,13 +158,18 @@ 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.
|
||||
- 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`.
|
||||
- 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 `,`
|
||||
(e.g. `ctrl+c,ctrl+v` or `shift+f4,super+t`).
|
||||
(e.g. `ctrl+c,ctrl+v` or `shift+f4,super+t`). The modifier order is
|
||||
`ctrl`, `alt`, `shift`, `super`, `altgr`; parsing accepts any order.
|
||||
|
||||
## Configuration format
|
||||
|
||||
@@ -108,18 +183,34 @@ G3=disabled:
|
||||
```
|
||||
|
||||
The same format is used by Export/Import. `key` holds a captured shortcut
|
||||
sequence, `command` a program or file path, `disabled` ignores the G key
|
||||
(similar to the `shortcut` / `run` / `nothing` hotkey types of the
|
||||
reference project above; a native C++ daemon for G-key detection and
|
||||
replay is planned next, no Python involved).
|
||||
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.
|
||||
|
||||
## Project structure
|
||||
|
||||
```
|
||||
src/main.cpp # app actions + GUI
|
||||
src/keyrow.h # KeyRow / kNumKeys
|
||||
src/mappings.{h,cpp} # mappings.conf read/write
|
||||
src/keyrow.h # KeyRow (one per G key)
|
||||
src/mappings.{h,cpp} # GUI glue: config file <-> row widgets
|
||||
src/capturedialog.{h,cpp} # key-capture dialog
|
||||
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)
|
||||
po/ # Translations (pot/po/mo targets)
|
||||
Makefile # Build via pkg-config + gettext
|
||||
.vscode/tasks.json # VS Code tasks for make
|
||||
@@ -153,7 +244,7 @@ export PKG_CONFIG_PATH=/usr/lib/x86_64-linux-gnu/pkgconfig:/usr/share/pkgconfig:
|
||||
|
||||
## Planned next steps
|
||||
|
||||
- Real capture of key presses (evdev/uinput or libinput).
|
||||
- Execution of commands when a G key is pressed.
|
||||
- Loading and saving profiles.
|
||||
- Autostart service / background daemon.
|
||||
- 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.
|
||||
|
||||
Reference in New Issue
Block a user