From 999f1f75045c38a63f960d29e8bc7b9fd19ad0e7 Mon Sep 17 00:00:00 2001 From: Harley Acheson Date: Tue, 29 Jun 2021 09:28:20 -0700 Subject: Win32: Window Placement DPI and Scale Adjustment When using multiple monitors that differ in scale and/or dpi, the varying sizes of the window titles and borders can cause the placement of those windows to be out by a small amount. This patch adjusts for those differences on Windows 10 and newer. see D10863 for details and examples. Differential Revision: https://developer.blender.org/D10863 Reviewed by Ray Molenkamp --- intern/ghost/intern/GHOST_WindowWin32.cpp | 96 +++++++++++++++++++------------ intern/ghost/intern/GHOST_WindowWin32.h | 11 ++++ 2 files changed, 69 insertions(+), 38 deletions(-) (limited to 'intern') diff --git a/intern/ghost/intern/GHOST_WindowWin32.cpp b/intern/ghost/intern/GHOST_WindowWin32.cpp index a6666c9961c..4f85e2ea8d0 100644 --- a/intern/ghost/intern/GHOST_WindowWin32.cpp +++ b/intern/ghost/intern/GHOST_WindowWin32.cpp @@ -38,6 +38,7 @@ #include #include +#include #include #include @@ -80,13 +81,10 @@ GHOST_WindowWin32::GHOST_WindowWin32(GHOST_SystemWin32 *system, m_wintab(NULL), m_lastPointerTabletData(GHOST_TABLET_DATA_NONE), m_normal_state(GHOST_kWindowStateNormal), - m_user32(NULL), + m_user32(::LoadLibrary("user32.dll")), m_parentWindowHwnd(parentwindow ? parentwindow->m_hWnd : HWND_DESKTOP), m_debug_context(is_debug) { - wchar_t *title_16 = alloc_utf16_from_8((char *)title, 0); - RECT win_rect = {left, top, (long)(left + width), (long)(top + height)}; - DWORD style = parentwindow ? WS_POPUPWINDOW | WS_CAPTION | WS_MAXIMIZEBOX | WS_MINIMIZEBOX | WS_SIZEBOX : WS_OVERLAPPEDWINDOW; @@ -105,27 +103,10 @@ GHOST_WindowWin32::GHOST_WindowWin32(GHOST_SystemWin32 *system, */ } - /* Monitor details. */ - MONITORINFOEX monitor; - monitor.cbSize = sizeof(MONITORINFOEX); - monitor.dwFlags = 0; - GetMonitorInfo(MonitorFromRect(&win_rect, MONITOR_DEFAULTTONEAREST), &monitor); - - /* Constrain requested size and position to fit within this monitor. */ - width = min(monitor.rcWork.right - monitor.rcWork.left, win_rect.right - win_rect.left); - height = min(monitor.rcWork.bottom - monitor.rcWork.top, win_rect.bottom - win_rect.top); - win_rect.left = min(max(monitor.rcWork.left, win_rect.left), monitor.rcWork.right - width); - win_rect.right = win_rect.left + width; - win_rect.top = min(max(monitor.rcWork.top, win_rect.top), monitor.rcWork.bottom - height); - win_rect.bottom = win_rect.top + height; - - /* Adjust to allow for caption, borders, shadows, scaling, etc. Resulting values can be - * correctly outside of monitor bounds. Note: You cannot specify WS_OVERLAPPED when calling. */ - AdjustWindowRectEx(&win_rect, style & ~WS_OVERLAPPED, FALSE, extended_style); - - /* But never allow a top position that can hide part of the title bar. */ - win_rect.top = max(monitor.rcWork.top, win_rect.top); + RECT win_rect = {left, top, (long)(left + width), (long)(top + height)}; + adjustWindowRectForClosestMonitor(&win_rect, style, extended_style); + wchar_t *title_16 = alloc_utf16_from_8((char *)title, 0); m_hWnd = ::CreateWindowExW(extended_style, // window extended style s_windowClassName, // pointer to registered class name title_16, // pointer to window name @@ -153,8 +134,6 @@ GHOST_WindowWin32::GHOST_WindowWin32(GHOST_SystemWin32 *system, return; } - m_user32 = ::LoadLibrary("user32.dll"); - RegisterTouchWindow(m_hWnd, 0); /* Register as droptarget. OleInitialize(0) required first, done in GHOST_SystemWin32. */ @@ -187,22 +166,22 @@ GHOST_WindowWin32::GHOST_WindowWin32(GHOST_SystemWin32 *system, ::ShowWindow(m_hWnd, nCmdShow); #ifdef WIN32_COMPOSITING - if (alphaBackground && parentwindowhwnd == 0) { + if (alphaBackground && parentwindowhwnd == 0) { - HRESULT hr = S_OK; + HRESULT hr = S_OK; - /* Create and populate the Blur Behind structure. */ - DWM_BLURBEHIND bb = {0}; + /* Create and populate the Blur Behind structure. */ + DWM_BLURBEHIND bb = {0}; - /* Enable Blur Behind and apply to the entire client area. */ - bb.dwFlags = DWM_BB_ENABLE | DWM_BB_BLURREGION; - bb.fEnable = true; - bb.hRgnBlur = CreateRectRgn(0, 0, -1, -1); + /* Enable Blur Behind and apply to the entire client area. */ + bb.dwFlags = DWM_BB_ENABLE | DWM_BB_BLURREGION; + bb.fEnable = true; + bb.hRgnBlur = CreateRectRgn(0, 0, -1, -1); - /* Apply Blur Behind. */ - hr = DwmEnableBlurBehindWindow(m_hWnd, &bb); - DeleteObject(bb.hRgnBlur); - } + /* Apply Blur Behind. */ + hr = DwmEnableBlurBehindWindow(m_hWnd, &bb); + DeleteObject(bb.hRgnBlur); + } #endif /* Force an initial paint of the window. */ @@ -267,6 +246,47 @@ GHOST_WindowWin32::~GHOST_WindowWin32() } } +void GHOST_WindowWin32::adjustWindowRectForClosestMonitor(LPRECT win_rect, + DWORD dwStyle, + DWORD dwExStyle) +{ + /* Get Details of the closest monitor. */ + HMONITOR hmonitor = MonitorFromRect(win_rect, MONITOR_DEFAULTTONEAREST); + MONITORINFOEX monitor; + monitor.cbSize = sizeof(MONITORINFOEX); + monitor.dwFlags = 0; + GetMonitorInfo(hmonitor, &monitor); + + /* Constrain requested size and position to fit within this monitor. */ + LONG width = min(monitor.rcWork.right - monitor.rcWork.left, win_rect->right - win_rect->left); + LONG height = min(monitor.rcWork.bottom - monitor.rcWork.top, win_rect->bottom - win_rect->top); + win_rect->left = min(max(monitor.rcWork.left, win_rect->left), monitor.rcWork.right - width); + win_rect->right = win_rect->left + width; + win_rect->top = min(max(monitor.rcWork.top, win_rect->top), monitor.rcWork.bottom - height); + win_rect->bottom = win_rect->top + height; + + /* With Windows 10 and newer we can adjust for chrome that differs with DPI and scale. */ + GHOST_WIN32_AdjustWindowRectExForDpi fpAdjustWindowRectExForDpi = nullptr; + if (m_user32) { + fpAdjustWindowRectExForDpi = (GHOST_WIN32_AdjustWindowRectExForDpi)::GetProcAddress( + m_user32, "AdjustWindowRectExForDpi"); + } + + /* Adjust to allow for caption, borders, shadows, scaling, etc. Resulting values can be + * correctly outside of monitor bounds. Note: You cannot specify WS_OVERLAPPED when calling. */ + if (fpAdjustWindowRectExForDpi) { + UINT dpiX, dpiY; + GetDpiForMonitor(hmonitor, MDT_EFFECTIVE_DPI, &dpiX, &dpiY); + fpAdjustWindowRectExForDpi(win_rect, dwStyle & ~WS_OVERLAPPED, FALSE, dwExStyle, dpiX); + } + else { + AdjustWindowRectEx(win_rect, dwStyle & ~WS_OVERLAPPED, FALSE, dwExStyle); + } + + /* But never allow a top position that can hide part of the title bar. */ + win_rect->top = max(monitor.rcWork.top, win_rect->top); +} + bool GHOST_WindowWin32::getValid() const { return GHOST_Window::getValid() && m_hWnd != 0 && m_hDC != 0; diff --git a/intern/ghost/intern/GHOST_WindowWin32.h b/intern/ghost/intern/GHOST_WindowWin32.h index f28ba266ed1..119092a001a 100644 --- a/intern/ghost/intern/GHOST_WindowWin32.h +++ b/intern/ghost/intern/GHOST_WindowWin32.h @@ -43,6 +43,9 @@ class GHOST_DropTargetWin32; // typedefs for user32 functions to allow dynamic loading of Windows 10 DPI scaling functions typedef UINT(API *GHOST_WIN32_GetDpiForWindow)(HWND); +typedef BOOL(API *GHOST_WIN32_AdjustWindowRectExForDpi)( + LPRECT lpRect, DWORD dwStyle, BOOL bMenu, DWORD dwExStyle, UINT dpi); + struct GHOST_PointerInfoWin32 { GHOST_TInt32 pointerId; GHOST_TInt32 isPrimary; @@ -98,6 +101,14 @@ class GHOST_WindowWin32 : public GHOST_Window { */ ~GHOST_WindowWin32(); + /** + * Adjusts a requested window rect to fit and position correctly in monitor. + * \param win_rect: pointer to rectangle that will be modified. + * \param dwStyle: The Window Style of the window whose required size is to be calculated. + * \param dwExStyle: The Extended Window Style of the window. + */ + void adjustWindowRectForClosestMonitor(LPRECT win_rect, DWORD dwStyle, DWORD dwExStyle); + /** * Returns indication as to whether the window is valid. * \return The validity of the window. -- cgit v1.2.3