diff --git a/build/bin/wis-free-v3.exe b/build/bin/wis-free-v3.exe deleted file mode 100644 index 167f6a8..0000000 Binary files a/build/bin/wis-free-v3.exe and /dev/null differ diff --git a/internal/ui/tray/tray.go b/internal/ui/tray/tray.go index 55bb8a6..f96f1c3 100644 --- a/internal/ui/tray/tray.go +++ b/internal/ui/tray/tray.go @@ -5,6 +5,7 @@ package tray import ( _ "embed" "os" + "runtime" "strings" "wis-free-v3/internal/config" @@ -40,6 +41,12 @@ var statusMenuItem *systray.MenuItem // Start initializes and runs the system tray. // This function blocks until the tray is terminated. func Start(app App) { + // Pin this goroutine to one OS thread for the whole lifetime of systray.Run. + // getlantern/systray creates the notify icon and runs the Win32 message pump on + // whichever thread calls Run; if the goroutine migrates between threads, callbacks + // and the tray context menu break until restart (often seen after sleep/resume). + runtime.LockOSThread() + defer runtime.UnlockOSThread() systray.Run( func() { onReady(app) }, onExit, diff --git a/main.go b/main.go index 4423d9d..a25dabd 100644 --- a/main.go +++ b/main.go @@ -6,6 +6,7 @@ import ( "embed" "os" "path/filepath" + "runtime" "strconv" "wis-free-v3/internal/logger" @@ -40,6 +41,11 @@ var instanceLock *os.File var AppVersion = "dev" func main() { + // systray's package init calls runtime.LockOSThread() on the program's startup + // thread. Undo that so the main goroutine is not permanently bound; the tray + // goroutine locks itself in tray.Start instead (see internal/ui/tray/tray.go). + runtime.UnlockOSThread() + // Ensure only one instance of the application is running if !acquireInstanceLock() { os.Exit(0)