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 #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(); }