Skip to content

Quarter brightness steps with Option+Shift, and Swift 6 language mode - #172

Merged
didriksg merged 4 commits into
mainfrom
brightness-quarter-steps
Sep 16, 2026
Merged

didriksg merged 4 commits into
mainfrom
brightness-quarter-steps

Conversation

@didriksg

@didriksg didriksg commented Sep 16, 2026

Copy link
Copy Markdown
Owner

macOS moves the built-in a quarter of a stop while Option and Shift are held. Crisp now moves the displays it drives the same way, so an external gets the same 64 steps, and a built-in under the pointer still passes through to macOS. No new setting: the people who know the shortcut will use it, and the people who do not are unaffected.

The OSD bezel on macOS 14 and 15 fills in proportion to filled over total, not in whole chiclets. I measured that on 14.8.7 and 15.7.7, so the bezel now gets 64 units instead of 16. It still draws sixteen segments, and only the fill edge moves in quarters. macOS 26 draws Crisp's own capsule and needed no change.

Verified on the desk: a plain press moves a full stop, Option+Shift moves a quarter of one. An external sat at 100 and one fine press down took it to 98.4375, exactly a quarter of a 6.25 stop. The unit tests cover the arithmetic, including the rounded value an external reports back over DDC.

The last commit moves every build path to Swift 6 language mode. The dev loop, the Xcode build, the release script and CI all compiled with -swift-version 5 and minimal strict concurrency, so none of the concurrency rules this code is written against were enforced. Twelve files needed a fix, and every one is a real isolation crossing: three timers that handed the timer itself into a main-actor block, a resume flag guarded by a lock over a captured var, the DDC completions that cross onto a per-display queue, Sparkle's delegate conformance, and a display link that ticks on the main actor.

The probe I measured that with is in the last commit, in scripts/ next to the other hardware probes.

The quarter-step idea came from #166.

macOS moves the built-in a quarter of a stop while Option and Shift are held, and Crisp's keys moved a whole stop whatever was held. The same quarter step now reaches every display Crisp drives, and the built-in under the pointer still passes through to macOS, which does it itself. A bound shortcut keeps the whole stop, since it has no modifiers of its own to read.

The pointer path steps through adjustDisplays like the other modes now, so the grid is picked in one place instead of two. Idea from #166.
The bezel fills in proportion to filled over total, not in whole chiclets, measured with a probe on 14.8.7 and 15.7.7. The fixed total of sixteen therefore kept three of every four quarter presses off the bar. Sixty-four units, four to a chiclet, put the fill edge where the press lands, and the bezel still draws its sixteen segments.

Crisp's own banner on macOS 26 already fills continuously, so it needed nothing.
The dev loop, the Xcode build, the release script and CI all compiled with -swift-version 5 and minimal strict concurrency, so none of the concurrency rules this code is written against were actually enforced. Every build path now uses -swift-version 6 and the SWIFT_STRICT_CONCURRENCY overrides are gone.

What that turned up, all of it real:

- The retry and watchdog timers in BrightnessKeyService, and the repeat timer in HotkeyService, handed the timer itself to a main-actor block. It stays in the callback now, where the run loop put it.
- CGHelpers guarded its resume flag with a lock over a captured var, which reads as a race whatever the lock does. The flag lives in a small class instead.
- DDCService's async completions run on a per-display queue, so they are @sendable.
- UpdateService is main-actor isolated, which is what Sparkle's standard user driver already asked of it, and the conformance says so.
- DDCOperationQueuePool.Hold and CGDisplayMode now state what they are: immutable or lock-guarded state that more than one queue reads.
- FrameSpring is main-actor, because a CADisplayLink made from a view ticks there.
- The Accessibility prompt key is named outright. The Command Line Tools SDK exposes it as a global var, which Swift 6 reads as shared mutable state.

docs/BUILDING.md carried the old flags too.
This is the tool that settled how the macOS 14 and 15 bezel draws a value that is not a whole chiclet. It asks OSDUIHelper for a given filled/total pair, which is how the proportional fill showed up, and that is why BrightnessHUDService passes 64 totals. Kept because the next question about that bezel would need it again.
@didriksg
didriksg merged commit 77e4380 into main Sep 16, 2026
2 checks passed
@didriksg
didriksg deleted the brightness-quarter-steps branch September 16, 2026 10:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant