Refactor (windows).

This commit is contained in:
Nuno Cruces 2022-06-18 03:45:49 +01:00
parent 176a3d48a1
commit d33a18d05a
13 changed files with 403 additions and 220 deletions

View file

@ -1,6 +1,10 @@
#!/usr/bin/env bash #!/usr/bin/env bash
set -Eeuo pipefail set -Eeuo pipefail
cd "${BASH_SOURCE%/*}"
go generate ../../...
TAG=$(git tag --points-at HEAD) TAG=$(git tag --points-at HEAD)
echo 'package main; const tag = "'$TAG'"' > tag.go echo 'package main; const tag = "'$TAG'"' > tag.go

View file

@ -4,11 +4,11 @@ import (
"image/color" "image/color"
"sync" "sync"
"unsafe" "unsafe"
"github.com/ncruces/zenity/internal/win"
) )
var ( var (
chooseColor = comdlg32.NewProc("ChooseColorW")
savedColors [16]uint32 savedColors [16]uint32
colorsMutex sync.Mutex colorsMutex sync.Mutex
) )
@ -19,32 +19,26 @@ func init() {
} }
} }
const (
_CC_RGBINIT = 0x00000001
_CC_FULLOPEN = 0x00000002
_CC_PREVENTFULLOPEN = 0x00000004
)
func selectColor(opts options) (color.Color, error) { func selectColor(opts options) (color.Color, error) {
// load custom colors // load custom colors
colorsMutex.Lock() colorsMutex.Lock()
customColors := savedColors customColors := savedColors
colorsMutex.Unlock() colorsMutex.Unlock()
var args _CHOOSECOLOR var args win.CHOOSECOLOR
args.StructSize = uint32(unsafe.Sizeof(args)) args.StructSize = uint32(unsafe.Sizeof(args))
args.Owner, _ = opts.attach.(uintptr) args.Owner, _ = opts.attach.(uintptr)
args.CustColors = &customColors args.CustColors = &customColors
if opts.color != nil { if opts.color != nil {
args.Flags |= _CC_RGBINIT args.Flags |= win.CC_RGBINIT
n := color.NRGBAModel.Convert(opts.color).(color.NRGBA) n := color.NRGBAModel.Convert(opts.color).(color.NRGBA)
args.RgbResult = uint32(n.R) | uint32(n.G)<<8 | uint32(n.B)<<16 args.RgbResult = uint32(n.R) | uint32(n.G)<<8 | uint32(n.B)<<16
} }
if opts.showPalette { if opts.showPalette {
args.Flags |= _CC_PREVENTFULLOPEN args.Flags |= win.CC_PREVENTFULLOPEN
} else { } else {
args.Flags |= _CC_FULLOPEN args.Flags |= win.CC_FULLOPEN
} }
defer setup()() defer setup()()
@ -57,12 +51,12 @@ func selectColor(opts options) (color.Color, error) {
defer unhook() defer unhook()
} }
s, _, _ := chooseColor.Call(uintptr(unsafe.Pointer(&args))) ok := win.ChooseColor(&args)
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 s == 0 { if !ok {
return nil, commDlgError() return nil, win.CommDlgError()
} }
// save custom colors back // save custom colors back
@ -75,16 +69,3 @@ func selectColor(opts options) (color.Color, error) {
b := uint8(args.RgbResult >> 16) b := uint8(args.RgbResult >> 16)
return color.RGBA{R: r, G: g, B: b, A: 255}, nil return color.RGBA{R: r, G: g, B: b, A: 255}, nil
} }
// https://docs.microsoft.com/en-us/windows/win32/api/commdlg/ns-commdlg-choosecolorw-r1
type _CHOOSECOLOR struct {
StructSize uint32
Owner uintptr
Instance uintptr
RgbResult uint32
CustColors *[16]uint32
Flags uint32
CustData uintptr
FnHook uintptr
TemplateName *uint16
}

View file

