Skip to content

calvincchan/displaytoggle

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

10 Commits
 
 
 
 
 
 
 
 
 
 

Repository files navigation

displaytoggle

A minimal Swift CLI to turn the MacBook built-in display on or off without closing the lid. Inspired by Lunar's BlackOut feature, with implementation patterns drawn from screen_tune.

Requirements

  • Apple Silicon Mac (M1 or later)
  • macOS 13 Ventura or later
  • At least one external monitor connected (when turning the built-in off)
  • Swift toolchain (swift build)

Intel Macs are not supported. The SLSConfigureDisplayEnabled API behaves differently on Intel and does not perform a true display disconnect.

Usage

displaytoggle <command>

Commands:
  on       Turn the built-in display on
  off      Turn the built-in display off (requires ≥1 external monitor)
  toggle   Toggle the built-in display on/off
  status   Print current state (on/off)

Examples:

displaytoggle status      # Built-in display: ON
displaytoggle off         # Built-in display: OFF
displaytoggle on          # Built-in display: ON
displaytoggle toggle      # Built-in display: OFF

Exit codes: 0 success, 1 bad arguments, 2 runtime error.

Build & Install

# Build and install to PATH
bash build.sh

No code signing is required. The binary runs unsigned.

How It Works

macOS exposes no public API for disconnecting the built-in display while the lid is open. This tool uses two private functions from SkyLight.framework:

Private API Purpose
SLSConfigureDisplayEnabled Enable or disable a display
SLSGetDisplayList Enumerate all displays, including disabled ones

SLSConfigureDisplayEnabled is called inside a Core Graphics display configuration transaction:

CGBeginDisplayConfiguration(&config)
SLSConfigureDisplayEnabled(config, displayID, enabled)
CGCompleteDisplayConfiguration(config, .permanently)

The .permanently flag commits the change persistently. A side effect of this is that CGGetOnlineDisplayList no longer returns the disabled display — SLSGetDisplayList is used instead to locate the built-in display ID when turning it back on.

On Apple Silicon, a disabled display is fully disconnected at the hardware level (not just dimmed), identical to clamshell mode. It disappears from CGGetActiveDisplayList entirely.

Safety

displaytoggle off refuses to run if no external monitor is detected, preventing you from blacking out your only display:

Error: No external monitor detected. Refusing to black out built-in display.

Project Structure

displaytoggle/
├── Package.swift
├── build.sh
└── Sources/displaytoggle/
    ├── SkyLightBridge.h       # Objective-C bridge declaring private SkyLight APIs
    ├── DisplayManager.swift   # Display detection and toggle logic
    └── main.swift             # CLI argument parsing and entry point

Caveats

  • macOS updates may break this. SLSConfigureDisplayEnabled and SLSGetDisplayList are private, undocumented APIs with no stability guarantees.
  • Sleep/wake may re-enable the built-in display. macOS can restore the display state after waking from sleep. Run displaytoggle off again if needed, or wrap it in a launchd wake trigger.
  • M3 (non-Pro/Max) second external monitor. Disabling the built-in on entry-level M3 does not unlock a second external port — this is a macOS hardware limitation unrelated to this tool.

Prior Art & References

This tool was inspired by Alin Panaitiu's reverse-engineering work behind Lunar's BlackOut feature. His blog post — Turn off MacBook display in clamshell mode — identified SLSConfigureDisplayEnabled as the correct private API and explained why it works on Apple Silicon. That was the spark.

The actual implementation patterns, however, are drawn directly from screen_tune by Anton Orlov, an open-source menu bar app using the same private APIs:

  • The CGDisplayConfigRef signature for SLSConfigureDisplayEnabled (vs. Lunar's CGSConnectionID variant)
  • The CGBeginDisplayConfiguration / CGCompleteDisplayConfiguration(.permanently) transaction pattern
  • SLSGetDisplayList for enumerating all displays including disabled ones — necessary because CGGetOnlineDisplayList omits displays that have been turned off via SLSConfigureDisplayEnabled

Apple's open-source PowerManagement — PMDisplay.m — provided additional context on how macOS itself calls into SkyLight for clamshell mode.


Built with Claude.

About

A minimal Swift CLI to turn the MacBook built-in display on or off without closing the lid.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors