A native macOS menu bar app that maps extra mouse buttons to keyboard shortcuts — per app.
Replaces bloated vendor software with a fast, focused, open-source alternative. Built with SwiftUI.
You have a mouse with extra buttons. The vendor's companion app is 200 MB, phones home, requires an account, and breaks after every macOS update. You just want Back to trigger Cmd+[ in Safari and Mission Control on the thumb button everywhere else.
Ptions+ does exactly that. Configure button mappings per-app, set system actions, and forget about it. Runs in the menu bar, uses ~8 MB of RAM, zero network calls.
Per-app profiles — Different mappings for every app. Safari gets browser navigation, Xcode gets build shortcuts, everything else gets your defaults.
System actions — Mission Control, App Expose, Show Desktop, and Launchpad use a dynamically detected CoreDock integration with graceful fallback when unavailable.
Shortcut recorder — Click "Assign", press your key combo. Supports all modifier combinations.
16 preset actions — Spotlight, Screenshot Tool, Notification Center, Lock Screen, and more. Logical shortcuts adapt to the active keyboard layout.
Supported mouse models — Manually select MX Master 4/3/3S/2S, MX Anywhere 3, MX Ergo, MX Vertical, G502, G604, or a generic 3/5-button model.
Launch at login — Native SMAppService integration.
Debug monitor — Live view of raw mouse events for troubleshooting.
| Logitech MX | Logitech G | Generic |
|---|---|---|
| MX Master 4 | G502 | Generic (5 buttons) |
| MX Master 3 | G604 | Generic (3 buttons) |
| MX Master 3S | ||
| MX Master 2S | ||
| MX Anywhere 3 | ||
| MX Ergo | ||
| MX Vertical |
Any mouse that sends
otherMouseDownevents via HID will work. Pick the closest model or use Generic.
- macOS 13 Ventura or later
- Xcode 15+
- The vendor's companion software must be uninstalled — it captures mouse events before Ptions+ can
git clone https://github.com/trsdn/PtionsPlus.git && cd PtionsPlus
xcodebuild -project PtionsPlus.xcodeproj -scheme "Ptions+" -configuration Release buildCopy the built app to /Applications:
cp -R ~/Library/Developer/Xcode/DerivedData/PtionsPlus-*/Build/Products/Release/Ptions+.app /Applications/
xattr -cr /Applications/Ptions+.app
open /Applications/Ptions+.appThe release flow is three commands: bump version, sign, notarize.
First create your local release config:
scripts/setup-notarization.sh --gui./scripts/bump-version.sh patch # or: minor, major
bash scripts/sign-release.sh
xcrun notarytool store-credentials "PtionsPlus" \
--apple-id "your@email.com" \
--team-id "YOUR_TEAM_ID" \
--password "app-specific-password"
bash scripts/notarize.shIf you already have a working notarytool keychain profile from another project, set it in .release.env or inline:
TEAM_ID="YOUR_TEAM_ID"
CODE_SIGN_IDENTITY="Developer ID Application: Your Name"
NOTARY_PROFILE="your-notary-profile"The release scripts produce four outputs:
build/PtionsPlus.xcarchive/Products/Applications/Ptions+.appdist/Ptions+.zipdist/Ptions+.dmgdist/Ptions+.dmg.sha256
The final ZIP and DMG are rebuilt from the stapled app and verified before upload. Verify a downloaded DMG with:
shasum -a 256 -c Ptions+.dmg.sha256The GitHub release workflow builds signed, notarized artifacts on v* tags. Configure these repository secrets first:
MACOS_CERTIFICATE, MACOS_CERTIFICATE_PWD, APPLE_ID, APPLE_TEAM_ID, and APPLE_APP_PASSWORD.
On first launch, Ptions+ will prompt for Accessibility permissions. This is required to intercept mouse events system-wide.
System Settings → Privacy & Security → Accessibility → enable Ptions+
Mouse Button Press
│
▼
CGEventTap (EventTapService)
│
▼
Active App Lookup (ActiveAppMonitor)
│
▼
Profile Match (MappingStore)
│
├── Has mapping? → Coordinated input / available system action → Suppress event pair
│
└── No mapping? → Pass through
A session-level CGEventTap intercepts otherMouseDown / otherMouseUp events. A deterministic state machine keeps each down/up suppression decision paired, coordinates held shortcuts, and passes unsupported model buttons through. Mapped buttons either simulate a keyboard shortcut via CGEvent posting or trigger an available system action.
All configuration lives in a single JSON file:
~/Library/Application Support/Ptions+/config.json
Reset to defaults:
rm ~/Library/Application\ Support/Ptions+/config.jsonThe app regenerates default config on next launch.
Configuration writes are validated and atomic. If the file is corrupt or semantically invalid, Ptions+ preserves it, blocks interception, and offers an explicit backed-up repair or reset.
xcodebuild -project PtionsPlus.xcodeproj -scheme "Ptions+" \
-configuration Debug -destination "platform=macOS" \
CODE_SIGNING_ALLOWED=NO test -only-testing:PtionsPlusTests
xcodebuild -project PtionsPlus.xcodeproj -scheme "Ptions+" \
-configuration Debug -destination "platform=macOS" \
test -only-testing:PtionsPlusUITestsPtionsPlus/
├── PtionsApp.swift # Entry point, AppDelegate, lifecycle
├── Model/
│ ├── ButtonMapping.swift # Data models: profiles, mappings, mice
│ ├── MappingStore.swift # Transactional state and profile lookup
│ └── ConfigurationPersistence.swift
├── Services/
│ ├── EventTapService.swift # CGEventTap lifecycle and diagnostics
│ ├── EventStateMachine.swift # Paired mouse-event decisions
│ ├── KeySimulator.swift # Coordinated CGEvent and preset execution
│ ├── RuntimeServiceCoordinator.swift
│ ├── LaunchAtLoginService.swift
│ ├── ApplicationDiscoveryService.swift
│ ├── ActiveAppMonitor.swift # Tracks frontmost app bundle ID
│ └── AccessibilityChecker.swift
├── Views/
│ ├── MenuBarView.swift # Menu bar dropdown
│ ├── SettingsView.swift # Settings window (tabbed)
│ ├── ProfileListView.swift # Sidebar profile list
│ ├── ProfileEditorView.swift # Button mapping editor
│ ├── ShortcutRecorderView.swift
│ ├── AppPickerView.swift
│ ├── PermissionGuideView.swift
│ └── DebugMonitorView.swift
└── Utilities/
├── KeyCodeMap.swift
└── Constants.swift
Ptions+ is an independent, open-source project. It is not affiliated with, endorsed by, or associated with Logitech, Logi, or any of their subsidiaries or products. All product names, trademarks, and registered trademarks mentioned in this project are the property of their respective owners. Mouse model names are used solely for compatibility identification purposes.
MIT