@ -7,27 +7,11 @@ import (
"syscall" "syscall"
"unicode/utf16" "unicode/utf16"
"unsafe" "unsafe"
)
var ( "github.com/ncruces/zenity/internal/win"
getOpenFileName = comdlg32.NewProc("GetOpenFileNameW")
getSaveFileName = comdlg32.NewProc("GetSaveFileNameW")
shBrowseForFolder = shell32.NewProc("SHBrowseForFolderW")
shCreateItemFromParsingName = shell32.NewProc("SHCreateItemFromParsingName")
shGetPathFromIDListEx = shell32.NewProc("SHGetPathFromIDListEx")
) )
const ( const (
_OFN_OVERWRITEPROMPT = 0x00000002
_OFN_NOCHANGEDIR = 0x00000008
_OFN_ALLOWMULTISELECT = 0x00000200
_OFN_PATHMUSTEXIST = 0x00000800
_OFN_FILEMUSTEXIST = 0x00001000
_OFN_CREATEPROMPT = 0x00002000
_OFN_NOREADONLYRETURN = 0x00008000
_OFN_EXPLORER = 0x00080000
_OFN_FORCESHOWHIDDEN = 0x10000000
_FOS_NOCHANGEDIR = 0x00000008 _FOS_NOCHANGEDIR = 0x00000008
_FOS_PICKFOLDERS = 0x00000020 _FOS_PICKFOLDERS = 0x00000020
_FOS_FORCEFILESYSTEM = 0x00000040 _FOS_FORCEFILESYSTEM = 0x00000040
@ -41,16 +25,16 @@ func selectFile(opts options) (string, error) {
return res, err return res, err
} }
var args _OPENFILENAME var args win.OPENFILENAME
args.StructSize = uint32(unsafe.Sizeof(args)) args.StructSize = uint32(unsafe.Sizeof(args))
args.Owner, _ = opts.attach.(uintptr) args.Owner, _ = opts.attach.(uintptr)
args.Flags = _OFN_NOCHANGEDIR | _OFN_FILEMUSTEXIST | _OFN_EXPLORER args.Flags = win.OFN_NOCHANGEDIR | win.OFN_FILEMUSTEXIST | win.OFN_EXPLORER
if opts.title != nil { if opts.title != nil {
args.Title = syscall.StringToUTF16Ptr(*opts.title) args.Title = syscall.StringToUTF16Ptr(*opts.title)
} }
if opts.showHidden { if opts.showHidden {
args.Flags |= _OFN_FORCESHOWHIDDEN args.Flags |= win.OFN_FORCESHOWHIDDEN
} }
if opts.fileFilters != nil { if opts.fileFilters != nil {
args.Filter = &initFilters(opts.fileFilters)[0] args.Filter = &initFilters(opts.fileFilters)[0]
@ -71,12 +55,12 @@ func selectFile(opts options) (string, error) {
defer unhook() defer unhook()
} }
s, _, _ := getOpenFileName.Call(uintptr(unsafe.Pointer(&args))) ok := win.GetOpenFileName(&args)
if opts.ctx != nil && opts.ctx.Err() != nil { if opts.ctx != nil && opts.ctx.Err() != nil {
return "", opts.ctx.Err() return "", opts.ctx.Err()
} }
if s == 0 { if !ok {
return "", commDlgError() return "", win.CommDlgError()
} }
return syscall.UTF16ToString(res[:]), nil return syscall.UTF16ToString(res[:]), nil
} }
@ -87,16 +71,16 @@ func selectFileMultiple(opts options) ([]string, error) {
return res, err return res, err
} }
var args _OPENFILENAME var args win.OPENFILENAME
args.StructSize = uint32(unsafe.Sizeof(args)) args.StructSize = uint32(unsafe.Sizeof(args))
args.Owner, _ = opts.attach.(uintptr) args.Owner, _ = opts.attach.(uintptr)
args.Flags = _OFN_NOCHANGEDIR | _OFN_ALLOWMULTISELECT | _OFN_FILEMUSTEXIST | _OFN_EXPLORER args.Flags = win.OFN_NOCHANGEDIR | win.OFN_ALLOWMULTISELECT | win.OFN_FILEMUSTEXIST | win.OFN_EXPLORER
if opts.title != nil { if opts.title != nil {
args.Title = syscall.StringToUTF16Ptr(*opts.title) args.Title = syscall.StringToUTF16Ptr(*opts.title)
} }
if opts.showHidden { if opts.showHidden {
args.Flags |= _OFN_FORCESHOWHIDDEN args.Flags |= win.OFN_FORCESHOWHIDDEN
} }
if opts.fileFilters != nil { if opts.fileFilters != nil {
args.Filter = &initFilters(opts.fileFilters)[0] args.Filter = &initFilters(opts.fileFilters)[0]
@ -117,12 +101,12 @@ func selectFileMultiple(opts options) ([]string, error) {
defer unhook() defer unhook()
} }
s, _, _ := getOpenFileName.Call(uintptr(unsafe.Pointer(&args))) ok := win.GetOpenFileName(&args)
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 s == 0 { if !ok {
return nil, commDlgError() return nil, win.CommDlgError()
} }
var i int var i int
@ -158,22 +142,22 @@ func selectFileSave(opts options) (string, error) {
return res, err return res, err
} }
var args _OPENFILENAME var args win.OPENFILENAME
args.StructSize = uint32(unsafe.Sizeof(args)) args.StructSize = uint32(unsafe.Sizeof(args))
args.Owner, _ = opts.attach.(uintptr) args.Owner, _ = opts.attach.(uintptr)
args.Flags = _OFN_NOCHANGEDIR | _OFN_PATHMUSTEXIST | _OFN_NOREADONLYRETURN | _OFN_EXPLORER args.Flags = win.OFN_NOCHANGEDIR | win.OFN_PATHMUSTEXIST | win.OFN_NOREADONLYRETURN | win.OFN_EXPLORER
if opts.title != nil { if opts.title != nil {
args.Title = syscall.StringToUTF16Ptr(*opts.title) args.Title = syscall.StringToUTF16Ptr(*opts.title)
} }
if opts.confirmOverwrite { if opts.confirmOverwrite {
args.Flags |= _OFN_OVERWRITEPROMPT args.Flags |= win.OFN_OVERWRITEPROMPT
} }
if opts.confirmCreate { if opts.confirmCreate {
args.Flags |= _OFN_CREATEPROMPT args.Flags |= win.OFN_CREATEPROMPT
} }
if opts.showHidden { if opts.showHidden {
args.Flags |= _OFN_FORCESHOWHIDDEN args.Flags |= win.OFN_FORCESHOWHIDDEN
} }
if opts.fileFilters != nil { if opts.fileFilters != nil {
args.Filter = &initFilters(opts.fileFilters)[0] args.Filter = &initFilters(opts.fileFilters)[0]
@ -194,12 +178,12 @@ func selectFileSave(opts options) (string, error) {
defer unhook() defer unhook()
} }
s, _, _ := getSaveFileName.Call(uintptr(unsafe.Pointer(&args))) ok := win.GetSaveFileName(&args)
if opts.ctx != nil && opts.ctx.Err() != nil { if opts.ctx != nil && opts.ctx.Err() != nil {
return "", opts.ctx.Err() return "", opts.ctx.Err()
} }
if s == 0 { if !ok {
return "", commDlgError() return "", win.CommDlgError()
} }
return syscall.UTF16ToString(res[:]), nil return syscall.UTF16ToString(res[:]), nil
} }
@ -207,19 +191,19 @@ func selectFileSave(opts options) (string, error) {
func pickFolders(opts options, multi bool) (str string, lst []string, err error) { func pickFolders(opts options, multi bool) (str string, lst []string, err error) {
defer setup()() defer setup()()
hr, _, _ := coInitializeEx.Call(0, 0x6) // COINIT_APARTMENTTHREADED|COINIT_DISABLE_OLE1DDE err = win.CoInitializeEx(0, 0x6) // COINIT_APARTMENTTHREADED|COINIT_DISABLE_OLE1DDE
if hr != 0x80010106 { // RPC_E_CHANGED_MODE if err != win.RPC_E_CHANGED_MODE {
if int32(hr) < 0 { if err != nil {
return "", nil, syscall.Errno(hr) return "", nil, err
} }
defer coUninitialize.Call() defer win.CoUninitialize()
} }
var dialog *_IFileOpenDialog var dialog *_IFileOpenDialog
hr, _, _ = coCreateInstance.Call( err = win.CoCreateInstance(
_CLSID_FileOpenDialog, 0, 0x17, // CLSCTX_ALL _CLSID_FileOpenDialog, nil, 0x17, // CLSCTX_ALL
_IID_IFileOpenDialog, uintptr(unsafe.Pointer(&dialog))) _IID_IFileOpenDialog, unsafe.Pointer(&dialog))
if int32(hr) < 0 { if err != nil {
if multi { if multi {
return "", nil, fmt.Errorf("%w: multiple directory", ErrUnsupported) return "", nil, fmt.Errorf("%w: multiple directory", ErrUnsupported)
} }
@ -228,7 +212,7 @@ func pickFolders(opts options, multi bool) (str string, lst []string, err error)
defer dialog.Call(dialog.Release) defer dialog.Call(dialog.Release)
var flgs int var flgs int
hr, _, _ = dialog.Call(dialog.GetOptions, uintptr(unsafe.Pointer(&flgs))) hr, _, _ := dialog.Call(dialog.GetOptions, uintptr(unsafe.Pointer(&flgs)))
if int32(hr) < 0 { if int32(hr) < 0 {
return "", nil, syscall.Errno(hr) return "", nil, syscall.Errno(hr)
} }
@ -250,12 +234,9 @@ func pickFolders(opts options, multi bool) (str string, lst []string, err error)
} }
if opts.filename != "" { if opts.filename != "" {
var item *_IShellItem var item *win.IShellItem
ptr := syscall.StringToUTF16Ptr(opts.filename) ptr := syscall.StringToUTF16Ptr(opts.filename)
hr, _, _ = shCreateItemFromParsingName.Call( win.SHCreateItemFromParsingName(ptr, nil, _IID_IShellItem, &item)
uintptr(unsafe.Pointer(ptr)), 0,
_IID_IShellItem,
uintptr(unsafe.Pointer(&item)))
if int32(hr) >= 0 && item != nil { if int32(hr) >= 0 && item != nil {
dialog.Call(dialog.SetFolder, uintptr(unsafe.Pointer(item))) dialog.Call(dialog.SetFolder, uintptr(unsafe.Pointer(item)))
@ -283,8 +264,8 @@ func pickFolders(opts options, multi bool) (str string, lst []string, err error)
return "", nil, syscall.Errno(hr) return "", nil, syscall.Errno(hr)
} }
shellItemPath := func(obj *_COMObject, trap uintptr, a ...uintptr) error { shellItemPath := func(obj *win.COMObject, trap uintptr, a ...uintptr) error {
var item *_IShellItem var item *win.IShellItem
hr, _, _ := obj.Call(trap, append(a, uintptr(unsafe.Pointer(&item)))...) hr, _, _ := obj.Call(trap, append(a, uintptr(unsafe.Pointer(&item)))...)
if int32(hr) < 0 { if int32(hr) < 0 {
return syscall.Errno(hr) return syscall.Errno(hr)
@ -298,11 +279,11 @@ func pickFolders(opts options, multi bool) (str string, lst []string, err error)
if int32(hr) < 0 { if int32(hr) < 0 {
return syscall.Errno(hr) return syscall.Errno(hr)
} }
defer coTaskMemFree.Call(ptr) defer win.CoTaskMemFree(ptr)
var res []uint16 var res []uint16
hdr := (*reflect.SliceHeader)(unsafe.Pointer(&res)) hdr := (*reflect.SliceHeader)(unsafe.Pointer(&res))
hdr.Data, hdr.Len, hdr.Cap = ptr, 32768, 32768 hdr.Data, hdr.Len, hdr.Cap = uintptr(ptr), 32768, 32768
str = syscall.UTF16ToString(res) str = syscall.UTF16ToString(res)
lst = append(lst, str) lst = append(lst, str)
return nil return nil
@ -322,16 +303,16 @@ func pickFolders(opts options, multi bool) (str string, lst []string, err error)
return "", nil, syscall.Errno(hr) return "", nil, syscall.Errno(hr)
} }
for i := uintptr(0); i < uintptr(count) && err == nil; i++ { for i := uintptr(0); i < uintptr(count) && err == nil; i++ {
err = shellItemPath(&items._COMObject, items.GetItemAt, i) err = shellItemPath(&items.COMObject, items.GetItemAt, i)
} }
} else { } else {
err = shellItemPath(&dialog._COMObject, dialog.GetResult) err = shellItemPath(&dialog.COMObject, dialog.GetResult)
} }
return return
} }
func browseForFolder(opts options) (string, []string, error) { func browseForFolder(opts options) (string, []string, error) {
var args _BROWSEINFO var args win.BROWSEINFO
args.Owner, _ = opts.attach.(uintptr) args.Owner, _ = opts.attach.(uintptr)
args.Flags = 0x1 // BIF_RETURNONLYFSDIRS args.Flags = 0x1 // BIF_RETURNONLYFSDIRS
@ -351,17 +332,17 @@ func browseForFolder(opts options) (string, []string, error) {
defer unhook() defer unhook()
} }
ptr, _, _ := shBrowseForFolder.Call(uintptr(unsafe.Pointer(&args))) ptr := win.SHBrowseForFolder(&args)
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 ptr == 0 { if ptr == 0 {
return "", nil, ErrCanceled return "", nil, ErrCanceled
} }
defer coTaskMemFree.Call(ptr) defer win.CoTaskMemFree(ptr)
var res [32768]uint16 var res [32768]uint16
shGetPathFromIDListEx.Call(ptr, uintptr(unsafe.Pointer(&res[0])), uintptr(len(res)), 0) win.SHGetPathFromIDListEx(ptr, &res[0], len(res), 0)
str := syscall.UTF16ToString(res[:]) str := syscall.UTF16ToString(res[:])
return str, []string{str}, nil return str, []string{str}, nil
@ -408,45 +389,6 @@ func initFilters(filters FileFilters) []uint16 {
return res return res
} }
// https://docs.microsoft.com/en-us/windows/win32/api/commdlg/ns-commdlg-openfilenamew
type _OPENFILENAME struct {
StructSize uint32
Owner uintptr
Instance uintptr
Filter *uint16
CustomFilter *uint16
MaxCustomFilter uint32
FilterIndex uint32
File *uint16
MaxFile uint32
FileTitle *uint16
MaxFileTitle uint32
InitialDir *uint16
Title *uint16
Flags uint32
FileOffset uint16
FileExtension uint16
DefExt *uint16
CustData uintptr
FnHook uintptr
TemplateName *uint16
PvReserved uintptr
DwReserved uint32
FlagsEx uint32
}
// https://docs.microsoft.com/en-us/windows/win32/api/shlobj_core/ns-shlobj_core-browseinfow
type _BROWSEINFO struct {
Owner uintptr
Root uintptr
DisplayName *uint16
Title *uint16
Flags uint32
CallbackFunc uintptr
LParam *uint16
Image int32
}
// https://github.com/wine-mirror/wine/blob/master/include/shobjidl.idl // https://github.com/wine-mirror/wine/blob/master/include/shobjidl.idl
var ( var (
@ -456,17 +398,12 @@ var (
) )
type _IFileOpenDialog struct { type _IFileOpenDialog struct {
_COMObject win.COMObject
*_IFileOpenDialogVtbl *_IFileOpenDialogVtbl
} }
type _IShellItem struct {
_COMObject
*_IShellItemVtbl
}
type _IShellItemArray struct { type _IShellItemArray struct {
_COMObject win.COMObject
*_IShellItemArrayVtbl *_IShellItemArrayVtbl
} }
@ -508,15 +445,6 @@ type _IModalWindowVtbl struct {
Show uintptr Show uintptr
} }
type _IShellItemVtbl struct {
_IUnknownVtbl
BindToHandler uintptr
GetParent uintptr
GetDisplayName uintptr
GetAttributes uintptr
Compare uintptr
}
type _IShellItemArrayVtbl struct { type _IShellItemArrayVtbl struct {
_IUnknownVtbl _IUnknownVtbl
BindToHandler uintptr BindToHandler uintptr

4
go.mod
View file

@ -8,8 +8,8 @@ require (
github.com/ncruces/go-strftime v0.1.8 github.com/ncruces/go-strftime v0.1.8
github.com/randall77/makefat v0.0.0-20210315173500-7ddd0e42c844 github.com/randall77/makefat v0.0.0-20210315173500-7ddd0e42c844
go.uber.org/goleak v1.1.12 // test go.uber.org/goleak v1.1.12 // test
golang.org/x/image v0.0.0-20220601225756-64ec528b34cd golang.org/x/image v0.0.0-20220617043117-41969df76e82
golang.org/x/sys v0.0.0-20220610221304-9f5ed59c137d golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c
) )
require ( require (

8
go.sum
View file

@ -24,8 +24,8 @@ go.uber.org/goleak v1.1.12 h1:gZAh5/EyT/HQwlpkCy6wTpqfH9H8Lz8zbm3dZh+OyzA=
go.uber.org/goleak v1.1.12/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= go.uber.org/goleak v1.1.12/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/image v0.0.0-20220601225756-64ec528b34cd h1:9NbNcTg//wfC5JskFW4Z3sqwVnjmJKHxLAol1bW2qgw= golang.org/x/image v0.0.0-20220617043117-41969df76e82 h1:KpZB5pUSBvrHltNEdK/tw0xlPeD13M6M6aGP32gKqiw=
golang.org/x/image v0.0.0-20220601225756-64ec528b34cd/go.mod h1:doUCurBvlfPMKfmIpRIywoHmhN3VyhnoFDbvIEWF4hY= golang.org/x/image v0.0.0-20220617043117-41969df76e82/go.mod h1:doUCurBvlfPMKfmIpRIywoHmhN3VyhnoFDbvIEWF4hY=
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 h1:VLliZ0d+/avPrXXH+OakdXhpJuEoBZuwh1m2j7U6Iug= golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 h1:VLliZ0d+/avPrXXH+OakdXhpJuEoBZuwh1m2j7U6Iug=
golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
@ -42,8 +42,8 @@ golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220610221304-9f5ed59c137d h1:Zu/JngovGLVi6t2J3nmAf3AoTDwuzw85YZ3b9o4yU7s= golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c h1:aFV+BgZ4svzjfabn8ERpuB4JI4N6/rdy1iusx77G3oU=
golang.org/x/sys v0.0.0-20220610221304-9f5ed59c137d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=

11
internal/win/comctl32.go Normal file
View file

@ -0,0 +1,11 @@
//go:build windows
package win
// https://docs.microsoft.com/en-us/windows/win32/api/commctrl/ns-commctrl-initcommoncontrolsex
type INITCOMMONCONTROLSEX struct {
Size uint32
ICC uint32
}
//sys InitCommonControlsEx(icc *INITCOMMONCONTROLSEX) (ok bool) = comctl32.InitCommonControlsEx

80
internal/win/comdlg32.go Normal file
View file

@ -0,0 +1,80 @@
//go:build windows
package win
import (
"fmt"
"github.com/ncruces/zenity/internal/zenutil"
)
const (
CC_RGBINIT = 0x00000001
CC_FULLOPEN = 0x00000002
CC_PREVENTFULLOPEN = 0x00000004
)
// https://docs.microsoft.com/en-us/windows/win32/api/commdlg/ns-commdlg-choosecolorw-r1
type CHOOSECOLOR struct {
StructSize uint32
Owner uintptr
Instance uintptr
RgbResult uint32
CustColors *[16]uint32
Flags uint32
CustData uintptr
FnHook uintptr
TemplateName *uint16
}
const (
OFN_OVERWRITEPROMPT = 0x00000002
OFN_NOCHANGEDIR = 0x00000008
OFN_ALLOWMULTISELECT = 0x00000200
OFN_PATHMUSTEXIST = 0x00000800
OFN_FILEMUSTEXIST = 0x00001000
OFN_CREATEPROMPT = 0x00002000
OFN_NOREADONLYRETURN = 0x00008000
OFN_EXPLORER = 0x00080000
OFN_FORCESHOWHIDDEN = 0x10000000
)
// https://docs.microsoft.com/en-us/windows/win32/api/commdlg/ns-commdlg-openfilenamew
type OPENFILENAME struct {
StructSize uint32
Owner uintptr
Instance uintptr
Filter *uint16
CustomFilter *uint16
MaxCustomFilter uint32
FilterIndex uint32
File *uint16
MaxFile uint32
FileTitle *uint16
MaxFileTitle uint32
InitialDir *uint16
Title *uint16
Flags uint32
FileOffset uint16
FileExtension uint16
DefExt *uint16
CustData uintptr
FnHook uintptr
TemplateName *uint16
PvReserved uintptr
DwReserved uint32
FlagsEx uint32
}
func CommDlgError() error {
if code := commDlgExtendedError(); code == 0 {
return zenutil.ErrCanceled
} else {
return fmt.Errorf("Common Dialog error: %x", code)
}
}
//sys commDlgExtendedError() (code int) = comdlg32.CommDlgExtendedError
//sys ChooseColor(cc *CHOOSECOLOR) (ok bool) = comdlg32.ChooseColorW
//sys GetOpenFileName(ofn *OPENFILENAME) (ok bool) = comdlg32.GetOpenFileNameW
//sys GetSaveFileName(ofn *OPENFILENAME) (ok bool) = comdlg32.GetSaveFileNameW

47
internal/win/ole32.go Normal file
View file

@ -0,0 +1,47 @@
//go:build windows
package win
import (
"syscall"
"unsafe"
"golang.org/x/sys/windows"
)
const (
RPC_E_CHANGED_MODE syscall.Errno = 0x80010106
)
func CoInitializeEx(reserved uintptr, coInit uint32) error {
return windows.CoInitializeEx(reserved, coInit)
}
func CoUninitialize() { windows.CoUninitialize() }
// https://github.com/wine-mirror/wine/blob/master/include/unknwn.idl
type IUnknownVtbl struct {
QueryInterface uintptr
AddRef uintptr
Release uintptr
}
type COMObject struct{}
//go:uintptrescapes
func (o *COMObject) Call(trap uintptr, a ...uintptr) (r1, r2 uintptr, lastErr error) {
switch nargs := uintptr(len(a)); nargs {
case 0:
return syscall.Syscall(trap, nargs+1, uintptr(unsafe.Pointer(o)), 0, 0)
case 1:
return syscall.Syscall(trap, nargs+1, uintptr(unsafe.Pointer(o)), a[0], 0)
case 2:
return syscall.Syscall(trap, nargs+1, uintptr(unsafe.Pointer(o)), a[0], a[1])
default:
panic("COM call with too many arguments.")
}
}
//sys CoTaskMemFree(address uintptr) = ole32.CoTaskMemFree
//sys CoCreateInstance(clsid uintptr, unkOuter unsafe.Pointer, clsContext int32, iid uintptr, address unsafe.Pointer) (ret error) = ole32.CoCreateInstance

5
internal/win/pkg.go Normal file
View file

@ -0,0 +1,5 @@
// Package win is internal. DO NOT USE.
package win
//go:generate -command mkwinsyscall go run golang.org/x/sys/windows/mkwinsyscall -output zsyscall_windows.go
//go:generate mkwinsyscall comctl32.go comdlg32.go ole32.go shell32.go

58
internal/win/shell32.go Normal file
View file

@ -0,0 +1,58 @@
//go:build windows
package win
const (
NIM_ADD = 0
NIM_DELETE = 2
)
// https://docs.microsoft.com/en-us/windows/win32/api/shlobj_core/ns-shlobj_core-browseinfow
type BROWSEINFO struct {
Owner uintptr
Root uintptr
DisplayName *uint16
Title *uint16
Flags uint32
CallbackFunc uintptr
LParam *uint16
Image int32
}
// https://docs.microsoft.com/en-us/windows/win32/api/shellapi/ns-shellapi-notifyicondataw
type NOTIFYICONDATA struct {
StructSize uint32
Wnd uintptr
ID uint32
Flags uint32
CallbackMessage uint32
Icon uintptr
Tip [128]uint16 // NOTIFYICONDATAA_V1_SIZE
State uint32
StateMask uint32
Info [256]uint16
Version uint32
InfoTitle [64]uint16
InfoFlags uint32
// GuidItem [16]byte // NOTIFYICONDATAA_V2_SIZE
// BalloonIcon syscall.Handle // NOTIFYICONDATAA_V3_SIZE
}
type IShellItem struct {
COMObject
*_IShellItemVtbl
}
type _IShellItemVtbl struct {
IUnknownVtbl
BindToHandler uintptr
GetParent uintptr
GetDisplayName uintptr
GetAttributes uintptr
Compare uintptr
}
//sys SHBrowseForFolder(bi *BROWSEINFO) (ptr uintptr) = shell32.SHBrowseForFolder
//sys SHCreateItemFromParsingName(path *uint16, bc unsafe.Pointer, iid uintptr, item **IShellItem) (err error) = shell32.SHCreateItemFromParsingName
//sys SHGetPathFromIDListEx(ptr uintptr, path *uint16, pathLen int, opts int) (err error) = shell32.SHGetPathFromIDListEx
//sys ShellNotifyIcon(message uint32, data *NOTIFYICONDATA) (ret int, err error) = shell32.Shell_NotifyIconW

View file

@ -0,0 +1,131 @@
// Code generated by 'go generate'; DO NOT EDIT.
package win
import (
"syscall"
"unsafe"
"golang.org/x/sys/windows"
)
var _ unsafe.Pointer
// Do the interface allocations only once for common
// Errno values.
const (
errnoERROR_IO_PENDING = 997
)
var (
errERROR_IO_PENDING error = syscall.Errno(errnoERROR_IO_PENDING)
errERROR_EINVAL error = syscall.EINVAL
)
// errnoErr returns common boxed Errno values, to prevent
// allocations at runtime.
func errnoErr(e syscall.Errno) error {
switch e {
case 0:
return errERROR_EINVAL
case errnoERROR_IO_PENDING:
return errERROR_IO_PENDING
}
// TODO: add more here, after collecting data on the common
// error values see on Windows. (perhaps when running
// all.bat?)
return e
}
var (
modcomctl32 = windows.NewLazySystemDLL("comctl32.dll")
modcomdlg32 = windows.NewLazySystemDLL("comdlg32.dll")
modole32 = windows.NewLazySystemDLL("ole32.dll")
modshell32 = windows.NewLazySystemDLL("shell32.dll")
procInitCommonControlsEx = modcomctl32.NewProc("InitCommonControlsEx")
procChooseColorW = modcomdlg32.NewProc("ChooseColorW")
procCommDlgExtendedError = modcomdlg32.NewProc("CommDlgExtendedError")
procGetOpenFileNameW = modcomdlg32.NewProc("GetOpenFileNameW")
procGetSaveFileNameW = modcomdlg32.NewProc("GetSaveFileNameW")
procCoCreateInstance = modole32.NewProc("CoCreateInstance")
procCoTaskMemFree = modole32.NewProc("CoTaskMemFree")
procSHBrowseForFolder = modshell32.NewProc("SHBrowseForFolder")
procSHCreateItemFromParsingName = modshell32.NewProc("SHCreateItemFromParsingName")
procSHGetPathFromIDListEx = modshell32.NewProc("SHGetPathFromIDListEx")
procShell_NotifyIconW = modshell32.NewProc("Shell_NotifyIconW")
)
func InitCommonControlsEx(icc *INITCOMMONCONTROLSEX) (ok bool) {
r0, _, _ := syscall.Syscall(procInitCommonControlsEx.Addr(), 1, uintptr(unsafe.Pointer(icc)), 0, 0)
ok = r0 != 0
return
}
func ChooseColor(cc *CHOOSECOLOR) (ok bool) {
r0, _, _ := syscall.Syscall(procChooseColorW.Addr(), 1, uintptr(unsafe.Pointer(cc)), 0, 0)
ok = r0 != 0
return
}
func commDlgExtendedError() (code int) {
r0, _, _ := syscall.Syscall(procCommDlgExtendedError.Addr(), 0, 0, 0, 0)
code = int(r0)
return
}
func GetOpenFileName(ofn *OPENFILENAME) (ok bool) {
r0, _, _ := syscall.Syscall(procGetOpenFileNameW.Addr(), 1, uintptr(unsafe.Pointer(ofn)), 0, 0)
ok = r0 != 0
return
}
func GetSaveFileName(ofn *OPENFILENAME) (ok bool) {
r0, _, _ := syscall.Syscall(procGetSaveFileNameW.Addr(), 1, uintptr(unsafe.Pointer(ofn)), 0, 0)
ok = r0 != 0
return
}
func CoCreateInstance(clsid uintptr, unkOuter unsafe.Pointer, clsContext int32, iid uintptr, address unsafe.Pointer) (ret error) {
r0, _, _ := syscall.Syscall6(procCoCreateInstance.Addr(), 5, uintptr(clsid), uintptr(unkOuter), uintptr(clsContext), uintptr(iid), uintptr(address), 0)
if r0 != 0 {
ret = syscall.Errno(r0)
}
return
}
func CoTaskMemFree(address uintptr) {
syscall.Syscall(procCoTaskMemFree.Addr(), 1, uintptr(address), 0, 0)
return
}
func SHBrowseForFolder(bi *BROWSEINFO) (ptr uintptr) {
r0, _, _ := syscall.Syscall(procSHBrowseForFolder.Addr(), 1, uintptr(unsafe.Pointer(bi)), 0, 0)
ptr = uintptr(r0)
return
}
func SHCreateItemFromParsingName(path *uint16, bc unsafe.Pointer, iid uintptr, item **IShellItem) (err error) {
r1, _, e1 := syscall.Syscall6(procSHCreateItemFromParsingName.Addr(), 4, uintptr(unsafe.Pointer(path)), uintptr(bc), uintptr(iid), uintptr(unsafe.Pointer(item)), 0, 0)
if r1 == 0 {
err = errnoErr(e1)
}
return
}
func SHGetPathFromIDListEx(ptr uintptr, path *uint16, pathLen int, opts int) (err error) {
r1, _, e1 := syscall.Syscall6(procSHGetPathFromIDListEx.Addr(), 4, uintptr(ptr), uintptr(unsafe.Pointer(path)), uintptr(pathLen), uintptr(opts), 0, 0)
if r1 == 0 {
err = errnoErr(e1)
}
return
}
func ShellNotifyIcon(message uint32, data *NOTIFYICONDATA) (ret int, err error) {
r0, _, e1 := syscall.Syscall(procShell_NotifyIconW.Addr(), 2, uintptr(message), uintptr(unsafe.Pointer(data)), 0)
ret = int(r0)
if ret == 0 {
err = errnoErr(e1)
}
return
}

View file

@ -7,12 +7,12 @@ import (
"time" "time"
"unsafe" "unsafe"
"github.com/ncruces/zenity/internal/win"
"github.com/ncruces/zenity/internal/zenutil" "github.com/ncruces/zenity/internal/zenutil"
) )
var ( var (
rtlGetNtVersionNumbers = ntdll.NewProc("RtlGetNtVersionNumbers") rtlGetNtVersionNumbers = ntdll.NewProc("RtlGetNtVersionNumbers")
shellNotifyIcon = shell32.NewProc("Shell_NotifyIconW")
wtsSendMessage = wtsapi32.NewProc("WTSSendMessageW") wtsSendMessage = wtsapi32.NewProc("WTSSendMessageW")
) )
@ -21,7 +21,7 @@ func notify(text string, opts options) error {
return opts.ctx.Err() return opts.ctx.Err()
} }
var args _NOTIFYICONDATA var args win.NOTIFYICONDATA
args.StructSize = uint32(unsafe.Sizeof(args)) args.StructSize = uint32(unsafe.Sizeof(args))
args.ID = rand.Uint32() args.ID = rand.Uint32()
args.Flags = 0x00000010 // NIF_INFO args.Flags = 0x00000010 // NIF_INFO
@ -55,7 +55,7 @@ func notify(text string, opts options) error {
runtime.LockOSThread() runtime.LockOSThread()
defer runtime.UnlockOSThread() defer runtime.UnlockOSThread()
s, _, err := shellNotifyIcon.Call(0 /* NIM_ADD */, uintptr(unsafe.Pointer(&args))) s, err := win.ShellNotifyIcon(win.NIM_ADD, &args)
if s == 0 { if s == 0 {
if errno, ok := err.(syscall.Errno); ok && errno == 0 { if errno, ok := err.(syscall.Errno); ok && errno == 0 {
return wtsMessage(text, opts) return wtsMessage(text, opts)
@ -81,7 +81,7 @@ func notify(text string, opts options) error {
} }
} }
shellNotifyIcon.Call(2 /* NIM_DELETE */, uintptr(unsafe.Pointer(&args))) win.ShellNotifyIcon(win.NIM_DELETE, &args)
return nil return nil
} }
@ -125,22 +125,3 @@ func wtsMessage(text string, opts options) error {
} }
return nil return nil
} }
// https://docs.microsoft.com/en-us/windows/win32/api/shellapi/ns-shellapi-notifyicondataw
type _NOTIFYICONDATA struct {
StructSize uint32
Wnd uintptr
ID uint32
Flags uint32
CallbackMessage uint32
Icon uintptr
Tip [128]uint16 // NOTIFYICONDATAA_V1_SIZE
State uint32
StateMask uint32
Info [256]uint16
Version uint32
InfoTitle [64]uint16
InfoFlags uint32
// GuidItem [16]byte // NOTIFYICONDATAA_V2_SIZE
// BalloonIcon syscall.Handle // NOTIFYICONDATAA_V3_SIZE
}

