Skip to content
pixelactions

How to automate mouse clicks and keystrokes on macOS, Windows, and Linux

Every OS has a built-in way to synthesize a click — they're below, and for a one-off they're all you need. The part that bites comes later: the coordinate you hardcoded points where the UI was, and nothing in the return value says whether the click worked. The OS accepting an event is not the app reacting to one. Whatever tool you use, know what happens when the button moves.

macOS

Built in adjacent: cliclick (one brew installaway) or AppleScript through System Events. Both are fine for a click you'll watch happen.

cliclick c:812,440                # click at (812,440) in logical points
osascript -e 'tell application "System Events" to click at {812, 440}'

The pitfall: without an Accessibility grant, event posting is a silent no-op — the call succeeds and nothing happens. And the grant attaches to the application that launched the tool (your terminal), not the tool itself. pixelactions proves the grant instead of assuming it, then acts on regions a human marked:

pixelactions doctor --probe       # move the cursor 1px, ask the OS, put it back
pixelactions run --session <dir> click:submit verify:done --yes

A failed probe raises the system dialog and exits 3. Targets are re-located against a fresh capture before acting, and verify confirms the result from another capture — with the cursor-in-a-corner kill switch armed the whole run.

Windows

Built in: PowerShell can send keystrokes today; a mouse click takes a P/Invoke into user32. For anything past a one-liner, the institution is AutoHotkey (full comparison).

Add-Type -AssemblyName System.Windows.Forms
[System.Windows.Forms.SendKeys]::SendWait("hello{ENTER}")   # keystrokes only

The pitfalls: per-monitor DPI scaling means the coordinate a screenshot gave you and the coordinate the input API wants can disagree per display — and input aimed at an elevated window is blocked unless the sender is elevated too.

Honesty note: pixelactions does not run on Windows yet — it's next, and the goal is the same flow file running unmodified.

Linux

Built in on X11: xdotool — the classic answer, still the right one for a one-off.

xdotool mousemove 812 440 click 1   # X11: physical pixels
ydotool click 0xC0                  # Wayland: needs the ydotoold daemon + /dev/uinput access

The pitfall is Wayland: its security design keeps applications from injecting input into each other. xdotool's synthesis reaches only XWayland windows, and ydotool works by becoming a virtual input device — which takes a running daemon and permission on /dev/uinput.

Honesty note: pixelactions targets X11 next, alongside Windows; Wayland comes after.

When one click isn't enough

A click you fire once answers today's question. If the same interaction matters tomorrow — in a script, a test, an agent — the coordinate needs to survive the UI moving, and the run needs to know whether it worked. That's the pixelactions contract: a human marks regions once in pixelcoords, then every run re-locates each target against a fresh capture, refuses rather than guesses, and reports verified as distinct from executed. The front page shows the whole loop in sixty seconds.

Questions people actually ask

Why does my automated click do nothing on macOS?

Almost always the Accessibility grant. Without it, posting an input event is a silent no-op — the call succeeds and nothing happens. The grant attaches to the application that launched your tool (the terminal), not the tool itself. pixelactions doctor --probe proves the permission empirically: it moves the cursor one pixel, asks the OS where it ended up, and puts it back — raising the system dialog if the grant is missing.

How do I automate mouse clicks on Wayland?

With difficulty, by design: Wayland keeps applications from injecting input into each other. xdotool only reaches XWayland windows, and ydotool works by becoming a virtual input device — which needs its daemon running and permission on /dev/uinput. pixelactions targets X11 next, alongside Windows; Wayland comes after.

How can a script tell whether a click actually worked?

The OS accepting an event is not the app reacting to one — a click API returning normally proves nothing. Capture the screen again and check: pixelactions verifies a region against its saved crop after acting and reports verified as distinct from executed, with exit codes a script can gate on: 0 done, 1 a step failed, 2 malformed, 3 refused.

Can I drive desktop automation from my own language?

Yes. pixelactions serve speaks a JSON line protocol on stdin/stdout — one request per line, one response back — so a program in any language owns the loop: branching, retries, data. A complete client is forty lines of stdlib Python, in the docs. There is no embedded interpreter, deliberately.

Is there a maintained alternative to PyAutoGUI?

Depends on the job. As of mid-2026, PyAutoGUI has not shipped a release since May 2023. The browser belongs to Playwright and Selenium; where an accessibility tree exists, a11y-first tools are more robust. For coordinate execution against human-marked regions — canvas apps, legacy software, streamed desktops — pixelactions is built for exactly that, early and macOS-only today.

Two ways in

cargo install pixelactions

Or skip the toolchain: prebuilt binaries — download, unpack, run.

Rust 1.88+ for the cargo route. pixelactions drives the pixelcoords binary for capture-time work — install both:

cargo install pixelcoords pixelactions

macOS asks for an Accessibility grant on first run. The grant attaches to the terminal that launches pixelactions, not the binary — doctor --probe proves the grant instead of assuming it.