Skip to content

Add Tern hardware brightness and Night Vision controls - #1

Merged
fortun8te merged 9 commits into
mainfrom
codex/xdr-performance-20261017
Oct 17, 2026
Merged

fortun8te merged 9 commits into
mainfrom
codex/xdr-performance-20261017

Conversation

@fortun8te

Copy link
Copy Markdown
Owner

Tern USB-C displays previously fell back to software dimming. This adds a verified hardware brightness route and a native slider that updates while dragging, keeps only the newest pending value, and verifies the settled value before saving it.

The update also suppresses stale readbacks, fixes rapid hotkey accumulation and failed-write rollback, removes the duplicate display-refresh observer that could hide reconnections, and replaces process-status polling with termination notification. The extra slider tick row and clipped subtitle are removed.

This publishes the previously local Night Vision work, matching blue icon assets, the MIT-licensed DDC helper in rebuildable source form, build documentation and verification workflow. Existing remote README history was merged without force-pushing. The Swift language setting is corrected from unsupported 5.10 to 5.0.

Validation:

  • The old scheduler reproduced zero writes during continuous dragging. Production-controller fake-CLI tests now verify live writes, bounded coalescing, final-value verification, stale-result suppression, rollback and shutdown.
  • Installed native XDR 1.2.0 was tested against the actual Tern monitor at 95% and rapid adjustments back to 100%; hardware and saved-preset values matched.
  • Full app compiled with static dependencies and hardened-runtime signing. GPU or RAM reductions are not claimed; the unchanged XDR EDR trigger is retained for headroom stability.
  • Source helper build, portable regression script and whitespace checks pass. GitHub-hosted build/tests are reported separately by the workflow.

fortun8te and others added 9 commits June 12, 2026 20:56
Overlay-only red filter that is meaningfully stronger than macOS Night Shift,
for astronomy/red-light dark adaptation while keeping the screen readable.

- NightVision service: one full-screen, click-through, all-spaces, screen-share
  excluded (sharingType .none) overlay window per display, pinned one level above
  the boost/dimmer shielding window. Two CALayers: a multiplyBlendMode tint layer
  (keeps red, suppresses green/blue so the screen is red-channel-only rather than
  a flat red wash) over a black darkening layer for the deep, dimmed end.
- NightVisionCurve: pure intensity -> color/alpha mapping. Red pinned full, green
  and blue eased down (blue suppressed hardest), darkening fades in above 50%.
- Touches no display gamma table anywhere, so it never fights the EDR boost engine
  or SoftwareDimmer and survives macOS gamma wipes on display reconfig.
- Handles display connect/disconnect via didChangeScreenParameters; tears down
  overlays for vanished screens; isReleasedWhenClosed = false.
- UserDefaults persistence (enabled + intensity), restored after launch.
- UI: Night Vision section in the menu bar popover (toggle + red intensity slider),
  matching DisplayCard/DimSlider styling and Typography tokens.
- 12 unit tests for the pure curve math in XDRTests; all 45 tests green.

No changes to the forbidden EDR-trigger / gamma / Metal-overlay paths.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…tore

Retune the curve so high intensity is a readable salmon-deep-red instead of
an illegible wall of red: green floor 0.30, blue floor 0.12, max darken 0.22
starting at 60%, with an eased (smoothstep) falloff. Low end stays a gentle
warm shift.

Engage now fades: overlay fades in over 1.2s, out over 0.4s, and slider moves
glide over 0.18s instead of snapping.

First-ever enable defaults to 35% (warm zone). On launch a forgotten extreme
setting (enabled with intensity > 70%) restores OFF while keeping the stored
intensity, so it never ambushes the user at login.

Add a live zone caption (Warm / Red shift / Dark adaptation) under the slider
and keep panel accents on red-surviving tones.

Lock the floors, ramp math, and restore rule with tests (23 night-vision
tests, 56 total, all green).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…able state, Dock window section

ROOT CAUSE of the red wall + activation glitch: CALayer compositingFilter
(multiplyBlendMode) cannot blend with other windows; the window server
composites windows source-over only, so the 'multiply tint' painted
near-opaque red over the screen and artifacted while trying.

- New model: black dim layer + deep-red wash layer, plain source-over,
  honest math (white response floors locked by tests: g>=0.30 b>=0.28)
- Window hygiene: animationBehavior=.none, layers configured before
  orderFront, first composited frame fully transparent
- NightVision is now @observable: popover and main window share one truth
  (stale per-view state copies removed)
- Night Vision section added to the Dock app main window
- 53 tests green

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Research (astronomy dark adaptation): proper red mode suppresses blue/green
WAVELENGTHS at the hardware level and stays dim, not a translucent red film.
Rebuilt to match.

- Rendering: per-display gamma LUT via CGSetDisplayTransferByFormula (the
  SoftwareDimmer's proven mechanism). Green and (harder) blue ceilings drop
  with intensity; red stays bright, dims slightly only past 70%. White -> deep
  red, blue -> near black: a true red filter reaching every pixel incl.
  fullscreen video and cursor, not an overlay wash.
- MULTI-MONITOR FIX: enumerate with CGGetOnlineDisplayList instead of
  NSScreen.screens/displayID (which reports 0 for some externals and was
  silently skipping them). Re-assert on display-change AND wake (macOS wipes
  gamma on both), mirroring SoftwareDimmer.
- Boost/dimmer coexistence: a display whose gamma is owned by the boost engine
  (brightness > SDR max) or the software dimmer gets a red wash overlay instead
  of gamma, so Night Vision never fights the forbidden zone for a LUT. Only
  ever resets gamma it set itself.
- Gentle engage: gamma is stepped over 1.2s in / 0.4s out (eased), never snaps.
- 48 tests green.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The user's diagnosis was right: night vision, the boost engine, and the
software dimmer all write display gamma, and three writers on one LUT is
the fight that made night vision behave erratically.

- setNightVisionEnabled(true): parks live boosts at SDR max (saved),
  suspends the dimmer (identity gamma, overlays gone, levels kept), then
  enables NV. NV now always owns gamma outright; overlay fallback retired.
- setNightVisionEnabled(false): NV ramps out, then boosts and dim levels
  are restored automatically (0.7s handoff).
- While NV is on: brightness requests clamp to SDR max, the reconfig
  boost re-assert is skipped, and the dimmer ignores reassert/display
  events (levels still persist).
- 48 tests green.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Previously night vision fully suspended the dimmer, so dimming other
monitors did nothing while night vision was on. Now they coexist:
- SoftwareDimmer gains gammaSuspended: while NV owns gamma the dimmer stops
  writing gamma but keeps managing its deep-end black overlay, and exposes
  gammaScale(for:).
- NightVision folds each display's dim scale into its red gamma write
  (ceiling * dimScale), so a dimmed monitor stays dim AND red. A dim change
  while NV is on calls nightVision.reapplyGamma() via onDimChangedWhileSuspended.
- Disable restores the dimmer's own gamma. Boost parking unchanged.
- 23 tests green.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
A dim-slider drag while Night Vision was on called reapplyGamma() on every
tick, rewriting every display's LUT dozens of times per second. Coalesce to
one gamma pass per frame (16ms). 23 tests green.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@fortun8te
fortun8te merged commit 1493e2a into main Oct 17, 2026
1 check passed
@fortun8te
fortun8te deleted the codex/xdr-performance-20261017 branch October 17, 2026 15:15
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