Skip to content

Latest commit

 

History

6 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Mouseless

Your hands are on the home row. The button you want is on the far side of the screen. Between the two sits a mouse, and reaching for it is the slowest thing you will do this minute.

Mouseless is a keyboard-driven pointer for Omarchy. Press a key, the screen fills with labels, you type three letters, and the pointer lands on a 15x14px target and clicks. That is close-button precision in fewer keystrokes than it takes to find where the cursor wandered off to. It does what warpd does, as a native overlay plugin for the Omarchy shell.

Mouseless hint grid over a terminal

Under the hood: two letters pick a cell from a 26x26 grid on a 1920x1080 screen, then one letter from a keyboard-shaped block picks the exact spot inside it. Arrow keys, double click, right and middle click, move-without-click, and a scroll mode are all one modifier or one key away.

Full disclosure: this idea is not new, and the section below points you at the alternatives. Mouseless started as an excuse to learn Quickshell and see how far an Omarchy plugin could go, and it was mostly vibe coded with an AI pair. The security section further down exists because of that: every shell call was reviewed by hand and is spelled out there. If it also turns out to be the pointer you reach for every day, even better.

How it compares

The marketplace also lists OmaGrid by GrzeskoByte, which takes the same approach with a coarser 16x9 grid, Shift-to-zoom refinement across up to five levels, and clicks through ydotool. Mouseless reaches finer precision in fewer keys, adds right, middle, and double clicks, move-only, and scrolling, and clicks through wlrctl, which needs no daemon and no /dev/uinput access. If you prefer arbitrary zoom depth or per-column grid weights, use OmaGrid.

Requirements

  • Omarchy with the Quickshell-based shell (omarchy-shell) on Hyprland. The plugin uses Hyprland's Lua dispatcher to warp the pointer and a Hyprland submap for scrolling, so it is not portable to other compositors as is.
  • wlrctl from the AUR for clicks and scrolling. It speaks the wlr-virtual-pointer protocol: no daemon, no root, no uinput. Install it with omarchy pkg aur add wlrctl.

Install

omarchy pkg aur add wlrctl
omarchy plugin add https://github.com/wkuehler/mouseless.git --enable

Bind a key

Add to ~/.config/hypr/bindings.lua (SUPER+M is unbound in stock Omarchy):

o.bind("SUPER + M", "Mouse: hint grid", "omarchy-shell shell toggle wkuehler.mouseless")

Then hyprctl reload.

Using it

Pick a target. Labels cover the screen in a grid. Type the row letter, then the column letter. The cell splits into a block of single-letter subcells laid out like the left of a keyboard (q w e r t / a s d f g / z x c v b) and a third letter picks one. Or move a highlight with the arrow keys: it starts on the cell under the pointer, Enter drills into the cell, the arrows move within the block, and Enter picks the subcell. Letters and arrows mix freely. Backspace steps back one level, + and - resize the grid, and Esc closes without doing anything.

Act on it. The final key or Enter does the action:

Modifier Action
none left click
Shift right click
Ctrl middle click
Alt double click
Space (instead of the final key) move the pointer there, no click
Tab (instead of the final key) move the pointer there and enter scroll mode

The overlay closes, the pointer warps, and the click is sent to whatever is underneath.

Scroll. Tab parks the pointer and enters a Hyprland submap named mouseless-scroll; a pill at the bottom of the screen shows while it is active. Arrows or hjkl scroll, Shift scrolls faster, PgUp and PgDn page, Enter clicks where the pointer sits (Shift right, Ctrl middle). Esc, Space, or Tab leave the submap. The submap is defined by the plugin at runtime, so nothing needs adding to bindings.lua.

Settings

Press ? inside the overlay for the settings card. Each row has a key. Changes save immediately to ~/.config/omarchy/mouseless.json, which is also watched, so hand edits apply on the next open.

