Parent window id (windows).

This commit is contained in:
Nuno Cruces 2022-06-28 14:52:02 +01:00
parent e6b85bb427
commit 6899d6b87c
21 changed files with 102 additions and 68 deletions

View file

@ -41,7 +41,7 @@ func selectColor(opts options) (color.Color, error) {
args.Flags |= win.CC_FULLOPEN args.Flags |= win.CC_FULLOPEN
} }
defer setup()() defer setup(args.Owner)()
unhook, err := hookDialog(opts.ctx, opts.windowIcon, opts.title, nil) unhook, err := hookDialog(opts.ctx, opts.windowIcon, opts.title, nil)
if err != nil { if err != nil {
return nil, err return nil, err

View file

@ -37,7 +37,8 @@ type calendarDialog struct {
} }
func (dlg *calendarDialog) setup(text string, opts options) (time.Time, error) { func (dlg *calendarDialog) setup(text string, opts options) (time.Time, error) {
defer setup()() owner, _ := opts.attach.(win.HWND)
defer setup(owner)()
dlg.font = getFont() dlg.font = getFont()
defer dlg.font.delete() defer dlg.font.delete()
icon := getIcon(opts.windowIcon) icon := getIcon(opts.windowIcon)
@ -58,7 +59,6 @@ func (dlg *calendarDialog) setup(text string, opts options) (time.Time, error) {
} }
defer win.UnregisterClass(cls, instance) defer win.UnregisterClass(cls, instance)
owner, _ := opts.attach.(win.HWND)
dlg.wnd, _ = win.CreateWindowEx(_WS_EX_ZEN_DIALOG, dlg.wnd, _ = win.CreateWindowEx(_WS_EX_ZEN_DIALOG,
cls, strptr(*opts.title), _WS_ZEN_DIALOG, cls, strptr(*opts.title), _WS_ZEN_DIALOG,
win.CW_USEDEFAULT, win.CW_USEDEFAULT, win.CW_USEDEFAULT, win.CW_USEDEFAULT,

View file

@ -36,7 +36,8 @@ type entryDialog struct {
} }
func (dlg *entryDialog) setup(text string, opts options) (string, error) { func (dlg *entryDialog) setup(text string, opts options) (string, error) {
defer setup()() owner, _ := opts.attach.(win.HWND)
defer setup(owner)()
dlg.font = getFont() dlg.font = getFont()
defer dlg.font.delete() defer dlg.font.delete()
icon := getIcon(opts.windowIcon) icon := getIcon(opts.windowIcon)
@ -57,7 +58,6 @@ func (dlg *entryDialog) setup(text string, opts options) (string, error) {
} }
defer win.UnregisterClass(cls, instance) defer win.UnregisterClass(cls, instance)
owner, _ := opts.attach.(win.HWND)
dlg.wnd, _ = win.CreateWindowEx(_WS_EX_ZEN_DIALOG, dlg.wnd, _ = win.CreateWindowEx(_WS_EX_ZEN_DIALOG,
cls, strptr(*opts.title), _WS_ZEN_DIALOG, cls, strptr(*opts.title), _WS_ZEN_DIALOG,
win.CW_USEDEFAULT, win.CW_USEDEFAULT, win.CW_USEDEFAULT, win.CW_USEDEFAULT,

View file

@ -36,7 +36,7 @@ func selectFile(opts options) (string, error) {
args.MaxFile = uint32(len(res)) args.MaxFile = uint32(len(res))
args.InitialDir, args.DefExt = initDirNameExt(opts.filename, res[:]) args.InitialDir, args.DefExt = initDirNameExt(opts.filename, res[:])
defer setup()() defer setup(args.Owner)()
unhook, err := hookDialog(opts.ctx, opts.windowIcon, nil, nil) unhook, err := hookDialog(opts.ctx, opts.windowIcon, nil, nil)
if err != nil { if err != nil {
return "", err return "", err
@ -79,7 +79,7 @@ func selectFileMultiple(opts options) ([]string, error) {
args.MaxFile = uint32(len(res)) args.MaxFile = uint32(len(res))
args.InitialDir, args.DefExt = initDirNameExt(opts.filename, res[:]) args.InitialDir, args.DefExt = initDirNameExt(opts.filename, res[:])
defer setup()() defer setup(args.Owner)()
unhook, err := hookDialog(opts.ctx, opts.windowIcon, nil, nil) unhook, err := hookDialog(opts.ctx, opts.windowIcon, nil, nil)
if err != nil { if err != nil {
return nil, err return nil, err
@ -153,7 +153,7 @@ func selectFileSave(opts options) (string, error) {
args.MaxFile = uint32(len(res)) args.MaxFile = uint32(len(res))
args.InitialDir, args.DefExt = initDirNameExt(opts.filename, res[:]) args.InitialDir, args.DefExt = initDirNameExt(opts.filename, res[:])
defer setup()() defer setup(args.Owner)()
unhook, err := hookDialog(opts.ctx, opts.windowIcon, nil, nil) unhook, err := hookDialog(opts.ctx, opts.windowIcon, nil, nil)
if err != nil { if err != nil {
return "", err return "", err
@ -171,7 +171,8 @@ func selectFileSave(opts options) (string, error) {
} }
func pickFolders(opts options, multi bool) (string, []string, error) { func pickFolders(opts options, multi bool) (string, []string, error) {
defer setup()() owner, _ := opts.attach.(win.HWND)
defer setup(owner)()
err := win.CoInitializeEx(0, win.COINIT_APARTMENTTHREADED|win.COINIT_DISABLE_OLE1DDE) err := win.CoInitializeEx(0, win.COINIT_APARTMENTTHREADED|win.COINIT_DISABLE_OLE1DDE)
if err != win.RPC_E_CHANGED_MODE { if err != win.RPC_E_CHANGED_MODE {
@ -230,7 +231,6 @@ func pickFolders(opts options, multi bool) (string, []string, error) {
defer unhook() defer unhook()
} }
owner, _ := opts.attach.(win.HWND)
err = dialog.Show(owner) err = dialog.Show(owner)
if opts.ctx != nil && opts.ctx.Err() != nil { if opts.ctx != nil && opts.ctx.Err() != nil {
return "", nil, opts.ctx.Err() return "", nil, opts.ctx.Err()

View file

@ -3,6 +3,7 @@
package win package win
const ( const (
// InitCommonControlsEx flags
ICC_LISTVIEW_CLASSES = 0x00000001 ICC_LISTVIEW_CLASSES = 0x00000001
ICC_TREEVIEW_CLASSES = 0x00000002 ICC_TREEVIEW_CLASSES = 0x00000002
ICC_BAR_CLASSES = 0x00000004 ICC_BAR_CLASSES = 0x00000004

View file

@ -9,6 +9,7 @@ import (
) )
const ( const (
// ChooseColor flags
CC_RGBINIT = 0x00000001 CC_RGBINIT = 0x00000001
CC_FULLOPEN = 0x00000002 CC_FULLOPEN = 0x00000002
CC_PREVENTFULLOPEN = 0x00000004 CC_PREVENTFULLOPEN = 0x00000004

View file

@ -5,6 +5,7 @@ package win
import "golang.org/x/sys/windows" import "golang.org/x/sys/windows"
const ( const (
// CreateActCtx flags
ACTCTX_FLAG_PROCESSOR_ARCHITECTURE_VALID = 0x001 ACTCTX_FLAG_PROCESSOR_ARCHITECTURE_VALID = 0x001
ACTCTX_FLAG_LANGID_VALID = 0x002 ACTCTX_FLAG_LANGID_VALID = 0x002
ACTCTX_FLAG_ASSEMBLY_DIRECTORY_VALID = 0x004 ACTCTX_FLAG_ASSEMBLY_DIRECTORY_VALID = 0x004
@ -12,6 +13,13 @@ const (
ACTCTX_FLAG_SET_PROCESS_DEFAULT = 0x010 ACTCTX_FLAG_SET_PROCESS_DEFAULT = 0x010
ACTCTX_FLAG_APPLICATION_NAME_VALID = 0x020 ACTCTX_FLAG_APPLICATION_NAME_VALID = 0x020
ACTCTX_FLAG_HMODULE_VALID = 0x080 ACTCTX_FLAG_HMODULE_VALID = 0x080
// Control signals
CTRL_C_EVENT = 0
CTRL_BREAK_EVENT = 1
CTRL_CLOSE_EVENT = 2
CTRL_LOGOFF_EVENT = 5
CTRL_SHUTDOWN_EVENT = 6
) )
// https://docs.microsoft.com/en-us/windows/win32/api/winbase/ns-winbase-actctxw // https://docs.microsoft.com/en-us/windows/win32/api/winbase/ns-winbase-actctxw
@ -33,6 +41,7 @@ func GetSystemDirectory() (string, error) { return windows.GetSystemDirectory()
//sys ActivateActCtx(actCtx Handle, cookie *uintptr) (err error) = kernel32.ActivateActCtx //sys ActivateActCtx(actCtx Handle, cookie *uintptr) (err error) = kernel32.ActivateActCtx
//sys CreateActCtx(actCtx *ACTCTX) (ret Handle, err error) = kernel32.CreateActCtxW //sys CreateActCtx(actCtx *ACTCTX) (ret Handle, err error) = kernel32.CreateActCtxW
//sys DeactivateActCtx(flags uint32, cookie uintptr) (err error) = kernel32.DeactivateActCtx //sys DeactivateActCtx(flags uint32, cookie uintptr) (err error) = kernel32.DeactivateActCtx
//sys GenerateConsoleCtrlEvent(ctrlEvent uint32, processGroupId int) (err error) = kernel32.GenerateConsoleCtrlEvent
//sys GetConsoleWindow() (ret HWND) = kernel32.GetConsoleWindow //sys GetConsoleWindow() (ret HWND) = kernel32.GetConsoleWindow
//sys GetModuleHandle(moduleName *uint16) (ret Handle, err error) = kernel32.GetModuleHandleW //sys GetModuleHandle(moduleName *uint16) (ret Handle, err error) = kernel32.GetModuleHandleW
//sys ReleaseActCtx(actCtx Handle) = kernel32.ReleaseActCtx //sys ReleaseActCtx(actCtx Handle) = kernel32.ReleaseActCtx

View file

@ -71,6 +71,7 @@ const (
FOS_FORCEPREVIEWPANEON = 0x40000000 FOS_FORCEPREVIEWPANEON = 0x40000000
FOS_SUPPORTSTREAMABLEITEMS = 0x80000000 FOS_SUPPORTSTREAMABLEITEMS = 0x80000000
// IShellItem.GetDisplayName forms
SIGDN_NORMALDISPLAY = 0x00000000 SIGDN_NORMALDISPLAY = 0x00000000
SIGDN_PARENTRELATIVEPARSING = ^(^0x18001 + 0x80000000) SIGDN_PARENTRELATIVEPARSING = ^(^0x18001 + 0x80000000)
SIGDN_DESKTOPABSOLUTEPARSING = ^(^0x28000 + 0x80000000) SIGDN_DESKTOPABSOLUTEPARSING = ^(^0x28000 + 0x80000000)

View file

@ -58,6 +58,7 @@ var (
procActivateActCtx = modkernel32.NewProc("ActivateActCtx") procActivateActCtx = modkernel32.NewProc("ActivateActCtx")
procCreateActCtxW = modkernel32.NewProc("CreateActCtxW") procCreateActCtxW = modkernel32.NewProc("CreateActCtxW")
procDeactivateActCtx = modkernel32.NewProc("DeactivateActCtx") procDeactivateActCtx = modkernel32.NewProc("DeactivateActCtx")
procGenerateConsoleCtrlEvent = modkernel32.NewProc("GenerateConsoleCtrlEvent")
procGetConsoleWindow = modkernel32.NewProc("GetConsoleWindow") procGetConsoleWindow = modkernel32.NewProc("GetConsoleWindow")
procGetModuleHandleW = modkernel32.NewProc("GetModuleHandleW") procGetModuleHandleW = modkernel32.NewProc("GetModuleHandleW")
procReleaseActCtx = modkernel32.NewProc("ReleaseActCtx") procReleaseActCtx = modkernel32.NewProc("ReleaseActCtx")
@ -179,6 +180,14 @@ func DeactivateActCtx(flags uint32, cookie uintptr) (err error) {
return return
} }
func GenerateConsoleCtrlEvent(ctrlEvent uint32, processGroupId int) (err error) {
r1, _, e1 := syscall.Syscall(procGenerateConsoleCtrlEvent.Addr(), 2, uintptr(ctrlEvent), uintptr(processGroupId), 0)
if r1 == 0 {
err = errnoErr(e1)
}
return
}
func GetConsoleWindow() (ret HWND) { func GetConsoleWindow() (ret HWND) {
r0, _, _ := syscall.Syscall(procGetConsoleWindow.Addr(), 0, 0, 0, 0) r0, _, _ := syscall.Syscall(procGetConsoleWindow.Addr(), 0, 0, 0, 0)
ret = HWND(r0) ret = HWND(r0)

View file

@ -1,4 +1,8 @@
package zencmd package zencmd
import "github.com/ncruces/zenity/internal/win"
// KillParent is internal. // KillParent is internal.
func KillParent() {} func KillParent() {
win.GenerateConsoleCtrlEvent(win.CTRL_BREAK_EVENT, 0)
}

View file

@ -3,6 +3,7 @@ package zencmd
import ( import (
"strconv" "strconv"
"github.com/ncruces/zenity/internal/win"
"golang.org/x/sys/windows" "golang.org/x/sys/windows"
) )
@ -13,6 +14,6 @@ func ParseWindowId(id string) windows.HWND {
} }
// GetParentWindowId is internal. // GetParentWindowId is internal.
func GetParentWindowId(pid int) int { func GetParentWindowId(pid int) windows.HWND {
return 0 return win.GetConsoleWindow()
} }

View file

@ -53,7 +53,8 @@ type listDialog struct {
} }
func (dlg *listDialog) setup(text string, opts options) ([]string, error) { func (dlg *listDialog) setup(text string, opts options) ([]string, error) {
defer setup()() owner, _ := opts.attach.(win.HWND)
defer setup(owner)()
dlg.font = getFont() dlg.font = getFont()
defer dlg.font.delete() defer dlg.font.delete()
icon := getIcon(opts.windowIcon) icon := getIcon(opts.windowIcon)
@ -74,7 +75,6 @@ func (dlg *listDialog) setup(text string, opts options) ([]string, error) {
} }
defer win.UnregisterClass(cls, instance) defer win.UnregisterClass(cls, instance)
owner, _ := opts.attach.(win.HWND)
dlg.wnd, _ = win.CreateWindowEx(_WS_EX_ZEN_DIALOG, dlg.wnd, _ = win.CreateWindowEx(_WS_EX_ZEN_DIALOG,
cls, strptr(*opts.title), _WS_ZEN_DIALOG, cls, strptr(*opts.title), _WS_ZEN_DIALOG,
win.CW_USEDEFAULT, win.CW_USEDEFAULT, win.CW_USEDEFAULT, win.CW_USEDEFAULT,

View file

@ -52,7 +52,8 @@ func message(kind messageKind, text string, opts options) error {
} }
} }
defer setup()() owner, _ := opts.attach.(win.HWND)
defer setup(owner)()
unhook, err := hookMessageDialog(opts) unhook, err := hookMessageDialog(opts)
if err != nil { if err != nil {
return err return err
@ -64,7 +65,6 @@ func message(kind messageKind, text string, opts options) error {
title = strptr(*opts.title) title = strptr(*opts.title)
} }
owner, _ := opts.attach.(win.HWND)
s, err := win.MessageBox(owner, strptr(text), title, flags) s, err := win.MessageBox(owner, strptr(text), title, flags)
if opts.ctx != nil && opts.ctx.Err() != nil { if opts.ctx != nil && opts.ctx.Err() != nil {

View file

@ -116,7 +116,8 @@ func (dlg *progressDialog) setup(opts options) error {
var once sync.Once var once sync.Once
defer once.Do(dlg.init.Done) defer once.Do(dlg.init.Done)
defer setup()() owner, _ := opts.attach.(win.HWND)
defer setup(owner)()
dlg.font = getFont() dlg.font = getFont()
defer dlg.font.delete() defer dlg.font.delete()
icon := getIcon(opts.windowIcon) icon := getIcon(opts.windowIcon)
@ -137,7 +138,6 @@ func (dlg *progressDialog) setup(opts options) error {
} }
defer win.UnregisterClass(cls, instance) defer win.UnregisterClass(cls, instance)
owner, _ := opts.attach.(win.HWND)
dlg.wnd, _ = win.CreateWindowEx(_WS_EX_ZEN_DIALOG, dlg.wnd, _ = win.CreateWindowEx(_WS_EX_ZEN_DIALOG,
cls, strptr(*opts.title), _WS_ZEN_DIALOG, cls, strptr(*opts.title), _WS_ZEN_DIALOG,
win.CW_USEDEFAULT, win.CW_USEDEFAULT, win.CW_USEDEFAULT, win.CW_USEDEFAULT,

View file

@ -46,7 +46,8 @@ type passwordDialog struct {
} }
func (dlg *passwordDialog) setup(opts options) (string, string, error) { func (dlg *passwordDialog) setup(opts options) (string, string, error) {
defer setup()() owner, _ := opts.attach.(win.HWND)
defer setup(owner)()
dlg.font = getFont() dlg.font = getFont()
defer dlg.font.delete() defer dlg.font.delete()
icon := getIcon(opts.windowIcon) icon := getIcon(opts.windowIcon)
@ -67,7 +68,6 @@ func (dlg *passwordDialog) setup(opts options) (string, string, error) {
} }
defer win.UnregisterClass(cls, instance) defer win.UnregisterClass(cls, instance)
owner, _ := opts.attach.(win.HWND)
dlg.wnd, _ = win.CreateWindowEx(_WS_EX_ZEN_DIALOG, dlg.wnd, _ = win.CreateWindowEx(_WS_EX_ZEN_DIALOG,
cls, strptr(*opts.title), _WS_ZEN_DIALOG, cls, strptr(*opts.title), _WS_ZEN_DIALOG,
win.CW_USEDEFAULT, win.CW_USEDEFAULT, win.CW_USEDEFAULT, win.CW_USEDEFAULT,

View file

@ -5,7 +5,6 @@ package zenity
import ( import (
"bytes" "bytes"
"os/exec" "os/exec"
"reflect"
"strconv" "strconv"
"strings" "strings"
@ -99,10 +98,6 @@ func pwdResult(sep string, opts options, out []byte, err error) (string, string,
return "", str, err return "", str, err
} }
func hwnd(v reflect.Value) uintptr {
return uintptr(v.Uint())
}
// Replace with strings.Cut after 1.18. // Replace with strings.Cut after 1.18.
func cut(s, sep string) (before, after string, found bool) { func cut(s, sep string) (before, after string, found bool) {
if i := strings.Index(s, sep); i >= 0 { if i := strings.Index(s, sep); i >= 0 {

View file

@ -4,7 +4,6 @@ import (
"bytes" "bytes"
"context" "context"
"os" "os"
"reflect"
"runtime" "runtime"
"strconv" "strconv"
"sync" "sync"
@ -26,13 +25,10 @@ const (
func intptr(i int64) uintptr { return uintptr(i) } func intptr(i int64) uintptr { return uintptr(i) }
func strptr(s string) *uint16 { return syscall.StringToUTF16Ptr(s) } func strptr(s string) *uint16 { return syscall.StringToUTF16Ptr(s) }
func hwnd(v reflect.Value) win.HWND { func setup(wnd win.HWND) context.CancelFunc {
return win.HWND(uintptr(v.Uint())) if wnd == 0 {
} win.EnumWindows(syscall.NewCallback(setupEnumCallback), unsafe.Pointer(&wnd))
}
func setup() context.CancelFunc {
var wnd win.HWND
win.EnumWindows(syscall.NewCallback(setupEnumCallback), unsafe.Pointer(&wnd))
if wnd == 0 { if wnd == 0 {
wnd = win.GetConsoleWindow() wnd = win.GetConsoleWindow()
} }

View file

@ -13,8 +13,6 @@ package zenity
import ( import (
"context" "context"
"image/color" "image/color"
"reflect"
"runtime"
"time" "time"
"github.com/ncruces/zenity/internal/zenutil" "github.com/ncruces/zenity/internal/zenutil"
@ -191,38 +189,6 @@ func CustomIcon(path string) Option {
return Icon(path) return Icon(path)
} }
// Attach returns an Option to set the parent window to attach to.
//
// Attach accepts:
// - a window id (int) on Unix
// - a window handle (~uintptr) on Windows
// - an application name (string) or process id (int) on macOS
func Attach(id any) Option {
switch runtime.GOOS {
case "windows":
if v := reflect.ValueOf(id); v.Kind() == reflect.Uintptr {
id = hwnd(v)
} else {
panic("interface conversion: expected uintptr")
}
case "darwin":
switch id.(type) {
case int, string:
default:
panic("interface conversion: expected int or string")
}
default:
switch id.(type) {
case int:
default:
panic("interface conversion: expected int")
}
}
return funcOption(func(o *options) { o.attach = id })
}
// Modal returns an Option to set the modal hint. // Modal returns an Option to set the modal hint.
func Modal() Option { func Modal() Option {
return funcOption(func(o *options) { o.modal = true }) return funcOption(func(o *options) { o.modal = true })

16
zenity_darwin.go Normal file
View file

@ -0,0 +1,16 @@
package zenity
// Attach returns an Option to set the parent window to attach to.
//
// Attach accepts:
// - a window id (int) on Unix
// - a window handle (~uintptr) on Windows
// - an application name (string) or process id (int) on macOS
func Attach(id any) Option {
switch id.(type) {
case int, string:
default:
panic("interface conversion: expected int or string")
}
return funcOption(func(o *options) { o.attach = id })
}

13
zenity_unix.go Normal file
View file

@ -0,0 +1,13 @@
//go:build !windows && !darwin
package zenity
// Attach returns an Option to set the parent window to attach to.
//
// Attach accepts:
// - a window id (int) on Unix
// - a window handle (~uintptr) on Windows
// - an application name (string) or process id (int) on macOS
func Attach(id int) Option {
return funcOption(func(o *options) { o.attach = id })
}

22
zenity_windows.go Normal file
View file

@ -0,0 +1,22 @@
package zenity
import (
"reflect"
"github.com/ncruces/zenity/internal/win"
)
// Attach returns an Option to set the parent window to attach to.
//
// Attach accepts:
// - a window id (int) on Unix
// - a window handle (~uintptr) on Windows
// - an application name (string) or process id (int) on macOS
func Attach(id any) Option {
if v := reflect.ValueOf(id); v.Kind() == reflect.Uintptr {
id = win.HWND(uintptr(v.Uint()))
} else {
panic("interface conversion: expected uintptr")
}
return funcOption(func(o *options) { o.attach = id })
}