Server search/hotkeys; Windows close-to-tray; background EnsureCore; SSE status with rare poll fallback. Co-authored-by: Cursor <cursoragent@cursor.com>
172 lines
3.8 KiB
Go
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 ""
|
|
}
|