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>
This commit is contained in:
@@ -0,0 +1,194 @@
|
||||
//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)
|
||||
}
|
||||
Reference in New Issue
Block a user