1 /////////////////////////////////////////////////////////////////////////////
3 // Purpose: MSW Implementation of wxDisplay class
4 // Author: Royce Mitchell III
5 // Modified by: VZ (resolutions enumeration/change support, DirectDraw, ...)
8 // Copyright: (c) wxWindows team
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
12 // ===========================================================================
14 // ===========================================================================
16 // ---------------------------------------------------------------------------
18 // ---------------------------------------------------------------------------
21 #pragma implementation "display.h"
24 // For compilers that support precompilation, includes "wx.h".
25 #include "wx/wxprec.h"
34 #include "wx/dynarray.h"
37 #include "wx/dynload.h"
39 #include "wx/display.h"
41 // the following define is necessary to access the multi-monitor function
42 // declarations in a manner safe to use w/ Windows 95
43 #define COMPILE_MULTIMON_STUBS
45 // if you don't have multimon.h you can download the file from:
47 // http://www.microsoft.com/msj/0697/monitor/monitortextfigs.htm#fig4
51 // as (m)any standard header(s), this one doesn't compile without warnings
53 #pragma warning(disable:4706)
59 #pragma warning(default:4706)
64 // we don't want to link with ddraw.lib which contains the real
65 // IID_IDirectDraw2 definition
66 const GUID wxIID_IDirectDraw2
=
67 { 0xB3A6F3E0, 0x2B43, 0x11CF, { 0xA2,0xDE,0x00,0xAA,0x00,0xB9,0x33,0x56 } };
69 // ----------------------------------------------------------------------------
71 // ----------------------------------------------------------------------------
74 #define WINFUNC(x) _T(#x) L"W"
76 #define WINFUNC(x) #x "A"
79 // ----------------------------------------------------------------------------
80 // typedefs for dynamically loaded Windows functions
81 // ----------------------------------------------------------------------------
83 typedef LONG (WINAPI
*ChangeDisplaySettingsEx_t
)(LPCTSTR lpszDeviceName
,
89 typedef BOOL (PASCAL
*DDEnumExCallback_t
)(GUID
*pGuid
,
90 LPTSTR driverDescription
,
95 typedef HRESULT (WINAPI
*DirectDrawEnumerateEx_t
)(DDEnumExCallback_t lpCallback
,
99 typedef HRESULT (WINAPI
*DirectDrawCreate_t
)(GUID
*lpGUID
,
100 LPDIRECTDRAW
*lplpDD
,
101 IUnknown
*pUnkOuter
);
103 // ----------------------------------------------------------------------------
105 // ----------------------------------------------------------------------------
110 // handle of this monitor used by MonitorXXX() functions, never NULL
113 // IDirectDraw object used to control this display, may be NULL
114 IDirectDraw2
*m_pDD2
;
116 // DirectDraw GUID for this display, only valid when using DirectDraw
119 // the entire area of this monitor in virtual screen coordinates
122 // the display device name for this monitor, empty initially and retrieved
123 // on demand by DoGetName()
126 wxDisplayInfo() { m_hmon
= NULL
; m_pDD2
= NULL
; }
127 ~wxDisplayInfo() { if ( m_pDD2
) m_pDD2
->Release(); }
130 WX_DECLARE_OBJARRAY(wxDisplayInfo
, wxDisplayInfoArray
);
131 #include "wx/arrimpl.cpp"
132 WX_DEFINE_OBJARRAY(wxDisplayInfoArray
);
134 // this module is used to cleanup gs_displays array
135 class wxDisplayModule
: public wxModule
138 virtual bool OnInit() { return true; }
139 virtual void OnExit();
141 DECLARE_DYNAMIC_CLASS(wxDisplayModule
)
144 IMPLEMENT_DYNAMIC_CLASS(wxDisplayModule
, wxModule
)
146 // ----------------------------------------------------------------------------
148 // ----------------------------------------------------------------------------
150 // do we use DirectX?
151 static bool gs_useDirectX
= false;
153 // dynamically resolved DirectDrawCreate()
154 static DirectDrawCreate_t gs_DirectDrawCreate
= NULL
;
156 // this is not really MT-unsafe as wxDisplay is only going to be used from the
157 // main thread, i.e. we consider that it's a GUI class and so don't protect it
158 static wxDisplayInfoArray
*gs_displays
= NULL
;
160 // ===========================================================================
162 // ===========================================================================
164 // ----------------------------------------------------------------------------
165 // callbacks for monitor/modes enumeration stuff
166 // ----------------------------------------------------------------------------
168 static BOOL CALLBACK
wxmswMonitorEnumProc (
169 HMONITOR hMonitor
, // handle to display monitor
170 HDC hdcMonitor
, // handle to monitor-appropriate device context (NULL)
171 LPRECT lprcMonitor
, // pointer to monitor intersection rectangle
172 LPARAM dwData
// data passed from EnumDisplayMonitors (unused)
175 wxDisplayInfo
*info
= new wxDisplayInfo();
177 // we need hMonitor to be able to map display id to it which is needed for
178 // MonitorXXX() functions, in particular MonitorFromPoint()
179 info
->m_hmon
= hMonitor
;
181 // we also store the display geometry
182 info
->m_rect
.SetX ( lprcMonitor
->left
);
183 info
->m_rect
.SetY ( lprcMonitor
->top
);
184 info
->m_rect
.SetWidth ( lprcMonitor
->right
- lprcMonitor
->left
);
185 info
->m_rect
.SetHeight ( lprcMonitor
->bottom
- lprcMonitor
->top
);
187 // now add this monitor to the array
188 gs_displays
->Add(info
);
190 // continue the enumeration
195 wxDDEnumExCallback(GUID
*pGuid
,
196 LPTSTR driverDescription
,
203 wxDisplayInfo
*info
= new wxDisplayInfo();
206 info
->m_guid
= *pGuid
;
207 info
->m_devName
= driverName
;
209 gs_displays
->Add(info
);
211 //else: we're called for the primary monitor, skip it
213 // continue the enumeration
217 HRESULT WINAPI
wxDDEnumModesCallback(LPDDSURFACEDESC lpDDSurfaceDesc
,
220 // we need at least the mode size
221 static const int FLAGS_REQUIRED
= DDSD_HEIGHT
| DDSD_WIDTH
;
222 if ( (lpDDSurfaceDesc
->dwFlags
& FLAGS_REQUIRED
) == FLAGS_REQUIRED
)
224 wxArrayVideoModes
* const modes
= (wxArrayVideoModes
*)lpContext
;
226 modes
->Add(wxVideoMode(lpDDSurfaceDesc
->dwWidth
,
227 lpDDSurfaceDesc
->dwHeight
,
228 lpDDSurfaceDesc
->ddpfPixelFormat
.dwRGBBitCount
,
229 lpDDSurfaceDesc
->dwRefreshRate
));
232 // continue the enumeration
236 // ----------------------------------------------------------------------------
238 // ----------------------------------------------------------------------------
240 // initialize gs_displays using DirectX functions
241 static bool DoInitDirectX()
243 // suppress the errors if ddraw.dll is not found
244 wxLog::EnableLogging(false);
246 wxDynamicLibrary
dllDX(_T("ddraw.dll"));
248 wxLog::EnableLogging(true);
250 if ( !dllDX
.IsLoaded() )
253 DirectDrawEnumerateEx_t pDDEnumEx
= (DirectDrawEnumerateEx_t
)
254 dllDX
.GetSymbol(WINFUNC(DirectDrawEnumerateEx
));
258 // we'll also need DirectDrawCreate() later, resolve it right now
259 gs_DirectDrawCreate
= (DirectDrawCreate_t
)
260 dllDX
.GetSymbol(_T("DirectDrawCreate"));
261 if ( !gs_DirectDrawCreate
)
264 if ( (*pDDEnumEx
)(wxDDEnumExCallback
,
266 DDENUM_ATTACHEDSECONDARYDEVICES
) != DD_OK
)
271 // ok, it seems like we're going to use DirectDraw and so we're going to
272 // need ddraw.dll all the time, don't unload it
278 // initialize gs_displays using the standard Windows functions
279 static void DoInitStdWindows()
281 // enumerate all displays
282 if ( !::EnumDisplayMonitors(NULL
, NULL
, wxmswMonitorEnumProc
, 0) )
284 wxLogLastError(wxT("EnumDisplayMonitors"));
286 // TODO: still create at least one (valid) entry in gs_displays for the
291 // this function must be called before accessing gs_displays array as it
292 // creates and initializes it
293 static void InitDisplays()
298 gs_displays
= new wxDisplayInfoArray();
300 if ( !gs_useDirectX
|| !DoInitDirectX() )
302 // either we were told not to try to use DirectX or fall back to std
303 // functions if DirectX method failed
304 gs_useDirectX
= false;
310 // convert a DEVMODE to our wxVideoMode
311 wxVideoMode
ConvertToVideoMode(const DEVMODE
& dm
)
313 // note that dmDisplayFrequency may be 0 or 1 meaning "standard one" and
314 // although 0 is ok for us we don't want to return modes with 1hz refresh
315 return wxVideoMode(dm
.dmPelsWidth
,
318 dm
.dmDisplayFrequency
> 1 ? dm
.dmDisplayFrequency
: 0);
321 // emulation of ChangeDisplaySettingsEx() for Win95
322 LONG WINAPI
ChangeDisplaySettingsExForWin95(LPCTSTR
WXUNUSED(lpszDeviceName
),
326 LPVOID
WXUNUSED(lParam
))
328 return ::ChangeDisplaySettings(lpDevMode
, dwFlags
);
331 // ----------------------------------------------------------------------------
333 // ----------------------------------------------------------------------------
335 void wxDisplayModule::OnExit()
340 // ---------------------------------------------------------------------------
342 // ---------------------------------------------------------------------------
345 void wxDisplay::UseDirectX(bool useDX
)
347 wxCHECK_RET( !gs_displays
, _T("it is too late to call UseDirectX") );
349 gs_useDirectX
= useDX
;
352 // helper of GetFromPoint() and GetFromWindow()
353 static int DisplayFromHMONITOR(HMONITOR hmon
)
357 const size_t count
= wxDisplay::GetCount();
359 for ( size_t n
= 0; n
< count
; n
++ )
361 if ( hmon
== (*gs_displays
)[n
].m_hmon
)
370 size_t wxDisplayBase::GetCount()
374 // I'm not sure if they really always return the same thing and if this is
375 // not true I'd like to know in which situation does it happen
376 wxASSERT_MSG( gs_displays
->GetCount() == (size_t)::GetSystemMetrics(SM_CMONITORS
),
377 _T("So how many displays does this system have?") );
379 return gs_displays
->GetCount();
383 int wxDisplayBase::GetFromPoint ( const wxPoint
& pt
)
389 return DisplayFromHMONITOR(::MonitorFromPoint(pt2
, MONITOR_DEFAULTTONULL
));
393 int wxDisplayBase::GetFromWindow(wxWindow
*window
)
395 return DisplayFromHMONITOR
397 ::MonitorFromWindow(GetHwndOf(window
), MONITOR_DEFAULTTONULL
)
401 // ----------------------------------------------------------------------------
402 // wxDisplay ctor/dtor
403 // ----------------------------------------------------------------------------
405 wxDisplay::wxDisplay ( size_t n
)
406 : wxDisplayBase ( n
)
408 // if we do this in ctor we won't have to call it from all the member
414 wxDisplayInfo
& dpyInfo
= (*gs_displays
)[n
];
416 LPDIRECTDRAW2
& pDD2
= dpyInfo
.m_pDD2
;
419 if ( !gs_DirectDrawCreate
)
426 HRESULT hr
= (*gs_DirectDrawCreate
)(&dpyInfo
.m_guid
, &pDD
, NULL
);
428 if ( FAILED(hr
) || !pDD
)
431 wxLogApiError(_T("DirectDrawCreate"), hr
);
433 else // got IDirectDraw, we want IDirectDraw2
435 hr
= pDD
->QueryInterface(wxIID_IDirectDraw2
, (void **)&pDD2
);
436 if ( FAILED(hr
) || !pDD2
)
438 wxLogApiError(_T("IDirectDraw::QueryInterface(IDD2)"), hr
);
444 //else: DirectDraw object corresponding to our display already exists
446 // increment its ref count to account for Release() in dtor
448 // NB: pDD2 will be only really Release()d when gs_displays is
449 // destroyed which is ok as we don't want to recreate DD objects
455 wxDisplay::~wxDisplay()
457 wxDisplayInfo
& dpyInfo
= (*gs_displays
)[m_index
];
459 LPDIRECTDRAW2
& pDD2
= dpyInfo
.m_pDD2
;
466 // ----------------------------------------------------------------------------
467 // wxDisplay simple accessors
468 // ----------------------------------------------------------------------------
470 bool wxDisplay::IsOk() const
472 return m_index
< GetCount() &&
473 (!gs_useDirectX
|| (*gs_displays
)[m_index
].m_pDD2
);
476 wxRect
wxDisplay::GetGeometry() const
478 wxDisplayInfo
& dpyInfo
= (*gs_displays
)[m_index
];
479 wxRect
& rect
= dpyInfo
.m_rect
;
483 wxZeroMemory(monInfo
);
484 monInfo
.cbSize
= sizeof(monInfo
);
486 if ( !::GetMonitorInfo(dpyInfo
.m_hmon
, &monInfo
) )
488 wxLogLastError(_T("GetMonitorInfo"));
492 wxCopyRECTToRect(monInfo
.rcMonitor
, rect
);
499 wxString
wxDisplay::GetName() const
501 wxDisplayInfo
& dpyInfo
= (*gs_displays
)[m_index
];
502 if ( dpyInfo
.m_devName
.empty() )
504 MONITORINFOEX monInfo
;
505 wxZeroMemory(monInfo
);
506 monInfo
.cbSize
= sizeof(monInfo
);
508 if ( !::GetMonitorInfo(dpyInfo
.m_hmon
, &monInfo
) )
510 wxLogLastError(_T("GetMonitorInfo"));
514 dpyInfo
.m_devName
= monInfo
.szDevice
;
518 return dpyInfo
.m_devName
;
521 wxString
wxDisplay::GetNameForEnumSettings() const
524 const bool isWin95
= wxGetOsVersion(&major
, &minor
) == wxWIN95
&&
525 major
== 4 && minor
== 0;
527 // the first parameter of EnumDisplaySettings() must be NULL under Win95
528 // according to MSDN but GetMonitorInfo() stub in multimon.h still returns
529 // something even in this case, so we have to correct this manually
537 // ----------------------------------------------------------------------------
538 // video modes enumeration
539 // ----------------------------------------------------------------------------
542 wxDisplay::DoGetModesDirectX(const wxVideoMode
& modeMatch
) const
544 wxArrayVideoModes modes
;
546 IDirectDraw2
*pDD
= (*gs_displays
)[m_index
].m_pDD2
;
550 HRESULT hr
= pDD
->EnumDisplayModes
553 NULL
, // all modes (TODO: use modeMatch!)
554 &modes
, // callback parameter
555 wxDDEnumModesCallback
560 wxLogApiError(_T("IDirectDraw::EnumDisplayModes"), hr
);
568 wxDisplay::DoGetModesWindows(const wxVideoMode
& modeMatch
) const
570 wxArrayVideoModes modes
;
572 const wxString name
= GetNameForEnumSettings();
574 const wxChar
* const deviceName
= name
.empty() ? NULL
: name
.c_str();
577 for ( int iModeNum
= 0;
578 ::EnumDisplaySettings(deviceName
, iModeNum
, &dm
);
581 const wxVideoMode mode
= ConvertToVideoMode(dm
);
582 if ( mode
.Matches(modeMatch
) )
591 wxArrayVideoModes
wxDisplay::GetModes(const wxVideoMode
& modeMatch
) const
593 return gs_useDirectX
? DoGetModesDirectX(modeMatch
)
594 : DoGetModesWindows(modeMatch
);
597 wxVideoMode
wxDisplay::GetCurrentMode() const
601 const wxString name
= GetNameForEnumSettings();
604 if ( !::EnumDisplaySettings(name
.empty() ? NULL
: name
.c_str(),
605 ENUM_CURRENT_SETTINGS
,
608 wxLogLastError(_T("EnumDisplaySettings(ENUM_CURRENT_SETTINGS)"));
612 mode
= ConvertToVideoMode(dm
);
618 // ----------------------------------------------------------------------------
619 // video mode switching
620 // ----------------------------------------------------------------------------
622 bool wxDisplay::DoChangeModeDirectX(const wxVideoMode
& mode
)
624 IDirectDraw2
*pDD
= (*gs_displays
)[m_index
].m_pDD2
;
628 wxWindow
*winTop
= wxTheApp
->GetTopWindow();
629 wxCHECK_MSG( winTop
, false, _T("top level window required for DirectX") );
631 HRESULT hr
= pDD
->SetCooperativeLevel
634 DDSCL_EXCLUSIVE
| DDSCL_FULLSCREEN
638 wxLogApiError(_T("IDirectDraw::SetCooperativeLevel"), hr
);
643 hr
= pDD
->SetDisplayMode(mode
.w
, mode
.h
, mode
.bpp
, mode
.refresh
, 0);
646 wxLogApiError(_T("IDirectDraw::SetDisplayMode"), hr
);
655 bool wxDisplay::DoChangeModeWindows(const wxVideoMode
& mode
)
657 // prepare ChangeDisplaySettingsEx() parameters
662 if ( mode
== wxDefaultVideoMode
)
664 // reset the video mode to default
668 else // change to the given mode
670 wxCHECK_MSG( mode
.w
&& mode
.h
, false,
671 _T("at least the width and height must be specified") );
674 dm
.dmSize
= sizeof(dm
);
675 dm
.dmFields
= DM_PELSWIDTH
| DM_PELSHEIGHT
;
676 dm
.dmPelsWidth
= mode
.w
;
677 dm
.dmPelsHeight
= mode
.h
;
681 dm
.dmFields
|= DM_BITSPERPEL
;
682 dm
.dmBitsPerPel
= mode
.bpp
;
687 dm
.dmFields
|= DM_DISPLAYFREQUENCY
;
688 dm
.dmDisplayFrequency
= mode
.refresh
;
693 flags
= CDS_FULLSCREEN
;
697 // get pointer to the function dynamically
699 // we're only called from the main thread, so it's ok to use static
701 static ChangeDisplaySettingsEx_t pfnChangeDisplaySettingsEx
= NULL
;
702 if ( !pfnChangeDisplaySettingsEx
)
704 wxDynamicLibrary
dllUser32(_T("user32.dll"));
705 if ( dllUser32
.IsLoaded() )
707 pfnChangeDisplaySettingsEx
= (ChangeDisplaySettingsEx_t
)
708 dllUser32
.GetSymbol(WINFUNC(ChangeDisplaySettingsEx
));
710 //else: huh, no user32.dll??
712 if ( !pfnChangeDisplaySettingsEx
)
714 // we must be under Win95 and so there is no multiple monitors
716 pfnChangeDisplaySettingsEx
= ChangeDisplaySettingsExForWin95
;
720 // do change the mode
721 switch ( pfnChangeDisplaySettingsEx
723 GetName(), // display name
724 pDevMode
, // dev mode or NULL to reset
727 NULL
// pointer to video parameters (not used)
730 case DISP_CHANGE_SUCCESSFUL
:
733 // If we have a top-level, full-screen frame, emulate
734 // the DirectX behavior and resize it. This makes this
735 // API quite a bit easier to use.
736 wxWindow
*winTop
= wxTheApp
->GetTopWindow();
737 wxFrame
*frameTop
= wxDynamicCast(winTop
, wxFrame
);
738 if (frameTop
&& frameTop
->IsFullScreen())
740 wxVideoMode current
= GetCurrentMode();
741 frameTop
->SetClientSize(current
.w
, current
.h
);
746 case DISP_CHANGE_BADMODE
:
747 // don't complain about this, this is the only "expected" error
751 wxFAIL_MSG( _T("unexpected ChangeDisplaySettingsEx() return value") );
757 bool wxDisplay::ChangeMode(const wxVideoMode
& mode
)
759 return gs_useDirectX
? DoChangeModeDirectX(mode
)
760 : DoChangeModeWindows(mode
);
763 #endif // wxUSE_DISPLAY