From 9c193e17741af11f8ae546a6312037b2a07ffb1a Mon Sep 17 00:00:00 2001 From: Maarten Bent Date: Tue, 20 Aug 2019 22:23:11 +0200 Subject: [PATCH 1/2] Add wxWindow::GetDPI() This is simpler to use than wxDisplay(window).GetPPI() which was used instead of it so far in all ports and can be implemented more efficiently for wxMSW. Remove wxGetWinTLW, GetDPI already tries to get the top window. --- include/wx/msw/window.h | 2 ++ include/wx/window.h | 8 +++-- interface/wx/window.h | 14 +++++++++ src/common/wincmn.cpp | 6 ++++ src/msw/window.cpp | 70 ++++++++++++++++++++++++++++------------- 5 files changed, 76 insertions(+), 24 deletions(-) diff --git a/include/wx/msw/window.h b/include/wx/msw/window.h index b9b911bf7e..876d0d2b93 100644 --- a/include/wx/msw/window.h +++ b/include/wx/msw/window.h @@ -99,6 +99,8 @@ public: virtual bool Reparent(wxWindowBase *newParent) wxOVERRIDE; + virtual wxSize GetDPI() const wxOVERRIDE; + virtual void WarpPointer(int x, int y) wxOVERRIDE; virtual bool EnableTouchEvents(int eventsMask) wxOVERRIDE; diff --git a/include/wx/window.h b/include/wx/window.h index 5dbecc519a..c08ffa7712 100644 --- a/include/wx/window.h +++ b/include/wx/window.h @@ -949,14 +949,16 @@ public: // translation between different units // ----------------------------------- + // Get the DPI used by the given window or wxSize(0, 0) if unknown. + virtual wxSize GetDPI() const; + // DPI-independent pixels, or DIPs, are pixel values for the standard // 96 DPI display, they are scaled to take the current resolution into // account (i.e. multiplied by the same factor as returned by // GetContentScaleFactor()) if necessary for the current platform. // - // Currently the conversion factor is the same for all windows but this - // will change with the monitor-specific resolution support in the - // future, so prefer using the non-static member functions. + // To support monitor-specific resolutions, prefer using the non-static + // member functions or use a valid (non-null) window pointer. // // Similarly, currently in practice the factor is the same in both // horizontal and vertical directions, but this could, in principle, diff --git a/interface/wx/window.h b/interface/wx/window.h index ee69245147..8628ce5d26 100644 --- a/interface/wx/window.h +++ b/interface/wx/window.h @@ -2036,6 +2036,20 @@ public: */ virtual wxVisualAttributes GetDefaultAttributes() const; + /** + Return the DPI of the display used by this window. + + The returned value can be different for different windows on systems + with support for per-monitor DPI values, such as Microsoft Windows 10. + + If the DPI is not available, returns @c wxSize(0,0) object. + + @see wxDisplay::GetPPI() + + @since 3.1.3 + */ + virtual wxSize GetDPI() const; + /** Returns the font for this window. diff --git a/src/common/wincmn.cpp b/src/common/wincmn.cpp index 4939c3be30..63cc64514a 100644 --- a/src/common/wincmn.cpp +++ b/src/common/wincmn.cpp @@ -74,6 +74,7 @@ #include "wx/sysopt.h" #endif +#include "wx/display.h" #include "wx/platinfo.h" #include "wx/recguard.h" #include "wx/private/window.h" @@ -2855,6 +2856,11 @@ void wxWindowBase::OnInternalIdle() // DPI-independent pixels and dialog units translations // ---------------------------------------------------------------------------- +wxSize wxWindowBase::GetDPI() const +{ + return wxDisplay(static_cast(this)).GetPPI(); +} + #ifndef wxHAVE_DPI_INDEPENDENT_PIXELS /* static */ diff --git a/src/msw/window.cpp b/src/msw/window.cpp index 7d9b33a068..77a883460b 100644 --- a/src/msw/window.cpp +++ b/src/msw/window.cpp @@ -4719,23 +4719,28 @@ wxWindowMSW::MSWOnMeasureItem(int id, WXMEASUREITEMSTRUCT *itemStruct) namespace { -static inline const wxTopLevelWindow* wxGetWinTLW(const wxWindow* win) +static wxSize GetWindowDPI(HWND hwnd) { - if ( win ) +#if wxUSE_DYNLIB_CLASS + typedef UINT (WINAPI *GetDpiForWindow_t)(HWND hwnd); + static GetDpiForWindow_t s_pfnGetDpiForWindow = NULL; + static bool s_initDone = false; + + if ( !s_initDone ) { - const wxWindow* tlwWin = wxGetTopLevelParent(const_cast(win)); - return wxDynamicCast(tlwWin, wxTopLevelWindow); - } - else if ( wxTheApp ) - { - wxWindow* window = wxTheApp->GetTopWindow(); - if ( window ) - { - return wxDynamicCast(wxGetTopLevelParent(window), wxTopLevelWindow); - } + wxLoadedDLL dllUser32("user32.dll"); + wxDL_INIT_FUNC(s_pfn, GetDpiForWindow, dllUser32); + s_initDone = true; } - return NULL; + if ( s_pfnGetDpiForWindow ) + { + const int dpi = static_cast(s_pfnGetDpiForWindow(hwnd)); + return wxSize(dpi, dpi); + } +#endif // wxUSE_DYNLIB_CLASS + + return wxSize(); } } @@ -4744,9 +4749,9 @@ static inline const wxTopLevelWindow* wxGetWinTLW(const wxWindow* win) int wxGetSystemMetrics(int nIndex, const wxWindow* win) { #if wxUSE_DYNLIB_CLASS - const wxTopLevelWindow* tlw = wxGetWinTLW(win); + const wxWindow* window = (!win && wxTheApp) ? wxTheApp->GetTopWindow() : win; - if ( tlw ) + if ( window ) { typedef int (WINAPI * GetSystemMetricsForDpi_t)(int nIndex, UINT dpi); static GetSystemMetricsForDpi_t s_pfnGetSystemMetricsForDpi = NULL; @@ -4761,8 +4766,7 @@ int wxGetSystemMetrics(int nIndex, const wxWindow* win) if ( s_pfnGetSystemMetricsForDpi ) { - WindowHDC hdc(tlw->GetHWND()); - const int dpi = ::GetDeviceCaps(hdc, LOGPIXELSY); + const int dpi = window->GetDPI().y; return s_pfnGetSystemMetricsForDpi(nIndex, (UINT)dpi); } } @@ -4777,9 +4781,9 @@ int wxGetSystemMetrics(int nIndex, const wxWindow* win) bool wxSystemParametersInfo(UINT uiAction, UINT uiParam, PVOID pvParam, UINT fWinIni, const wxWindow* win) { #if wxUSE_DYNLIB_CLASS - const wxTopLevelWindow* tlw = wxGetWinTLW(win); + const wxWindow* window = (!win && wxTheApp) ? wxTheApp->GetTopWindow() : win; - if ( tlw ) + if ( window ) { typedef int (WINAPI * SystemParametersInfoForDpi_t)(UINT uiAction, UINT uiParam, PVOID pvParam, UINT fWinIni, UINT dpi); static SystemParametersInfoForDpi_t s_pfnSystemParametersInfoForDpi = NULL; @@ -4794,8 +4798,7 @@ bool wxSystemParametersInfo(UINT uiAction, UINT uiParam, PVOID pvParam, UINT fWi if ( s_pfnSystemParametersInfoForDpi ) { - WindowHDC hdc(tlw->GetHWND()); - const int dpi = ::GetDeviceCaps(hdc, LOGPIXELSY); + const int dpi = window->GetDPI().y; if ( s_pfnSystemParametersInfoForDpi(uiAction, uiParam, pvParam, fWinIni, (UINT)dpi) == TRUE ) { return true; @@ -4809,6 +4812,31 @@ bool wxSystemParametersInfo(UINT uiAction, UINT uiParam, PVOID pvParam, UINT fWi return ::SystemParametersInfo(uiAction, uiParam, pvParam, fWinIni) == TRUE; } +wxSize wxWindowMSW::GetDPI() const +{ + HWND hwnd = GetHwnd(); + + if ( hwnd == NULL ) + { + const wxWindow* topWin = wxGetTopLevelParent(const_cast(this)); + if ( topWin ) + { + hwnd = GetHwndOf(topWin); + } + } + + wxSize dpi = GetWindowDPI(hwnd); + + if ( !dpi.x || !dpi.y ) + { + WindowHDC hdc(GetHwnd()); + dpi.x = ::GetDeviceCaps(hdc, LOGPIXELSX); + dpi.y = ::GetDeviceCaps(hdc, LOGPIXELSY); + } + + return dpi; +} + // --------------------------------------------------------------------------- // colours and palettes // --------------------------------------------------------------------------- From 5b37d32c5084abcf15965035bc308a8671995944 Mon Sep 17 00:00:00 2001 From: Maarten Bent Date: Tue, 20 Aug 2019 22:27:30 +0200 Subject: [PATCH 2/2] Use the wxWindow DPI in FromDIP, ToDIP and GetContentScaleFactor --- src/common/wincmn.cpp | 36 +++++++++++++++++++++++++++--------- 1 file changed, 27 insertions(+), 9 deletions(-) diff --git a/src/common/wincmn.cpp b/src/common/wincmn.cpp index 63cc64514a..99c05f20cf 100644 --- a/src/common/wincmn.cpp +++ b/src/common/wincmn.cpp @@ -788,15 +788,33 @@ wxSize wxWindowBase::DoGetBestSize() const return best; } +namespace +{ + +static wxSize GetDPIHelper(const wxWindowBase* w) +{ + wxSize dpi; + + if ( w ) + dpi = w->GetDPI(); + if ( !dpi.x || !dpi.y ) + dpi = wxScreenDC().GetPPI(); + if ( !dpi.x || !dpi.y ) + dpi = wxSize(BASELINE_DPI, BASELINE_DPI); + + return dpi; +} + +} + double wxWindowBase::GetContentScaleFactor() const { - // Currently we don't support per-monitor DPI, so it's useless to construct - // a DC associated with this window, just use the global value. - // - // We also use just the vertical component of the DPI because it's the one + const wxSize dpi = GetDPIHelper(this); + + // We use just the vertical component of the DPI because it's the one // that counts most and, in practice, it's equal to the horizontal one // anyhow. - return double(wxScreenDC().GetPPI().y) / BASELINE_DPI; + return dpi.y / (double)BASELINE_DPI; } // helper of GetWindowBorderSize(): as many ports don't implement support for @@ -2865,9 +2883,9 @@ wxSize wxWindowBase::GetDPI() const /* static */ wxSize -wxWindowBase::FromDIP(const wxSize& sz, const wxWindowBase* WXUNUSED(w)) +wxWindowBase::FromDIP(const wxSize& sz, const wxWindowBase* w) { - const wxSize dpi = wxScreenDC().GetPPI(); + const wxSize dpi = GetDPIHelper(w); // Take care to not scale -1 because it has a special meaning of // "unspecified" which should be preserved. @@ -2877,9 +2895,9 @@ wxWindowBase::FromDIP(const wxSize& sz, const wxWindowBase* WXUNUSED(w)) /* static */ wxSize -wxWindowBase::ToDIP(const wxSize& sz, const wxWindowBase* WXUNUSED(w)) +wxWindowBase::ToDIP(const wxSize& sz, const wxWindowBase* w) { - const wxSize dpi = wxScreenDC().GetPPI(); + const wxSize dpi = GetDPIHelper(w); // Take care to not scale -1 because it has a special meaning of // "unspecified" which should be preserved.