diff --git a/app.go b/app.go index e33cf9f..24ef537 100644 --- a/app.go +++ b/app.go @@ -74,6 +74,14 @@ func (a *App) startup(ctx context.Context) { // Initialize components a.startupHeadless() + // When the user launches the app again while it is already running (tray-only), + // the second process signals us here so the settings window becomes visible. + go func() { + for range secondInstanceWake { + wailsruntime.WindowShow(ctx) + } + }() + // Start system tray in a goroutine go tray.Start(a) } diff --git a/internal/logger/logger.go b/internal/logger/logger.go index 160b106..f5905ff 100644 --- a/internal/logger/logger.go +++ b/internal/logger/logger.go @@ -134,7 +134,5 @@ func getLogPath() (string, error) { return "", err } - return filepath.Join(homeDir, ".wis-free-v3", "logs"), nil + return filepath.Join(homeDir, ".wis-free-v3", "logs", time.Now().Format("2006-01-02")+".log"), nil } - - diff --git a/main.go b/main.go index 39a1cd7..ff25859 100644 --- a/main.go +++ b/main.go @@ -48,8 +48,13 @@ func main() { // Ensure only one instance of the application is running if !acquireInstanceLock() { + // Another copy is already running (often left in the tray). Without this, + // we would exit silently and the user sees "nothing happens" when clicking + // the launcher again. + tryNotifyRunningInstanceToShow() os.Exit(0) } + runSecondInstanceListener() // Initialize the application app := NewApp() @@ -64,9 +69,7 @@ func main() { OnStartup: app.startup, OnShutdown: app.Shutdown, OnBeforeClose: app.beforeClose, - // On Linux, starting hidden makes it look like "nothing happens" if the tray icon - // fails to initialize (missing libayatana-appindicator, etc) or is hidden by the DE. - StartHidden: runtime.GOOS == "windows", + StartHidden: true, Bind: []interface{}{app}, }) @@ -147,6 +150,3 @@ func getLockPath() (string, error) { } return filepath.Join(homeDir, configDir, lockFile), nil } - - - diff --git a/main_instance_stub.go b/main_instance_stub.go new file mode 100644 index 0000000..a9cc509 --- /dev/null +++ b/main_instance_stub.go @@ -0,0 +1,10 @@ +//go:build windows + +package main + +// secondInstanceWake is unused on Windows (second-instance UX not wired here). +var secondInstanceWake = make(chan struct{}, 8) + +func tryNotifyRunningInstanceToShow() {} + +func runSecondInstanceListener() {} diff --git a/main_instance_unix.go b/main_instance_unix.go new file mode 100644 index 0000000..24278f2 --- /dev/null +++ b/main_instance_unix.go @@ -0,0 +1,65 @@ +//go:build unix && !windows + +package main + +import ( + "io" + "net" + "os" + "path/filepath" + "time" +) + +const instanceSockName = "instance.sock" + +// secondInstanceWake is closed when a second process asks the running app to show its window. +var secondInstanceWake = make(chan struct{}, 8) + +func instanceSocketPath() (string, error) { + home, err := os.UserHomeDir() + if err != nil { + return "", err + } + return filepath.Join(home, configDir, instanceSockName), nil +} + +func tryNotifyRunningInstanceToShow() { + path, err := instanceSocketPath() + if err != nil { + return + } + c, err := net.DialTimeout("unix", path, 500*time.Millisecond) + if err != nil { + return + } + defer c.Close() + _, _ = c.Write([]byte{1}) +} + +func runSecondInstanceListener() { + path, err := instanceSocketPath() + if err != nil { + return + } + _ = os.Remove(path) + l, err := net.Listen("unix", path) + if err != nil { + return + } + go func() { + for { + c, err := l.Accept() + if err != nil { + return + } + go func(conn net.Conn) { + defer conn.Close() + _, _ = io.Copy(io.Discard, conn) + select { + case secondInstanceWake <- struct{}{}: + default: + } + }(c) + } + }() +} diff --git a/scripts/build-linux.sh b/scripts/build-linux.sh index 3f23940..2d4b776 100755 --- a/scripts/build-linux.sh +++ b/scripts/build-linux.sh @@ -150,12 +150,15 @@ if [[ "$INSTALL" == "y" || "$INSTALL" == "Y" ]]; then sudo cp "build/appicon.png" "$ICON_PATH" fi + # Absolute Exec path: some desktop environments do not put /usr/local/bin on PATH + # for .desktop launches, so "Exec=wis-free-v3" can fail with no visible error. cat << EOF > /tmp/$APP_NAME.desktop [Desktop Entry] Type=Application Name=WIS Free V3 Comment=Voice Dictation App -Exec=$APP_NAME +Exec=/usr/local/bin/$APP_NAME +TryExec=/usr/local/bin/$APP_NAME Icon=$APP_NAME Terminal=false Categories=Utility;Audio;