Skip to content

Commit 92b2ea8

Browse files
committed
Add per-monitor DPI scaling for Windows
Use per-monitor DPI scaling across Windows platform code: apply GetScaleFactorForMonitor/Window to convert between physical pixels and logical coordinates for displays, cursor, and window geometry. Updated DisplayManager::GetCursorPosition, Display::GetPosition/GetSize/GetWorkArea/GetScaleFactor, and many Window methods (SetBounds/GetBounds/SetSize/GetSize/SetContentSize/GetContentSize/SetContentBounds/SetPosition/GetPosition and min/max handling) to scale values and round when setting sizes/positions. Added dpi_utils_windows.h declaration and exposed GetScaleFactorForMonitor in dpi_utils_windows.cpp, included dpi header where needed, and added <cmath> for std::lround. All DPI calls fallback to 1.0 when unavailable; added a comment noting Center uses physical pixels.
1 parent 784fa08 commit 92b2ea8

5 files changed

Lines changed: 101 additions & 41 deletions

File tree

src/platform/windows/display_manager_windows.cpp

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#include "../../display.h"
77
#include "../../display_event.h"
88
#include "../../display_manager.h"
9+
#include "dpi_utils_windows.h"
910

1011
namespace nativeapi {
1112

@@ -68,7 +69,14 @@ Display DisplayManager::GetPrimary() {
6869
Point DisplayManager::GetCursorPosition() {
6970
POINT cursorPos;
7071
if (GetCursorPos(&cursorPos)) {
71-
return {static_cast<double>(cursorPos.x), static_cast<double>(cursorPos.y)};
72+
// Determine which monitor the cursor is on for DPI scaling
73+
HMONITOR hMonitor =
74+
MonitorFromPoint(cursorPos, MONITOR_DEFAULTTONEAREST);
75+
double scale = GetScaleFactorForMonitor(hMonitor);
76+
if (scale <= 0.0)
77+
scale = 1.0;
78+
return {static_cast<double>(cursorPos.x) / scale,
79+
static_cast<double>(cursorPos.y) / scale};
7280
}
7381
return {0.0, 0.0};
7482
}

src/platform/windows/display_windows.cpp

Lines changed: 20 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#include "../../display.h"
22

33
#include <windows.h>
4+
#include "dpi_utils_windows.h"
45
#include "string_utils_windows.h"
56

67
namespace nativeapi {
@@ -73,38 +74,44 @@ Point Display::GetPosition() const {
7374
return {0.0, 0.0};
7475
MONITORINFOEXW monitorInfo = GetMonitorInfoEx(pimpl_->h_monitor_);
7576
RECT rect = monitorInfo.rcMonitor;
76-
return {static_cast<double>(rect.left), static_cast<double>(rect.top)};
77+
double scale = GetScaleFactorForMonitor(pimpl_->h_monitor_);
78+
if (scale <= 0.0)
79+
scale = 1.0;
80+
return {static_cast<double>(rect.left) / scale,
81+
static_cast<double>(rect.top) / scale};
7782
}
7883

7984
Size Display::GetSize() const {
8085
if (!pimpl_->h_monitor_)
8186
return {0.0, 0.0};
8287
MONITORINFOEXW monitorInfo = GetMonitorInfoEx(pimpl_->h_monitor_);
8388
RECT rect = monitorInfo.rcMonitor;
84-
return {static_cast<double>(rect.right - rect.left), static_cast<double>(rect.bottom - rect.top)};
89+
double scale = GetScaleFactorForMonitor(pimpl_->h_monitor_);
90+
if (scale <= 0.0)
91+
scale = 1.0;
92+
return {static_cast<double>(rect.right - rect.left) / scale,
93+
static_cast<double>(rect.bottom - rect.top) / scale};
8594
}
8695

8796
Rectangle Display::GetWorkArea() const {
8897
if (!pimpl_->h_monitor_)
8998
return {0.0, 0.0, 0.0, 0.0};
9099
MONITORINFOEXW monitorInfo = GetMonitorInfoEx(pimpl_->h_monitor_);
91100
RECT workRect = monitorInfo.rcWork;
92-
return {static_cast<double>(workRect.left), static_cast<double>(workRect.top),
93-
static_cast<double>(workRect.right - workRect.left),
94-
static_cast<double>(workRect.bottom - workRect.top)};
101+
double scale = GetScaleFactorForMonitor(pimpl_->h_monitor_);
102+
if (scale <= 0.0)
103+
scale = 1.0;
104+
return {static_cast<double>(workRect.left) / scale,
105+
static_cast<double>(workRect.top) / scale,
106+
static_cast<double>(workRect.right - workRect.left) / scale,
107+
static_cast<double>(workRect.bottom - workRect.top) / scale};
95108
}
96109

97110
double Display::GetScaleFactor() const {
98111
if (!pimpl_->h_monitor_)
99112
return 1.0;
100-
HDC hdc = GetDC(nullptr);
101-
double scaleFactor = 1.0;
102-
if (hdc) {
103-
int dpiX = GetDeviceCaps(hdc, LOGPIXELSX);
104-
scaleFactor = dpiX / 96.0; // 96 DPI is 100% scale
105-
ReleaseDC(nullptr, hdc);
106-
}
107-
return scaleFactor;
113+
double scale = GetScaleFactorForMonitor(pimpl_->h_monitor_);
114+
return (scale > 0.0) ? scale : 1.0;
108115
}
109116

110117
bool Display::IsPrimary() const {

src/platform/windows/dpi_utils_windows.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
namespace nativeapi {
44

55
// Internal: per-monitor DPI via Shcore when available
6-
static double GetScaleFactorForMonitor(HMONITOR hmonitor) {
6+
double GetScaleFactorForMonitor(HMONITOR hmonitor) {
77
if (!hmonitor)
88
return 1.0;
99
typedef HRESULT(WINAPI * GetDpiForMonitorFunc)(HMONITOR, int, UINT*, UINT*);

src/platform/windows/dpi_utils_windows.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,7 @@ namespace nativeapi {
66
// Returns the DPI scale factor for the given window (1.0 at 96 DPI)
77
double GetScaleFactorForWindow(HWND hwnd);
88

9+
// Returns the DPI scale factor for the given monitor (1.0 at 96 DPI)
10+
double GetScaleFactorForMonitor(HMONITOR hmonitor);
11+
912
} // namespace nativeapi

src/platform/windows/window_windows.cpp

Lines changed: 68 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#include <dwmapi.h>
22
#include <windows.h>
3+
#include <cmath>
34
#include <iostream>
45
#include "../../foundation/id_allocator.h"
56
#include "../../window.h"
@@ -346,8 +347,14 @@ bool Window::IsFullScreen() const {
346347

347348
void Window::SetBounds(Rectangle bounds) {
348349
if (pimpl_->hwnd_) {
349-
SetWindowPos(pimpl_->hwnd_, nullptr, static_cast<int>(bounds.x), static_cast<int>(bounds.y),
350-
static_cast<int>(bounds.width), static_cast<int>(bounds.height), SWP_NOZORDER);
350+
double scale = GetScaleFactorForWindow(pimpl_->hwnd_);
351+
if (scale <= 0.0)
352+
scale = 1.0;
353+
SetWindowPos(pimpl_->hwnd_, nullptr,
354+
static_cast<int>(std::lround(bounds.x * scale)),
355+
static_cast<int>(std::lround(bounds.y * scale)),
356+
static_cast<int>(std::lround(bounds.width * scale)),
357+
static_cast<int>(std::lround(bounds.height * scale)), SWP_NOZORDER);
351358
}
352359
}
353360

@@ -356,10 +363,13 @@ Rectangle Window::GetBounds() const {
356363
if (pimpl_->hwnd_) {
357364
RECT rect;
358365
GetWindowRect(pimpl_->hwnd_, &rect);
359-
bounds.x = rect.left;
360-
bounds.y = rect.top;
361-
bounds.width = rect.right - rect.left;
362-
bounds.height = rect.bottom - rect.top;
366+
double scale = GetScaleFactorForWindow(pimpl_->hwnd_);
367+
if (scale <= 0.0)
368+
scale = 1.0;
369+
bounds.x = static_cast<double>(rect.left) / scale;
370+
bounds.y = static_cast<double>(rect.top) / scale;
371+
bounds.width = static_cast<double>(rect.right - rect.left) / scale;
372+
bounds.height = static_cast<double>(rect.bottom - rect.top) / scale;
363373
}
364374
return bounds;
365375
}
@@ -368,8 +378,13 @@ void Window::SetSize(Size size, bool animate) {
368378
if (pimpl_->hwnd_) {
369379
// Windows doesn't have built-in animation for window resizing
370380
// Animation would require custom implementation
371-
SetWindowPos(pimpl_->hwnd_, nullptr, 0, 0, static_cast<int>(size.width),
372-
static_cast<int>(size.height), SWP_NOMOVE | SWP_NOZORDER);
381+
double scale = GetScaleFactorForWindow(pimpl_->hwnd_);
382+
if (scale <= 0.0)
383+
scale = 1.0;
384+
SetWindowPos(pimpl_->hwnd_, nullptr, 0, 0,
385+
static_cast<int>(std::lround(size.width * scale)),
386+
static_cast<int>(std::lround(size.height * scale)),
387+
SWP_NOMOVE | SWP_NOZORDER);
373388
}
374389
}
375390

@@ -378,8 +393,11 @@ Size Window::GetSize() const {
378393
if (pimpl_->hwnd_) {
379394
RECT rect;
380395
GetWindowRect(pimpl_->hwnd_, &rect);
381-
size.width = rect.right - rect.left;
382-
size.height = rect.bottom - rect.top;
396+
double scale = GetScaleFactorForWindow(pimpl_->hwnd_);
397+
if (scale <= 0.0)
398+
scale = 1.0;
399+
size.width = static_cast<double>(rect.right - rect.left) / scale;
400+
size.height = static_cast<double>(rect.bottom - rect.top) / scale;
383401
}
384402
return size;
385403
}
@@ -394,8 +412,13 @@ void Window::SetContentSize(Size size) {
394412
int borderWidth = (windowRect.right - windowRect.left) - clientRect.right;
395413
int borderHeight = (windowRect.bottom - windowRect.top) - clientRect.bottom;
396414

397-
SetWindowPos(pimpl_->hwnd_, nullptr, 0, 0, static_cast<int>(size.width) + borderWidth,
398-
static_cast<int>(size.height) + borderHeight, SWP_NOMOVE | SWP_NOZORDER);
415+
double scale = GetScaleFactorForWindow(pimpl_->hwnd_);
416+
if (scale <= 0.0)
417+
scale = 1.0;
418+
SetWindowPos(pimpl_->hwnd_, nullptr, 0, 0,
419+
static_cast<int>(std::lround(size.width * scale)) + borderWidth,
420+
static_cast<int>(std::lround(size.height * scale)) + borderHeight,
421+
SWP_NOMOVE | SWP_NOZORDER);
399422
}
400423
}
401424

@@ -404,8 +427,11 @@ Size Window::GetContentSize() const {
404427
if (pimpl_->hwnd_) {
405428
RECT rect;
406429
GetClientRect(pimpl_->hwnd_, &rect);
407-
size.width = rect.right;
408-
size.height = rect.bottom;
430+
double scale = GetScaleFactorForWindow(pimpl_->hwnd_);
431+
if (scale <= 0.0)
432+
scale = 1.0;
433+
size.width = static_cast<double>(rect.right) / scale;
434+
size.height = static_cast<double>(rect.bottom) / scale;
409435
}
410436
return size;
411437
}
@@ -428,11 +454,15 @@ void Window::SetContentBounds(Rectangle bounds) {
428454
int offsetX = clientTopLeft.x - windowRect.left;
429455
int offsetY = clientTopLeft.y - windowRect.top;
430456

457+
double scale = GetScaleFactorForWindow(pimpl_->hwnd_);
458+
if (scale <= 0.0)
459+
scale = 1.0;
460+
431461
// Calculate window position so that client area is at bounds position
432-
int windowX = static_cast<int>(bounds.x) - offsetX;
433-
int windowY = static_cast<int>(bounds.y) - offsetY;
434-
int windowWidth = static_cast<int>(bounds.width) + borderWidth;
435-
int windowHeight = static_cast<int>(bounds.height) + borderHeight;
462+
int windowX = static_cast<int>(std::lround(bounds.x * scale)) - offsetX;
463+
int windowY = static_cast<int>(std::lround(bounds.y * scale)) - offsetY;
464+
int windowWidth = static_cast<int>(std::lround(bounds.width * scale)) + borderWidth;
465+
int windowHeight = static_cast<int>(std::lround(bounds.height * scale)) + borderHeight;
436466

437467
SetWindowPos(pimpl_->hwnd_, nullptr, windowX, windowY, windowWidth, windowHeight, SWP_NOZORDER);
438468
}
@@ -488,13 +518,16 @@ static int RegisterMinMaxInfoHandler(HWND hwnd, int existing_handler_id) {
488518
auto minSize = window->GetMinimumSize();
489519
auto maxSize = window->GetMaximumSize();
490520
MINMAXINFO* mmi = reinterpret_cast<MINMAXINFO*>(lparam);
521+
double scale_mm = GetScaleFactorForWindow(hwnd);
522+
if (scale_mm <= 0.0)
523+
scale_mm = 1.0;
491524
if (minSize.width > 0 && minSize.height > 0) {
492-
mmi->ptMinTrackSize.x = static_cast<LONG>(minSize.width);
493-
mmi->ptMinTrackSize.y = static_cast<LONG>(minSize.height);
525+
mmi->ptMinTrackSize.x = static_cast<LONG>(std::lround(minSize.width * scale_mm));
526+
mmi->ptMinTrackSize.y = static_cast<LONG>(std::lround(minSize.height * scale_mm));
494527
}
495528
if (maxSize.width > 0 && maxSize.height > 0) {
496-
mmi->ptMaxTrackSize.x = static_cast<LONG>(maxSize.width);
497-
mmi->ptMaxTrackSize.y = static_cast<LONG>(maxSize.height);
529+
mmi->ptMaxTrackSize.x = static_cast<LONG>(std::lround(maxSize.width * scale_mm));
530+
mmi->ptMaxTrackSize.y = static_cast<LONG>(std::lround(maxSize.height * scale_mm));
498531
}
499532
return std::make_optional(0);
500533
}
@@ -668,8 +701,13 @@ bool Window::IsAlwaysOnTop() const {
668701

669702
void Window::SetPosition(Point point) {
670703
if (pimpl_->hwnd_) {
671-
SetWindowPos(pimpl_->hwnd_, nullptr, static_cast<int>(point.x), static_cast<int>(point.y), 0, 0,
672-
SWP_NOSIZE | SWP_NOZORDER);
704+
double scale = GetScaleFactorForWindow(pimpl_->hwnd_);
705+
if (scale <= 0.0)
706+
scale = 1.0;
707+
SetWindowPos(pimpl_->hwnd_, nullptr,
708+
static_cast<int>(std::lround(point.x * scale)),
709+
static_cast<int>(std::lround(point.y * scale)),
710+
0, 0, SWP_NOSIZE | SWP_NOZORDER);
673711
}
674712
}
675713

@@ -678,8 +716,11 @@ Point Window::GetPosition() const {
678716
if (pimpl_->hwnd_) {
679717
RECT rect;
680718
GetWindowRect(pimpl_->hwnd_, &rect);
681-
point.x = rect.left;
682-
point.y = rect.top;
719+
double scale = GetScaleFactorForWindow(pimpl_->hwnd_);
720+
if (scale <= 0.0)
721+
scale = 1.0;
722+
point.x = static_cast<double>(rect.left) / scale;
723+
point.y = static_cast<double>(rect.top) / scale;
683724
}
684725
return point;
685726
}
@@ -700,6 +741,7 @@ void Window::Center() {
700741
GetMonitorInfo(monitor, &mi);
701742

702743
// Calculate the center position on the monitor's work area
744+
// All values here are in physical pixels (GetWindowRect and rcWork), so no DPI scaling needed
703745
int centerX = mi.rcWork.left + (mi.rcWork.right - mi.rcWork.left - windowWidth) / 2;
704746
int centerY = mi.rcWork.top + (mi.rcWork.bottom - mi.rcWork.top - windowHeight) / 2;
705747

0 commit comments

Comments
 (0)