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.
|
||||
Some of that is intended (reflecting platform differences),
|
||||
other bits are unfortunate limitations,
|
||||
others still are open to be fixed.
|
||||
other bits are unfortunate limitations.
|
||||
|
||||
## Why?
|
||||
|
||||
|
@ -37,7 +36,8 @@ Why reinvent this particular wheel?
|
|||
* Explorer shell not required
|
||||
* works in Server Core
|
||||
* 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)
|
||||
* on macOS:
|
||||
* only dependency is `osascript`
|
||||
|
|
|
@ -9,7 +9,7 @@ import (
|
|||
var (
|
||||
chooseColor = comdlg32.NewProc("ChooseColorW")
|
||||
|
||||
savedColors = [16]uint32{}
|
||||
savedColors [16]uint32
|
||||
colorsMutex sync.Mutex
|
||||
)
|
||||
|
||||
|
|
|
@ -37,7 +37,7 @@ func selectFile(opts options) (string, error) {
|
|||
args.Filter = &initFilters(opts.fileFilters)[0]
|
||||
}
|
||||
|
||||
res := [32768]uint16{}
|
||||
var res [32768]uint16
|
||||
args.File = &res[0]
|
||||
args.MaxFile = uint32(len(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]
|
||||
}
|
||||
|
||||
res := [32768 + 1024*256]uint16{}
|
||||
var res [32768 + 1024*256]uint16
|
||||
args.File = &res[0]
|
||||
args.MaxFile = uint32(len(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]
|
||||
}
|
||||
|
||||
res := [32768]uint16{}
|
||||
var res [32768]uint16
|
||||
args.File = &res[0]
|
||||
args.MaxFile = uint32(len(res))
|
||||
args.InitialDir, args.DefExt = initDirNameExt(opts.filename, res[:])
|
||||
|
@ -339,7 +339,7 @@ func browseForFolder(opts options) (string, []string, error) {
|
|||
}
|
||||
defer coTaskMemFree.Call(ptr)
|
||||
|
||||
res := [32768]uint16{}
|
||||
var res [32768]uint16
|
||||
shGetPathFromIDListEx.Call(ptr, uintptr(unsafe.Pointer(&res[0])), uintptr(len(res)), 0)
|
||||
|
||||
str := syscall.UTF16ToString(res[:])
|
||||
|
|
|
@ -80,7 +80,7 @@ func hookMessageLabels(kind messageKind, opts options) (unhook context.CancelFun
|
|||
return hookDialog(opts.ctx, func(wnd uintptr) {
|
||||
enumChildWindows.Call(wnd,
|
||||
syscall.NewCallback(func(wnd, lparam uintptr) uintptr {
|
||||
name := [8]uint16{}
|
||||
var name [8]uint16
|
||||
getClassName.Call(wnd, uintptr(unsafe.Pointer(&name)), uintptr(len(name)))
|
||||
if syscall.UTF16ToString(name[:]) == "Button" {
|
||||
ctl, _, _ := getDlgCtrlID.Call(wnd)
|
||||
|
|
|
@ -129,7 +129,7 @@ func progress(opts options) (ProgressDialog, error) {
|
|||
centerWindow(wnd)
|
||||
showWindow.Call(wnd, 1 /* SW_SHOWNORMAL */, 0)
|
||||
if opts.maxValue < 0 {
|
||||
sendMessage.Call(progCtl, 0x410 /* PBM_SETMARQUEE */, 1, 0)
|
||||
sendMessage.Call(progCtl, 0x40a /* PBM_SETMARQUEE */, 1, 0)
|
||||
} else {
|
||||
sendMessage.Call(progCtl, 0x402 /* PBM_SETPOS */, 33, 0)
|
||||
sendMessage.Call(progCtl, 0x406 /* PBM_SETRANGE32 */, 0, uintptr(opts.maxValue))
|
||||
|
|
|
@ -32,6 +32,10 @@ var (
|
|||
getModuleHandle = kernel32.NewProc("GetModuleHandleW")
|
||||
getCurrentThreadId = kernel32.NewProc("GetCurrentThreadId")
|
||||
getConsoleWindow = kernel32.NewProc("GetConsoleWindow")
|
||||
getSystemDirectory = kernel32.NewProc("GetSystemDirectoryW")
|
||||
createActCtx = kernel32.NewProc("CreateActCtxW")
|
||||
activateActCtx = kernel32.NewProc("ActivateActCtx")
|
||||
deactivateActCtx = kernel32.NewProc("DeactivateActCtx")
|
||||
|
||||
coInitializeEx = ole32.NewProc("CoInitializeEx")
|
||||
coUninitialize = ole32.NewProc("CoUninitialize")
|
||||
|
@ -98,15 +102,17 @@ func setup() context.CancelFunc {
|
|||
setForegroundWindow.Call(hwnd)
|
||||
}
|
||||
|
||||
var old uintptr
|
||||
runtime.LockOSThread()
|
||||
|
||||
var restore uintptr
|
||||
cookie := enableVisualStyles()
|
||||
if setThreadDpiAwarenessContext.Find() == nil {
|
||||
// try:
|
||||
// DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2
|
||||
// DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE
|
||||
// DPI_AWARENESS_CONTEXT_SYSTEM_AWARE
|
||||
for i := -4; i <= -2; i++ {
|
||||
restore, _, _ := setThreadDpiAwarenessContext.Call(uintptr(i))
|
||||
restore, _, _ = setThreadDpiAwarenessContext.Call(uintptr(i))
|
||||
if restore != 0 {
|
||||
break
|
||||
}
|
||||
|
@ -118,8 +124,11 @@ func setup() context.CancelFunc {
|
|||
icc.ICC = 0x00004020 // ICC_STANDARD_CLASSES|ICC_PROGRESS_CLASS
|
||||
|
||||
return func() {
|
||||
if old != 0 {
|
||||
setThreadDpiAwarenessContext.Call(old)
|
||||
if restore != 0 {
|
||||
setThreadDpiAwarenessContext.Call(restore)
|
||||
}
|
||||
if cookie != 0 {
|
||||
deactivateActCtx.Call(cookie)
|
||||
}
|
||||
runtime.UnlockOSThread()
|
||||
}
|
||||
|
@ -145,7 +154,7 @@ func hookDialog(ctx context.Context, initDialog func(wnd uintptr)) (unhook conte
|
|||
hook, _, err = setWindowsHookEx.Call(12, // WH_CALLWNDPROCRET
|
||||
syscall.NewCallback(func(code int32, wparam uintptr, lparam *_CWPRETSTRUCT) uintptr {
|
||||
if lparam.Message == 0x0110 { // WM_INITDIALOG
|
||||
name := [8]uint16{}
|
||||
var name [8]uint16
|
||||
getClassName.Call(lparam.Wnd, uintptr(unsafe.Pointer(&name)), uintptr(len(name)))
|
||||
if syscall.UTF16ToString(name[:]) == "#32770" { // The class for a dialog box
|
||||
var close bool
|
||||
|
@ -290,26 +299,6 @@ func registerClass(instance, proc uintptr) (uintptr, error) {
|
|||
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
|
||||
func messageLoop(wnd uintptr) error {
|
||||
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
|
||||
type _CWPRETSTRUCT struct {
|
||||
Result uintptr
|
||||
|
@ -420,12 +449,6 @@ type _WNDCLASSEX struct {
|
|||
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
|
||||
|
||||
type _IUnknownVtbl struct {
|
||||
|
|
Loading…
Reference in a new issue