From e6ec7f896280e3ebc3fb621cc32ab6a768da1be1 Mon Sep 17 00:00:00 2001 From: Nuno Cruces Date: Thu, 29 Apr 2021 16:38:59 +0100 Subject: [PATCH] WIP: progress (windows). --- entry_windows.go | 4 +- list_windows.go | 4 +- progress_windows.go | 103 +++++++++++++++++++++++++++++++++++++++----- util_windows.go | 19 ++++---- 4 files changed, 106 insertions(+), 24 deletions(-) diff --git a/entry_windows.go b/entry_windows.go index f216270..0c2f604 100644 --- a/entry_windows.go +++ b/entry_windows.go @@ -69,8 +69,8 @@ func entry(text string, opts options) (out string, err error) { layout(dpi(uint32(wparam) >> 16)) default: - ret, _, _ := syscall.Syscall6(defWindowProc, 4, wnd, uintptr(msg), wparam, lparam, 0, 0) - return ret + res, _, _ := syscall.Syscall6(defWindowProc, 4, wnd, uintptr(msg), wparam, lparam, 0, 0) + return res } return 0 diff --git a/list_windows.go b/list_windows.go index 63a82f2..f8efece 100644 --- a/list_windows.go +++ b/list_windows.go @@ -99,8 +99,8 @@ func listDlg(text string, items []string, multiple bool, opts options) (out []st layout(dpi(uint32(wparam) >> 16)) default: - ret, _, _ := syscall.Syscall6(defWindowProc, 4, wnd, uintptr(msg), wparam, lparam, 0, 0) - return ret + res, _, _ := syscall.Syscall6(defWindowProc, 4, wnd, uintptr(msg), wparam, lparam, 0, 0) + return res } return 0 diff --git a/progress_windows.go b/progress_windows.go index 16830c6..c0b46ba 100644 --- a/progress_windows.go +++ b/progress_windows.go @@ -1,6 +1,8 @@ package zenity import ( + "context" + "sync" "syscall" ) @@ -17,7 +19,30 @@ func progress(opts options) (ProgressDialog, error) { if opts.maxValue == 0 { opts.maxValue = 100 } + if opts.ctx == nil { + opts.ctx = context.Background() + } + dlg := &progressDialog{ + done: make(chan struct{}), + max: opts.maxValue, + } + dlg.init.Add(1) + + go func() { + err := progressDlg(opts, dlg) + if cerr := opts.ctx.Err(); cerr != nil { + err = cerr + } + dlg.err = err + close(dlg.done) + }() + + dlg.init.Wait() + return dlg, nil +} + +func progressDlg(opts options, dlg *progressDialog) (err error) { defer setup()() font := getFont() defer font.Delete() @@ -51,6 +76,7 @@ func progress(opts options) (ProgressDialog, error) { postQuitMessage.Call(0) case 0x0010: // WM_CLOSE + err = ErrCanceled destroyWindow.Call(wnd) case 0x0111: // WM_COMMAND @@ -58,8 +84,11 @@ func progress(opts options) (ProgressDialog, error) { default: return 1 case 1, 6: // IDOK, IDYES + // case 2: // IDCANCEL + err = ErrCanceled case 7: // IDNO + err = ErrExtraButton } destroyWindow.Call(wnd) @@ -67,25 +96,25 @@ func progress(opts options) (ProgressDialog, error) { layout(dpi(uint32(wparam) >> 16)) default: - ret, _, _ := syscall.Syscall6(defWindowProc, 4, wnd, uintptr(msg), wparam, lparam, 0, 0) - return ret + res, _, _ := syscall.Syscall6(defWindowProc, 4, wnd, uintptr(msg), wparam, lparam, 0, 0) + return res } return 0 } if opts.ctx != nil && opts.ctx.Err() != nil { - return nil, opts.ctx.Err() + return opts.ctx.Err() } instance, _, err := getModuleHandle.Call(0) if instance == 0 { - return nil, err + return err } cls, err := registerClass(instance, syscall.NewCallback(proc)) if cls == 0 { - return nil, err + return err } defer unregisterClass.Call(cls, instance) @@ -112,7 +141,7 @@ func progress(opts options) (ProgressDialog, error) { okBtn, _, _ = createWindowEx.Call(0, strptr("BUTTON"), strptr(*opts.okLabel), - 0x50030001, // WS_CHILD|WS_VISIBLE|WS_GROUP|WS_TABSTOP|BS_DEFPUSHBUTTON + 0x58030001, // WS_CHILD|WS_VISIBLE|WS_DISABLED|WS_GROUP|WS_TABSTOP|BS_DEFPUSHBUTTON 12, 66, 75, 24, wnd, 1 /* IDOK */, instance, 0) cancelBtn, _, _ = createWindowEx.Call(0, strptr("BUTTON"), strptr(*opts.cancelLabel), @@ -131,9 +160,13 @@ func progress(opts options) (ProgressDialog, error) { if opts.maxValue < 0 { sendMessage.Call(progCtl, 0x40a /* PBM_SETMARQUEE */, 1, 0) } else { - sendMessage.Call(progCtl, 0x402 /* PBM_SETPOS */, 33, 0) sendMessage.Call(progCtl, 0x406 /* PBM_SETRANGE32 */, 0, uintptr(opts.maxValue)) } + dlg.prog = progCtl + dlg.text = textCtl + dlg.ok = okBtn + dlg.wnd = wnd + dlg.init.Done() if opts.ctx != nil { wait := make(chan struct{}) @@ -148,13 +181,61 @@ func progress(opts options) (ProgressDialog, error) { } // set default values - // out, ok, err = "", false, nil + err = nil if err := messageLoop(wnd); err != nil { - return nil, err + return err } if opts.ctx != nil && opts.ctx.Err() != nil { - return nil, opts.ctx.Err() + return opts.ctx.Err() } - return nil, err + return err +} + +type progressDialog struct { + err error + done chan struct{} + init sync.WaitGroup + prog uintptr + text uintptr + wnd uintptr + ok uintptr + max int +} + +func (d *progressDialog) Close() error { + sendMessage.Call(d.wnd, 0x0112 /* WM_SYSCOMMAND */, 0xf060 /* SC_CLOSE */, 0) + <-d.done + return d.err +} + +func (d *progressDialog) Text(text string) error { + select { + default: + setWindowText.Call(d.text, strptr(text)) + return nil + case <-d.done: + return d.err + } +} + +func (d *progressDialog) Value(value int) error { + select { + default: + sendMessage.Call(d.prog, 0x402 /* PBM_SETPOS */, uintptr(value), 0) + if value >= d.max { + enableWindow.Call(d.ok, 1) + } + return nil + case <-d.done: + return d.err + } +} + +func (d *progressDialog) MaxValue() int { + return d.max +} + +func (d *progressDialog) Done() <-chan struct{} { + return d.done } diff --git a/util_windows.go b/util_windows.go index fa2ca3c..0444c0b 100644 --- a/util_windows.go +++ b/util_windows.go @@ -72,6 +72,7 @@ var ( destroyWindow = user32.NewProc("DestroyWindow") createWindowEx = user32.NewProc("CreateWindowExW") showWindow = user32.NewProc("ShowWindow") + enableWindow = user32.NewProc("EnableWindow") setFocus = user32.NewProc("SetFocus") defWindowProc = user32.NewProc("DefWindowProcW") ) @@ -267,8 +268,8 @@ func (f *font) Delete() { func centerWindow(wnd uintptr) { getMetric := func(i uintptr) int32 { - ret, _, _ := getSystemMetrics.Call(i) - return int32(ret) + n, _, _ := getSystemMetrics.Call(i) + return int32(n) } var rect _RECT @@ -295,8 +296,8 @@ func registerClass(instance, proc uintptr) (uintptr, error) { wcx.Background = 5 // COLOR_WINDOW wcx.ClassName = syscall.StringToUTF16Ptr(name) - ret, _, err := registerClassEx.Call(uintptr(unsafe.Pointer(&wcx))) - return ret, err + atom, _, err := registerClassEx.Call(uintptr(unsafe.Pointer(&wcx))) + return atom, err } // https://docs.microsoft.com/en-us/windows/win32/winmsg/using-messages-and-message-queues @@ -308,16 +309,16 @@ func messageLoop(wnd uintptr) error { for { var msg _MSG - ret, _, err := syscall.Syscall6(getMessage, 4, uintptr(unsafe.Pointer(&msg)), 0, 0, 0, 0, 0) - if int32(ret) == -1 { + s, _, err := syscall.Syscall6(getMessage, 4, uintptr(unsafe.Pointer(&msg)), 0, 0, 0, 0, 0) + if int32(s) == -1 { return err } - if ret == 0 { + if s == 0 { return nil } - ret, _, _ = syscall.Syscall(isDialogMessage, 2, wnd, uintptr(unsafe.Pointer(&msg)), 0) - if ret == 0 { + s, _, _ = syscall.Syscall(isDialogMessage, 2, wnd, uintptr(unsafe.Pointer(&msg)), 0) + if s == 0 { syscall.Syscall(translateMessage, 1, uintptr(unsafe.Pointer(&msg)), 0, 0) syscall.Syscall(dispatchMessage, 1, uintptr(unsafe.Pointer(&msg)), 0, 0) }