Match combined brightness by estimated luminance - #130
Merged
Conversation
Normalize built-in and external displays on an estimated nit scale, use the built-in panel's linear brightness API, and account for Crisp's low-range DDC/gamma blend. Apply the same mapping to absolute auto-brightness, expose a fine-tuning ratio, and add focused math tests and localization.
The six points from the review of #83, on top of alexlee2046's commit as it was. The dlsym'd CoreDisplay name is on the lint allowlist with the other two. The built-in keeps the proportional mapping when the linear DisplayServices symbols do not resolve: BrightnessService.supportsLinearBrightness gates the linear target in the view, and the two linear setters are no-ops without the API instead of feeding a linear value to the native percent API. The external luminance lookup finds its registry node the way DDC pairing finds its channel: DDCService.displayIdentity(from:) is shared (with its handling of the negative Int bit-patterns real monitors return) and the nodes go through DDCServiceMatcher, so a monitor reads its luminance from the node it pairs on; one walk is cached for the displays of a reconfiguration. The lookup runs from loadDetails off the main actor, and nominalMaxNits is published, since the walk covers the whole service plane. The absolute auto-brightness branch clamps to display.maxBrightness, both the nits path and the old percentage mirror. The defaults key has no .v2, and the math lives in Models.
This was referenced Sep 7, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Takes over #83 from @alexlee2046, whose commit is the first one here under their own author line. Fixes #83's six review points in the second commit and adds the credit line in the third.
What it does, in the user's terms: the combined brightness slider now moves the built-in panel by estimated luminance instead of by percentage. The built-in's slider curve is nothing like a DDC monitor's, so equal percentages never looked equal; with the panel's rated nits on both sides the slider aims both at the same light output, and the built-in is driven through the linear brightness API so the mapping holds along the whole range. Auto Brightness gets the same conversion in its "follow the built-in" mode. "Keep display offsets" is untouched. A new "Absolute brightness ratio" slider under Show Combined Brightness fine-tunes the pair when the rated numbers are off.
The review points, in order: the lint allowlist for the dlsym'd CoreDisplay name; the linear fallback that mixed domains is gone (a machine without the linear API keeps the old percentage path, gated by BrightnessService.supportsLinearBrightness); the external lookup reuses DDCService's registry walk and DDCServiceMatcher for node pairing, cached for 5 s; the lookup runs off the main actor as part of loadDetails; both Auto Brightness targets clamp to display.maxBrightness so Extra Brightness is not dragged down; the defaults key lost its .v2 and the math lives in Models with its tests in CrispTests.
Verified live on this desk with the slider on: the built-in resolves 600 nits, one external 1156, the other publishes nothing and stays proportional. Dragging the combined slider, externals at 21.8 percent put the built-in at linear 0.462 (73.5 on its own slider), at 29.2 about 0.62, and from about 52 percent up the built-in sits at its ceiling. The Settings slider reads 212 percent for this pair. Auto Brightness follow mode is computed from the same numbers, not driven, since I run with offsets.
One thing to know: the only luminance an EDID publishes is in its HDR static metadata block, so HDR monitors report their HDR peak while SDR panels report nothing. A mini-LED monitor running in SDR is nowhere near its peak, which is why the built-in tops out at half the slider on my desk. The ratio slider is the way back, and its caption now says so. The math is right; the reference number is what the monitor chooses to publish.