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.
This commit is contained in:
parent
aa9f0a6962
commit
62bce5b50a
@ -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)) {
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user