mirror of
https://github.com/fltk/fltk.git
synced 2026-05-31 22:04:26 +08:00
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:
+62
-36
@@ -88,8 +88,7 @@ void fl_cleanup_dc_list(void);
|
|||||||
# include <wchar.h>
|
# include <wchar.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
typedef HRESULT(WINAPI * SetProcessDpiAwareness_type)(int);
|
static bool is_dpi_aware = false;
|
||||||
static SetProcessDpiAwareness_type fl_SetProcessDpiAwareness = NULL;
|
|
||||||
|
|
||||||
extern bool fl_clipboard_notify_empty(void);
|
extern bool fl_clipboard_notify_empty(void);
|
||||||
extern void fl_trigger_clipboard_notify(int source);
|
extern void fl_trigger_clipboard_notify(int source);
|
||||||
@@ -542,13 +541,21 @@ void Fl_WinAPI_Screen_Driver::open_display_platform() {
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
beenHereDoneThat = 1;
|
beenHereDoneThat = 1;
|
||||||
HMODULE hMod = LoadLibrary("Shcore.DLL");
|
typedef void *fl_DPI_AWARENESS_CONTEXT;
|
||||||
if (hMod) {
|
typedef BOOL(WINAPI * SetProcessDpiAwarenessContext_type)(fl_DPI_AWARENESS_CONTEXT);
|
||||||
fl_SetProcessDpiAwareness = (SetProcessDpiAwareness_type)GetProcAddress(hMod, "SetProcessDpiAwareness");
|
SetProcessDpiAwarenessContext_type fl_SetProcessDpiAwarenessContext =
|
||||||
const int PROCESS_PER_MONITOR_DPI_AWARE = 2;
|
(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) {
|
if (fl_SetProcessDpiAwareness) {
|
||||||
HRESULT hr = fl_SetProcessDpiAwareness(PROCESS_PER_MONITOR_DPI_AWARE);
|
const int fl_PROCESS_PER_MONITOR_DPI_AWARE = 2;
|
||||||
if (hr != S_OK) fl_SetProcessDpiAwareness = NULL;
|
if (fl_SetProcessDpiAwareness(fl_PROCESS_PER_MONITOR_DPI_AWARE) == S_OK) is_dpi_aware = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
OleInitialize(0L);
|
OleInitialize(0L);
|
||||||
@@ -559,19 +566,18 @@ void Fl_WinAPI_Screen_Driver::open_display_platform() {
|
|||||||
|
|
||||||
void Fl_WinAPI_Screen_Driver::desktop_scale_factor() {
|
void Fl_WinAPI_Screen_Driver::desktop_scale_factor() {
|
||||||
typedef HRESULT(WINAPI * GetDpiForMonitor_type)(HMONITOR, int, UINT *, UINT *);
|
typedef HRESULT(WINAPI * GetDpiForMonitor_type)(HMONITOR, int, UINT *, UINT *);
|
||||||
HMODULE hMod = LoadLibrary("Shcore.DLL");
|
|
||||||
GetDpiForMonitor_type fl_GetDpiForMonitor = NULL;
|
GetDpiForMonitor_type fl_GetDpiForMonitor = NULL;
|
||||||
if (hMod && fl_SetProcessDpiAwareness)
|
if (is_dpi_aware)
|
||||||
fl_GetDpiForMonitor = (GetDpiForMonitor_type)GetProcAddress(hMod, "GetDpiForMonitor");
|
fl_GetDpiForMonitor = (GetDpiForMonitor_type)GetProcAddress(LoadLibrary("Shcore.DLL"), "GetDpiForMonitor");
|
||||||
if (fl_GetDpiForMonitor) {
|
for (int ns = 0; ns < screen_count(); ns++) {
|
||||||
for (int ns = 0; ns < screen_count(); ns++) {
|
HMONITOR hm = MonitorFromRect(&screens[ns], MONITOR_DEFAULTTONEAREST);
|
||||||
HMONITOR hm = MonitorFromRect(&screens[ns], MONITOR_DEFAULTTONEAREST);
|
UINT dpiX, dpiY;
|
||||||
UINT dpiX, dpiY;
|
HRESULT r = fl_GetDpiForMonitor ? fl_GetDpiForMonitor(hm, 0, &dpiX, &dpiY) : !S_OK;
|
||||||
HRESULT r = fl_GetDpiForMonitor(hm, 0, &dpiX, &dpiY);
|
if (r != S_OK) { dpiX = dpiY = 96; }
|
||||||
float f = (r == S_OK ? dpiX / 96. : 1);
|
dpi[ns][0] = dpiX;
|
||||||
scale(ns, f);
|
dpi[ns][1] = dpiY;
|
||||||
//fprintf(LOG, "desktop_scale_factor ns=%d factor=%.2f\n", ns, scale(ns));fflush(LOG);
|
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) {
|
switch (uMsg) {
|
||||||
|
|
||||||
case WM_DPICHANGED: { // 0x02E0
|
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;
|
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.;
|
float f = HIWORD(wParam) / 96.;
|
||||||
GetClientRect(hWnd, &r);
|
GetClientRect(hWnd, &r);
|
||||||
float old_f = float(r.right) / window->w();
|
float old_f = float(r.right) / window->w();
|
||||||
int ns = Fl_Window_Driver::driver(window)->screen_num();
|
|
||||||
Fl::screen_driver()->scale(ns, f);
|
Fl::screen_driver()->scale(ns, f);
|
||||||
Fl_Window_Driver::driver(window)->resize_after_scale_change(ns, old_f, 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);
|
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
|
// 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
|
// 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;
|
int fallback = 1;
|
||||||
float s = Fl::screen_driver()->scale(Fl_Window_Driver::driver(w)->screen_num());
|
float s = Fl::screen_driver()->scale(Fl_Window_Driver::driver(w)->screen_num());
|
||||||
if (!w->parent()) {
|
if (!w->parent()) {
|
||||||
if (fl_xid(w)) {
|
if (fl_xid(w) || style) {
|
||||||
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) {
|
|
||||||
// The block below calculates the window borders by requesting the
|
// The block below calculates the window borders by requesting the
|
||||||
// required decorated window rectangle for a desired client rectangle.
|
// required decorated window rectangle for a desired client rectangle.
|
||||||
// If any part of the function above fails, we will drop to a
|
// 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.right = (w->x() + w->w()) * s;
|
||||||
r.bottom = (w->y() + w->h()) * s;
|
r.bottom = (w->y() + w->h()) * s;
|
||||||
// get the decoration rectangle for the desired client rectangle
|
// 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) {
|
if (ok) {
|
||||||
X = r.left;
|
X = r.left;
|
||||||
Y = r.top;
|
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) {
|
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;
|
UINT flags = SWP_NOSENDCHANGING | SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOOWNERZORDER;
|
||||||
int is_a_resize = (W != w() || H != h() || is_a_rescale());
|
int is_a_resize = (W != w() || H != h() || is_a_rescale());
|
||||||
int resize_from_program = (pWindow != resize_bug_fix);
|
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;
|
int dummy_x, dummy_y, bt, bx, by;
|
||||||
// compute window position and size in scaled units
|
// compute window position and size in scaled units
|
||||||
float s = Fl::screen_driver()->scale(screen_num());
|
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
|
// Ignore window managing when resizing, so that windows (and more
|
||||||
// specifically menus) can be moved offscreen.
|
// specifically menus) can be moved offscreen.
|
||||||
if (fake_X_wm(dummy_x, dummy_y, bt, bx, by)) {
|
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:
|
protected:
|
||||||
RECT screens[MAX_SCREENS];
|
RECT screens[MAX_SCREENS];
|
||||||
RECT work_area[MAX_SCREENS];
|
RECT work_area[MAX_SCREENS];
|
||||||
float dpi[MAX_SCREENS][2];
|
|
||||||
float scale_of_screen[MAX_SCREENS];
|
float scale_of_screen[MAX_SCREENS];
|
||||||
|
|
||||||
static BOOL CALLBACK screen_cb(HMONITOR mon, HDC, LPRECT r, LPARAM);
|
static BOOL CALLBACK screen_cb(HMONITOR mon, HDC, LPRECT r, LPARAM);
|
||||||
@@ -44,6 +43,7 @@ protected:
|
|||||||
int get_mouse_unscaled(int &mx, int &my);
|
int get_mouse_unscaled(int &mx, int &my);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
float dpi[MAX_SCREENS][2];
|
||||||
Fl_WinAPI_Screen_Driver() : Fl_Screen_Driver() {
|
Fl_WinAPI_Screen_Driver() : Fl_Screen_Driver() {
|
||||||
for (int i = 0; i < MAX_SCREENS; i++) scale_of_screen[i] = 1;
|
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;
|
screens[num_screens] = mi.rcMonitor;
|
||||||
// If we also want to record the work area, we would also store mi.rcWork at this point
|
// If we also want to record the work area, we would also store mi.rcWork at this point
|
||||||
work_area[num_screens] = mi.rcWork;
|
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++;
|
num_screens++;
|
||||||
}
|
}
|
||||||
return TRUE;
|
return TRUE;
|
||||||
@@ -128,6 +113,7 @@ void Fl_WinAPI_Screen_Driver::init()
|
|||||||
// we do a run-time check for the required functions...
|
// we do a run-time check for the required functions...
|
||||||
HMODULE hMod = GetModuleHandle("USER32.DLL");
|
HMODULE hMod = GetModuleHandle("USER32.DLL");
|
||||||
|
|
||||||
|
int old_num_screens = num_screens;
|
||||||
if (hMod) {
|
if (hMod) {
|
||||||
// check that EnumDisplayMonitors is available
|
// check that EnumDisplayMonitors is available
|
||||||
fl_edm_func fl_edm = (fl_edm_func)GetProcAddress(hMod, "EnumDisplayMonitors");
|
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...
|
// NOTE: num_screens is incremented in screen_cb so we must first reset it here...
|
||||||
num_screens = 0;
|
num_screens = 0;
|
||||||
fl_edm(0, 0, screen_cb, (LPARAM)this);
|
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);
|
screens[0].bottom = GetSystemMetrics(SM_CYSCREEN);
|
||||||
work_area[0] = screens[0];
|
work_area[0] = screens[0];
|
||||||
scale_of_screen[0] = 1;
|
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();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user