A lightweight macOS menu bar utility for switching display resolutions, creating virtual displays, and managing multi-monitor setups.
Built for macOS 15+ (Sequoia/Tahoe). Runs natively on Apple Silicon and Intel.
- Switch resolutions for any connected display from the menu bar
- Exposes all available modes including hidden ones macOS doesn't show in System Settings
- HiDPI (Retina) modes clearly labeled
- Current resolution shown with a checkmark
- Add custom resolution entries and attempt to apply them
- Searches all display modes (including hidden/unsafe ones) for matches
- Falls back to private CoreGraphics APIs for unsupported resolutions
- Create virtual displays at any resolution (1080p, 1440p, 4K, ultrawide, or custom)
- Perfect for remote desktop — RustDesk, VNC, and other tools see virtual displays as real screens
- HiDPI support for virtual displays
- Auto-recreate on app launch
- Useful for headless Mac setups and clamshell mode with remote access
- Keep using a MacBook over RustDesk/VNC with the lid shut — no external display or dummy HDMI plug needed
- Closing the lid brings up your virtual display and hands it the menu bar automatically
- Opening the lid tears it down and returns the built-in panel to main, so every window comes back with it
- ⌃⌥⌘D recovers from anywhere — including when the menu bar is stranded on a screen you can't see
- Arms itself on a cold start too, so a login-item launch with the lid already closed works
- Set any display as the main display directly from the menu (including virtual displays)
- Remembers display state (main display + resolutions) across restarts
- Auto-refreshes when displays are connected or disconnected
- Native menu bar icon — no dock icon, no windows
- Launch at Login support
- Lightweight and stays out of your way
Requires Xcode 16+ and macOS 15+.
git clone https://github.com/adevra/macOS-Dispres.git
cd macOS-Dispres
./bundle.shThis builds a release binary and creates Dispres.app. Then either:
# Copy to Applications
cp -r Dispres.app /Applications/
# Or just open it directly
open Dispres.appswift build
.build/debug/Dispres- Launch Dispres — a display icon appears in the menu bar
- Click it to see all connected displays
- Hover over a display to see available resolutions
- Click a resolution to switch to it
If you need a resolution your physical display doesn't support (e.g., 2560x1440 for RustDesk on a 1080p monitor):
- Click the menu bar icon
- Go to Virtual Displays → Create Virtual Display...
- Select a preset (1440p, 4K, etc.) or enter a custom resolution
- Click Create
The virtual display appears as a real screen to macOS and remote desktop software. Enable Auto-create on launch to have it persist across restarts.
Each display submenu has a Main Display option. Click it to make that display the primary screen. This works for both physical and virtual displays.
For running a MacBook headless over RustDesk or VNC with the lid shut and no external display attached.
First, stop macOS sleeping when the lid closes:
sudo pmset -a disablesleep 1Then create a virtual display at the resolution you want to work at, with Auto-create on launch enabled. From then on:
- Close the lid — Dispres creates the virtual display if it isn't already up and makes it the main display, so your remote session switches to that resolution
- Open the lid — the virtual display is removed and the built-in panel becomes main again, bringing your windows back
- ⌃⌥⌘D — global hot key that runs the same recovery by hand
Turn the behaviour off with Auto-Switch Virtual Display on Lid Close/Open in the menu.
To undo the sleep change afterwards: sudo pmset -a disablesleep 0. It applies on battery too, so the Mac won't sleep unplugged until you do.
Why this exists: a virtual display left as main while the lid is shut keeps the menu bar and every window after you reopen the lid. The built-in panel comes back showing an empty desktop, nothing is clickable, and the menu bar icon that would undo it is itself off-screen. Recovering from that used to mean a hard restart.
- Uses CoreGraphics public APIs for display enumeration and resolution switching
- Uses
kCGDisplayShowDuplicateLowResolutionModesto expose hidden display modes - Virtual displays use the private
CGVirtualDisplayAPI (macOS 14+) - Private
CGSConfigureDisplayModeAPI as fallback for custom resolutions - Display state is persisted using stable identifiers (vendor/model/serial) that survive reboots
- Lid state comes from
IOPMrootDomain'sAppleClamshellState, via an IOKit interest notification plus a 1s poll (the property lags the physical lid, so a single read on the notification drops edges) - The recovery hot key uses Carbon
RegisterEventHotKey, so it needs no Accessibility permission and still fires when the menu bar is unreachable - Virtual displays are only ever made main
.forSession, never.permanently, so they can't be written into the saved display arrangement - No sandbox — required for CoreGraphics display configuration APIs
Sources/
├── CGVirtualDisplayBridge/ # ObjC headers for private CGVirtualDisplay API
│ ├── include/
│ │ └── CGVirtualDisplayBridge.h
│ └── CGVirtualDisplayBridge.m
└── Dispres/
├── DispresApp.swift # App entry point, MenuBarExtra, AppDelegate
├── Models/
│ └── DisplayModels.swift # DisplayInfo, DisplayModeInfo, CustomResolution
├── Services/
│ ├── AppServices.swift # Service container, bootstrapped at launch
│ ├── DisplayManager.swift # Display enumeration, mode switching, state persistence
│ ├── VirtualDisplayService.swift # Virtual display lifecycle management
│ ├── RecoveryService.swift # Clamshell monitor, lid handling, recovery hot key
│ ├── LoginItemService.swift # Launch at Login (SMAppService / LaunchAgent)
│ └── PrivateAPIs.swift # CGS private API declarations
└── Views/
├── MenuContentView.swift # Top-level menu
├── DisplaySectionView.swift # Per-display submenu
├── VirtualDisplayView.swift # Virtual display menu + create form
└── CustomResolutionSheet.swift # Custom resolution form
- Bit depth is read-only on modern macOS — Apple Silicon doesn't allow programmatic changes
- Custom resolutions only work if the display hardware supports them at some level. You can't force a 1080p panel to physically output 4K
- Virtual displays require macOS 14+ and use private APIs that may change between OS versions
- Not for App Store — this app uses private CoreGraphics APIs (
CGVirtualDisplay,CGSConfigureDisplayMode) that Apple does not allow on the App Store - Launch at Login via
SMAppServicerequires running from the.appbundle - Clamshell mode needs
sudo pmset -a disablesleep 1— without an external display macOS sleeps on lid close regardless. This also stops the Mac sleeping on battery until you revert it - FileVault + clamshell: if the Mac reboots with the lid shut you land at the pre-boot unlock screen, where nothing is running and remote access isn't possible yet
GPL-3.0 — see LICENSE
This project uses private macOS APIs and is not suitable for App Store distribution.


