Refresh wxDisplay when a monitor is added to or removed from the system.

If a monitor was attached or detached while a wx program was running, the
monitor handles stored in wxDisplayFactoryMSW became invalid and all display
operations (e.g. getting display size) failed from this moment onwards
requiring a program restart to work again.

Fix this by updating the monitor handles when we get WM_SETTINGCHANGE as it is
sent when a monitor is added or removed (while it's also sent in quite a few
other cases re-enumerating the monitors shouldn't take very long so just do it
always).

git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@69171 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
This commit is contained in:
Vadim Zeitlin
2011-09-21 15:07:35 +00:00
parent b481194f51
commit ad6f09f543

View File

@@ -42,6 +42,7 @@
#include "wx/msw/wrapwin.h" #include "wx/msw/wrapwin.h"
#include "wx/msw/missing.h" #include "wx/msw/missing.h"
#include "wx/msw/private.h" #include "wx/msw/private.h"
#include "wx/msw/private/hiddenwin.h"
#ifndef __WXWINCE__ #ifndef __WXWINCE__
// Older versions of windef.h don't define HMONITOR. Unfortunately, we // Older versions of windef.h don't define HMONITOR. Unfortunately, we
@@ -171,6 +172,9 @@ public:
// m_displays array if they're available // m_displays array if they're available
wxDisplayFactoryMSW(); wxDisplayFactoryMSW();
// Dtor destroys the hidden window we use for getting WM_SETTINGCHANGE.
virtual ~wxDisplayFactoryMSW();
bool IsOk() const { return !m_displays.empty(); } bool IsOk() const { return !m_displays.empty(); }
virtual wxDisplayImpl *CreateDisplay(unsigned n); virtual wxDisplayImpl *CreateDisplay(unsigned n);
@@ -178,6 +182,11 @@ public:
virtual int GetFromPoint(const wxPoint& pt); virtual int GetFromPoint(const wxPoint& pt);
virtual int GetFromWindow(const wxWindow *window); virtual int GetFromWindow(const wxWindow *window);
// Called when we receive WM_SETTINGCHANGE to refresh the list of monitor
// handles.
static void RefreshMonitors() { ms_factory->DoRefreshMonitors(); }
private: private:
// EnumDisplayMonitors() callback // EnumDisplayMonitors() callback
static BOOL CALLBACK MultimonEnumProc(HMONITOR hMonitor, static BOOL CALLBACK MultimonEnumProc(HMONITOR hMonitor,
@@ -189,13 +198,29 @@ private:
// return wxNOT_FOUND if not found // return wxNOT_FOUND if not found
int FindDisplayFromHMONITOR(HMONITOR hmon) const; int FindDisplayFromHMONITOR(HMONITOR hmon) const;
// Update m_displays array, used by RefreshMonitors().
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 array containing information about all available displays, filled by // the array containing information about all available displays, filled by
// MultimonEnumProc() // MultimonEnumProc()
wxMonitorHandleArray m_displays; wxMonitorHandleArray m_displays;
// The hidden window we use for receiving WM_SETTINGCHANGE and its class
// name.
HWND m_hiddenHwnd;
const wxChar* m_hiddenClass;
wxDECLARE_NO_COPY_CLASS(wxDisplayFactoryMSW); wxDECLARE_NO_COPY_CLASS(wxDisplayFactoryMSW);
}; };
wxDisplayFactoryMSW* wxDisplayFactoryMSW::ms_factory = NULL;
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// wxDisplay implementation // wxDisplay implementation
@@ -445,8 +470,30 @@ bool wxDisplayMSW::ChangeMode(const wxVideoMode& mode)
// wxDisplayFactoryMSW implementation // wxDisplayFactoryMSW implementation
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
LRESULT APIENTRY
wxDisplayWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
if ( msg == WM_SETTINGCHANGE )
{
wxDisplayFactoryMSW::RefreshMonitors();
return 0;
}
return ::DefWindowProc(hwnd, msg, wParam, lParam);
}
wxDisplayFactoryMSW::wxDisplayFactoryMSW() 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;
if ( gs_MonitorFromPoint==NULL || gs_MonitorFromWindow==NULL if ( gs_MonitorFromPoint==NULL || gs_MonitorFromWindow==NULL
|| gs_GetMonitorInfo==NULL || gs_EnumDisplayMonitors==NULL ) || gs_GetMonitorInfo==NULL || gs_EnumDisplayMonitors==NULL )
{ {
@@ -466,7 +513,44 @@ wxDisplayFactoryMSW::wxDisplayFactoryMSW()
|| gs_GetMonitorInfo==NULL || gs_EnumDisplayMonitors==NULL ) || gs_GetMonitorInfo==NULL || gs_EnumDisplayMonitors==NULL )
return; return;
// enumerate all displays DoRefreshMonitors();
// Also create a hidden window to listen for WM_SETTINGCHANGE that we
// receive when a monitor is added to or removed from the system as we must
// refresh our monitor handles information then.
m_hiddenHwnd = wxCreateHiddenWindow
(
&m_hiddenClass,
wxT("wxDisplayHiddenWindow"),
wxDisplayWndProc
);
}
wxDisplayFactoryMSW::~wxDisplayFactoryMSW()
{
if ( m_hiddenHwnd )
{
if ( !::DestroyWindow(m_hiddenHwnd) )
{
wxLogLastError(wxT("DestroyWindow(wxDisplayHiddenWindow)"));
}
if ( m_hiddenClass )
{
if ( !::UnregisterClass(m_hiddenClass, wxGetInstance()) )
{
wxLogLastError(wxT("UnregisterClass(wxDisplayHiddenWindow)"));
}
}
}
ms_factory = NULL;
}
void wxDisplayFactoryMSW::DoRefreshMonitors()
{
m_displays.Clear();
if ( !gs_EnumDisplayMonitors(NULL, NULL, MultimonEnumProc, (LPARAM)this) ) if ( !gs_EnumDisplayMonitors(NULL, NULL, MultimonEnumProc, (LPARAM)this) )
{ {
wxLogLastError(wxT("EnumDisplayMonitors")); wxLogLastError(wxT("EnumDisplayMonitors"));