@@ -3,6 +3,10 @@ package dbus
33import (
44 "encoding/json"
55 "fmt"
6+ "log/slog"
7+ "os"
8+ "path/filepath"
9+ "strings"
610
711 "github.com/godbus/dbus/v5"
812 "github.com/godbus/dbus/v5/introspect"
@@ -61,7 +65,71 @@ func (r *Receiver) Close() {
6165 r .conn .Close ()
6266}
6367
68+ // getDBusAddress attempts to get the latest DBus address
69+ func getDBusAddress () (string , error ) {
70+ // 1. First try to get from environment variable
71+ if addr := os .Getenv ("DBUS_SESSION_BUS_ADDRESS" ); addr != "" {
72+ return addr , nil
73+ }
74+
75+ // 2. Try to get from systemd user session
76+ uid := os .Getuid ()
77+ systemdSocket := fmt .Sprintf ("/run/user/%d/bus" , uid )
78+ if _ , err := os .Stat (systemdSocket ); err == nil {
79+ return fmt .Sprintf ("unix:path=%s" , systemdSocket ), nil
80+ }
81+
82+ // 3. Try to get from session file
83+ home , err := os .UserHomeDir ()
84+ if err != nil {
85+ return "" , fmt .Errorf ("failed to get home directory: %w" , err )
86+ }
87+
88+ // Get DISPLAY environment variable, use ":0" as default if not set
89+ display := os .Getenv ("DISPLAY" )
90+ if display == "" {
91+ display = ":0"
92+ }
93+
94+ // Try to get from session file
95+ sessionFile := filepath .Join (home , ".dbus" , "session-bus" , display )
96+ if _ , err := os .Stat (sessionFile ); err == nil {
97+ content , err := os .ReadFile (sessionFile )
98+ if err != nil {
99+ return "" , fmt .Errorf ("failed to read session file: %w" , err )
100+ }
101+
102+ lines := strings .Split (string (content ), "\n " )
103+ for _ , line := range lines {
104+ if strings .HasPrefix (line , "DBUS_SESSION_BUS_ADDRESS=" ) {
105+ addr := strings .TrimPrefix (line , "DBUS_SESSION_BUS_ADDRESS=" )
106+ addr = strings .Trim (addr , "'\" " )
107+ return addr , nil
108+ }
109+ }
110+ }
111+
112+ // 4. Try to get from XDG_RUNTIME_DIR
113+ if runtimeDir := os .Getenv ("XDG_RUNTIME_DIR" ); runtimeDir != "" {
114+ busPath := filepath .Join (runtimeDir , "bus" )
115+ if _ , err := os .Stat (busPath ); err == nil {
116+ return fmt .Sprintf ("unix:path=%s" , busPath ), nil
117+ }
118+ }
119+
120+ return "" , fmt .Errorf ("failed to find DBus address" )
121+ }
122+
64123func (r * Receiver ) setupDBus () error {
124+ // Get the latest DBus address
125+ addr , err := getDBusAddress ()
126+ if err != nil {
127+ return fmt .Errorf ("failed to get DBus address: %w" , err )
128+ }
129+
130+ // Set environment variable
131+ os .Setenv ("DBUS_SESSION_BUS_ADDRESS" , addr )
132+
65133 conn , err := dbus .ConnectSessionBus ()
66134 if err != nil {
67135 return err
@@ -109,7 +177,35 @@ func WithActiveWindowChangeCallback(callback wininfo.ActiveWindowChangeCallback)
109177 }
110178}
111179
112- func New (opt ... Option ) (* Receiver , error ) {
180+ // DegradedReceiver is a degraded mode implementation, used when DBus is unavailable
181+ type DegradedReceiver struct {
182+ * options
183+ current * wininfo.WinInfo
184+ }
185+
186+ func (r * DegradedReceiver ) UpdateActiveWindow (in string ) * dbus.Error {
187+ return nil
188+ }
189+
190+ func (r * DegradedReceiver ) GetActiveWindow () (* wininfo.WinInfo , error ) {
191+ if r .current == nil {
192+ return nil , fmt .Errorf ("no active window" )
193+ }
194+ return r .current , nil
195+ }
196+
197+ func (r * DegradedReceiver ) Close () {
198+ // Perform cleanup for DegradedReceiver
199+ r .options = nil // Reset options to prevent further use
200+ slog .Info ("DegradedReceiver closed" )
201+ }
202+
203+ func (r * DegradedReceiver ) OnActiveWindowChange (callback wininfo.ActiveWindowChangeCallback ) error {
204+ r .options .onChange = callback
205+ return nil
206+ }
207+
208+ func New (opt ... Option ) (wininfo.WinGetter , error ) {
113209 r := & Receiver {
114210 options : & options {},
115211 }
@@ -120,7 +216,11 @@ func New(opt ...Option) (*Receiver, error) {
120216
121217 err := r .setupDBus ()
122218 if err != nil {
123- return nil , err
219+ // If DBus connection fails, return degraded mode implementation
220+ slog .Warn ("Failed to connect to DBus, running in degraded mode" , "error" , err )
221+ return & DegradedReceiver {
222+ options : r .options ,
223+ }, nil
124224 }
125225
126226 return r , nil
0 commit comments