Releases: aquitaine/OpenDisplay
Release list
OpenDisplay 0.11.0
Added
- XDR Brightness now knows about your battery. The boost drives the built-in panel's backlight
to its HDR maximum — the single largest power draw a laptop has — and until now nothing ever
turned it back off. Four opt-in rules, under the Labs toggle in Settings → Health & Recovery, each
independent: turn the boost off when the Mac switches to battery power, turn it off below a
battery percentage you choose (10/20/30/50%), turn it off while Low Power Mode is on, and turn it
off a set time after you engaged it (15 min / 30 min / 1 h / 2 h). The first three are conditions,
so a fifth setting — "Bring the boost back when power returns", on by default — puts the exact
brightness you had back once all of them clear, and stays out of the way if you changed the boost
yourself in the meantime. The timer is not: time spent is spent, and an elapsed allowance is never
given back. Each automatic switch-off says why in a notification ("Battery at 18%") if you have
display notifications on. The power watcher is only held while XDR Brightness is on and one of
the battery-family rules is enabled — no IOKit listeners for a user who asked for none — and a
desktop Mac, which reports no battery at all, is unaffected by every one of these rules. - A boost no longer outlives the panel it was applied to. Closing the lid (or otherwise taking
the built-in out of service) while boosted used to leave the fraction set and the slider up on a
display nobody was looking at. It's now cleared through the normal path, with no restore when the
panel comes back — the same session-only rule the feature has always stated, where a relaunch
starts at normal brightness.
OpenDisplay 0.10.3
Added
- An app icon. OpenDisplay finally has a face: a neon-outline display with a power symbol on a
dark squircle, in the macOS Big Sur+ icon style. It shows up in Finder, the app switcher,
notifications, and System Settings panes — everywhere the generic app placeholder used to be.
Both app variants (full and public-API-only) carry it; the 1024px master lives in
Docs/assets/app-icon-1024.png.
OpenDisplay 0.10.2
Fixed
- A saved "built-in off" now survives a slow external across lid-open + unlock. Live case: lid
opened, thumbprint unlock, the built-in lit first (the always-one-active net doing its job) — but
the ultrawide became an active surface only after the 20-second wake window had closed. At that
edge the app concluded the user had re-enabled the built-in, erased the owed "off" from the
ledger, and when the external finally lit, nothing put the built-in back off (the auto-disconnect
edge didn't fire either — the external never left enumeration during this sleep, so there was no
"arrival"). The wake's attribution is now stamped onto the ledger entry the moment it is made
(relitDuringWakeAt): a stamped "off" stays owed past the window — time-to-unlock is unbounded —
and is paid whenever the covering display actually lights, then the stamp comes off. The stamp
persists with the ledger, so an app relaunch mid-convergence doesn't amnesty it; outside a wake,
un-stamped entries are still forgotten exactly as before (a System Settings re-enable stays the
user's decision). Past the window the backstop poll slows from 2 s to 30 s — the topology event
the external raises on activation is what normally answers anyway.
OpenDisplay 0.10.1
Fixed
- Every automatic display switch now leaves an audit entry. The 0.10.0 wake fix could turn a
display off (or light one) without a trace inaudit.jsonl— live, a built-in that relit on wake
and was put back off left the log still ending at the prior CLI disconnect. Wake-convergence
writes now record under their own actor,wakeConvergence:reassertOffwhen a ledger "off" is
re-asserted after wake,netActivatewhen the always-one-active net lights its fallback, and
netEscalatewhen it falls through to the permanent-configuration restore. The
auto-disconnect-on-external-arrival path — which on a wake can fire before the re-assert gets its
turn, and previously wrote nothing — records asautoDisconnect. Entries carry the real outcome
(committed/failed) instead of assuming success, so Settings → Recent Activity answers "why
did my display just switch?" with the exact path that did it.
Notes
- Known issue: driving a group leader's brightness to its maximum can overwrite the group's
learned per-display offsets, after which followers track the leader too bright. Recreating the
group resets the offsets; a proper fix is in progress.
OpenDisplay 0.10.0
Everything since 0.8.2 in one release: 0.9.0 was versioned in the tree but never published,
so its features ship here.
Added
-
One-click updates. "Check for Updates…" now does the whole job: OpenDisplay downloads the new
version, verifies it, installs it, and relaunches itself into it — no trip to the browser, no
dragging a new app into/Applications. Every download is checked against the project's EdDSA
signing key and the Developer ID signature of the copy you already have, so an update that
doesn't come from us doesn't run. Two settings govern it: "Check for updates automatically" (on by
default, about once a day, and it only tells you) and the opt-in "Download and install updates
automatically". A background check that finds something never interrupts you — it leaves a version
badge on the menu's update row and waits until you click it. -
Protected layout (Issue #38) — mark your current display arrangement as protected, and
OpenDisplay puts it back when something else moves it. Origins, resolution, refresh,
mirroring, and the main display are captured per display set — your laptop-alone layout and
your desk layout are protected independently — and re-applied within seconds after hotplug,
wake, or a stray arrangement change, with a notification saying exactly what was restored.
Rotation drift is detected and reported, but not auto-restored: rotation writes require the
experimental rotation helper (Labs), so a rotated-out-from-under-you display produces a
"couldn't restore" notification rather than a silent half-restore.
Restores run through the same checkpointed, verified, audited transaction path as every other
change, so the always-one-display-active guarantee holds throughout; a display you deliberately
turned off stays off, restore attempts are capped (never a tug-of-war with macOS), and the app's
own changes never trigger a restore. Managed from Settings → Arrange, or
opendisplay layout protect|unprotect|status. -
Display groups & brightness sync (Issue #39) — group displays so moving any member's
brightness moves them all. Each member keeps a learned offset: nudge one display to taste and
the group remembers the difference instead of fighting you. Optional contrast sync, media-key
support (one OSD, on the display you're driving), and agroup:selector for the CLI
(opendisplay brightness group:desk 0.6;opendisplay group create|add|remove|list).
Grouped displays are excluded from Adaptive Display's brightness targeting so the two features
can't fight. Groups are configured in Settings → Displays.
Fixed
-
A display you turned off no longer lights back up when the Mac wakes. With externals
connected and the built-in panel deliberately switched off, waking the Mac brought the built-in
back and left it on for the rest of the session. Three separate things had to be true for that,
and all three are fixed:macOS relights the panel itself on wake — its display-configuration transactions don't survive a
sleep — and OpenDisplay read that as you having switched it back on, so it forgot the display
was ever meant to be off. It now tells the two apart, and switches the display back off once
another screen is lit to take over (bounded, so it never turns into a tug-of-war with macOS).
This works whether or not Protected Layout is on: a display you turned off is already an
explicit instruction, not something that should need a second switch.The always-one-display-active safety net could also do it. For a second or two after a wake an
external that is still re-negotiating its link is indistinguishable from one that was unplugged,
and the net answered "nothing is on screen" by lighting the display you had turned off. It now
waits for a display that is plainly on its way back — while still guaranteeing a screen within
eight seconds no matter what the display list says, so the 0.8.2 black screen cannot return.And Protected Layout could not have helped, because a switched-off display leaves macOS's
display list entirely: the moment it came back, the arrangement was filed under one display set
and looked up under another. Protected layouts now cover the displays they keep switched off —
in the key, in what is captured, and in what is restored.
Notes
- Updating in place only works from a build that has the updater in it. If you are on 0.8.2 or
older, install this release by hand once; from then on OpenDisplay keeps itself current. - Releases now carry a second asset,
appcast.xml, alongsideOpenDisplay.zip— that file is the
update feed, produced and signed byscripts/release-signed.sh(seescripts/sparkle-setup.md). - CLI edits to app-owned settings made while the app is running —
groupedits and
layout protect|unprotectalike — are overwritten when the app next saves its settings.
Quit the app first, or use the Settings window. A shared on-disk store is planned.
0.8.2 — Never left with a black screen
A safety fix. If you use OpenDisplay to turn your built-in panel off, please take this one.
Fixed
-
Unplugging your last external display no longer leaves you with a black screen. If OpenDisplay had turned the built-in off (for example via turn the built-in off when an external connects), pulling the external left nothing on screen and the built-in never came back — the exact failure the always-one-display-active guarantee exists to prevent.
The cause turned out to be surprising: when the last real display disappears, macOS does not report zero displays. It substitutes a synthetic placeholder to keep the window server alive — active, main, and showing you nothing. So the safety net's "nothing is active any more" condition could never become true, and it never fired once. OpenDisplay now recognises that placeholder for what it is and counts only displays you can actually see.
-
Recovery now verifies itself. Re-enabling a display can report success while still producing no picture, so the result is checked against the live topology and escalated to a system-level configuration restore if the screen is still dark.
-
A sleeping display is no longer mistaken for a missing one.
CGDisplayIsActivereads false while a display sleeps, which made an idle Mac look display-less — so ordinary sleep could silently re-enable a built-in you had deliberately turned off, and forget it was owed a restore. This also fixes the built-in quietly coming back on, and your preferred layout not sticking. -
A failed reconnect no longer discards the record of a display that is owed a restore.
Install / upgrade: brew install --cask aquitaine/tap/opendisplay or the notarized zip below.
0.8.1 — CLI recovery for turned-off displays
A patch release fixing a hole in the recovery path.
Fixed
- The
opendisplayCLI can now see and recover displays the app turned off. A display that OpenDisplay logically disconnects vanishes from macOS's display list entirely, and the CLI kept no record of it — soopendisplay reconnect builtinanswered "no display matches". The documented recovery path was unusable from the one surface that still works when a display is off. Turned-off displays now appear inopendisplay list(marked, each with the exact command to bring it back), resolve through every selector, and reconnect correctly. opendisplay disconnectremembers what it turned off — previously a display disconnected from the CLI was unreachable by that same CLI moments later, because nothing recorded it.- The app and the CLI now share one on-disk definition of the turned-off-display list, so they can't disagree about which displays are owed a reconnect. Existing files are read unchanged.
- CLI display names fall back to a readable "Built-in Display" / class + resolution label instead of a raw
cg:37D8832A-…identifier.
Install / upgrade: brew install --cask aquitaine/tap/opendisplay or the notarized zip below.
0.8.0 — XDR Brightness
Every feature Lunar markets is now matched. This release closes the last row of the parity table.
Added
- XDR Brightness (Labs) — drive the MacBook Pro's XDR panel past its 500-nit SDR cap. A compact sun badge on the built-in's brightness row toggles a 2× boost: a tiny extended-dynamic-range trigger makes macOS raise the physical backlight, and a gamma-table remap hands that raised range to your normal SDR content — the whole desktop gets genuinely brighter (hardware-verified around 1600 nits at the internal 3.2× ceiling).
- Public Metal/Core Graphics only — no private API, so it even works in the public-API-only build (and survives the macOS 26.3 change that broke private-API approaches).
- Session-only and crash-safe by design: quit — or crash — and the panel is back to normal. Nothing persists.
- Stated trade-offs: HDR content looks clipped while boosted; sustained boost warms the panel and uses more battery.
- Opt-in: Settings → Labs → XDR Brightness.
Fixed
- The app now enforces a single running instance — a second copy (e.g. a debug build next to the installed release) exits immediately instead of fighting the first for the gamma slot, DDC bus, settings file, hotkeys, and menu bar.
Install / upgrade: brew install --cask aquitaine/tap/opendisplay or the notarized zip below.
0.7.1 — Review & hardening pass
A whole-codebase review and hardening pass — no new features, nine real bug fixes.
Fixed
- App Presets no longer lose a display's saved baseline when it's unplugged while a preset is active — the restore stays owed and is paid back when the display returns. A display that connects while a preset is active is now governed immediately instead of waiting for the next app switch.
- Settings resilience: one unreadable field (e.g. after a downgrade) now falls back to that field's default instead of resetting every setting and the FaceLight / app-preset / evening-preset restore ledgers.
- Display identity: a paired record can no longer absorb an unrelated anonymous monitor without corroborating evidence.
- Refresh-rate-only mode changes and 180° rotations now advance the topology generation instead of always waiting out the 2s stabilization timeout.
opendisplay edidno longer reports the built-in panel's EDID for an external whose model number is unreadable.- CLI:
favorite set <display> @2xreports a usage error instead of crashing; DDC brightness writes round instead of truncate. - Clock Mode solar anchors queried just after midnight on a DST-transition day are no longer an hour off.
- Concurrent audit-log writes from the app and CLI can no longer interleave.
Changed
- Night Shift detection reuses one CoreBrightness client instead of reconnecting every 5 seconds.
Install / upgrade: brew install --cask aquitaine/tap/opendisplay or download the notarized zip below.
0.7.0 — Location Mode & App Presets
Wave 2 of the Lunar-parity work. With this release, every Lunar-marketed feature except the XDR brightness unlock is matched — free and GPL. Signed and notarized: unzip and run.
New
- Location Mode (#31) — brightness follows the sun's real elevation at your location: night floor below civil twilight, a smooth ramp through dawn and dusk, full brightness with the sun high. No light sensor needed — ideal for lid-closed setups in rooms with natural light. Slots into Adaptive Display as a fallback source: live readings (built-in mirror, ambient sensor) still win when available, Clock Mode schedules still outrank it, and manual tweaks teach it your preferred offset. All computed locally from public NOAA equations; location is one-shot opt-in or manual lat/long.
- App Presets (#33) — per-app display presets: when a chosen app comes to the front, its brightness (and optionally contrast / colour preset) applies to the target display or all displays, and your prior state comes back when you switch away. Debounced so rapid ⌘-tabbing only commits where you land; crash-safe (pre-preset state is persisted before the first write); precedence with FaceLight, Clock Mode, and Adaptive Display is documented and unit-tested.
Install
brew install --cask aquitaine/tap/opendisplay…or download the zip below, unzip, and drag to Applications.
Full changelog: https://github.com/aquitaine/OpenDisplay/blob/main/CHANGELOG.md