summaryrefslogtreecommitdiff
path: root/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/trayrecovery
diff options
context:
space:
mode:
authorNippy <nippy@rp1.hu>2026-05-09 13:33:09 +0200
committerNippy <nippy@rp1.hu>2026-05-09 13:33:09 +0200
commita2b924d7e9492b978ad6dc33b6c1ffcb304b4bc5 (patch)
tree1a8769217f84bfe9d6216fafaadaf8cdd876d457 /raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/trayrecovery
parent34b9c34f982b2596cfa9e368a2d7f74e9a17477c (diff)
downloadRaveOS-PKGBUILD-a2b924d7e9492b978ad6dc33b6c1ffcb304b4bc5.tar.gz
RaveOS-PKGBUILD-a2b924d7e9492b978ad6dc33b6c1ffcb304b4bc5.zip
raveos update
Diffstat (limited to 'raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/trayrecovery')
-rw-r--r--raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/trayrecovery/manager.go93
-rw-r--r--raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/trayrecovery/recovery.go262
2 files changed, 0 insertions, 355 deletions
diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/trayrecovery/manager.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/trayrecovery/manager.go
deleted file mode 100644
index 3610f1b..0000000
--- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/trayrecovery/manager.go
+++ /dev/null
@@ -1,93 +0,0 @@
-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")
-}
diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/trayrecovery/recovery.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/trayrecovery/recovery.go
deleted file mode 100644
index c7b98fb..0000000
--- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/trayrecovery/recovery.go
+++ /dev/null
@@ -1,262 +0,0 @@
-package trayrecovery
-
-import (
- "context"
- "strings"
- "sync"
- "time"
-
- "github.com/AvengeMedia/DankMaterialShell/core/internal/log"
- "github.com/godbus/dbus/v5"
-)
-
-const (
- sniWatcherDest = "org.kde.StatusNotifierWatcher"
- sniWatcherPath = "/StatusNotifierWatcher"
- sniWatcherIface = "org.kde.StatusNotifierWatcher"
- sniItemIface = "org.kde.StatusNotifierItem"
- dbusIface = "org.freedesktop.DBus"
- propsIface = "org.freedesktop.DBus.Properties"
- probeTimeout = 300 * time.Millisecond
- connProbeTimeout = 150 * time.Millisecond
- batchSize = 30
-)
-
-var excludedPrefixes = []string{
- "org.freedesktop.",
- "org.gnome.",
- "org.kde.StatusNotifier",
- "com.canonical.AppMenu",
- "org.mpris.",
- "org.pipewire.",
- "org.pulseaudio",
- "fi.epitaph",
- "quickshell",
- "org.kde.quickshell",
-}
-
-func (m *Manager) recoverTrayItems() {
- registeredItems := m.getRegisteredItems()
- allNames := m.getDBusNames()
- if allNames == nil {
- return
- }
-
- registeredConnIDs := m.buildRegisteredConnIDs(registeredItems)
-
- count := len(registeredItems)
- log.Infof("TrayRecoveryService: scanning DBus for unregistered SNI items (%d already registered)...", count)
-
- m.scanWellKnownNames(allNames, registeredItems, registeredConnIDs)
- m.scanConnectionIDs(allNames, registeredItems, registeredConnIDs)
-}
-
-func (m *Manager) getRegisteredItems() []string {
- obj := m.conn.Object(sniWatcherDest, sniWatcherPath)
- variant, err := obj.GetProperty(sniWatcherIface + ".RegisteredStatusNotifierItems")
- if err != nil {
- log.Warnf("TrayRecoveryService: failed to get registered items: %v", err)
- return nil
- }
-
- switch v := variant.Value().(type) {
- case []string:
- return v
- case []any:
- items := make([]string, 0, len(v))
- for _, elem := range v {
- if s, ok := elem.(string); ok {
- items = append(items, s)
- }
- }
- return items
- }
- return nil
-}
-
-func (m *Manager) getDBusNames() []string {
- var names []string
- err := m.conn.BusObject().Call(dbusIface+".ListNames", 0).Store(&names)
- if err != nil {
- log.Warnf("TrayRecoveryService: failed to list bus names: %v", err)
- return nil
- }
- return names
-}
-
-func (m *Manager) getNameOwner(name string) string {
- var owner string
- err := m.conn.BusObject().Call(dbusIface+".GetNameOwner", 0, name).Store(&owner)
- if err != nil {
- return ""
- }
- return owner
-}
-
-// buildRegisteredConnIDs resolves every registered SNI item (well-known name
-// or :1.xxx connection ID) to a canonical connection ID. This prevents
-// duplicates in both directions.
-func (m *Manager) buildRegisteredConnIDs(registeredItems []string) map[string]bool {
- connIDs := make(map[string]bool, len(registeredItems))
- for _, item := range registeredItems {
- name := extractName(item)
- if strings.HasPrefix(name, ":1.") {
- connIDs[name] = true
- } else {
- owner := m.getNameOwner(name)
- if owner != "" {
- connIDs[owner] = true
- }
- }
- }
- return connIDs
-}
-
-// scanWellKnownNames probes well-known names (e.g. DinoX, nm-applet) for
-// unregistered SNI items and re-registers them.
-func (m *Manager) scanWellKnownNames(allNames []string, registeredItems []string, registeredConnIDs map[string]bool) {
- registeredRaw := strings.Join(registeredItems, "\n")
-
- for _, name := range allNames {
- if strings.HasPrefix(name, ":") {
- continue
- }
-
- if strings.Contains(registeredRaw, name) {
- continue
- }
-
- // Skip if this name's connection ID is already in the registered set
- // (handles the case where the app registered via connection ID instead)
- connForName := m.getNameOwner(name)
- if connForName != "" && registeredConnIDs[connForName] {
- continue
- }
-
- if isExcludedName(name) {
- continue
- }
-
- short := shortName(name)
- objectPaths := []string{
- "/StatusNotifierItem",
- "/org/ayatana/NotificationItem/" + short,
- }
-
- for _, objPath := range objectPaths {
- if m.probeSNI(name, objPath, probeTimeout) {
- m.registerSNI(name)
- // Update set so the connection-ID section won't double-register this app
- if connForName != "" {
- registeredConnIDs[connForName] = true
- }
- break
- }
- }
- }
-}
-
-// scanConnectionIDs probes all :1.xxx connections in parallel for unregistered
-// SNI items (e.g. Vesktop, Electron apps). Most non-SNI connections return an
-// error instantly, so this is fast.
-func (m *Manager) scanConnectionIDs(allNames []string, registeredItems []string, registeredConnIDs map[string]bool) {
- registeredRaw := strings.Join(registeredItems, "\n")
- registeredLower := strings.ToLower(registeredRaw)
-
- var wg sync.WaitGroup
- sem := make(chan struct{}, batchSize)
-
- for _, name := range allNames {
- if !strings.HasPrefix(name, ":1.") {
- continue
- }
- if registeredConnIDs[name] {
- continue
- }
-
- sem <- struct{}{}
- wg.Add(1)
- go func(conn string) {
- defer wg.Done()
- defer func() { <-sem }()
-
- sniID := m.getSNIId(conn, connProbeTimeout)
- if sniID == "" {
- return
- }
-
- // Skip if an item with the same Id is already registered (case-insensitive)
- if strings.Contains(registeredLower, strings.ToLower(sniID)) {
- return
- }
-
- m.registerSNI(conn)
- log.Infof("TrayRecovery: re-registered %s (Id: %s)", conn, sniID)
- }(name)
- }
- wg.Wait()
-}
-
-func (m *Manager) probeSNI(dest, path string, timeout time.Duration) bool {
- ctx, cancel := context.WithTimeout(context.Background(), timeout)
- defer cancel()
-
- obj := m.conn.Object(dest, dbus.ObjectPath(path))
- var props map[string]dbus.Variant
- err := obj.CallWithContext(ctx, propsIface+".GetAll", 0, sniItemIface).Store(&props)
- if err != nil {
- return false
- }
-
- _, hasID := props["Id"]
- return hasID
-}
-
-func (m *Manager) getSNIId(dest string, timeout time.Duration) string {
- ctx, cancel := context.WithTimeout(context.Background(), timeout)
- defer cancel()
-
- obj := m.conn.Object(dest, "/StatusNotifierItem")
- var variant dbus.Variant
- err := obj.CallWithContext(ctx, propsIface+".Get", 0, sniItemIface, "Id").Store(&variant)
- if err != nil {
- return ""
- }
-
- id, _ := variant.Value().(string)
- return id
-}
-
-func (m *Manager) registerSNI(name string) {
- obj := m.conn.Object(sniWatcherDest, sniWatcherPath)
- call := obj.Call(sniWatcherIface+".RegisterStatusNotifierItem", 0, name)
- if call.Err != nil {
- log.Warnf("TrayRecovery: failed to register %s: %v", name, call.Err)
- return
- }
- log.Infof("TrayRecovery: re-registered %s", name)
-}
-
-func extractName(item string) string {
- if idx := strings.IndexByte(item, '/'); idx != -1 {
- return item[:idx]
- }
- return item
-}
-
-func shortName(name string) string {
- parts := strings.Split(name, ".")
- if len(parts) > 0 {
- return parts[len(parts)-1]
- }
- return name
-}
-
-func isExcludedName(name string) bool {
- for _, prefix := range excludedPrefixes {
- if strings.HasPrefix(name, prefix) {
- return true
- }
- }
- return false
-}