Skip to content

hoainam12k/mac-display-control

Repository files navigation

macOS Display Control

A native Swift command-line tool to programmatically enable/disable macOS displays and persist display configurations across system restarts.

Features

  • List all connected displays with their IDs and status
  • Enable/disable specific displays by ID
  • Save display configurations to auto-apply at system startup
  • Built-in LaunchAgent installer for automatic configuration restoration
  • Safety checks to prevent disabling the last active display
  • Configuration persistence via JSON config file

Why This Tool?

macOS doesn't provide a native way to disable external displays without physically disconnecting them. This tool solves that problem by using Core Graphics Display Configuration APIs to programmatically control display states.

Installation

Quick Install (from Release)

  1. Download the latest release:

    • Go to Releases
    • Download mac-display-control-vX.X.X.tar.gz
  2. Extract and install:

    tar -xzf mac-display-control-v1.0.0.tar.gz
    cd mac-display-control-v1.0.0
    chmod +x install.sh
    ./install.sh
  3. Configure your displays:

    # List displays to get IDs
    mac-display-control list
    
    # Disable the displays you want (use --save to persist)
    mac-display-control disable <DISPLAY_ID> --save
  4. Enable automatic monitoring:

    mac-display-control install

Now your display configuration will automatically apply at every login!

See QUICK_START.md for detailed setup instructions.

Building from Source

If you want to build from source instead, see BUILDING.md.

Usage

List Displays

mac-display-control list

Output example:

Connected Displays:
==================
Display 4: Built-in Display [main, built-in]
  Status: enabled
Display 5: Dell U2720Q
  Status: enabled → will be disabled at startup

Saved Configuration:
-------------------
Displays to disable at startup: 5
Config file: /Users/username/.config/mac-display-control/config.json

The list command shows:

  • All connected displays with their IDs
  • Current status (enabled/disabled)
  • Whether they will be automatically disabled at startup
  • Configuration file location (if any displays are configured)

Disable a Display

# Temporarily disable display 5
mac-display-control disable 5

# Disable and save configuration (will re-apply at startup)
mac-display-control disable 5 --save

Enable a Display

mac-display-control enable 5

Restore Saved Configuration

mac-display-control restore

This applies all saved display configurations (useful for testing before installing the LaunchAgent).

Install LaunchAgent

Monitoring Mode (Default - Recommended):

mac-display-control install

This will:

  1. Create a LaunchAgent plist at ~/Library/LaunchAgents/com.mac-display-control.plist
  2. Load the LaunchAgent using launchctl
  3. Run a continuous monitoring daemon in the background

The monitoring daemon automatically re-disables displays if macOS re-enables them (e.g., after screen unlock/wake). This ensures your configuration stays active throughout your session.

Restore-Only Mode (Login Only):

mac-display-control install --restore-only

This mode only restores your display configuration once at login, without continuous monitoring. macOS may re-enable displays after unlock/wake events.

Uninstall LaunchAgent

mac-display-control uninstall

This removes the LaunchAgent and stops automatic configuration restoration.

How It Works

Display Control

The tool uses private Core Graphics APIs (CGSConfigureDisplayEnabled) to enable/disable displays. The API is loaded dynamically using dlsym to avoid compile-time linking issues.

Key safety features:

  • Prevents disabling the last active display
  • Uses atomic display configuration transactions
  • Validates display IDs before operations

Configuration Persistence

Display configurations are saved to ~/.config/mac-display-control/config.json:

{
  "disabledDisplayIDs": [5, 37038191]
}

LaunchAgent & Monitoring

Monitoring Mode (Default): The LaunchAgent runs mac-display-control monitor at login, which:

  • Applies your saved configuration immediately
  • Continues running silently in the background
  • Checks every 5 seconds if configured displays are active
  • Automatically re-disables them if macOS re-enables them (e.g., after unlock/wake)
  • Uses ~0% CPU when idle, minimal memory (~5MB)

Restore-Only Mode: The LaunchAgent runs mac-display-control restore once at login, then exits.

Plist location: ~/Library/LaunchAgents/com.mac-display-control.plist Log files:

  • stdout: /tmp/mac-display-control.log
  • stderr: /tmp/mac-display-control.error.log

Use Cases

  1. Disable MacBook built-in display when using external monitors
  2. Automatically disable specific displays at startup
  3. Create display profiles for different work setups
  4. Script display management for automation workflows

Troubleshooting

"Could not find CGSConfigureDisplayEnabled symbol"

This means the private API is not available on your macOS version. The API is available on macOS 10.6+ but may have changed in recent versions.

LaunchAgent not working

Check the log files:

cat /tmp/mac-display-control.log
cat /tmp/mac-display-control.error.log

Verify the LaunchAgent is loaded:

launchctl list | grep mac-display-control

Display comes back on after unlock/wake

If you installed in monitoring mode (default), the daemon automatically re-disables displays within 5 seconds.

If you installed with --restore-only, macOS may re-enable displays after unlock/wake. Solutions:

  • Reinstall without --restore-only flag for automatic monitoring
  • Manually run mac-display-control restore after waking
  • Create automation using tools like Hammerspoon or BetterTouchTool

Technical Details

APIs Used

  • Core Graphics: Display configuration and enumeration

    • CGGetActiveDisplayList: List all active displays
    • CGBeginDisplayConfiguration/CGCompleteDisplayConfiguration: Atomic config changes
    • CGSConfigureDisplayEnabled: Private API to enable/disable displays
    • CGDisplayIsBuiltin, CGDisplayIsActive, etc.: Display state queries
  • IOKit: Display hardware information

    • IOServiceMatching: Match display services
    • IODisplayCreateInfoDictionary: Get display metadata
    • Display name extraction from IORegistry

Project Structure

Sources/
├── SimpleMain.swift              # CLI interface and commands
├── DisplayControl/
│   └── DisplayManager.swift      # Core display control logic
└── Config/
    └── ConfigManager.swift       # Configuration persistence

Limitations

  • Uses private APIs (may break in future macOS versions)
  • Cannot physically power off displays (only tells macOS to not use them)
  • Requires user to be logged in (LaunchAgent runs at login, not boot)
  • Monitoring mode uses minimal CPU (~0%) but runs continuously in the background

Tested Platform

  • Tested on: Apple Silicon (M1/M2/M3) running macOS Sequoia
  • Intel Macs: Not tested, but should work on macOS 10.15 (Catalina) or later
  • Feedback welcome: If you test on Intel or other macOS versions, please report your experience!

License

This is a utility tool for personal use. Use at your own risk.

Contributing

Feel free to submit issues or pull requests if you find bugs or have improvements!

Acknowledgments

Based on research from:

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Packages

 
 
 

Contributors