Refactor (windows).

This commit is contained in:
Nuno Cruces 2022-06-22 00:58:14 +01:00
parent 6928e74895
commit e2dc86414f
7 changed files with 265 additions and 169 deletions

View file

@ -89,11 +89,11 @@ func (dlg *calendarDialog) setup(text string, opts options) (time.Time, error) {
} }
if opts.time != nil { if opts.time != nil {
var date _SYSTEMTIME var date win.SYSTEMTIME
year, month, day := opts.time.Date() year, month, day := opts.time.Date()
date.year = uint16(year) date.Year = uint16(year)
date.month = uint16(month) date.Month = uint16(month)
date.day = uint16(day) date.Day = uint16(day)
win.SendMessagePointer(dlg.dateCtl, win.MCM_SETCURSEL, 0, unsafe.Pointer(&date)) win.SendMessagePointer(dlg.dateCtl, win.MCM_SETCURSEL, 0, unsafe.Pointer(&date))
} }
@ -168,9 +168,9 @@ func calendarProc(wnd win.HWND, msg uint32, wparam uintptr, lparam *unsafe.Point
default: default:
return 1 return 1
case win.IDOK, win.IDYES: case win.IDOK, win.IDYES:
var date _SYSTEMTIME var date win.SYSTEMTIME
win.SendMessagePointer(dlg.dateCtl, win.MCM_GETCURSEL, 0, unsafe.Pointer(&date)) win.SendMessagePointer(dlg.dateCtl, win.MCM_GETCURSEL, 0, unsafe.Pointer(&date))
dlg.out = time.Date(int(date.year), time.Month(date.month), int(date.day), 0, 0, 0, 0, time.UTC) dlg.out = time.Date(int(date.Year), time.Month(date.Month), int(date.Day), 0, 0, 0, 0, time.UTC)
case win.IDCANCEL: case win.IDCANCEL:
dlg.err = ErrCanceled dlg.err = ErrCanceled
case win.IDNO: case win.IDNO:

View file

@ -3,7 +3,6 @@ package zenity
import ( import (
"fmt" "fmt"
"path/filepath" "path/filepath"
"reflect"
"syscall" "syscall"
"unicode/utf16" "unicode/utf16"
"unsafe" "unsafe"
@ -201,22 +200,21 @@ func pickFolders(opts options, multi bool) (str string, lst []string, err error)
defer win.CoUninitialize() defer win.CoUninitialize()
} }
var dialog *_IFileOpenDialog var dialog *win.IFileOpenDialog
err = win.CoCreateInstance( err = win.CoCreateInstance(
_CLSID_FileOpenDialog, nil, win.CLSCTX_ALL, win.CLSID_FileOpenDialog, nil, win.CLSCTX_ALL,
_IID_IFileOpenDialog, unsafe.Pointer(&dialog)) win.IID_IFileOpenDialog, unsafe.Pointer(&dialog))
if err != nil { if err != nil {
if multi { if multi {
return "", nil, fmt.Errorf("%w: multiple directory", ErrUnsupported) return "", nil, fmt.Errorf("%w: multiple directory", ErrUnsupported)
} }
return browseForFolder(opts) return browseForFolder(opts)
} }
defer dialog.Call(dialog.Release) defer dialog.Release()
var flgs int flgs, err := dialog.GetOptions()
hr, _, _ := dialog.Call(dialog.GetOptions, uintptr(unsafe.Pointer(&flgs))) if err != nil {
if int32(hr) < 0 { return "", nil, err
return "", nil, syscall.Errno(hr)
} }
flgs |= _FOS_NOCHANGEDIR | _FOS_PICKFOLDERS | _FOS_FORCEFILESYSTEM flgs |= _FOS_NOCHANGEDIR | _FOS_PICKFOLDERS | _FOS_FORCEFILESYSTEM
if multi { if multi {
@ -225,24 +223,21 @@ func pickFolders(opts options, multi bool) (str string, lst []string, err error)
if opts.showHidden { if opts.showHidden {
flgs |= _FOS_FORCESHOWHIDDEN flgs |= _FOS_FORCESHOWHIDDEN
} }
hr, _, _ = dialog.Call(dialog.SetOptions, uintptr(flgs)) err = dialog.SetOptions(flgs)
if int32(hr) < 0 { if err != nil {
return "", nil, syscall.Errno(hr) return "", nil, err
} }
if opts.title != nil { if opts.title != nil {
ptr := strptr(*opts.title) dialog.SetTitle(strptr(*opts.title))
dialog.Call(dialog.SetTitle, uintptr(unsafe.Pointer(ptr)))
} }
if opts.filename != "" { if opts.filename != "" {
var item *win.IShellItem var item *win.IShellItem
ptr := strptr(opts.filename) win.SHCreateItemFromParsingName(strptr(opts.filename), nil, win.IID_IShellItem, &item)
win.SHCreateItemFromParsingName(ptr, nil, _IID_IShellItem, &item) if item != nil {
dialog.SetFolder(item)
if int32(hr) >= 0 && item != nil { item.Release()
dialog.Call(dialog.SetFolder, uintptr(unsafe.Pointer(item)))
item.Call(item.Release)
} }
} }
@ -255,59 +250,54 @@ func pickFolders(opts options, multi bool) (str string, lst []string, err error)
} }
owner, _ := opts.attach.(win.HWND) owner, _ := opts.attach.(win.HWND)
hr, _, _ = dialog.Call(dialog.Show, uintptr(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()
} }
if hr == uintptr(win.E_CANCELED) { if err == win.E_CANCELED {
return "", nil, ErrCanceled return "", nil, ErrCanceled
} }
if int32(hr) < 0 { if err != nil {
return "", nil, syscall.Errno(hr) return "", nil, err
} }
shellItemPath := func(obj *win.COMObject, trap uintptr, a ...uintptr) error { shellItemPath := func(item *win.IShellItem) error {
var item *win.IShellItem defer item.Release()
hr, _, _ := obj.Call(trap, append(a, uintptr(unsafe.Pointer(&item)))...) str, err := item.GetDisplayName(_SIGDN_FILESYSPATH)
if int32(hr) < 0 { if err == nil {
return syscall.Errno(hr)
}
defer item.Call(item.Release)
var ptr win.Pointer
hr, _, _ = item.Call(item.GetDisplayName,
_SIGDN_FILESYSPATH, uintptr(unsafe.Pointer(&ptr)))
if int32(hr) < 0 {
return syscall.Errno(hr)
}
defer win.CoTaskMemFree(ptr)
var res []uint16
hdr := (*reflect.SliceHeader)(unsafe.Pointer(&res))
hdr.Data, hdr.Len, hdr.Cap = uintptr(ptr), 32768, 32768
str = syscall.UTF16ToString(res)
lst = append(lst, str) lst = append(lst, str)
return nil }
return err
} }
if multi { if multi {
var items *_IShellItemArray items, err := dialog.GetResults()
hr, _, _ = dialog.Call(dialog.GetResults, uintptr(unsafe.Pointer(&items))) if err != nil {
if int32(hr) < 0 { return "", nil, err
return "", nil, syscall.Errno(hr)
} }
defer items.Call(items.Release) defer items.Release()
var count uint32 count, err := items.GetCount()
hr, _, _ = items.Call(items.GetCount, uintptr(unsafe.Pointer(&count))) if err != nil {
if int32(hr) < 0 { return "", nil, err
return "", nil, syscall.Errno(hr) }
for i := uint32(0); i < count; i++ {
item, err := items.GetItemAt(i)
if err == nil {
err = shellItemPath(item)
}
if err != nil {
return "", nil, err
} }
for i := uintptr(0); i < uintptr(count) && err == nil; i++ {
err = shellItemPath(&items.COMObject, items.GetItemAt, i)
} }
} else { } else {
err = shellItemPath(&dialog.COMObject, dialog.GetResult) item, err := dialog.GetResult()
if err == nil {
err = shellItemPath(item)
}
if err != nil {
return "", nil, err
}
} }
return return
} }
@ -389,70 +379,3 @@ func initFilters(filters FileFilters) []uint16 {
} }
return res return res
} }
// https://github.com/wine-mirror/wine/blob/master/include/shobjidl.idl
var (
_IID_IShellItem = uuid("\x1e\x6d\x82\x43\x18\xe7\xee\x42\xbc\x55\xa1\xe2\x61\xc3\x7b\xfe")
_IID_IFileOpenDialog = uuid("\x88\x72\x7c\xd5\xad\xd4\x68\x47\xbe\x02\x9d\x96\x95\x32\xd9\x60")
_CLSID_FileOpenDialog = uuid("\x9c\x5a\x1c\xdc\x8a\xe8\xde\x4d\xa5\xa1\x60\xf8\x2a\x20\xae\xf7")
)
type _IFileOpenDialog struct {
win.COMObject
*_IFileOpenDialogVtbl
}
type _IShellItemArray struct {
win.COMObject
*_IShellItemArrayVtbl
}
type _IFileOpenDialogVtbl struct {
_IFileDialogVtbl
GetResults uintptr
GetSelectedItems uintptr
}
type _IFileDialogVtbl struct {
_IModalWindowVtbl
SetFileTypes uintptr
SetFileTypeIndex uintptr
GetFileTypeIndex uintptr
Advise uintptr
Unadvise uintptr
SetOptions uintptr
GetOptions uintptr
SetDefaultFolder uintptr
SetFolder uintptr
GetFolder uintptr
GetCurrentSelection uintptr
SetFileName uintptr
GetFileName uintptr
SetTitle uintptr
SetOkButtonLabel uintptr
SetFileNameLabel uintptr
GetResult uintptr
AddPlace uintptr
SetDefaultExtension uintptr
Close uintptr
SetClientGuid uintptr
ClearClientData uintptr
SetFilter uintptr
}
type _IModalWindowVtbl struct {
_IUnknownVtbl
Show uintptr
}
type _IShellItemArrayVtbl struct {
_IUnknownVtbl
BindToHandler uintptr
GetPropertyStore uintptr
GetPropertyDescriptionList uintptr
GetAttributes uintptr
GetCount uintptr
GetItemAt uintptr
EnumItems uintptr
}

