1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/msw/display.cpp
3 // Purpose: MSW Implementation of wxDisplay class
4 // Author: Royce Mitchell III, Vadim Zeitlin
5 // Modified by: Ryan Norton (IsPrimary override)
8 // Copyright: (c) wxWidgets team
9 // Copyright: (c) 2002-2006 wxWidgets team
10 // Licence: wxWindows licence
11 /////////////////////////////////////////////////////////////////////////////
13 // ===========================================================================
15 // ===========================================================================
17 // ---------------------------------------------------------------------------
19 // ---------------------------------------------------------------------------
21 // For compilers that support precompilation, includes "wx.h".
22 #include "wx/wxprec.h"
30 #include "wx/display.h"
33 #include "wx/dynarray.h"
38 #include "wx/dynlib.h"
39 #include "wx/sysopt.h"
41 #include "wx/display_impl.h"
42 #include "wx/msw/wrapwin.h"
43 #include "wx/msw/missing.h"
44 #include "wx/msw/private.h"
45 #include "wx/msw/private/hiddenwin.h"
48 // Older versions of windef.h don't define HMONITOR. Unfortunately, we
49 // can't directly test whether HMONITOR is defined or not in windef.h as
50 // it's not a macro but a typedef, so we test for an unrelated symbol which
51 // is only defined in winuser.h if WINVER >= 0x0500
52 #if !defined(HMONITOR_DECLARED) && !defined(MNS_NOCHECK)
53 DECLARE_HANDLE(HMONITOR
);
54 typedef BOOL(CALLBACK
* MONITORENUMPROC
)(HMONITOR
, HDC
, LPRECT
, LPARAM
);
55 typedef struct tagMONITORINFO
61 } MONITORINFO
, *LPMONITORINFO
;
62 typedef struct tagMONITORINFOEX
: public tagMONITORINFO
64 TCHAR szDevice
[CCHDEVICENAME
];
65 } MONITORINFOEX
, *LPMONITORINFOEX
;
66 #define MONITOR_DEFAULTTONULL 0x00000000
67 #define MONITOR_DEFAULTTOPRIMARY 0x00000001
68 #define MONITOR_DEFAULTTONEAREST 0x00000002
69 #define MONITORINFOF_PRIMARY 0x00000001
70 #define HMONITOR_DECLARED
72 #endif // !__WXWINCE__
74 // display functions are found in different DLLs under WinCE and normal Win32
76 static const wxChar displayDllName
[] = wxT("coredll.dll");
78 static const wxChar displayDllName
[] = wxT("user32.dll");
81 // ----------------------------------------------------------------------------
82 // typedefs for dynamically loaded Windows functions
83 // ----------------------------------------------------------------------------
85 typedef LONG (WINAPI
*ChangeDisplaySettingsEx_t
)(LPCTSTR lpszDeviceName
,
91 typedef BOOL (WINAPI
*EnumDisplayMonitors_t
)(HDC
,LPCRECT
,MONITORENUMPROC
,LPARAM
);
92 typedef HMONITOR (WINAPI
*MonitorFromPoint_t
)(POINT
,DWORD
);
93 typedef HMONITOR (WINAPI
*MonitorFromWindow_t
)(HWND
,DWORD
);
94 typedef BOOL (WINAPI
*GetMonitorInfo_t
)(HMONITOR
,LPMONITORINFO
);
97 // emulation of ChangeDisplaySettingsEx() for Win95
98 LONG WINAPI
ChangeDisplaySettingsExForWin95(LPCTSTR
WXUNUSED(lpszDeviceName
),
102 LPVOID
WXUNUSED(lParam
))
104 return ::ChangeDisplaySettings(lpDevMode
, dwFlags
);
106 #endif // !__WXWINCE__
109 // ----------------------------------------------------------------------------
110 // wxDisplayMSW declaration
111 // ----------------------------------------------------------------------------
113 class wxDisplayMSW
: public wxDisplayImpl
116 wxDisplayMSW(unsigned n
, HMONITOR hmon
)
122 virtual wxRect
GetGeometry() const;
123 virtual wxRect
GetClientArea() const;
124 virtual wxString
GetName() const;
125 virtual bool IsPrimary() const;
127 virtual wxVideoMode
GetCurrentMode() const;
128 virtual wxArrayVideoModes
GetModes(const wxVideoMode
& mode
) const;
129 virtual bool ChangeMode(const wxVideoMode
& mode
);
132 // convert a DEVMODE to our wxVideoMode
133 static wxVideoMode
ConvertToVideoMode(const DEVMODE
& dm
)
135 // note that dmDisplayFrequency may be 0 or 1 meaning "standard one"
136 // and although 0 is ok for us we don't want to return modes with 1hz
138 return wxVideoMode(dm
.dmPelsWidth
,
141 dm
.dmDisplayFrequency
> 1 ? dm
.dmDisplayFrequency
: 0);
144 // Call GetMonitorInfo() and fill in the provided struct and return true if
145 // it succeeded, otherwise return false.
146 bool GetMonInfo(MONITORINFOEX
& monInfo
) const;
151 wxDECLARE_NO_COPY_CLASS(wxDisplayMSW
);
155 // ----------------------------------------------------------------------------
156 // wxDisplayFactoryMSW declaration
157 // ----------------------------------------------------------------------------
159 WX_DEFINE_ARRAY(HMONITOR
, wxMonitorHandleArray
);
161 // functions dynamically bound by wxDisplayFactoryMSW ctor.
162 static MonitorFromPoint_t gs_MonitorFromPoint
= NULL
;
163 static MonitorFromWindow_t gs_MonitorFromWindow
= NULL
;
164 static GetMonitorInfo_t gs_GetMonitorInfo
= NULL
;
165 static EnumDisplayMonitors_t gs_EnumDisplayMonitors
= NULL
;
167 class wxDisplayFactoryMSW
: public wxDisplayFactory
170 // ctor checks if the current system supports multimon API and dynamically
171 // bind the functions we need if this is the case and fills the
172 // m_displays array if they're available
173 wxDisplayFactoryMSW();
175 // Dtor destroys the hidden window we use for getting WM_SETTINGCHANGE.
176 virtual ~wxDisplayFactoryMSW();
178 bool IsOk() const { return !m_displays
.empty(); }
180 virtual wxDisplayImpl
*CreateDisplay(unsigned n
);
181 virtual unsigned GetCount() { return unsigned(m_displays
.size()); }
182 virtual int GetFromPoint(const wxPoint
& pt
);
183 virtual int GetFromWindow(const wxWindow
*window
);
185 // Called when we receive WM_SETTINGCHANGE to refresh the list of monitor
187 static void RefreshMonitors() { ms_factory
->DoRefreshMonitors(); }
191 // EnumDisplayMonitors() callback
192 static BOOL CALLBACK
MultimonEnumProc(HMONITOR hMonitor
,
197 // find the monitor corresponding to the given handle,
198 // return wxNOT_FOUND if not found
199 int FindDisplayFromHMONITOR(HMONITOR hmon
) const;
201 // Update m_displays array, used by RefreshMonitors().
202 void DoRefreshMonitors();
205 // The unique factory being used (as we don't have direct access to the
206 // global factory pointer in the common code so we just duplicate this
207 // variable (also making it of correct type for us) here).
208 static wxDisplayFactoryMSW
* ms_factory
;
211 // the array containing information about all available displays, filled by
212 // MultimonEnumProc()
213 wxMonitorHandleArray m_displays
;
215 // The hidden window we use for receiving WM_SETTINGCHANGE and its class
218 const wxChar
* m_hiddenClass
;
220 wxDECLARE_NO_COPY_CLASS(wxDisplayFactoryMSW
);
223 wxDisplayFactoryMSW
* wxDisplayFactoryMSW::ms_factory
= NULL
;
225 // ----------------------------------------------------------------------------
226 // wxDisplay implementation
227 // ----------------------------------------------------------------------------
229 /* static */ wxDisplayFactory
*wxDisplay::CreateFactory()
231 wxDisplayFactoryMSW
*factoryMM
= new wxDisplayFactoryMSW
;
233 if ( factoryMM
->IsOk() )
238 // fall back to a stub implementation if no multimon support (Win95?)
239 return new wxDisplayFactorySingle
;
243 // ----------------------------------------------------------------------------
244 // wxDisplayMSW implementation
245 // ----------------------------------------------------------------------------
247 bool wxDisplayMSW::GetMonInfo(MONITORINFOEX
& monInfo
) const
249 if ( !gs_GetMonitorInfo(m_hmon
, &monInfo
) )
251 wxLogLastError(wxT("GetMonitorInfo"));
258 wxRect
wxDisplayMSW::GetGeometry() const
260 WinStruct
<MONITORINFOEX
> monInfo
;
263 if ( GetMonInfo(monInfo
) )
264 wxCopyRECTToRect(monInfo
.rcMonitor
, rect
);
269 wxRect
wxDisplayMSW::GetClientArea() const
271 WinStruct
<MONITORINFOEX
> monInfo
;
274 if ( GetMonInfo(monInfo
) )
275 wxCopyRECTToRect(monInfo
.rcWork
, rectClient
);
280 wxString
wxDisplayMSW::GetName() const
282 WinStruct
<MONITORINFOEX
> monInfo
;
285 if ( GetMonInfo(monInfo
) )
286 name
= monInfo
.szDevice
;
291 bool wxDisplayMSW::IsPrimary() const
293 WinStruct
<MONITORINFOEX
> monInfo
;
295 if ( !GetMonInfo(monInfo
) )
298 return (monInfo
.dwFlags
& MONITORINFOF_PRIMARY
) != 0;
301 wxVideoMode
wxDisplayMSW::GetCurrentMode() const
305 // The first parameter of EnumDisplaySettings() must be NULL under Win95
306 // according to MSDN. The version of GetName() we implement for Win95
307 // returns an empty string.
308 const wxString name
= GetName();
309 const wxChar
* const deviceName
= name
.empty()
310 ? (const wxChar
*)NULL
311 : (const wxChar
*)name
.c_str();
314 dm
.dmSize
= sizeof(dm
);
315 dm
.dmDriverExtra
= 0;
317 if ( !::EnumDisplaySettings(deviceName
, ENUM_CURRENT_SETTINGS
, &dm
) )
319 wxLogLastError(wxT("EnumDisplaySettings(ENUM_CURRENT_SETTINGS)"));
323 mode
= ConvertToVideoMode(dm
);
329 wxArrayVideoModes
wxDisplayMSW::GetModes(const wxVideoMode
& modeMatch
) const
331 wxArrayVideoModes modes
;
333 // The first parameter of EnumDisplaySettings() must be NULL under Win95
334 // according to MSDN. The version of GetName() we implement for Win95
335 // returns an empty string.
336 const wxString name
= GetName();
337 const wxChar
* const deviceName
= name
.empty()
338 ? (const wxChar
*)NULL
339 : (const wxChar
*)name
.c_str();
342 dm
.dmSize
= sizeof(dm
);
343 dm
.dmDriverExtra
= 0;
345 for ( int iModeNum
= 0;
346 ::EnumDisplaySettings(deviceName
, iModeNum
, &dm
);
349 const wxVideoMode mode
= ConvertToVideoMode(dm
);
350 if ( mode
.Matches(modeMatch
) )
359 bool wxDisplayMSW::ChangeMode(const wxVideoMode
& mode
)
361 // prepare ChangeDisplaySettingsEx() parameters
367 if ( mode
== wxDefaultVideoMode
)
369 // reset the video mode to default
373 else // change to the given mode
375 wxCHECK_MSG( mode
.GetWidth() && mode
.GetHeight(), false,
376 wxT("at least the width and height must be specified") );
379 dm
.dmSize
= sizeof(dm
);
380 dm
.dmDriverExtra
= 0;
381 dm
.dmFields
= DM_PELSWIDTH
| DM_PELSHEIGHT
;
382 dm
.dmPelsWidth
= mode
.GetWidth();
383 dm
.dmPelsHeight
= mode
.GetHeight();
385 if ( mode
.GetDepth() )
387 dm
.dmFields
|= DM_BITSPERPEL
;
388 dm
.dmBitsPerPel
= mode
.GetDepth();
391 if ( mode
.GetRefresh() )
393 dm
.dmFields
|= DM_DISPLAYFREQUENCY
;
394 dm
.dmDisplayFrequency
= mode
.GetRefresh();
401 #else // !__WXWINCE__
402 flags
= CDS_FULLSCREEN
;
403 #endif // __WXWINCE__/!__WXWINCE__
407 // get pointer to the function dynamically
409 // we're only called from the main thread, so it's ok to use static
411 static ChangeDisplaySettingsEx_t pfnChangeDisplaySettingsEx
= NULL
;
412 if ( !pfnChangeDisplaySettingsEx
)
414 wxDynamicLibrary
dllDisplay(displayDllName
, wxDL_VERBATIM
| wxDL_QUIET
);
415 if ( dllDisplay
.IsLoaded() )
417 wxDL_INIT_FUNC_AW(pfn
, ChangeDisplaySettingsEx
, dllDisplay
);
419 //else: huh, no this DLL must always be present, what's going on??
422 if ( !pfnChangeDisplaySettingsEx
)
424 // we must be under Win95 and so there is no multiple monitors
426 pfnChangeDisplaySettingsEx
= ChangeDisplaySettingsExForWin95
;
428 #endif // !__WXWINCE__
431 // do change the mode
432 switch ( pfnChangeDisplaySettingsEx
434 GetName().t_str(), // display name
435 pDevMode
, // dev mode or NULL to reset
438 NULL
// pointer to video parameters (not used)
441 case DISP_CHANGE_SUCCESSFUL
:
444 // If we have a top-level, full-screen frame, emulate
445 // the DirectX behaviour and resize it. This makes this
446 // API quite a bit easier to use.
447 wxWindow
*winTop
= wxTheApp
->GetTopWindow();
448 wxFrame
*frameTop
= wxDynamicCast(winTop
, wxFrame
);
449 if (frameTop
&& frameTop
->IsFullScreen())
451 wxVideoMode current
= GetCurrentMode();
452 frameTop
->SetClientSize(current
.GetWidth(), current
.GetHeight());
457 case DISP_CHANGE_BADMODE
:
458 // don't complain about this, this is the only "expected" error
462 wxFAIL_MSG( wxT("unexpected ChangeDisplaySettingsEx() return value") );
469 // ----------------------------------------------------------------------------
470 // wxDisplayFactoryMSW implementation
471 // ----------------------------------------------------------------------------
474 wxDisplayWndProc(HWND hwnd
, UINT msg
, WPARAM wParam
, LPARAM lParam
)
476 if ( msg
== WM_SETTINGCHANGE
)
478 wxDisplayFactoryMSW::RefreshMonitors();
483 return ::DefWindowProc(hwnd
, msg
, wParam
, lParam
);
486 wxDisplayFactoryMSW::wxDisplayFactoryMSW()
488 // This is not supposed to happen with the current code, the factory is
489 // implicitly a singleton.
490 wxASSERT_MSG( !ms_factory
, wxS("Using more than one factory?") );
495 m_hiddenClass
= NULL
;
497 if ( gs_MonitorFromPoint
==NULL
|| gs_MonitorFromWindow
==NULL
498 || gs_GetMonitorInfo
==NULL
|| gs_EnumDisplayMonitors
==NULL
)
500 // First initialization, or last initialization failed.
501 wxDynamicLibrary
dllDisplay(displayDllName
, wxDL_VERBATIM
| wxDL_QUIET
);
503 wxDL_INIT_FUNC(gs_
, MonitorFromPoint
, dllDisplay
);
504 wxDL_INIT_FUNC(gs_
, MonitorFromWindow
, dllDisplay
);
505 wxDL_INIT_FUNC_AW(gs_
, GetMonitorInfo
, dllDisplay
);
506 wxDL_INIT_FUNC(gs_
, EnumDisplayMonitors
, dllDisplay
);
508 // we can safely let dllDisplay go out of scope, the DLL itself will
509 // still remain loaded as all programs link to it statically anyhow
512 if ( gs_MonitorFromPoint
==NULL
|| gs_MonitorFromWindow
==NULL
513 || gs_GetMonitorInfo
==NULL
|| gs_EnumDisplayMonitors
==NULL
)
518 // Also create a hidden window to listen for WM_SETTINGCHANGE that we
519 // receive when a monitor is added to or removed from the system as we must
520 // refresh our monitor handles information then.
521 m_hiddenHwnd
= wxCreateHiddenWindow
524 wxT("wxDisplayHiddenWindow"),
529 wxDisplayFactoryMSW::~wxDisplayFactoryMSW()
533 if ( !::DestroyWindow(m_hiddenHwnd
) )
535 wxLogLastError(wxT("DestroyWindow(wxDisplayHiddenWindow)"));
540 if ( !::UnregisterClass(m_hiddenClass
, wxGetInstance()) )
542 wxLogLastError(wxT("UnregisterClass(wxDisplayHiddenWindow)"));
550 void wxDisplayFactoryMSW::DoRefreshMonitors()
554 if ( !gs_EnumDisplayMonitors(NULL
, NULL
, MultimonEnumProc
, (LPARAM
)this) )
556 wxLogLastError(wxT("EnumDisplayMonitors"));
562 wxDisplayFactoryMSW::MultimonEnumProc(
563 HMONITOR hMonitor
, // handle to display monitor
564 HDC
WXUNUSED(hdcMonitor
), // handle to monitor-appropriate device context
565 LPRECT
WXUNUSED(lprcMonitor
), // pointer to monitor intersection rectangle
566 LPARAM dwData
) // data passed from EnumDisplayMonitors (this)
568 wxDisplayFactoryMSW
*const self
= (wxDisplayFactoryMSW
*)dwData
;
570 self
->m_displays
.Add(hMonitor
);
572 // continue the enumeration
576 wxDisplayImpl
*wxDisplayFactoryMSW::CreateDisplay(unsigned n
)
578 wxCHECK_MSG( n
< m_displays
.size(), NULL
, wxT("An invalid index was passed to wxDisplay") );
580 return new wxDisplayMSW(n
, m_displays
[n
]);
583 // helper for GetFromPoint() and GetFromWindow()
584 int wxDisplayFactoryMSW::FindDisplayFromHMONITOR(HMONITOR hmon
) const
588 const size_t count
= m_displays
.size();
589 for ( size_t n
= 0; n
< count
; n
++ )
591 if ( hmon
== m_displays
[n
] )
599 int wxDisplayFactoryMSW::GetFromPoint(const wxPoint
& pt
)
605 return FindDisplayFromHMONITOR(gs_MonitorFromPoint(pt2
,
606 MONITOR_DEFAULTTONULL
));
609 int wxDisplayFactoryMSW::GetFromWindow(const wxWindow
*window
)
612 return FindDisplayFromHMONITOR(gs_MonitorFromWindow(GetHwndOf(window
),
613 MONITOR_DEFAULTTONULL
));
615 const wxSize halfsize
= window
->GetSize() / 2;
616 wxPoint pt
= window
->GetScreenPosition();
619 return GetFromPoint(pt
);
623 #endif // wxUSE_DISPLAY
625 void wxClientDisplayRect(int *x
, int *y
, int *width
, int *height
)
627 #if defined(__WXMICROWIN__)
629 wxDisplaySize(width
, height
);
631 // Determine the desktop dimensions minus the taskbar and any other
632 // special decorations...
635 SystemParametersInfo(SPI_GETWORKAREA
, 0, &r
, 0);
638 if (width
) *width
= r
.right
- r
.left
;
639 if (height
) *height
= r
.bottom
- r
.top
;