package main import ( "bytes" "fmt" "net/http" "os" "path/filepath" "strconv" "strings" "time" ) func lockFilePath() string { return filepath.Join(appDir(), ".hpltoolbox.lock") } // focusExistingInstance returns true if another instance was found and // successfully asked to focus its window. In that case the caller should exit. // Returns false if no live instance exists (and clears any stale lock). func focusExistingInstance() bool { data, err := os.ReadFile(lockFilePath()) if err != nil { return false } parts := strings.Split(strings.TrimSpace(string(data)), "\n") if len(parts) != 2 { _ = os.Remove(lockFilePath()) return false } pid, errPid := strconv.Atoi(strings.TrimSpace(parts[0])) port, errPort := strconv.Atoi(strings.TrimSpace(parts[1])) if errPid != nil || errPort != nil { _ = os.Remove(lockFilePath()) return false } // Quick liveness check: PID must exist. if proc, err := os.FindProcess(pid); err != nil || proc == nil { _ = os.Remove(lockFilePath()) return false } // Ask the running instance to focus its window. client := &http.Client{Timeout: 1500 * time.Millisecond} url := fmt.Sprintf("http://127.0.0.1:%d/api/focus", port) resp, err := client.Post(url, "application/json", bytes.NewReader(nil)) if err != nil { // Lock file is stale (process died but didn't clean up). _ = os.Remove(lockFilePath()) return false } _ = resp.Body.Close() if resp.StatusCode != http.StatusOK { _ = os.Remove(lockFilePath()) return false } return true } func writeLockFile(port int) error { content := fmt.Sprintf("%d\n%d\n", os.Getpid(), port) return os.WriteFile(lockFilePath(), []byte(content), 0644) } func removeLockFile() { _ = os.Remove(lockFilePath()) }