[v3 windows] Initial systray support

This commit is contained in:
Lea Anthony
2023-05-30 16:31:55 +08:00
committed by Misite Bao
parent 143f090422
commit 58138ac09b
12 changed files with 861 additions and 142 deletions
+4
View File
@@ -137,6 +137,10 @@ type macosApp struct {
parent *App
}
func getNativeApplication() *macosApp {
return globalApplication.impl.(*macosApp)
}
func (m *macosApp) hide() {
C.hide()
}
+18 -4
View File
@@ -20,7 +20,8 @@ type windowsApp struct {
instance w32.HINSTANCE
windowMap map[w32.HWND]*windowsWebviewWindow
windowMap map[w32.HWND]*windowsWebviewWindow
systrayMap map[w32.HMENU]*windowsSystemTray
mainThreadID w32.HANDLE
mainThreadWindowHWND w32.HWND
@@ -33,6 +34,10 @@ type windowsApp struct {
isDarkMode bool
}
func getNativeApplication() *windowsApp {
return globalApplication.impl.(*windowsApp)
}
func (m *windowsApp) getPrimaryScreen() (*Screen, error) {
//TODO implement me
panic("implement me")
@@ -194,6 +199,10 @@ func (m *windowsApp) wndProc(hwnd w32.HWND, msg uint32, wParam, lParam uintptr)
return window.WndProc(msg, wParam, lParam)
}
if systray, ok := m.systrayMap[hwnd]; ok {
return systray.wndProc(msg, wParam, lParam)
}
// Dispatch the message to the appropriate window
return w32.DefWindowProc(hwnd, msg, wParam, lParam)
@@ -203,6 +212,10 @@ func (m *windowsApp) registerWindow(result *windowsWebviewWindow) {
m.windowMap[result.hwnd] = result
}
func (m *windowsApp) registerSystemTray(result *windowsSystemTray) {
m.systrayMap[result.hwnd] = result
}
func (m *windowsApp) unregisterWindow(w *windowsWebviewWindow) {
delete(m.windowMap, w.hwnd)
@@ -220,9 +233,10 @@ func newPlatformApp(app *App) *windowsApp {
}
result := &windowsApp{
parent: app,
instance: w32.GetModuleHandle(""),
windowMap: make(map[w32.HWND]*windowsWebviewWindow),
parent: app,
instance: w32.GetModuleHandle(""),
windowMap: make(map[w32.HWND]*windowsWebviewWindow),
systrayMap: make(map[w32.HWND]*windowsSystemTray),
}
result.init()
+6
View File
@@ -30,6 +30,11 @@ type SystemTray struct {
icon []byte
iconPosition int
leftButtonClickHandler func()
rightButtonClickHandler func()
leftButtonDoubleClickHandler func()
rightButtonDoubleClickHandler func()
// Platform specific implementation
impl systemTrayImpl
menu *Menu
@@ -41,6 +46,7 @@ func NewSystemTray(id uint) *SystemTray {
id: id,
label: "",
iconPosition: NSImageLeading,
icon: DefaultApplicationIcon,
}
}
+182 -41
View File
@@ -2,10 +2,24 @@
package application
import (
"github.com/wailsapp/wails/v3/pkg/events"
"github.com/wailsapp/wails/v3/pkg/w32"
"syscall"
"unsafe"
)
type windowsSystemTray struct {
id uint
icon []byte
menu *Menu
parent *SystemTray
// Platform specific implementation
uid uint32
hwnd w32.HWND
appIcon w32.HICON
lightModeIcon w32.HICON
darkModeIcon w32.HICON
currentIcon w32.HICON
//menu *w32.PopupMenu
}
func (s *windowsSystemTray) setIconPosition(position int) {
@@ -13,56 +27,183 @@ func (s *windowsSystemTray) setIconPosition(position int) {
}
func (s *windowsSystemTray) setMenu(menu *Menu) {
s.menu = menu
//s.menu = menu
panic("implement me")
}
func (s *windowsSystemTray) run() {
globalApplication.dispatchOnMainThread(func() {
//if s.nsStatusItem != nil {
// Fatal("System tray '%d' already running", s.id)
//}
//s.nsStatusItem = unsafe.Pointer(C.systemTrayNew())
//if s.label != "" {
// C.systemTraySetLabel(s.nsStatusItem, C.CString(s.label))
//}
//if s.icon != nil {
// s.nsImage = unsafe.Pointer(C.imageFromBytes((*C.uchar)(&s.icon[0]), C.int(len(s.icon))))
// C.systemTraySetIcon(s.nsStatusItem, s.nsImage, C.int(s.iconPosition), C.bool(s.isTemplateIcon))
//}
//if s.menu != nil {
// s.menu.Update()
// // Convert impl to macosMenu object
// s.nsMenu = (s.menu.impl).(*macosMenu).nsMenu
// C.systemTraySetMenu(s.nsStatusItem, s.nsMenu)
//}
panic("implement me")
NotifyIconClassName := "WailsSystray"
_, err := w32.RegisterWindow(NotifyIconClassName, getNativeApplication().wndProc)
if err != nil {
panic(err)
}
s.hwnd = w32.CreateWindowEx(
0,
w32.MustStringToUTF16Ptr(NotifyIconClassName),
nil,
0,
0,
0,
0,
0,
w32.HWND_MESSAGE,
0,
0,
nil)
if s.hwnd == 0 {
panic(syscall.GetLastError())
}
nid := w32.NOTIFYICONDATA{
HWnd: s.hwnd,
UID: uint32(s.parent.id),
UFlags: w32.NIF_ICON | w32.NIF_MESSAGE,
HIcon: s.currentIcon,
UCallbackMessage: w32.WM_USER + uint32(s.parent.id),
}
nid.CbSize = uint32(unsafe.Sizeof(nid))
if !w32.ShellNotifyIcon(w32.NIM_ADD, &nid) {
panic(syscall.GetLastError())
}
nid.UVersion = w32.NOTIFYICON_VERSION
if !w32.ShellNotifyIcon(w32.NIM_SETVERSION, &nid) {
panic(syscall.GetLastError())
}
defaultIcon, err := w32.CreateHIconFromPNG(s.parent.icon)
if err != nil {
panic(err)
}
s.lightModeIcon = defaultIcon
s.darkModeIcon = defaultIcon
s.uid = nid.UID
// TODO: Set Menu
// Update the icon
s.updateIcon()
// Listen for dark mode changes
globalApplication.On(events.Windows.SystemThemeChanged, func() {
s.updateIcon()
})
// Register the system tray
getNativeApplication().registerSystemTray(s)
}
func (s *windowsSystemTray) updateIcon() {
var newIcon w32.HICON
if w32.IsCurrentlyDarkMode() {
newIcon = s.darkModeIcon
} else {
newIcon = s.lightModeIcon
}
if s.currentIcon == newIcon {
return
}
s.currentIcon = newIcon
nid := s.newNotifyIconData()
nid.UFlags = w32.NIF_ICON | w32.NIF_MESSAGE
if s.currentIcon != 0 {
nid.HIcon = s.currentIcon
}
if !w32.ShellNotifyIcon(w32.NIM_MODIFY, &nid) {
panic(syscall.GetLastError())
}
return
}
func (s *windowsSystemTray) newNotifyIconData() w32.NOTIFYICONDATA {
nid := w32.NOTIFYICONDATA{
UID: s.uid,
HWnd: s.hwnd,
}
nid.CbSize = uint32(unsafe.Sizeof(nid))
return nid
}
func (s *windowsSystemTray) setIcon(icon []byte) {
s.icon = icon
globalApplication.dispatchOnMainThread(func() {
//s.nsImage = unsafe.Pointer(C.imageFromBytes((*C.uchar)(&icon[0]), C.int(len(icon))))
//C.systemTraySetIcon(s.nsStatusItem, s.nsImage, C.int(s.iconPosition), C.bool(s.isTemplateIcon))
})
}
func (s *windowsSystemTray) setTemplateIcon(icon []byte) {
// Unsupported - do nothing
}
func newSystemTrayImpl(s *SystemTray) systemTrayImpl {
return &windowsSystemTray{
id: s.id,
icon: s.icon,
menu: s.menu,
// TODO:
var err error
if w32.IsCurrentlyDarkMode() {
s.darkModeIcon, err = w32.CreateHIconFromPNG(icon)
if err != nil {
panic(syscall.GetLastError())
}
} else {
s.lightModeIcon, err = w32.CreateHIconFromPNG(icon)
if err != nil {
panic(syscall.GetLastError())
}
}
// Update the icon
s.updateIcon()
}
func (s *windowsSystemTray) setLabel(label string) {
// Unsupported - do nothing
func newSystemTrayImpl(parent *SystemTray) systemTrayImpl {
return &windowsSystemTray{
parent: parent,
}
}
func (s *windowsSystemTray) destroy() {
panic("implement me")
}
func (s *windowsSystemTray) wndProc(msg uint32, wParam, lParam uintptr) uintptr {
switch msg {
case w32.WM_USER + uint32(s.parent.id):
switch lParam {
case w32.WM_LBUTTONUP:
if s.parent.leftButtonClickHandler != nil {
s.parent.leftButtonClickHandler()
}
case w32.WM_RBUTTONUP:
if s.parent.rightButtonClickHandler != nil {
s.parent.rightButtonClickHandler()
}
case w32.WM_LBUTTONDBLCLK:
if s.parent.leftButtonDoubleClickHandler != nil {
s.parent.leftButtonDoubleClickHandler()
}
case w32.WM_RBUTTONDBLCLK:
if s.parent.rightButtonDoubleClickHandler != nil {
s.parent.rightButtonDoubleClickHandler()
}
default:
println(w32.WMMessageToString(lParam))
}
// TODO: Menu processing
//case w32.WM_COMMAND:
// cmdMsgID := int(wparam & 0xffff)
// switch cmdMsgID {
// default:
// p.menu.ProcessCommand(cmdMsgID)
// }
default:
msg := int(wParam & 0xffff)
println(w32.WMMessageToString(uintptr(msg)))
}
return w32.DefWindowProc(s.hwnd, msg, wParam, lParam)
}
// ---- Unsupported ----
func (s *windowsSystemTray) setLabel(_ string) {
// Unsupported - do nothing
}
func (s *windowsSystemTray) setTemplateIcon(_ []byte) {
// Unsupported - do nothing
}
+1 -2
View File
@@ -115,8 +115,7 @@ func (w *windowsWebviewWindow) run() {
}
// Register the window with the application
windowsApp := globalApplication.impl.(*windowsApp)
windowsApp.registerWindow(w)
getNativeApplication().registerWindow(w)
w.setResizable(!options.DisableResize)
+106 -76
View File
@@ -1046,39 +1046,39 @@ const (
)
const (
TBM_GETPOS = (WM_USER)
TBM_GETRANGEMIN = (WM_USER + 1)
TBM_GETRANGEMAX = (WM_USER + 2)
TBM_GETTIC = (WM_USER + 3)
TBM_SETTIC = (WM_USER + 4)
TBM_SETPOS = (WM_USER + 5)
TBM_SETRANGE = (WM_USER + 6)
TBM_SETRANGEMIN = (WM_USER + 7)
TBM_SETRANGEMAX = (WM_USER + 8)
TBM_CLEARTICS = (WM_USER + 9)
TBM_SETSEL = (WM_USER + 10)
TBM_SETSELSTART = (WM_USER + 11)
TBM_SETSELEND = (WM_USER + 12)
TBM_GETPTICS = (WM_USER + 14)
TBM_GETTICPOS = (WM_USER + 15)
TBM_GETNUMTICS = (WM_USER + 16)
TBM_GETSELSTART = (WM_USER + 17)
TBM_GETSELEND = (WM_USER + 18)
TBM_CLEARSEL = (WM_USER + 19)
TBM_SETTICFREQ = (WM_USER + 20)
TBM_SETPAGESIZE = (WM_USER + 21)
TBM_GETPAGESIZE = (WM_USER + 22)
TBM_SETLINESIZE = (WM_USER + 23)
TBM_GETLINESIZE = (WM_USER + 24)
TBM_GETTHUMBRECT = (WM_USER + 25)
TBM_GETCHANNELRECT = (WM_USER + 26)
TBM_SETTHUMBLENGTH = (WM_USER + 27)
TBM_GETTHUMBLENGTH = (WM_USER + 28)
TBM_SETTOOLTIPS = (WM_USER + 29)
TBM_GETTOOLTIPS = (WM_USER + 30)
TBM_SETTIPSIDE = (WM_USER + 31)
TBM_SETBUDDY = (WM_USER + 32)
TBM_GETBUDDY = (WM_USER + 33)
TBM_GETPOS = WM_USER
TBM_GETRANGEMIN = WM_USER + 1
TBM_GETRANGEMAX = WM_USER + 2
TBM_GETTIC = WM_USER + 3
TBM_SETTIC = WM_USER + 4
TBM_SETPOS = WM_USER + 5
TBM_SETRANGE = WM_USER + 6
TBM_SETRANGEMIN = WM_USER + 7
TBM_SETRANGEMAX = WM_USER + 8
TBM_CLEARTICS = WM_USER + 9
TBM_SETSEL = WM_USER + 10
TBM_SETSELSTART = WM_USER + 11
TBM_SETSELEND = WM_USER + 12
TBM_GETPTICS = WM_USER + 14
TBM_GETTICPOS = WM_USER + 15
TBM_GETNUMTICS = WM_USER + 16
TBM_GETSELSTART = WM_USER + 17
TBM_GETSELEND = WM_USER + 18
TBM_CLEARSEL = WM_USER + 19
TBM_SETTICFREQ = WM_USER + 20
TBM_SETPAGESIZE = WM_USER + 21
TBM_GETPAGESIZE = WM_USER + 22
TBM_SETLINESIZE = WM_USER + 23
TBM_GETLINESIZE = WM_USER + 24
TBM_GETTHUMBRECT = WM_USER + 25
TBM_GETCHANNELRECT = WM_USER + 26
TBM_SETTHUMBLENGTH = WM_USER + 27
TBM_GETTHUMBLENGTH = WM_USER + 28
TBM_SETTOOLTIPS = WM_USER + 29
TBM_GETTOOLTIPS = WM_USER + 30
TBM_SETTIPSIDE = WM_USER + 31
TBM_SETBUDDY = WM_USER + 32
TBM_GETBUDDY = WM_USER + 33
)
const (
@@ -1809,8 +1809,8 @@ const (
RRF_RT_REG_DWORD = 0x00000010
RRF_RT_REG_MULTI_SZ = 0x00000020
RRF_RT_REG_QWORD = 0x00000040
RRF_RT_DWORD = (RRF_RT_REG_BINARY | RRF_RT_REG_DWORD)
RRF_RT_QWORD = (RRF_RT_REG_BINARY | RRF_RT_REG_QWORD)
RRF_RT_DWORD = RRF_RT_REG_BINARY | RRF_RT_REG_DWORD
RRF_RT_QWORD = RRF_RT_REG_BINARY | RRF_RT_REG_QWORD
RRF_RT_ANY = 0x0000ffff
RRF_NOEXPAND = 0x10000000
RRF_ZEROONFAILURE = 0x20000000
@@ -2047,39 +2047,39 @@ const (
// Tooltip messages
const (
TTM_ACTIVATE = (WM_USER + 1)
TTM_SETDELAYTIME = (WM_USER + 3)
TTM_ADDTOOL = (WM_USER + 50)
TTM_DELTOOL = (WM_USER + 51)
TTM_NEWTOOLRECT = (WM_USER + 52)
TTM_RELAYEVENT = (WM_USER + 7)
TTM_GETTOOLINFO = (WM_USER + 53)
TTM_SETTOOLINFO = (WM_USER + 54)
TTM_HITTEST = (WM_USER + 55)
TTM_GETTEXT = (WM_USER + 56)
TTM_UPDATETIPTEXT = (WM_USER + 57)
TTM_GETTOOLCOUNT = (WM_USER + 13)
TTM_ENUMTOOLS = (WM_USER + 58)
TTM_GETCURRENTTOOL = (WM_USER + 59)
TTM_WINDOWFROMPOINT = (WM_USER + 16)
TTM_TRACKACTIVATE = (WM_USER + 17)
TTM_TRACKPOSITION = (WM_USER + 18)
TTM_SETTIPBKCOLOR = (WM_USER + 19)
TTM_SETTIPTEXTCOLOR = (WM_USER + 20)
TTM_GETDELAYTIME = (WM_USER + 21)
TTM_GETTIPBKCOLOR = (WM_USER + 22)
TTM_GETTIPTEXTCOLOR = (WM_USER + 23)
TTM_SETMAXTIPWIDTH = (WM_USER + 24)
TTM_GETMAXTIPWIDTH = (WM_USER + 25)
TTM_SETMARGIN = (WM_USER + 26)
TTM_GETMARGIN = (WM_USER + 27)
TTM_POP = (WM_USER + 28)
TTM_UPDATE = (WM_USER + 29)
TTM_GETBUBBLESIZE = (WM_USER + 30)
TTM_ADJUSTRECT = (WM_USER + 31)
TTM_SETTITLE = (WM_USER + 33)
TTM_POPUP = (WM_USER + 34)
TTM_GETTITLE = (WM_USER + 35)
TTM_ACTIVATE = WM_USER + 1
TTM_SETDELAYTIME = WM_USER + 3
TTM_ADDTOOL = WM_USER + 50
TTM_DELTOOL = WM_USER + 51
TTM_NEWTOOLRECT = WM_USER + 52
TTM_RELAYEVENT = WM_USER + 7
TTM_GETTOOLINFO = WM_USER + 53
TTM_SETTOOLINFO = WM_USER + 54
TTM_HITTEST = WM_USER + 55
TTM_GETTEXT = WM_USER + 56
TTM_UPDATETIPTEXT = WM_USER + 57
TTM_GETTOOLCOUNT = WM_USER + 13
TTM_ENUMTOOLS = WM_USER + 58
TTM_GETCURRENTTOOL = WM_USER + 59
TTM_WINDOWFROMPOINT = WM_USER + 16
TTM_TRACKACTIVATE = WM_USER + 17
TTM_TRACKPOSITION = WM_USER + 18
TTM_SETTIPBKCOLOR = WM_USER + 19
TTM_SETTIPTEXTCOLOR = WM_USER + 20
TTM_GETDELAYTIME = WM_USER + 21
TTM_GETTIPBKCOLOR = WM_USER + 22
TTM_GETTIPTEXTCOLOR = WM_USER + 23
TTM_SETMAXTIPWIDTH = WM_USER + 24
TTM_GETMAXTIPWIDTH = WM_USER + 25
TTM_SETMARGIN = WM_USER + 26
TTM_GETMARGIN = WM_USER + 27
TTM_POP = WM_USER + 28
TTM_UPDATE = WM_USER + 29
TTM_GETBUBBLESIZE = WM_USER + 30
TTM_ADJUSTRECT = WM_USER + 31
TTM_SETTITLE = WM_USER + 33
TTM_POPUP = WM_USER + 34
TTM_GETTITLE = WM_USER + 35
)
// Tooltip icons
@@ -2097,10 +2097,10 @@ const (
const (
TTN_FIRST = -520
TTN_LAST = -549
TTN_GETDISPINFO = (TTN_FIRST - 10)
TTN_SHOW = (TTN_FIRST - 1)
TTN_POP = (TTN_FIRST - 2)
TTN_LINKCLICK = (TTN_FIRST - 3)
TTN_GETDISPINFO = TTN_FIRST - 10
TTN_SHOW = TTN_FIRST - 1
TTN_POP = TTN_FIRST - 2
TTN_LINKCLICK = TTN_FIRST - 3
TTN_NEEDTEXT = TTN_GETDISPINFO
)
@@ -2244,8 +2244,8 @@ const (
// WM_NCHITTEST and MOUSEHOOKSTRUCT Mouse Position Codes
const (
HTERROR = (-2)
HTTRANSPARENT = (-1)
HTERROR = -2
HTTRANSPARENT = -1
HTNOWHERE = 0
HTCLIENT = 1
HTCAPTION = 2
@@ -2328,8 +2328,8 @@ const (
GMEM_LOWER = GMEM_NOT_BANKED
GMEM_VALID_FLAGS = 0x7F72
GMEM_INVALID_HANDLE = 0x8000
GHND = (GMEM_MOVEABLE | GMEM_ZEROINIT)
GPTR = (GMEM_FIXED | GMEM_ZEROINIT)
GHND = GMEM_MOVEABLE | GMEM_ZEROINIT
GPTR = GMEM_FIXED | GMEM_ZEROINIT
)
// Ternary raster operations
@@ -2511,6 +2511,31 @@ const (
MAX_PATH = 260
)
const (
NIM_ADD = 0x00000000
NIM_MODIFY = 0x00000001
NIM_DELETE = 0x00000002
NIM_SETVERSION = 0x00000004
NIF_MESSAGE = 0x00000001
NIF_ICON = 0x00000002
NIF_TIP = 0x00000004
NIF_STATE = 0x00000008
NIF_INFO = 0x00000010
NIS_HIDDEN = 0x00000001
NIIF_NONE = 0x00000000
NIIF_INFO = 0x00000001
NIIF_WARNING = 0x00000002
NIIF_ERROR = 0x00000003
NIIF_USER = 0x00000004
NIIF_NOSOUND = 0x00000010
NIIF_LARGE_ICON = 0x00000020
NIIF_RESPECT_QUIET_TIME = 0x00000080
NIIF_ICON_MASK = 0x0000000F
)
const (
FOREGROUND_BLUE = 0x0001
FOREGROUND_GREEN = 0x0002
@@ -2675,6 +2700,11 @@ const (
XBUTTON2 = 2
)
const (
LR_LOADFROMFILE = 0x00000010
LR_DEFAULTSIZE = 0x00000040
)
// Devmode
const (
DM_SPECVERSION = 0x0401
+41
View File
@@ -0,0 +1,41 @@
//go:build windows
package w32
import (
"unsafe"
)
func CreateIconFromResourceEx(presbits uintptr, dwResSize uint32, isIcon bool, version uint32, cxDesired int, cyDesired int, flags uint) (uintptr, error) {
icon := 0
if isIcon {
icon = 1
}
r, _, err := procCreateIconFromResourceEx.Call(
presbits,
uintptr(dwResSize),
uintptr(icon),
uintptr(version),
uintptr(cxDesired),
uintptr(cyDesired),
uintptr(flags),
)
if r == 0 {
return 0, err
}
return r, nil
}
// CreateHIconFromPNG creates a HICON from a PNG file
func CreateHIconFromPNG(pngData []byte) (HICON, error) {
icon, err := CreateIconFromResourceEx(
uintptr(unsafe.Pointer(&pngData[0])),
uint32(len(pngData)),
true,
0x00030000,
0,
0,
LR_DEFAULTSIZE)
return HICON(icon), err
}
+8
View File
@@ -78,6 +78,8 @@ const (
CSIDL_FLAG_NO_ALIAS = 0x1000
CSIDL_FLAG_PER_USER_INIT = 0x8000
CSIDL_FLAG_MASK = 0xFF00
NOTIFYICON_VERSION = 4
)
var (
@@ -92,8 +94,14 @@ var (
procShellExecute = modshell32.NewProc("ShellExecuteW")
procExtractIcon = modshell32.NewProc("ExtractIconW")
procGetSpecialFolderPath = modshell32.NewProc("SHGetSpecialFolderPathW")
procShellNotifyIcon = modshell32.NewProc("Shell_NotifyIconW")
)
func ShellNotifyIcon(cmd uintptr, nid *NOTIFYICONDATA) bool {
ret, _, _ := procShellNotifyIcon.Call(cmd, uintptr(unsafe.Pointer(nid)))
return ret == 1
}
func SHBrowseForFolder(bi *BROWSEINFO) uintptr {
ret, _, _ := procSHBrowseForFolder.Call(uintptr(unsafe.Pointer(bi)))
+19
View File
@@ -9,6 +9,7 @@ package w32
import (
"fmt"
"golang.org/x/sys/windows"
"unsafe"
)
@@ -653,6 +654,24 @@ type TRACKMOUSEEVENT struct {
DwHoverTime uint32
}
type NOTIFYICONDATA struct {
CbSize uint32
HWnd HWND
UID uint32
UFlags uint32
UCallbackMessage uint32
HIcon HICON
SzTip [128]uint16
DwState uint32
DwStateMask uint32
SzInfo [256]uint16
UVersion uint32
SzInfoTitle [64]uint16
DwInfoFlags uint32
GuidItem windows.GUID
HBalloonIcon HICON
}
// http://msdn.microsoft.com/en-us/library/windows/desktop/ms534067.aspx
type GdiplusStartupInput struct {
GdiplusVersion uint32
+20 -19
View File
@@ -69,27 +69,28 @@ var (
procGetSystemMetrics = moduser32.NewProc("GetSystemMetrics")
procPostThreadMessageW = moduser32.NewProc("PostThreadMessageW")
procRegisterWindowMessageA = moduser32.NewProc("RegisterWindowMessageA")
procCopyRect = moduser32.NewProc("CopyRect")
procEqualRect = moduser32.NewProc("EqualRect")
procInflateRect = moduser32.NewProc("InflateRect")
procIntersectRect = moduser32.NewProc("IntersectRect")
procIsRectEmpty = moduser32.NewProc("IsRectEmpty")
procOffsetRect = moduser32.NewProc("OffsetRect")
procPtInRect = moduser32.NewProc("PtInRect")
procSetRect = moduser32.NewProc("SetRect")
procSetRectEmpty = moduser32.NewProc("SetRectEmpty")
procSubtractRect = moduser32.NewProc("SubtractRect")
procUnionRect = moduser32.NewProc("UnionRect")
procCreateDialogParam = moduser32.NewProc("CreateDialogParamW")
procDialogBoxParam = moduser32.NewProc("DialogBoxParamW")
procGetDlgItem = moduser32.NewProc("GetDlgItem")
procDrawIcon = moduser32.NewProc("DrawIcon")
procCreateMenu = moduser32.NewProc("CreateMenu")
procDestroyMenu = moduser32.NewProc("DestroyMenu")
procCreatePopupMenu = moduser32.NewProc("CreatePopupMenu")
procCheckMenuRadioItem = moduser32.NewProc("CheckMenuRadioItem")
procCreateIconFromResourceEx = moduser32.NewProc("CreateIconFromResourceEx")
//procSysColorBrush = moduser32.NewProc("GetSysColorBrush")
procCopyRect = moduser32.NewProc("CopyRect")
procEqualRect = moduser32.NewProc("EqualRect")
procInflateRect = moduser32.NewProc("InflateRect")
procIntersectRect = moduser32.NewProc("IntersectRect")
procIsRectEmpty = moduser32.NewProc("IsRectEmpty")
procOffsetRect = moduser32.NewProc("OffsetRect")
procPtInRect = moduser32.NewProc("PtInRect")
procSetRect = moduser32.NewProc("SetRect")
procSetRectEmpty = moduser32.NewProc("SetRectEmpty")
procSubtractRect = moduser32.NewProc("SubtractRect")
procUnionRect = moduser32.NewProc("UnionRect")
procCreateDialogParam = moduser32.NewProc("CreateDialogParamW")
procDialogBoxParam = moduser32.NewProc("DialogBoxParamW")
procGetDlgItem = moduser32.NewProc("GetDlgItem")
procDrawIcon = moduser32.NewProc("DrawIcon")
procCreateMenu = moduser32.NewProc("CreateMenu")
//procSetMenu = moduser32.NewProc("SetMenu")
procDestroyMenu = moduser32.NewProc("DestroyMenu")
procCreatePopupMenu = moduser32.NewProc("CreatePopupMenu")
procCheckMenuRadioItem = moduser32.NewProc("CheckMenuRadioItem")
//procDrawMenuBar = moduser32.NewProc("DrawMenuBar")
//procInsertMenuItem = moduser32.NewProc("InsertMenuItemW") // FIXIT:
+407
View File
@@ -8,6 +8,7 @@
package w32
import (
"fmt"
"syscall"
"unicode/utf16"
"unsafe"
@@ -228,3 +229,409 @@ func ComInvoke(disp *IDispatch, dispid int32, dispatch int16, params ...interfac
result = &ret
return
}
func WMMessageToString(msg uintptr) string {
// Convert windows message to string
switch msg {
case WM_APP:
return "WM_APP"
case WM_ACTIVATE:
return "WM_ACTIVATE"
case WM_ACTIVATEAPP:
return "WM_ACTIVATEAPP"
case WM_AFXFIRST:
return "WM_AFXFIRST"
case WM_AFXLAST:
return "WM_AFXLAST"
case WM_ASKCBFORMATNAME:
return "WM_ASKCBFORMATNAME"
case WM_CANCELJOURNAL:
return "WM_CANCELJOURNAL"
case WM_CANCELMODE:
return "WM_CANCELMODE"
case WM_CAPTURECHANGED:
return "WM_CAPTURECHANGED"
case WM_CHANGECBCHAIN:
return "WM_CHANGECBCHAIN"
case WM_CHAR:
return "WM_CHAR"
case WM_CHARTOITEM:
return "WM_CHARTOITEM"
case WM_CHILDACTIVATE:
return "WM_CHILDACTIVATE"
case WM_CLEAR:
return "WM_CLEAR"
case WM_CLOSE:
return "WM_CLOSE"
case WM_COMMAND:
return "WM_COMMAND"
case WM_COMMNOTIFY /* OBSOLETE */ :
return "WM_COMMNOTIFY"
case WM_COMPACTING:
return "WM_COMPACTING"
case WM_COMPAREITEM:
return "WM_COMPAREITEM"
case WM_CONTEXTMENU:
return "WM_CONTEXTMENU"
case WM_COPY:
return "WM_COPY"
case WM_COPYDATA:
return "WM_COPYDATA"
case WM_CREATE:
return "WM_CREATE"
case WM_CTLCOLORBTN:
return "WM_CTLCOLORBTN"
case WM_CTLCOLORDLG:
return "WM_CTLCOLORDLG"
case WM_CTLCOLOREDIT:
return "WM_CTLCOLOREDIT"
case WM_CTLCOLORLISTBOX:
return "WM_CTLCOLORLISTBOX"
case WM_CTLCOLORMSGBOX:
return "WM_CTLCOLORMSGBOX"
case WM_CTLCOLORSCROLLBAR:
return "WM_CTLCOLORSCROLLBAR"
case WM_CTLCOLORSTATIC:
return "WM_CTLCOLORSTATIC"
case WM_CUT:
return "WM_CUT"
case WM_DEADCHAR:
return "WM_DEADCHAR"
case WM_DELETEITEM:
return "WM_DELETEITEM"
case WM_DESTROY:
return "WM_DESTROY"
case WM_DESTROYCLIPBOARD:
return "WM_DESTROYCLIPBOARD"
case WM_DEVICECHANGE:
return "WM_DEVICECHANGE"
case WM_DEVMODECHANGE:
return "WM_DEVMODECHANGE"
case WM_DISPLAYCHANGE:
return "WM_DISPLAYCHANGE"
case WM_DRAWCLIPBOARD:
return "WM_DRAWCLIPBOARD"
case WM_DRAWITEM:
return "WM_DRAWITEM"
case WM_DROPFILES:
return "WM_DROPFILES"
case WM_ENABLE:
return "WM_ENABLE"
case WM_ENDSESSION:
return "WM_ENDSESSION"
case WM_ENTERIDLE:
return "WM_ENTERIDLE"
case WM_ENTERMENULOOP:
return "WM_ENTERMENULOOP"
case WM_ENTERSIZEMOVE:
return "WM_ENTERSIZEMOVE"
case WM_ERASEBKGND:
return "WM_ERASEBKGND"
case WM_EXITMENULOOP:
return "WM_EXITMENULOOP"
case WM_EXITSIZEMOVE:
return "WM_EXITSIZEMOVE"
case WM_FONTCHANGE:
return "WM_FONTCHANGE"
case WM_GETDLGCODE:
return "WM_GETDLGCODE"
case WM_GETFONT:
return "WM_GETFONT"
case WM_GETHOTKEY:
return "WM_GETHOTKEY"
case WM_GETICON:
return "WM_GETICON"
case WM_GETMINMAXINFO:
return "WM_GETMINMAXINFO"
case WM_GETTEXT:
return "WM_GETTEXT"
case WM_GETTEXTLENGTH:
return "WM_GETTEXTLENGTH"
case WM_HANDHELDFIRST:
return "WM_HANDHELDFIRST"
case WM_HANDHELDLAST:
return "WM_HANDHELDLAST"
case WM_HELP:
return "WM_HELP"
case WM_HOTKEY:
return "WM_HOTKEY"
case WM_HSCROLL:
return "WM_HSCROLL"
case WM_HSCROLLCLIPBOARD:
return "WM_HSCROLLCLIPBOARD"
case WM_ICONERASEBKGND:
return "WM_ICONERASEBKGND"
case WM_INITDIALOG:
return "WM_INITDIALOG"
case WM_INITMENU:
return "WM_INITMENU"
case WM_INITMENUPOPUP:
return "WM_INITMENUPOPUP"
case WM_INPUT:
return "WM_INPUT"
case WM_INPUTLANGCHANGE:
return "WM_INPUTLANGCHANGE"
case WM_INPUTLANGCHANGEREQUEST:
return "WM_INPUTLANGCHANGEREQUEST"
case WM_KEYDOWN:
return "WM_KEYDOWN"
case WM_KEYUP:
return "WM_KEYUP"
case WM_KILLFOCUS:
return "WM_KILLFOCUS"
case WM_MDIACTIVATE:
return "WM_MDIACTIVATE"
case WM_MDICASCADE:
return "WM_MDICASCADE"
case WM_MDICREATE:
return "WM_MDICREATE"
case WM_MDIDESTROY:
return "WM_MDIDESTROY"
case WM_MDIGETACTIVE:
return "WM_MDIGETACTIVE"
case WM_MDIICONARRANGE:
return "WM_MDIICONARRANGE"
case WM_MDIMAXIMIZE:
return "WM_MDIMAXIMIZE"
case WM_MDINEXT:
return "WM_MDINEXT"
case WM_MDIREFRESHMENU:
return "WM_MDIREFRESHMENU"
case WM_MDIRESTORE:
return "WM_MDIRESTORE"
case WM_MDISETMENU:
return "WM_MDISETMENU"
case WM_MDITILE:
return "WM_MDITILE"
case WM_MEASUREITEM:
return "WM_MEASUREITEM"
case WM_GETOBJECT:
return "WM_GETOBJECT"
case WM_CHANGEUISTATE:
return "WM_CHANGEUISTATE"
case WM_UPDATEUISTATE:
return "WM_UPDATEUISTATE"
case WM_QUERYUISTATE:
return "WM_QUERYUISTATE"
case WM_UNINITMENUPOPUP:
return "WM_UNINITMENUPOPUP"
case WM_MENURBUTTONUP:
return "WM_MENURBUTTONUP"
case WM_MENUCOMMAND:
return "WM_MENUCOMMAND"
case WM_MENUGETOBJECT:
return "WM_MENUGETOBJECT"
case WM_MENUDRAG:
return "WM_MENUDRAG"
case WM_APPCOMMAND:
return "WM_APPCOMMAND"
case WM_MENUCHAR:
return "WM_MENUCHAR"
case WM_MENUSELECT:
return "WM_MENUSELECT"
case WM_MOVE:
return "WM_MOVE"
case WM_MOVING:
return "WM_MOVING"
case WM_NCACTIVATE:
return "WM_NCACTIVATE"
case WM_NCCALCSIZE:
return "WM_NCCALCSIZE"
case WM_NCCREATE:
return "WM_NCCREATE"
case WM_NCDESTROY:
return "WM_NCDESTROY"
case WM_NCHITTEST:
return "WM_NCHITTEST"
case WM_NCLBUTTONDBLCLK:
return "WM_NCLBUTTONDBLCLK"
case WM_NCLBUTTONDOWN:
return "WM_NCLBUTTONDOWN"
case WM_NCLBUTTONUP:
return "WM_NCLBUTTONUP"
case WM_NCMBUTTONDBLCLK:
return "WM_NCMBUTTONDBLCLK"
case WM_NCMBUTTONDOWN:
return "WM_NCMBUTTONDOWN"
case WM_NCMBUTTONUP:
return "WM_NCMBUTTONUP"
case WM_NCXBUTTONDOWN:
return "WM_NCXBUTTONDOWN"
case WM_NCXBUTTONUP:
return "WM_NCXBUTTONUP"
case WM_NCXBUTTONDBLCLK:
return "WM_NCXBUTTONDBLCLK"
case WM_NCMOUSEHOVER:
return "WM_NCMOUSEHOVER"
case WM_NCMOUSELEAVE:
return "WM_NCMOUSELEAVE"
case WM_NCMOUSEMOVE:
return "WM_NCMOUSEMOVE"
case WM_NCPAINT:
return "WM_NCPAINT"
case WM_NCRBUTTONDBLCLK:
return "WM_NCRBUTTONDBLCLK"
case WM_NCRBUTTONDOWN:
return "WM_NCRBUTTONDOWN"
case WM_NCRBUTTONUP:
return "WM_NCRBUTTONUP"
case WM_NEXTDLGCTL:
return "WM_NEXTDLGCTL"
case WM_NEXTMENU:
return "WM_NEXTMENU"
case WM_NOTIFY:
return "WM_NOTIFY"
case WM_NOTIFYFORMAT:
return "WM_NOTIFYFORMAT"
case WM_NULL:
return "WM_NULL"
case WM_PAINT:
return "WM_PAINT"
case WM_PAINTCLIPBOARD:
return "WM_PAINTCLIPBOARD"
case WM_PAINTICON:
return "WM_PAINTICON"
case WM_PALETTECHANGED:
return "WM_PALETTECHANGED"
case WM_PALETTEISCHANGING:
return "WM_PALETTEISCHANGING"
case WM_PARENTNOTIFY:
return "WM_PARENTNOTIFY"
case WM_PASTE:
return "WM_PASTE"
case WM_PENWINFIRST:
return "WM_PENWINFIRST"
case WM_PENWINLAST:
return "WM_PENWINLAST"
case WM_POWER:
return "WM_POWER"
case WM_PRINT:
return "WM_PRINT"
case WM_PRINTCLIENT:
return "WM_PRINTCLIENT"
case WM_QUERYDRAGICON:
return "WM_QUERYDRAGICON"
case WM_QUERYENDSESSION:
return "WM_QUERYENDSESSION"
case WM_QUERYNEWPALETTE:
return "WM_QUERYNEWPALETTE"
case WM_QUERYOPEN:
return "WM_QUERYOPEN"
case WM_QUEUESYNC:
return "WM_QUEUESYNC"
case WM_QUIT:
return "WM_QUIT"
case WM_RENDERALLFORMATS:
return "WM_RENDERALLFORMATS"
case WM_RENDERFORMAT:
return "WM_RENDERFORMAT"
case WM_SETCURSOR:
return "WM_SETCURSOR"
case WM_SETFOCUS:
return "WM_SETFOCUS"
case WM_SETFONT:
return "WM_SETFONT"
case WM_SETHOTKEY:
return "WM_SETHOTKEY"
case WM_SETICON:
return "WM_SETICON"
case WM_SETREDRAW:
return "WM_SETREDRAW"
case WM_SETTEXT:
return "WM_SETTEXT"
case WM_SETTINGCHANGE:
return "WM_SETTINGCHANGE"
case WM_SHOWWINDOW:
return "WM_SHOWWINDOW"
case WM_SIZE:
return "WM_SIZE"
case WM_SIZECLIPBOARD:
return "WM_SIZECLIPBOARD"
case WM_SIZING:
return "WM_SIZING"
case WM_SPOOLERSTATUS:
return "WM_SPOOLERSTATUS"
case WM_STYLECHANGED:
return "WM_STYLECHANGED"
case WM_STYLECHANGING:
return "WM_STYLECHANGING"
case WM_SYSCHAR:
return "WM_SYSCHAR"
case WM_SYSCOLORCHANGE:
return "WM_SYSCOLORCHANGE"
case WM_SYSCOMMAND:
return "WM_SYSCOMMAND"
case WM_SYSDEADCHAR:
return "WM_SYSDEADCHAR"
case WM_SYSKEYDOWN:
return "WM_SYSKEYDOWN"
case WM_SYSKEYUP:
return "WM_SYSKEYUP"
case WM_TCARD:
return "WM_TCARD"
case WM_THEMECHANGED:
return "WM_THEMECHANGED"
case WM_TIMECHANGE:
return "WM_TIMECHANGE"
case WM_TIMER:
return "WM_TIMER"
case WM_UNDO:
return "WM_UNDO"
case WM_USER:
return "WM_USER"
case WM_USERCHANGED:
return "WM_USERCHANGED"
case WM_VKEYTOITEM:
return "WM_VKEYTOITEM"
case WM_VSCROLL:
return "WM_VSCROLL"
case WM_VSCROLLCLIPBOARD:
return "WM_VSCROLLCLIPBOARD"
case WM_WINDOWPOSCHANGED:
return "WM_WINDOWPOSCHANGED"
case WM_WINDOWPOSCHANGING:
return "WM_WINDOWPOSCHANGING"
case WM_KEYLAST:
return "WM_KEYLAST"
case WM_SYNCPAINT:
return "WM_SYNCPAINT"
case WM_MOUSEACTIVATE:
return "WM_MOUSEACTIVATE"
case WM_MOUSEMOVE:
return "WM_MOUSEMOVE"
case WM_LBUTTONDOWN:
return "WM_LBUTTONDOWN"
case WM_LBUTTONUP:
return "WM_LBUTTONUP"
case WM_LBUTTONDBLCLK:
return "WM_LBUTTONDBLCLK"
case WM_RBUTTONDOWN:
return "WM_RBUTTONDOWN"
case WM_RBUTTONUP:
return "WM_RBUTTONUP"
case WM_RBUTTONDBLCLK:
return "WM_RBUTTONDBLCLK"
case WM_MBUTTONDOWN:
return "WM_MBUTTONDOWN"
case WM_MBUTTONUP:
return "WM_MBUTTONUP"
case WM_MBUTTONDBLCLK:
return "WM_MBUTTONDBLCLK"
case WM_MOUSEWHEEL:
return "WM_MOUSEWHEEL"
case WM_XBUTTONDOWN:
return "WM_XBUTTONDOWN"
case WM_XBUTTONUP:
return "WM_XBUTTONUP"
case WM_MOUSELAST:
return "WM_MOUSELAST"
case WM_MOUSEHOVER:
return "WM_MOUSEHOVER"
case WM_MOUSELEAVE:
return "WM_MOUSELEAVE"
case WM_CLIPBOARDUPDATE:
return "WM_CLIPBOARDUPDATE"
default:
return fmt.Sprintf("0x%08x", msg)
}
}
+49
View File
@@ -1,4 +1,5 @@
//go:build windows
package w32
import (
@@ -6,6 +7,7 @@ import (
"github.com/samber/lo"
"log"
"strconv"
"sync"
"syscall"
"unsafe"
)
@@ -162,3 +164,50 @@ func GetMonitorInfoForWindow(hwnd HWND) *MONITORINFO {
GetMonitorInfo(currentMonitor, &info)
return &info
}
type WindowProc func(hwnd HWND, msg uint32, wParam, lParam uintptr) uintptr
var windowClasses = make(map[string]HINSTANCE)
var windowClassesLock sync.Mutex
func getWindowClass(name string) (HINSTANCE, bool) {
windowClassesLock.Lock()
defer windowClassesLock.Unlock()
result, exists := windowClasses[name]
return result, exists
}
func setWindowClass(name string, instance HINSTANCE) {
windowClassesLock.Lock()
defer windowClassesLock.Unlock()
windowClasses[name] = instance
}
func RegisterWindow(name string, proc WindowProc) (HINSTANCE, error) {
classInstance, exists := getWindowClass(name)
if exists {
return classInstance, nil
}
applicationInstance := GetModuleHandle("")
if applicationInstance == 0 {
return 0, fmt.Errorf("get module handle failed")
}
var wc WNDCLASSEX
wc.Size = uint32(unsafe.Sizeof(wc))
wc.WndProc = syscall.NewCallback(proc)
wc.Instance = applicationInstance
wc.Icon = LoadIconWithResourceID(0, uint16(IDI_APPLICATION))
wc.Cursor = LoadCursorWithResourceID(0, uint16(IDC_ARROW))
wc.Background = COLOR_BTNFACE + 1
wc.ClassName = MustStringToUTF16Ptr(name)
atom := RegisterClassEx(&wc)
if atom == 0 {
panic(syscall.GetLastError())
}
setWindowClass(name, applicationInstance)
return applicationInstance, nil
}