fix(fans): use IOKit power notifications for sleep/wake fan state#3189
Draft
marxo126 wants to merge 2 commits intoexelban:masterfrom
Draft
fix(fans): use IOKit power notifications for sleep/wake fan state#3189marxo126 wants to merge 2 commits intoexelban:masterfrom
marxo126 wants to merge 2 commits intoexelban:masterfrom
Conversation
NSWorkspace.didWakeNotification fires too late and unreliably for SMC restoration. Move sleep/wake handling to IORegisterForSystemPower in a new app-level FanPowerManager singleton, decoupled from the popup view. Fixes the case where the popup is closed at sleep time — previously the observers were torn down with the popup and the user's manual fan mode was lost on wake. Closes exelban#3091 Closes exelban#2977
…onstants Build fixes after initial commit: - IOPowerSourceCallbackType does not exist; correct type is IOServiceInterestCallback - kIOMessageSystemWillSleep/kIOMessageSystemHasPoweredOn are not bridged through IOKit.pwr_mgt; declare locally with their canonical hex values
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.
Problem
Fan sleep/wake handling currently lives in
Modules/Sensors/popup.swiftand usesNSWorkspace.didWakeNotification/NSWorkspace.willSleepNotification. This has three structural issues:NSWorkspace.didWakeNotificationarrives after SMC is ready only intermittently —IORegisterForSystemPower(IOKit) fires earlier and more reliably.FanView). If the user closes the menu bar popup between sleep and wake, the observers are torn down with the view and the user's manual fan mode is never restored.willSleepMode/willSleepSpeedsnapshot lives on the popup view, so it is only valid while the popup is alive.This causes the regressions reported in #3091 ("manual mode resets to auto after sleep/wake") and #2977 ("fan goes to 100% after sleep" — same root cause path: snapshot lost, restoration falls back to default).
Change
Modules/Sensors/fanPower.swift—FanPowerManager.sharedsingleton subscribing toIORegisterForSystemPower. Holds the per-fan(mode, speed)snapshot dictionary at the app level, independent of any view lifecycle.kIOMessageSystemWillSleep: snapshot every registered fan with non-automatic mode, then set automatic before sleep. Acknowledge withIOAllowPowerChange.kIOMessageSystemHasPoweredOn: 2-second delay (let SMC settle), then restore mode + speed viaSMCHelper.shared. Clear snapshot.Modules/Sensors/popup.swift— removed theNSWorkspaceobservers,wakeListener,sleepListener,willSleepMode,willSleepSpeed, and the now-deadresetModeAfterSleepre-sync block. The reader's normal tick picks up the restored mode on next read.Modules/Sensors/main.swift— registers each discovered fan withFanPowerManager.sharedat module init time.Net diff
Testing
Logic-reviewed against the existing fan code paths but not yet tested on real hardware — flagging this as Draft so you can take a look at the approach before I run it through sleep/wake on Apple Silicon.
If the approach lands well I'm happy to:
Marking Draft. Happy to iterate or split further if you'd prefer the new singleton lives elsewhere (e.g.
Kit/).