diff --git a/color_windows.go b/color_windows.go index 32306fb..4b98420 100644 --- a/color_windows.go +++ b/color_windows.go @@ -42,14 +42,11 @@ func selectColor(opts options) (color.Color, error) { } defer setup()() - - if opts.ctx != nil || opts.title != nil { - unhook, err := hookDialog(opts.ctx, opts.windowIcon, opts.title, nil) - if err != nil { - return nil, err - } - defer unhook() + unhook, err := hookDialog(opts.ctx, opts.windowIcon, opts.title, nil) + if err != nil { + return nil, err } + defer unhook() ok := win.ChooseColor(&args) if opts.ctx != nil && opts.ctx.Err() != nil { diff --git a/entry_windows.go b/entry_windows.go index 982baae..0c54628 100644 --- a/entry_windows.go +++ b/entry_windows.go @@ -163,7 +163,7 @@ func entryProc(wnd win.HWND, msg uint32, wparam uintptr, lparam *unsafe.Pointer) default: return 1 case win.IDOK, win.IDYES: - dlg.out = getWindowString(dlg.editCtl) + dlg.out = win.GetWindowText(dlg.editCtl) case win.IDCANCEL: dlg.err = ErrCanceled case win.IDNO: diff --git a/file_windows.go b/file_windows.go index ced30f7..5977ad8 100644 --- a/file_windows.go +++ b/file_windows.go @@ -37,14 +37,11 @@ func selectFile(opts options) (string, error) { args.InitialDir, args.DefExt = initDirNameExt(opts.filename, res[:]) defer setup()() - - if opts.ctx != nil { - unhook, err := hookDialog(opts.ctx, opts.windowIcon, nil, nil) - if err != nil { - return "", err - } - defer unhook() + unhook, err := hookDialog(opts.ctx, opts.windowIcon, nil, nil) + if err != nil { + return "", err } + defer unhook() ok := win.GetOpenFileName(&args) if opts.ctx != nil && opts.ctx.Err() != nil { @@ -83,14 +80,11 @@ func selectFileMultiple(opts options) ([]string, error) { args.InitialDir, args.DefExt = initDirNameExt(opts.filename, res[:]) defer setup()() - - if opts.ctx != nil { - unhook, err := hookDialog(opts.ctx, opts.windowIcon, nil, nil) - if err != nil { - return nil, err - } - defer unhook() + unhook, err := hookDialog(opts.ctx, opts.windowIcon, nil, nil) + if err != nil { + return nil, err } + defer unhook() ok := win.GetOpenFileName(&args) if opts.ctx != nil && opts.ctx.Err() != nil { @@ -160,14 +154,11 @@ func selectFileSave(opts options) (string, error) { args.InitialDir, args.DefExt = initDirNameExt(opts.filename, res[:]) defer setup()() - - if opts.ctx != nil { - unhook, err := hookDialog(opts.ctx, opts.windowIcon, nil, nil) - if err != nil { - return "", err - } - defer unhook() + unhook, err := hookDialog(opts.ctx, opts.windowIcon, nil, nil) + if err != nil { + return "", err } + defer unhook() ok := win.GetSaveFileName(&args) if opts.ctx != nil && opts.ctx.Err() != nil { @@ -231,7 +222,7 @@ func pickFolders(opts options, multi bool) (string, []string, error) { } } - if opts.ctx != nil { + if opts.ctx != nil || opts.windowIcon != nil { unhook, err := hookDialog(opts.ctx, opts.windowIcon, nil, nil) if err != nil { return "", nil, err @@ -302,7 +293,7 @@ func browseForFolder(opts options) (string, []string, error) { args.CallbackFunc = syscall.NewCallback(browseForFolderCallback) } - if opts.ctx != nil { + if opts.ctx != nil || opts.windowIcon != nil { unhook, err := hookDialog(opts.ctx, opts.windowIcon, nil, nil) if err != nil { return "", nil, err diff --git a/internal/win/user32.go b/internal/win/user32.go index ad48156..bf52b0a 100644 --- a/internal/win/user32.go +++ b/internal/win/user32.go @@ -268,6 +268,13 @@ func GetWindowThreadProcessId(hwnd HWND, pid *uint32) (tid uint32, err error) { return windows.GetWindowThreadProcessId(hwnd, pid) } +func GetWindowText(wnd HWND) string { + len, _ := getWindowTextLength(wnd) + buf := make([]uint16, len+1) + getWindowText(wnd, &buf[0], len+1) + return syscall.UTF16ToString(buf) +} + func SendMessagePointer(wnd HWND, msg uint32, wparam uintptr, lparam unsafe.Pointer) (ret uintptr) { r0, _, _ := syscall.Syscall6(procSendMessageW.Addr(), 4, uintptr(wnd), uintptr(msg), uintptr(wparam), uintptr(lparam), 0, 0) ret = uintptr(r0) @@ -389,16 +396,15 @@ type CWPRETSTRUCT struct { //sys DestroyWindow(wnd HWND) (err error) = user32.DestroyWindow //sys DispatchMessage(msg *MSG) (ret uintptr) = user32.DispatchMessageW //sys EnableWindow(wnd HWND, enable bool) (ok bool) = user32.EnableWindow -//sys EnumChildWindows(parent HWND, enumFunc uintptr, lparam unsafe.Pointer) = user32.EnumChildWindows //sys EnumWindows(enumFunc uintptr, lparam unsafe.Pointer) (err error) = user32.EnumChildWindows -//sys GetDlgCtrlID(wnd HWND) (ret int) = user32.GetDlgCtrlID +//sys GetDlgItem(dlg HWND, dlgItemID int) (ret HWND, err error) = user32.GetDlgItem //sys getDpiForWindow(wnd HWND) (ret int) = user32.GetDpiForWindow //sys GetMessage(msg *MSG, wnd HWND, msgFilterMin uint32, msgFilterMax uint32) (ret uintptr) = user32.GetMessageW //sys GetSystemMetrics(index int) (ret int) = user32.GetSystemMetrics //sys GetWindowDC(wnd HWND) (ret Handle) = user32.GetWindowDC //sys GetWindowRect(wnd HWND, cmdShow *RECT) (err error) = user32.GetWindowRect -//sys GetWindowText(wnd HWND, str *uint16, maxCount int) (ret int, err error) = user32.GetWindowTextW -//sys GetWindowTextLength(wnd HWND) (ret int, err error) = user32.GetWindowTextLengthW +//sys getWindowText(wnd HWND, str *uint16, maxCount int) (ret int, err error) = user32.GetWindowTextW +//sys getWindowTextLength(wnd HWND) (ret int, err error) = user32.GetWindowTextLengthW //sys IsDialogMessage(wnd HWND, msg *MSG) (ok bool) = user32.IsDialogMessageW //sys LoadIcon(instance Handle, resource uintptr) (ret Handle, err error) = user32.LoadIconW //sys LoadImage(instance Handle, name *uint16, typ int, cx int, cy int, load int) (ret Handle, err error) = user32.LoadImageW @@ -406,6 +412,7 @@ type CWPRETSTRUCT struct { //sys RegisterClassEx(cls *WNDCLASSEX) (err error) = user32.RegisterClassExW //sys ReleaseDC(wnd HWND, dc Handle) (ok bool) = user32.ReleaseDC //sys SendMessage(wnd HWND, msg uint32, wparam uintptr, lparam uintptr) (ret uintptr) = user32.SendMessageW +//sys SetDlgItemText(dlg HWND, dlgItemID int, str *uint16) (err error) = user32.SetDlgItemTextW //sys SetFocus(wnd HWND) (ret HWND, err error) = user32.SetFocus //sys SetForegroundWindow(wnd HWND) (ok bool) = user32.SetForegroundWindow //sys setThreadDpiAwarenessContext(dpiContext uintptr) (ret uintptr) = user32.SetThreadDpiAwarenessContext diff --git a/internal/win/zsyscall_windows.go b/internal/win/zsyscall_windows.go index 25cce39..4ac75dd 100644 --- a/internal/win/zsyscall_windows.go +++ b/internal/win/zsyscall_windows.go @@ -75,7 +75,7 @@ var ( procDispatchMessageW = moduser32.NewProc("DispatchMessageW") procEnableWindow = moduser32.NewProc("EnableWindow") procEnumChildWindows = moduser32.NewProc("EnumChildWindows") - procGetDlgCtrlID = moduser32.NewProc("GetDlgCtrlID") + procGetDlgItem = moduser32.NewProc("GetDlgItem") procGetDpiForWindow = moduser32.NewProc("GetDpiForWindow") procGetMessageW = moduser32.NewProc("GetMessageW") procGetSystemMetrics = moduser32.NewProc("GetSystemMetrics") @@ -90,6 +90,7 @@ var ( procRegisterClassExW = moduser32.NewProc("RegisterClassExW") procReleaseDC = moduser32.NewProc("ReleaseDC") procSendMessageW = moduser32.NewProc("SendMessageW") + procSetDlgItemTextW = moduser32.NewProc("SetDlgItemTextW") procSetFocus = moduser32.NewProc("SetFocus") procSetForegroundWindow = moduser32.NewProc("SetForegroundWindow") procSetThreadDpiAwarenessContext = moduser32.NewProc("SetThreadDpiAwarenessContext") @@ -305,11 +306,6 @@ func EnableWindow(wnd HWND, enable bool) (ok bool) { return } -func EnumChildWindows(parent HWND, enumFunc uintptr, lparam unsafe.Pointer) { - syscall.Syscall(procEnumChildWindows.Addr(), 3, uintptr(parent), uintptr(enumFunc), uintptr(lparam)) - return -} - func EnumWindows(enumFunc uintptr, lparam unsafe.Pointer) (err error) { r1, _, e1 := syscall.Syscall(procEnumChildWindows.Addr(), 2, uintptr(enumFunc), uintptr(lparam), 0) if r1 == 0 { @@ -318,9 +314,12 @@ func EnumWindows(enumFunc uintptr, lparam unsafe.Pointer) (err error) { return } -func GetDlgCtrlID(wnd HWND) (ret int) { - r0, _, _ := syscall.Syscall(procGetDlgCtrlID.Addr(), 1, uintptr(wnd), 0, 0) - ret = int(r0) +func GetDlgItem(dlg HWND, dlgItemID int) (ret HWND, err error) { + r0, _, e1 := syscall.Syscall(procGetDlgItem.Addr(), 2, uintptr(dlg), uintptr(dlgItemID), 0) + ret = HWND(r0) + if ret == 0 { + err = errnoErr(e1) + } return } @@ -356,7 +355,7 @@ func GetWindowRect(wnd HWND, cmdShow *RECT) (err error) { return } -func GetWindowTextLength(wnd HWND) (ret int, err error) { +func getWindowTextLength(wnd HWND) (ret int, err error) { r0, _, e1 := syscall.Syscall(procGetWindowTextLengthW.Addr(), 1, uintptr(wnd), 0, 0) ret = int(r0) if ret == 0 { @@ -365,7 +364,7 @@ func GetWindowTextLength(wnd HWND) (ret int, err error) { return } -func GetWindowText(wnd HWND, str *uint16, maxCount int) (ret int, err error) { +func getWindowText(wnd HWND, str *uint16, maxCount int) (ret int, err error) { r0, _, e1 := syscall.Syscall(procGetWindowTextW.Addr(), 3, uintptr(wnd), uintptr(unsafe.Pointer(str)), uintptr(maxCount)) ret = int(r0) if ret == 0 { @@ -423,6 +422,14 @@ func SendMessage(wnd HWND, msg uint32, wparam uintptr, lparam uintptr) (ret uint return } +func SetDlgItemText(dlg HWND, dlgItemID int, str *uint16) (err error) { + r1, _, e1 := syscall.Syscall(procSetDlgItemTextW.Addr(), 3, uintptr(dlg), uintptr(dlgItemID), uintptr(unsafe.Pointer(str))) + if r1 == 0 { + err = errnoErr(e1) + } + return +} + func SetFocus(wnd HWND) (ret HWND, err error) { r0, _, e1 := syscall.Syscall(procSetFocus.Addr(), 1, uintptr(wnd), 0, 0) ret = HWND(r0) diff --git a/msg_windows.go b/msg_windows.go index 46b483f..988311f 100644 --- a/msg_windows.go +++ b/msg_windows.go @@ -2,8 +2,6 @@ package zenity import ( "context" - "syscall" - "unsafe" "github.com/ncruces/zenity/internal/win" ) @@ -55,14 +53,11 @@ func message(kind messageKind, text string, opts options) error { } defer setup()() - - if opts.ctx != nil || opts.okLabel != nil || opts.cancelLabel != nil || opts.extraButton != nil || opts.icon != nil { - unhook, err := hookMessageDialog(opts) - if err != nil { - return err - } - defer unhook() + unhook, err := hookMessageDialog(opts) + if err != nil { + return err } + defer unhook() var title *uint16 if opts.title != nil { @@ -88,35 +83,27 @@ func message(kind messageKind, text string, opts options) error { } func hookMessageDialog(opts options) (unhook context.CancelFunc, err error) { - // TODO: use GetDlgItem, SetDlgItemText instead of EnumChildWindows. - return hookDialog(opts.ctx, opts.windowIcon, nil, func(wnd win.HWND) { - win.EnumChildWindows(wnd, syscall.NewCallback(hookMessageDialogCallback), - unsafe.Pointer(&opts)) - }) -} + var init func(wnd win.HWND) + if opts.okLabel != nil || opts.cancelLabel != nil || opts.extraButton != nil || opts.icon != nil { + init = func(wnd win.HWND) { + if opts.okLabel != nil { + win.SetDlgItemText(wnd, win.IDOK, strptr(*opts.okLabel)) + win.SetDlgItemText(wnd, win.IDYES, strptr(*opts.okLabel)) + } + if opts.cancelLabel != nil { + win.SetDlgItemText(wnd, win.IDCANCEL, strptr(*opts.cancelLabel)) + } + if opts.extraButton != nil { + win.SetDlgItemText(wnd, win.IDNO, strptr(*opts.extraButton)) + } -func hookMessageDialogCallback(wnd win.HWND, lparam *options) uintptr { - ctl := win.GetDlgCtrlID(wnd) - - var text *string - switch ctl { - case win.IDOK, win.IDYES: - text = lparam.okLabel - case win.IDCANCEL: - text = lparam.cancelLabel - case win.IDNO: - text = lparam.extraButton - } - if text != nil { - win.SetWindowText(wnd, strptr(*text)) - } - - if ctl == win.IDC_STATIC_OK { - icon := getIcon(lparam.icon) - if icon.handle != 0 { - defer icon.delete() - win.SendMessage(wnd, win.STM_SETICON, uintptr(icon.handle), 0) + icon := getIcon(opts.icon) + if icon.handle != 0 { + defer icon.delete() + ctl, _ := win.GetDlgItem(wnd, win.IDC_STATIC_OK) + win.SendMessage(ctl, win.STM_SETICON, uintptr(icon.handle), 0) + } } } - return 1 + return hookDialog(opts.ctx, opts.windowIcon, nil, init) } diff --git a/pwd_windows.go b/pwd_windows.go index adfa726..4315dab 100644 --- a/pwd_windows.go +++ b/pwd_windows.go @@ -183,8 +183,8 @@ func passwordProc(wnd win.HWND, msg uint32, wparam uintptr, lparam *unsafe.Point default: return 1 case win.IDOK, win.IDYES: - dlg.usr = getWindowString(dlg.uEditCtl) - dlg.pwd = getWindowString(dlg.pEditCtl) + dlg.usr = win.GetWindowText(dlg.uEditCtl) + dlg.pwd = win.GetWindowText(dlg.pEditCtl) case win.IDCANCEL: dlg.err = ErrCanceled case win.IDNO: diff --git a/util_windows.go b/util_windows.go index e97b8cf..e7e61cb 100644 --- a/util_windows.go +++ b/util_windows.go @@ -79,6 +79,9 @@ func setupEnumCallback(wnd win.HWND, lparam *win.HWND) uintptr { } func hookDialog(ctx context.Context, icon any, title *string, init func(wnd win.HWND)) (unhook context.CancelFunc, err error) { + if ctx == nil && icon == nil && init == nil { + return func() {}, nil + } if ctx != nil && ctx.Err() != nil { return nil, ctx.Err() } @@ -314,13 +317,6 @@ func centerWindow(wnd win.HWND) { win.SetWindowPos(wnd, 0, x/2, y/2, 0, 0, win.SWP_NOZORDER|win.SWP_NOSIZE) } -func getWindowString(wnd win.HWND) string { - len, _ := win.GetWindowTextLength(wnd) - buf := make([]uint16, len+1) - win.GetWindowText(wnd, &buf[0], len+1) - return syscall.UTF16ToString(buf) -} - func registerClass(instance, icon win.Handle, proc uintptr) (*uint16, error) { name := "WC_" + strconv.FormatUint(uint64(proc), 16)