Make one mouse left-handed (swap its left/right buttons) while leaving every other pointer alone — and have it work identically on X11 and Wayland, applied automatically at boot/plug-in.
Handy when you want to offload your right hand onto a dedicated left-side mouse for a while: keep your normal mouse on the right, grab the left one whenever you like. Works great with two mice (the project was built for exactly that), but it's just as useful with one.
$ sudo lefthand-mouse enable --click
Click a button on the mouse to use as the left-hand mouse...
enabled: Logitech USB Optical Mouse is now left-handed (tier T1, …)| One device only | X11 | Wayland | Tells identical mice apart | |
|---|---|---|---|---|
| GNOME Left-handed toggle | ❌ all pointers | ✅ | ✅ | ❌ |
xinput set-button-map |
✅ | ✅ | ❌ XWayland only | |
| lefthand-mouse | ✅ | ✅ | ✅ | ✅ by USB port |
lefthand-mouse swaps the buttons in the kernel input core, below the display
server, so the result is the same everywhere and only the device you pick is
affected.
git clone https://github.com/TimRots/lefthand-mouse && cd lefthand-mouse
sudo ./install.sh # builds, installs, runs daemon-reload
# then:
sudo lefthand-mouse enable --click./install.sh is a thin wrapper around make && sudo make install. Default prefix
is /usr/local; packagers can override (make install PREFIX=/usr).
sudo dnf copr enable timrots/lefthand-mouse && sudo dnf install lefthand-mouse # Fedora
yay -S lefthand-mouse # Arch (AUR)See PUBLISHING.md to set those up.
Requirements: a systemd distro (Fedora 42+, Ubuntu, Arch, Debian…), gcc+make
to build. No GUI, no compositor plugins.
lefthand-mouse list # show pointers, state, and a selector for each
lefthand-mouse status # what's configured, whether each is ACTIVE, + live state
lefthand-mouse doctor # explain any rule that isn't active
sudo lefthand-mouse enable --click # pick a mouse by clicking it
sudo lefthand-mouse enable --vidpid 046d:c077
sudo lefthand-mouse enable --path IDPATH --label left-desk
sudo lefthand-mouse reapply # re-activate configured devices that aren't live
sudo lefthand-mouse doctor --fix # re-point a rule at a mouse that changed USB port
sudo lefthand-mouse doctor --fix --click # ...clicking to say which of two identical mice it is
sudo lefthand-mouse disable --click # restore one mouse
sudo lefthand-mouse disable --all # restore everythinglist, status, doctor (without --fix) and enable --dry-run only read
/dev/input/event*, so they need no sudo if you're in the input group. Only the
commands that change configuration require root.
enable/disable take one selector; --click is the default.
| Selector | Use when |
|---|---|
--click |
easiest — just click the mouse you want |
--vidpid 046d:c077 |
you have one mouse of that model (portable across machines) |
--serial S |
the mouse reports a real serial (best — survives ports/machines) |
--path IDPATH |
two identical mice: pick by USB port (lefthand-mouse list shows it) |
--name "…" |
match by device name |
lefthand-mouse list prints the recommended selector for each device. It picks the
most specific stable identifier automatically when you enable: a real serial if
present, else the model (VID:PID) if unique, else model + USB port for identical
twins.
Two identical mice (same model, no serial) can only be told apart by which USB port they're in — so the rule for those is per-machine. The contract is simply: the mouse plugged into the chosen port is the left-hand one. On your other machines, run
enableagain (it's the same command).Because the only handle on a serial-less twin is its USB port, moving that mouse to a different port silently breaks the swap — the port-bound rule no longer matches anything.
lefthand-mouse statusmakes this obvious (the rule showsINACTIVE — device not connected), andlefthand-mouse doctor --fixrepairs it by re-pointing the rule at the port the mouse is in now, keeping its tier and label. With both twins plugged in they're indistinguishable, sodoctorwon't guess — add--clickand click the one you mean.enable --clickon the new port does the same thing and prunes the old port's dead rule, so rules don't pile up.A repair keeps the rule pinned to a port. That's deliberate: if it re-derived the matcher freely it could widen to a bare
VID:PIDrule whenever the twin happened to be unplugged — and that rule would grab the twin as well once it came back, making both mice left-handed.--upgradeopts into the wider rule on purpose. For the same reason,enablewarns when it generates aVID:PIDrule for a mouse with no serial, and--pin-portpins it instead.Give each one a
--labelandstatusstays readable:$ lefthand-mouse status configured (/etc/udev/rules.d/71-lefthand-mouse.rules): left-desk (046d:c077@pci-0000:00:14.0-usb-0:5.2:1.0) tier=T1 ACTIVE — swapped [/dev/input/event8]
Mouse buttons are keycodes (BTN_LEFT/BTN_RIGHT) in the Linux input core, keyed by
a HID scancode. lefthand-mouse writes a managed udev rule
(/etc/udev/rules.d/71-lefthand-mouse.rules) that swaps them for the one device you
chose, and picks the lightest mechanism that actually works — an escalation ladder:
- T1 (default) — the stock udev
keyboardbuiltin remaps the button scancodes in place (RUN{builtin}="keyboard"). No daemon, no process, nothing to grab. - T2 — a tiny systemd one-shot runs the same
EVIOCSKEYCODEswap, for the rare distro/version where T1 won't apply. - T3 — a small daemon grabs the device and re-emits a swapped copy via
uinput, for devices whose buttons aren't keymap-remappable (e.g. some trackpoints/touchpads).
enable auto-detects the device's button scancodes, picks the lowest viable tier,
verifies the swap actually took effect, and escalates if not. Everything is
transactional: a failed enable rolls back and resets the device.
- GNOME's global Left-handed is on → it swaps all pointers and cancels this
per-device swap. Turn it off (Settings ▸ Mouse).
lefthand-mouse statuswarns you. remappable: noinlist→ the device's buttons aren't keymap-settable;enableautomatically uses the T3 daemon. Nothing to do.- It worked before, now it doesn't → most often a serial-less mouse was moved to
a different USB port.
lefthand-mouse statuswill show the rule asINACTIVE — device not connected. Runlefthand-mouse doctorfor the diagnosis andsudo lefthand-mouse doctor --fixto re-point the rule (orlefthand-mouse reapplyif the mouse is back in its configured port). - Nothing happened → check
lefthand-mouse statusandjournalctl -t systemd-udevd -e. Verify the device directly:lefthand-mouse-evdev check /dev/input/eventN. - SELinux (Enforcing) → the default T1 path runs entirely inside
udevd(no custom binary) and needs no policy changes.
sudo ./uninstall.sh # disables all swaps, then removes files| Path | What |
|---|---|
lefthand-mouse |
the CLI (bash) |
lefthand-mouse-evdev |
privileged evdev helper (C): scan/detect/check/swap/reset/daemon |
/etc/udev/rules.d/71-lefthand-mouse.rules |
managed drop-in (one block per device) |
lefthand-mouse@.service, lefthand-mouse-oneshot@.service |
T3 / T2 units |
MIT — see LICENSE.