summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorManoloFLTK <41016272+ManoloFLTK@users.noreply.github.com>2019-06-16 12:00:38 +0200
committerManoloFLTK <41016272+ManoloFLTK@users.noreply.github.com>2019-06-16 12:00:38 +0200
commit62bce5b50a25d99aff5fc792132dd94d74744268 (patch)
treefacbbf47cd81a60145e2eb43ccc5a988f7d1a4b2
parentaa9f0a6962012991aa8a1fac005442efdc970757 (diff)
Make FLTK Windows apps "Per-Monitor-V2 DPI Aware"
Per-Monitor V2 awareness mode is supported on Windows 10 1703 or above and has window title bars correctly scaled on HighDPI screens. Before this commit, FLTK Windows apps were "Per-Monitor-V1 DPI Aware". FLTK apps detect at run-time whether the V2 mode is possible.
-rw-r--r--src/Fl_win32.cxx98
-rw-r--r--src/drivers/WinAPI/Fl_WinAPI_Screen_Driver.H2
-rw-r--r--src/drivers/WinAPI/Fl_WinAPI_Screen_Driver.cxx21
3 files changed, 68 insertions, 53 deletions
diff --git a/src/Fl_win32.cxx b/src/Fl_win32.cxx
index 915a18b52..23ebd6ccb 100644
--- a/src/Fl_win32.cxx
+++ b/src/Fl_win32.cxx
@@ -88,8 +88,7 @@ void fl_cleanup_dc_list(void);
# include <wchar.h>
#endif
-typedef HRESULT(WINAPI * SetProcessDpiAwareness_type)(int);
-static SetProcessDpiAwareness_type fl_SetProcessDpiAwareness = NULL;
+static bool is_dpi_aware = false;
extern bool fl_clipboard_notify_empty(void);
extern void fl_trigger_clipboard_notify(int source);
@@ -542,13 +541,21 @@ void Fl_WinAPI_Screen_Driver::open_display_platform() {
return;
beenHereDoneThat = 1;
- HMODULE hMod = LoadLibrary("Shcore.DLL");
- if (hMod) {
- fl_SetProcessDpiAwareness = (SetProcessDpiAwareness_type)GetProcAddress(hMod, "SetProcessDpiAwareness");
- const int PROCESS_PER_MONITOR_DPI_AWARE = 2;
+ typedef void *fl_DPI_AWARENESS_CONTEXT;
+ typedef BOOL(WINAPI * SetProcessDpiAwarenessContext_type)(fl_DPI_AWARENESS_CONTEXT);
+ SetProcessDpiAwarenessContext_type fl_SetProcessDpiAwarenessContext =
+ (SetProcessDpiAwarenessContext_type)GetProcAddress(LoadLibrary("User32.DLL"), "SetProcessDpiAwarenessContext");
+ if (fl_SetProcessDpiAwarenessContext) {
+ const fl_DPI_AWARENESS_CONTEXT fl_DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 = (fl_DPI_AWARENESS_CONTEXT)(-4);
+ is_dpi_aware = fl_SetProcessDpiAwarenessContext(fl_DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2);
+ }
+ if (!is_dpi_aware) {
+ typedef HRESULT(WINAPI * SetProcessDpiAwareness_type)(int);
+ SetProcessDpiAwareness_type fl_SetProcessDpiAwareness =
+ (SetProcessDpiAwareness_type)GetProcAddress(LoadLibrary("Shcore.DLL"), "SetProcessDpiAwareness");
if (fl_SetProcessDpiAwareness) {
- HRESULT hr = fl_SetProcessDpiAwareness(PROCESS_PER_MONITOR_DPI_AWARE);
- if (hr != S_OK) fl_SetProcessDpiAwareness = NULL;
+ const int fl_PROCESS_PER_MONITOR_DPI_AWARE = 2;
+ if (fl_SetProcessDpiAwareness(fl_PROCESS_PER_MONITOR_DPI_AWARE) == S_OK) is_dpi_aware = true;
}
}
OleInitialize(0L);
@@ -559,19 +566,18 @@ void Fl_WinAPI_Screen_Driver::open_display_platform() {
void Fl_WinAPI_Screen_Driver::desktop_scale_factor() {
typedef HRESULT(WINAPI * GetDpiForMonitor_type)(HMONITOR, int, UINT *, UINT *);
- HMODULE hMod = LoadLibrary("Shcore.DLL");
GetDpiForMonitor_type fl_GetDpiForMonitor = NULL;
- if (hMod && fl_SetProcessDpiAwareness)
- fl_GetDpiForMonitor = (GetDpiForMonitor_type)GetProcAddress(hMod, "GetDpiForMonitor");
- if (fl_GetDpiForMonitor) {
- for (int ns = 0; ns < screen_count(); ns++) {
- HMONITOR hm = MonitorFromRect(&screens[ns], MONITOR_DEFAULTTONEAREST);
- UINT dpiX, dpiY;
- HRESULT r = fl_GetDpiForMonitor(hm, 0, &dpiX, &dpiY);
- float f = (r == S_OK ? dpiX / 96. : 1);
- scale(ns, f);
- //fprintf(LOG, "desktop_scale_factor ns=%d factor=%.2f\n", ns, scale(ns));fflush(LOG);
- }
+ if (is_dpi_aware)
+ fl_GetDpiForMonitor = (GetDpiForMonitor_type)GetProcAddress(LoadLibrary("Shcore.DLL"), "GetDpiForMonitor");
+ for (int ns = 0; ns < screen_count(); ns++) {
+ HMONITOR hm = MonitorFromRect(&screens[ns], MONITOR_DEFAULTTONEAREST);
+ UINT dpiX, dpiY;
+ HRESULT r = fl_GetDpiForMonitor ? fl_GetDpiForMonitor(hm, 0, &dpiX, &dpiY) : !S_OK;
+ if (r != S_OK) { dpiX = dpiY = 96; }
+ dpi[ns][0] = dpiX;
+ dpi[ns][1] = dpiY;
+ scale(ns, dpiX / 96.);
+ //fprintf(LOG, "desktop_scale_factor ns=%d factor=%.2f dpi=%.1f\n", ns, scale(ns), dpi[ns][0]);
}
}
@@ -1196,12 +1202,14 @@ static LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lPar
switch (uMsg) {
case WM_DPICHANGED: { // 0x02E0
- if (fl_SetProcessDpiAwareness && !Fl_WinAPI_Window_Driver::data_for_resize_window_between_screens_.busy) {
+ if (is_dpi_aware && !Fl_WinAPI_Window_Driver::data_for_resize_window_between_screens_.busy) {
RECT r;
+ Fl_WinAPI_Screen_Driver *sd = (Fl_WinAPI_Screen_Driver*)Fl::screen_driver();
+ int ns = Fl_Window_Driver::driver(window)->screen_num();
+ sd->dpi[ns][0] = sd->dpi[ns][1] = HIWORD(wParam);
float f = HIWORD(wParam) / 96.;
GetClientRect(hWnd, &r);
float old_f = float(r.right) / window->w();
- int ns = Fl_Window_Driver::driver(window)->screen_num();
Fl::screen_driver()->scale(ns, f);
Fl_Window_Driver::driver(window)->resize_after_scale_change(ns, old_f, f);
}
@@ -1657,6 +1665,24 @@ static LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lPar
return DefWindowProcW(hWnd, uMsg, wParam, lParam);
}
+/* Implementation note about the API to get the dimensions of the top/left borders and the title bar
+
+ Function fake_X_wm_style() below is used before calling CreateWindowExW() to create
+ a window and before calling SetWindowPos(). Both of these Windows functions need the window size
+ including borders and title bar. Function fake_X_wm_style() uses AdjustWindowRectExForDpi() or
+ AdjustWindowRectEx() to get the sizes of borders and title bar. The gotten values don't always match
+ what is seen on the display, but they are the **required** values so the subsequent calls to
+ CreateWindowExW() or SetWindowPos() correctly size the window.
+ The Windows doc of AdjustWindowRectExForDpi/AdjustWindowRectEx makes this very clear:
+ Calculates the required size of the window rectangle, based on the desired size of the client
+ rectangle [and the provided DPI]. This window rectangle can then be passed to the CreateWindowEx
+ function to create a window with a client area of the desired size.
+
+ Conversely, Fl_WinAPI_Window_Driver::border_width_title_bar_height() is used to get
+ the true sizes of borders and title bar of a mapped window. The correct API for that is
+ DwmGetWindowAttribute().
+ */
+
////////////////////////////////////////////////////////////////
// This function gets the dimensions of the top/left borders and
// the title bar, if there is one, based on the FL_BORDER, FL_MODAL
@@ -1676,18 +1702,7 @@ static int fake_X_wm_style(const Fl_Window *w, int &X, int &Y, int &bt, int &bx,
int fallback = 1;
float s = Fl::screen_driver()->scale(Fl_Window_Driver::driver(w)->screen_num());
if (!w->parent()) {
- if (fl_xid(w)) {
- Fl_WinAPI_Window_Driver *dr = Fl_WinAPI_Window_Driver::driver(w);
- dr->border_width_title_bar_height(bx, by, bt);
- xoff = bx;
- yoff = by + bt;
- dx = 2 * bx;
- dy = 2 * by + bt;
- X = w->x() * s - bx;
- Y = w->y() * s - bt - by;
- W = w->w() * s + dx;
- H = w->h() * s + dy;
- } else if (fl_xid(w) || style) {
+ if (fl_xid(w) || style) {
// The block below calculates the window borders by requesting the
// required decorated window rectangle for a desired client rectangle.
// If any part of the function above fails, we will drop to a
@@ -1706,7 +1721,17 @@ static int fake_X_wm_style(const Fl_Window *w, int &X, int &Y, int &bt, int &bx,
r.right = (w->x() + w->w()) * s;
r.bottom = (w->y() + w->h()) * s;
// get the decoration rectangle for the desired client rectangle
- BOOL ok = AdjustWindowRectEx(&r, style, FALSE, styleEx);
+
+ typedef BOOL(WINAPI* AdjustWindowRectExForDpi_type)(LPRECT, DWORD, BOOL, DWORD, UINT);
+ static AdjustWindowRectExForDpi_type fl_AdjustWindowRectExForDpi =
+ (AdjustWindowRectExForDpi_type)GetProcAddress(LoadLibrary("User32.DLL"), "AdjustWindowRectExForDpi");
+ BOOL ok;
+ if ( fl_AdjustWindowRectExForDpi) {
+ Fl_WinAPI_Screen_Driver *sd = (Fl_WinAPI_Screen_Driver*)Fl::screen_driver();
+ UINT dpi = sd->dpi[Fl_Window_Driver::driver(w)->screen_num()][0];
+ ok = fl_AdjustWindowRectExForDpi(&r, style, FALSE, styleEx, dpi);
+ } else
+ ok = AdjustWindowRectEx(&r, style, FALSE, styleEx);
if (ok) {
X = r.left;
Y = r.top;
@@ -1803,6 +1828,7 @@ int Fl_WinAPI_Window_Driver::fake_X_wm(int &X, int &Y, int &bt, int &bx, int &by
////////////////////////////////////////////////////////////////
void Fl_WinAPI_Window_Driver::resize(int X, int Y, int W, int H) {
+//fprintf(stderr, "resize w()=%d W=%d h()=%d H=%d\n",pWindow->w(), W,pWindow->h(), H);
UINT flags = SWP_NOSENDCHANGING | SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOOWNERZORDER;
int is_a_resize = (W != w() || H != h() || is_a_rescale());
int resize_from_program = (pWindow != resize_bug_fix);
@@ -1838,7 +1864,7 @@ void Fl_WinAPI_Window_Driver::resize(int X, int Y, int W, int H) {
int dummy_x, dummy_y, bt, bx, by;
// compute window position and size in scaled units
float s = Fl::screen_driver()->scale(screen_num());
- int scaledX = ceil(X * s), scaledY = ceil(Y * s), scaledW = ceil(W * s), scaledH = ceil(H * s);
+ int scaledX = int(X * s), scaledY = int(Y * s), scaledW = int(W * s), scaledH = int(H * s);
// Ignore window managing when resizing, so that windows (and more
// specifically menus) can be moved offscreen.
if (fake_X_wm(dummy_x, dummy_y, bt, bx, by)) {
diff --git a/src/drivers/WinAPI/Fl_WinAPI_Screen_Driver.H b/src/drivers/WinAPI/Fl_WinAPI_Screen_Driver.H
index 605f3bffd..09beb1108 100644
--- a/src/drivers/WinAPI/Fl_WinAPI_Screen_Driver.H
+++ b/src/drivers/WinAPI/Fl_WinAPI_Screen_Driver.H
@@ -36,7 +36,6 @@ class FL_EXPORT Fl_WinAPI_Screen_Driver : public Fl_Screen_Driver
protected:
RECT screens[MAX_SCREENS];
RECT work_area[MAX_SCREENS];
- float dpi[MAX_SCREENS][2];
float scale_of_screen[MAX_SCREENS];
static BOOL CALLBACK screen_cb(HMONITOR mon, HDC, LPRECT r, LPARAM);
@@ -44,6 +43,7 @@ protected:
int get_mouse_unscaled(int &mx, int &my);
public:
+ float dpi[MAX_SCREENS][2];
Fl_WinAPI_Screen_Driver() : Fl_Screen_Driver() {
for (int i = 0; i < MAX_SCREENS; i++) scale_of_screen[i] = 1;
}
diff --git a/src/drivers/WinAPI/Fl_WinAPI_Screen_Driver.cxx b/src/drivers/WinAPI/Fl_WinAPI_Screen_Driver.cxx
index f390a4f50..0a079a43f 100644
--- a/src/drivers/WinAPI/Fl_WinAPI_Screen_Driver.cxx
+++ b/src/drivers/WinAPI/Fl_WinAPI_Screen_Driver.cxx
@@ -100,21 +100,6 @@ BOOL Fl_WinAPI_Screen_Driver::screen_cb(HMONITOR mon, HDC, LPRECT r)
screens[num_screens] = mi.rcMonitor;
// If we also want to record the work area, we would also store mi.rcWork at this point
work_area[num_screens] = mi.rcWork;
-//extern FILE*LOG;fprintf(LOG,"screen_cb ns=%d\n",num_screens);fflush(LOG);
- /*fl_alert("screen %d %d,%d,%d,%d work %d,%d,%d,%d",num_screens,
- screens[num_screens].left,screens[num_screens].right,screens[num_screens].top,screens[num_screens].bottom,
- work_area[num_screens].left,work_area[num_screens].right,work_area[num_screens].top,work_area[num_screens].bottom);
- */
- // find the pixel size
- if (mi.cbSize == sizeof(mi)) {
- HDC screen = CreateDC(mi.szDevice, NULL, NULL, NULL);
- if (screen) {
- dpi[num_screens][0] = (float)GetDeviceCaps(screen, LOGPIXELSX);
- dpi[num_screens][1] = (float)GetDeviceCaps(screen, LOGPIXELSY);
- }
- DeleteDC(screen);
- }
-
num_screens++;
}
return TRUE;
@@ -128,6 +113,7 @@ void Fl_WinAPI_Screen_Driver::init()
// we do a run-time check for the required functions...
HMODULE hMod = GetModuleHandle("USER32.DLL");
+ int old_num_screens = num_screens;
if (hMod) {
// check that EnumDisplayMonitors is available
fl_edm_func fl_edm = (fl_edm_func)GetProcAddress(hMod, "EnumDisplayMonitors");
@@ -142,7 +128,7 @@ void Fl_WinAPI_Screen_Driver::init()
// NOTE: num_screens is incremented in screen_cb so we must first reset it here...
num_screens = 0;
fl_edm(0, 0, screen_cb, (LPARAM)this);
- return;
+ goto way_out;
}
}
}
@@ -155,6 +141,9 @@ void Fl_WinAPI_Screen_Driver::init()
screens[0].bottom = GetSystemMetrics(SM_CYSCREEN);
work_area[0] = screens[0];
scale_of_screen[0] = 1;
+way_out:
+ // prevent desktop_scale_factor() from being called twice at app startup
+ if (old_num_screens >= 0) desktop_scale_factor();
}