diff --git a/cmd/theta-agent-tray/main.go b/cmd/theta-agent-tray/main.go index 151021c..7cb6661 100644 --- a/cmd/theta-agent-tray/main.go +++ b/cmd/theta-agent-tray/main.go @@ -26,6 +26,8 @@ import ( "log" "net" "os" + "path/filepath" + "runtime" "time" "fyne.io/systray" @@ -33,7 +35,16 @@ import ( // ── IPC types (duplicated from the main agent package; tray is its own binary) ── -const TraySocket = "/run/theta/tray.sock" +// traySocketPaths returns the daemon IPC socket paths for this platform, in +// the same order the daemon tries to bind them. Windows has no /run or /tmp, +// so it uses a Unix socket under the per-user temp dir; Linux keeps the +// original pair. +func traySocketPaths() []string { + if runtime.GOOS == "windows" { + return []string{filepath.Join(os.TempDir(), "theta-tray.sock")} + } + return []string{"/run/theta/tray.sock", "/tmp/theta-tray.sock"} +} type TrayColor string @@ -73,8 +84,10 @@ var ( // ── Main ───────────────────────────────────────────────────────────────────── func main() { - // Silent exit if no graphical session is available. - if os.Getenv("DISPLAY") == "" && os.Getenv("WAYLAND_DISPLAY") == "" { + // Silent exit if no graphical session is available. Only meaningful on + // X11/Wayland: Windows never sets these variables and the tray is simply + // always a valid thing to run there. + if runtime.GOOS != "windows" && os.Getenv("DISPLAY") == "" && os.Getenv("WAYLAND_DISPLAY") == "" { log.Println("theta-agent-tray: no graphical session detected (DISPLAY/WAYLAND_DISPLAY not set), exiting") os.Exit(0) } @@ -149,7 +162,7 @@ func onReady() { // between attempts. On connect it streams status updates until the connection // drops, then retries. func connectWithRetry() { - socketPaths := []string{"/run/theta/tray.sock", "/tmp/theta-tray.sock"} + socketPaths := traySocketPaths() for { var conn net.Conn var err error diff --git a/tray_ipc.go b/tray_ipc.go index 87ec469..bab09e0 100644 --- a/tray_ipc.go +++ b/tray_ipc.go @@ -7,15 +7,33 @@ package main // Protocol: newline-delimited JSON. The daemon streams TrayStatus messages to // any connected tray client. The tray sends TrayCommand messages to the daemon. -import "encoding/json" +import ( + "encoding/json" + "os" + "path/filepath" + "runtime" +) -// TraySocket is the path to the IPC socket the daemon listens on. -// Falls back to /tmp/theta-tray.sock if /run/theta/ is not writable. -var TraySocketPaths = []string{ - "/run/theta/tray.sock", - "/tmp/theta-tray.sock", -} -const TraySocket = "/tmp/theta-tray.sock" +// TraySocketPaths are the paths the daemon tries to bind, in order. +// +// Windows has no /run or /tmp; a Unix socket in the per-user temp dir works +// there (AF_UNIX is supported since Windows 10 1803) and needs no admin +// rights. Linux keeps the original /run/theta path with a /tmp fallback. +var TraySocketPaths = func() []string { + if runtime.GOOS == "windows" { + return []string{filepath.Join(os.TempDir(), "theta-tray.sock")} + } + return []string{"/run/theta/tray.sock", "/tmp/theta-tray.sock"} +}() + +// TraySocket is the canonical socket path the tray dials. It matches the last +// entry in TraySocketPaths on each platform. +var TraySocket = func() string { + if runtime.GOOS == "windows" { + return filepath.Join(os.TempDir(), "theta-tray.sock") + } + return "/tmp/theta-tray.sock" +}() // TrayColor represents the icon color state.