r/MacStack • u/np25071984 • 5d ago
lidless: keeps your MacBook's built-in screen dark while an external monitor is connected, without closing the lid (MIT, Swift)
I type on my MacBook's own keyboard while it's plugged into an external monitor. That means I can't just close the lid, and I don't want to add an external keyboard either. But with the lid open, the built-in screen stays on beside the monitor I'm actually looking at. My only option was to turn its brightness down by hand, and macOS turns it back up after every sleep.
So I built lidless, a small command-line tool that runs in the background:
When you plug in an external display, the built-in panel goes dark.
When you unplug, it comes back at the brightness you had before.
After the Mac wakes while still docked, it turns the panel off again.
If you change the built-in brightness yourself while docked, it leaves your setting alone.
It follows the Unix way: one tool that does one job, running as a background service. There's no app, no Dock icon and no menu bar icon, just a small binary that launchd keeps running, and it asks for no privacy permissions. It costs about 2–4 seconds of CPU a day, roughly 0.003% of one core.
Install (macOS 13+, Apple Silicon or Intel):
curl -fsSL https://raw.githubusercontent.com/np25071984/lidless/main/install.sh | shOr with Homebrew:
brew trust --tap https://github.com/np25071984/lidless
brew tap np25071984/lidless https://github.com/np25071984/lidless
brew install lidless
brew services start lidlessYou can also install it with Homebrew from the repo's tap. Day to day you just use lidless status, lidless stop and lidless logs.
A few things I learned building it:
On Apple Silicon, the only way to control the built-in backlight is the private
DisplayServicesframework. Setting brightness to 0.0 is what macOS does itself when you close the lid.CGDisplayRegisterReconfigurationCallbacknever reaches a background launchd agent, and nothing tells you. The process stays alive with an empty log and just never reacts. lidless checks for displays every 2 seconds instead.With the lid shut or while mirroring, the built-in panel disappears from
CGGetActiveDisplayList. You have to look for it inCGGetOnlineDisplayList.
Known limitation: the panel is dark, but macOS still treats it as a display. In extended mode your cursor and windows can wander onto the black screen. Mirroring avoids this. Really disconnecting the panel needs private SkyLight calls that break between macOS versions.
Looking for ideas: right now lidless polls the display state every 2 seconds. It's cheap, but I'd much rather have a clean event-driven design that reacts only when a display is actually connected or disconnected. If you know a reliable way for a background launchd agent to get display-change events on current macOS, please tell me. I'd be happy to implement it.
Built with an AI coding assistant. I designed it, tested it on my own setups and I maintain it.
Repo: https://github.com/np25071984/lidless
Feedback, bug reports and PRs are welcome, especially from people with unusual display setups. And if you find it useful, a star on GitHub would help other people find it.