From 75f04573753e78c5c3cd4c3c733b89b07f7843d2 Mon Sep 17 00:00:00 2001 From: stffabi Date: Tue, 2 May 2023 09:39:13 +0200 Subject: [PATCH] [v3 windows] Add HiDPI awareness --- v3/pkg/application/application.go | 10 ++++- v3/pkg/application/webview_window_windows.go | 45 ++++++++++++++++---- v3/pkg/w32/constants.go | 1 + v3/pkg/w32/user32.go | 9 ++++ 4 files changed, 55 insertions(+), 10 deletions(-) diff --git a/v3/pkg/application/application.go b/v3/pkg/application/application.go index 8fb8152ed..b31347b76 100644 --- a/v3/pkg/application/application.go +++ b/v3/pkg/application/application.go @@ -1,7 +1,6 @@ package application import ( - "github.com/samber/lo" "log" "net/http" "os" @@ -9,6 +8,8 @@ import ( "strconv" "sync" + "github.com/samber/lo" + "github.com/wailsapp/wails/v2/pkg/assetserver" "github.com/wailsapp/wails/v2/pkg/assetserver/webview" assetserveroptions "github.com/wailsapp/wails/v2/pkg/options/assetserver" @@ -16,6 +17,7 @@ import ( wailsruntime "github.com/wailsapp/wails/v3/internal/runtime" "github.com/wailsapp/wails/v3/pkg/events" "github.com/wailsapp/wails/v3/pkg/logger" + "github.com/wailsapp/wails/v3/pkg/w32" ) var globalApplication *App @@ -33,6 +35,12 @@ func New(appOptions Options) *App { return globalApplication } + err := w32.SetProcessDPIAware() + if err != nil { + println("Fatal error in application initialisation: ", err.Error()) + os.Exit(1) + } + mergeApplicationDefaults(&appOptions) result := &App{ diff --git a/v3/pkg/application/webview_window_windows.go b/v3/pkg/application/webview_window_windows.go index 7f72caea5..90a05014c 100644 --- a/v3/pkg/application/webview_window_windows.go +++ b/v3/pkg/application/webview_window_windows.go @@ -36,9 +36,9 @@ func (w *windowsWebviewWindow) setTitle(title string) { } func (w *windowsWebviewWindow) setSize(width, height int) { - x, y := w.position() - // TODO: Take scaling/DPI into consideration - w32.MoveWindow(w.hwnd, x, y, width, height, true) + rect := w32.GetWindowRect(w.hwnd) + width, height = w.scaleWithWindowDPI(width, height) + w32.MoveWindow(w.hwnd, int(rect.Left), int(rect.Top), width, height, true) } func (w *windowsWebviewWindow) setAlwaysOnTop(alwaysOnTop bool) { @@ -206,7 +206,10 @@ func (w *windowsWebviewWindow) enableSizeConstraints() { func (w *windowsWebviewWindow) size() (int, int) { rect := w32.GetWindowRect(w.hwnd) - return int(rect.Right - rect.Left), int(rect.Bottom - rect.Top) + width := int(rect.Right - rect.Left) + height := int(rect.Bottom - rect.Top) + width, height = w.scaleToDefaultDPI(width, height) + return width, height } func (w *windowsWebviewWindow) setForeground() { @@ -218,18 +221,19 @@ func (w *windowsWebviewWindow) update() { } func (w *windowsWebviewWindow) width() int { - rect := w32.GetWindowRect(w.hwnd) - return int(rect.Right - rect.Left) + width, _ := w.size() + return width } func (w *windowsWebviewWindow) height() int { - rect := w32.GetWindowRect(w.hwnd) - return int(rect.Bottom - rect.Top) + _, height := w.size() + return height } func (w *windowsWebviewWindow) position() (int, int) { rect := w32.GetWindowRect(w.hwnd) - return int(rect.Left), int(rect.Right) + left, right := w.scaleToDefaultDPI(int(rect.Left), int(rect.Right)) + return left, right } func (w *windowsWebviewWindow) destroy() { @@ -293,6 +297,7 @@ func (w *windowsWebviewWindow) setHTML(html string) { } func (w *windowsWebviewWindow) setPosition(x int, y int) { + x, y = w.scaleWithWindowDPI(x, y) info := w32.GetMonitorInfoForWindow(w.hwnd) workRect := info.RcWork w32.SetWindowPos(w.hwnd, w32.HWND_TOP, int(workRect.Left)+x, int(workRect.Top)+y, 0, 0, w32.SWP_NOSIZE) @@ -635,6 +640,16 @@ func (w *windowsWebviewWindow) WndProc(msg uint32, wparam, lparam uintptr) uintp if hasConstraints { return 0 } + case w32.WM_DPICHANGED: + newWindowSize := (*w32.RECT)(unsafe.Pointer(lparam)) + w32.SetWindowPos(w.hwnd, + uintptr(0), + int(newWindowSize.Left), + int(newWindowSize.Top), + int(newWindowSize.Right-newWindowSize.Left), + int(newWindowSize.Bottom-newWindowSize.Top), + w32.SWP_NOZORDER|w32.SWP_NOACTIVATE) + } if options := w.parent.options; options.Frameless { @@ -743,10 +758,22 @@ func (w *windowsWebviewWindow) scaleWithWindowDPI(width, height int) (int, int) return scaledWidth, scaledHeight } +func (w *windowsWebviewWindow) scaleToDefaultDPI(width, height int) (int, int) { + dpix, dpiy := w.DPI() + scaledWidth := ScaleToDefaultDPI(width, dpix) + scaledHeight := ScaleToDefaultDPI(height, dpiy) + + return scaledWidth, scaledHeight +} + func ScaleWithDPI(pixels int, dpi uint) int { return (pixels * int(dpi)) / 96 } +func ScaleToDefaultDPI(pixels int, dpi uint) int { + return (pixels * 96) / int(dpi) +} + func NewIconFromResource(instance w32.HINSTANCE, resId uint16) (w32.HICON, error) { var err error var result w32.HICON diff --git a/v3/pkg/w32/constants.go b/v3/pkg/w32/constants.go index 1b200046d..a06db4fda 100644 --- a/v3/pkg/w32/constants.go +++ b/v3/pkg/w32/constants.go @@ -552,6 +552,7 @@ const ( WM_MOUSEHOVER = 0x2A1 WM_MOUSELEAVE = 0x2A3 WM_CLIPBOARDUPDATE = 0x031D + WM_DPICHANGED = 0x02E0 ) // WM_ACTIVATE diff --git a/v3/pkg/w32/user32.go b/v3/pkg/w32/user32.go index caac6fa39..002836c1f 100644 --- a/v3/pkg/w32/user32.go +++ b/v3/pkg/w32/user32.go @@ -130,6 +130,7 @@ var ( procGetMonitorInfo = moduser32.NewProc("GetMonitorInfoW") procGetDpiForSystem = moduser32.NewProc("GetDpiForSystem") procGetDpiForWindow = moduser32.NewProc("GetDpiForWindow") + procSetProcessDPIAware = moduser32.NewProc("SetProcessDPIAware") procEnumDisplayMonitors = moduser32.NewProc("EnumDisplayMonitors") procEnumDisplaySettingsEx = moduser32.NewProc("EnumDisplaySettingsExW") procChangeDisplaySettingsEx = moduser32.NewProc("ChangeDisplaySettingsExW") @@ -303,6 +304,14 @@ func GetDpiForWindow(hwnd HWND) UINT { return uint(dpi) } +func SetProcessDPIAware() error { + status, r, err := procSetProcessDPIAware.Call() + if status == 0 { + return fmt.Errorf("SetProcessDPIAware failed %d: %v %v", status, r, err) + } + return nil +} + func GetForegroundWindow() HWND { ret, _, _ := procGetForegroundWindow.Call() return HWND(ret)