1
0
mirror of https://github.com/AvengeMedia/DankMaterialShell.git synced 2026-08-09 15:08:31 -04:00

gamma: fix scheduler losing track during suspend

port 1.5
This commit is contained in:
bbedward
2026-07-14 22:26:44 -04:00
parent 3cd52ca327
commit 5d2093e54a
2 changed files with 50 additions and 18 deletions
+48 -15
View File
@@ -23,6 +23,9 @@ import (
const animKelvinStep = 25 const animKelvinStep = 25
// Go timers freeze during suspend; cap sleeps so wall-clock deadlines can't be missed.
const maxScheduleWait = 5 * time.Minute
func NewManager(display wlclient.WaylandDisplay, config Config) (*Manager, error) { func NewManager(display wlclient.WaylandDisplay, config Config) (*Manager, error) {
if err := config.Validate(); err != nil { if err := config.Validate(); err != nil {
return nil, err return nil, err
@@ -304,8 +307,8 @@ func (m *Manager) setupControlHandlers(state *outputState, control *wlr_gamma_co
out.rampSize = size out.rampSize = size
out.failed = false out.failed = false
out.retryCount = 0 out.retryCount = 0
out.lastTemp = 0
} }
m.lastAppliedTemp = 0
m.applyCurrentTemp("gamma_size") m.applyCurrentTemp("gamma_size")
}) })
}) })
@@ -507,6 +510,10 @@ func (m *Manager) recalcSchedule(now time.Time) {
config.ManualSunrise.Hour(), config.ManualSunrise.Minute(), config.ManualSunrise.Second(), 0, now.Location()) config.ManualSunrise.Hour(), config.ManualSunrise.Minute(), config.ManualSunrise.Second(), 0, now.Location())
sunset := time.Date(now.Year(), now.Month(), now.Day(), sunset := time.Date(now.Year(), now.Month(), now.Day(),
config.ManualSunset.Hour(), config.ManualSunset.Minute(), config.ManualSunset.Second(), 0, now.Location()) config.ManualSunset.Hour(), config.ManualSunset.Minute(), config.ManualSunset.Second(), 0, now.Location())
if !sunset.After(sunrise) {
// night start past midnight belongs to the next day
sunset = sunset.Add(24 * time.Hour)
}
times = SunTimes{ times = SunTimes{
Dawn: sunrise.Add(-dur), Dawn: sunrise.Add(-dur),
Sunrise: sunrise, Sunrise: sunrise,
@@ -518,6 +525,8 @@ func (m *Manager) recalcSchedule(now time.Time) {
lat, lon := m.getLocation() lat, lon := m.getLocation()
if lat == nil || lon == nil { if lat == nil || lon == nil {
m.gammaState = StateStatic m.gammaState = StateStatic
// stale times from a previous config must not drive applies
m.schedule = sunSchedule{}
return return
} }
times, cond = CalculateSunTimesWithTwilight(*lat, *lon, now, config.ElevationTwilight, config.ElevationDaylight) times, cond = CalculateSunTimesWithTwilight(*lat, *lon, now, config.ElevationTwilight, config.ElevationDaylight)
@@ -611,7 +620,26 @@ func (m *Manager) getSunPosition(now time.Time) float64 {
return 1.0 return 1.0
} }
func shiftTimes(times SunTimes, d time.Duration) SunTimes {
return SunTimes{
Dawn: times.Dawn.Add(d),
Sunrise: times.Sunrise.Add(d),
Sunset: times.Sunset.Add(d),
Night: times.Night.Add(d),
}
}
// activeCycle maps early-morning hours back to yesterday's cycle when the
// schedule crosses midnight.
func activeCycle(now time.Time, times SunTimes) SunTimes {
if now.Before(times.Night.Add(-24 * time.Hour)) {
return shiftTimes(times, -24*time.Hour)
}
return times
}
func (m *Manager) getSunPositionNormal(now time.Time, times SunTimes) float64 { func (m *Manager) getSunPositionNormal(now time.Time, times SunTimes) float64 {
times = activeCycle(now, times)
if now.Before(times.Dawn) { if now.Before(times.Dawn) {
return 0.0 return 0.0
} }
@@ -666,7 +694,7 @@ func (m *Manager) getNextDeadline(now time.Time) time.Time {
} }
func (m *Manager) getDeadlineNormal(now time.Time, sched sunSchedule) time.Time { func (m *Manager) getDeadlineNormal(now time.Time, sched sunSchedule) time.Time {
times := sched.times times := activeCycle(now, sched.times)
switch { switch {
case now.Before(times.Dawn): case now.Before(times.Dawn):
return times.Dawn return times.Dawn
@@ -737,8 +765,11 @@ func (m *Manager) schedulerLoop() {
if enabled { if enabled {
deadline := m.getNextDeadline(now) deadline := m.getNextDeadline(now)
waitDur = time.Until(deadline) waitDur = time.Until(deadline)
if waitDur < time.Second { switch {
case waitDur < time.Second:
waitDur = time.Second waitDur = time.Second
case waitDur > maxScheduleWait:
waitDur = maxScheduleWait
} }
} else { } else {
waitDur = 24 * time.Hour waitDur = 24 * time.Hour
@@ -818,8 +849,6 @@ func (m *Manager) applyGamma(temp int) {
return return
case !m.controlsInitialized: case !m.controlsInitialized:
return return
case m.lastAppliedTemp == temp && m.lastAppliedGamma == gamma:
return
} }
var outs []*outputState var outs []*outputState
@@ -845,6 +874,8 @@ func (m *Manager) applyGamma(temp int) {
continue continue
case out.gammaControl == nil: case out.gammaControl == nil:
continue continue
case out.lastTemp == temp && out.lastGamma == gamma:
continue
case !m.outputStillValid(out): case !m.outputStillValid(out):
continue continue
} }
@@ -865,19 +896,19 @@ func (m *Manager) applyGamma(temp int) {
for _, j := range jobs { for _, j := range jobs {
err := m.setGammaBytes(j.out, j.data) err := m.setGammaBytes(j.out, j.data)
if err == nil { if err == nil {
j.out.lastTemp = temp
j.out.lastGamma = gamma
continue continue
} }
log.Warnf("gamma: failed to set output %d: %v", j.out.id, err) log.Warnf("gamma: failed to set output %d: %v", j.out.id, err)
j.out.failed = true j.out.failed = true
j.out.rampSize = 0 j.out.rampSize = 0
j.out.lastTemp = 0
if isConnectionDeadErr(err) { if isConnectionDeadErr(err) {
m.markConnectionDead(err) m.markConnectionDead(err)
return return
} }
} }
m.lastAppliedTemp = temp
m.lastAppliedGamma = gamma
} }
func (m *Manager) setGammaBytes(out *outputState, data []byte) error { func (m *Manager) setGammaBytes(out *outputState, data []byte) error {
@@ -939,7 +970,8 @@ func (m *Manager) updateStateFromSchedule() {
pos = m.getSunPosition(now) pos = m.getSunPosition(now)
temp = m.getTempFromPosition(pos) temp = m.getTempFromPosition(pos)
deadline = m.getNextDeadline(now) deadline = m.getNextDeadline(now)
isDay = now.After(times.Sunrise) && now.Before(times.Sunset) cycle := activeCycle(now, times)
isDay = now.After(cycle.Sunrise) && now.Before(cycle.Sunset)
} }
newState := State{ newState := State{
@@ -1053,14 +1085,15 @@ func (m *Manager) handleResume() {
return return
} }
// Compositors (Niri, Hyprland, wlroots-based) re-apply the cached gamma // Compositor gamma state is unknown after resume; force a resend (#1235)
// ramp to DRM on resume; gamma_control objects stay valid. We just need // and re-arm the scheduler timer, which froze during suspend.
// to force a resend so the schedule catches up with the current time of m.outputs.Range(func(_ uint32, out *outputState) bool {
// day — the original #1235 regression was caused by lastAppliedTemp out.lastTemp = 0
// matching and the send being skipped. return true
})
m.recalcSchedule(time.Now()) m.recalcSchedule(time.Now())
m.lastAppliedTemp = 0
m.applyCurrentTemp("resume") m.applyCurrentTemp("resume")
m.triggerUpdate()
} }
func (m *Manager) triggerUpdate() { func (m *Manager) triggerUpdate() {
+2 -3
View File
@@ -102,9 +102,6 @@ type Manager struct {
dbusSignal chan *dbus.Signal dbusSignal chan *dbus.Signal
geoClient geolocation.Client geoClient geolocation.Client
lastAppliedTemp int
lastAppliedGamma float64
} }
type outputState struct { type outputState struct {
@@ -117,6 +114,8 @@ type outputState struct {
isVirtual bool isVirtual bool
retryCount int retryCount int
lastFailTime time.Time lastFailTime time.Time
lastTemp int
lastGamma float64
} }
func DefaultConfig() Config { func DefaultConfig() Config {