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
This commit is contained in:
parent
2ff714269e
commit
999f1f7504
Notes:
blender-bot
2023-02-21 17:59:30 +01:00
Referenced by issue #88384, Window size/location are not properly saved in default start-up file
|
@ -38,6 +38,7 @@
|
|||
|
||||
#include <assert.h>
|
||||
#include <math.h>
|
||||
#include <shellscalingapi.h>
|
||||
#include <string.h>
|
||||
#include <windowsx.h>
|
||||
|
||||
|
@ -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;
|
||||
|
|
|
@ -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.
|
||||
|
|
Loading…
Reference in New Issue