Release 3.8.2.11: idle rev-cache, port-safe Connect, single-instance tray UX.
Faster unchanged polls; orphan-core cleanup on listen ports; clearer busy-port errors; cores only from binDir; incremental server list; CSP without Google Fonts; single-instance activates existing window; close-to-tray without kill-others or balloon. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,6 @@
|
||||
package singleinstance
|
||||
|
||||
import "errors"
|
||||
|
||||
// ErrAlreadyRunning means another Navis GUI instance holds the lock.
|
||||
var ErrAlreadyRunning = errors.New("Navis уже запущен")
|
||||
@@ -0,0 +1,8 @@
|
||||
//go:build plan9
|
||||
|
||||
package singleinstance
|
||||
|
||||
import "os"
|
||||
|
||||
func flockExclusive(f *os.File) error { return nil }
|
||||
func funlock(f *os.File) error { return nil }
|
||||
@@ -0,0 +1,16 @@
|
||||
//go:build !windows && !plan9
|
||||
|
||||
package singleinstance
|
||||
|
||||
import (
|
||||
"os"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
func flockExclusive(f *os.File) error {
|
||||
return syscall.Flock(int(f.Fd()), syscall.LOCK_EX|syscall.LOCK_NB)
|
||||
}
|
||||
|
||||
func funlock(f *os.File) error {
|
||||
return syscall.Flock(int(f.Fd()), syscall.LOCK_UN)
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
//go:build !windows
|
||||
|
||||
package singleinstance
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
// Lock uses an exclusive lock file under the user config dir when available.
|
||||
func Lock(appName string) (func(), error) {
|
||||
if appName == "" {
|
||||
appName = "Navis"
|
||||
}
|
||||
dir, err := os.UserConfigDir()
|
||||
if err != nil {
|
||||
return func() {}, nil
|
||||
}
|
||||
path := filepath.Join(dir, appName, "singleinstance.lock")
|
||||
if err := os.MkdirAll(filepath.Dir(path), 0o755); err != nil {
|
||||
return func() {}, nil
|
||||
}
|
||||
f, err := os.OpenFile(path, os.O_CREATE|os.O_RDWR, 0o600)
|
||||
if err != nil {
|
||||
return func() {}, nil
|
||||
}
|
||||
if err := flockExclusive(f); err != nil {
|
||||
_ = f.Close()
|
||||
return nil, fmt.Errorf("%w", ErrAlreadyRunning)
|
||||
}
|
||||
return func() {
|
||||
_ = funlock(f)
|
||||
_ = f.Close()
|
||||
}, nil
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
//go:build windows
|
||||
|
||||
package singleinstance
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
var (
|
||||
user32 = windows.NewLazySystemDLL("user32.dll")
|
||||
procFindWindow = user32.NewProc("FindWindowW")
|
||||
procShowWindow = user32.NewProc("ShowWindow")
|
||||
procSetForeground = user32.NewProc("SetForegroundWindow")
|
||||
procIsIconic = user32.NewProc("IsIconic")
|
||||
)
|
||||
|
||||
const (
|
||||
swShow = 5
|
||||
swRestore = 9
|
||||
)
|
||||
|
||||
// Lock acquires a per-user mutex. If another Navis holds it, activates that
|
||||
// window (title "Navis") and returns ErrAlreadyRunning.
|
||||
func Lock(appName string) (func(), error) {
|
||||
if appName == "" {
|
||||
appName = "Navis"
|
||||
}
|
||||
name, err := windows.UTF16PtrFromString("Local\\" + appName + "SingleInstance")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
handle, err := windows.CreateMutex(nil, false, name)
|
||||
if err == windows.ERROR_ALREADY_EXISTS {
|
||||
if handle != 0 {
|
||||
_ = windows.CloseHandle(handle)
|
||||
}
|
||||
activateWindow(appName)
|
||||
return nil, ErrAlreadyRunning
|
||||
}
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("singleinstance: %w", err)
|
||||
}
|
||||
return func() {
|
||||
_ = windows.ReleaseMutex(handle)
|
||||
_ = windows.CloseHandle(handle)
|
||||
}, nil
|
||||
}
|
||||
|
||||
func activateWindow(title string) {
|
||||
t, err := windows.UTF16PtrFromString(title)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
hwnd, _, _ := procFindWindow.Call(0, uintptr(unsafe.Pointer(t)))
|
||||
if hwnd == 0 {
|
||||
return
|
||||
}
|
||||
iconic, _, _ := procIsIconic.Call(hwnd)
|
||||
if iconic != 0 {
|
||||
procShowWindow.Call(hwnd, swRestore)
|
||||
} else {
|
||||
procShowWindow.Call(hwnd, swShow)
|
||||
}
|
||||
procSetForeground.Call(hwnd)
|
||||
}
|
||||
Reference in New Issue
Block a user