Files
M4andCursor 621c847cb3 Release 3.8.2.1: reliability, UI probe/logs, CI, signing gates.
Clear sysproxy on core death; probe+reconnect; subscription prune;
Best ignores soft UDP; Windows tray; Android protocol gate; CI workflows.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-30 02:44:48 +03:00

194 lines
4.9 KiB
Go

//go:build windows
package trayhost
import (
"runtime"
"unsafe"
"golang.org/x/sys/windows"
)
var (
shell32 = windows.NewLazySystemDLL("shell32.dll")
user32 = windows.NewLazySystemDLL("user32.dll")
procShellNotify = shell32.NewProc("Shell_NotifyIconW")
procLoadIcon = user32.NewProc("LoadIconW")
procCreatePopup = user32.NewProc("CreatePopupMenu")
procAppendMenu = user32.NewProc("AppendMenuW")
procTrackPopup = user32.NewProc("TrackPopupMenu")
procDestroyMenu = user32.NewProc("DestroyMenu")
procDefWindowProc = user32.NewProc("DefWindowProcW")
procRegisterClass = user32.NewProc("RegisterClassExW")
procCreateWindow = user32.NewProc("CreateWindowExW")
procGetMessage = user32.NewProc("GetMessageW")
procTranslate = user32.NewProc("TranslateMessage")
procDispatch = user32.NewProc("DispatchMessageW")
procPostQuit = user32.NewProc("PostQuitMessage")
procSetForeground = user32.NewProc("SetForegroundWindow")
procGetCursorPos = user32.NewProc("GetCursorPos")
)
const (
nimAdd = 0x00000000
nimModify = 0x00000001
nimDelete = 0x00000002
nifMessage = 0x00000001
nifIcon = 0x00000002
nifTip = 0x00000004
wmApp = 0x8000
wmTray = wmApp + 1
wmDestroy = 0x0002
wmCommand = 0x0111
wmRButtonUp = 0x0205
wmLButtonUp = 0x0202
mfString = 0x00000000
mfSeparator = 0x00000800
tpmRight = 0x0020
tpmBottom = 0x0020
idiApplication = 32512
idConnect = 1001
idDisconnect = 1002
idQuit = 1003
)
type notifyIconData struct {
CbSize uint32
Hwnd windows.Handle
UID uint32
UFlags uint32
UCallbackMessage uint32
HIcon windows.Handle
SzTip [128]uint16
}
type wndClassEx struct {
CbSize uint32
Style uint32
LpfnWndProc uintptr
CbClsExtra int32
CbWndExtra int32
HInstance windows.Handle
HIcon windows.Handle
HCursor windows.Handle
HbrBackground windows.Handle
LpszMenuName *uint16
LpszClassName *uint16
HIconSm windows.Handle
}
type point struct{ X, Y int32 }
type msg struct {
Hwnd windows.Handle
Message uint32
WParam uintptr
LParam uintptr
Time uint32
Pt point
}
func start(appName string, h Hooks) bool {
if h.Quit == nil {
return false
}
go func() {
runtime.LockOSThread()
defer runtime.UnlockOSThread()
runTray(appName, h)
}()
return true
}
func runTray(appName string, h Hooks) {
className, _ := windows.UTF16PtrFromString("NavisTrayClass")
title, _ := windows.UTF16PtrFromString(appName)
var wndProc = windows.NewCallback(func(hwnd windows.Handle, msgU uint32, wParam, lParam uintptr) uintptr {
switch msgU {
case wmTray:
if lParam == wmRButtonUp || lParam == wmLButtonUp {
showMenu(hwnd, h)
}
return 0
case wmCommand:
switch int(wParam & 0xffff) {
case idConnect:
if h.Connect != nil {
go h.Connect()
}
case idDisconnect:
if h.Disconnect != nil {
go h.Disconnect()
}
case idQuit:
go h.Quit()
}
return 0
case wmDestroy:
procPostQuit.Call(0)
return 0
}
r, _, _ := procDefWindowProc.Call(uintptr(hwnd), uintptr(msgU), wParam, lParam)
return r
})
wc := wndClassEx{
CbSize: uint32(unsafe.Sizeof(wndClassEx{})),
LpfnWndProc: wndProc,
LpszClassName: className,
}
procRegisterClass.Call(uintptr(unsafe.Pointer(&wc)))
hwnd, _, _ := procCreateWindow.Call(0, uintptr(unsafe.Pointer(className)), uintptr(unsafe.Pointer(title)), 0, 0, 0, 0, 0, 0, 0, 0, 0)
if hwnd == 0 {
return
}
icon, _, _ := procLoadIcon.Call(0, uintptr(idiApplication))
var nid notifyIconData
nid.CbSize = uint32(unsafe.Sizeof(nid))
nid.Hwnd = windows.Handle(hwnd)
nid.UID = 1
nid.UFlags = nifMessage | nifIcon | nifTip
nid.UCallbackMessage = wmTray
nid.HIcon = windows.Handle(icon)
tip, _ := windows.UTF16FromString(appName)
copy(nid.SzTip[:], tip)
procShellNotify.Call(nimAdd, uintptr(unsafe.Pointer(&nid)))
var m msg
for {
ret, _, _ := procGetMessage.Call(uintptr(unsafe.Pointer(&m)), 0, 0, 0)
if int32(ret) <= 0 {
break
}
procTranslate.Call(uintptr(unsafe.Pointer(&m)))
procDispatch.Call(uintptr(unsafe.Pointer(&m)))
}
procShellNotify.Call(nimDelete, uintptr(unsafe.Pointer(&nid)))
}
func showMenu(hwnd windows.Handle, h Hooks) {
menu, _, _ := procCreatePopup.Call()
if menu == 0 {
return
}
defer procDestroyMenu.Call(menu)
add := func(id int, text string) {
p, _ := windows.UTF16PtrFromString(text)
procAppendMenu.Call(menu, mfString, uintptr(id), uintptr(unsafe.Pointer(p)))
}
up := h.IsUp != nil && h.IsUp()
if up {
add(idDisconnect, "Отключить")
} else {
add(idConnect, "Подключить")
}
procAppendMenu.Call(menu, mfSeparator, 0, 0)
add(idQuit, "Выход")
var pt point
procGetCursorPos.Call(uintptr(unsafe.Pointer(&pt)))
procSetForeground.Call(uintptr(hwnd))
procTrackPopup.Call(menu, tpmRight|tpmBottom, uintptr(pt.X), uintptr(pt.Y), 0, uintptr(hwnd), 0)
}