A tiny Windows system-tray application that does what a USB "mouse jiggler" dongle does, without the dongle. When your machine has been idle for a while it nudges the pointer one pixel and immediately moves it back — enough to keep the display awake and keep presence indicators from flipping to Away.
Single ~3 MB executable. No installer, no admin rights, no console window, no runtime dependencies, no network access.
- Install
- Using it
- How it decides when to nudge
- Configuration
- Troubleshooting
- Uninstall
- Security and privacy
- Building from source
- Architecture
- Project status
- License
- Download
mousemover.exefrom the latest release. - Put it anywhere you like —
%LOCALAPPDATA%\Programs\is a good spot. - Double-click it. Look for the icon in the notification area; you may need
to expand the overflow arrow (
^) to see it. - Click the icon and tick Enabled.
Optionally verify the download against the published checksum:
Get-FileHash mousemover.exe -Algorithm SHA256Compare the result with the mousemover.exe.sha256 file attached to the same
release.
Requirements: Windows 10 or 11, 64-bit. No .NET, no Visual C++ redistributable, nothing else to install.
Everything lives in the tray icon's menu:
| Item | What it does |
|---|---|
| Enabled | Turns nudging on and off. Icon is blue when on, grey when off. |
| Idle threshold | How long the machine must sit untouched before a nudge is allowed. 30s / 1m / 2m / 5m. Default 1 minute. |
| Nudge interval | How often the idle check runs while enabled. 15s / 30s / 1m. Default 30 seconds. |
| Start with Windows | Adds or removes a per-user startup entry. No admin rights needed. |
| Quit | Exits. Your settings are saved on the way out. |
The icon and its tooltip always reflect live state, so you can tell at a glance
whether it is armed: Mouse Mover — enabled (1m0s idle) or
Mouse Mover — disabled.
This is the part that makes it pleasant to leave running, so it is worth being precise about.
Every nudge interval, the app asks Windows how long it has been since your
last real keyboard or mouse input (GetLastInputInfo). If that figure is below
your idle threshold, it does nothing at all. Only once you have genuinely
stopped touching the machine does it inject a one-pixel relative move followed
immediately by the opposite move.
Timing: because the check is periodic, a nudge lands at the first check
after the threshold is crossed — so the real bound on idle time is
idle threshold + nudge interval, up to 90 seconds with the defaults. Size
your screen timeout against that number, not against the threshold alone.
Two consequences worth knowing:
- It will never fight your cursor. While you are actively working, it is inert — it cannot jump your pointer mid-click or mid-drag, because it does not act at all until you have been idle past the threshold.
- The pointer does not drift. Each nudge is
+1pxthen−1px, so the cursor finishes exactly where it started. Left running overnight, it ends up in the same place, not in the corner of the screen.
The nudge goes through SendInput, the standard Windows input-injection API,
so Windows and applications see it as genuine input — which is what keeps the
display awake and presence indicators green.
Settings live in %APPDATA%\mousemover\config.json and are written whenever
you change something in the menu:
{
"enabled": true,
"idle_threshold": "1m0s",
"nudge_interval": "30s"
}| Field | Type | Default | Notes |
|---|---|---|---|
enabled |
bool | false |
Whether nudging is armed. |
idle_threshold |
duration string | "1m0s" |
Idle time required before nudging. |
nudge_interval |
duration string | "30s" |
How often the idle check runs. |
Durations are Go duration strings — "45s", "90s", "2m", "1m30s". Both
are clamped to between 5 seconds and 60 minutes; values outside that range
are silently corrected on load.
Editing the file by hand is supported — do it while the app is closed, since it rewrites the file on exit. A missing or corrupt config is never fatal: the app falls back to defaults, logs a warning, and repairs the file on the next save.
The tray menu only offers a few common values. If you want something else (say a 90-second threshold), set it in the config file — the app will honour it, though no menu item will appear ticked.
The tray icon doesn't appear. Windows hides new tray icons by default.
Click the overflow arrow (^) next to the clock; drag the icon onto the
taskbar to pin it. If it is not there either, check the log (below) — the app
may have exited during startup.
It's enabled but the screen still sleeps. Your Windows screen-off timeout needs to be longer than idle threshold + nudge interval, not just longer than the threshold.
The idle check runs on a fixed interval, so a nudge fires at the first check after the threshold is crossed — worst case, one full interval late. With the defaults (1 minute threshold, 30 second interval) the pointer can sit idle for up to 90 seconds before it is nudged, so a screen timeout of 1 minute would still win the race. Measured on real hardware: with those defaults, observed idle peaked at 83 seconds before the nudge landed.
Give yourself margin — a 3-minute screen timeout against the 1-minute default is comfortable. If you need to defeat a short timeout, lower both values: a 30-second threshold with a 15-second interval bounds worst-case idle at 45 seconds. Also confirm the icon is blue, not grey.
Nothing happens / no nudges. Read %APPDATA%\mousemover\mousemover.log.
Every failure is recorded there with a reason. A repeated
nudging the pointer error usually means something is blocking synthetic
input — see the security note below.
"Start with Windows" won't stick. The setting reads its state back from the
registry rather than trusting the click, so if the tick-box reverts, the write
itself failed. That normally means a policy is locking
HKCU\Software\Microsoft\Windows\CurrentVersion\Run. The log will say so.
It disappeared after a reboot. Unless you ticked Start with Windows, it does not launch itself.
Where are the logs? %APPDATA%\mousemover\mousemover.log, append-mode,
plain text. Routine nudges are not logged (that would grow the file without
bound) — errors and state changes are.
There is nothing to uninstall in the Windows sense. To remove it completely:
- Quit from the tray menu.
- Untick Start with Windows before quitting, or manually delete the
mousemovervalue underHKCU\Software\Microsoft\Windows\CurrentVersion\Run. - Delete
mousemover.exe. - Delete
%APPDATA%\mousemover\for the config and log.
What it does: reads the system idle timer, injects mouse movement, and
optionally writes one registry value under HKCU. That is the complete list.
What it does not do: no network access of any kind, no telemetry, no analytics, no auto-update, no keylogging, no reading of window titles or screen contents. It has no code to do any of those things — the source is about 600 lines and you are welcome to read all of it.
The honest caveat: this works by injecting synthetic input through
SendInput. That is a normal, documented Windows API, and nothing here is
hidden, obfuscated, or disguised — the binary is plainly named and the source
is public. But some corporate endpoint-security products flag any
synthetic-input tool on principle, regardless of intent. If your machine is
managed by an employer:
- Check your acceptable-use policy first. Some organisations explicitly prohibit idle-defeating tools.
- Expect the possibility of quarantine or an alert to your security team.
This is not something the software can design around, and no attempt is made to evade detection.
The toolchain is containerised, so Docker is the only prerequisite — you do not need Go installed.
git clone https://github.com/danbadds38/mousemover.git
cd mousemover
make docker-image # build the toolchain image (once, ~2 min)
make docker-dist # full gate, then report the binaryThe result is dist/mousemover.exe. The container runs as your host UID/GID,
so the binary comes out owned by you rather than root, and the Go build and
module caches live on named volumes so repeat builds are fast.
| Target | Does |
|---|---|
make docker-image |
Build the pinned toolchain image. |
make docker-check |
Vet + race tests + Windows cross-compile. The gate. |
make docker-dist |
The gate, then report the built binary. |
make docker-shell |
Drop into a shell in the build container. |
make docker-run CMD="…" |
Run one toolchain command in the container. |
make docker-clean |
Tear down volumes and remove dist/. |
If you have Go 1.26+ installed, every target also works natively without the
docker- prefix: make check, make build-windows, make dist. No cgo and
no C toolchain either way, so it cross-compiles cleanly from Linux and macOS.
The test suite runs on any platform — see below for why.
Four small packages, each with one job:
| Package | Responsibility |
|---|---|
internal/config |
Load/save settings as JSON, atomically, with clamping. |
internal/winapi |
Thin wrappers over GetLastInputInfo, SendInput, and the HKCU Run key. Logic-free. |
internal/mover |
The scheduling engine — decides, each tick, whether to nudge. |
internal/tray |
Menu construction and click wiring. |
The load-bearing design decision: internal/mover never imports
internal/winapi. It talks to the operating system only through a two-method
interface:
type Platform interface {
IdleTime() (time.Duration, error)
Jiggle() error
}That is what allows the entire scheduling logic — idle gating, enable/disable,
timing changes, error handling, concurrency — to be unit-tested with fakes on a
Linux CI runner, despite the product being Windows-only. The ticker is
abstracted the same way, so tests drive time by hand and never sleep. CI
enforces the boundary on every push; if someone imports winapi into mover,
the build fails.
What remains untestable off Windows is the syscall layer itself, which is deliberately kept to a handful of lines per function with no branching logic.
Design notes and the full implementation plan are in docs/.
Working and complete for its stated scope. It is a small tool that does one thing; there is no roadmap, and features beyond the above are not planned.
Verification status, stated plainly: the scheduling logic is covered by 28
automated tests running under the race detector on every push. The Windows
syscall layer compiles and passes GOOS=windows go vet, but CI cannot execute
a Windows binary or inject Windows input — so that layer is verified by
inspection plus manual testing on real hardware, not by machine.
As of v0.2.1, all seven manual checks pass on Windows 11 Pro build 26200,
including measured confirmation that SendInput reaches the system and that
the display stays awake. Details and method in
docs/MANUAL-VERIFICATION.md; the history of
what changed and why is in docs/BUILD-NOTES.md.
That hardware pass matters: it is what caught the SendInput defect in v0.1.0
and v0.2.0, which the entire automated suite had waved through.
Bug reports are welcome, especially anything about behaviour on real hardware.
MIT.