mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2026-01-24 21:42:51 -05:00
251 lines
6.5 KiB
Go
251 lines
6.5 KiB
Go
package freedesktop
|
|
|
|
import (
|
|
"path/filepath"
|
|
"strings"
|
|
"sync/atomic"
|
|
"time"
|
|
|
|
"github.com/AvengeMedia/DankMaterialShell/core/internal/log"
|
|
"github.com/godbus/dbus/v5"
|
|
"github.com/godbus/dbus/v5/introspect"
|
|
)
|
|
|
|
type screensaverHandler struct {
|
|
manager *Manager
|
|
}
|
|
|
|
func (m *Manager) initializeScreensaver() error {
|
|
if m.sessionConn == nil {
|
|
m.stateMutex.Lock()
|
|
m.state.Screensaver.Available = false
|
|
m.stateMutex.Unlock()
|
|
return nil
|
|
}
|
|
|
|
reply, err := m.sessionConn.RequestName(dbusScreensaverName, dbus.NameFlagDoNotQueue)
|
|
if err != nil {
|
|
log.Warnf("Failed to request screensaver name: %v", err)
|
|
m.stateMutex.Lock()
|
|
m.state.Screensaver.Available = false
|
|
m.stateMutex.Unlock()
|
|
return nil
|
|
}
|
|
|
|
if reply != dbus.RequestNameReplyPrimaryOwner {
|
|
log.Warnf("Screensaver name already owned by another process")
|
|
m.stateMutex.Lock()
|
|
m.state.Screensaver.Available = false
|
|
m.stateMutex.Unlock()
|
|
return nil
|
|
}
|
|
|
|
handler := &screensaverHandler{manager: m}
|
|
|
|
if err := m.sessionConn.Export(handler, dbusScreensaverPath, dbusScreensaverInterface); err != nil {
|
|
log.Warnf("Failed to export screensaver on %s: %v", dbusScreensaverPath, err)
|
|
return nil
|
|
}
|
|
|
|
if err := m.sessionConn.Export(handler, dbusScreensaverPath2, dbusScreensaverInterface); err != nil {
|
|
log.Warnf("Failed to export screensaver on %s: %v", dbusScreensaverPath2, err)
|
|
return nil
|
|
}
|
|
|
|
introNode := &introspect.Node{
|
|
Name: dbusScreensaverPath,
|
|
Interfaces: []introspect.Interface{
|
|
introspect.IntrospectData,
|
|
{Name: dbusScreensaverInterface},
|
|
},
|
|
}
|
|
if err := m.sessionConn.Export(introspect.NewIntrospectable(introNode), dbusScreensaverPath, "org.freedesktop.DBus.Introspectable"); err != nil {
|
|
log.Warnf("Failed to export introspectable on %s: %v", dbusScreensaverPath, err)
|
|
}
|
|
|
|
introNode2 := &introspect.Node{
|
|
Name: dbusScreensaverPath2,
|
|
Interfaces: []introspect.Interface{
|
|
introspect.IntrospectData,
|
|
{Name: dbusScreensaverInterface},
|
|
},
|
|
}
|
|
if err := m.sessionConn.Export(introspect.NewIntrospectable(introNode2), dbusScreensaverPath2, "org.freedesktop.DBus.Introspectable"); err != nil {
|
|
log.Warnf("Failed to export introspectable on %s: %v", dbusScreensaverPath2, err)
|
|
}
|
|
|
|
go m.watchPeerDisconnects()
|
|
|
|
m.stateMutex.Lock()
|
|
m.state.Screensaver.Available = true
|
|
m.state.Screensaver.Inhibited = false
|
|
m.state.Screensaver.Inhibitors = []ScreensaverInhibitor{}
|
|
m.stateMutex.Unlock()
|
|
|
|
log.Info("Screensaver inhibit listener initialized")
|
|
return nil
|
|
}
|
|
|
|
func (h *screensaverHandler) Inhibit(sender dbus.Sender, appName, reason string) (uint32, *dbus.Error) {
|
|
if appName == "" {
|
|
return 0, dbus.NewError("org.freedesktop.DBus.Error.InvalidArgs", []any{"application name required"})
|
|
}
|
|
|
|
if reason == "" {
|
|
return 0, dbus.NewError("org.freedesktop.DBus.Error.InvalidArgs", []any{"reason required"})
|
|
}
|
|
|
|
if strings.Contains(strings.ToLower(reason), "audio") && !strings.Contains(strings.ToLower(reason), "video") {
|
|
log.Debugf("Ignoring audio-only inhibit from %s: %s", appName, reason)
|
|
return 0, nil
|
|
}
|
|
|
|
if idx := strings.LastIndex(appName, "/"); idx != -1 && idx < len(appName)-1 {
|
|
appName = appName[idx+1:]
|
|
}
|
|
appName = filepath.Base(appName)
|
|
|
|
cookie := atomic.AddUint32(&h.manager.screensaverCookieCounter, 1)
|
|
|
|
inhibitor := ScreensaverInhibitor{
|
|
Cookie: cookie,
|
|
AppName: appName,
|
|
Reason: reason,
|
|
Peer: string(sender),
|
|
StartTime: time.Now().Unix(),
|
|
}
|
|
|
|
h.manager.stateMutex.Lock()
|
|
h.manager.state.Screensaver.Inhibitors = append(h.manager.state.Screensaver.Inhibitors, inhibitor)
|
|
h.manager.state.Screensaver.Inhibited = len(h.manager.state.Screensaver.Inhibitors) > 0
|
|
h.manager.stateMutex.Unlock()
|
|
|
|
log.Infof("Screensaver inhibited by %s (%s): %s -> cookie %08X", appName, sender, reason, cookie)
|
|
|
|
h.manager.NotifyScreensaverSubscribers()
|
|
|
|
return cookie, nil
|
|
}
|
|
|
|
func (h *screensaverHandler) UnInhibit(sender dbus.Sender, cookie uint32) *dbus.Error {
|
|
h.manager.stateMutex.Lock()
|
|
defer h.manager.stateMutex.Unlock()
|
|
|
|
found := false
|
|
inhibitors := h.manager.state.Screensaver.Inhibitors
|
|
for i, inh := range inhibitors {
|
|
if inh.Cookie != cookie {
|
|
continue
|
|
}
|
|
log.Infof("Screensaver uninhibited by %s (%s) cookie %08X", inh.AppName, sender, cookie)
|
|
h.manager.state.Screensaver.Inhibitors = append(inhibitors[:i], inhibitors[i+1:]...)
|
|
found = true
|
|
break
|
|
}
|
|
|
|
if !found {
|
|
log.Debugf("UnInhibit: no match for cookie %08X", cookie)
|
|
return nil
|
|
}
|
|
|
|
h.manager.state.Screensaver.Inhibited = len(h.manager.state.Screensaver.Inhibitors) > 0
|
|
|
|
go h.manager.NotifyScreensaverSubscribers()
|
|
|
|
return nil
|
|
}
|
|
|
|
func (m *Manager) watchPeerDisconnects() {
|
|
if m.sessionConn == nil {
|
|
return
|
|
}
|
|
|
|
if err := m.sessionConn.AddMatchSignal(
|
|
dbus.WithMatchInterface("org.freedesktop.DBus"),
|
|
dbus.WithMatchMember("NameOwnerChanged"),
|
|
); err != nil {
|
|
log.Warnf("Failed to watch peer disconnects: %v", err)
|
|
return
|
|
}
|
|
|
|
signals := make(chan *dbus.Signal, 64)
|
|
m.sessionConn.Signal(signals)
|
|
|
|
for sig := range signals {
|
|
if sig.Name != "org.freedesktop.DBus.NameOwnerChanged" {
|
|
continue
|
|
}
|
|
if len(sig.Body) < 3 {
|
|
continue
|
|
}
|
|
|
|
name, ok1 := sig.Body[0].(string)
|
|
newOwner, ok2 := sig.Body[2].(string)
|
|
if !ok1 || !ok2 {
|
|
continue
|
|
}
|
|
if newOwner != "" {
|
|
continue
|
|
}
|
|
|
|
m.removeInhibitorsByPeer(name)
|
|
}
|
|
}
|
|
|
|
func (m *Manager) removeInhibitorsByPeer(peer string) {
|
|
m.stateMutex.Lock()
|
|
defer m.stateMutex.Unlock()
|
|
|
|
var remaining []ScreensaverInhibitor
|
|
var removed []ScreensaverInhibitor
|
|
for _, inh := range m.state.Screensaver.Inhibitors {
|
|
if inh.Peer == peer {
|
|
removed = append(removed, inh)
|
|
continue
|
|
}
|
|
remaining = append(remaining, inh)
|
|
}
|
|
|
|
if len(removed) == 0 {
|
|
return
|
|
}
|
|
|
|
for _, inh := range removed {
|
|
log.Infof("Screensaver: peer %s died, removing inhibitor from %s (cookie %08X)", peer, inh.AppName, inh.Cookie)
|
|
}
|
|
|
|
m.state.Screensaver.Inhibitors = remaining
|
|
m.state.Screensaver.Inhibited = len(remaining) > 0
|
|
|
|
go m.NotifyScreensaverSubscribers()
|
|
}
|
|
|
|
func (m *Manager) GetScreensaverState() ScreensaverState {
|
|
m.stateMutex.RLock()
|
|
defer m.stateMutex.RUnlock()
|
|
return m.state.Screensaver
|
|
}
|
|
|
|
func (m *Manager) SubscribeScreensaver(id string) chan ScreensaverState {
|
|
ch := make(chan ScreensaverState, 64)
|
|
m.screensaverSubscribers.Store(id, ch)
|
|
return ch
|
|
}
|
|
|
|
func (m *Manager) UnsubscribeScreensaver(id string) {
|
|
if val, ok := m.screensaverSubscribers.LoadAndDelete(id); ok {
|
|
close(val)
|
|
}
|
|
}
|
|
|
|
func (m *Manager) NotifyScreensaverSubscribers() {
|
|
state := m.GetScreensaverState()
|
|
m.screensaverSubscribers.Range(func(key string, ch chan ScreensaverState) bool {
|
|
select {
|
|
case ch <- state:
|
|
default:
|
|
}
|
|
return true
|
|
})
|
|
}
|