Pure-Rust daemon that reads a Wacom Express Key Remote pad via evdev and injects keyboard shortcuts and scroll events via a uinput virtual device.
Replaces input-remapper (3 × python3 processes, 84 MB RSS) with a single Rust binary (~600 KB RSS).
- Linux with
/dev/uinput(kernel moduleuinput) - Read/write access to
/dev/uinputand Wacom input devices notify-sendfrom libnotify (mode-switch popups)- GTK4 (optional, for
wacomCQpro gui)
cargo build --release
cp target/release/wacomCQpro ~/.local/bin/
wacomCQpro status # show pad layout + current mappings
wacomCQpro run # start daemonBuild without GUI (daemon only, fewer dependencies):
cargo build --release --no-default-featuresFrom the repo root (the directory containing PKGBUILD):
makepkg -si
systemctl --user enable --now wacomCQproNote: This PKGBUILD builds in-tree (
source=()). Runmakepkgfrom the project root so Cargo output lands in./target/release/.
This installs the binary, systemd user unit, and udev rule for /dev/uinput.
Add your user to the input and uinput groups, then log out and back in:
sudo usermod -aG input,uinput "$USER"| Command | Description |
|---|---|
wacomCQpro run |
Start the daemon (default) |
wacomCQpro status |
Show pad schematic + all 18 buttons + current mappings |
wacomCQpro identify |
Press keys/turn the ring; prints the BTN_* name to use for each |
wacomCQpro validate |
Check config for errors |
wacomCQpro reload |
Reload config in running daemon |
wacomCQpro edit |
Open config in $EDITOR |
wacomCQpro gui |
Graphical config editor — a labeled schematic (each key shows its BTN_* code, mapping, and action description) with a Learn buttons mode that lights up the key you press + ring modes |
wacomCQpro --version |
Print version |
wacomCQpro help |
Show help |
Config lives at ~/.config/wacomCQpro/config.toml — created automatically
on first run with defaults. Edit button mappings and ring modes freely.
[buttons]
BTN_0 = "cycle_ring_mode"
BTN_1 = "Ctrl+Alt+8"
BTN_Z = "M"
[[ring_mode]]
name = "Brush Size"
cw = "scroll_up"
ccw = "scroll_down"
[[ring_mode]]
name = "Zoom"
cw = "Ctrl+Equal"
ccw = "Ctrl+Minus"Buttons (all 18): BTN_0–BTN_9, BTN_A, BTN_B, BTN_C, BTN_X,
BTN_Y, BTN_Z, BTN_BASE, BTN_BASE2. Not sure which physical key is which?
Open wacomCQpro gui, click Learn buttons, and press each one (it lights up
the matching key) — or from a terminal run wacomCQpro identify.
Key names: single letters (A–Z), numbers (0–9), Ctrl, Shift, Alt, Super, Equal, Minus, LeftBrace, RightBrace, Space, Tab, Return, Escape, Delete, Backspace, Left, Right, Up, Down, F1–F12.
Combos: "Ctrl+Z", "Ctrl+Shift+Z", "Ctrl+Alt+8"
Special actions: "cycle_ring_mode", "scroll_up", "scroll_down"
Button descriptions: Optionally add human-readable action names in a
[button_desc] section — shown in the GUI beneath each key's mapping entry:
[button_desc]
BTN_1 = "Eraser toggle"
BTN_3 = "Undo"The daemon reloads config automatically when config.toml changes (within ~1s),
or immediately via wacomCQpro reload. Invalid configs are rejected and the
previous mappings are kept.
The GUI draws the remote as a labeled schematic: a touch ring with the center
(mode) button inside it, then the remaining 17 keys as a grid. Each key shows its
BTN_* code with an inline mapping entry and an action description beneath it —
the picture is the editor, so there's no guessing which key a field controls or
what it does. Clearing a field unmaps that button. Ring modes are edited alongside
it; Save validates before writing.
Learn buttons: don't know which physical key is BTN_3? Click Learn
buttons and press a key on the remote — its keycap lights up and the status line
names it. The daemon grabs the pad exclusively, so the GUI stops it for the
duration of Learn mode and restarts it automatically when you turn Learn off or
close the window.
| Device | USB ID |
|---|---|
Wacom Cintiq Pro 24 Pen (056a:037c) |
built-in USB |
Wacom Express Key Remote (056a:0331) |
bundled dongle |
Scans /dev/input/ for Wacom vendor (0x056a) devices with "Pad" in the
name. Prioritizes devices with ABS_WHEEL (touch ring). Reconnects
automatically on USB disconnect/reconnect.
The Express Key Remote exposes 18 buttons (BTN_0–BTN_9, BTN_A/B/C/X/Y/Z,
BTN_BASE/BTN_BASE2) — all remappable. The schematic below names the most
commonly used ones; run wacomCQpro identify to learn the exact code for every
physical key on your unit.
┌─────────────────────────────────┐
│ [BTN_Z] │ top
├─────┬───────────────────┬───────┤
│ │ │ │
│BTN_1│ [BTN_0] │ BTN_4 │ center ring
│ │ (cycle mode) │ │
│ │ │ │
├─────┴───────────────────┴───────┤
│ [BTN_BASE2] │ upper-mid
│ [BTN_BASE] │ lower-mid
├─────────────────────────────────┤
│ [BTN_C] │ bottom
└─────────────────────────────────┘
The ring reports position 1–71 while touched and 0 when your finger is off
it; the daemon emits one step per position change and ignores the landing/lift
transitions (so releasing your finger never fires a stray scroll). Three cycling
modes, toggled by pressing the center ring (mode) button:
| Mode | Clockwise | Counter-clockwise |
|---|---|---|
| Brush Size | REL_WHEEL +1 |
REL_WHEEL -1 |
| Zoom | Ctrl++ |
Ctrl+- |
| Rotate Canvas | ] |
[ |
USB dongle → /dev/input/eventN (auto-detected)
│
▼ EVIOCGRAB (exclusive access)
wacomCQpro daemon
│
├── ABS_WHEEL deltas → uinput REL_WHEEL / key combos
└── KEY events (BTN_*) → uinput key combos
│
▼
uinput virtual device (/dev/input/eventN)
│
▼
Hyprland / Krita / any app
User service installed by the PKGBUILD or manually:
# ~/.config/systemd/user/wacomCQpro.service
[Service]
ExecStart=%h/.local/bin/wacomCQpro run
Restart=on-failuresystemctl --user enable --now wacomCQpro| Path | Purpose |
|---|---|
~/.config/wacomCQpro/config.toml |
Button + ring mappings |
~/.config/wacomCQpro/wacomCQpro.lock |
Daemon PID lock file |
/usr/bin/wacomCQpro |
Installed binary (PKGBUILD) |
/usr/lib/systemd/user/wacomCQpro.service |
systemd user unit |
wacomCQpro status # config + detected device
evtest "$(wacomCQpro status | awk '/Device:/{print $2}')"
grep -A5 "wacomCQpro" /proc/bus/input/devices
systemctl --user status wacomCQproMIT — see LICENSE.