From e9ca12aaacb8792a8d737e0e2c03e6aa862973dd Mon Sep 17 00:00:00 2001 From: dasimx Date: Sat, 30 Mar 2019 10:30:15 +0100 Subject: [PATCH 1/4] Remove the now unnecessary wxDisplayFactoryMSW::RefreshMonitors() wxDisplay::InvalidateCache() does the same thing this function does, so just remove it to avoid confusion. This also allows to get rid of ms_factory duplicating the factory pointer stored at wxDisplay level. See https://github.com/wxWidgets/wxWidgets/pull/1281 --- src/msw/display.cpp | 21 +-------------------- 1 file changed, 1 insertion(+), 20 deletions(-) diff --git a/src/msw/display.cpp b/src/msw/display.cpp index 993b3cdaa9..7c23d54f15 100644 --- a/src/msw/display.cpp +++ b/src/msw/display.cpp @@ -198,10 +198,6 @@ public: DoRefreshMonitors(); } - // Called when we receive WM_SETTINGCHANGE to refresh the list of monitor - // handles. - static void RefreshMonitors() { ms_factory->InvalidateCache(); } - // Declare the second argument as int to avoid problems with older SDKs not // declaring MONITOR_DPI_TYPE enum. typedef HRESULT (WINAPI *GetDpiForMonitor_t)(HMONITOR, int, UINT*, UINT*); @@ -221,15 +217,9 @@ private: // return wxNOT_FOUND if not found int FindDisplayFromHMONITOR(HMONITOR hmon) const; - // Update m_displays array, used by RefreshMonitors(). + // Update m_displays array, used initially and by InvalidateCache(). void DoRefreshMonitors(); - - // The unique factory being used (as we don't have direct access to the - // global factory pointer in the common code so we just duplicate this - // variable (also making it of correct type for us) here). - static wxDisplayFactoryMSW* ms_factory; - // The pointer to GetDpiForMonitorPtr(), retrieved on demand, and the // related data, including the DLL containing the function that we must // keep loaded. @@ -285,7 +275,6 @@ private: wxDECLARE_NO_COPY_CLASS(wxDisplayFactoryMSW); }; -wxDisplayFactoryMSW* wxDisplayFactoryMSW::ms_factory = NULL; wxDisplayFactoryMSW::GetDpiForMonitorData wxDisplayFactoryMSW::ms_getDpiForMonitorData; @@ -547,12 +536,6 @@ wxDisplayWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) wxDisplayFactoryMSW::wxDisplayFactoryMSW() { - // This is not supposed to happen with the current code, the factory is - // implicitly a singleton. - wxASSERT_MSG( !ms_factory, wxS("Using more than one factory?") ); - - ms_factory = this; - m_hiddenHwnd = NULL; m_hiddenClass = NULL; @@ -592,8 +575,6 @@ wxDisplayFactoryMSW::~wxDisplayFactoryMSW() ms_getDpiForMonitorData.UnloadIfNecessary(); ms_getDpiForMonitorData.m_initialized = false; } - - ms_factory = NULL; } /* static */ From 8927d7b092d6014eea7dd4d84eb72edf19c36781 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Sat, 30 Mar 2019 14:21:01 +0100 Subject: [PATCH 2/4] Update displays sample correctly when displays change wxEVT_DISPLAY_CHANGED notifies not only about the change of resolution of a display, but also about removal or addition of a new display, so we need to completely repopulate the notebook showing the displays on receiving it. --- samples/display/display.cpp | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/samples/display/display.cpp b/samples/display/display.cpp index ed5420d147..2d5b264dcb 100644 --- a/samples/display/display.cpp +++ b/samples/display/display.cpp @@ -81,6 +81,9 @@ public: void OnLeftClick(wxMouseEvent& event); private: + // Fill m_book with the information about all the displays. + void PopuplateWithDisplayInfo(); + #if wxUSE_DISPLAY // convert video mode to textual description wxString VideoModeToText(const wxVideoMode& mode); @@ -233,6 +236,11 @@ MyFrame::MyFrame(const wxString& title, const wxPoint& pos, const wxSize& size, // create child controls m_book = new wxBookCtrl(this, wxID_ANY); + PopuplateWithDisplayInfo(); +} + +void MyFrame::PopuplateWithDisplayInfo() +{ const size_t countDpy = wxDisplay::GetCount(); for ( size_t nDpy = 0; nDpy < countDpy; nDpy++ ) { @@ -426,16 +434,8 @@ void MyFrame::OnLeftClick(wxMouseEvent& event) void MyFrame::OnDisplayChanged(wxDisplayChangedEvent& event) { - // update the current mode text - for ( size_t n = 0; n < m_book->GetPageCount(); n++ ) - { - wxStaticText *label = wxDynamicCast(m_book->GetPage(n)-> - FindWindow(Display_CurrentMode), - wxStaticText); - if ( label ) - label->SetLabel(VideoModeToText(wxDisplay(n).GetCurrentMode())); - } - + m_book->DeleteAllPages(); + PopuplateWithDisplayInfo(); wxLogStatus(this, "Display resolution was changed."); From f3c5923e1a6f66b2e5fb8852b0e288f073a6b83e Mon Sep 17 00:00:00 2001 From: dasimx Date: Sun, 31 Mar 2019 13:18:41 +0200 Subject: [PATCH 3/4] Fix refreshing display information in wxMSW in some special cases Passing screen HDC to EnumDisplayMonitors() doesn't work if we do it while a UAC prompt is shown or during log-off process, so change the code enumerating the displays to use monitor-appropriate HDC instead. This fixes the problem with losing display information entirely if WM_SETTINGSCHANGE was generated when showing UAC prompt, as it happens when "automatic background color" option is set under Windows 10. --- src/msw/display.cpp | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/src/msw/display.cpp b/src/msw/display.cpp index 7c23d54f15..6c783971be 100644 --- a/src/msw/display.cpp +++ b/src/msw/display.cpp @@ -594,10 +594,10 @@ void wxDisplayFactoryMSW::DoRefreshMonitors() { m_displays.clear(); - // We need to pass a valid HDC here in order to get valid hdcMonitor in our - // callback. - ScreenHDC dc; - if ( !::EnumDisplayMonitors(dc, NULL, MultimonEnumProc, (LPARAM)this) ) + // Note that we pass NULL as first parameter here because using screen HDC + // doesn't work reliably: notably, it doesn't enumerate any displays if + // this code is executed while a UAC prompt is shown or during log-off. + if ( !::EnumDisplayMonitors(NULL, NULL, MultimonEnumProc, (LPARAM)this) ) { wxLogLastError(wxT("EnumDisplayMonitors")); } @@ -607,13 +607,24 @@ void wxDisplayFactoryMSW::DoRefreshMonitors() BOOL CALLBACK wxDisplayFactoryMSW::MultimonEnumProc( HMONITOR hMonitor, // handle to display monitor - HDC hdcMonitor, // handle to monitor-appropriate device context + HDC /* hdcMonitor */, // handle to monitor-appropriate device context: + // not set due to our use of EnumDisplayMonitors(NULL, ...) LPRECT WXUNUSED(lprcMonitor), // pointer to monitor intersection rectangle LPARAM dwData) // data passed from EnumDisplayMonitors (this) { wxDisplayFactoryMSW *const self = (wxDisplayFactoryMSW *)dwData; - self->m_displays.push_back(wxDisplayInfo(hMonitor, wxGetHDCDepth(hdcMonitor))); + WinStruct monInfo; + if ( !::GetMonitorInfo(hMonitor, &monInfo) ) + { + wxLogLastError(wxT("GetMonitorInfo")); + } + + HDC hdcMonitor = ::CreateDC(NULL, monInfo.szDevice, NULL, NULL); + const int hdcDepth = wxGetHDCDepth(hdcMonitor); + ::DeleteDC(hdcMonitor); + + self->m_displays.push_back(wxDisplayInfo(hMonitor, hdcDepth)); // continue the enumeration return TRUE; From 3959810d42fd3d0cd8d3a8bafa346fcfa5ed8e23 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Sun, 31 Mar 2019 13:22:30 +0200 Subject: [PATCH 4/4] Avoid unnecessary cached display information updates Only invalidate display cache when something related to the displays has changed and not whenever any system option has. This should avoid completely unnecessary refreshes when a UAC prompt is shown, for example: even if doing this works now, after the previous commit, we still don't have to do it at all. --- src/msw/display.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/msw/display.cpp b/src/msw/display.cpp index 6c783971be..9f1892f07d 100644 --- a/src/msw/display.cpp +++ b/src/msw/display.cpp @@ -524,7 +524,8 @@ bool wxDisplayMSW::ChangeMode(const wxVideoMode& mode) LRESULT APIENTRY wxDisplayWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { - if ( msg == WM_SETTINGCHANGE || msg == WM_DISPLAYCHANGE ) + if ( (msg == WM_SETTINGCHANGE && wParam == SPI_SETWORKAREA) || + msg == WM_DISPLAYCHANGE ) { wxDisplay::InvalidateCache();