Maps an active-pen or Bluetooth pen button to desktop actions on Linux, independent of the desktop environment.
Built for the Dell Active Pen PN579X (whose top button, over Bluetooth, sends
Super+F20 / Super+F19 / Super+F18 for single / double / long-press), but it is
model-agnostic: penbind learn captures whatever keys your pen sends, so it
adapts to other pens that use different codes.
- Reads the pen's evdev device directly and grabs it, so its keys never reach logind or the desktop.
- Learns each gesture's key interactively (
penbind learn), so it works with pens other than the Dell PN579X. - Runs a preset or custom shell command per gesture (
penbind run, managed by asystemd --userservice). - Matches the device by USB/Bluetooth
vendor:productand/or a name regex, so it survives reconnects. - Installs a
uaccessudev rule, so noinputgroup membership or re-login is needed. - Warns during
learnwhen a pen sends the same key for several gestures.
The pen's top button also emits KEY_POWER, which makes the laptop sleep before
any shortcut can fire. penbind grabs the input device, so those keys never reach
logind/your desktop — no more accidental sleep — and penbind runs your action itself.
git clone https://github.com/y0c0ms/penbind.git
cd penbind
./install.shThe installer:
- installs
python3-evdev, - copies
penbindto~/.local/bin, - adds a
udevrule (uaccess) so you can read the pen without joining theinputgroup or logging out, - installs a
systemd --userservice.
penbind pair # optional: pair the pen over Bluetooth
penbind learn # press single / double / long when prompted, then pick actions
systemctl --user enable --now penbind # run it in the background
penbind status # see mappings + connection state
penbind configure # change actions later (then: systemctl --user restart penbind)penbind list-actions prints them. Current set:
| name | command |
|---|---|
none |
(disabled) |
open-rnote |
flatpak run com.github.flxzt.rnote |
screenshot |
first of spectacle / gnome-screenshot / flameshot / grim |
lock-screen |
loginctl lock-session |
volume-up / volume-down / mute |
wpctl … |
media-playpause |
playerctl play-pause |
| custom command | anything you type |
- Config:
~/.config/penbind/config.json(device matcher + per-gesture trigger key and action). Editable by hand. - Device match: by USB/BT
vendor:product(locked in duringlearn) and/or a name regex — so it keeps working across reconnects and ignores your other keyboards. - The pen distinguishes single/double/long itself (different keys), so there's no
timing logic. If a pen sends the same key for several gestures,
learnwarns you.
penbind learnfinds nothing → the pen idle-disconnects to save battery; press the top button once to wake it, then re-run.- Permission denied on the device → the
uaccessrule applies when the device is added; disconnect/reconnect the pen once after installing, or runpenbind learnwithsudoonce. - Action doesn't launch a GUI app → the daemon injects
WAYLAND_DISPLAY; if your app still won't start, run it frompenbind runin a terminal to see errors. - Different pen model → just run
penbind learn; setPENBIND_NAME_GLOBbefore./install.shif the udev name match needs to differ from*Active Pen*.
Apache License 2.0 — see LICENSE.