package brightness import ( "net" "strings" "sync" "time" "github.com/AvengeMedia/DankMaterialShell/core/internal/log" ) // devd(8) publishes device events on this SOCK_SEQPACKET socket, one // "!system=... subsystem=... type=..." record per packet. const devdSocketPath = "/var/run/devd.seqpacket.pipe" const ( devdMaxRetries = 5 devdBaseDelay = 2 * time.Second devdMaxDelay = 60 * time.Second ) type DevdMonitor struct { stop chan struct{} rescanMutex sync.Mutex rescanTimer *time.Timer rescanPending bool } func newDevdMonitor(manager *Manager) *DevdMonitor { m := &DevdMonitor{ stop: make(chan struct{}), } go m.run(manager) return m } func (m *DevdMonitor) run(manager *Manager) { failures := 0 for { if err := m.monitorLoop(manager); err != nil { log.Errorf("Devd monitor error: %v", err) } select { case <-m.stop: return default: } failures++ if failures > devdMaxRetries { log.Errorf("Devd monitor exceeded %d retries, giving up", devdMaxRetries) return } delay := min(devdBaseDelay*time.Duration(1<<(failures-1)), devdMaxDelay) log.Infof("Devd monitor reconnecting in %v (attempt %d/%d)", delay, failures, devdMaxRetries) select { case <-m.stop: return case <-time.After(delay): } } } func (m *DevdMonitor) monitorLoop(manager *Manager) error { conn, err := net.Dial("unixpacket", devdSocketPath) if err != nil { return err } defer conn.Close() done := make(chan struct{}) defer close(done) go func() { select { case <-m.stop: conn.Close() case <-done: } }() log.Info("Devd monitor started for backlight/drm events") buf := make([]byte, 8192) for { n, err := conn.Read(buf) if err != nil { select { case <-m.stop: return nil default: return err } } m.handleEvent(manager, string(buf[:n])) } } func (m *DevdMonitor) handleEvent(manager *Manager, event string) { notification, ok := strings.CutPrefix(event, "!") if !ok { return } fields := parseDevdEvent(notification) switch fields["system"] { case "DRM": m.debouncedRescan(manager) case "DEVFS": if fields["subsystem"] != "CDEV" { return } if !strings.HasPrefix(fields["cdev"], "backlight/") { return } m.debouncedRescan(manager) } } func parseDevdEvent(s string) map[string]string { fields := make(map[string]string) for _, part := range strings.Fields(s) { k, v, ok := strings.Cut(part, "=") if !ok { continue } fields[k] = v } return fields } func (m *DevdMonitor) debouncedRescan(manager *Manager) { m.rescanMutex.Lock() defer m.rescanMutex.Unlock() m.rescanPending = true if m.rescanTimer != nil { m.rescanTimer.Reset(2 * time.Second) return } m.rescanTimer = time.AfterFunc(2*time.Second, func() { m.rescanMutex.Lock() pending := m.rescanPending m.rescanPending = false m.rescanMutex.Unlock() if !pending { return } manager.Rescan() }) } func (m *DevdMonitor) Close() { close(m.stop) }