1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/msw/display.cpp
3 // Purpose: MSW Implementation of wxDisplay class
4 // Author: Royce Mitchell III
5 // Modified by: VZ (resolutions enumeration/change support, DirectDraw, ...)
6 // Ryan Norton (IsPrimary override)
9 // Copyright: (c) 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"
32 #include "wx/dynarray.h"
36 #include "wx/dynload.h"
38 #include "wx/display.h"
40 // the following define is necessary to access the multi-monitor function
41 // declarations in a manner safe to use w/ Windows 95
42 #define COMPILE_MULTIMON_STUBS
44 // if you don't have multimon.h you can download the file from:
46 // http://www.microsoft.com/msj/0697/monitor/monitortextfigs.htm#fig4
50 // as (m)any standard header(s), this one doesn't compile without warnings
52 #pragma warning(disable:4706)
55 // with mingw32, we must include windows.h first and it doesn't hurt with other
57 #include "wx/msw/wrapwin.h"
62 // HMONITOR can be declared either in new enough windef.h or in multimon.h
63 // itself if _WIN32_WINNT < 0x0500, but the trouble is that we set
64 // _WIN32_WINNT to maximal possible value ourselves in wx/msw/wrapwin.h so
65 // that multimon.h doesn't define it but with old headers, neither does
66 // windef.h, in spite of _WIN32_WINNT value. Even more unfortunately, we
67 // can't directly test whether HMONITOR is defined or not in windef.h as
68 // it's not a macro but a typedef, so we test for an unrelated symbol which
69 // is only defined in winuser.h if WINVER >= 0x0500
70 #if !defined(HMONITOR_DECLARED) && !defined(MNS_NOCHECK)
71 DECLARE_HANDLE(HMONITOR
);
72 #define HMONITOR_DECLARED
74 #endif // !__WXWINCE__
77 #pragma warning(default:4706)
82 // we don't want to link with ddraw.lib which contains the real
83 // IID_IDirectDraw2 definition
84 const GUID wxIID_IDirectDraw2
=
85 { 0xB3A6F3E0, 0x2B43, 0x11CF, { 0xA2,0xDE,0x00,0xAA,0x00,0xB9,0x33,0x56 } };
87 // ----------------------------------------------------------------------------
89 // ----------------------------------------------------------------------------
92 #define WINFUNC(x) _T(#x) L"W"
94 #define WINFUNC(x) #x "A"
97 // ----------------------------------------------------------------------------
98 // typedefs for dynamically loaded Windows functions
99 // ----------------------------------------------------------------------------
101 typedef LONG (WINAPI
*ChangeDisplaySettingsEx_t
)(LPCTSTR lpszDeviceName
,
107 typedef BOOL (PASCAL
*DDEnumExCallback_t
)(GUID
*pGuid
,
108 LPTSTR driverDescription
,
113 typedef HRESULT (WINAPI
*DirectDrawEnumerateEx_t
)(DDEnumExCallback_t lpCallback
,
117 typedef HRESULT (WINAPI
*DirectDrawCreate_t
)(GUID
*lpGUID
,
118 LPDIRECTDRAW
*lplpDD
,
119 IUnknown
*pUnkOuter
);
121 // ----------------------------------------------------------------------------
123 // ----------------------------------------------------------------------------
128 // handle of this monitor used by MonitorXXX() functions, never NULL
131 // IDirectDraw object used to control this display, may be NULL
132 IDirectDraw2
*m_pDD2
;
134 // DirectDraw GUID for this display, only valid when using DirectDraw
137 // the entire area of this monitor in virtual screen coordinates
140 // the display device name for this monitor, empty initially and retrieved
141 // on demand by DoGetName()
144 wxDisplayInfo() { m_hmon
= NULL
; m_pDD2
= NULL
; }
145 ~wxDisplayInfo() { if ( m_pDD2
) m_pDD2
->Release(); }
148 WX_DECLARE_OBJARRAY(wxDisplayInfo
, wxDisplayInfoArray
);
149 #include "wx/arrimpl.cpp"
150 WX_DEFINE_OBJARRAY(wxDisplayInfoArray
);
152 // this module is used to cleanup gs_displays array
153 class wxDisplayModule
: public wxModule
156 virtual bool OnInit() { return true; }
157 virtual void OnExit();
159 DECLARE_DYNAMIC_CLASS(wxDisplayModule
)
162 IMPLEMENT_DYNAMIC_CLASS(wxDisplayModule
, wxModule
)
164 // ----------------------------------------------------------------------------
166 // ----------------------------------------------------------------------------
168 // do we use DirectX?
169 static bool gs_useDirectX
= false;
171 // dynamically resolved DirectDrawCreate()
172 static DirectDrawCreate_t gs_DirectDrawCreate
= NULL
;
174 // this is not really MT-unsafe as wxDisplay is only going to be used from the
175 // main thread, i.e. we consider that it's a GUI class and so don't protect it
176 static wxDisplayInfoArray
*gs_displays
= NULL
;
178 // ===========================================================================
180 // ===========================================================================
182 // ----------------------------------------------------------------------------
183 // callbacks for monitor/modes enumeration stuff
184 // ----------------------------------------------------------------------------
186 static BOOL CALLBACK
wxmswMonitorEnumProc (
187 HMONITOR hMonitor
, // handle to display monitor
188 HDC
WXUNUSED(hdcMonitor
), // handle to monitor-appropriate device context
189 LPRECT lprcMonitor
, // pointer to monitor intersection rectangle
190 LPARAM
WXUNUSED(dwData
) // data passed from EnumDisplayMonitors (unused)
193 wxDisplayInfo
*info
= new wxDisplayInfo();
195 // we need hMonitor to be able to map display id to it which is needed for
196 // MonitorXXX() functions, in particular MonitorFromPoint()
197 info
->m_hmon
= hMonitor
;
199 // we also store the display geometry
200 info
->m_rect
.SetX ( lprcMonitor
->left
);
201 info
->m_rect
.SetY ( lprcMonitor
->top
);
202 info
->m_rect
.SetWidth ( lprcMonitor
->right
- lprcMonitor
->left
);
203 info
->m_rect
.SetHeight ( lprcMonitor
->bottom
- lprcMonitor
->top
);
205 // now add this monitor to the array
206 gs_displays
->Add(info
);
208 // continue the enumeration
213 wxDDEnumExCallback(GUID
*pGuid
,
214 LPTSTR
WXUNUSED(driverDescription
),
216 LPVOID
WXUNUSED(lpContext
),
221 wxDisplayInfo
*info
= new wxDisplayInfo();
224 info
->m_guid
= *pGuid
;
225 info
->m_devName
= driverName
;
227 gs_displays
->Add(info
);
229 //else: we're called for the primary monitor, skip it
231 // continue the enumeration
235 HRESULT WINAPI
wxDDEnumModesCallback(LPDDSURFACEDESC lpDDSurfaceDesc
,
238 // we need at least the mode size
239 static const DWORD FLAGS_REQUIRED
= DDSD_HEIGHT
| DDSD_WIDTH
;
240 if ( (lpDDSurfaceDesc
->dwFlags
& FLAGS_REQUIRED
) == FLAGS_REQUIRED
)
242 wxArrayVideoModes
* const modes
= (wxArrayVideoModes
*)lpContext
;
244 modes
->Add(wxVideoMode(lpDDSurfaceDesc
->dwWidth
,
245 lpDDSurfaceDesc
->dwHeight
,
246 lpDDSurfaceDesc
->ddpfPixelFormat
.dwRGBBitCount
,
247 lpDDSurfaceDesc
->dwRefreshRate
));
250 // continue the enumeration
254 // ----------------------------------------------------------------------------
256 // ----------------------------------------------------------------------------
258 // initialize gs_displays using DirectX functions
259 static bool DoInitDirectX()
262 // suppress the errors if ddraw.dll is not found
263 wxLog::EnableLogging(false);
266 wxDynamicLibrary
dllDX(_T("ddraw.dll"));
269 wxLog::EnableLogging();
272 if ( !dllDX
.IsLoaded() )
275 DirectDrawEnumerateEx_t pDDEnumEx
= (DirectDrawEnumerateEx_t
)
276 dllDX
.GetSymbol(WINFUNC(DirectDrawEnumerateEx
));
280 // we'll also need DirectDrawCreate() later, resolve it right now
281 gs_DirectDrawCreate
= (DirectDrawCreate_t
)
282 dllDX
.GetSymbol(_T("DirectDrawCreate"));
283 if ( !gs_DirectDrawCreate
)
286 if ( (*pDDEnumEx
)(wxDDEnumExCallback
,
288 DDENUM_ATTACHEDSECONDARYDEVICES
) != DD_OK
)
293 // ok, it seems like we're going to use DirectDraw and so we're going to
294 // need ddraw.dll all the time, don't unload it
300 // initialize gs_displays using the standard Windows functions
301 static void DoInitStdWindows()
303 // enumerate all displays
304 if ( !::EnumDisplayMonitors(NULL
, NULL
, wxmswMonitorEnumProc
, 0) )
306 wxLogLastError(wxT("EnumDisplayMonitors"));
308 // TODO: still create at least one (valid) entry in gs_displays for the
313 // this function must be called before accessing gs_displays array as it
314 // creates and initializes it
315 static void InitDisplays()
320 gs_displays
= new wxDisplayInfoArray();
322 if ( !gs_useDirectX
|| !DoInitDirectX() )
324 // either we were told not to try to use DirectX or fall back to std
325 // functions if DirectX method failed
326 gs_useDirectX
= false;
332 // convert a DEVMODE to our wxVideoMode
333 wxVideoMode
ConvertToVideoMode(const DEVMODE
& dm
)
335 // note that dmDisplayFrequency may be 0 or 1 meaning "standard one" and
336 // although 0 is ok for us we don't want to return modes with 1hz refresh
337 return wxVideoMode(dm
.dmPelsWidth
,
340 dm
.dmDisplayFrequency
> 1 ? dm
.dmDisplayFrequency
: 0);
344 // emulation of ChangeDisplaySettingsEx() for Win95
345 LONG WINAPI
ChangeDisplaySettingsExForWin95(LPCTSTR
WXUNUSED(lpszDeviceName
),
349 LPVOID
WXUNUSED(lParam
))
351 return ::ChangeDisplaySettings(lpDevMode
, dwFlags
);
353 #endif // !__WXWINCE__
355 // ----------------------------------------------------------------------------
357 // ----------------------------------------------------------------------------
359 void wxDisplayModule::OnExit()
364 // ---------------------------------------------------------------------------
366 // ---------------------------------------------------------------------------
369 void wxDisplay::UseDirectX(bool useDX
)
371 wxCHECK_RET( !gs_displays
, _T("it is too late to call UseDirectX") );
373 gs_useDirectX
= useDX
;
376 // helper of GetFromPoint() and GetFromWindow()
377 static int DisplayFromHMONITOR(HMONITOR hmon
)
381 const size_t count
= wxDisplay::GetCount();
383 for ( size_t n
= 0; n
< count
; n
++ )
385 if ( hmon
== (*gs_displays
)[n
].m_hmon
)
394 size_t wxDisplayBase::GetCount()
398 //RN: FIXME: This is wrong - the display info array should reload after every call
399 //to GetCount() - otherwise it will not be accurate.
400 //The user can change the number of displays in display properties/settings
401 //after GetCount or similar is called and really mess this up...
402 //wxASSERT_MSG( gs_displays->GetCount() == (size_t)::GetSystemMetrics(SM_CMONITORS),
403 // _T("So how many displays does this system have?") );
405 return gs_displays
->GetCount();
409 int wxDisplayBase::GetFromPoint ( const wxPoint
& pt
)
415 return DisplayFromHMONITOR(::MonitorFromPoint(pt2
, MONITOR_DEFAULTTONULL
));
419 int wxDisplayBase::GetFromWindow(wxWindow
*window
)
421 return DisplayFromHMONITOR
423 ::MonitorFromWindow(GetHwndOf(window
), MONITOR_DEFAULTTONULL
)
427 // ----------------------------------------------------------------------------
428 // wxDisplay ctor/dtor
429 // ----------------------------------------------------------------------------
431 wxDisplay::wxDisplay ( size_t n
)
432 : wxDisplayBase ( n
)
434 // if we do this in ctor we won't have to call it from all the member
440 wxDisplayInfo
& dpyInfo
= (*gs_displays
)[n
];
442 LPDIRECTDRAW2
& pDD2
= dpyInfo
.m_pDD2
;
445 if ( !gs_DirectDrawCreate
)
452 HRESULT hr
= (*gs_DirectDrawCreate
)(&dpyInfo
.m_guid
, &pDD
, NULL
);
454 if ( FAILED(hr
) || !pDD
)
457 wxLogApiError(_T("DirectDrawCreate"), hr
);
459 else // got IDirectDraw, we want IDirectDraw2
461 hr
= pDD
->QueryInterface(wxIID_IDirectDraw2
, (void **)&pDD2
);
462 if ( FAILED(hr
) || !pDD2
)
464 wxLogApiError(_T("IDirectDraw::QueryInterface(IDD2)"), hr
);
470 //else: DirectDraw object corresponding to our display already exists
472 // increment its ref count to account for Release() in dtor
474 // NB: pDD2 will be only really Release()d when gs_displays is
475 // destroyed which is ok as we don't want to recreate DD objects
481 wxDisplay::~wxDisplay()
483 wxDisplayInfo
& dpyInfo
= (*gs_displays
)[m_index
];
485 LPDIRECTDRAW2
& pDD2
= dpyInfo
.m_pDD2
;
492 // ----------------------------------------------------------------------------
493 // wxDisplay simple accessors
494 // ----------------------------------------------------------------------------
496 bool wxDisplay::IsOk() const
498 return m_index
< GetCount() &&
499 (!gs_useDirectX
|| (*gs_displays
)[m_index
].m_pDD2
);
502 wxRect
wxDisplay::GetGeometry() const
504 wxDisplayInfo
& dpyInfo
= (*gs_displays
)[m_index
];
505 wxRect
& rect
= dpyInfo
.m_rect
;
509 wxZeroMemory(monInfo
);
510 monInfo
.cbSize
= sizeof(monInfo
);
512 if ( !::GetMonitorInfo(dpyInfo
.m_hmon
, &monInfo
) )
514 wxLogLastError(_T("GetMonitorInfo"));
518 wxCopyRECTToRect(monInfo
.rcMonitor
, rect
);
525 wxString
wxDisplay::GetName() const
527 wxDisplayInfo
& dpyInfo
= (*gs_displays
)[m_index
];
528 if ( dpyInfo
.m_devName
.empty() )
530 MONITORINFOEX monInfo
;
531 wxZeroMemory(monInfo
);
532 monInfo
.cbSize
= sizeof(monInfo
);
534 // NB: Cast from MONITORINFOEX* to MONITORINFO* is done because
535 // Mingw headers - unlike the ones from Microsoft's Platform SDK -
536 // don't derive the former from the latter in C++ mode and so
537 // the pointer's type is not converted implicitly.
538 if ( !::GetMonitorInfo(dpyInfo
.m_hmon
, (LPMONITORINFO
)&monInfo
) )
540 wxLogLastError(_T("GetMonitorInfo"));
544 dpyInfo
.m_devName
= monInfo
.szDevice
;
548 return dpyInfo
.m_devName
;
551 wxString
wxDisplay::GetNameForEnumSettings() const
554 const bool isWin95
= wxGetOsVersion(&major
, &minor
) == wxWIN95
&&
555 major
== 4 && minor
== 0;
557 // the first parameter of EnumDisplaySettings() must be NULL under Win95
558 // according to MSDN but GetMonitorInfo() stub in multimon.h still returns
559 // something even in this case, so we have to correct this manually
567 // ----------------------------------------------------------------------------
568 // determine if this is the primary display
569 // ----------------------------------------------------------------------------
571 bool wxDisplay::IsPrimary() const
573 wxDisplayInfo
& dpyInfo
= (*gs_displays
)[m_index
];
575 MONITORINFOEX monInfo
;
576 wxZeroMemory(monInfo
);
577 monInfo
.cbSize
= sizeof(monInfo
);
579 // NB: Cast from MONITORINFOEX* to MONITORINFO* is done because
580 // Mingw headers - unlike the ones from Microsoft's Platform SDK -
581 // don't derive the former from the latter in C++ mode and so
582 // the pointer's type is not converted implicitly.
583 if ( !::GetMonitorInfo(dpyInfo
.m_hmon
, (LPMONITORINFO
)&monInfo
) )
585 wxLogLastError(_T("GetMonitorInfo"));
588 return (monInfo
.dwFlags
& MONITORINFOF_PRIMARY
) == MONITORINFOF_PRIMARY
;
591 // ----------------------------------------------------------------------------
592 // video modes enumeration
593 // ----------------------------------------------------------------------------
596 wxDisplay::DoGetModesDirectX(const wxVideoMode
& WXUNUSED(modeMatch
)) const
598 wxArrayVideoModes modes
;
600 IDirectDraw2
*pDD
= (*gs_displays
)[m_index
].m_pDD2
;
604 HRESULT hr
= pDD
->EnumDisplayModes
607 NULL
, // all modes (TODO: use modeMatch!)
608 &modes
, // callback parameter
609 wxDDEnumModesCallback
614 wxLogApiError(_T("IDirectDraw::EnumDisplayModes"), hr
);
622 wxDisplay::DoGetModesWindows(const wxVideoMode
& modeMatch
) const
624 wxArrayVideoModes modes
;
626 const wxString name
= GetNameForEnumSettings();
628 const wxChar
* const deviceName
= name
.empty() ? NULL
: name
.c_str();
631 for ( int iModeNum
= 0;
632 ::EnumDisplaySettings(deviceName
, iModeNum
, &dm
);
635 const wxVideoMode mode
= ConvertToVideoMode(dm
);
636 if ( mode
.Matches(modeMatch
) )
645 wxArrayVideoModes
wxDisplay::GetModes(const wxVideoMode
& modeMatch
) const
647 return gs_useDirectX
? DoGetModesDirectX(modeMatch
)
648 : DoGetModesWindows(modeMatch
);
651 wxVideoMode
wxDisplay::GetCurrentMode() const
655 const wxString name
= GetNameForEnumSettings();
658 if ( !::EnumDisplaySettings(name
.empty() ? NULL
: name
.c_str(),
659 ENUM_CURRENT_SETTINGS
,
662 wxLogLastError(_T("EnumDisplaySettings(ENUM_CURRENT_SETTINGS)"));
666 mode
= ConvertToVideoMode(dm
);
672 // ----------------------------------------------------------------------------
673 // video mode switching
674 // ----------------------------------------------------------------------------
676 bool wxDisplay::DoChangeModeDirectX(const wxVideoMode
& mode
)
678 IDirectDraw2
*pDD
= (*gs_displays
)[m_index
].m_pDD2
;
682 wxWindow
*winTop
= wxTheApp
->GetTopWindow();
683 wxCHECK_MSG( winTop
, false, _T("top level window required for DirectX") );
685 HRESULT hr
= pDD
->SetCooperativeLevel
688 DDSCL_EXCLUSIVE
| DDSCL_FULLSCREEN
692 wxLogApiError(_T("IDirectDraw::SetCooperativeLevel"), hr
);
697 hr
= pDD
->SetDisplayMode(mode
.w
, mode
.h
, mode
.bpp
, mode
.refresh
, 0);
700 wxLogApiError(_T("IDirectDraw::SetDisplayMode"), hr
);
709 bool wxDisplay::DoChangeModeWindows(const wxVideoMode
& mode
)
711 // prepare ChangeDisplaySettingsEx() parameters
716 if ( mode
== wxDefaultVideoMode
)
718 // reset the video mode to default
722 else // change to the given mode
724 wxCHECK_MSG( mode
.w
&& mode
.h
, false,
725 _T("at least the width and height must be specified") );
728 dm
.dmSize
= sizeof(dm
);
729 dm
.dmFields
= DM_PELSWIDTH
| DM_PELSHEIGHT
;
730 dm
.dmPelsWidth
= mode
.w
;
731 dm
.dmPelsHeight
= mode
.h
;
735 dm
.dmFields
|= DM_BITSPERPEL
;
736 dm
.dmBitsPerPel
= mode
.bpp
;
741 dm
.dmFields
|= DM_DISPLAYFREQUENCY
;
742 dm
.dmDisplayFrequency
= mode
.refresh
;
749 #else // !__WXWINCE__
750 flags
= CDS_FULLSCREEN
;
751 #endif // __WXWINCE__/!__WXWINCE__
755 // get pointer to the function dynamically
757 // we're only called from the main thread, so it's ok to use static
759 static ChangeDisplaySettingsEx_t pfnChangeDisplaySettingsEx
= NULL
;
760 if ( !pfnChangeDisplaySettingsEx
)
762 wxDynamicLibrary
dllUser32(_T("user32.dll"));
763 if ( dllUser32
.IsLoaded() )
765 pfnChangeDisplaySettingsEx
= (ChangeDisplaySettingsEx_t
)
766 dllUser32
.GetSymbol(WINFUNC(ChangeDisplaySettingsEx
));
768 //else: huh, no user32.dll??
771 if ( !pfnChangeDisplaySettingsEx
)
773 // we must be under Win95 and so there is no multiple monitors
775 pfnChangeDisplaySettingsEx
= ChangeDisplaySettingsExForWin95
;
777 #endif // !__WXWINCE__
780 // do change the mode
781 switch ( pfnChangeDisplaySettingsEx
783 GetName(), // display name
784 pDevMode
, // dev mode or NULL to reset
787 NULL
// pointer to video parameters (not used)
790 case DISP_CHANGE_SUCCESSFUL
:
793 // If we have a top-level, full-screen frame, emulate
794 // the DirectX behavior and resize it. This makes this
795 // API quite a bit easier to use.
796 wxWindow
*winTop
= wxTheApp
->GetTopWindow();
797 wxFrame
*frameTop
= wxDynamicCast(winTop
, wxFrame
);
798 if (frameTop
&& frameTop
->IsFullScreen())
800 wxVideoMode current
= GetCurrentMode();
801 frameTop
->SetClientSize(current
.w
, current
.h
);
806 case DISP_CHANGE_BADMODE
:
807 // don't complain about this, this is the only "expected" error
811 wxFAIL_MSG( _T("unexpected ChangeDisplaySettingsEx() return value") );
817 bool wxDisplay::ChangeMode(const wxVideoMode
& mode
)
819 return gs_useDirectX
? DoChangeModeDirectX(mode
)
820 : DoChangeModeWindows(mode
);
823 #endif // wxUSE_DISPLAY