Multi MouseMover is a Windows keyboard-to-mouse utility written in Rust. It installs a global keyboard hook, routes configured key chords into mouse actions, and shows lightweight overlays for status, jump grids, help, and optional final cursor adjustment.
- Install Rust and make sure
cargois available in yourPATH. - Build the release binary:
cargo build --release- Run it from the project root so the checked-in
config.tomlis found:
cargo run --releaseAdministrator privileges are often needed for Windows global hooks. Configuration lives in config.toml; edit it, then press RightAlt+R to reload without restarting. If reload validation fails, the old runtime config stays active.
The crate and binary id is multi_mousemover. On Windows, cargo build produces target/debug/multi_mousemover.exe, and cargo build --release produces target/release/multi_mousemover.exe.
System bindings are always available, even while the app is idle:
| Key | Action |
|---|---|
Ctrl+Q |
Toggle active/idle mode |
Ctrl+Escape |
Exit |
RightAlt+Escape |
Panic reset |
Action bindings run only while active mode is enabled. The checked-in and runtime-default keymap is the ergonomic E/S/D/F layout:
| Key | Action |
|---|---|
E / S / D / F |
Move up / left / down / right |
LeftShift |
Slow held movement |
Z |
Surgical held precision modifier (hold with movement key) |
J / K / L |
Left / right / middle click |
N |
Toggle left-button drag mode |
. |
Click, then switch to idle |
W / R |
Wheel up / down |
M / , |
Mouse speed down / up |
U / I |
Wheel speed down / up |
RightAlt+C / RightAlt+X |
Next / previous movement profile |
RightAlt+V / RightAlt+B |
Next / previous wheel profile |
T |
Jump mode |
G |
Grid mode |
0 |
UI Hints mode |
B |
Enter bookmark mode |
1..9 |
Save bookmark slot 1..9 (in bookmark mode) / recall slot 1..9 (outside bookmark mode) |
V |
Show bookmark markers / bookmark list fallback |
Backspace+1..9 |
Clear bookmark slot 1..9 (in bookmark mode) |
Escape |
Cancel bookmark mode or active exclusive overlays/modes |
C |
Screen select |
H |
Navigate back |
Y |
Navigate forward |
Q / P |
Switch to idle |
RightAlt+E / RightAlt+S / RightAlt+D / RightAlt+F |
Jump to monitor edge (top / left / bottom / right) |
RightAlt+Ctrl+E / RightAlt+Ctrl+S / RightAlt+Ctrl+D / RightAlt+Ctrl+F |
Jump to active-window edge (top / left / bottom / right) |
RightAlt+Ctrl+H / RightAlt+Ctrl+Y |
Jump to active-window center / titlebar |
Alt+E / Alt+S / Alt+D / Alt+F |
Step move up / left / down / right (normal tier) |
Alt+Ctrl+E / Alt+Ctrl+S / Alt+Ctrl+D / Alt+Ctrl+F |
Step move up / left / down / right (small tier) |
Alt+Shift+E / Alt+Shift+S / Alt+Shift+D / Alt+Shift+F |
Step move up / left / down / right (large tier) |
RightAlt+R |
Reload config.toml |
/ |
Toggle help tooltip/panel with runtime stats + keybinds |
The app starts active. Press Ctrl+Q to switch between active and idle mode. In active mode, configured action bindings are swallowed and translated into mouse commands. In idle mode, normal action bindings are ignored so the same keys can pass through to Windows and other apps.
System bindings bypass the active-mode gate. The configured toggle-active chord can always reactivate control, and Ctrl+Escape remains the configured exit key.
Bookmark mode is configurable under [bookmarks] in config.toml (including slot count, key validation fallbacks, desktop behavior, and coordinate policy). Visual bookmark markers are configured under [bookmark_markers]; by default the show_bookmarks binding (V) shows marker overlays, with the older text-list tooltip retained as the fallback when marker display is disabled or unavailable. The bookmark JSON file path is resolved relative to the resolved config file path when a relative bookmarks.file value is used.
Press N to toggle left-button drag mode. When drag is on, Multi MouseMover holds the left mouse button down so movement keys can drag windows, text selections, sliders, or canvas objects. Press N again to release it.
RightAlt+Escape performs a panic reset: it releases drag, clears currently held actions, exits jump mode, hides overlays, resets runtime speed tiers, and follows system_bindings.panic_reset_sets_idle (the default is true, so it returns to idle mode).
Hold W or R for repeated vertical wheel ticks. Horizontal wheel bindings are commented out in the checked-in config.toml by default. Wheel repeat timing comes from [wheel].tick_interval; wheel strength comes from the current wheel speed and axis multipliers.
Mouse movement has two layers. [mouse_speed] controls the baseline tier changed by M and ,; acceleration, acceleration_rate, and top_speed then shape how held movement ramps while a direction key is down. LeftShift slows movement while held.
Profiles let you swap groups of speed settings at runtime:
["RightAlt+C", "movement_profile_next"]
["RightAlt+X", "movement_profile_previous"]
["RightAlt+V", "wheel_profile_next"]
["RightAlt+B", "wheel_profile_previous"]Runtime action syntax also supports direct profile selection with either movement_profile:<name> / movement_profile.<name> and wheel_profile:<name> / wheel_profile.<name>.
Press T to enter jump mode. A labeled grid appears over the start region. Type the visible cell label to narrow the target or complete the jump. The default config uses precision mode with coarse, fine, and precise stages.
The base [jump] table chooses the mode, start region, cursor behavior between stages, preview edge behavior, and visuals. Each stage table controls grid size, target region behavior, zoom, context margin, aim point, and labels.
Jump profiles live under jump.profiles.<name> and merge over the base jump settings for one launch. Profiles can override just the fields they need:
["T", "jump_mode"]
["RightAlt+F", "jump_mode_profile:precise"]
["RightAlt+3", "jump_mode_profile.window"]The default config includes fast, precise, window, and monitor profiles. fast is a single-stage jump. precise keeps multiple stages and previews between them. window starts from the active window bounds. monitor starts from the current monitor.
Copy-pasteable jump configuration:
[jump]
mode = "precision"
cursor_between_stages = "none"
start_region = "virtual_screen"
preview_edge_behavior = "clamp"
[jump.visuals]
selected_region_outline = true
preview_outline = true
active_grid_outline = true
cell_centers = false
final_crosshair = true
[jump.coarse]
enabled = true
width = 10
height = 10
aim_point = "center"
aim_offset_x_px = 0
aim_offset_y_px = 0
target_region_mode = "exact_region"
target_margin_percent = 0
visual_context_margin_percent = 0
zoom_scale = 1.0
[jump.coarse.labels]
font_scale = 1.0
center_marker = false
separators = true
hide_threshold_px = 0
[jump.fine]
enabled = true
width = 8
height = 8
aim_point = "center"
aim_offset_x_px = 0
aim_offset_y_px = 0
target_region_mode = "exact_region"
target_margin_percent = 0
visual_context_margin_percent = 1
zoom_scale = 1.5
[jump.fine.labels]
font_scale = 1.0
center_marker = false
separators = true
hide_threshold_px = 0
[jump.precise]
enabled = true
width = 5
height = 5
aim_point = "center"
aim_offset_x_px = 0
aim_offset_y_px = 0
target_region_mode = "exact_region"
target_margin_percent = 0
visual_context_margin_percent = 5
zoom_scale = 2.5
[jump.precise.labels]
font_scale = 1.0
center_marker = false
separators = true
hide_threshold_px = 0UI Hints mode discovers accessible controls from the foreground app and overlays short labels so you can jump the cursor directly to UI targets. Example binding in config.toml:
["0", "ui_hint_mode"]Interaction flow:
- Press the UI Hints binding.
- Labels appear after the UI Automation (UIA) query completes.
- Type the label for your target control.
Escapecancels and exits UI Hints mode at any point.
Optional tooltip events for query lifecycle can be toggled under [tooltip_overlay.events]:
ui_hints_query_startui_hints_query_failui_hints_query_emptyui_hints_query_capped_count
Limitations and caveats:
- Some apps expose weak or incomplete UIA trees, so hint coverage can be sparse or inconsistent.
- Elevation boundaries apply: if the foreground app is elevated and Multi MouseMover is not (or vice versa), UIA visibility and interaction can be reduced.
- Custom-rendered or game UIs often provide little/no actionable UIA metadata, so hints may be missing.
- High-density UI apps (especially browsers/Electron apps) can generate crowded overlays; tune
ui_hints.min_hint_spacing_pxandui_hints.max_hintsto reduce noise.
[final_adjust] is disabled by default. When enabled, a completed jump enters a small adjustment state before the cursor move is committed. Use the movement keys to nudge by small_step_px; hold Shift for large_step_px; press Enter to confirm, Escape to cancel, or Backspace to return to the previous jump stage. The status overlay can show when final-adjust is active.
[status_overlay] controls the compact persistent status indicator. It can show active/idle, drag, slow movement, surgical held precision state, jump state, speed flashes, wheel state, and final-adjust state.
[tooltip_overlay] controls temporary explanatory messages and the larger help panel toggled by /. Temporary tooltips are short-lived notices for runtime changes such as mouse speed, wheel speed, profile changes, drag, reload, and panic reset. The help panel does not auto-expire; it shows current runtime stats plus configured keybinds until / toggles it again, Escape dismisses it, or the app enters an exclusive mode such as jump.
Press RightAlt+/ (or bind keybind_lookup_mode to another chord) to enter keybind lookup mode. The next key-down is swallowed and shown in the help/tooltip overlay with the matched action or system binding, competing bindings, preserved-shortcut status, and an explanation of the routing decision. This uses the same chord specificity as runtime dispatch, so side-specific bindings such as RightAlt+E are reported as winning over generic Alt+E.
Use enabled = false to disable all tooltip/help overlay rendering, show_temporary_tooltips = false to keep / help while hiding short runtime notices, and show_help = false to keep runtime notices while disabling the help panel. Individual temporary trigger classes can be controlled under [tooltip_overlay.events]:
[tooltip_overlay]
enabled = true
show_temporary_tooltips = true
show_help = true
positioning = "cursor"
duration_ms = 900
help_positioning = "center"
help_width = 420
help_max_bindings = 40
[tooltip_overlay.events]
mouse = true
wheel = true
profile = true
drag = true
reload = true
panic = true
surgical = trueThe tooltip/help overlay is intentionally separate from [status_overlay]: status is a small always-on indicator for current state, while tooltip/help is temporary explanatory text or the explicit Slash panel.
Jump overlays are configured under [jump.visuals] and per-stage label tables. You can show or hide selected-region outlines, preview outlines, active-grid outlines, cell centers, and the final crosshair.
Ctrl+Escape exits through [system_bindings]. RightAlt+Escape is the runtime panic reset and is useful if drag is stuck, a jump overlay is active, or held keys need to be cleared.
RightAlt+R reloads config.toml atomically from the user perspective: invalid configs are rejected and the last valid runtime config remains active. Compatibility fields are still accepted for older configs, but new configs should use the current field names documented in DEVELOPMENT.md.
For input-pipeline regressions, run the diagnostic smoke test in docs/smoke_test_input_pipeline.md. It uses the current MULTI_MOUSEMOVER_DEBUG=1 flag and lists the expected startup, heartbeat, key routing, command, click, and exit log fragments.
This project is licensed under the terms of the MIT license. See LICENSE for details.
Bookmarks are in-memory/session-backed by default with optional JSON persistence via [bookmarks].
Default flow:
- Press
Bto enter bookmark mode. - Press
1..9while in bookmark mode to save to that slot. - Press
Backspace+1..9while in bookmark mode to clear that slot. - Press
1..9outside bookmark mode to recall that slot. - Press
Escapeto cancel bookmark mode without changing slots. - Press
V(show_bookmarks/bookmark_list) to show visual bookmark markers by default. If[bookmark_markers].enabled = falseor marker display is otherwise unavailable, the old text-list tooltip behavior remains the fallback.