View file

@ -3,6 +3,7 @@
package win package win
import ( import (
"reflect"
"syscall" "syscall"
"unsafe" "unsafe"
@ -33,12 +34,18 @@ func CoUninitialize() { windows.CoUninitialize() }
// https://github.com/wine-mirror/wine/blob/master/include/unknwn.idl // https://github.com/wine-mirror/wine/blob/master/include/unknwn.idl
type IUnknownVtbl struct { type _IUnknownBase struct{ COMObject }
type _IUnknownVtbl struct {
QueryInterface uintptr QueryInterface uintptr
AddRef uintptr AddRef uintptr
Release uintptr Release uintptr
} }
func (c *_IUnknownBase) Release() {
vtbl := (*(**_IUnknownVtbl)(unsafe.Pointer(c)))
c.Call(vtbl.Release)
}
type COMObject struct{} type COMObject struct{}
//go:uintptrescapes //go:uintptrescapes
@ -57,3 +64,7 @@ func (o *COMObject) Call(trap uintptr, a ...uintptr) (r1, r2 uintptr, lastErr er
//sys CoCreateInstance(clsid uintptr, unkOuter *COMObject, clsContext int32, iid uintptr, address unsafe.Pointer) (res error) = ole32.CoCreateInstance //sys CoCreateInstance(clsid uintptr, unkOuter *COMObject, clsContext int32, iid uintptr, address unsafe.Pointer) (res error) = ole32.CoCreateInstance
//sys CoTaskMemFree(address Pointer) = ole32.CoTaskMemFree //sys CoTaskMemFree(address Pointer) = ole32.CoTaskMemFree
func uuid(s string) uintptr {
return (*reflect.StringHeader)(unsafe.Pointer(&s)).Data
}

View file

@ -2,6 +2,12 @@
package win package win
import (
"reflect"
"syscall"
"unsafe"
)
const ( const (
BIF_RETURNONLYFSDIRS = 0x00000001 BIF_RETURNONLYFSDIRS = 0x00000001
@ -43,13 +49,131 @@ type NOTIFYICONDATA struct {
// BalloonIcon Handle // NOTIFYICONDATAA_V3_SIZE // BalloonIcon Handle // NOTIFYICONDATAA_V3_SIZE
} }
type IShellItem struct { // https://github.com/wine-mirror/wine/blob/master/include/shobjidl.idl
COMObject
*_IShellItemVtbl var (
IID_IShellItem = uuid("\x1e\x6d\x82\x43\x18\xe7\xee\x42\xbc\x55\xa1\xe2\x61\xc3\x7b\xfe")
IID_IFileOpenDialog = uuid("\x88\x72\x7c\xd5\xad\xd4\x68\x47\xbe\x02\x9d\x96\x95\x32\xd9\x60")
CLSID_FileOpenDialog = uuid("\x9c\x5a\x1c\xdc\x8a\xe8\xde\x4d\xa5\xa1\x60\xf8\x2a\x20\xae\xf7")
)
type IFileOpenDialog struct {
_IFileOpenDialogBase
_ *_IFileOpenDialogVtbl
} }
type _IFileOpenDialogBase struct{ _IFileDialogBase }
type _IFileOpenDialogVtbl struct {
_IFileDialogVtbl
GetResults uintptr
GetSelectedItems uintptr
}
func (c *_IFileOpenDialogBase) GetResults() (res *IShellItemArray, err error) {
vtbl := (*(**_IFileOpenDialogVtbl)(unsafe.Pointer(c)))
hr, _, _ := c.Call(vtbl.GetResults, uintptr(unsafe.Pointer(&res)))
if hr != 0 {
err = syscall.Errno(hr)
}
return
}
type _IFileDialogBase struct{ _IModalWindowBase }
type _IFileDialogVtbl struct {
_IModalWindowVtbl
SetFileTypes uintptr
SetFileTypeIndex uintptr
GetFileTypeIndex uintptr
Advise uintptr
Unadvise uintptr
SetOptions uintptr
GetOptions uintptr
SetDefaultFolder uintptr
SetFolder uintptr
GetFolder uintptr
GetCurrentSelection uintptr
SetFileName uintptr
GetFileName uintptr
SetTitle uintptr
SetOkButtonLabel uintptr
SetFileNameLabel uintptr
GetResult uintptr
AddPlace uintptr
SetDefaultExtension uintptr
Close uintptr
SetClientGuid uintptr
ClearClientData uintptr
SetFilter uintptr
}
func (c *_IFileDialogBase) SetOptions(fos int) (err error) {
vtbl := (*(**_IFileDialogVtbl)(unsafe.Pointer(c)))
hr, _, _ := c.Call(vtbl.SetOptions, uintptr(fos))
if hr != 0 {
err = syscall.Errno(hr)
}
return
}
func (c *_IFileDialogBase) GetOptions() (fos int, err error) {
vtbl := (*(**_IFileDialogVtbl)(unsafe.Pointer(c)))
hr, _, _ := c.Call(vtbl.GetOptions, uintptr(unsafe.Pointer(&fos)))
if hr != 0 {
err = syscall.Errno(hr)
}
return
}
func (c *_IFileDialogBase) SetFolder(item *IShellItem) (err error) {
vtbl := (*(**_IFileDialogVtbl)(unsafe.Pointer(c)))
hr, _, _ := c.Call(vtbl.SetFolder, uintptr(unsafe.Pointer(item)))
if hr != 0 {
err = syscall.Errno(hr)
}
return
}
func (c *_IFileDialogBase) SetTitle(title *uint16) (err error) {
vtbl := (*(**_IFileDialogVtbl)(unsafe.Pointer(c)))
hr, _, _ := c.Call(vtbl.SetTitle, uintptr(unsafe.Pointer(title)))
if hr != 0 {
err = syscall.Errno(hr)
}
return
}
func (c *_IFileDialogBase) GetResult() (item *IShellItem, err error) {
vtbl := (*(**_IFileDialogVtbl)(unsafe.Pointer(c)))
hr, _, _ := c.Call(vtbl.GetResult, uintptr(unsafe.Pointer(&item)))
if hr != 0 {
err = syscall.Errno(hr)
}
return
}
type _IModalWindowBase struct{ _IUnknownBase }
type _IModalWindowVtbl struct {
_IUnknownVtbl
Show uintptr
}
func (c *_IModalWindowBase) Show(wnd HWND) (err error) {
vtbl := (*(**_IModalWindowVtbl)(unsafe.Pointer(c)))
hr, _, _ := c.Call(vtbl.Show, uintptr(wnd))
if hr != 0 {
err = syscall.Errno(hr)
}
return
}
type IShellItem struct {
_IShellItemBase
_ *_IShellItemVtbl
}
type _IShellItemBase struct{ _IUnknownBase }
type _IShellItemVtbl struct { type _IShellItemVtbl struct {
IUnknownVtbl _IUnknownVtbl
BindToHandler uintptr BindToHandler uintptr
GetParent uintptr GetParent uintptr
GetDisplayName uintptr GetDisplayName uintptr
@ -57,6 +181,56 @@ type _IShellItemVtbl struct {
Compare uintptr Compare uintptr
} }
func (c *_IShellItemBase) GetDisplayName(name int) (res string, err error) {
var ptr uintptr
vtbl := (*(**_IShellItemVtbl)(unsafe.Pointer(c)))
hr, _, _ := c.Call(vtbl.GetDisplayName, uintptr(name), uintptr(unsafe.Pointer(&ptr)))
if hr != 0 {
err = syscall.Errno(hr)
} else {
var buf []uint16
hdr := (*reflect.SliceHeader)(unsafe.Pointer(&buf))
hdr.Data, hdr.Len, hdr.Cap = uintptr(ptr), 32768, 32768
res = syscall.UTF16ToString(buf)
}
return
}
type IShellItemArray struct {
_IShellItemArrayBase
_ *_IShellItemArrayVtbl
}
type _IShellItemArrayBase struct{ _IUnknownBase }
type _IShellItemArrayVtbl struct {
_IUnknownVtbl
BindToHandler uintptr
GetPropertyStore uintptr
GetPropertyDescriptionList uintptr
GetAttributes uintptr
GetCount uintptr
GetItemAt uintptr
EnumItems uintptr
}
func (c *_IShellItemArrayBase) GetCount() (numItems uint32, err error) {
vtbl := (*(**_IShellItemArrayVtbl)(unsafe.Pointer(c)))
hr, _, _ := c.Call(vtbl.GetCount, uintptr(unsafe.Pointer(&numItems)))
if hr != 0 {
err = syscall.Errno(hr)
}
return
}
func (c *_IShellItemArrayBase) GetItemAt(index uint32) (item *IShellItem, err error) {
vtbl := (*(**_IShellItemArrayVtbl)(unsafe.Pointer(c)))
hr, _, _ := c.Call(vtbl.GetItemAt, uintptr(index), uintptr(unsafe.Pointer(&item)))
if hr != 0 {
err = syscall.Errno(hr)
}
return
}
//sys SHBrowseForFolder(bi *BROWSEINFO) (ret Pointer) = shell32.SHBrowseForFolder //sys SHBrowseForFolder(bi *BROWSEINFO) (ret Pointer) = shell32.SHBrowseForFolder
//sys SHCreateItemFromParsingName(path *uint16, bc *COMObject, iid uintptr, item **IShellItem) (res error) = shell32.SHCreateItemFromParsingName //sys SHCreateItemFromParsingName(path *uint16, bc *COMObject, iid uintptr, item **IShellItem) (res error) = shell32.SHCreateItemFromParsingName
//sys ShellNotifyIcon(message uint32, data *NOTIFYICONDATA) (ret int, err error) = shell32.Shell_NotifyIconW //sys ShellNotifyIcon(message uint32, data *NOTIFYICONDATA) (ret int, err error) = shell32.Shell_NotifyIconW

View file

@ -353,6 +353,15 @@ type NONCLIENTMETRICS struct {
MessageFont LOGFONT MessageFont LOGFONT
} }
// 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 HWND
}
//sys CallNextHookEx(hk Handle, code int32, wparam uintptr, lparam unsafe.Pointer) (ret uintptr) = user32.CallNextHookEx //sys CallNextHookEx(hk Handle, code int32, wparam uintptr, lparam unsafe.Pointer) (ret uintptr) = user32.CallNextHookEx
//sys CreateIconFromResource(resBits []byte, icon bool, ver uint32) (ret Handle, err error) = user32.CreateIconFromResource //sys CreateIconFromResource(resBits []byte, icon bool, ver uint32) (ret Handle, err error) = user32.CreateIconFromResource
//sys CreateWindowEx(exStyle uint32, className *uint16, windowName *uint16, style uint32, x int, y int, width int, height int, parent HWND, menu Handle, instance Handle, param unsafe.Pointer) (ret HWND, err error) = user32.CreateWindowExW //sys CreateWindowEx(exStyle uint32, className *uint16, windowName *uint16, style uint32, x int, y int, width int, height int, parent HWND, menu Handle, instance Handle, param unsafe.Pointer) (ret HWND, err error) = user32.CreateWindowExW

View file

@ -9,6 +9,18 @@ type Handle = windows.Handle
type HWND = windows.HWND type HWND = windows.HWND
type Pointer uintptr type Pointer uintptr
// https://docs.microsoft.com/en-us/windows/win32/api/minwinbase/ns-minwinbase-systemtime
type SYSTEMTIME struct {
Year uint16
Month uint16
DayOfWeek uint16
Day uint16
Hour uint16
Minute uint16
Second uint16
Milliseconds uint16
}
func RtlGetNtVersionNumbers() (majorVersion, minorVersion, buildNumber uint32) { func RtlGetNtVersionNumbers() (majorVersion, minorVersion, buildNumber uint32) {
return windows.RtlGetNtVersionNumbers() return windows.RtlGetNtVersionNumbers()
} }

View file

@ -127,7 +127,7 @@ func newDialogHook(ctx context.Context, icon any, title *string, init func(wnd w
return &hook, nil return &hook, nil
} }
func dialogHookProc(code int32, wparam uintptr, lparam *_CWPRETSTRUCT) uintptr { func dialogHookProc(code int32, wparam uintptr, lparam *win.CWPRETSTRUCT) uintptr {
if lparam.Message == win.WM_INITDIALOG { if lparam.Message == win.WM_INITDIALOG {
tid := win.GetCurrentThreadId() tid := win.GetCurrentThreadId()
hook := (*dialogHook)(loadBackRef(uintptr(tid))) hook := (*dialogHook)(loadBackRef(uintptr(tid)))
@ -360,36 +360,3 @@ func enableVisualStyles() (cookie uintptr) {
} }
return return
} }
// 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 win.HWND
}
// https://docs.microsoft.com/en-us/windows/win32/api/minwinbase/ns-minwinbase-systemtime
type _SYSTEMTIME struct {
year uint16
month uint16
dayOfWeek uint16
day uint16
hour uint16
minute uint16
second uint16
milliseconds uint16
}
// https://github.com/wine-mirror/wine/blob/master/include/unknwn.idl
type _IUnknownVtbl struct {
QueryInterface uintptr
AddRef uintptr
Release uintptr
}
func uuid(s string) uintptr {
return (*reflect.StringHeader)(unsafe.Pointer(&s)).Data
}