Files
gKeyMapper/README.md
2026-10-17 03:53:51 +02:00

8.5 KiB
Raw Blame History

gKeyMapper

GTK4 application (C++) for remapping the G keys G1–G9 of a Logitech G910 to other keys or commands (Linux Mint / Ubuntu).

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.

Requirements

  • Linux Mint / Ubuntu
  • g++ (C++17)
  • 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

sudo apt update
sudo apt install -y build-essential gettext libgtk-4-dev libusb-1.0-0-dev libxkbcommon-dev

Afterwards pkg-config --modversion gtk4 and pkg-config --modversion libusb-1.0 should print versions.

Setting up the project

Change to the project directory:

cd /home/trager/Nextcloud/Projekte/C++/gKeyMapper

(Alternatively clone the repository or copy the project folder to your own location.)

Build

make

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

make translations

Run

Normal:

./gkeymapper

With explicit language files (useful for testing from the build directory):

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):

./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:

    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)

sudo make install

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:

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

make clean

Removes the binary and the generated locale/ directory.

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.
  • 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: modifiers are joined with +, steps are separated with , (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

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 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 (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
README.md

Troubleshooting

Package gtk4 was not found

The GTK 4 development files are missing. Install them:

sudo apt install -y libgtk-4-dev

Then verify:

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:

export PKG_CONFIG_PATH=/usr/lib/x86_64-linux-gnu/pkgconfig:/usr/share/pkgconfig:/usr/lib/pkgconfig

Planned next steps

  • 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.