Skip to content

museslabs/stochos

Repository files navigation

stochos

stochos (/'sto.xos/) — from Greek στόχος: aim, target, goal.

Keyboard-driven mouse control overlay for Wayland, X11, and macOS. OSS alternative to mouseless.

example

Displays a letter grid over your screen. Type a two-key combo to jump to a cell, refine with a sub-grid key, then act. Runs once per invocation (no daemon).

Wayland: Tested on Hyprland. Should work on any wlroots-based compositor with zwlr_layer_shell_v1 and zwlr_virtual_pointer_v1.

X11: Tested on i3. Should work on any X11 window manager with the XTest extension.

macOS: Single-display only. Requires Accessibility permission (see Setup → macOS below). Cold-start launches dismiss Control Center / WiFi-brightness widgets; other system menus and notification center are unaffected.

Install

With curl:

curl -fsSL https://raw.githubusercontent.com/museslabs/stochos/main/install.sh | sh

With cargo:

cargo install stochos

From source:

git clone https://github.com/museslabs/stochos
cd stochos
cargo build --release                                          # Linux: both backends
cargo build --release --no-default-features --features wayland # Wayland only
cargo build --release --no-default-features --features x11     # X11 only
cargo build --release --no-default-features                    # macOS

Setup

Hyprland

Bind it to a key in hyprland.conf:

bind = , SUPER_L, exec, stochos

i3

Bind it to a key in ~/.config/i3/config:

bindsym Super_L exec stochos

macOS

Bind it to a key with your launcher of choice (Raycast, skhd, Aerospace, etc.). For example, in ~/.aerospace.toml:

alt-enter = 'exec-and-forget /usr/local/bin/stochos'

On first launch, macOS will prompt for Accessibility permission. Open System Settings → Privacy & Security → Accessibility and enable the binary (or the terminal you launched from). stochos uses the permission to capture global keystrokes via CGEventTap and to synthesize mouse events via CGEventPost.

If CGEventTapCreate failed is printed instead, also enable the binary under Input Monitoring.

Released binaries are not codesigned with a Developer ID, so after every upgrade macOS treats the new binary as a different program and re-prompts for Accessibility (and Input Monitoring, if applicable). Re-enable it in System Settings each time. Install via the curl | sh command above — curl does not set the Gatekeeper quarantine attribute, so the binary runs without a "cannot be opened" prompt.

Usage

  1. Trigger the overlay
  2. Type two letters to select a grid cell (e.g. a then s)
  3. Type one more letter to refine within the sub-grid
  4. Perform an action (see below)

Default keys

Key Action
Space Click
Enter Double click
m Triple click
Delete Right click
Insert Middle click (macOS is not supported)
Escape Close overlay
Backspace Undo last step
Arrow keys Scroll (up/down/left/right)
/ Start drag (select end point, then Space)
` Toggle macro recording
@ Replay macro by bind key
Tab Search macros / quick-save position
b Switch to bisect mode
n Switch back to normal mode (from bisect)
v Switch to free mode

All keys are configurable (see Configuration below).

CLI flags

Flag Effect
--bisect Start the overlay directly in bisect mode
--free Start the overlay directly in free mode (cursor starts at current mouse position)
--free-center Start the overlay directly in free mode with the cursor at the center of the screen
--allow-multiple Skip the single-instance lock
--print-default-config Print the default config (TOML) to stdout and exit. Diff against your config.toml to discover options added by newer versions

Bisect mode

An alternative grid mode that recursively subdivides. Instead of two-key combos and a sub-grid, each hint press zooms the grid into the cell you chose, re-rendering a fresh grid inside that region. Keep pressing hints to home in, then click.

  • Enter bisect mode: press b from normal mode, or launch with --bisect
  • Each hint key splits the current region into a smaller grid (2x2 by default)
  • Space / Enter / m / Delete / Insert act (click / double-click / triple-click / right-click / middle-click) at the center of the current region and exit
  • Backspace pops back up one level
  • Subdivision stops automatically once a cell would fall below min_cell_size pixels — the region is highlighted and you can act or back out
  • Press n to switch back to normal mode

Free mode

An alternative mode for direct keyboard-driven cursor movement. Instead of selecting a grid cell, you steer the cursor continuously with movement keys, then act.

  • Enter free mode: press v from normal or bisect mode, or launch with --free (starts at mouse position) or --free-center (starts at screen center)
  • Move the cursor with h / j / k / l (left / down / up / right)
  • Press = to increase speed and - to decrease it; the current speed is shown in the top-left corner
  • Space / Enter / m / Delete / Insert act (click / double-click / triple-click / right-click / middle-click) and exit
  • Backspace returns to the previous mode; Escape closes the overlay
  • Key repeat is supported: hold a movement key to move continuously

Limitations: drag and text selection are not available in free mode. Use normal mode for those actions.

Key repeat cadence: On Wayland the overlay drives its own repeat (300 ms delay, 25 Hz). On macOS and X11 the OS provides repeats, so the initial delay and rate follow your system keyboard settings (System Settings → Keyboard on macOS, xset r rate on X11).

Macros

Record multi-step mouse sequences for replay.

Record: Press ` to start recording. Navigate and act normally (Space to click, Enter to double-click, m to triple-click, Tab to hover-only, / to drag). Press ` again to stop. You'll be prompted for an optional bind key and a name.

Replay: Press @ then the bind key. Or press Tab to search by name, then Enter to select.

Timing. Stochos captures the delay between each recorded action and replays at the same rhythm, so animations and page loads have time to settle. The first action has no wait (you weren't waiting for anything to start the macro). On replay, delays are scaled by macros.playback_speed in config.toml (default 1.0, 2.0 plays twice as fast, 0.0 or negative plays instantly with no waits). You can also hand-edit the per-action wait_ms values in macros.json for fine-grained tuning.

Macros are resolution-independent and stored at ~/.config/stochos/macros.json. Macros recorded before this version still load and replay (without inter-action delays).

Configuration

Config file location: ~/.config/stochos/config.toml (respects XDG_CONFIG_HOME).

All fields are optional. Missing fields use defaults.

font_size = 2  # Glyph scale multiplier for the 8x8 bitmap font: 1=8px, 2=16px, 3=24px
sub_hint_font_size = 2  # Optional override for sub-grid hint glyphs; defaults to font_size when omitted
panel_font_size = 2  # Optional override for macro/search popup panels; defaults to sub_hint_font_size, then font_size

[grid]
hints = ["a", "s", "d", "f", "j", "k", "l", ";", "g", "h", "q", "w", "e", "r", "t", "y", "u", "i", "o", "p"]
sub_hints = ["a", "s", "d", "f", "j", "k", "l", ";", "g", "h", "q", "w", "e", "r", "t", "y", "u", "i", "o", "p", "z", "x", "c", "v", "b"]
sub_cols = 5
target_cell_size = 90  # Enable dynamic grid by setting this value

[bisect]
hints = ["a", "s", "d", "f"]  # One per cell, row-major (length must be >= rows * cols)
rows = 2
cols = 2
min_cell_size = 16  # Stop subdividing once a cell would be smaller than this, in pixels

[macros]
playback_speed = 1.0  # 1.0 = recorded speed, 2.0 = twice as fast, 0.0 or negative = instant (no waits)

[keys]
normal = "n"
bisect = "b"
free_mode = "v"
click = "space"
double_click = "enter"
triple_click = "m"
close = "escape"
undo = "backspace"
right_click = "delete"
middle_click = "insert"
scroll_up = "up"
scroll_down = "down"
scroll_left = "left"
scroll_right = "right"
macro_menu = "tab"
macro_record = "`"

[colors]
# Grid colors - Maximum visibility in all conditions
cell_normal = "#00000050"        # Semi-black overlay (works on light backgrounds)
text_dim = "#ffffff30"           # Semi-white dim (slightly visible on dark)
sub_cell_normal = "#20202080"    # Dark grey with good opacity
sub_bg = "#20202080"             # Matches sub cell
text_first = "#00ff88ff"         # Teal-green (good in sunlight)
text_second = "#ff8800ff"        # Orange (high visibility)
cell_highlight = "#00000028"     # Subtle dark tint (no color, just darkens slightly)
text_highlight = "#ffff00ff"     # Pure yellow (maximum contrast)
cell_drag = "#ff00ddaa"          # Hot pink (extremely visible)
panel_bg = "#121212f5"           # Near-black (96% opaque) - creates strong contrast barrier
text_white = "#f5f5f5ff"         # Off-white (comfortable for long reading)
text_grey = "#b8b8b8ff"          # Light grey (maintains 4.5:1 contrast ratio)
selected_bg = "#2196f3ff"        # Material Design Blue (works in light/dark)
rec_bg = "#f44336ff"             # Material Design Red (urgent, visible everywhere)
border = "#00e676ff"             # Material Green (fresh, visible)
border_dragging = "#e91e63ff"    # Material Pink (strong attention grabber)
crosshair = "#ffaa00ff"          # Amber crosshair in free mode

[free]
left = "h"
down = "j"
up = "k"
right = "l"
fast = "="
slow = "-"
base_speed = 25
fast_multiplier = 3.0
slow_multiplier = 3.0
min_speed = 1
max_speed = 500

Font

  • font_size sets the glyph scale multiplier. Stochos uses an 8x8 bitmap font, so each step adds 8px: 1=8px, 2=16px, 3=24px, and so on. Default is 2.
  • Increase font_size for high-DPI displays such as 3 or 4 on 4K monitors. Valid range: 1-10.
  • sub_hint_font_size sets the glyph scale multiplier for sub-grid hints. If omitted, it inherits font_size. It uses the same 8px steps and 1-10 range, and still clamps down to fit inside each sub-cell.
  • panel_font_size sets the glyph scale multiplier for macro and search popup panels. If omitted, it inherits sub_hint_font_size, or font_size if that is also unset. It uses the same 8px steps and 1-10 range.

Grid

  • hints sets the characters for the main grid. Grid size is len(hints) x len(hints) (default 20x20 = 400 cells).
  • sub_hints sets the characters for the sub-grid. Sub-grid size is sub_cols x (len(sub_hints) / sub_cols) (default 5x5 = 25 cells).
  • sub_cols sets how many columns the sub-grid has.

Bisect

  • hints sets the characters for each bisect cell, in row-major order. Must contain at least rows * cols entries; extras are ignored.
  • rows and cols set the grid shape used at every subdivision level (default 2x2).
  • min_cell_size is the pixel floor at which subdivision stops. The glyph scale auto-shrinks to fit the cell, so lowering this lets you reach tiny regions.

Free mode

  • left, down, up, right set the movement keys (default: h, j, k, l).
  • fast / slow increase or decrease the step size by fast_multiplier / slow_multiplier (default: = and -).
  • base_speed is the initial step size in pixels (default: 25).
  • fast_multiplier / slow_multiplier are the scale factors applied when pressing the speed keys (default: 3.0).
  • min_speed / max_speed clamp the step size in pixels (default: 1 and 500).

Macros

  • playback_speed scales the per-action delays recorded into each macro. 1.0 plays at the recorded rhythm, 2.0 plays twice as fast, 0.5 plays at half speed. Setting it to 0.0 (or any negative value) disables waits entirely for instant playback. Default is 1.0.

Keys

Any keyboard key can be bound to any action.

Character keys use the character itself: "a", ";", "/", "@", "!".

Special keys use snake_case names:

Name Key
space Space
enter Enter
escape Escape
backspace Backspace
tab Tab
delete Delete
insert Insert
home Home
end End
page_up Page Up
page_down Page Down
up down left right Arrow keys
f1 through f12 Function keys
caps_lock num_lock scroll_lock Lock keys
print_screen pause context_menu System keys
num_pad_0 through num_pad_9 Numpad digits
num_pad_add num_pad_subtract num_pad_multiply num_pad_divide num_pad_decimal num_pad_enter Numpad operators

Shifted characters work too: "@", "!", "~", "A" (uppercase), etc.