Skip to content

Repository files navigation

lefthand-mouse

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, …)

Why not the desktop setting?

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.

Install

From source

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

From a package (once published)

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.

Usage

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 everything

list, 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.

Selecting which mouse

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 enable again (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 status makes this obvious (the rule shows INACTIVE — device not connected), and lefthand-mouse doctor --fix repairs 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, so doctor won't guess — add --click and click the one you mean. enable --click on 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:PID rule 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. --upgrade opts into the wider rule on purpose. For the same reason, enable warns when it generates a VID:PID rule for a mouse with no serial, and --pin-port pins it instead.

Give each one a --label and status stays 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]

How it works

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 keyboard builtin 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 EVIOCSKEYCODE swap, 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.

Troubleshooting

  • GNOME's global Left-handed is on → it swaps all pointers and cancels this per-device swap. Turn it off (Settings ▸ Mouse). lefthand-mouse status warns you.
  • remappable: no in list → the device's buttons aren't keymap-settable; enable automatically 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 status will show the rule as INACTIVE — device not connected. Run lefthand-mouse doctor for the diagnosis and sudo lefthand-mouse doctor --fix to re-point the rule (or lefthand-mouse reapply if the mouse is back in its configured port).
  • Nothing happened → check lefthand-mouse status and journalctl -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.

Uninstall

sudo ./uninstall.sh          # disables all swaps, then removes files

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

License

MIT — see LICENSE.

About

Make one mouse left-handed (swap its buttons), per device, identically on X11 and Wayland — via a udev rule in the kernel input core.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages