From a17cf5d4da68a92007d843146a0cd1a5de8470a3 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Thu, 8 Jan 2026 13:20:36 +0100 Subject: [PATCH] Add wxDisplay::GetRawPPI() Return resolution corresponding to the reported display physical size. Implement the new function for wxMSW, wxGTK and wxOSX. Show it in the "display" sample if it's different from the logical one. --- include/wx/display.h | 3 +++ include/wx/private/display.h | 4 ++++ interface/wx/display.h | 13 +++++++++++++ samples/display/display.cpp | 15 +++++++++++++-- src/common/dpycmn.cpp | 10 ++++++++++ src/gtk/display.cpp | 17 +++++++++++++++++ src/msw/display.cpp | 32 +++++++++++++++++++++++++++++--- src/osx/core/display.cpp | 17 +++++++++++++++++ 8 files changed, 106 insertions(+), 5 deletions(-) diff --git a/include/wx/display.h b/include/wx/display.h index 9d88425d77..f82c0fcf7e 100644 --- a/include/wx/display.h +++ b/include/wx/display.h @@ -94,6 +94,9 @@ public: // get the resolution of this monitor in pixels per inch wxSize GetPPI() const; + // get raw resolution in pixels per inch, i.e. without applying any scaling + wxSize GetRawPPI() const; + // get the default resolution for displays on this platform static int GetStdPPIValue() { diff --git a/include/wx/private/display.h b/include/wx/private/display.h index 23a7bf2e96..7dad779a1f 100644 --- a/include/wx/private/display.h +++ b/include/wx/private/display.h @@ -102,6 +102,10 @@ public: // but can be also overridden directly, as is done in wxMSW virtual wxSize GetPPI() const { return wxDisplay::GetStdPPI()*GetScaleFactor(); } + // return raw resolution of the display, by default returns standard PPI + // but should be overridden + virtual wxSize GetRawPPI() const { return wxDisplay::GetStdPPI(); } + // return the name (may be empty) virtual wxString GetName() const { return wxString(); } diff --git a/interface/wx/display.h b/interface/wx/display.h index 6fe7170ac2..d0a0fb2cc9 100644 --- a/interface/wx/display.h +++ b/interface/wx/display.h @@ -167,6 +167,19 @@ public: */ wxSize GetPPI() const; + /** + Returns raw display resolution in pixels per inch, i.e.\ without + applying any scaling. + + Horizontal and vertical resolution are returned in @c x and @c y + components of the wxSize object respectively. + + If the resolution information is not available, returns `wxSize(0, 0)`. + + @since 3.3.2 + */ + wxSize GetRawPPI() const; + /** Returns scaling factor used by this display. diff --git a/samples/display/display.cpp b/samples/display/display.cpp index 4f175bc877..e96d144944 100644 --- a/samples/display/display.cpp +++ b/samples/display/display.cpp @@ -299,8 +299,19 @@ void MyFrame::PopuplateWithDisplayInfo() sizer->Add(new wxStaticText(page, wxID_ANY, "Resolution: ")); const wxSize ppi = display.GetPPI(); - sizer->Add(new wxStaticText(page, wxID_ANY, - wxString::Format("%d*%d", ppi.x, ppi.y))); + const wxSize ppiRaw = display.GetRawPPI(); + wxString resolutionText; + if ( ppiRaw == ppi ) + { + resolutionText = wxString::Format("%d*%d", ppi.x, ppi.y); + } + else + { + resolutionText = wxString::Format("%d*%d (logical), %d*%d (raw)", + ppi.x, ppi.y, ppiRaw.x, ppiRaw.y); + } + + sizer->Add(new wxStaticText(page, wxID_ANY, resolutionText)); sizer->Add(new wxStaticText(page, wxID_ANY, "Depth: ")); sizer->Add(new wxStaticText(page, wxID_ANY, diff --git a/src/common/dpycmn.cpp b/src/common/dpycmn.cpp index 4c4883293a..4f83b41049 100644 --- a/src/common/dpycmn.cpp +++ b/src/common/dpycmn.cpp @@ -167,6 +167,16 @@ wxSize wxDisplay::GetPPI() const return m_impl->GetPPI(); } +wxSize wxDisplay::GetRawPPI() const +{ + wxCHECK_MSG( IsOk(), wxSize(), wxT("invalid wxDisplay object") ); + + if ( !m_impl->IsConnected() ) + return wxSize(); + + return m_impl->GetRawPPI(); +} + double wxDisplay::GetScaleFactor() const { wxCHECK_MSG( IsOk(), 0, wxT("invalid wxDisplay object") ); diff --git a/src/gtk/display.cpp b/src/gtk/display.cpp index 2a4112c840..55e8b7c8c0 100644 --- a/src/gtk/display.cpp +++ b/src/gtk/display.cpp @@ -246,6 +246,7 @@ public: #if GTK_CHECK_VERSION(3,10,0) virtual double GetScaleFactor() const override; #endif // GTK+ 3.10 + virtual wxSize GetRawPPI() const override; #if wxUSE_DISPLAY virtual bool IsPrimary() const override; @@ -340,6 +341,22 @@ double wxDisplayImplGTK::GetScaleFactor() const } #endif // GTK+ 3.10 +wxSize wxDisplayImplGTK::GetRawPPI() const +{ + auto const widthInMM = gdk_screen_get_monitor_width_mm(m_screen, m_index); + auto const heightInMM = gdk_screen_get_monitor_height_mm(m_screen, m_index); + + // Don't use manifestly invalid values. + if ( widthInMM <= 0 && heightInMM <= 0 ) + return wxDisplay::GetStdPPI(); + + const wxRect geometry = GetGeometry(); + const double ppiX = geometry.width * 25.4 / widthInMM; + const double ppiY = geometry.height * 25.4 / heightInMM; + + return wxSize(wxRound(ppiX), wxRound(ppiY)); +} + #if wxUSE_DISPLAY bool wxDisplayImplGTK::IsPrimary() const { diff --git a/src/msw/display.cpp b/src/msw/display.cpp index a2bb9149f2..16645e7a89 100644 --- a/src/msw/display.cpp +++ b/src/msw/display.cpp @@ -93,6 +93,7 @@ protected: #ifndef DPI_ENUMS_DECLARED #define MDT_EFFECTIVE_DPI 0 + #define MDT_RAW_DPI 2 #endif namespace @@ -131,6 +132,7 @@ public: virtual wxRect GetClientArea() const override; virtual int GetDepth() const override; virtual wxSize GetPPI() const override; + virtual wxSize GetRawPPI() const override; virtual double GetScaleFactor() const override; virtual wxString GetName() const override; @@ -160,6 +162,12 @@ protected: wxDisplayInfo m_info; private: + // Wrapper around GetDpiForMonitor() call: check if it's available and for + // its success. + // + // Return wxSize(0, 0) on failure. + wxSize CallGetDpiForMonitor(int type) const; + wxDECLARE_NO_COPY_CLASS(wxDisplayMSW); }; @@ -321,7 +329,7 @@ int wxDisplayMSW::GetDepth() const return m_info.depth; } -wxSize wxDisplayMSW::GetPPI() const +wxSize wxDisplayMSW::CallGetDpiForMonitor(int type) const { if ( const wxDisplayFactoryMSW::GetDpiForMonitor_t getFunc = wxDisplayFactoryMSW::GetDpiForMonitorPtr() ) @@ -329,14 +337,32 @@ wxSize wxDisplayMSW::GetPPI() const UINT dpiX = 0, dpiY = 0; const HRESULT - hr = (*getFunc)(m_info.hmon, MDT_EFFECTIVE_DPI, &dpiX, &dpiY); + hr = (*getFunc)(m_info.hmon, type, &dpiX, &dpiY); if ( SUCCEEDED(hr) ) return wxSize(dpiX, dpiY); wxLogApiError("GetDpiForMonitor", hr); } - return IsPrimary() ? wxDisplayImplSingleMSW().GetPPI() : wxSize(0, 0); + return wxSize(); +} + +wxSize wxDisplayMSW::GetPPI() const +{ + wxSize ppi = CallGetDpiForMonitor(MDT_EFFECTIVE_DPI); + if ( ppi.IsEmpty() && IsPrimary() ) + ppi = wxDisplayImplSingleMSW().GetPPI(); + + return ppi; +} + +wxSize wxDisplayMSW::GetRawPPI() const +{ + wxSize ppi = CallGetDpiForMonitor(MDT_RAW_DPI); + if ( ppi.IsEmpty() && IsPrimary() ) + ppi = wxDisplayImplSingleMSW().GetRawPPI(); + + return ppi; } double wxDisplayMSW::GetScaleFactor() const diff --git a/src/osx/core/display.cpp b/src/osx/core/display.cpp index ffe997a0d8..22d2cf509a 100644 --- a/src/osx/core/display.cpp +++ b/src/osx/core/display.cpp @@ -24,6 +24,7 @@ #ifndef WX_PRECOMP #include "wx/dynarray.h" #include "wx/log.h" + #include "wx/math.h" #include "wx/string.h" #include "wx/gdicmn.h" #include "wx/nonownedwnd.h" @@ -105,6 +106,7 @@ public: virtual wxRect GetClientArea() const override; virtual int GetDepth() const override; virtual double GetScaleFactor() const override; + virtual wxSize GetRawPPI() const override; virtual wxArrayVideoModes GetModes(const wxVideoMode& mode) const override; virtual wxVideoMode GetCurrentMode() const override; @@ -310,6 +312,21 @@ double wxDisplayImplMacOSX::GetScaleFactor() const return wxGetScaleFactor(m_id); } +wxSize wxDisplayImplMacOSX::GetRawPPI() const +{ + auto const sizeInMM = CGDisplayScreenSize(m_id); + + // Don't use manifestly invalid values. + if ( sizeInMM.width <= 0 && sizeInMM.height <= 0 ) + return wxDisplay::GetStdPPI(); + + const wxRect geometry = GetGeometry(); + const double ppiX = geometry.width * 25.4 / sizeInMM.width; + const double ppiY = geometry.height * 25.4 / sizeInMM.height; + + return wxSize(wxRound(ppiX), wxRound(ppiY)); +} + static int wxOSXCGDisplayModeGetBitsPerPixel( CGDisplayModeRef theValue ) { wxCFRef pixelEncoding( CGDisplayModeCopyPixelEncoding(theValue) );