Mouse Cursor Management for Multi-Display macOS
MousePortal is a macOS utility for managing mouse cursor movement between multiple displays. It provides two main features:
- Hotkey Jump - Global keyboard shortcuts to instantly teleport the cursor to specific displays
- Portals - Custom edge-to-edge teleportation zones that warp the cursor when crossing defined screen boundaries
- Configure custom global shortcuts for each display
- Support for any key + modifier combinations
- Support for mapping via display layout name or ID
- Define custom teleportation zones on display edges
- Support for bidirectional portals (from one display edge to another)
- Two trigger modes:
- Automatic - Always active
- Key-held - Requires holding a modifier key (default: Option)
- Visual editor for easy creation and adjustment of portal zones
Available in 20 languages: English, Simplified Chinese, Traditional Chinese, Japanese, Korean, German, French, Spanish, Portuguese (Brazil), Russian, Italian, Dutch, Polish, Turkish, Arabic, Hindi, Thai, Vietnamese, Indonesian, Malay.
- Launch at login
- Menu bar quick access
- Configuration profile management (for different setups)
- Display layout visualization
- macOS 13.0 or later
- Accessibility Permission required (System Settings > Privacy & Security > Accessibility)
- Download the latest
.zipfile from Releases - Extract and drag
MousePortal.appto/Applicationsfolder - On first launch, grant Accessibility permission in System Settings
# Clone the repository
git clone https://github.com/ai-eks/MousePortal.git
cd MousePortal
# Build
swift build -c release
# Run
swift run- Launch MousePortal
- Open System Settings > Privacy & Security > Accessibility
- Add MousePortal and ensure the toggle is enabled
- Return to MousePortal main window
- Open MousePortal main window
- Select Hotkeys in the left sidebar
- Click the hotkey input field and press your desired shortcut
- Select the display layout for this hotkey
- Click Save
- Select Portal Editor in the left sidebar
- Click + New Portal
- Select source and target displays
- Choose an edge (top/bottom/left/right)
- Adjust the portal zone start and end positions
- Select trigger mode (automatic or key-held)
- Name and save your portal
- Define your display arrangement in Display Layouts
- Assign a name to each layout (e.g., "Dual Display", "Triple Display")
- Specify relative positions of each display
| Service | Description |
|---|---|
PortalService |
Monitors mouse movement, detects when cursor crosses portal lines, warps cursor using CGWarpMouseCursorPosition |
HotkeyService |
Listens for global keyboard shortcuts via CGEvent |
PermissionService |
Handles macOS Accessibility permission |
DisplayService |
Fetches display info via CGGetActiveDisplayList/CGDisplayBounds |
LanguageService |
Handles dynamic language switching |
LaunchAtLoginService |
Manages launch at login |
PortalPair- TwoPortalLineobjects defining a bidirectional warp zoneHotkeyConfig- Maps a key+modifiers combo to a display layoutConfigProfile- Named configuration snapshots for different setups
- macOS 13+
- Swift 5.9+
- Xcode 15+
# Open in Xcode
open Package.swift
# Run tests
swift test
# Build release version
swift build -c releaseMousePortal/
├── MousePortal/ # Main application
│ ├── Models/ # Data models
│ ├── Views/ # SwiftUI views
│ ├── Services/ # Core services
│ ├── Utils/ # Utilities
│ ├── Protocols/ # Protocol definitions
│ └── Resources/ # Resources and localization
├── MousePortalTests/ # Unit tests
│ ├── Models/
│ ├── Services/
│ ├── Utils/
│ └── Mocks/
└── docs/ # Documentation
MousePortal is a focused utility and is mainly maintained by the project owner.
Bug reports and small focused fixes are welcome. Larger feature ideas may be discussed in issues first.
See PRIVACY.md for the privacy note.
This project is licensed under the Apache 2.0 License - see the LICENSE file for details.
A: MousePortal needs to listen for global keyboard events and monitor mouse position, which requires macOS Accessibility permission.
A: Please ensure:
- Accessibility permission is granted
- The portal is enabled
- If using key-held mode, make sure you're holding the modifier key
