From fe1fd92953d5a27d38be4961c81f0a49676ecd94 Mon Sep 17 00:00:00 2001 From: Dimariqe Date: Wed, 15 Apr 2026 19:52:06 +0700 Subject: [PATCH] fix: gate startup tray scan on prior suspend history (#2225) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The unconditional startup scan introduced duplicate tray icons on normal boot because apps were still registering their own SNI items when the scan ran. Use CLOCK_BOOTTIME − CLOCK_MONOTONIC to detect whether the system has ever been suspended. The startup scan now only runs when the difference exceeds 5 s, meaning at least one suspend/resume cycle has occurred. On a fresh boot the difference is ≈ 0 and the scan is skipped entirely. --- core/internal/server/trayrecovery/manager.go | 32 +++++++++++++++++--- 1 file changed, 27 insertions(+), 5 deletions(-) diff --git a/core/internal/server/trayrecovery/manager.go b/core/internal/server/trayrecovery/manager.go index 3610f1b8..6053609d 100644 --- a/core/internal/server/trayrecovery/manager.go +++ b/core/internal/server/trayrecovery/manager.go @@ -8,6 +8,7 @@ import ( "github.com/AvengeMedia/DankMaterialShell/core/internal/log" "github.com/AvengeMedia/DankMaterialShell/core/internal/server/loginctl" "github.com/godbus/dbus/v5" + "golang.org/x/sys/unix" ) const resumeDelay = 3 * time.Second @@ -29,11 +30,14 @@ func NewManager() (*Manager, error) { 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() + // Only run a startup scan when the system has been suspended at least once. + // On a fresh boot CLOCK_BOOTTIME ≈ CLOCK_MONOTONIC (difference ~0). + // After any suspend/resume cycle the difference grows by the time spent + // sleeping. This avoids duplicate registrations on normal boot where apps + // are still starting up and will register their own tray icons shortly. + if timeSuspended() > 5*time.Second { + go m.scheduleRecovery() + } return m, nil } @@ -91,3 +95,21 @@ func (m *Manager) Close() { } log.Info("TrayRecovery manager closed") } + +// timeSuspended returns how long the system has spent in suspend since boot. +// It is the difference between CLOCK_BOOTTIME (includes suspend) and +// CLOCK_MONOTONIC (excludes suspend). +func timeSuspended() time.Duration { + var bt, mt unix.Timespec + if err := unix.ClockGettime(unix.CLOCK_BOOTTIME, &bt); err != nil { + return 0 + } + if err := unix.ClockGettime(unix.CLOCK_MONOTONIC, &mt); err != nil { + return 0 + } + diff := (bt.Sec-mt.Sec)*int64(time.Second) + (bt.Nsec - mt.Nsec) + if diff < 0 { + return 0 + } + return time.Duration(diff) +}