Mousip is a tiny menu bar app for mice with a tilting scroll wheel. Tilt the wheel right or left and you move to the next or previous Space: desktops and full-screen apps alike. Optionally, a middle click on an empty spot opens Mission Control.
Built for the HP 480 Comfort Bluetooth Mouse, it works with any mouse that reports wheel tilt as horizontal scrolling (HID "AC Pan").
Screen.Recording.2026-10-07.at.13.41.51.mov
- Tilt to switch Spaces. One tilt is one Space, however many events the mouse sends.
- Middle click for Mission Control (optional). Only on empty spots: middle clicks on links, tabs, buttons and text keep working as usual.
- Your shortcuts, respected. Mousip posts the system "Move left/right a space" shortcuts, reading your custom bindings if you changed them.
- Trackpads and Magic Mouse untouched. Only notched scroll wheels are intercepted.
- Sideways scrolling on demand. Hold ⌥ while tilting to scroll horizontally as usual.
Paste this in Terminal:
curl -fsSL https://raw.githubusercontent.com/daveleone/mousip/main/scripts/install.sh | shIt downloads the latest release, installs it in
/Applications and launches it. After that Mousip updates itself (see below). No Gatekeeper prompt: files downloaded with
curl aren't quarantined the way browser downloads are.
On first launch macOS asks for the Accessibility permission (System Settings › Privacy & Security › Accessibility). As soon as you grant it, Mousip turns on by itself.
Prefer downloading the DMG from the browser?
Grab Mousip.dmg from the releases page and drag
Mousip to Applications. Unless the release is notarized, macOS will say it can't verify the developer
the first time: open System Settings › Privacy & Security, scroll down and click Open Anyway.
Or clear the quarantine flag yourself:
xattr -dr com.apple.quarantine /Applications/Mousip.appYou only need the Command Line Tools (Swift 6), not Xcode.
| Command | What it does |
|---|---|
./build.sh |
Builds build/Mousip.app |
./build.sh run |
Builds and launches from build/ |
./build.sh install |
Builds, copies to /Applications and launches |
scripts/package.sh |
Packs the built app into build/Mousip.zip and build/Mousip.dmg |
Note
Local builds are signed ad-hoc, so every rebuild invalidates the Accessibility permission:
run and install reset it (tccutil reset Accessibility com.mousip.app) and the app asks again.
To keep it across rebuilds, sign with a stable certificate:
SIGN_IDENTITY="Certificate name" ./build.sh install.
Click the Mousip icon (a mouse) in the menu bar to open its menu.
| Item | |
|---|---|
| Main switch (next to the name) | Pauses or resumes everything. The icon turns gray while paused. |
| Invert Direction | For when tilting right takes you left. |
| Repeat While Held | Keeps switching Spaces every 0.45 s while the wheel stays tilted. |
| Middle Click › Mission Control | Opens Mission Control on a middle click on an empty spot. Off by default. |
| Launch at Login | Starts Mousip when you log in. |
| Check for Updates | Looks for a new release at launch and once a day. On by default. |
If something needs your attention (missing permission, disabled shortcuts) a banner at the top takes you straight to the right settings page.
When a new version is out, a banner offers Install & Relaunch: Mousip downloads the release from GitHub, checks its bundle and signature, replaces itself and starts again. The ↻ button next to Quit checks right away. Releases are ad-hoc signed, so after an update macOS asks for the Accessibility permission again. Local builds report version 1.0 and never update themselves.
Hold ⌥ while opening the menu for the extras:
-
Left / Right / Mission Ctrl buttons: check the shortcuts without the mouse.
-
Debug Logging: logs every scroll event and every middle click target. To read it:
log stream --predicate 'subsystem == "com.mousip.app"'
A middle click often means something: open a link in a new tab, close a tab, paste in a terminal. So Mousip looks at the cursor and asks the Accessibility API what's under it, and steps in only if it's empty space:
| Under the cursor | Middle click |
|---|---|
| Anything showing the pointing-hand cursor (links, in any browser) | Passed to the app as usual |
| Link, tab, button, menu, Dock icon, text field | Passed to the app as usual |
| A window's title bar or tab strip (top 48 pt) | Passed to the app as usual |
| Page background, plain text, window background, desktop | Opens Mission Control |
| Anything, with ⌘ ⇧ ⌥ or ⌃ held | Passed to the app as usual |
| An app that doesn't answer | Passed to the app as usual |
Middle drags (panning a canvas in Figma, Blender, maps…) are recognized after a few points of movement and replayed to the app, so they keep working.
Tip
Chrome doesn't expose web pages to Accessibility unless a screen reader is running, so to Mousip a link there looks like an empty area. That's why the pointing-hand cursor is checked first: it works in every browser and web app without slowing them down.
- A
CGEventTapintercepts scroll events. Only horizontal events from notched scroll wheels (isContinuous == 0) are considered: trackpads and the Magic Mouse are left alone. - A burst of events from the same tilt counts as a single gesture (0.3 s pause between one gesture and the next).
- For each gesture it posts the Mission Control shortcut "Move left/right a space"
(⌃← / ⌃→ by default), read from
com.apple.symbolichotkeys. - Handled horizontal events are consumed, so apps don't scroll sideways. With ⌥ (or ⇧) held, they pass through untouched.
- A second tap watches the middle button. On press it checks whether the cursor is the pointing hand
(
NSCursor.currentSystem) or the click lands in the top band of a window (CGWindowListCopyWindowInfo, so browser tabs can be closed), then hit-tests the element under the cursor (AXUIElementCopyElementAtPosition) and walks up its ancestors looking for something clickable. If there's nothing, it swallows the click and opens Mission Control on release (by launchingMission Control.app, so it works whatever shortcut is assigned to it).
| Symptom | Likely cause |
|---|---|
| Nothing happens, the icon is grayed out | The Accessibility permission is missing, or the one from the previous build is still listed: remove it, add it again and restart the app. |
| "Test" doesn't switch Spaces | The "Move left/right a space" shortcuts are disabled (Keyboard › Keyboard Shortcuts › Mission Control). |
| A single tilt skips several Spaces | The mouse repeats the event at intervals longer than 0.3 s: increase gestureGap in ScrollInterceptor.swift. |
The log never shows a non-zero h= |
The mouse doesn't report tilt as horizontal scrolling (for example it sends buttons 4/5 instead). |
| A middle click on a link opens Mission Control | The link doesn't show the pointing hand and isn't exposed to Accessibility. Enable Debug Logging to see the cursor and the element roles under it, and add the missing role to interactiveRoles in MiddleClickInterceptor.swift. |
| A middle click on an empty spot does nothing | The app didn't answer in time, or the spot is inside a clickable element: check the log. |
Sources/Mousip/
├── AppDelegate.swift menu bar icon, menu and permissions
├── MenuView.swift the menu content (SwiftUI)
├── EventTap.swift shared CGEventTap wrapper
├── ScrollInterceptor.swift tilt filtering and grouping into gestures
├── MiddleClickInterceptor.swift middle click → Mission Control (cursor + Accessibility checks)
├── SpaceSwitcher.swift reading and posting the system shortcuts
├── Updater.swift checking GitHub Releases and installing updates
└── Settings.swift preferences (UserDefaults)
Resources/
├── AppIcon.icns, icon.png generated by scripts/make-icon.swift
└── Info.plist
scripts/
├── install.sh one-line installer (downloads the latest release)
├── package.sh zip + DMG of the built app
└── make-icon.swift draws the app icon
.github/workflows/release.yml CI build, signing, notarization and releases
To redraw the icon after editing scripts/make-icon.swift:
swift scripts/make-icon.swiftMousip is released under the MIT License: use it, modify it and share it freely.