From 408d1642d33cebb380b640cfcc407321cb601038 Mon Sep 17 00:00:00 2001 From: Nuno Cruces Date: Fri, 26 Mar 2021 12:40:24 +0000 Subject: [PATCH] DPI awareness improvements (windows). --- color_windows.go | 5 +--- file_windows.go | 18 +++----------- go.mod | 8 +++--- go.sum | 11 +++++++++ msg_windows.go | 6 ++--- util_windows.go | 63 ++++++++++++++++++++++++++++++------------------ 6 files changed, 62 insertions(+), 49 deletions(-) diff --git a/color_windows.go b/color_windows.go index 53d4eac..c720f53 100644 --- a/color_windows.go +++ b/color_windows.go @@ -2,7 +2,6 @@ package zenity import ( "image/color" - "runtime" "sync" "unsafe" ) @@ -41,8 +40,7 @@ func selectColor(opts options) (color.Color, error) { args.Flags |= 0x2 // CC_FULLOPEN } - runtime.LockOSThread() - defer runtime.UnlockOSThread() + defer setup()() if opts.ctx != nil || opts.title != nil { unhook, err := hookDialogTitle(opts.ctx, opts.title) @@ -52,7 +50,6 @@ func selectColor(opts options) (color.Color, error) { defer unhook() } - activate() s, _, _ := chooseColor.Call(uintptr(unsafe.Pointer(&args))) if opts.ctx != nil && opts.ctx.Err() != nil { return nil, opts.ctx.Err() diff --git a/file_windows.go b/file_windows.go index 57c8b8c..2c0d2d9 100644 --- a/file_windows.go +++ b/file_windows.go @@ -3,7 +3,6 @@ package zenity import ( "path/filepath" "reflect" - "runtime" "syscall" "unicode/utf16" "unsafe" @@ -42,8 +41,7 @@ func selectFile(opts options) (string, error) { args.MaxFile = uint32(len(res)) args.InitialDir, args.DefExt = initDirNameExt(opts.filename, res[:]) - runtime.LockOSThread() - defer runtime.UnlockOSThread() + defer setup()() if opts.ctx != nil { unhook, err := hookDialog(opts.ctx, nil) @@ -53,7 +51,6 @@ func selectFile(opts options) (string, error) { defer unhook() } - activate() s, _, _ := getOpenFileName.Call(uintptr(unsafe.Pointer(&args))) if opts.ctx != nil && opts.ctx.Err() != nil { return "", opts.ctx.Err() @@ -89,8 +86,7 @@ func selectFileMutiple(opts options) ([]string, error) { args.MaxFile = uint32(len(res)) args.InitialDir, args.DefExt = initDirNameExt(opts.filename, res[:]) - runtime.LockOSThread() - defer runtime.UnlockOSThread() + defer setup()() if opts.ctx != nil { unhook, err := hookDialog(opts.ctx, nil) @@ -100,7 +96,6 @@ func selectFileMutiple(opts options) ([]string, error) { defer unhook() } - activate() s, _, _ := getOpenFileName.Call(uintptr(unsafe.Pointer(&args))) if opts.ctx != nil && opts.ctx.Err() != nil { return nil, opts.ctx.Err() @@ -167,8 +162,7 @@ func selectFileSave(opts options) (string, error) { args.MaxFile = uint32(len(res)) args.InitialDir, args.DefExt = initDirNameExt(opts.filename, res[:]) - runtime.LockOSThread() - defer runtime.UnlockOSThread() + defer setup()() if opts.ctx != nil { unhook, err := hookDialog(opts.ctx, nil) @@ -178,7 +172,6 @@ func selectFileSave(opts options) (string, error) { defer unhook() } - activate() s, _, _ := getSaveFileName.Call(uintptr(unsafe.Pointer(&args))) if opts.ctx != nil && opts.ctx.Err() != nil { return "", opts.ctx.Err() @@ -190,8 +183,7 @@ func selectFileSave(opts options) (string, error) { } func pickFolders(opts options, multi bool) (str string, lst []string, err error) { - runtime.LockOSThread() - defer runtime.UnlockOSThread() + defer setup()() hr, _, _ := coInitializeEx.Call(0, 0x6) // COINIT_APARTMENTTHREADED|COINIT_DISABLE_OLE1DDE if hr != 0x80010106 { // RPC_E_CHANGED_MODE @@ -253,7 +245,6 @@ func pickFolders(opts options, multi bool) (str string, lst []string, err error) defer unhook() } - activate() hr, _, _ = dialog.Call(dialog.Show, 0) if opts.ctx != nil && opts.ctx.Err() != nil { return "", nil, opts.ctx.Err() @@ -338,7 +329,6 @@ func browseForFolder(opts options) (string, []string, error) { defer unhook() } - activate() ptr, _, _ := shBrowseForFolder.Call(uintptr(unsafe.Pointer(&args))) if opts.ctx != nil && opts.ctx.Err() != nil { return "", nil, opts.ctx.Err() diff --git a/go.mod b/go.mod index e0f1666..b55b5a3 100644 --- a/go.mod +++ b/go.mod @@ -5,9 +5,9 @@ go 1.15 require ( github.com/dchest/jsmin v0.0.0-20160823214000-faeced883947 // indirect github.com/josephspurrier/goversioninfo v1.2.0 // indirect - github.com/stretchr/testify v1.6.1 // indirect - go.uber.org/goleak v1.0.0 // test - golang.org/x/image v0.0.0-20201208152932-35266b937fa6 - golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f // indirect + github.com/stretchr/testify v1.7.0 // indirect + go.uber.org/goleak v1.1.10 // test + golang.org/x/image v0.0.0-20210220032944-ac19c3e999fb + golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5 // indirect golang.org/x/tools v0.1.0 // indirect ) diff --git a/go.sum b/go.sum index 6f6e250..d70573e 100644 --- a/go.sum +++ b/go.sum @@ -13,21 +13,31 @@ github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= go.uber.org/goleak v1.0.0 h1:qsup4IcBdlmsnGfqyLl4Ntn3C2XCCuKAE7DwHpScyUo= go.uber.org/goleak v1.0.0/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= +go.uber.org/goleak v1.1.10 h1:z+mqJhf6ss6BSfSM671tgKyZBFPTTJM+HLxnhPC3wu0= +go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= 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-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/image v0.0.0-20201208152932-35266b937fa6 h1:nfeHNc1nAqecKCy2FCy4HY+soOOe5sDLJ/gZLbx6GYI= golang.org/x/image v0.0.0-20201208152932-35266b937fa6/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/image v0.0.0-20210220032944-ac19c3e999fb h1:fqpd0EBDzlHRCjiphRR5Zo/RSWWQlWv34418dnEixWk= +golang.org/x/image v0.0.0-20210220032944-ac19c3e999fb/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f h1:J5lckAjkw6qYlOZNj90mLYNTEKDvWeuc1yieZ8qUzUE= golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= +golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5 h1:2M3HP5CCK1Si9FQhwnzYhXdG6DXeebvUHFpre8QvbyI= +golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= @@ -48,6 +58,7 @@ golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3 golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.1.0 h1:po9/4sTYwZU9lPhi1tOrb4hCv3qrhiQ77LZfGa2OjwY= golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/msg_windows.go b/msg_windows.go index 8342b3e..8423398 100644 --- a/msg_windows.go +++ b/msg_windows.go @@ -2,7 +2,6 @@ package zenity import ( "context" - "runtime" "syscall" "unsafe" ) @@ -42,10 +41,9 @@ func message(kind messageKind, text string, opts options) (bool, error) { } } - if opts.ctx != nil || opts.okLabel != nil || opts.cancelLabel != nil || opts.extraButton != nil { - runtime.LockOSThread() - defer runtime.UnlockOSThread() + defer setup()() + if opts.ctx != nil || opts.okLabel != nil || opts.cancelLabel != nil || opts.extraButton != nil { unhook, err := hookMessageLabels(kind, opts) if err != nil { return false, err diff --git a/util_windows.go b/util_windows.go index 76a62e0..ea0019e 100644 --- a/util_windows.go +++ b/util_windows.go @@ -5,7 +5,8 @@ import ( "fmt" "os" "reflect" - "sync" + "runtime" + "sync/atomic" "syscall" "unsafe" ) @@ -28,21 +29,22 @@ var ( coCreateInstance = ole32.NewProc("CoCreateInstance") coTaskMemFree = ole32.NewProc("CoTaskMemFree") - getMessage = user32.NewProc("GetMessageW") - sendMessage = user32.NewProc("SendMessageW") - getClassName = user32.NewProc("GetClassNameW") - setWindowsHookEx = user32.NewProc("SetWindowsHookExW") - unhookWindowsHookEx = user32.NewProc("UnhookWindowsHookEx") - callNextHookEx = user32.NewProc("CallNextHookEx") - enumWindows = user32.NewProc("EnumWindows") - enumChildWindows = user32.NewProc("EnumChildWindows") - getDlgCtrlID = user32.NewProc("GetDlgCtrlID") - setWindowText = user32.NewProc("SetWindowTextW") - setForegroundWindow = user32.NewProc("SetForegroundWindow") - getWindowThreadProcessId = user32.NewProc("GetWindowThreadProcessId") + getMessage = user32.NewProc("GetMessageW") + sendMessage = user32.NewProc("SendMessageW") + getClassName = user32.NewProc("GetClassNameW") + setWindowsHookEx = user32.NewProc("SetWindowsHookExW") + unhookWindowsHookEx = user32.NewProc("UnhookWindowsHookEx") + callNextHookEx = user32.NewProc("CallNextHookEx") + enumWindows = user32.NewProc("EnumWindows") + enumChildWindows = user32.NewProc("EnumChildWindows") + getDlgCtrlID = user32.NewProc("GetDlgCtrlID") + setWindowText = user32.NewProc("SetWindowTextW") + setForegroundWindow = user32.NewProc("SetForegroundWindow") + getWindowThreadProcessId = user32.NewProc("GetWindowThreadProcessId") + setThreadDpiAwarenessContext = user32.NewProc("SetThreadDpiAwarenessContext") ) -func activate() { +func setup() context.CancelFunc { var hwnd uintptr enumWindows.Call(syscall.NewCallback(func(wnd, lparam uintptr) uintptr { var pid uintptr @@ -59,6 +61,28 @@ func activate() { if hwnd != 0 { setForegroundWindow.Call(hwnd) } + + var old uintptr + runtime.LockOSThread() + 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)) + if restore != 0 { + break + } + } + } + + return func() { + if old != 0 { + setThreadDpiAwarenessContext.Call(old) + runtime.UnlockOSThread() + } + } } func commDlgError() error { @@ -84,7 +108,6 @@ func hookDialog(ctx context.Context, initDialog func(wnd uintptr)) (unhook conte return nil, ctx.Err() } - var mtx sync.Mutex var hook, wnd uintptr tid, _, _ := getCurrentThreadId.Call() hook, _, err = setWindowsHookEx.Call(12, // WH_CALLWNDPROCRET @@ -95,13 +118,11 @@ func hookDialog(ctx context.Context, initDialog func(wnd uintptr)) (unhook conte if syscall.UTF16ToString(name[:]) == "#32770" { // The class for a dialog box var close bool - mtx.Lock() if ctx != nil && ctx.Err() != nil { close = true } else { - wnd = lparam.Wnd + atomic.StoreUintptr(&wnd, lparam.Wnd) } - mtx.Unlock() if close { sendMessage.Call(lparam.Wnd, 0x0112 /* WM_SYSCOMMAND */, 0xf060 /* SC_CLOSE */, 0) @@ -125,11 +146,7 @@ func hookDialog(ctx context.Context, initDialog func(wnd uintptr)) (unhook conte go func() { select { case <-ctx.Done(): - mtx.Lock() - w := wnd - mtx.Unlock() - - if w != 0 { + if w := atomic.LoadUintptr(&wnd); w != 0 { sendMessage.Call(w, 0x0112 /* WM_SYSCOMMAND */, 0xf060 /* SC_CLOSE */, 0) } case <-wait: