initial commit

This commit is contained in:
2026-04-07 23:46:35 +03:00
commit cdbd8abaa4
13 changed files with 681 additions and 0 deletions

146
monitor/monitor.go Normal file
View File

@@ -0,0 +1,146 @@
package monitor
import (
"context"
"log"
"github.com/godbus/dbus/v5"
"led-controller/config"
)
type Event int
const (
DisplayOn Event = iota
DisplayOff
)
type Monitor struct {
cfg *config.Config
events chan<- Event
}
func New(cfg *config.Config, events chan<- Event) (*Monitor, error) {
return &Monitor{cfg: cfg, events: events}, nil
}
func (m *Monitor) Run(ctx context.Context) {
method := m.cfg.Monitor.Method
if method == "screensaver" || method == "all" {
go m.watchScreenSaver(ctx)
}
if method == "logind" || method == "all" {
go m.watchLogind(ctx)
}
<-ctx.Done()
}
// watchScreenSaver listens on the session bus for org.freedesktop.ScreenSaver.ActiveChanged
// This covers GNOME, KDE, XFCE, and other freedesktop-compliant DEs.
func (m *Monitor) watchScreenSaver(ctx context.Context) {
conn, err := dbus.ConnectSessionBus()
if err != nil {
log.Printf("screensaver monitor: cannot connect to session bus: %v", err)
return
}
defer conn.Close()
// Match both the freedesktop and GNOME screensaver interfaces
rules := []string{
"type='signal',interface='org.freedesktop.ScreenSaver',member='ActiveChanged'",
"type='signal',interface='org.gnome.ScreenSaver',member='ActiveChanged'",
}
for _, rule := range rules {
call := conn.BusObject().Call("org.freedesktop.DBus.AddMatch", 0, rule)
if call.Err != nil {
log.Printf("screensaver monitor: AddMatch failed for %q: %v", rule, call.Err)
}
}
sigCh := make(chan *dbus.Signal, 16)
conn.Signal(sigCh)
log.Println("screensaver monitor: listening for ActiveChanged signals")
for {
select {
case <-ctx.Done():
return
case sig := <-sigCh:
if sig == nil {
return
}
if sig.Name == "org.freedesktop.ScreenSaver.ActiveChanged" ||
sig.Name == "org.gnome.ScreenSaver.ActiveChanged" {
if len(sig.Body) < 1 {
continue
}
active, ok := sig.Body[0].(bool)
if !ok {
continue
}
if active {
log.Println("screensaver monitor: screen locked/blanked")
m.events <- DisplayOff
} else {
log.Println("screensaver monitor: screen unlocked/unblanked")
m.events <- DisplayOn
}
}
}
}
}
// watchLogind listens on the system bus for org.freedesktop.login1.Manager.PrepareForSleep.
// This catches system suspend/resume events.
func (m *Monitor) watchLogind(ctx context.Context) {
conn, err := dbus.ConnectSystemBus()
if err != nil {
log.Printf("logind monitor: cannot connect to system bus: %v", err)
return
}
defer conn.Close()
rule := "type='signal',interface='org.freedesktop.login1.Manager',member='PrepareForSleep'"
call := conn.BusObject().Call("org.freedesktop.DBus.AddMatch", 0, rule)
if call.Err != nil {
log.Printf("logind monitor: AddMatch failed: %v", call.Err)
return
}
sigCh := make(chan *dbus.Signal, 16)
conn.Signal(sigCh)
log.Println("logind monitor: listening for PrepareForSleep signals")
for {
select {
case <-ctx.Done():
return
case sig := <-sigCh:
if sig == nil {
return
}
if sig.Name == "org.freedesktop.login1.Manager.PrepareForSleep" {
if len(sig.Body) < 1 {
continue
}
preparing, ok := sig.Body[0].(bool)
if !ok {
continue
}
if preparing {
log.Println("logind monitor: system preparing to sleep")
m.events <- DisplayOff
} else {
log.Println("logind monitor: system woke up")
m.events <- DisplayOn
}
}
}
}
}