Skip to content

Brightness sync never resumes after display/system sleep on Intel Macs (soberNow doesn't restart job) #1911

Description

@LuisMa9L

Before opening the issue, have you...?

  • Searched for existing issues
  • Looked through the wiki
  • Updated MonitorControl to the latest version (if applicable)

Describe the bug

On an Intel Mac, "Sync brightness changes from built-in display" works after launch, but stops for good after the first display sleep or system sleep. The app keeps running and keyboard brightness control still works, yet ambient light sensor changes are no longer synced to the external display until MonitorControl is relaunched. Same symptom as #922 and #1372 (both closed); still present in v4.4.0 and on main.

Cause (from reading the v4.4.0 source, AppDelegate.swift): sleepNotification() increments sleepID, so the next job() tick exits ("MonitorControl job died because of sleep or reconfiguration."). On wake, soberNow() only restarts the job in two cases:

if self.reconfigureID != 0 {
  self.configure(dispatchedReconfigureID: dispatchedReconfigureID) // ends with job(start: true)
} else if Arm64DDC.isArm64 {
  DisplayManager.shared.updateArm64AVServices()
  self.job(start: true)
}

On Intel, when no display reconfiguration happened during sleep, neither branch runs, so job() is never restarted.

Suggested fix: restart the job regardless of architecture. job(start:) already guards against a second loop via jobRunning.

} else {
  if Arm64DDC.isArm64 {
    os_log("Displays don't need reconfig after sober but might need AVServices update", type: .info)
    DisplayManager.shared.updateArm64AVServices()
  }
  self.job(start: true)
}

Workaround: relaunching MonitorControl a few seconds after NSWorkspace.screensDidWakeNotification / didWakeNotification (I use a small LaunchAgent) restores sync.

Side note, possibly a separate issue: with smooth brightness transitions enabled and combined hardware/software dimming, I got the "Is f.lux or similar running?" alert right after a relaunch. From the code, restoreSwBrightnessForAllDisplays(async: true) animates the gamma in the background while the first job() tick calls OtherDisplay.setBrightness() -> checkGammaInterference(), which can read the half-animated gamma table as interference (and resets software brightness to 1). With smooth transitions disabled it didn't happen again in my tests.

Steps to reproduce

  1. On an Intel Mac with a built-in display (ambient light sensor) and an external DDC display, enable "Sync brightness changes from built-in display" (combined dimming on, startup action "Apply last saved values").
  2. Launch MonitorControl and cover the ambient light sensor: the external display follows.
  3. Put the displays to sleep (pmset displaysleepnow), wait a few seconds, wake them.
  4. Cover the sensor again: the external display no longer follows.
  5. Relaunch MonitorControl: sync works again until the next sleep.

Log captured with /usr/bin/log stream --info --predicate 'process == "MonitorControl"' during step 3:

12:46:54  Sleeping with sleep 1
12:46:55  MonitorControl job died because of sleep or reconfiguration.
12:47:14  Waking up from sleep 1
12:47:17  Sober from sleep 1

No "MonitorControl job started." follows. In earlier runs, sync stayed dead for 20+ minutes until a manual relaunch.

Expected behavior

After waking, brightness sync resumes automatically, as the Apple Silicon branch of soberNow() already does, without relaunching the app.

Anything else?

No response

Environment Information (please complete the following information)

- macOS version: 13.7.8 Ventura
- Mac model: iMac (Retina 4K, 21.5-inch, 2017), Intel Core i5-7500
- MonitorControl version: v4.4.0
- Monitor(s): Gigabyte G27F 2 (via USB-C to HDMI adapter)
- Apple Silicon/M1 (yes or no): no

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

    bugIssue is a bug (e.g. Crash, …)

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions