WIP: progress (windows).
This commit is contained in:
parent
de7c119f33
commit
7b98716a20
6 changed files with 64 additions and 41 deletions
|
@ -19,8 +19,7 @@ Implemented dialogs:
|
||||||
|
|
||||||
Behavior on Windows, macOS and other Unixes might differ slightly.
|
Behavior on Windows, macOS and other Unixes might differ slightly.
|
||||||
Some of that is intended (reflecting platform differences),
|
Some of that is intended (reflecting platform differences),
|
||||||
other bits are unfortunate limitations,
|
other bits are unfortunate limitations.
|
||||||
others still are open to be fixed.
|
|
||||||
|
|
||||||
## Why?
|
## Why?
|
||||||
|
|
||||||
|
@ -37,7 +36,8 @@ Why reinvent this particular wheel?
|
||||||
* Explorer shell not required
|
* Explorer shell not required
|
||||||
* works in Server Core
|
* works in Server Core
|
||||||
* Unicode support
|
* Unicode support
|
||||||
* High DPI support (no manifest required)
|
* High DPI (no manifest required)
|
||||||
|
* Visual Styles (no manifest required)
|
||||||
* WSL/Cygwin/MSYS2 [support](https://github.com/ncruces/zenity/wiki/Zenity-for-WSL,-Cygwin,-MSYS2)
|
* WSL/Cygwin/MSYS2 [support](https://github.com/ncruces/zenity/wiki/Zenity-for-WSL,-Cygwin,-MSYS2)
|
||||||
* on macOS:
|
* on macOS:
|
||||||
* only dependency is `osascript`
|
* only dependency is `osascript`
|
||||||
|
|
|
@ -9,7 +9,7 @@ import (
|
||||||
var (
|
var (
|
||||||
chooseColor = comdlg32.NewProc("ChooseColorW")
|
chooseColor = comdlg32.NewProc("ChooseColorW")
|
||||||
|
|
||||||
savedColors = [16]uint32{}
|
savedColors [16]uint32
|
||||||
colorsMutex sync.Mutex
|
colorsMutex sync.Mutex
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -37,7 +37,7 @@ func selectFile(opts options) (string, error) {
|
||||||
args.Filter = &initFilters(opts.fileFilters)[0]
|
args.Filter = &initFilters(opts.fileFilters)[0]
|
||||||
}
|
}
|
||||||
|
|
||||||
res := [32768]uint16{}
|
var res [32768]uint16
|
||||||
args.File = &res[0]
|
args.File = &res[0]
|
||||||
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[:])
|
||||||
|
@ -82,7 +82,7 @@ func selectFileMutiple(opts options) ([]string, error) {
|
||||||
args.Filter = &initFilters(opts.fileFilters)[0]
|
args.Filter = &initFilters(opts.fileFilters)[0]
|
||||||
}
|
}
|
||||||
|
|
||||||
res := [32768 + 1024*256]uint16{}
|
var res [32768 + 1024*256]uint16
|
||||||
args.File = &res[0]
|
args.File = &res[0]
|
||||||
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[:])
|
||||||
|
@ -158,7 +158,7 @@ func selectFileSave(opts options) (string, error) {
|
||||||
args.Filter = &initFilters(opts.fileFilters)[0]
|
args.Filter = &initFilters(opts.fileFilters)[0]
|
||||||
}
|
}
|
||||||
|
|
||||||
res := [32768]uint16{}
|
var res [32768]uint16
|
||||||
args.File = &res[0]
|
args.File = &res[0]
|
||||||
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[:])
|
||||||
|
@ -339,7 +339,7 @@ func browseForFolder(opts options) (string, []string, error) {
|
||||||
}
|
}
|
||||||
defer coTaskMemFree.Call(ptr)
|
defer coTaskMemFree.Call(ptr)
|
||||||
|
|
||||||
res := [32768]uint16{}
|
var res [32768]uint16
|
||||||
shGetPathFromIDListEx.Call(ptr, uintptr(unsafe.Pointer(&res[0])), uintptr(len(res)), 0)
|
shGetPathFromIDListEx.Call(ptr, uintptr(unsafe.Pointer(&res[0])), uintptr(len(res)), 0)
|
||||||
|
|
||||||
str := syscall.UTF16ToString(res[:])
|
str := syscall.UTF16ToString(res[:])
|
||||||
|
|
|
@ -80,7 +80,7 @@ func hookMessageLabels(kind messageKind, opts options) (unhook context.CancelFun
|
||||||
return hookDialog(opts.ctx, func(wnd uintptr) {
|
return hookDialog(opts.ctx, func(wnd uintptr) {
|
||||||
enumChildWindows.Call(wnd,
|
enumChildWindows.Call(wnd,
|
||||||
syscall.NewCallback(func(wnd, lparam uintptr) uintptr {
|
syscall.NewCallback(func(wnd, lparam uintptr) uintptr {
|
||||||
name := [8]uint16{}
|
var 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)
|
||||||
|
|
|
@ -129,7 +129,7 @@ func progress(opts options) (ProgressDialog, error) {
|
||||||
centerWindow(wnd)
|
centerWindow(wnd)
|
||||||
showWindow.Call(wnd, 1 /* SW_SHOWNORMAL */, 0)
|
showWindow.Call(wnd, 1 /* SW_SHOWNORMAL */, 0)
|
||||||
if opts.maxValue < 0 {
|
if opts.maxValue < 0 {
|
||||||
sendMessage.Call(progCtl, 0x410 /* PBM_SETMARQUEE */, 1, 0)
|
sendMessage.Call(progCtl, 0x40a /* PBM_SETMARQUEE */, 1, 0)
|
||||||
} else {
|
} else {
|
||||||
sendMessage.Call(progCtl, 0x402 /* PBM_SETPOS */, 33, 0)
|
sendMessage.Call(progCtl, 0x402 /* PBM_SETPOS */, 33, 0)
|
||||||
sendMessage.Call(progCtl, 0x406 /* PBM_SETRANGE32 */, 0, uintptr(opts.maxValue))
|
sendMessage.Call(progCtl, 0x406 /* PBM_SETRANGE32 */, 0, uintptr(opts.maxValue))
|
||||||
|
|
|
@ -32,6 +32,10 @@ var (
|
||||||
getModuleHandle = kernel32.NewProc("GetModuleHandleW")
|
getModuleHandle = kernel32.NewProc("GetModuleHandleW")
|
||||||
getCurrentThreadId = kernel32.NewProc("GetCurrentThreadId")
|
getCurrentThreadId = kernel32.NewProc("GetCurrentThreadId")
|
||||||
getConsoleWindow = kernel32.NewProc("GetConsoleWindow")
|
getConsoleWindow = kernel32.NewProc("GetConsoleWindow")
|
||||||
|
getSystemDirectory = kernel32.NewProc("GetSystemDirectoryW")
|
||||||
|
createActCtx = kernel32.NewProc("CreateActCtxW")
|
||||||
|
activateActCtx = kernel32.NewProc("ActivateActCtx")
|
||||||
|
deactivateActCtx = kernel32.NewProc("DeactivateActCtx")
|
||||||
|
|
||||||
coInitializeEx = ole32.NewProc("CoInitializeEx")
|
coInitializeEx = ole32.NewProc("CoInitializeEx")
|
||||||
coUninitialize = ole32.NewProc("CoUninitialize")
|
coUninitialize = ole32.NewProc("CoUninitialize")
|
||||||
|
@ -98,15 +102,17 @@ func setup() context.CancelFunc {
|
||||||
setForegroundWindow.Call(hwnd)
|
setForegroundWindow.Call(hwnd)
|
||||||
}
|
}
|
||||||
|
|
||||||
var old uintptr
|
|
||||||
runtime.LockOSThread()
|
runtime.LockOSThread()
|
||||||
|
|
||||||
|
var restore uintptr
|
||||||
|
cookie := enableVisualStyles()
|
||||||
if setThreadDpiAwarenessContext.Find() == nil {
|
if setThreadDpiAwarenessContext.Find() == nil {
|
||||||
// try:
|
// try:
|
||||||
// DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2
|
// DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2
|
||||||
// DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE
|
// DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE
|
||||||
// DPI_AWARENESS_CONTEXT_SYSTEM_AWARE
|
// DPI_AWARENESS_CONTEXT_SYSTEM_AWARE
|
||||||
for i := -4; i <= -2; i++ {
|
for i := -4; i <= -2; i++ {
|
||||||
restore, _, _ := setThreadDpiAwarenessContext.Call(uintptr(i))
|
restore, _, _ = setThreadDpiAwarenessContext.Call(uintptr(i))
|
||||||
if restore != 0 {
|
if restore != 0 {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
@ -118,8 +124,11 @@ func setup() context.CancelFunc {
|
||||||
icc.ICC = 0x00004020 // ICC_STANDARD_CLASSES|ICC_PROGRESS_CLASS
|
icc.ICC = 0x00004020 // ICC_STANDARD_CLASSES|ICC_PROGRESS_CLASS
|
||||||
|
|
||||||
return func() {
|
return func() {
|
||||||
if old != 0 {
|
if restore != 0 {
|
||||||
setThreadDpiAwarenessContext.Call(old)
|
setThreadDpiAwarenessContext.Call(restore)
|
||||||
|
}
|
||||||
|
if cookie != 0 {
|
||||||
|
deactivateActCtx.Call(cookie)
|
||||||
}
|
}
|
||||||
runtime.UnlockOSThread()
|
runtime.UnlockOSThread()
|
||||||
}
|
}
|
||||||
|
@ -145,7 +154,7 @@ func hookDialog(ctx context.Context, initDialog func(wnd uintptr)) (unhook conte
|
||||||
hook, _, err = setWindowsHookEx.Call(12, // WH_CALLWNDPROCRET
|
hook, _, err = setWindowsHookEx.Call(12, // WH_CALLWNDPROCRET
|
||||||
syscall.NewCallback(func(code int32, wparam uintptr, lparam *_CWPRETSTRUCT) uintptr {
|
syscall.NewCallback(func(code int32, wparam uintptr, lparam *_CWPRETSTRUCT) uintptr {
|
||||||
if lparam.Message == 0x0110 { // WM_INITDIALOG
|
if lparam.Message == 0x0110 { // WM_INITDIALOG
|
||||||
name := [8]uint16{}
|
var 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
|
||||||
var close bool
|
var close bool
|
||||||
|
@ -290,26 +299,6 @@ func registerClass(instance, proc uintptr) (uintptr, error) {
|
||||||
return ret, err
|
return ret, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://stackoverflow.com/questions/4308503/how-to-enable-visual-styles-without-a-manifest
|
|
||||||
// ULONG_PTR EnableVisualStyles(VOID)
|
|
||||||
// {
|
|
||||||
// TCHAR dir[MAX_PATH];
|
|
||||||
// ULONG_PTR ulpActivationCookie = FALSE;
|
|
||||||
// ACTCTX actCtx =
|
|
||||||
// {
|
|
||||||
// sizeof(actCtx),
|
|
||||||
// ACTCTX_FLAG_RESOURCE_NAME_VALID
|
|
||||||
// | ACTCTX_FLAG_SET_PROCESS_DEFAULT
|
|
||||||
// | ACTCTX_FLAG_ASSEMBLY_DIRECTORY_VALID,
|
|
||||||
// TEXT("shell32.dll"), 0, 0, dir, (LPCTSTR)124
|
|
||||||
// };
|
|
||||||
// UINT cch = GetSystemDirectory(dir, sizeof(dir) / sizeof(*dir));
|
|
||||||
// if (cch >= sizeof(dir) / sizeof(*dir)) { return FALSE; /*shouldn't happen*/ }
|
|
||||||
// dir[cch] = TEXT('\0');
|
|
||||||
// ActivateActCtx(CreateActCtx(&actCtx), &ulpActivationCookie);
|
|
||||||
// return ulpActivationCookie;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// https://docs.microsoft.com/en-us/windows/win32/winmsg/using-messages-and-message-queues
|
// https://docs.microsoft.com/en-us/windows/win32/winmsg/using-messages-and-message-queues
|
||||||
func messageLoop(wnd uintptr) error {
|
func messageLoop(wnd uintptr) error {
|
||||||
getMessage := getMessage.Addr()
|
getMessage := getMessage.Addr()
|
||||||
|
@ -335,6 +324,46 @@ func messageLoop(wnd uintptr) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// https://stackoverflow.com/questions/4308503/how-to-enable-visual-styles-without-a-manifest
|
||||||
|
func enableVisualStyles() (cookie uintptr) {
|
||||||
|
var dir [260]uint16
|
||||||
|
n, _, _ := getSystemDirectory.Call(uintptr(unsafe.Pointer(&dir[0])), uintptr(len(dir)))
|
||||||
|
if n == 0 || int(n) >= len(dir) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var ctx _ACTCTX
|
||||||
|
ctx.Size = uint32(unsafe.Sizeof(ctx))
|
||||||
|
ctx.Flags = 0x01c // ACTCTX_FLAG_RESOURCE_NAME_VALID|ACTCTX_FLAG_SET_PROCESS_DEFAULT|ACTCTX_FLAG_ASSEMBLY_DIRECTORY_VALID
|
||||||
|
ctx.Source = syscall.StringToUTF16Ptr("shell32.dll")
|
||||||
|
ctx.AssemblyDirectory = &dir[0]
|
||||||
|
ctx.ResourceName = 124
|
||||||
|
|
||||||
|
if h, _, _ := createActCtx.Call(uintptr(unsafe.Pointer(&ctx))); h != 0 {
|
||||||
|
activateActCtx.Call(h, uintptr(unsafe.Pointer(&cookie)))
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://docs.microsoft.com/en-us/windows/win32/api/winbase/ns-winbase-actctxw
|
||||||
|
type _ACTCTX struct {
|
||||||
|
Size uint32
|
||||||
|
Flags uint32
|
||||||
|
Source *uint16
|
||||||
|
ProcessorArchitecture uint16
|
||||||
|
LangId uint16
|
||||||
|
AssemblyDirectory *uint16
|
||||||
|
ResourceName uintptr
|
||||||
|
ApplicationName *uint16
|
||||||
|
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
|
||||||
|
@ -420,12 +449,6 @@ type _WNDCLASSEX struct {
|
||||||
IconSm uintptr
|
IconSm uintptr
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://docs.microsoft.com/en-us/windows/win32/api/commctrl/ns-commctrl-initcommoncontrolsex
|
|
||||||
type _INITCOMMONCONTROLSEX struct {
|
|
||||||
Size uint32
|
|
||||||
ICC uint32
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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 _IUnknownVtbl struct {
|
||||||
|
|
Loading…
Reference in a new issue