A tiny, free, open-source macOS menu bar app that rescues your mouse cursor when it gets trapped, hidden, or lost, including when Universal Control has carried it off to another Mac.
brew install --cask uncSoft/mousetrapped/mousetrapped
Or grab the notarized build from the latest release.
On macOS, when a nearby Mac has Universal Control / mouse sharing enabled (made worse by remote-control tools like ScreenConnect), the cursor can get "eaten": some window captures pointer focus, hides the cursor, and leaves you in no-mouse limbo. Or the pointer crosses to the other Mac and something over there swallows it.
Under the hood the cursor usually isn't gone. It has been disassociated
from the mouse (CGAssociateMouseAndMouseCursorPosition(false)), hidden by a
process that never restored it, or routed to another machine. Mousetrapped
forcibly undoes all of that.
Press the hotkey (⌃⌥⌘M by default) or shake the mouse hard left-right, and Mousetrapped will:
UniversalControl agent, which forces macOS to bring the pointer home,
then warp it to your chosen displayRemote-pointer detection compares the raw HID device stream against the local session's event counters. If the physical mouse is moving but the local session sees nothing (or the cursor is parked at a screen edge, where Universal Control leaves it), the pointer is on the other Mac.
With Homebrew:
brew install --cask uncSoft/mousetrapped/mousetrapped
Or download the notarized build from Releases.
Or build from source (requires Xcode command line tools):
git clone https://github.com/uncSoft/Mousetrapped.git
cd Mousetrapped
./build.sh # or ./build.sh --universal for arm64 + x86_64
cp -R dist/Mousetrapped.app /Applications/
open /Applications/Mousetrapped.app
build.sh signs with your first available Apple Development / Developer ID
identity (override with CODESIGN_IDENTITY=...), falling back to ad-hoc. A
stable identity matters: macOS ties the Input Monitoring grant to the code
signature, so ad-hoc builds lose the grant on every rebuild.
Mousetrapped runs at three privilege levels, using the lowest one that works:
RegisterEventHotKey) and the
menu bar button always work for local rescues.IOHIDManager. This watches the physical mouse and keyboard below
the layer where Universal Control captures input, so shake and hotkey
detection (and rescue) work even while the pointer is controlling
another Mac. Grant it under System Settings → Privacy & Security → Input
Monitoring, then relaunch the app.The hotkey is stored in UserDefaults as a Carbon key code + modifier mask.
Example: set it to ⌃⌥⌘R (key code 15):
defaults write dev.mousetrapped.Mousetrapped hotKeyCode -int 15
defaults write dev.mousetrapped.Mousetrapped hotKeyModifiers -int 6400
killall Mousetrapped && open /Applications/Mousetrapped.app
Modifier mask is the sum of Carbon flags: ⌘ 256, ⇧ 512, ⌥ 2048, ⌃ 4096 (6400 = ⌘ + ⌥ + ⌃).
Debugging: the app logs events to the unified log (subsystem
dev.mousetrapped.Mousetrapped; view with log stream --predicate 'subsystem == "dev.mousetrapped.Mousetrapped"' --level info). To also
mirror to ~/Library/Logs/Mousetrapped.log, and for per-stroke shake
diagnostics:
defaults write dev.mousetrapped.Mousetrapped debugLogging -bool true
defaults write dev.mousetrapped.Mousetrapped shakeDebug -bool true
macOS binds the Input Monitoring grant to an app's code-signature identity (its designated requirement). Mousetrapped's released builds all share one stable Developer ID identity, so the grant is meant to carry across updates.
Two things can still break it:
build.sh
now prefers your Developer ID identity to avoid this; the released cask
build is always Developer ID signed.Clean fix, after which updates should persist:
tccutil reset ListenEvent dev.mousetrapped.Mousetrapped
Then relaunch Mousetrapped and enable it once more under System Settings → Privacy & Security → Input Monitoring.
Shake and hotkey both rescue a lost cursor on the Mac they run on, trackpad or mouse. Across Macs (Universal Control), there's one difference:
CGDisplayHideCursor and is truly
wedged, macOS scopes that hide to the offending process; the rescue
restores control and shows you where the cursor is, but killing the wedged
process is the final fix.PolyForm Noncommercial 1.0.0: the source is free to use, modify, and share, but not for commercial use. You can't sell it or ship it inside a paid product.
11 commits
Swift
97.7%
Shell
2.3%
A tiny, free, open-source macOS menu bar app that rescues your mouse cursor when it gets trapped, hidden, or lost, including when Universal Control has carried it off to another Mac.
brew install --cask uncSoft/mousetrapped/mousetrapped
Or grab the notarized build from the latest release.
On macOS, when a nearby Mac has Universal Control / mouse sharing enabled (made worse by remote-control tools like ScreenConnect), the cursor can get "eaten": some window captures pointer focus, hides the cursor, and leaves you in no-mouse limbo. Or the pointer crosses to the other Mac and something over there swallows it.
Under the hood the cursor usually isn't gone. It has been disassociated
from the mouse (CGAssociateMouseAndMouseCursorPosition(false)), hidden by a
process that never restored it, or routed to another machine. Mousetrapped
forcibly undoes all of that.
Press the hotkey (⌃⌥⌘M by default) or shake the mouse hard left-right, and Mousetrapped will:
UniversalControl agent, which forces macOS to bring the pointer home,
then warp it to your chosen displayRemote-pointer detection compares the raw HID device stream against the local session's event counters. If the physical mouse is moving but the local session sees nothing (or the cursor is parked at a screen edge, where Universal Control leaves it), the pointer is on the other Mac.
With Homebrew:
brew install --cask uncSoft/mousetrapped/mousetrapped
Or download the notarized build from Releases.
Or build from source (requires Xcode command line tools):
git clone https://github.com/uncSoft/Mousetrapped.git
cd Mousetrapped
./build.sh # or ./build.sh --universal for arm64 + x86_64
cp -R dist/Mousetrapped.app /Applications/
open /Applications/Mousetrapped.app
build.sh signs with your first available Apple Development / Developer ID
identity (override with CODESIGN_IDENTITY=...), falling back to ad-hoc. A
stable identity matters: macOS ties the Input Monitoring grant to the code
signature, so ad-hoc builds lose the grant on every rebuild.
Mousetrapped runs at three privilege levels, using the lowest one that works:
RegisterEventHotKey) and the
menu bar button always work for local rescues.IOHIDManager. This watches the physical mouse and keyboard below
the layer where Universal Control captures input, so shake and hotkey
detection (and rescue) work even while the pointer is controlling
another Mac. Grant it under System Settings → Privacy & Security → Input
Monitoring, then relaunch the app.The hotkey is stored in UserDefaults as a Carbon key code + modifier mask.
Example: set it to ⌃⌥⌘R (key code 15):
defaults write dev.mousetrapped.Mousetrapped hotKeyCode -int 15
defaults write dev.mousetrapped.Mousetrapped hotKeyModifiers -int 6400
killall Mousetrapped && open /Applications/Mousetrapped.app
Modifier mask is the sum of Carbon flags: ⌘ 256, ⇧ 512, ⌥ 2048, ⌃ 4096 (6400 = ⌘ + ⌥ + ⌃).
Debugging: the app logs events to the unified log (subsystem
dev.mousetrapped.Mousetrapped; view with log stream --predicate 'subsystem == "dev.mousetrapped.Mousetrapped"' --level info). To also
mirror to ~/Library/Logs/Mousetrapped.log, and for per-stroke shake
diagnostics:
defaults write dev.mousetrapped.Mousetrapped debugLogging -bool true
defaults write dev.mousetrapped.Mousetrapped shakeDebug -bool true
macOS binds the Input Monitoring grant to an app's code-signature identity (its designated requirement). Mousetrapped's released builds all share one stable Developer ID identity, so the grant is meant to carry across updates.
Two things can still break it:
build.sh
now prefers your Developer ID identity to avoid this; the released cask
build is always Developer ID signed.Clean fix, after which updates should persist:
tccutil reset ListenEvent dev.mousetrapped.Mousetrapped
Then relaunch Mousetrapped and enable it once more under System Settings → Privacy & Security → Input Monitoring.
Shake and hotkey both rescue a lost cursor on the Mac they run on, trackpad or mouse. Across Macs (Universal Control), there's one difference:
CGDisplayHideCursor and is truly
wedged, macOS scopes that hide to the offending process; the rescue
restores control and shows you where the cursor is, but killing the wedged
process is the final fix.PolyForm Noncommercial 1.0.0: the source is free to use, modify, and share, but not for commercial use. You can't sell it or ship it inside a paid product.
11 commits
Swift
97.7%
Shell
2.3%