Per-application profile manager for the Logitech MX Creative Keypad on Linux.
Switch what the keypad's 9 LCD keys display and which keyboard shortcuts they fire, automatically, based on the application currently focused on your desktop. Author profiles in a small Tk editor; a background daemon handles the rest.
logimap-demo-reduced-size.mp4
Disclaimer: This is not an official Logitech™ application. This project is not affiliated with, endorsed by, or sponsored by Logitech.
| Need | Supported |
|---|---|
| Hardware | Logitech MX Creative Keypad (USB 046d:c354) |
| OS | Linux (kernel ≥ 5.10 for uinput) |
| Desktop | KDE Plasma 6 on Wayland |
| Keystroke injection | Built-in /dev/uinput (no third-party daemon) |
Requires KDE Plasma as KWin script is being used and the MX Dialpad aren't shipped yet; the focus-source
abstraction in logimap/focus/ is designed for them, contributions welcome.
git clone --recursive https://github.com/abishekmuthian/logimap
cd logimap
./install.sh
logimap install-kwin # one-time per Plasma session
logimap gui # author your first profile
logimap run # start the daemonIf you're adding yourself to the input group for the first time you'll
need to log out and back in once before the daemon can write /dev/uinput
across reboots. install.sh will tell you.
It's a plain bash script, so read it before running. In order, it:
- Refuses to proceed unless you're on Linux + KDE Plasma 6 Wayland (pass
--forceto override). - Detects your distro (Fedora, Debian/Ubuntu, or Arch) and installs the
build deps (
cmake,g++,python3-tkinter,python3-pillow-tk,pybind11,git,acl). git submodule update --init, which pullslogilinux/andlogilinux-sdk/from the upstreamgithub.com/logilinux/org.- Builds the C++ core (
logilinux/) with CMake. - Symlinks the SDK's empty
logilinux-driverstub to../logilinuxso the pybind11 build resolves headers. - Creates
~/.local/share/logimap/venvand pip-installspybind11,logilinux-sdk(editable), andlogimap(editable) into it. The venv uses--system-site-packagesso it inherits Tk + Pillow-tk from the distro. - Drops two udev rules into
/etc/udev/rules.d/:99-logitech-creator.rules:MODE=0666for HID PIDs046d:bc00(Dialpad, reserved) and046d:c354(MX Creative Keypad).99-logimap-uinput.rules:GROUP=input MODE=0660for/dev/uinput.
- Adds your user to the
inputgroup if you're not already a member, plus a temporary ACL bridge for the current boot. - Drops a launcher at
~/.local/bin/logimapand a.desktopentry under~/.local/share/applications/. - Drops a
systemd --userunit at~/.config/systemd/user/logimap.service(installed but not enabled; you opt in).
You'll be prompted for sudo at the udev / usermod steps.
-
logimap guiopens the Tk editor. -
Click Add, pick a profile name.
-
In the Match section, set
wm_classto the focused window class reported by KWin. The easy way to discover this: withlogimap runactive, alt-tab to the app you care about and tail the log:tail -F ~/.local/state/logimap/logimap.log | grep 'focus ->'
You'll see lines like
wm_class='org.mozilla.firefox'; copy that string verbatim. Plasma 6 uses reverse-DNS, so it'sorg.mozilla.firefox, notfirefox. -
Click a key in the 3×3 grid (e.g.
GRID_0). -
Type a Title (what the LCD will show) and assign a shortcut. Two ways:
- Capture: click the button, press the chord (e.g.
Ctrl+T). Works for app-level shortcuts the compositor doesn't intercept. Capture records a single chord — for multi-chord shortcuts like tmux prefixes, type directly (see below). - Type directly into the Shortcut field: required for chords
KWin grabs globally before they reach the editor (e.g.
Meta+Lfor lock screen,Meta+Dfor show desktop). Use the canonical+-joined form:logo+l,ctrl+shift+t,alt+f4. Modifier names:ctrl,shift,alt,logo(also acceptsmeta/super/control). The daemon synthesizes these via uinput, and KWin's global-shortcut handler honors them on Plasma 6. - Multi-chord shortcuts (tmux, screen, etc.): separate chords
with a comma. For example, tmux's vertical split (
Ctrl+Bthen") is entered asctrl+b,"(the"auto-expands toshift+'). The daemon injects each chord with a 50 ms pause between them so the receiving application can distinguish the prefix from the action. Other examples:ctrl+b,%— tmux horizontal splitctrl+b,o— tmux switch panectrl+b,x— tmux close pane
- Capture: click the button, press the chord (e.g.
-
Save. The daemon hot-reloads the config file (mtime watcher;
SIGHUPalso works).
~/.config/logimap/profiles.json:
{
"version": 1,
"default_profile": "default",
"profiles": {
"firefox": {
"match": {"wm_class": "org.mozilla.firefox", "title_regex": null},
"bg_color": [30, 30, 30],
"fg_color": [255, 255, 255],
"keys": {
"GRID_0": {"title": "New Tab", "shortcut": "ctrl+t"},
"GRID_1": {"title": "Close Tab", "shortcut": "ctrl+w"},
"GRID_2": {"title": "Reopen Tab", "shortcut": "ctrl+shift+t"},
"GRID_3": {"title": "Split V", "shortcut": "ctrl+b,\""},
"GRID_4": {"title": "Split H", "shortcut": "ctrl+b,%"}
}
},
"default": {
"match": {"wm_class": "*", "title_regex": null},
"keys": {}
}
}
}Layout:
GRID_0 GRID_1 GRID_2
GRID_3 GRID_4 GRID_5
GRID_6 GRID_7 GRID_8
Profile selection: first profile whose wm_class matches (with
title_regex checked if set), otherwise default_profile.
systemctl --user enable --now logimap
journalctl --user -u logimap -f # follow the daemon's logsKWin JS ──D-Bus──▶ daemon ──JPEG bytes──▶ MX Keypad
▲ │
└──── ButtonEvent ─────────┘
│
└──── /dev/uinput EV_KEY ──▶ focused app
logimap/focus/kwin_dbus.pyownsorg.logimap.Focus1on the session bus.kwin_script/logimap-focus.jssubscribes toworkspace.windowActivatedand pings us viacallDBus.logimap/device.pywrapslogilinux.MXKeypadDevicewith auto-reconnect.logimap/inject/uinput.pyopens/dev/uinput, registers a virtual keyboard, writesEV_KEYevents for the chord.wtype/ydotool/xdotoolare auto-probed fallbacks (rarely needed on KWin 6; see Troubleshooting).logimap/render.pyproduces a 118×118 JPEG from a title string to fill the LCD region programmed byMXKeypadDevice.set_key_image().
Logs are in ~/.local/state/logimap/logimap.log (rotating, 1 MB × 3
backups). Pass -v for DEBUG.
tail -F ~/.local/state/logimap/logimap.log
# Verify D-Bus owner is the live daemon:
busctl --user status org.logimap.Focus1| Symptom | First thing to check |
|---|---|
| LCDs don't change on alt-tab | grep 'focus ->' ~/.local/state/logimap/logimap.log; if empty, the KWin script isn't reaching the daemon. Run logimap install-kwin. |
| Key press does nothing | Look for press GRID_N has no binding in profile '…'. The resolved profile name shows whether your wm_class matched. Profile must use the reverse-DNS class (e.g. org.mozilla.firefox). |
Capture button doesn't react to Meta+L, Meta+D, etc. |
KWin grabs those globally before the editor sees them. Type the canonical form (e.g. logo+l) directly into the Shortcut field instead. |
No usable keystroke injector found |
Your user can't open /dev/uinput. Re-login after install.sh (the input group needs a fresh session) or sudo setfacl -m u:$USER:rw /dev/uinput for a one-shot. |
wtype failed for ... exit 1 |
KWin 6 hides zwp_virtual_keyboard_v1 from non-IME clients. The probe should have skipped wtype and selected uinput; check the startup log line using built-in uinput injector. |
MX Keypad not found |
lsusb | grep 046d:c354 should show the device. If /dev/hidraw* is crw-rw---- instead of crw-rw-rw-, the udev rule didn't apply; try sudo udevadm trigger --subsystem-match=hidraw and replug. |
RequestNameReply.IN_QUEUE in startup log |
A second daemon is fighting for the D-Bus name. pkill -f "logimap run" and restart. |
ModuleNotFoundError: _logilinux_native |
The SDK was built against a different Python version than the one running the daemon. Re-run ./install.sh; it picks an interpreter that matches the compiled .so. |
| systemd unit fails to start | journalctl --user -u logimap -n 50 and inspect. Most often: not in input group yet (re-login), or ~/.local/bin not on the systemd user PATH. |
pip install -e ".[dev]" # installs pytest (declared under [project.optional-dependencies].dev)
PYTHONPATH=. python3 -m pytest tests/ -qThe test suite covers the shortcut parser, config round-trip, profile resolver, JPEG rendering, and the uinput event-sequence logic, covering everything that doesn't need the physical keypad or a Wayland session.
Contributions: open a PR against github.com/abishekmuthian/logimap.
logimap is GPL-3.0-licensed. See LICENSE.
- Pagination: Support scrolling through multiple pages of keypad bindings (more than 9 keys per profile)
Built on top of the LogiLinux project:
logilinux: C++ core library and hardware abstractionlogilinux-sdk: pybind11 Python bindings
The core logilinux and logilinux-sdk repos are pulled in as git submodules.