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)
7 // Copyright: (c) wxWidgets team
8 // Copyright: (c) 2002-2006 wxWidgets team
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
12 // ===========================================================================
14 // ===========================================================================
16 // ---------------------------------------------------------------------------
18 // ---------------------------------------------------------------------------
20 // For compilers that support precompilation, includes "wx.h".
21 #include "wx/wxprec.h"
29 #include "wx/display.h"
32 #include "wx/dynarray.h"
37 #include "wx/dynlib.h"
38 #include "wx/sysopt.h"
40 #include "wx/display_impl.h"
41 #include "wx/msw/wrapwin.h"
42 #include "wx/msw/missing.h"
43 #include "wx/msw/private.h"
44 #include "wx/msw/private/hiddenwin.h"
47 // Older versions of windef.h don't define HMONITOR. Unfortunately, we
48 // can't directly test whether HMONITOR is defined or not in windef.h as
49 // it's not a macro but a typedef, so we test for an unrelated symbol which
50 // is only defined in winuser.h if WINVER >= 0x0500
51 #if !defined(HMONITOR_DECLARED) && !defined(MNS_NOCHECK)
52 DECLARE_HANDLE(HMONITOR
);
53 typedef BOOL(CALLBACK
* MONITORENUMPROC
)(HMONITOR
, HDC
, LPRECT
, LPARAM
);
54 typedef struct tagMONITORINFO
60 } MONITORINFO
, *LPMONITORINFO
;
61 typedef struct tagMONITORINFOEX
: public tagMONITORINFO
63 TCHAR szDevice
[CCHDEVICENAME
];
64 } MONITORINFOEX
, *LPMONITORINFOEX
;
65 #define MONITOR_DEFAULTTONULL 0x00000000
66 #define MONITOR_DEFAULTTOPRIMARY 0x00000001
67 #define MONITOR_DEFAULTTONEAREST 0x00000002
68 #define MONITORINFOF_PRIMARY 0x00000001
69 #define HMONITOR_DECLARED
71 #endif // !__WXWINCE__
73 // display functions are found in different DLLs under WinCE and normal Win32
75 static const wxChar displayDllName
[] = wxT("coredll.dll");
77 static const wxChar displayDllName
[] = wxT("user32.dll");
80 // ----------------------------------------------------------------------------
81 // typedefs for dynamically loaded Windows functions
82 // ----------------------------------------------------------------------------
84 typedef LONG (WINAPI
*ChangeDisplaySettingsEx_t
)(LPCTSTR lpszDeviceName
,
90 typedef BOOL (WINAPI
*EnumDisplayMonitors_t
)(HDC
,LPCRECT
,MONITORENUMPROC
,LPARAM
);
91 typedef HMONITOR (WINAPI
*MonitorFromPoint_t
)(POINT
,DWORD
);
92 typedef HMONITOR (WINAPI
*MonitorFromWindow_t
)(HWND
,DWORD
);
93 typedef BOOL (WINAPI
*GetMonitorInfo_t
)(HMONITOR
,LPMONITORINFO
);
96 // emulation of ChangeDisplaySettingsEx() for Win95
97 LONG WINAPI
ChangeDisplaySettingsExForWin95(LPCTSTR
WXUNUSED(lpszDeviceName
),
101 LPVOID
WXUNUSED(lParam
))
103 return ::ChangeDisplaySettings(lpDevMode
, dwFlags
);
105 #endif // !__WXWINCE__
108 // ----------------------------------------------------------------------------
109 // wxDisplayMSW declaration
110 // ----------------------------------------------------------------------------
112 class wxDisplayMSW
: public wxDisplayImpl
115 wxDisplayMSW(unsigned n
, HMONITOR hmon
)
121 virtual wxRect
GetGeometry() const;
122 virtual wxRect
GetClientArea() const;
123 virtual wxString
GetName() const;
124 virtual bool IsPrimary() const;
126 virtual wxVideoMode
GetCurrentMode() const;
127 virtual wxArrayVideoModes
GetModes(const wxVideoMode
& mode
) const;
128 virtual bool ChangeMode(const wxVideoMode
& mode
);
131 // convert a DEVMODE to our wxVideoMode
132 static wxVideoMode
ConvertToVideoMode(const DEVMODE
& dm
)
134 // note that dmDisplayFrequency may be 0 or 1 meaning "standard one"
135 // and although 0 is ok for us we don't want to return modes with 1hz
137 return wxVideoMode(dm
.dmPelsWidth
,
140 dm
.dmDisplayFrequency
> 1 ? dm
.dmDisplayFrequency
: 0);
143 // Call GetMonitorInfo() and fill in the provided struct and return true if
144 // it succeeded, otherwise return false.
145 bool GetMonInfo(MONITORINFOEX
& monInfo
) const;
150 wxDECLARE_NO_COPY_CLASS(wxDisplayMSW
);
154 // ----------------------------------------------------------------------------
155 // wxDisplayFactoryMSW declaration
156 // ----------------------------------------------------------------------------
158 WX_DEFINE_ARRAY(HMONITOR
, wxMonitorHandleArray
);
160 // functions dynamically bound by wxDisplayFactoryMSW ctor.
161 static MonitorFromPoint_t gs_MonitorFromPoint
= NULL
;
162 static MonitorFromWindow_t gs_MonitorFromWindow
= NULL
;
163 static GetMonitorInfo_t gs_GetMonitorInfo
= NULL
;
164 static EnumDisplayMonitors_t gs_EnumDisplayMonitors
= NULL
;
166 class wxDisplayFactoryMSW
: public wxDisplayFactory
169 // ctor checks if the current system supports multimon API and dynamically
170 // bind the functions we need if this is the case and fills the
171 // m_displays array if they're available
172 wxDisplayFactoryMSW();
174 // Dtor destroys the hidden window we use for getting WM_SETTINGCHANGE.
175 virtual ~wxDisplayFactoryMSW();
177 bool IsOk() const { return !m_displays
.empty(); }
179 virtual wxDisplayImpl
*CreateDisplay(unsigned n
);
180 virtual unsigned GetCount() { return unsigned(m_displays
.size()); }
181 virtual int GetFromPoint(const wxPoint
& pt
);
182 virtual int GetFromWindow(const wxWindow
*window
);
184 // Called when we receive WM_SETTINGCHANGE to refresh the list of monitor
186 static void RefreshMonitors() { ms_factory
->DoRefreshMonitors(); }
190 // EnumDisplayMonitors() callback
191 static BOOL CALLBACK
MultimonEnumProc(HMONITOR hMonitor
,
196 // find the monitor corresponding to the given handle,
197 // return wxNOT_FOUND if not found
198 int FindDisplayFromHMONITOR(HMONITOR hmon
) const;
200 // Update m_displays array, used by RefreshMonitors().
201 void DoRefreshMonitors();
204 // The unique factory being used (as we don't have direct access to the
205 // global factory pointer in the common code so we just duplicate this
206 // variable (also making it of correct type for us) here).
207 static wxDisplayFactoryMSW
* ms_factory
;
210 // the array containing information about all available displays, filled by
211 // MultimonEnumProc()
212 wxMonitorHandleArray m_displays
;
214 // The hidden window we use for receiving WM_SETTINGCHANGE and its class
217 const wxChar
* m_hiddenClass
;
219 wxDECLARE_NO_COPY_CLASS(wxDisplayFactoryMSW
);
222 wxDisplayFactoryMSW
* wxDisplayFactoryMSW::ms_factory
= NULL
;
224 // ----------------------------------------------------------------------------
225 // wxDisplay implementation
226 // ----------------------------------------------------------------------------
228 /* static */ wxDisplayFactory
*wxDisplay::CreateFactory()
230 wxDisplayFactoryMSW
*factoryMM
= new wxDisplayFactoryMSW
;
232 if ( factoryMM
->IsOk() )
237 // fall back to a stub implementation if no multimon support (Win95?)
238 return new wxDisplayFactorySingle
;
242 // ----------------------------------------------------------------------------
243 // wxDisplayMSW implementation
244 // ----------------------------------------------------------------------------
246 bool wxDisplayMSW::GetMonInfo(MONITORINFOEX
& monInfo
) const
248 if ( !gs_GetMonitorInfo(m_hmon
, &monInfo
) )
250 wxLogLastError(wxT("GetMonitorInfo"));
257 wxRect
wxDisplayMSW::GetGeometry() const
259 WinStruct
<MONITORINFOEX
> monInfo
;
262 if ( GetMonInfo(monInfo
) )
263 wxCopyRECTToRect(monInfo
.rcMonitor
, rect
);
268 wxRect
wxDisplayMSW::GetClientArea() const
270 WinStruct
<MONITORINFOEX
> monInfo
;
273 if ( GetMonInfo(monInfo
) )
274 wxCopyRECTToRect(monInfo
.rcWork
, rectClient
);
279 wxString
wxDisplayMSW::GetName() const
281 WinStruct
<MONITORINFOEX
> monInfo
;
284 if ( GetMonInfo(monInfo
) )
285 name
= monInfo
.szDevice
;
290 bool wxDisplayMSW::IsPrimary() const
292 WinStruct
<MONITORINFOEX
> monInfo
;
294 if ( !GetMonInfo(monInfo
) )
297 return (monInfo
.dwFlags
& MONITORINFOF_PRIMARY
) != 0;
300 wxVideoMode
wxDisplayMSW::GetCurrentMode() const
304 // The first parameter of EnumDisplaySettings() must be NULL under Win95
305 // according to MSDN. The version of GetName() we implement for Win95
306 // returns an empty string.
307 const wxString name
= GetName();
308 const wxChar
* const deviceName
= name
.empty()
309 ? (const wxChar
*)NULL
310 : (const wxChar
*)name
.c_str();
313 dm
.dmSize
= sizeof(dm
);
314 dm
.dmDriverExtra
= 0;
316 if ( !::EnumDisplaySettings(deviceName
, ENUM_CURRENT_SETTINGS
, &dm
) )
318 wxLogLastError(wxT("EnumDisplaySettings(ENUM_CURRENT_SETTINGS)"));
322 mode
= ConvertToVideoMode(dm
);
328 wxArrayVideoModes
wxDisplayMSW::GetModes(const wxVideoMode
& modeMatch
) const
330 wxArrayVideoModes modes
;
332 // The first parameter of EnumDisplaySettings() must be NULL under Win95
333 // according to MSDN. The version of GetName() we implement for Win95
334 // returns an empty string.
335 const wxString name
= GetName();
336 const wxChar
* const deviceName
= name
.empty()
337 ? (const wxChar
*)NULL
338 : (const wxChar
*)name
.c_str();
341 dm
.dmSize
= sizeof(dm
);
342 dm
.dmDriverExtra
= 0;
344 for ( int iModeNum
= 0;
345 ::EnumDisplaySettings(deviceName
, iModeNum
, &dm
);
348 const wxVideoMode mode
= ConvertToVideoMode(dm
);
349 if ( mode
.Matches(modeMatch
) )
358 bool wxDisplayMSW::ChangeMode(const wxVideoMode
& mode
)
360 // prepare ChangeDisplaySettingsEx() parameters
366 if ( mode
== wxDefaultVideoMode
)
368 // reset the video mode to default
372 else // change to the given mode
374 wxCHECK_MSG( mode
.GetWidth() && mode
.GetHeight(), false,
375 wxT("at least the width and height must be specified") );
378 dm
.dmSize
= sizeof(dm
);
379 dm
.dmDriverExtra
= 0;
380 dm
.dmFields
= DM_PELSWIDTH
| DM_PELSHEIGHT
;
381 dm
.dmPelsWidth
= mode
.GetWidth();
382 dm
.dmPelsHeight
= mode
.GetHeight();
384 if ( mode
.GetDepth() )
386 dm
.dmFields
|= DM_BITSPERPEL
;
387 dm
.dmBitsPerPel
= mode
.GetDepth();
390 if ( mode
.GetRefresh() )
392 dm
.dmFields
|= DM_DISPLAYFREQUENCY
;
393 dm
.dmDisplayFrequency
= mode
.GetRefresh();
400 #else // !__WXWINCE__
401 flags
= CDS_FULLSCREEN
;
402 #endif // __WXWINCE__/!__WXWINCE__
406 // get pointer to the function dynamically
408 // we're only called from the main thread, so it's ok to use static
410 static ChangeDisplaySettingsEx_t pfnChangeDisplaySettingsEx
= NULL
;
411 if ( !pfnChangeDisplaySettingsEx
)
413 wxDynamicLibrary
dllDisplay(displayDllName
, wxDL_VERBATIM
| wxDL_QUIET
);
414 if ( dllDisplay
.IsLoaded() )
416 wxDL_INIT_FUNC_AW(pfn
, ChangeDisplaySettingsEx
, dllDisplay
);
418 //else: huh, no this DLL must always be present, what's going on??
421 if ( !pfnChangeDisplaySettingsEx
)
423 // we must be under Win95 and so there is no multiple monitors
425 pfnChangeDisplaySettingsEx
= ChangeDisplaySettingsExForWin95
;
427 #endif // !__WXWINCE__
430 // do change the mode
431 switch ( pfnChangeDisplaySettingsEx
433 GetName().t_str(), // display name
434 pDevMode
, // dev mode or NULL to reset
437 NULL
// pointer to video parameters (not used)
440 case DISP_CHANGE_SUCCESSFUL
:
443 // If we have a top-level, full-screen frame, emulate
444 // the DirectX behaviour and resize it. This makes this
445 // API quite a bit easier to use.
446 wxWindow
*winTop
= wxTheApp
->GetTopWindow();
447 wxFrame
*frameTop
= wxDynamicCast(winTop
, wxFrame
);
448 if (frameTop
&& frameTop
->IsFullScreen())
450 wxVideoMode current
= GetCurrentMode();
451 frameTop
->SetClientSize(current
.GetWidth(), current
.GetHeight());
456 case DISP_CHANGE_BADMODE
:
457 // don't complain about this, this is the only "expected" error
461 wxFAIL_MSG( wxT("unexpected ChangeDisplaySettingsEx() return value") );
468 // ----------------------------------------------------------------------------
469 // wxDisplayFactoryMSW implementation
470 // ----------------------------------------------------------------------------
473 wxDisplayWndProc(HWND hwnd
, UINT msg
, WPARAM wParam
, LPARAM lParam
)
475 if ( msg
== WM_SETTINGCHANGE
)
477 wxDisplayFactoryMSW::RefreshMonitors();
482 return ::DefWindowProc(hwnd
, msg
, wParam
, lParam
);
485 wxDisplayFactoryMSW::wxDisplayFactoryMSW()
487 // This is not supposed to happen with the current code, the factory is
488 // implicitly a singleton.
489 wxASSERT_MSG( !ms_factory
, wxS("Using more than one factory?") );
494 m_hiddenClass
= NULL
;
496 if ( gs_MonitorFromPoint
==NULL
|| gs_MonitorFromWindow
==NULL
497 || gs_GetMonitorInfo
==NULL
|| gs_EnumDisplayMonitors
==NULL
)
499 // First initialization, or last initialization failed.
500 wxDynamicLibrary
dllDisplay(displayDllName
, wxDL_VERBATIM
| wxDL_QUIET
);
502 wxDL_INIT_FUNC(gs_
, MonitorFromPoint
, dllDisplay
);
503 wxDL_INIT_FUNC(gs_
, MonitorFromWindow
, dllDisplay
);
504 wxDL_INIT_FUNC_AW(gs_
, GetMonitorInfo
, dllDisplay
);
505 wxDL_INIT_FUNC(gs_
, EnumDisplayMonitors
, dllDisplay
);
507 // we can safely let dllDisplay go out of scope, the DLL itself will
508 // still remain loaded as all programs link to it statically anyhow
511 if ( gs_MonitorFromPoint
==NULL
|| gs_MonitorFromWindow
==NULL
512 || gs_GetMonitorInfo
==NULL
|| gs_EnumDisplayMonitors
==NULL
)
517 // Also create a hidden window to listen for WM_SETTINGCHANGE that we
518 // receive when a monitor is added to or removed from the system as we must
519 // refresh our monitor handles information then.
520 m_hiddenHwnd
= wxCreateHiddenWindow
523 wxT("wxDisplayHiddenWindow"),
528 wxDisplayFactoryMSW::~wxDisplayFactoryMSW()
532 if ( !::DestroyWindow(m_hiddenHwnd
) )
534 wxLogLastError(wxT("DestroyWindow(wxDisplayHiddenWindow)"));
539 if ( !::UnregisterClass(m_hiddenClass
, wxGetInstance()) )
541 wxLogLastError(wxT("UnregisterClass(wxDisplayHiddenWindow)"));
549 void wxDisplayFactoryMSW::DoRefreshMonitors()
553 if ( !gs_EnumDisplayMonitors(NULL
, NULL
, MultimonEnumProc
, (LPARAM
)this) )
555 wxLogLastError(wxT("EnumDisplayMonitors"));
561 wxDisplayFactoryMSW::MultimonEnumProc(
562 HMONITOR hMonitor
, // handle to display monitor
563 HDC
WXUNUSED(hdcMonitor
), // handle to monitor-appropriate device context
564 LPRECT
WXUNUSED(lprcMonitor
), // pointer to monitor intersection rectangle
565 LPARAM dwData
) // data passed from EnumDisplayMonitors (this)
567 wxDisplayFactoryMSW
*const self
= (wxDisplayFactoryMSW
*)dwData
;
569 self
->m_displays
.Add(hMonitor
);
571 // continue the enumeration
575 wxDisplayImpl
*wxDisplayFactoryMSW::CreateDisplay(unsigned n
)
577 wxCHECK_MSG( n
< m_displays
.size(), NULL
, wxT("An invalid index was passed to wxDisplay") );
579 return new wxDisplayMSW(n
, m_displays
[n
]);
582 // helper for GetFromPoint() and GetFromWindow()
583 int wxDisplayFactoryMSW::FindDisplayFromHMONITOR(HMONITOR hmon
) const
587 const size_t count
= m_displays
.size();
588 for ( size_t n
= 0; n
< count
; n
++ )
590 if ( hmon
== m_displays
[n
] )
598 int wxDisplayFactoryMSW::GetFromPoint(const wxPoint
& pt
)
604 return FindDisplayFromHMONITOR(gs_MonitorFromPoint(pt2
,
605 MONITOR_DEFAULTTONULL
));
608 int wxDisplayFactoryMSW::GetFromWindow(const wxWindow
*window
)
611 return FindDisplayFromHMONITOR(gs_MonitorFromWindow(GetHwndOf(window
),
612 MONITOR_DEFAULTTONULL
));
614 const wxSize halfsize
= window
->GetSize() / 2;
615 wxPoint pt
= window
->GetScreenPosition();
618 return GetFromPoint(pt
);
622 #endif // wxUSE_DISPLAY
624 void wxClientDisplayRect(int *x
, int *y
, int *width
, int *height
)
626 #if defined(__WXMICROWIN__)
628 wxDisplaySize(width
, height
);
630 // Determine the desktop dimensions minus the taskbar and any other
631 // special decorations...
634 SystemParametersInfo(SPI_GETWORKAREA
, 0, &r
, 0);
637 if (width
) *width
= r
.right
- r
.left
;
638 if (height
) *height
= r
.bottom
- r
.top
;