From 205db7d4be9232108f9e375b45b5f44425bfd973 Mon Sep 17 00:00:00 2001 From: Nuno Cruces Date: Tue, 24 May 2022 00:12:38 +0100 Subject: [PATCH] Window icons (windows). --- date_windows.go | 4 +++- entry_windows.go | 4 +++- list_windows.go | 4 +++- notify_windows.go | 10 ++++++++++ progress_windows.go | 15 ++++++--------- pwd_windows.go | 4 +++- util_windows.go | 30 ++++++++++++++++++++---------- 7 files changed, 48 insertions(+), 23 deletions(-) diff --git a/date_windows.go b/date_windows.go index 0a7b983..4748739 100644 --- a/date_windows.go +++ b/date_windows.go @@ -38,6 +38,8 @@ func (dlg *calendarDialog) setup(text string, opts options) (time.Time, error) { defer setup()() dlg.font = getFont() defer dlg.font.delete() + icon := getIcon(opts.windowIcon) + defer icon.delete() if opts.ctx != nil && opts.ctx.Err() != nil { return time.Time{}, opts.ctx.Err() @@ -48,7 +50,7 @@ func (dlg *calendarDialog) setup(text string, opts options) (time.Time, error) { return time.Time{}, err } - cls, err := registerClass(instance, syscall.NewCallback(calendarProc)) + cls, err := registerClass(instance, icon.handle, syscall.NewCallback(calendarProc)) if cls == 0 { return time.Time{}, err } diff --git a/entry_windows.go b/entry_windows.go index 7fc0e18..0038cfa 100644 --- a/entry_windows.go +++ b/entry_windows.go @@ -37,6 +37,8 @@ func (dlg *entryDialog) setup(text string, opts options) (string, error) { defer setup()() dlg.font = getFont() defer dlg.font.delete() + icon := getIcon(opts.windowIcon) + defer icon.delete() if opts.ctx != nil && opts.ctx.Err() != nil { return "", opts.ctx.Err() @@ -47,7 +49,7 @@ func (dlg *entryDialog) setup(text string, opts options) (string, error) { return "", err } - cls, err := registerClass(instance, syscall.NewCallback(entryProc)) + cls, err := registerClass(instance, icon.handle, syscall.NewCallback(entryProc)) if cls == 0 { return "", err } diff --git a/list_windows.go b/list_windows.go index e95bb94..fc2cdeb 100644 --- a/list_windows.go +++ b/list_windows.go @@ -54,6 +54,8 @@ func (dlg *listDialog) setup(text string, opts options) ([]string, error) { defer setup()() dlg.font = getFont() defer dlg.font.delete() + icon := getIcon(opts.windowIcon) + defer icon.delete() if opts.ctx != nil && opts.ctx.Err() != nil { return nil, opts.ctx.Err() @@ -64,7 +66,7 @@ func (dlg *listDialog) setup(text string, opts options) ([]string, error) { return nil, err } - cls, err := registerClass(instance, syscall.NewCallback(listProc)) + cls, err := registerClass(instance, icon.handle, syscall.NewCallback(listProc)) if cls == 0 { return nil, err } diff --git a/notify_windows.go b/notify_windows.go index e017443..c335943 100644 --- a/notify_windows.go +++ b/notify_windows.go @@ -42,6 +42,16 @@ func notify(text string, opts options) error { args.InfoFlags |= 0x2 // NIIF_WARNING case ErrorIcon: args.InfoFlags |= 0x3 // NIIF_ERROR + case NoIcon: + // + default: + icon := getIcon(opts.icon) + if icon.handle != 0 { + defer icon.delete() + args.Icon = icon.handle + args.Flags |= 0x00000002 // NIF_ICON + args.InfoFlags |= 0x4 // NIIF_USER + } } runtime.LockOSThread() diff --git a/progress_windows.go b/progress_windows.go index 23b1e8e..f2aa241 100644 --- a/progress_windows.go +++ b/progress_windows.go @@ -111,16 +111,14 @@ func (d *progressDialog) Close() error { } func (dlg *progressDialog) setup(opts options) error { - done := false - defer func() { - if !done { - dlg.init.Done() - } - }() + var once sync.Once + defer once.Do(dlg.init.Done) defer setup()() dlg.font = getFont() defer dlg.font.delete() + icon := getIcon(opts.windowIcon) + defer icon.delete() if opts.ctx != nil && opts.ctx.Err() != nil { return opts.ctx.Err() @@ -131,7 +129,7 @@ func (dlg *progressDialog) setup(opts options) error { return err } - cls, err := registerClass(instance, syscall.NewCallback(progressProc)) + cls, err := registerClass(instance, icon.handle, syscall.NewCallback(progressProc)) if cls == 0 { return err } @@ -182,8 +180,7 @@ func (dlg *progressDialog) setup(opts options) error { } else { sendMessage.Call(dlg.progCtl, _PBM_SETRANGE32, 0, uintptr(opts.maxValue)) } - dlg.init.Done() - done = true + once.Do(dlg.init.Done) if opts.ctx != nil { wait := make(chan struct{}) diff --git a/pwd_windows.go b/pwd_windows.go index 0af8504..9f85423 100644 --- a/pwd_windows.go +++ b/pwd_windows.go @@ -47,6 +47,8 @@ func (dlg *passwordDialog) setup(opts options) (string, string, error) { defer setup()() dlg.font = getFont() defer dlg.font.delete() + icon := getIcon(opts.windowIcon) + defer icon.delete() if opts.ctx != nil && opts.ctx.Err() != nil { return "", "", opts.ctx.Err() @@ -57,7 +59,7 @@ func (dlg *passwordDialog) setup(opts options) (string, string, error) { return "", "", err } - cls, err := registerClass(instance, syscall.NewCallback(passwordProc)) + cls, err := registerClass(instance, icon.handle, syscall.NewCallback(passwordProc)) if cls == 0 { return "", "", err } diff --git a/util_windows.go b/util_windows.go index d854991..cfdc602 100644 --- a/util_windows.go +++ b/util_windows.go @@ -180,7 +180,7 @@ type dialogHook struct { func newDialogHook(ctx context.Context, icon any, title *string, init func(wnd uintptr)) (*dialogHook, error) { tid, _, _ := getCurrentThreadId.Call() - hk, _, err := setWindowsHookEx.Call(5, // WH_CBT + hk, _, err := setWindowsHookEx.Call(12, // WH_CALLWNDPROCRET syscall.NewCallback(dialogHookProc), 0, tid) if hk == 0 { return nil, err @@ -203,31 +203,31 @@ func newDialogHook(ctx context.Context, icon any, title *string, init func(wnd u return &hook, nil } -func dialogHookProc(code int32, wparam, lparam uintptr) uintptr { - if code == 5 { // HCBT_ACTIVATE +func dialogHookProc(code int32, wparam uintptr, lparam *_CWPRETSTRUCT) uintptr { + if lparam.Message == 0x0110 { // WM_INITDIALOG tid, _, _ := getCurrentThreadId.Call() hook := (*dialogHook)(loadBackRef(tid)) - atomic.StoreUintptr(&hook.wnd, wparam) + atomic.StoreUintptr(&hook.wnd, lparam.Wnd) if hook.ctx != nil && hook.ctx.Err() != nil { - sendMessage.Call(wparam, _WM_SYSCOMMAND, _SC_CLOSE, 0) + sendMessage.Call(lparam.Wnd, _WM_SYSCOMMAND, _SC_CLOSE, 0) } else { if hook.icon != nil { icon := getIcon(hook.icon) if icon.handle != 0 { defer icon.delete() - sendMessage.Call(wparam, _WM_SETICON, 0, icon.handle) + sendMessage.Call(lparam.Wnd, _WM_SETICON, 0, icon.handle) } } if hook.title != nil { - setWindowText.Call(wparam, strptr(*hook.title)) + setWindowText.Call(lparam.Wnd, strptr(*hook.title)) } if hook.init != nil { - hook.init(wparam) + hook.init(lparam.Wnd) } } } next, _, _ := callNextHookEx.Call( - 0, uintptr(code), wparam, lparam) + 0, uintptr(code), wparam, uintptr(unsafe.Pointer(lparam))) return next } @@ -408,12 +408,13 @@ func getWindowString(wnd uintptr) string { return syscall.UTF16ToString(buf) } -func registerClass(instance, proc uintptr) (uintptr, error) { +func registerClass(instance, icon, proc uintptr) (uintptr, error) { name := "WC_" + strconv.FormatUint(uint64(proc), 16) var wcx _WNDCLASSEX wcx.Size = uint32(unsafe.Sizeof(wcx)) wcx.WndProc = proc + wcx.Icon = icon wcx.Instance = instance wcx.Background = 5 // COLOR_WINDOW wcx.ClassName = syscall.StringToUTF16Ptr(name) @@ -487,6 +488,15 @@ type _INITCOMMONCONTROLSEX struct { ICC uint32 } +// https://docs.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-cwpretstruct +type _CWPRETSTRUCT struct { + Result uintptr + LParam uintptr + WParam uintptr + Message uint32 + Wnd uintptr +} + // https://docs.microsoft.com/en-us/windows/win32/api/wingdi/ns-wingdi-logfontw type _LOGFONT struct { Height int32