diff --git a/include/wx/defs.h b/include/wx/defs.h
index 1871ae13e4..f349de2cb2 100644
--- a/include/wx/defs.h
+++ b/include/wx/defs.h
@@ -2830,6 +2830,7 @@ WX_MSW_DECLARE_HANDLE(HBITMAP);
WX_MSW_DECLARE_HANDLE(HIMAGELIST);
WX_MSW_DECLARE_HANDLE(HGLOBAL);
WX_MSW_DECLARE_HANDLE(HDC);
+WX_MSW_DECLARE_HANDLE(DPI_AWARENESS_CONTEXT);
typedef WXHINSTANCE WXHMODULE;
#undef WX_MSW_DECLARE_HANDLE
diff --git a/include/wx/event.h b/include/wx/event.h
index c57e6ff0ef..3b5a975c01 100644
--- a/include/wx/event.h
+++ b/include/wx/event.h
@@ -641,6 +641,7 @@ class WXDLLIMPEXP_FWD_CORE wxMenuEvent;
class WXDLLIMPEXP_FWD_CORE wxContextMenuEvent;
class WXDLLIMPEXP_FWD_CORE wxSysColourChangedEvent;
class WXDLLIMPEXP_FWD_CORE wxDisplayChangedEvent;
+class WXDLLIMPEXP_FWD_CORE wxDPIChangedEvent;
class WXDLLIMPEXP_FWD_CORE wxQueryNewPaletteEvent;
class WXDLLIMPEXP_FWD_CORE wxPaletteChangedEvent;
class WXDLLIMPEXP_FWD_CORE wxJoystickEvent;
@@ -793,6 +794,7 @@ wxDECLARE_EXPORTED_EVENT(WXDLLIMPEXP_CORE, wxEVT_MENU_HIGHLIGHT, wxMenuEvent);
wxDECLARE_EXPORTED_EVENT(WXDLLIMPEXP_CORE, wxEVT_CONTEXT_MENU, wxContextMenuEvent);
wxDECLARE_EXPORTED_EVENT(WXDLLIMPEXP_CORE, wxEVT_SYS_COLOUR_CHANGED, wxSysColourChangedEvent);
wxDECLARE_EXPORTED_EVENT(WXDLLIMPEXP_CORE, wxEVT_DISPLAY_CHANGED, wxDisplayChangedEvent);
+wxDECLARE_EXPORTED_EVENT(WXDLLIMPEXP_CORE, wxEVT_DPI_CHANGED, wxDPIChangedEvent);
wxDECLARE_EXPORTED_EVENT(WXDLLIMPEXP_CORE, wxEVT_QUERY_NEW_PALETTE, wxQueryNewPaletteEvent);
wxDECLARE_EXPORTED_EVENT(WXDLLIMPEXP_CORE, wxEVT_PALETTE_CHANGED, wxPaletteChangedEvent);
wxDECLARE_EXPORTED_EVENT(WXDLLIMPEXP_CORE, wxEVT_JOY_BUTTON_DOWN, wxJoystickEvent);
@@ -3031,6 +3033,32 @@ public:
virtual wxEvent *Clone() const wxOVERRIDE { return new wxDisplayChangedEvent(*this); }
};
+/*
+ wxEVT_DPI_CHANGED
+ */
+class WXDLLIMPEXP_CORE wxDPIChangedEvent : public wxEvent
+{
+public:
+ explicit
+ wxDPIChangedEvent(const wxSize& oldDPI = wxDefaultSize,
+ const wxSize& newDPI = wxDefaultSize)
+ : wxEvent(0, wxEVT_DPI_CHANGED),
+ m_oldDPI(oldDPI),
+ m_newDPI(newDPI)
+ { }
+
+ wxSize GetOldDPI() const { return m_oldDPI; }
+ wxSize GetNewDPI() const { return m_newDPI; }
+
+ virtual wxEvent *Clone() const wxOVERRIDE { return new wxDPIChangedEvent(*this); }
+
+private:
+ wxSize m_oldDPI;
+ wxSize m_newDPI;
+
+ wxDECLARE_DYNAMIC_CLASS_NO_ASSIGN(wxDPIChangedEvent);
+};
+
/*
wxEVT_PALETTE_CHANGED
*/
@@ -4108,6 +4136,7 @@ typedef void (wxEvtHandler::*wxDropFilesEventFunction)(wxDropFilesEvent&);
typedef void (wxEvtHandler::*wxInitDialogEventFunction)(wxInitDialogEvent&);
typedef void (wxEvtHandler::*wxSysColourChangedEventFunction)(wxSysColourChangedEvent&);
typedef void (wxEvtHandler::*wxDisplayChangedEventFunction)(wxDisplayChangedEvent&);
+typedef void (wxEvtHandler::*wxDPIChangedEventFunction)(wxDPIChangedEvent&);
typedef void (wxEvtHandler::*wxUpdateUIEventFunction)(wxUpdateUIEvent&);
typedef void (wxEvtHandler::*wxCloseEventFunction)(wxCloseEvent&);
typedef void (wxEvtHandler::*wxShowEventFunction)(wxShowEvent&);
@@ -4171,6 +4200,8 @@ typedef void (wxEvtHandler::*wxPressAndTapEventFunction)(wxPressAndTapEvent&);
wxEVENT_HANDLER_CAST(wxSysColourChangedEventFunction, func)
#define wxDisplayChangedEventHandler(func) \
wxEVENT_HANDLER_CAST(wxDisplayChangedEventFunction, func)
+#define wxDPIChangedEventHandler(func) \
+ wxEVENT_HANDLER_CAST(wxDPIChangedEventFunction, func)
#define wxUpdateUIEventHandler(func) \
wxEVENT_HANDLER_CAST(wxUpdateUIEventFunction, func)
#define wxCloseEventHandler(func) \
@@ -4430,6 +4461,7 @@ typedef void (wxEvtHandler::*wxPressAndTapEventFunction)(wxPressAndTapEvent&);
#define EVT_INIT_DIALOG(func) wx__DECLARE_EVT0(wxEVT_INIT_DIALOG, wxInitDialogEventHandler(func))
#define EVT_SYS_COLOUR_CHANGED(func) wx__DECLARE_EVT0(wxEVT_SYS_COLOUR_CHANGED, wxSysColourChangedEventHandler(func))
#define EVT_DISPLAY_CHANGED(func) wx__DECLARE_EVT0(wxEVT_DISPLAY_CHANGED, wxDisplayChangedEventHandler(func))
+#define EVT_DPI_CHANGED(func) wx__DECLARE_EVT0(wxEVT_DPI_CHANGED, wxDPIChangedEventHandler(func))
#define EVT_SHOW(func) wx__DECLARE_EVT0(wxEVT_SHOW, wxShowEventHandler(func))
#define EVT_MAXIMIZE(func) wx__DECLARE_EVT0(wxEVT_MAXIMIZE, wxMaximizeEventHandler(func))
#define EVT_ICONIZE(func) wx__DECLARE_EVT0(wxEVT_ICONIZE, wxIconizeEventHandler(func))
diff --git a/include/wx/font.h b/include/wx/font.h
index 4e986bb49f..409302d2e4 100644
--- a/include/wx/font.h
+++ b/include/wx/font.h
@@ -501,6 +501,13 @@ public:
// account as well.
static int GetNumericWeightOf(wxFontWeight weight);
+ // Some ports need to modify the font object when the DPI of the window it
+ // is used with changes, this function can be used to do it.
+ //
+ // Currently it is only used in wxMSW and is not considered to be part of
+ // wxWidgets public API.
+ virtual void WXAdjustToPPI(const wxSize& WXUNUSED(ppi)) { }
+
// this doesn't do anything and is kept for compatibility only
#if WXWIN_COMPATIBILITY_2_8
wxDEPRECATED_INLINE(void SetNoAntiAliasing(bool no = true), wxUnusedVar(no);)
diff --git a/include/wx/fontutil.h b/include/wx/fontutil.h
index 2ae62eaf56..424c100136 100644
--- a/include/wx/fontutil.h
+++ b/include/wx/fontutil.h
@@ -120,25 +120,17 @@ public:
#elif defined(__WXMSW__)
wxNativeFontInfo(const LOGFONT& lf_)
: lf(lf_),
- pointSize(GetPointSizeFromLogFontHeight(lf.lfHeight))
+ pointSize(GetPointSizeAtPPI(lf.lfHeight))
{
}
- // MSW-specific: get point size from LOGFONT height using the default DPI.
- static float GetPointSizeFromLogFontHeight(int height);
+ // MSW-specific: get point size from LOGFONT height using specified DPI,
+ // or screen DPI when 0.
+ static float GetPointSizeAtPPI(int lfHeight, int ppi = 0);
// MSW-specific: get the height value in pixels using LOGFONT convention
// (i.e. negative) corresponding to the given size in points and DPI.
- static int GetLogFontHeightAtPPI(float size, int ppi)
- {
- return -wxRound(size * ppi / 72.0);
- }
-
- // And the same thing for the size of this font.
- int GetLogFontHeightAtPPI(int ppi) const
- {
- return GetLogFontHeightAtPPI(pointSize, ppi);
- }
+ static int GetLogFontHeightAtPPI(float size, int ppi);
LOGFONT lf;
diff --git a/include/wx/msw/font.h b/include/wx/msw/font.h
index ee803d9cce..70ede34b88 100644
--- a/include/wx/msw/font.h
+++ b/include/wx/msw/font.h
@@ -120,6 +120,8 @@ public:
virtual bool IsFixedWidth() const wxOVERRIDE;
+ virtual void WXAdjustToPPI(const wxSize& ppi) wxOVERRIDE;
+
wxDEPRECATED_MSG("use wxFONT{FAMILY,STYLE,WEIGHT}_XXX constants ie: wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD")
wxFont(int size,
int family,
diff --git a/include/wx/msw/missing.h b/include/wx/msw/missing.h
index e56a6bfa68..7f381bb898 100644
--- a/include/wx/msw/missing.h
+++ b/include/wx/msw/missing.h
@@ -23,7 +23,11 @@
#endif
#ifndef WM_PRINTCLIENT
- #define WM_PRINTCLIENT 0x318
+ #define WM_PRINTCLIENT 0x0318
+#endif
+
+#ifndef WM_DPICHANGED
+ #define WM_DPICHANGED 0x02E0
#endif
#ifndef DT_HIDEPREFIX
diff --git a/include/wx/msw/toplevel.h b/include/wx/msw/toplevel.h
index 0256104ebc..5cef6b1e8a 100644
--- a/include/wx/msw/toplevel.h
+++ b/include/wx/msw/toplevel.h
@@ -165,6 +165,9 @@ protected:
int& x, int& y,
int& w, int& h) const wxOVERRIDE;
+ // WM_DPICHANGED handler.
+ bool HandleDPIChange(const wxSize& newDPI, const wxRect& newRect);
+
// This field contains the show command to use when showing the window the
// next time and also indicates whether the window should be considered
// being iconized or maximized (which may be different from whether it's
@@ -191,6 +194,14 @@ protected:
wxWindowRef m_winLastFocused;
private:
+ // Keep track of the DPI used in this window. So when per-monitor dpi
+ // awareness is enabled, both old and new DPI are known for
+ // wxDPIChangedEvent and wxWindow::MSWUpdateOnDPIChange.
+ wxSize m_activeDPI;
+
+ // This window supports handling per-monitor DPI awareness when the
+ // application manifest contains PerMonitorV2.
+ bool m_perMonitorDPIaware;
// The system menu: initially NULL but can be set (once) by
// MSWGetSystemMenu(). Owned by this window.
diff --git a/include/wx/msw/window.h b/include/wx/msw/window.h
index 876d0d2b93..dd6c10a6b5 100644
--- a/include/wx/msw/window.h
+++ b/include/wx/msw/window.h
@@ -586,7 +586,17 @@ public:
// Should be overridden by all classes storing the "last focused" window.
virtual void WXDoUpdatePendingFocus(wxWindow* WXUNUSED(win)) {}
+ // Called from WM_DPICHANGED handler for all windows to let them update
+ // any sizes and fonts used internally when the DPI changes and generate
+ // wxDPIChangedEvent to let the user code do the same thing as well.
+ void MSWUpdateOnDPIChange(const wxSize& oldDPI, const wxSize& newDPI);
+
protected:
+ // Called from MSWUpdateOnDPIChange() specifically to update the control
+ // font, as this may need to be done differently for some specific native
+ // controls. The default version updates m_font of this window.
+ virtual void MSWUpdateFontOnDPIChange(const wxSize& newDPI);
+
// this allows you to implement standard control borders without
// repeating the code in different classes that are not derived from
// wxControl
diff --git a/interface/wx/event.h b/interface/wx/event.h
index 3b80a529fb..e9e124998d 100644
--- a/interface/wx/event.h
+++ b/interface/wx/event.h
@@ -3318,6 +3318,62 @@ public:
};
+
+/**
+ @class wxDPIChangedEvent
+
+ Event sent when the resolution (measured in dots-per-inch, or DPI) of the
+ monitor a window is on changes.
+
+ The event is sent to each wxTopLevelWindow affected by the change, and all
+ its children recursively. For example, this event is sent to the window
+ when it is moved, by the user, from a display using some DPI value to
+ another display using a different DPI value. It also sent to all program
+ windows on the given display if its DPI changes due to a change in the
+ system settings.
+
+ Currently this event is generated by wxMSW port if only and only if the
+ MSW application runs under Windows 10 Creators Update (v1703) or later and
+ is marked as being "per-monitor DPI aware", i.e. contains a @c dpiAwareness
+ tag with the value "PerMonitorV2" in its manifest (see Microsoft
+ "Application Manifests" documentation
+ for more details).
+
+ @note Per-monitor DPI support is an experimental feature that is still in
+ development. It might not work correctly for some controls.
+
+ @beginEventTable{wxDPIChangedEvent}
+ @event{EVT_DPI_CHANGED(func)}
+ Process a @c wxEVT_DPI_CHANGED event.
+ @endEventTable
+
+ @since 3.1.3
+
+ @library{wxcore}
+ @category{events}
+
+ @see @ref overview_events
+*/
+class wxDPIChangedEvent : public wxEvent
+{
+public:
+ /**
+ Returns the old DPI.
+
+ @since 3.1.3
+ */
+ wxSize GetOldDPI() const;
+
+ /**
+ Returns the new DPI.
+
+ @since 3.1.3
+ */
+ wxSize GetNewDPI() const;
+};
+
+
+
class wxPaletteChangedEvent : public wxEvent
{
public:
diff --git a/interface/wx/window.h b/interface/wx/window.h
index 8628ce5d26..d8d8fff28c 100644
--- a/interface/wx/window.h
+++ b/interface/wx/window.h
@@ -2044,7 +2044,7 @@ public:
If the DPI is not available, returns @c wxSize(0,0) object.
- @see wxDisplay::GetPPI()
+ @see wxDisplay::GetPPI(), wxDPIChangedEvent
@since 3.1.3
*/
diff --git a/src/common/event.cpp b/src/common/event.cpp
index 14249ede37..d10275b49b 100644
--- a/src/common/event.cpp
+++ b/src/common/event.cpp
@@ -92,6 +92,7 @@
wxIMPLEMENT_DYNAMIC_CLASS(wxSetCursorEvent, wxEvent);
wxIMPLEMENT_DYNAMIC_CLASS(wxSysColourChangedEvent, wxEvent);
wxIMPLEMENT_DYNAMIC_CLASS(wxDisplayChangedEvent, wxEvent);
+ wxIMPLEMENT_DYNAMIC_CLASS(wxDPIChangedEvent, wxEvent);
wxIMPLEMENT_DYNAMIC_CLASS(wxUpdateUIEvent, wxCommandEvent);
wxIMPLEMENT_DYNAMIC_CLASS(wxNavigationKeyEvent, wxEvent);
wxIMPLEMENT_DYNAMIC_CLASS(wxPaletteChangedEvent, wxEvent);
@@ -302,6 +303,7 @@ wxDEFINE_EVENT( wxEVT_MENU_HIGHLIGHT, wxMenuEvent );
wxDEFINE_EVENT( wxEVT_CONTEXT_MENU, wxContextMenuEvent );
wxDEFINE_EVENT( wxEVT_SYS_COLOUR_CHANGED, wxSysColourChangedEvent );
wxDEFINE_EVENT( wxEVT_DISPLAY_CHANGED, wxDisplayChangedEvent );
+wxDEFINE_EVENT( wxEVT_DPI_CHANGED, wxDPIChangedEvent );
wxDEFINE_EVENT( wxEVT_QUERY_NEW_PALETTE, wxQueryNewPaletteEvent );
wxDEFINE_EVENT( wxEVT_PALETTE_CHANGED, wxPaletteChangedEvent );
wxDEFINE_EVENT( wxEVT_JOY_BUTTON_DOWN, wxJoystickEvent );
diff --git a/src/common/wincmn.cpp b/src/common/wincmn.cpp
index 99c05f20cf..bc8a88139a 100644
--- a/src/common/wincmn.cpp
+++ b/src/common/wincmn.cpp
@@ -1726,6 +1726,8 @@ wxFont wxWindowBase::GetFont() const
if ( !font.IsOk() )
font = GetClassDefaultAttributes().font;
+ font.WXAdjustToPPI(GetDPI());
+
return font;
}
else
@@ -1744,6 +1746,9 @@ bool wxWindowBase::SetFont(const wxFont& font)
m_hasFont = font.IsOk();
m_inheritFont = m_hasFont;
+ if ( m_hasFont )
+ m_font.WXAdjustToPPI(GetDPI());
+
InvalidateBestSize();
return true;
diff --git a/src/msw/font.cpp b/src/msw/font.cpp
index e3e8012042..e83b07488a 100644
--- a/src/msw/font.cpp
+++ b/src/msw/font.cpp
@@ -159,7 +159,8 @@ public:
int GetLogFontHeightAtPPI(int ppi) const
{
- return m_nativeFontInfo.GetLogFontHeightAtPPI(ppi);
+ return m_nativeFontInfo.GetLogFontHeightAtPPI(
+ m_nativeFontInfo.pointSize, ppi);
}
// ... and setters: notice that all of them invalidate the currently
@@ -403,12 +404,18 @@ void wxFontRefData::Free()
// ----------------------------------------------------------------------------
/* static */
-float wxNativeFontInfo::GetPointSizeFromLogFontHeight(int height)
+float wxNativeFontInfo::GetPointSizeAtPPI(int lfHeight, int ppi)
{
- // Determine the size in points using the primary screen DPI as we don't
- // have anything else here.
- const float ppi = ::GetDeviceCaps(ScreenHDC(), LOGPIXELSY);
- return 72.0f * abs(height) / ppi;
+ if ( ppi == 0 )
+ ppi = ::GetDeviceCaps(ScreenHDC(), LOGPIXELSY);
+
+ return abs(lfHeight) * 72.0f / ppi;
+}
+
+/* static */
+int wxNativeFontInfo::GetLogFontHeightAtPPI(float size, int ppi)
+{
+ return -wxRound(size * ppi / 72.0f);
}
void wxNativeFontInfo::Init()
@@ -512,7 +519,8 @@ wxFontEncoding wxNativeFontInfo::GetEncoding() const
void wxNativeFontInfo::SetFractionalPointSize(float pointSizeNew)
{
// We don't have the correct DPI to use here, so use that of the
- // primary screen.
+ // primary screen and rely on WXAdjustToPPI() changing it later if
+ // necessary.
const int ppi = ::GetDeviceCaps(ScreenHDC(), LOGPIXELSY);
lf.lfHeight = GetLogFontHeightAtPPI(pointSizeNew, ppi);
@@ -535,7 +543,7 @@ void wxNativeFontInfo::SetPixelSize(const wxSize& pixelSize)
// We don't have the right DPI to use here neither, but we need to update
// the point size too, so fall back to the default.
- pointSize = GetPointSizeFromLogFontHeight(lf.lfHeight);
+ pointSize = GetPointSizeAtPPI(lf.lfHeight);
}
void wxNativeFontInfo::SetStyle(wxFontStyle style)
@@ -698,7 +706,7 @@ bool wxNativeFontInfo::FromString(const wxString& s)
return false;
lf.lfHeight = l;
if ( setPointSizeFromHeight )
- pointSize = GetPointSizeFromLogFontHeight(l);
+ pointSize = GetPointSizeAtPPI(l);
token = tokenizer.GetNextToken();
if ( !token.ToLong(&l) )
@@ -899,6 +907,19 @@ void wxFont::SetPixelSize(const wxSize& pixelSize)
M_FONTDATA->SetPixelSize(pixelSize);
}
+void wxFont::WXAdjustToPPI(const wxSize& ppi)
+{
+ // We only use vertical component here as we only adjust LOGFONT::lfHeight.
+ const int heightNew = M_FONTDATA->GetLogFontHeightAtPPI(ppi.y);
+
+ if ( heightNew != M_FONTDATA->GetLogFontHeight() )
+ {
+ AllocExclusive();
+
+ M_FONTDATA->SetLogFontHeight(heightNew);
+ }
+}
+
void wxFont::SetFamily(wxFontFamily family)
{
AllocExclusive();
diff --git a/src/msw/menuitem.cpp b/src/msw/menuitem.cpp
index 83733eb4aa..cbf114bb9a 100644
--- a/src/msw/menuitem.cpp
+++ b/src/msw/menuitem.cpp
@@ -392,7 +392,15 @@ void MenuDrawData::Init()
Offset = -12;
- Font = wxFont(wxNativeFontInfo(metrics.lfMenuFont));
+ wxNativeFontInfo info(metrics.lfMenuFont);
+ // wxNativeFontInfo constructor calculates the pointSize using the
+ // main screen DPI. But lfHeight is based on the window DPI.
+ if ( window )
+ {
+ info.pointSize = wxNativeFontInfo::GetPointSizeAtPPI(
+ info.lf.lfHeight, window->GetDPI().y);
+ }
+ Font = wxFont(info);
Theme = false;
}
diff --git a/src/msw/msgdlg.cpp b/src/msw/msgdlg.cpp
index 8e2a101a55..21fbfc4e4b 100644
--- a/src/msw/msgdlg.cpp
+++ b/src/msw/msgdlg.cpp
@@ -397,8 +397,17 @@ void wxMessageDialog::AdjustButtonLabels()
wxFont wxMessageDialog::GetMessageFont()
{
const wxWindow* win = wxTheApp ? wxTheApp->GetTopWindow() : NULL;
- const NONCLIENTMETRICS& ncm = wxMSWImpl::GetNonClientMetrics(win);
- return wxNativeFontInfo(ncm.lfMessageFont);
+ wxNativeFontInfo info(wxMSWImpl::GetNonClientMetrics(win).lfMessageFont);
+
+ // wxNativeFontInfo constructor calculates the pointSize using the
+ // main screen DPI. But lfHeight is based on the window DPI.
+ if ( win )
+ {
+ info.pointSize = wxNativeFontInfo::GetPointSizeAtPPI(
+ info.lf.lfHeight, win->GetDPI().y);
+ }
+
+ return info;
}
int wxMessageDialog::ShowMessageBox()
diff --git a/src/msw/settings.cpp b/src/msw/settings.cpp
index fc1783c1ac..c2029cc67f 100644
--- a/src/msw/settings.cpp
+++ b/src/msw/settings.cpp
@@ -183,8 +183,17 @@ wxFont wxSystemSettingsNative::GetFont(wxSystemFont index)
// controls may prefer to use lfStatusFont or lfCaptionFont if it
// is more appropriate for them
const wxWindow* win = wxTheApp ? wxTheApp->GetTopWindow() : NULL;
- const wxNativeFontInfo
+ wxNativeFontInfo
info(wxMSWImpl::GetNonClientMetrics(win).lfMessageFont);
+
+ // wxNativeFontInfo constructor calculates the pointSize using the
+ // main screen DPI. But lfHeight is based on the window DPI.
+ if ( win )
+ {
+ info.pointSize = wxNativeFontInfo::GetPointSizeAtPPI(
+ info.lf.lfHeight, win->GetDPI().y);
+ }
+
gs_fontDefault = new wxFont(info);
}
diff --git a/src/msw/toplevel.cpp b/src/msw/toplevel.cpp
index 5939b9636f..d2355b0179 100644
--- a/src/msw/toplevel.cpp
+++ b/src/msw/toplevel.cpp
@@ -104,6 +104,9 @@ void wxTopLevelWindowMSW::Init()
m_fsIsShowing = false;
m_menuSystem = NULL;
+
+ m_activeDPI = wxDefaultSize;
+ m_perMonitorDPIaware = false;
}
WXDWORD wxTopLevelWindowMSW::MSWGetStyle(long style, WXDWORD *exflags) const
@@ -246,6 +249,26 @@ WXHWND wxTopLevelWindowMSW::MSWGetParent() const
return (WXHWND)hwndParent;
}
+bool wxTopLevelWindowMSW::HandleDPIChange(const wxSize& newDPI, const wxRect& newRect)
+{
+ if ( !m_perMonitorDPIaware )
+ {
+ return false;
+ }
+
+ if ( newDPI != m_activeDPI )
+ {
+ MSWUpdateOnDPIChange(m_activeDPI, newDPI);
+ m_activeDPI = newDPI;
+ }
+
+ SetSize(newRect);
+
+ Refresh();
+
+ return true;
+}
+
WXLRESULT wxTopLevelWindowMSW::MSWWindowProc(WXUINT message, WXWPARAM wParam, WXLPARAM lParam)
{
WXLRESULT rc = 0;
@@ -306,6 +329,17 @@ WXLRESULT wxTopLevelWindowMSW::MSWWindowProc(WXUINT message, WXWPARAM wParam, WX
#endif // #ifndef __WXUNIVERSAL__
}
break;
+
+ case WM_DPICHANGED:
+ {
+ const RECT* const prcNewWindow =
+ reinterpret_cast(lParam);
+
+ processed = HandleDPIChange(wxSize(LOWORD(wParam),
+ HIWORD(wParam)),
+ wxRectFromRECT(*prcNewWindow));
+ }
+ break;
}
if ( !processed )
@@ -314,6 +348,47 @@ WXLRESULT wxTopLevelWindowMSW::MSWWindowProc(WXUINT message, WXWPARAM wParam, WX
return rc;
}
+namespace
+{
+
+static bool IsPerMonitorDPIAware(HWND hwnd)
+{
+ bool dpiAware = false;
+
+ // Determine if 'Per Monitor v2' DPI awareness is enabled in the
+ // applications manifest.
+#if wxUSE_DYNLIB_CLASS
+ #define WXDPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 ((WXDPI_AWARENESS_CONTEXT)-4)
+ typedef WXDPI_AWARENESS_CONTEXT(WINAPI * GetWindowDpiAwarenessContext_t)(HWND hwnd);
+ typedef BOOL(WINAPI * AreDpiAwarenessContextsEqual_t)(WXDPI_AWARENESS_CONTEXT dpiContextA, WXDPI_AWARENESS_CONTEXT dpiContextB);
+ static GetWindowDpiAwarenessContext_t s_pfnGetWindowDpiAwarenessContext = NULL;
+ static AreDpiAwarenessContextsEqual_t s_pfnAreDpiAwarenessContextsEqual = NULL;
+ static bool s_initDone = false;
+
+ if ( !s_initDone )
+ {
+ wxLoadedDLL dllUser32("user32.dll");
+ wxDL_INIT_FUNC(s_pfn, GetWindowDpiAwarenessContext, dllUser32);
+ wxDL_INIT_FUNC(s_pfn, AreDpiAwarenessContextsEqual, dllUser32);
+ s_initDone = true;
+ }
+
+ if ( s_pfnGetWindowDpiAwarenessContext && s_pfnAreDpiAwarenessContextsEqual )
+ {
+ WXDPI_AWARENESS_CONTEXT dpiAwarenessContext = s_pfnGetWindowDpiAwarenessContext(hwnd);
+
+ if ( s_pfnAreDpiAwarenessContextsEqual(dpiAwarenessContext, WXDPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2) == TRUE )
+ {
+ dpiAware = true;
+ }
+ }
+#endif // wxUSE_DYNLIB_CLASS
+
+ return dpiAware;
+}
+
+}
+
bool wxTopLevelWindowMSW::CreateDialog(const void *dlgTemplate,
const wxString& title,
const wxPoint& pos,
@@ -484,6 +559,10 @@ bool wxTopLevelWindowMSW::Create(wxWindow *parent,
EnableCloseButton(false);
}
+ m_activeDPI = GetDPI();
+
+ m_perMonitorDPIaware = IsPerMonitorDPIAware(GetHwnd());
+
// for standard dialogs the dialog manager generates WM_CHANGEUISTATE
// itself but for custom windows we have to do it ourselves in order to
// make the keyboard indicators (such as underlines for accelerators and
diff --git a/src/msw/window.cpp b/src/msw/window.cpp
index 77a883460b..f2ca075797 100644
--- a/src/msw/window.cpp
+++ b/src/msw/window.cpp
@@ -4837,6 +4837,65 @@ wxSize wxWindowMSW::GetDPI() const
return dpi;
}
+void wxWindowMSW::MSWUpdateFontOnDPIChange(const wxSize& newDPI)
+{
+ if ( m_font.IsOk() )
+ {
+ m_font.WXAdjustToPPI(newDPI);
+
+ // WXAdjustToPPI() changes the HFONT, so reassociate it with the window.
+ wxSetWindowFont(GetHwnd(), m_font);
+ }
+}
+
+// Helper function to update the given coordinate by the scaling factor if it
+// is set, i.e. different from wxDefaultCoord.
+static void ScaleCoordIfSet(int& coord, float scaleFactor)
+{
+ if ( coord != wxDefaultCoord )
+ {
+ const float coordScaled = coord * scaleFactor;
+ coord = scaleFactor > 1.0 ? ceil(coordScaled) : floor(coordScaled);
+ }
+}
+
+void
+wxWindowMSW::MSWUpdateOnDPIChange(const wxSize& oldDPI, const wxSize& newDPI)
+{
+ // update min and max size if necessary
+ const float scaleFactor = (float)newDPI.y / oldDPI.y;
+
+ ScaleCoordIfSet(m_minHeight, scaleFactor);
+ ScaleCoordIfSet(m_minWidth, scaleFactor);
+ ScaleCoordIfSet(m_maxHeight, scaleFactor);
+ ScaleCoordIfSet(m_maxWidth, scaleFactor);
+
+ InvalidateBestSize();
+
+ // update font if necessary
+ MSWUpdateFontOnDPIChange(newDPI);
+
+ // update children
+ wxWindowList::compatibility_iterator current = GetChildren().GetFirst();
+ while ( current )
+ {
+ wxWindow *childWin = current->GetData();
+ // Update all children, except other top-level windows.
+ // These could be on a different monitor and will get their own
+ // dpi-changed event.
+ if ( childWin && !childWin->IsTopLevel() )
+ {
+ childWin->MSWUpdateOnDPIChange(oldDPI, newDPI);
+ }
+
+ current = current->GetNext();
+ }
+
+ wxDPIChangedEvent event(oldDPI, newDPI);
+ event.SetEventObject(this);
+ HandleWindowEvent(event);
+}
+
// ---------------------------------------------------------------------------
// colours and palettes
// ---------------------------------------------------------------------------