mouseless drives the Linux pointer from the keyboard, on Wayland too
A mouse replacement and key remapper for Linux
At a glance
- What is it?
- mouseless is a Go program that lets you move, click and scroll the mouse pointer with the keyboard, using YAML layers, key remapping and a virtual input device. It runs across Linux distributions including Wayland sessions, and it needs root to create that device.
- Who is it for?
- mouseless fits a Linux user who wants to keep both hands on the keyboard, who has a trackpad they dislike or none at all, or who needs precise pointer steps, and who is willing to give one program root access to the input subsystem. It is the wrong tool if you need a pointer that works without root, if your session is on a system where a virtual uinput device is not permitted, or if you simply want a pointing device rather than a keyboard driven one.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 6 days ago.
- What is it written in?
- Mainly Go, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.
Editorial analysis
It reads the keyboard and creates a virtual pointer
mouseless is a program that lets you control the mouse pointer on Linux using the keyboard, and the readme makes the compatibility claim up front: it works across all Linux distributions, including those running Wayland.
That claim is the point of the project, because it is also the successor to xmouseless, and the note about a Wayland session is the reason the rewrite happened.
The mechanism explains the compatibility. The Go module requires github.com/gvalkov/golang-evdev, which is a library for reading input devices through the evdev interface, and github.com/jbensmann/uinput, which is a fork of the user input library for writing to the kernel's virtual input subsystem. So the program reads key events at the kernel level, interprets them according to its configuration, and creates a virtual device that the rest of the desktop sees as an ordinary mouse.
That is a different design from injecting pointer motion into a display server, and it is why the tool is not tied to a particular session type. The readme also notes an unrelated commercial app that ended up with the same name, and states that this project is the free and open source one.
The feature list is short: move the pointer continuously, change the pointer speed on the fly, click, grab and scroll, remap keys, and define arbitrary layers.
Layers are the whole configuration language
The configuration is YAML, and the readme makes a point of lowering the barrier: you do not need to know exactly what YAML means, just make sure the indentation is correct, and lines starting with a hash are comments.
The structure is two levels. There are global speed settings, baseMouseSpeed and baseScrollSpeed, and then a list of layers, each with a name, a set of bindings, and an optional passThrough flag. The first layer is the one active at start.
Here is the minimal example from the readme, the first half of it.
# the default speed for mouse movement and scrolling
baseMouseSpeed: 750.0
baseScrollSpeed: 20.0
# the rest of the config defines the layers with their bindings
layers:
# the first layer is active at start
- name: initial
bindings:
# when tab is held and another key pressed, activate mouse layer
tab: tap-hold-next tab ; toggle-layer mouse ; 500
# when a is held for 300ms, activate mouse layer
a: tap-hold a ; toggle-layer mouse ; 300The bindings are written as a sequence of actions separated by semicolons, and a tap-hold action with a duration in milliseconds is what makes a layer reachable without stealing a key outright.
The second half shows the movement layer itself.
- name: mouse
# when true, keys that are not mapped keep their original meaning
passThrough: true
bindings:
# quit mouse layer
q: layer initial
# keep the mouse layer active
space: layer mouse
l: move 1 0
j: move -1 0
k: move 0 1
i: move 0 -1and the speed and button bindings follow in the same layer.
leftalt: speed 4.0
e: speed 0.3
capslock: speed 0.1
f: button left
d: button middle
s: button rightThe passThrough flag is the detail that makes this usable rather than a toy. With it set, keys that are not mapped in that layer keep their original meaning, so a movement layer can sit over your normal typing without swallowing the keyboard.
The action set is small and the naming is guessable
The readme lists the available actions in a table, and they fall into a few families.
A bare key combination maps to that key or combo, so a maps to a and shift+a maps to shift with a. layer <name> switches to a layer, and toggle-layer <name> switches to that layer only while the mapped key is held, which is why the config pairs it with tap-hold. mod-layer <key> <layer> switches layers for bound keys only. There is a move action with a horizontal and vertical offset, scroll up and scroll down, speed with a multiplier, and button left, button middle and button right, with a grab action in the feature list as well.
The naming convention for keys is worth spelling out, because it is a common stumbling block. The readme explains that you can find the name of a key by starting the program with the --debug flag, pressing the key and looking for output like Pressed: rightalt (100), which gives you both the name and the keycode in parentheses. It also warns that a key name does not necessarily match what is printed on your keyboard: with a German layout the y and z keys are swapped relative to the English layout, but the name of the z key is y and vice versa.
Remapping is a first-class feature, not a side effect. You can map esc to capslock, and a key can map to several keys, with a: leftshift+k1 producing an exclamation mark on an English or German layout. A more complete example illustrating most available options lives in the repository at example_configs/config_full.yaml.
Three ways to install it, one of them a shell script
There is a precompiled release, a convenience script and a build from source.
The release archive is mouseless_linux_amd64.tar.gz, and it contains an executable named mouseless that you place somewhere sensible and make executable.
tar -xvf mouseless_linux_amd64.tar.gz
sudo mv mouseless /usr/local/bin/mouseless
sudo chmod +x /usr/local/bin/mouselessThe convenience script installs the latest release and creates a config file if none exists yet.
wget -O install.sh https://raw.githubusercontent.com/jbensmann/mouseless/refs/heads/main/install.sh
sh install.shFrom source, a single command is enough if you have Go installed.
go install github.com/jbensmann/mouseless@latestThat puts the binary in $GOPATH/bin/mouseless, usually ~/go/bin/mouseless. If you prefer to see the code, the clone and build path is two more commands.
git clone https://github.com/jbensmann/mouseless.git
cd mouseless
go build .The module requires Go 1.22 or newer, and the release builds are produced with goreleaser, with the release configuration in .goreleaser.yaml. The project also publishes a CHANGELOG.md and a .golangci.yml for linting.
Running it needs root, and there is a stuck key caveat
The program is run with elevated privileges and an explicit config path.
sudo mouseless --config ~/.config/mouseless/config.yamlRoot is needed because the tool writes to the kernel input subsystem to create its virtual pointer, and the config path is given explicitly rather than discovered, so the same command works from a systemd unit or a script.
The caveat in the readme is the one to know before you start typing. After starting, you should release all keys and wait one second before typing, otherwise keys might get stuck in the pressed state. If that happens, you stop the program and press the stuck key before starting again. That is a small artefact of a program that takes over the keyboard while it runs, and it is exactly the kind of detail that is nicer to read than to discover.
For troubleshooting there is a --debug flag that shows more verbose log messages, and the same flag is how you discover key names for the configuration.
The readme's table of contents also lists sections for running without sudo, running at startup with systemd, working with custom devices, pointer acceleration, and disabling the touchpad while typing, so the project addresses the everyday friction of a tool like this rather than only the movement itself.
The layout of the Go rewrite, and where it differs from its predecessor
The repository top level shows how the project is decomposed, and the names map onto the pipeline.
keyboard/ is the input side, virtual/ is where the virtual pointer device lives, handlers/ holds the logic that turns events into actions, actions/ holds the action implementations themselves, config/ holds the configuration handling, and main.go ties them together. There is an example_configs/ directory, an install.sh, a build.sh and a .golangci.yml.
The dependencies confirm the design. golang-evdev reads the physical keyboard, jbensmann/uinput writes the virtual pointer, go-flags handles the command line, logrus does the logging that the --debug flag controls, and yaml.v2 parses the configuration. The go.mod declares Go 1.22 and the module path github.com/jbensmann/mouseless.
The alternative to this approach is the one its predecessor represents, and the difference is where the pointer is synthesised. Injecting pointer motion through a display server is tied to that server, which is why the readme highlights Wayland compatibility as a feature of the rewrite rather than a detail. Reading evdev and writing uinput puts the tool below the display server, so the same binary works in an X11 session and in a Wayland one.
The second alternative is a hardware pointing device, which the readme's list of reasons for controlling the mouse with the keyboard makes clear is the other side of the same coin: keep your hands on the keyboard, cope with a poor touchpad, work with no mouse nearby, need precise control, or do it for fun.
Editorial conclusion
mouseless fits a Linux user who wants to keep both hands on the keyboard, who has a trackpad they dislike or none at all, or who needs precise pointer steps, and who is willing to give one program root access to the input subsystem. It is the wrong tool if you need a pointer that works without root, if your session is on a system where a virtual uinput device is not permitted, or if you simply want a pointing device rather than a keyboard driven one. Check first that your distribution permits uinput access, that the key names in your config match the layout you type on, and that the layer switching bindings you rely on do not collide with the keys your terminal or editor already uses.
Frequently asked questions
How do I install mouseless on Linux?
Download mouseless_linux_amd64.tar.gz from the Releases page and put the executable in /usr/local/bin, or run the install.sh convenience script which installs the latest release and creates a config file, or build from source with go install github.com/jbensmann/mouseless@latest.
Does mouseless work on Wayland?
The readme states that it works across all Linux distributions, including those running Wayland. The project is the successor to xmouseless and reads the keyboard through evdev while writing a virtual pointer through uinput.
How do I find the key names to use in the mouseless config?
Start mouseless with the --debug flag, press the key, and look for output such as Pressed: rightalt (100). The name before the parenthesis and the keycode inside it are both usable in the configuration.
Why do keys get stuck when I start mouseless?
The readme says to release all keys and wait one second before typing after starting, otherwise keys may stay in the pressed state. If it happens, stop mouseless and press the stuck key before starting it again.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/jbensmann-mouseless)Was this useful?