Single instance

This commit is contained in:
2026-10-18 16:59:51 +02:00
parent 798708b3e7
commit 74503c27dd
19 changed files with 670 additions and 99 deletions

View File

@@ -87,6 +87,12 @@ The window starts from the saved mappings: it loads
`~/.config/gKeyMapper/mappings.conf` when it opens, so `Save mappings` in a fresh
window rewrites the same content instead of emptying the file.
Only one window is open at a time, and it is the one you just started: a new
start closes the instance that is already running — including one from an older
build — and then shows its own window, so a stale window can never stay next to a
fresh one. Row edits that were not saved are lost with the replaced window,
because saving is always explicit (`Save mappings`).
## Running the daemon
The daemon reads G1–G9 from the keyboard and replays the configured action
@@ -129,6 +135,13 @@ The daemon reads G1–G9 from the keyboard and replays the configured action
- A G key fires when its report first shows it as pressed: holding it down does
not repeat the action, and two G keys held at once both fire (the report
carries the state of all nine keys, not one key per report).
- `keyhold` is the exception to that: its keys stay pressed for as long as the G
key is held, like a normal key (including the desktop's auto-repeat), so a game
or a push-to-talk sees a key that is down exactly as long as you hold the G key.
The keyboard's release report ends it. If the keyboard is unplugged or the
daemon stops while a key is held, the daemon lets it go, so nothing stays stuck.
Holding only makes sense for a single step: `keyhold:ctrl+w` is held,
`keyhold:ctrl+c,ctrl+v` is replayed as a tap (the log says so).
- Only one instance may run: a second one prints why it cannot get the bus name
and exits, instead of fighting over the keyboard and creating a second virtual
keyboard.
@@ -252,13 +265,16 @@ modifiers are joined with `+`, steps are separated with `,`
G1=key:ctrl+c,ctrl+v
G2=command:/usr/bin/firefox
G3=disabled:
G4=keyhold:ctrl+w
```
The same format is used by Export/Import. `key` holds a captured shortcut
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.
sequence and fires it once, `command` a program path or command line,
`disabled` ignores the G key, and `keyhold` keeps a single shortcut pressed for
as long as the G key is held (in the GUI it is the `Key (hold)` entry in the
Type dropdown). 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.
@@ -271,6 +287,7 @@ 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
src/daemonclient.{h,cpp} # D-Bus client for the daemon (status/reload)
src/singleinstance.{h,cpp} # one GUI only: a new start closes a running instance
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
@@ -279,7 +296,7 @@ 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/action.{h,cpp} # dispatch "key" / "command" / "disabled" / "keyhold"
daemon/config_watch.{h,cpp} # inotify watch on mappings.conf (auto-reload)
daemon/ipc_dbus.{h,cpp} # D-Bus service (status/reload/test/signals)
data/60-gkeymapper.rules # udev rule (uaccess for device + /dev/uinput)
@@ -320,10 +337,16 @@ export PKG_CONFIG_PATH=/usr/lib/x86_64-linux-gnu/pkgconfig:/usr/share/pkgconfig:
## Planned next steps
- Make the media keys remappable (they are forwarded unchanged today, see above)
and support the M1–M3 / MR keys of the G910.
- `typeout`: type out a fixed text, like the reference project's action of that
name (the daemon would resolve the text through the active layout).
- Profiles: swap mapping sets on the fly (the G910's M keys), which is what the
reserved `SetProfile` D-Bus method was meant for.
- The M1–M3 / MR keys of the G910 and profiles: swap mapping sets on the fly,
which is what the reserved `SetProfile` D-Bus method was meant for. Deliberately
left for later (see `docs/daemon-plan.md` §11).
- Key sequence timing: per-step delays for recorded macros.
- `typeout`: type out a fixed text, like the reference project's action of that
name — not decided yet (see §11).
## Not planned
- Remapping the media keys. The daemon forwards mute / volume / play / next /
previous / stop unchanged, and that is meant to stay that way (see the note
above): they are not G keys, so they get no rows in the GUI and no entry in the
config file.