Windows’ ClickLock, for macOS

Hold the button.
Let go. It stays down.

Select three pages of text, or drag a file across two screens, without holding the mouse button the whole way. One more click lets go. Works with a mouse and with the trackpad.

Download for macOS Source on GitHub

Version 0.1.0 · 517 KB · Apple silicon · macOS 14 or later

Press and hold the mouse button anywhere in this panel. A ring fills around the pointer. Keep holding until it closes, then let go — the button stays down as far as this page is concerned. Move the pointer with nothing held and the text selects itself, the way every app on your Mac behaves once ClickLatch has the button. One more click and it lets go again.

Hold the button here

Ready.

A recreation of the real thing, in the browser. With a keyboard: focus the panel and hold Space.

Out of the way

A menu bar icon and nothing else. No Dock icon, no window sitting in front of your work.

Never leaves a button stuck

On quit, on switch-off, on a withdrawn permission — a final mouse-up always goes out.

Nothing leaves your Mac

The only request it ever makes is to GitHub, to ask whether there is a newer version.

Yours to read

GPL-3, every line on GitHub — down to the code that draws the app’s own icon.

Features

What you get

Your own hold time

Anywhere from 200 to 2200 ms, a second by default. A practice pad in the settings shows a filling bar while you press and tells you how long your last press actually was.

A ring around the pointer

It fills as you hold and closes into a full ring the moment the button locks. Colour, thickness, size, delay and fade are all yours — or turn it off and lock invisibly.

A sound when it catches

Tink on locking, Pop on release, picked from the sounds macOS already ships, each with its own volume and a preview button.

Mouse or trackpad

The same gesture on both, because the app works on the events rather than the hardware. The right button can lock too, if you ask for it.

Safety nets, if you want them

Don’t lock when the mouse moves while holding. Let Escape release. Let go by itself after a few seconds. All off by default — with them off, the behaviour is exactly Windows’.

Keeps itself current

Checks GitHub for a new release once a day, installs it on request, and refuses any build that isn’t signed with the same key as the copy you are running.

Mechanism

How it works

A CGEventTap at session level rewrites three things on their way past. Nothing is simulated, and no app needs to know.

One press, from your hand to the app Three lanes on one timeline. Your hand presses, holds past the threshold, lets go, moves the pointer and clicks again. ClickLatch fills the ring, swallows the mouse-up, rewrites moves as drags, and turns the second click into the mouse-up it held back. Every other app sees one uninterrupted press, from the first mouse-down to that final mouse-up. Press Threshold Let go Move Click again Your hand ClickLatch Every app holding the button nothing held 1 2 3 ring fills swallows it moves become drags hands it over mouse-down mouse-up one uninterrupted press
The second and third lanes are the whole trick: what your hand does and what every other app believes stop matching the moment you let go.
  1. 1

    You let go, late

    Held past the threshold, the mouseUp is swallowed. As far as macOS is concerned the button is still down.

  2. 2

    You move the pointer

    Each mouseMoved is rewritten into a mouseDragged, so every app sees an ordinary drag — selecting, dragging, resizing.

  3. 3

    You click again

    That mouseDown becomes the mouseUp that was held back. The drag ends exactly where you clicked.

Its own thread

The tap runs on a dedicated thread with its own run loop, so a busy main thread can never make it time out. If macOS switches it off anyway, the app switches it straight back on. The ring is a separate click-through window driven by a display link, so drawing it cannot slow the tap down.

The rule it never breaks

Quit the app, switch it off, or take the permission away, and it sends a final mouseUp on the way out. A mouse button can never be left hanging.

Screenshots

A look inside

The General tab of ClickLatch’s settings: a switch to enable it, a Short to Long slider with the threshold in milliseconds, and a practice pad.

The hold duration, with a practice pad that reports your last press in milliseconds.

Every setting that holds a value has a small ⟲ beside it that puts that one setting back to its factory value.

Getting started

Install

  1. Download ClickLatch 0.1.0

    A 517 KB .zip straight from the GitHub release. Apple silicon, macOS 14 or later. Prefer to see the release page?

  2. Unzip it and drag ClickLatch.app into your Applications folder.

  3. macOS will refuse to open it the first time. The build is signed, but with a self-signed certificate rather than an Apple-issued one, and it is not notarised — so Gatekeeper treats anything that came through a browser as suspect. This is expected, not a sign that something is wrong. Clear the quarantine flag once and it opens normally from then on:

    xattr -dr com.apple.quarantine /Applications/ClickLatch.app
    open /Applications/ClickLatch.app

    Rather not touch Terminal? Double-click the app, let macOS block it, then open System Settings → Privacy & Security, scroll down to the note about ClickLatch and click Open Anyway.

One permission, and why

ClickLatch has to alter mouse events, and macOS only allows that with the Accessibility permission. Pick Grant Accessibility permission… from the menu bar icon, or switch ClickLatch on yourself under System Settings → Privacy & Security → Accessibility. Then turn on Enable ClickLatch in the same menu.

macOS ties that permission to the app’s code signature. A downloaded build keeps the same signature across updates, so the permission sticks.

FAQ

Questions

Which apps does it not work in?

Apps that read the button state directly, with NSEvent.pressedMouseButtons, instead of following drag events, do not notice the lock. No event tap sees anything inside secure input contexts either — password fields and the login window. And Mission Control, switching Spaces or a trip to the menu bar can drop an active lock.

Does it send anything anywhere?

Only a request to GitHub’s releases API, once a day, to ask what the latest version is — and only while update checking is switched on. Nothing about you, your clicks or your keyboard goes anywhere. If you turn on Escape releases a lock, key presses are routed past the app; it examines nothing but Escape, passes everything else through untouched, and stores none of it.

Why does macOS block the download?

Because the app is not notarised by Apple. Notarisation needs a paid Apple Developer account; this is a free app that does not have one. The build is signed, with a self-signed certificate, which is what makes the Accessibility permission and the self-updater work. Clearing the quarantine flag once, as shown above, is all it takes.

I have an Intel Mac.

The download is built for Apple silicon. On an Intel Mac, build it from source with the steps above — the code itself has nothing architecture-specific about it.

Does it work with a trackpad?

Yes. Press and hold on the trackpad the same way you would a mouse button. The app works on the events macOS produces, so it does not care what produced them.

How do I get rid of it?

Quit it from the menu bar icon and drag ClickLatch.app to the Trash. To tidy up completely, remove it from System Settings → Privacy & Security → Accessibility with the button.

Try it for a day.

Long selections and long drags stop being something you brace for.

Download for macOS

Version 0.1.0 · Apple silicon · macOS 14 or later