View file

@ -3,7 +3,6 @@ package zenity
import ( import (
"bytes" "bytes"
"context" "context"
"fmt"
"os" "os"
"reflect" "reflect"
"runtime" "runtime"
@ -13,23 +12,17 @@ import (
"syscall" "syscall"
"unsafe" "unsafe"
"github.com/ncruces/zenity/internal/win"
"golang.org/x/sys/windows" "golang.org/x/sys/windows"
) )
var ( var (
comctl32 = windows.NewLazySystemDLL("comctl32.dll")
comdlg32 = windows.NewLazySystemDLL("comdlg32.dll")
gdi32 = windows.NewLazySystemDLL("gdi32.dll") gdi32 = windows.NewLazySystemDLL("gdi32.dll")
kernel32 = windows.NewLazySystemDLL("kernel32.dll") kernel32 = windows.NewLazySystemDLL("kernel32.dll")
ntdll = windows.NewLazySystemDLL("ntdll.dll") ntdll = windows.NewLazySystemDLL("ntdll.dll")
ole32 = windows.NewLazySystemDLL("ole32.dll")
shell32 = windows.NewLazySystemDLL("shell32.dll")
user32 = windows.NewLazySystemDLL("user32.dll") user32 = windows.NewLazySystemDLL("user32.dll")
wtsapi32 = windows.NewLazySystemDLL("wtsapi32.dll") wtsapi32 = windows.NewLazySystemDLL("wtsapi32.dll")
commDlgExtendedError = comdlg32.NewProc("CommDlgExtendedError")
initCommonControlsEx = comctl32.NewProc("InitCommonControlsEx")
createFontIndirect = gdi32.NewProc("CreateFontIndirectW") createFontIndirect = gdi32.NewProc("CreateFontIndirectW")
deleteObject = gdi32.NewProc("DeleteObject") deleteObject = gdi32.NewProc("DeleteObject")
getDeviceCaps = gdi32.NewProc("GetDeviceCaps") getDeviceCaps = gdi32.NewProc("GetDeviceCaps")
@ -42,11 +35,6 @@ var (
getModuleHandle = kernel32.NewProc("GetModuleHandleW") getModuleHandle = kernel32.NewProc("GetModuleHandleW")
getSystemDirectory = kernel32.NewProc("GetSystemDirectoryW") getSystemDirectory = kernel32.NewProc("GetSystemDirectoryW")
coCreateInstance = ole32.NewProc("CoCreateInstance")
coInitializeEx = ole32.NewProc("CoInitializeEx")
coTaskMemFree = ole32.NewProc("CoTaskMemFree")
coUninitialize = ole32.NewProc("CoUninitialize")
callNextHookEx = user32.NewProc("CallNextHookEx") callNextHookEx = user32.NewProc("CallNextHookEx")
createIconFromResource = user32.NewProc("CreateIconFromResource") createIconFromResource = user32.NewProc("CreateIconFromResource")
createWindowEx = user32.NewProc("CreateWindowExW") createWindowEx = user32.NewProc("CreateWindowExW")
@ -121,10 +109,10 @@ func setup() context.CancelFunc {
} }
} }
var icc _INITCOMMONCONTROLSEX var icc win.INITCOMMONCONTROLSEX
icc.Size = uint32(unsafe.Sizeof(icc)) icc.Size = uint32(unsafe.Sizeof(icc))
icc.ICC = 0x00004020 // ICC_STANDARD_CLASSES|ICC_PROGRESS_CLASS icc.ICC = 0x00004020 // ICC_STANDARD_CLASSES|ICC_PROGRESS_CLASS
initCommonControlsEx.Call(uintptr(unsafe.Pointer(&icc))) win.InitCommonControlsEx(&icc)
return func() { return func() {
if restore != 0 { if restore != 0 {
@ -147,15 +135,6 @@ func setupEnumCallback(wnd uintptr, lparam *uintptr) uintptr {
return 1 // continue enumeration return 1 // continue enumeration
} }
func commDlgError() error {
s, _, _ := commDlgExtendedError.Call()
if s == 0 {
return ErrCanceled
} else {
return fmt.Errorf("Common Dialog error: %x", s)
}
}
func hookDialog(ctx context.Context, icon any, title *string, init func(wnd uintptr)) (unhook context.CancelFunc, err error) { func hookDialog(ctx context.Context, icon any, title *string, init func(wnd uintptr)) (unhook context.CancelFunc, err error) {
if ctx != nil && ctx.Err() != nil { if ctx != nil && ctx.Err() != nil {
return nil, ctx.Err() return nil, ctx.Err()
@ -482,12 +461,6 @@ type _ACTCTX struct {
Module uintptr Module uintptr
} }
// https://docs.microsoft.com/en-us/windows/win32/api/commctrl/ns-commctrl-initcommoncontrolsex
type _INITCOMMONCONTROLSEX struct {
Size uint32
ICC uint32
}
// https://docs.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-cwpretstruct // https://docs.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-cwpretstruct
type _CWPRETSTRUCT struct { type _CWPRETSTRUCT struct {
Result uintptr Result uintptr
@ -596,19 +569,3 @@ type _IUnknownVtbl struct {
func uuid(s string) uintptr { func uuid(s string) uintptr {
return (*reflect.StringHeader)(unsafe.Pointer(&s)).Data return (*reflect.StringHeader)(unsafe.Pointer(&s)).Data
} }
type _COMObject struct{}
//go:uintptrescapes
func (o *_COMObject) Call(trap uintptr, a ...uintptr) (r1, r2 uintptr, lastErr error) {
switch nargs := uintptr(len(a)); nargs {
case 0:
return syscall.Syscall(trap, nargs+1, uintptr(unsafe.Pointer(o)), 0, 0)
case 1:
return syscall.Syscall(trap, nargs+1, uintptr(unsafe.Pointer(o)), a[0], 0)
case 2:
return syscall.Syscall(trap, nargs+1, uintptr(unsafe.Pointer(o)), a[0], a[1])
default:
panic("COM call with too many arguments.")
}
}