Key Setting Values
+ - Grid cell size (minimum px per cell; also works outside the card) 24 to 200
1 Label keys alphabet: a-z rows x a-z columns, up to 26x26. homerow: asdfghjkl; rows x qwertyuiop columns, 10x10
2 Fine pass on: third key refines inside the cell. off: second key clicks the cell centre
3 Fine block rows 3 (qwert / asdfg / zxcvb) or 2 (qwer / asdf)
4 Background dim 0%, 25%, 50%
5 Status bar shown or hidden
6 Arrow highlight starts under the pointer or at screen centre
7 Third level off, or on: after the fine key the subcell splits into an unlabeled 4x3 keyed qwer / asdf / zxcv, Enter clicks the subcell centre. Pairs well with home-row labels or a coarser grid, where a subcell is 30px or more
0 Reset to defaults

Shell IPC

The overlay answers the shell's standard plugin calls:

omarchy-shell shell toggle wkuehler.mouseless
omarchy-shell shell summon wkuehler.mouseless
omarchy-shell shell hide wkuehler.mouseless

Permissions and privacy

Omarchy plugins run unsandboxed inside the shell process, so here is exactly what this one does:

  • Runs /usr/bin/hyprctl cursorpos when the overlay opens, to start the arrow highlight under the pointer (only when that setting is on).
  • On a click, move, or scroll request runs /usr/bin/hyprctl dispatch to warp the pointer and /usr/bin/wlrctl pointer move / click / scroll to nudge, click, and scroll. Nothing runs until you press a final key.
  • On entering scroll mode runs /usr/bin/hyprctl eval once to define the mouseless-scroll submap, then hyprctl dispatch to enter it. The submap binds only run /usr/bin/wlrctl and hl.dsp.submap("reset"). It lives in Hyprland's memory until the next Hyprland restart and is never written to your config files.
  • Reads and writes exactly one file it owns, ~/.config/omarchy/mouseless.json, and only when you change a setting or hand-edit it.
  • Makes no network requests and installs no packages.
  • No sudo or pkexec is required.
  • Starts no daemons or background processes.

How those calls are bounded:

  • Executables are named by absolute path. Every argument is either a constant string or an integer computed from the grid geometry; no setting, file content, or key text is ever interpolated into a command. The submap Lua is a constant built from a fixed bind table and is passed to sh as a positional argument, not spliced into the shell string.
  • Each of the three child processes (cursor position, click, scroll mode) is a single instance with a 5-second watchdog that kills it. Their output is not collected, except cursorpos, where only the first line is read and only its first 64 bytes are parsed. Unloading the plugin stops every process and timer.
  • The settings file is capped at 64 KiB and every value is checked against an allowlist; anything else falls back to the defaults. All on-screen text is rendered as plain text.

Two Hyprland behaviours shape the design and are worth knowing:

  • A layer surface with exclusive keyboard focus also receives every pointer event, regardless of its input region. That is why the overlay closes before it clicks, and why scrolling happens from a submap rather than from inside the overlay.
  • wlrctl scroll and click events only reach an app after the virtual pointer has sent a motion event, so every warp is followed by a one-pixel nudge out and back.

Development

Install the plugin as above, then edit the files under ~/.config/omarchy/plugins/wkuehler.mouseless directly, or point that checkout's origin at your fork. Apply changes with omarchy restart shell: this overlay is keepLoaded, and the shell's file watcher does not replace a running keepLoaded component. Run scripts/check before committing: it validates the manifest, runs the HintModel.js tests under node, and runs qmllint when it is installed.

Layout:

File Role
manifest.json Plugin manifest: overlay kind, entry point.
Mouseless.qml The overlay, key handling, settings card, scroll pill, and the shell-outs.
HintModel.js Pure functions: grid and fine-block geometry, labels, settings defaults and sanitising.
tests/hint-model.test.js Unit tests for HintModel.js.
scripts/check All offline checks in one command.
docs/PLAN.md Design notes and findings.

Shell QML errors show up in qs log -i <instance> -t 100; find the instance with qs list --all.

Remove

omarchy plugin remove wkuehler.mouseless

This disables the plugin and deletes the installed copy. It does not touch the keybind you added, the wlrctl package, or ~/.config/omarchy/mouseless.json; remove those yourself for a fully clean uninstall.

License

MIT. See LICENSE.

About

Keyboard-driven mouse pointer for the Omarchy shell: hint grid, clicks, and scrolling without touching the mouse

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages