Timeouts, cancellation (windows).
This commit is contained in:
parent
71d3120393
commit
975c82db2a
5 changed files with 139 additions and 55 deletions
|
@ -46,15 +46,18 @@ func selectColor(options []Option) (color.Color, error) {
|
||||||
runtime.LockOSThread()
|
runtime.LockOSThread()
|
||||||
defer runtime.UnlockOSThread()
|
defer runtime.UnlockOSThread()
|
||||||
|
|
||||||
if opts.title != "" {
|
if opts.ctx != nil || opts.title != "" {
|
||||||
hook, err := hookDialogTitle(opts.title)
|
unhook, err := hookDialogTitle(opts.ctx, opts.title)
|
||||||
if hook == 0 {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
defer unhookWindowsHookEx.Call(hook)
|
defer unhook()
|
||||||
}
|
}
|
||||||
|
|
||||||
s, _, _ := chooseColor.Call(uintptr(unsafe.Pointer(&args)))
|
s, _, _ := chooseColor.Call(uintptr(unsafe.Pointer(&args)))
|
||||||
|
if opts.ctx != nil && opts.ctx.Err() != nil {
|
||||||
|
return nil, opts.ctx.Err()
|
||||||
|
}
|
||||||
if s == 0 {
|
if s == 0 {
|
||||||
return nil, commDlgError()
|
return nil, commDlgError()
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,7 +46,18 @@ func selectFile(options []Option) (string, error) {
|
||||||
runtime.LockOSThread()
|
runtime.LockOSThread()
|
||||||
defer runtime.UnlockOSThread()
|
defer runtime.UnlockOSThread()
|
||||||
|
|
||||||
|
if opts.ctx != nil {
|
||||||
|
unhook, err := hookDialog(opts.ctx, nil)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
defer unhook()
|
||||||
|
}
|
||||||
|
|
||||||
s, _, _ := getOpenFileName.Call(uintptr(unsafe.Pointer(&args)))
|
s, _, _ := getOpenFileName.Call(uintptr(unsafe.Pointer(&args)))
|
||||||
|
if opts.ctx != nil && opts.ctx.Err() != nil {
|
||||||
|
return "", opts.ctx.Err()
|
||||||
|
}
|
||||||
if s == 0 {
|
if s == 0 {
|
||||||
return "", commDlgError()
|
return "", commDlgError()
|
||||||
}
|
}
|
||||||
|
@ -82,7 +93,18 @@ func selectFileMutiple(options []Option) ([]string, error) {
|
||||||
runtime.LockOSThread()
|
runtime.LockOSThread()
|
||||||
defer runtime.UnlockOSThread()
|
defer runtime.UnlockOSThread()
|
||||||
|
|
||||||
|
if opts.ctx != nil {
|
||||||
|
unhook, err := hookDialog(opts.ctx, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer unhook()
|
||||||
|
}
|
||||||
|
|
||||||
s, _, _ := getOpenFileName.Call(uintptr(unsafe.Pointer(&args)))
|
s, _, _ := getOpenFileName.Call(uintptr(unsafe.Pointer(&args)))
|
||||||
|
if opts.ctx != nil && opts.ctx.Err() != nil {
|
||||||
|
return nil, opts.ctx.Err()
|
||||||
|
}
|
||||||
if s == 0 {
|
if s == 0 {
|
||||||
return nil, commDlgError()
|
return nil, commDlgError()
|
||||||
}
|
}
|
||||||
|
@ -149,7 +171,18 @@ func selectFileSave(options []Option) (string, error) {
|
||||||
runtime.LockOSThread()
|
runtime.LockOSThread()
|
||||||
defer runtime.UnlockOSThread()
|
defer runtime.UnlockOSThread()
|
||||||
|
|
||||||
|
if opts.ctx != nil {
|
||||||
|
unhook, err := hookDialog(opts.ctx, nil)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
defer unhook()
|
||||||
|
}
|
||||||
|
|
||||||
s, _, _ := getSaveFileName.Call(uintptr(unsafe.Pointer(&args)))
|
s, _, _ := getSaveFileName.Call(uintptr(unsafe.Pointer(&args)))
|
||||||
|
if opts.ctx != nil && opts.ctx.Err() != nil {
|
||||||
|
return "", opts.ctx.Err()
|
||||||
|
}
|
||||||
if s == 0 {
|
if s == 0 {
|
||||||
return "", commDlgError()
|
return "", commDlgError()
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package zenity
|
package zenity
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"runtime"
|
"runtime"
|
||||||
"syscall"
|
"syscall"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
|
@ -41,21 +42,24 @@ func message(kind messageKind, text string, options []Option) (bool, error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if opts.okLabel != "" || opts.cancelLabel != "" || opts.extraButton != "" {
|
if opts.ctx != nil || opts.okLabel != "" || opts.cancelLabel != "" || opts.extraButton != "" {
|
||||||
runtime.LockOSThread()
|
runtime.LockOSThread()
|
||||||
defer runtime.UnlockOSThread()
|
defer runtime.UnlockOSThread()
|
||||||
|
|
||||||
hook, err := hookMessageLabels(kind, opts)
|
unhook, err := hookMessageLabels(kind, opts)
|
||||||
if hook == 0 {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
defer unhookWindowsHookEx.Call(hook)
|
defer unhook()
|
||||||
}
|
}
|
||||||
|
|
||||||
s, _, err := messageBox.Call(0,
|
s, _, err := messageBox.Call(0,
|
||||||
uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(text))),
|
uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(text))),
|
||||||
uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(opts.title))), flags)
|
uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(opts.title))), flags)
|
||||||
|
|
||||||
|
if opts.ctx != nil && opts.ctx.Err() != nil {
|
||||||
|
return false, opts.ctx.Err()
|
||||||
|
}
|
||||||
if s == 0 {
|
if s == 0 {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
@ -68,17 +72,11 @@ func message(kind messageKind, text string, options []Option) (bool, error) {
|
||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func hookMessageLabels(kind messageKind, opts options) (hook uintptr, err error) {
|
func hookMessageLabels(kind messageKind, opts options) (unhook context.CancelFunc, err error) {
|
||||||
tid, _, _ := getCurrentThreadId.Call()
|
return hookDialog(opts.ctx, func(wnd uintptr) {
|
||||||
hook, _, err = setWindowsHookEx.Call(12, // WH_CALLWNDPROCRET
|
enumChildWindows.Call(wnd,
|
||||||
syscall.NewCallback(func(code int, wparam uintptr, lparam *_CWPRETSTRUCT) uintptr {
|
|
||||||
if lparam.Message == 0x0110 { // WM_INITDIALOG
|
|
||||||
name := [7]uint16{}
|
|
||||||
getClassName.Call(lparam.Wnd, uintptr(unsafe.Pointer(&name)), uintptr(len(name)))
|
|
||||||
if syscall.UTF16ToString(name[:]) == "#32770" { // The class for a dialog box
|
|
||||||
enumChildWindows.Call(lparam.Wnd,
|
|
||||||
syscall.NewCallback(func(wnd, lparam uintptr) uintptr {
|
syscall.NewCallback(func(wnd, lparam uintptr) uintptr {
|
||||||
name := [7]uint16{}
|
name := [8]uint16{}
|
||||||
getClassName.Call(wnd, uintptr(unsafe.Pointer(&name)), uintptr(len(name)))
|
getClassName.Call(wnd, uintptr(unsafe.Pointer(&name)), uintptr(len(name)))
|
||||||
if syscall.UTF16ToString(name[:]) == "Button" {
|
if syscall.UTF16ToString(name[:]) == "Button" {
|
||||||
ctl, _, _ := getDlgCtrlID.Call(wnd)
|
ctl, _, _ := getDlgCtrlID.Call(wnd)
|
||||||
|
@ -104,10 +102,5 @@ func hookMessageLabels(kind messageKind, opts options) (hook uintptr, err error)
|
||||||
}
|
}
|
||||||
return 1
|
return 1
|
||||||
}), 0)
|
}), 0)
|
||||||
}
|
})
|
||||||
}
|
|
||||||
next, _, _ := callNextHookEx.Call(hook, uintptr(code), wparam, uintptr(unsafe.Pointer(lparam)))
|
|
||||||
return next
|
|
||||||
}), 0, tid)
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
package zenity
|
package zenity
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"sync"
|
||||||
"syscall"
|
"syscall"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
)
|
)
|
||||||
|
@ -49,22 +51,73 @@ type _CWPRETSTRUCT struct {
|
||||||
Wnd uintptr
|
Wnd uintptr
|
||||||
}
|
}
|
||||||
|
|
||||||
func hookDialogTitle(title string) (hook uintptr, err error) {
|
func hookDialog(ctx context.Context, initDialog func(wnd uintptr)) (unhook context.CancelFunc, err error) {
|
||||||
|
if ctx != nil && ctx.Err() != nil {
|
||||||
|
return nil, ctx.Err()
|
||||||
|
}
|
||||||
|
|
||||||
|
var mtx sync.Mutex
|
||||||
|
var hook, wnd uintptr
|
||||||
tid, _, _ := getCurrentThreadId.Call()
|
tid, _, _ := getCurrentThreadId.Call()
|
||||||
hook, _, err = setWindowsHookEx.Call(12, // WH_CALLWNDPROCRET
|
hook, _, err = setWindowsHookEx.Call(12, // WH_CALLWNDPROCRET
|
||||||
syscall.NewCallback(func(code int, wparam uintptr, lparam *_CWPRETSTRUCT) uintptr {
|
syscall.NewCallback(func(code int, wparam uintptr, lparam *_CWPRETSTRUCT) uintptr {
|
||||||
if lparam.Message == 0x0110 { // WM_INITDIALOG
|
if lparam.Message == 0x0110 { // WM_INITDIALOG
|
||||||
name := [7]uint16{}
|
name := [8]uint16{}
|
||||||
getClassName.Call(lparam.Wnd, uintptr(unsafe.Pointer(&name)), uintptr(len(name)))
|
getClassName.Call(lparam.Wnd, uintptr(unsafe.Pointer(&name)), uintptr(len(name)))
|
||||||
if syscall.UTF16ToString(name[:]) == "#32770" { // The class for a dialog box
|
if syscall.UTF16ToString(name[:]) == "#32770" { // The class for a dialog box
|
||||||
ptr := syscall.StringToUTF16Ptr(title)
|
var close bool
|
||||||
setWindowText.Call(lparam.Wnd, uintptr(unsafe.Pointer(ptr)))
|
|
||||||
|
mtx.Lock()
|
||||||
|
if ctx != nil && ctx.Err() != nil {
|
||||||
|
close = true
|
||||||
|
} else {
|
||||||
|
wnd = lparam.Wnd
|
||||||
|
}
|
||||||
|
mtx.Unlock()
|
||||||
|
|
||||||
|
if close {
|
||||||
|
sendMessage.Call(lparam.Wnd, 0x0112 /* WM_SYSCOMMAND */, 0xf060 /* SC_CLOSE */, 0)
|
||||||
|
} else if initDialog != nil {
|
||||||
|
initDialog(lparam.Wnd)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
next, _, _ := callNextHookEx.Call(hook, uintptr(code), wparam, uintptr(unsafe.Pointer(lparam)))
|
next, _, _ := callNextHookEx.Call(hook, uintptr(code), wparam, uintptr(unsafe.Pointer(lparam)))
|
||||||
return next
|
return next
|
||||||
}), 0, tid)
|
}), 0, tid)
|
||||||
return
|
|
||||||
|
if hook == 0 {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if ctx == nil {
|
||||||
|
return func() { unhookWindowsHookEx.Call(hook) }, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
wait := make(chan struct{})
|
||||||
|
go func() {
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
mtx.Lock()
|
||||||
|
w := wnd
|
||||||
|
mtx.Unlock()
|
||||||
|
|
||||||
|
if w != 0 {
|
||||||
|
sendMessage.Call(w, 0x0112 /* WM_SYSCOMMAND */, 0xf060 /* SC_CLOSE */, 0)
|
||||||
|
}
|
||||||
|
case <-wait:
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
return func() {
|
||||||
|
unhookWindowsHookEx.Call(hook)
|
||||||
|
close(wait)
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func hookDialogTitle(ctx context.Context, title string) (unhook context.CancelFunc, err error) {
|
||||||
|
return hookDialog(ctx, func(wnd uintptr) {
|
||||||
|
ptr := syscall.StringToUTF16Ptr(title)
|
||||||
|
setWindowText.Call(wnd, uintptr(unsafe.Pointer(ptr)))
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
type _COMObject struct{}
|
type _COMObject struct{}
|
||||||
|
|
|
@ -73,7 +73,7 @@ func Title(title string) Option {
|
||||||
// DialogIcon is the enumeration for dialog icons.
|
// DialogIcon is the enumeration for dialog icons.
|
||||||
type DialogIcon int
|
type DialogIcon int
|
||||||
|
|
||||||
// Icons for
|
// The stock dialog icons.
|
||||||
const (
|
const (
|
||||||
ErrorIcon DialogIcon = iota + 1
|
ErrorIcon DialogIcon = iota + 1
|
||||||
WarningIcon
|
WarningIcon
|
||||||
|
@ -87,6 +87,8 @@ func Icon(icon DialogIcon) Option {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Context returns an Option to set a Context that can dismiss the dialog.
|
// Context returns an Option to set a Context that can dismiss the dialog.
|
||||||
|
//
|
||||||
|
// Dialogs dismissed by the Context return Context.Err.
|
||||||
func Context(ctx context.Context) Option {
|
func Context(ctx context.Context) Option {
|
||||||
return funcOption(func(o *options) { o.ctx = ctx })
|
return funcOption(func(o *options) { o.ctx = ctx })
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue