Fix GUI instance counts and launcher tmp paths
This commit is contained in:
216
cmd/vrc_osc_gui/tray_windows.go
Normal file
216
cmd/vrc_osc_gui/tray_windows.go
Normal file
@@ -0,0 +1,216 @@
|
||||
//go:build windows
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os/exec"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
const (
|
||||
wmUser = 0x0400
|
||||
wmTray = wmUser + 1
|
||||
wmCommand = 0x0111
|
||||
wmDestroy = 0x0002
|
||||
wmClose = 0x0010
|
||||
wmRButtonUp = 0x0205
|
||||
wmLButtonUp = 0x0202
|
||||
wmCreate = 0x0001
|
||||
wmQuit = 0x0012
|
||||
niAdd = 0x00000000
|
||||
niModify = 0x00000001
|
||||
niDelete = 0x00000002
|
||||
nifMessage = 0x00000001
|
||||
nifIcon = 0x00000002
|
||||
nifTip = 0x00000004
|
||||
nifInfo = 0x00000010
|
||||
nimPopup = 0x00000000
|
||||
miString = 0x00000000
|
||||
miSeparator = 0x00000800
|
||||
miDefault = 0x00001000
|
||||
swHide = 0
|
||||
idOpen = 1001
|
||||
idExit = 1002
|
||||
maxTip = 128
|
||||
maxClassName = 64
|
||||
)
|
||||
|
||||
type trayApp struct {
|
||||
uiURL string
|
||||
open func()
|
||||
}
|
||||
|
||||
func newTray(uiURL string, open func()) *trayApp {
|
||||
return &trayApp{uiURL: uiURL, open: open}
|
||||
}
|
||||
|
||||
func (t *trayApp) Run() error {
|
||||
return runTray(t.uiURL, t.open)
|
||||
}
|
||||
|
||||
func openBrowser(url string) {
|
||||
_ = exec.Command("rundll32", "url.dll,FileProtocolHandler", url).Start()
|
||||
}
|
||||
|
||||
func runTray(uiURL string, open func()) error {
|
||||
user32 := syscall.NewLazyDLL("user32.dll")
|
||||
shell32 := syscall.NewLazyDLL("shell32.dll")
|
||||
kernel32 := syscall.NewLazyDLL("kernel32.dll")
|
||||
|
||||
registerClass := user32.NewProc("RegisterClassW")
|
||||
createWindowEx := user32.NewProc("CreateWindowExW")
|
||||
defWindowProc := user32.NewProc("DefWindowProcW")
|
||||
getMessage := user32.NewProc("GetMessageW")
|
||||
translateMessage := user32.NewProc("TranslateMessage")
|
||||
dispatchMessage := user32.NewProc("DispatchMessageW")
|
||||
postQuitMessage := user32.NewProc("PostQuitMessage")
|
||||
createPopupMenu := user32.NewProc("CreatePopupMenu")
|
||||
appendMenu := user32.NewProc("AppendMenuW")
|
||||
setForegroundWindow := user32.NewProc("SetForegroundWindow")
|
||||
trackPopupMenu := user32.NewProc("TrackPopupMenu")
|
||||
showWindow := user32.NewProc("ShowWindow")
|
||||
loadIcon := user32.NewProc("LoadIconW")
|
||||
shellNotifyIcon := shell32.NewProc("Shell_NotifyIconW")
|
||||
getModuleHandle := kernel32.NewProc("GetModuleHandleW")
|
||||
|
||||
type wndClassEx struct {
|
||||
cbSize uint32
|
||||
style uint32
|
||||
lpfnWndProc uintptr
|
||||
cbClsExtra int32
|
||||
cbWndExtra int32
|
||||
hInstance uintptr
|
||||
hIcon uintptr
|
||||
hCursor uintptr
|
||||
hbrBackground uintptr
|
||||
lpszMenuName *uint16
|
||||
lpszClassName *uint16
|
||||
hIconSm uintptr
|
||||
}
|
||||
type point struct{ X, Y int32 }
|
||||
type msg struct {
|
||||
hwnd uintptr
|
||||
message uint32
|
||||
wParam uintptr
|
||||
lParam uintptr
|
||||
time uint32
|
||||
pt point
|
||||
}
|
||||
type notifyIconData struct {
|
||||
cbSize uint32
|
||||
hWnd uintptr
|
||||
uID uint32
|
||||
uFlags uint32
|
||||
uCallbackMessage uint32
|
||||
hIcon uintptr
|
||||
szTip [maxTip]uint16
|
||||
dwState uint32
|
||||
dwStateMask uint32
|
||||
szInfo [256]uint16
|
||||
uTimeoutVersion uint32
|
||||
szInfoTitle [64]uint16
|
||||
dwInfoFlags uint32
|
||||
guidItem [16]byte
|
||||
hBalloonIcon uintptr
|
||||
}
|
||||
|
||||
className, _ := syscall.UTF16PtrFromString("VRC_OSC_TRAY")
|
||||
windowTitle, _ := syscall.UTF16PtrFromString("VRC OSC Tray")
|
||||
icon, _, _ := loadIcon.Call(0, 32512) // IDI_APPLICATION
|
||||
|
||||
var hwnd uintptr
|
||||
var menu uintptr
|
||||
var nid notifyIconData
|
||||
|
||||
wndProc := syscall.NewCallback(func(hwnd uintptr, msg uint32, wParam, lParam uintptr) uintptr {
|
||||
switch msg {
|
||||
case wmCreate:
|
||||
return 0
|
||||
case wmTray:
|
||||
switch lParam {
|
||||
case wmLButtonUp:
|
||||
if open != nil {
|
||||
open()
|
||||
}
|
||||
case wmRButtonUp:
|
||||
if menu == 0 {
|
||||
m, _, _ := createPopupMenu.Call()
|
||||
menu = m
|
||||
openText, _ := syscall.UTF16PtrFromString("Open UI")
|
||||
exitText, _ := syscall.UTF16PtrFromString("Exit")
|
||||
appendMenu.Call(menu, miString|miDefault, idOpen, uintptr(unsafe.Pointer(openText)))
|
||||
appendMenu.Call(menu, miString, idExit, uintptr(unsafe.Pointer(exitText)))
|
||||
}
|
||||
setForegroundWindow.Call(hwnd)
|
||||
var p point
|
||||
user32.NewProc("GetCursorPos").Call(uintptr(unsafe.Pointer(&p)))
|
||||
trackPopupMenu.Call(menu, nimPopup, uintptr(p.X), uintptr(p.Y), 0, hwnd, 0)
|
||||
}
|
||||
return 0
|
||||
case wmCommand:
|
||||
switch uint16(wParam & 0xffff) {
|
||||
case idOpen:
|
||||
if open != nil {
|
||||
open()
|
||||
}
|
||||
case idExit:
|
||||
user32.NewProc("DestroyWindow").Call(hwnd)
|
||||
}
|
||||
return 0
|
||||
case wmDestroy:
|
||||
nid.cbSize = uint32(unsafe.Sizeof(nid))
|
||||
nid.hWnd = hwnd
|
||||
nid.uID = 1
|
||||
shellNotifyIcon.Call(niDelete, uintptr(unsafe.Pointer(&nid)))
|
||||
postQuitMessage.Call(0)
|
||||
return 0
|
||||
}
|
||||
ret, _, _ := defWindowProc.Call(hwnd, uintptr(msg), wParam, lParam)
|
||||
return ret
|
||||
})
|
||||
|
||||
hInstance, _, _ := getModuleHandle.Call(0)
|
||||
_, _, regErr := registerClass.Call(uintptr(unsafe.Pointer(&wndClassEx{
|
||||
cbSize: uint32(unsafe.Sizeof(wndClassEx{})),
|
||||
lpfnWndProc: wndProc,
|
||||
hInstance: hInstance,
|
||||
hIcon: icon,
|
||||
lpszClassName: className,
|
||||
})))
|
||||
if regErr != nil {
|
||||
return fmt.Errorf("RegisterClassW failed: %v", regErr)
|
||||
}
|
||||
|
||||
hwnd, _, err := createWindowEx.Call(0, uintptr(unsafe.Pointer(className)), uintptr(unsafe.Pointer(windowTitle)), 0, 0, 0, 0, 0, 0, 0, hInstance, 0)
|
||||
if hwnd == 0 {
|
||||
return fmt.Errorf("CreateWindowExW failed: %v", err)
|
||||
}
|
||||
showWindow.Call(hwnd, swHide)
|
||||
|
||||
nid.cbSize = uint32(unsafe.Sizeof(nid))
|
||||
nid.hWnd = hwnd
|
||||
nid.uID = 1
|
||||
nid.uFlags = nifMessage | nifIcon | nifTip
|
||||
nid.uCallbackMessage = wmTray
|
||||
nid.hIcon = icon
|
||||
tip := syscall.StringToUTF16("VRC OSC")
|
||||
copy(nid.szTip[:], tip)
|
||||
if ok, _, _ := shellNotifyIcon.Call(niAdd, uintptr(unsafe.Pointer(&nid))); ok == 0 {
|
||||
return fmt.Errorf("Shell_NotifyIconW add failed")
|
||||
}
|
||||
log.Printf("tray started")
|
||||
|
||||
var m msg
|
||||
for {
|
||||
r, _, _ := getMessage.Call(uintptr(unsafe.Pointer(&m)), 0, 0, 0)
|
||||
if int32(r) <= 0 {
|
||||
break
|
||||
}
|
||||
translateMessage.Call(uintptr(unsafe.Pointer(&m)))
|
||||
dispatchMessage.Call(uintptr(unsafe.Pointer(&m)))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user