Files
navi/cmd/vpnapp/platform_windows.go
T
M4andCursor 5d7c96f7fc
ci / test (macos-latest) (push) Waiting to run
ci / test (ubuntu-latest) (push) Waiting to run
ci / test (windows-latest) (push) Waiting to run
Release 3.8.2.6: perceived-speed UX — search, tray hide, core warm, SSE.
Server search/hotkeys; Windows close-to-tray; background EnsureCore; SSE status with rare poll fallback.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-01 16:32:19 +03:00

172 lines
3.8 KiB
Go

//go:build windows
package main
import (
"fmt"
"log"
"os"
"path/filepath"
"sync/atomic"
"unsafe"
"github.com/crgimenes/glaze"
"golang.org/x/sys/windows"
)
var (
user32 = windows.NewLazySystemDLL("user32.dll")
procGetWindowLongPtr = user32.NewProc("GetWindowLongPtrW")
procSetWindowLongPtr = user32.NewProc("SetWindowLongPtrW")
procCallWindowProc = user32.NewProc("CallWindowProcW")
procShowWindow = user32.NewProc("ShowWindow")
procSetForeground = user32.NewProc("SetForegroundWindow")
procPostMessage = user32.NewProc("PostMessageW")
procIsIconic = user32.NewProc("IsIconic")
mainHWND uintptr
origWndProc uintptr
closeHookCB uintptr
allowDestroy atomic.Bool
)
const (
// GWLP_WNDPROC as unsigned pointer-sized -4.
gwlpWndProc = ^uintptr(3)
wmClose = 0x0010
swHide = 0
swShow = 5
swRestore = 9
)
func openExternal(raw string) error {
verb, err := windows.UTF16PtrFromString("open")
if err != nil {
return err
}
file, err := windows.UTF16PtrFromString(raw)
if err != nil {
return err
}
return windows.ShellExecute(0, verb, file, nil, nil, windows.SW_SHOWNORMAL)
}
func fatalf(format string, args ...any) {
msg := fmt.Sprintf(format, args...)
log.Println(msg)
messageBox(msg)
os.Exit(1)
}
func shutdownSignals() []os.Signal {
return []os.Signal{os.Interrupt}
}
func decorateWindow(w glaze.WebView) {
if w == nil {
return
}
hwnd := w.Window()
applyWindowIcon(hwnd)
installCloseToHide(hwnd)
}
func installCloseToHide(hwnd unsafe.Pointer) {
if hwnd == nil {
return
}
mainHWND = uintptr(hwnd)
allowDestroy.Store(false)
if closeHookCB == 0 {
closeHookCB = windows.NewCallback(closeToHideWndProc)
}
orig, _, _ := procGetWindowLongPtr.Call(mainHWND, gwlpWndProc)
if orig == 0 {
return
}
origWndProc = orig
procSetWindowLongPtr.Call(mainHWND, gwlpWndProc, closeHookCB)
}
func closeToHideWndProc(hwnd uintptr, msg uint32, wParam, lParam uintptr) uintptr {
if msg == wmClose && !allowDestroy.Load() {
procShowWindow.Call(hwnd, uintptr(swHide))
return 0
}
r, _, _ := procCallWindowProc.Call(origWndProc, hwnd, uintptr(msg), wParam, lParam)
return r
}
func showMainWindow() {
if mainHWND == 0 {
return
}
iconic, _, _ := procIsIconic.Call(mainHWND)
if iconic != 0 {
procShowWindow.Call(mainHWND, uintptr(swRestore))
} else {
procShowWindow.Call(mainHWND, uintptr(swShow))
}
procSetForeground.Call(mainHWND)
}
func prepareWindowQuit() {
allowDestroy.Store(true)
}
func closeMainWindow() {
prepareWindowQuit()
if mainHWND == 0 {
os.Exit(0)
return
}
procPostMessage.Call(mainHWND, uintptr(wmClose), 0, 0)
}
func applyWindowIcon(hwnd unsafe.Pointer) {
ico := findIconPath()
if ico == "" || hwnd == nil {
return
}
pathPtr, err := windows.UTF16PtrFromString(ico)
if err != nil {
return
}
loadImage := user32.NewProc("LoadImageW")
sendMessage := user32.NewProc("SendMessageW")
const (
imageIcon = 1
lrLoadFromFile = 0x0010
lrDefaultSize = 0x0040
wmSetIcon = 0x0080
iconSmall = 0
iconBig = 1
)
h, _, _ := loadImage.Call(0, uintptr(unsafe.Pointer(pathPtr)), imageIcon, 0, 0, lrLoadFromFile|lrDefaultSize)
if h == 0 {
return
}
sendMessage.Call(uintptr(hwnd), wmSetIcon, iconSmall, h)
sendMessage.Call(uintptr(hwnd), wmSetIcon, iconBig, h)
}
func findIconPath() string {
candidates := []string{}
if exe, err := os.Executable(); err == nil {
dir := filepath.Dir(exe)
candidates = append(candidates,
filepath.Join(dir, "assets", "navis.ico"),
filepath.Join(dir, "navis.ico"),
)
}
if cwd, err := os.Getwd(); err == nil {
candidates = append(candidates, filepath.Join(cwd, "assets", "navis.ico"))
}
for _, c := range candidates {
if st, err := os.Stat(c); err == nil && !st.IsDir() {
return c
}
}
return ""
}