1
0
mirror of https://github.com/AvengeMedia/DankMaterialShell.git synced 2026-04-09 15:22:13 -04:00
Files
DankMaterialShell/core/internal/server/trayrecovery/manager.go
Dimariqe 903ab1e61d fix: add TrayRecoveryService with bidirectional SNI dedup (Go server) (#2137)
* fix: add TrayRecoveryService with bidirectional SNI dedup (Go server)

Add TrayRecoveryService manager that re-registers lost tray icons after
resume from suspend via native DBus scanning in the Go server.

The service resolves every registered SNI item (both well-known names and
:1.xxx connection IDs) to a canonical connection ID, building a unified
registeredConnIDs set before either scan section runs. This prevents
duplicates in both directions:

- If an app registered via well-known name, the connection-ID section
  skips its :1.xxx entry.
- If an app registered via connection ID, the well-known-name section
  skips its well-known name (checked through registeredConnIDs).
- After successfully registering via well-known name, registeredConnIDs
  is updated immediately so the connection-ID section won't probe the
  same app in the same run.

A startup scan (3 s delay) covers the common case where the DMS process
is killed during suspend and restarted by systemd (Type=dbus), so the
loginctl PrepareForSleep watcher alone is not sufficient. The startup
scan is harmless on a normal boot — it finds all items already registered
and exits early.

Go port of quickshell commit 1470aa3.

* fix: 'interface{}' can be replaced by 'any'

* TrayRecoveryService: Remove objPath parameter from registerSNI
2026-04-06 09:03:36 -04:00

94 lines
1.9 KiB
Go

package trayrecovery
import (
"fmt"
"sync"
"time"
"github.com/AvengeMedia/DankMaterialShell/core/internal/log"
"github.com/AvengeMedia/DankMaterialShell/core/internal/server/loginctl"
"github.com/godbus/dbus/v5"
)
const resumeDelay = 3 * time.Second
type Manager struct {
conn *dbus.Conn
stopChan chan struct{}
wg sync.WaitGroup
}
func NewManager() (*Manager, error) {
conn, err := dbus.ConnectSessionBus()
if err != nil {
return nil, fmt.Errorf("failed to connect to session bus: %w", err)
}
m := &Manager{
conn: conn,
stopChan: make(chan struct{}),
}
// Run a startup scan after a delay — covers the case where the process
// was killed during suspend and restarted by systemd (Type=dbus).
// The fresh process never sees the PrepareForSleep true→false transition,
// so the loginctl watcher alone is not enough.
go m.scheduleRecovery()
return m, nil
}
// WatchLoginctl subscribes to loginctl session state changes and triggers
// tray recovery after resume from suspend (PrepareForSleep false transition).
// This handles the case where the process survives suspend.
func (m *Manager) WatchLoginctl(lm *loginctl.Manager) {
ch := lm.Subscribe("tray-recovery")
m.wg.Add(1)
go func() {
defer m.wg.Done()
defer lm.Unsubscribe("tray-recovery")
wasSleeping := false
for {
select {
case <-m.stopChan:
return
case state, ok := <-ch:
if !ok {
return
}
if state.PreparingForSleep {
wasSleeping = true
continue
}
if wasSleeping {
wasSleeping = false
go m.scheduleRecovery()
}
}
}
}()
}
func (m *Manager) scheduleRecovery() {
select {
case <-time.After(resumeDelay):
m.recoverTrayItems()
case <-m.stopChan:
}
}
func (m *Manager) Close() {
select {
case <-m.stopChan:
return
default:
close(m.stopChan)
}
m.wg.Wait()
if m.conn != nil {
m.conn.Close()
}
log.Info("TrayRecovery manager closed")
}