From 45d98f689a35f7b534fef7d38f3992125410af53 Mon Sep 17 00:00:00 2001 From: Bouke Haarsma Date: Wed, 7 Sep 2022 22:17:10 +0200 Subject: [PATCH] Fix #9993: Handle DPI changes on macOS and Windows --- os/windows/openttd.manifest | 1 + src/gfx.cpp | 34 ++++++++++++++++++++++++++++++++++ src/gfx_func.h | 1 + src/video/cocoa/cocoa_wnd.mm | 4 ++++ src/video/win32_v.cpp | 22 ++++++++++++++++++++++ src/window.cpp | 15 +++++++++++++-- src/zoom_func.h | 11 +++++++++++ 7 files changed, 86 insertions(+), 2 deletions(-) diff --git a/os/windows/openttd.manifest b/os/windows/openttd.manifest index ee1c7ea224..cb536a819d 100644 --- a/os/windows/openttd.manifest +++ b/os/windows/openttd.manifest @@ -10,6 +10,7 @@ True/PM + PerMonitorV2,PerMonitor diff --git a/src/gfx.cpp b/src/gfx.cpp index cb9fb0651b..d96b2bdac4 100644 --- a/src/gfx.cpp +++ b/src/gfx.cpp @@ -17,10 +17,12 @@ #include "settings_type.h" #include "network/network.h" #include "network/network_func.h" +#include "window_gui.h" #include "window_func.h" #include "newgrf_debug.h" #include "thread.h" #include "core/backup_type.hpp" +#include "viewport_func.h" #include "table/palettes.h" #include "table/string_colours.h" @@ -2042,6 +2044,38 @@ void UpdateGUIZoom() } } +/** + * Resolve GUI zoom level and adjust GUI to new zoom, if auto-suggestion is requested. + * @returns true when the zoom level has changed, caller must call ReInitAllWindows(true) + * after resizing the application's window/buffer. + */ +bool AdjustGUIZoom() +{ + auto old_zoom = _gui_zoom; + UpdateGUIZoom(); + if (old_zoom == _gui_zoom) return false; + GfxClearSpriteCache(); + VideoDriver::GetInstance()->ClearSystemSprites(); + ClearFontCache(); + GfxClearSpriteCache(); + UpdateAllVirtCoords(); + + /* Adjust all window sizes to match the new zoom level, so that they don't appear + to move around when the application is moved to a screen with different DPI. */ + auto zoom_shift = old_zoom - _gui_zoom; + for (Window *w : Window::Iterate()) { + w->left = AdjustByZoom(w->left, zoom_shift); + w->top = AdjustByZoom(w->top, zoom_shift); + w->width = AdjustByZoom(w->width, zoom_shift); + w->height = AdjustByZoom(w->height, zoom_shift); + if (w->viewport != nullptr) { + w->viewport->zoom = Clamp(ZoomLevel(w->viewport->zoom - zoom_shift), _settings_client.gui.zoom_min, _settings_client.gui.zoom_max); + } + } + + return true; +} + void ChangeGameSpeed(bool enable_fast_forward) { if (enable_fast_forward) { diff --git a/src/gfx_func.h b/src/gfx_func.h index b811c089fe..4ebb85aed2 100644 --- a/src/gfx_func.h +++ b/src/gfx_func.h @@ -80,6 +80,7 @@ void DrawMouseCursor(); void ScreenSizeChanged(); void GameSizeChanged(); void UpdateGUIZoom(); +bool AdjustGUIZoom(); void UndrawMouseCursor(); /** Size of the buffer used for drawing strings. */ diff --git a/src/video/cocoa/cocoa_wnd.mm b/src/video/cocoa/cocoa_wnd.mm index af1bdcab99..36d010177d 100644 --- a/src/video/cocoa/cocoa_wnd.mm +++ b/src/video/cocoa/cocoa_wnd.mm @@ -1269,8 +1269,12 @@ void CocoaDialog(const char *title, const char *message, const char *buttonLabel /** Screen the window is on changed. */ - (void)windowDidChangeBackingProperties:(NSNotification *)notification { + bool did_adjust = AdjustGUIZoom(); + /* Reallocate screen buffer if necessary. */ driver->AllocateBackingStore(); + + if (did_adjust) ReInitAllWindows(true); } /** Presentation options to use for full screen mode. */ diff --git a/src/video/win32_v.cpp b/src/video/win32_v.cpp index 44368b2249..49c967bfa4 100644 --- a/src/video/win32_v.cpp +++ b/src/video/win32_v.cpp @@ -38,6 +38,10 @@ #define PM_QS_INPUT 0x20000 #endif +#ifndef WM_DPICHANGED +#define WM_DPICHANGED 0x02E0 +#endif + bool _window_maximize; static Dimension _bck_resolution; DWORD _imm_props; @@ -670,6 +674,24 @@ LRESULT CALLBACK WndProcGdi(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) return TRUE; } + case WM_DPICHANGED: { + auto did_adjust = AdjustGUIZoom(); + + /* Resize the window to match the new DPI setting. */ + RECT *prcNewWindow = (RECT *)lParam; + SetWindowPos(hwnd, + NULL, + prcNewWindow->left, + prcNewWindow->top, + prcNewWindow->right - prcNewWindow->left, + prcNewWindow->bottom - prcNewWindow->top, + SWP_NOZORDER | SWP_NOACTIVATE); + + if (did_adjust) ReInitAllWindows(true); + + return 0; + } + /* needed for wheel */ #if !defined(WM_MOUSEWHEEL) # define WM_MOUSEWHEEL 0x020A diff --git a/src/window.cpp b/src/window.cpp index 96cc5b3b97..603bca151f 100644 --- a/src/window.cpp +++ b/src/window.cpp @@ -3343,6 +3343,13 @@ void HideVitalWindows() CloseWindowById(WC_STATUS_BAR, 0); } +void ReInitWindow(Window *w, bool zoom_changed) +{ + if (w == nullptr) return; + if (zoom_changed) w->nested_root->AdjustPaddingForZoom(); + w->ReInit(); +} + /** Re-initialize all windows. */ void ReInitAllWindows(bool zoom_changed) { @@ -3352,9 +3359,13 @@ void ReInitAllWindows(bool zoom_changed) extern void InitDepotWindowBlockSizes(); InitDepotWindowBlockSizes(); + /* When _gui_zoom has changed, we need to resize toolbar and statusbar first, + * so EnsureVisibleCaption uses the updated size information. */ + ReInitWindow(FindWindowById(WC_MAIN_TOOLBAR, 0), zoom_changed); + ReInitWindow(FindWindowById(WC_STATUS_BAR, 0), zoom_changed); for (Window *w : Window::Iterate()) { - if (zoom_changed) w->nested_root->AdjustPaddingForZoom(); - w->ReInit(); + if (w->window_class == WC_MAIN_TOOLBAR || w->window_class == WC_STATUS_BAR) continue; + ReInitWindow(w, zoom_changed); } void NetworkReInitChatBoxSize(); diff --git a/src/zoom_func.h b/src/zoom_func.h index 485284a06c..0a5113186f 100644 --- a/src/zoom_func.h +++ b/src/zoom_func.h @@ -36,6 +36,17 @@ static inline int UnScaleByZoom(int value, ZoomLevel zoom) return (value + (1 << zoom) - 1) >> zoom; } +/** + * Adjust by zoom level; zoom < 0 shifts right, zoom >= 0 shifts left + * @param value value to shift + * @param zoom zoom level to shift to + * @return shifted value + */ +static inline int AdjustByZoom(int value, int zoom) +{ + return zoom < 0 ? UnScaleByZoom(value, ZoomLevel(-zoom)) : ScaleByZoom(value, ZoomLevel(zoom)); +} + /** * Scale by zoom level, usually shift left (when zoom > ZOOM_LVL_NORMAL) * @param value value to shift