Skip to content

Brightness read back after a crash can save the care dim as yours #95

Description

@Rydersel

What happens

When OLED care dims a display (for example while the screen is locked) and the
app then dies without cleaning up, the panel is left dim. On the next launch
Candela recovers such a display: it notices its own marker, puts the stored
brightness back, and clears the marker. A guard now stops the launch refresh from
reading the panel's own dimmed register back in before that recovery runs.

The same protection is missing on the native leg, which is the path the dim rides
whenever HDR is on and DDC cannot be used. Two places read the display's current
native brightness and persist it as the user's value with no marker check:

  • BrightnessController.adoptExternal and
    BrightnessController.adoptNativeForSurface, in
    CandelaKit/Sources/CandelaKit/Brightness/BrightnessController.swift, which
    adopt a brightness the app did not set (for example when the menu bar panel
    opens and reads the current value).
  • The brightness poller restarted by restartPoller in
    Candela/App/AppModel.swift, which starts before the interrupted-dim recovery
    runs.

Either one can ease into the leftover dim, save it as the user's brightness, and
leave the recovery with nothing correct to restore. The display then stays dim
and the app believes that is what the person chose.

Expected

While a display carries the interrupted-dim marker, nothing persists a native
read as the user's brightness. The recovery runs first, restores the stored
value, clears the marker, and only then does adoption or polling resume.

Notes

The fix shape suggested is the one already used on the DDC readback path: check
the marker before persisting, in both adoption entry points, and make the
recovery run before the poller is restarted. This was ruled out of scope for the
interrupted-dim work, whose guard was deliberately scoped to the readback path.

Note that the monitor which cannot answer DDC reads hides this class of defect
entirely; it only shows on a panel that answers reads, or on the native leg.
Whether the ordering change alone is enough, or both entry points need the guard
as well, is for the fix to establish; the readback guard's own test shape applies
to both. The recovery and the readback guard land with #90.

Hardware verification

  1. Enroll a read-answering external monitor in OLED care with HDR on, so the dim
    rides the native leg. Note the brightness the app shows. Lock the screen and
    confirm the panel visibly dims. Positive control: quit Candela, lock the
    screen again, and confirm the panel does not dim.
  2. While it is dim, kill the app rather than quitting it, so the marker survives.
    Confirm the panel is still dim with no app running.
  3. Relaunch and read the brightness Candela shows for that display. It must be
    the value from step 1. Positive control: clear the marker by hand before the
    relaunch and confirm the app instead adopts the dim value, which is the
    signature of this defect.
  4. Repeat steps 1 to 3 with HDR off, to confirm the DDC path still recovers.
    Positive control: the same run before this change already passed, so a failure
    here is a regression from the fix.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions