1 /////////////////////////////////////////////////////////////////////////////
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 #if defined(__GNUG__) && !defined(NO_GCC_PRAGMA)
22 #pragma implementation "display.h"
25 // For compilers that support precompilation, includes "wx.h".
26 #include "wx/wxprec.h"
36 #include "wx/dynarray.h"
40 #include "wx/dynload.h"
42 #include "wx/display.h"
44 // the following define is necessary to access the multi-monitor function
45 // declarations in a manner safe to use w/ Windows 95
46 #define COMPILE_MULTIMON_STUBS
48 // if you don't have multimon.h you can download the file from:
50 // http://www.microsoft.com/msj/0697/monitor/monitortextfigs.htm#fig4
54 // as (m)any standard header(s), this one doesn't compile without warnings
56 #pragma warning(disable:4706)
59 // with mingw32, we must include windows.h first and it doesn't hurt with other
66 #pragma warning(default:4706)
71 // we don't want to link with ddraw.lib which contains the real
72 // IID_IDirectDraw2 definition
73 const GUID wxIID_IDirectDraw2
=
74 { 0xB3A6F3E0, 0x2B43, 0x11CF, { 0xA2,0xDE,0x00,0xAA,0x00,0xB9,0x33,0x56 } };
76 // ----------------------------------------------------------------------------
78 // ----------------------------------------------------------------------------
81 #define WINFUNC(x) _T(#x) L"W"
83 #define WINFUNC(x) #x "A"
86 // ----------------------------------------------------------------------------
87 // typedefs for dynamically loaded Windows functions
88 // ----------------------------------------------------------------------------
90 typedef LONG (WINAPI
*ChangeDisplaySettingsEx_t
)(LPCTSTR lpszDeviceName
,
96 typedef BOOL (PASCAL
*DDEnumExCallback_t
)(GUID
*pGuid
,
97 LPTSTR driverDescription
,
102 typedef HRESULT (WINAPI
*DirectDrawEnumerateEx_t
)(DDEnumExCallback_t lpCallback
,
106 typedef HRESULT (WINAPI
*DirectDrawCreate_t
)(GUID
*lpGUID
,
107 LPDIRECTDRAW
*lplpDD
,
108 IUnknown
*pUnkOuter
);
110 // ----------------------------------------------------------------------------
112 // ----------------------------------------------------------------------------
117 // handle of this monitor used by MonitorXXX() functions, never NULL
120 // IDirectDraw object used to control this display, may be NULL
121 IDirectDraw2
*m_pDD2
;
123 // DirectDraw GUID for this display, only valid when using DirectDraw
126 // the entire area of this monitor in virtual screen coordinates
129 // the display device name for this monitor, empty initially and retrieved
130 // on demand by DoGetName()
133 wxDisplayInfo() { m_hmon
= NULL
; m_pDD2
= NULL
; }
134 ~wxDisplayInfo() { if ( m_pDD2
) m_pDD2
->Release(); }
137 WX_DECLARE_OBJARRAY(wxDisplayInfo
, wxDisplayInfoArray
);
138 #include "wx/arrimpl.cpp"
139 WX_DEFINE_OBJARRAY(wxDisplayInfoArray
);
141 // this module is used to cleanup gs_displays array
142 class wxDisplayModule
: public wxModule
145 virtual bool OnInit() { return true; }
146 virtual void OnExit();
148 DECLARE_DYNAMIC_CLASS(wxDisplayModule
)
151 IMPLEMENT_DYNAMIC_CLASS(wxDisplayModule
, wxModule
)
153 // ----------------------------------------------------------------------------
155 // ----------------------------------------------------------------------------
157 // do we use DirectX?
158 static bool gs_useDirectX
= false;
160 // dynamically resolved DirectDrawCreate()
161 static DirectDrawCreate_t gs_DirectDrawCreate
= NULL
;
163 // this is not really MT-unsafe as wxDisplay is only going to be used from the
164 // main thread, i.e. we consider that it's a GUI class and so don't protect it
165 static wxDisplayInfoArray
*gs_displays
= NULL
;
167 // ===========================================================================
169 // ===========================================================================
171 // ----------------------------------------------------------------------------
172 // callbacks for monitor/modes enumeration stuff
173 // ----------------------------------------------------------------------------
175 static BOOL CALLBACK
wxmswMonitorEnumProc (
176 HMONITOR hMonitor
, // handle to display monitor
177 HDC
WXUNUSED(hdcMonitor
), // handle to monitor-appropriate device context
178 LPRECT lprcMonitor
, // pointer to monitor intersection rectangle
179 LPARAM
WXUNUSED(dwData
) // data passed from EnumDisplayMonitors (unused)
182 wxDisplayInfo
*info
= new wxDisplayInfo();
184 // we need hMonitor to be able to map display id to it which is needed for
185 // MonitorXXX() functions, in particular MonitorFromPoint()
186 info
->m_hmon
= hMonitor
;
188 // we also store the display geometry
189 info
->m_rect
.SetX ( lprcMonitor
->left
);
190 info
->m_rect
.SetY ( lprcMonitor
->top
);
191 info
->m_rect
.SetWidth ( lprcMonitor
->right
- lprcMonitor
->left
);
192 info
->m_rect
.SetHeight ( lprcMonitor
->bottom
- lprcMonitor
->top
);
194 // now add this monitor to the array
195 gs_displays
->Add(info
);
197 // continue the enumeration
202 wxDDEnumExCallback(GUID
*pGuid
,
203 LPTSTR
WXUNUSED(driverDescription
),
205 LPVOID
WXUNUSED(lpContext
),
210 wxDisplayInfo
*info
= new wxDisplayInfo();
213 info
->m_guid
= *pGuid
;
214 info
->m_devName
= driverName
;
216 gs_displays
->Add(info
);
218 //else: we're called for the primary monitor, skip it
220 // continue the enumeration
224 HRESULT WINAPI
wxDDEnumModesCallback(LPDDSURFACEDESC lpDDSurfaceDesc
,
227 // we need at least the mode size
228 static const DWORD FLAGS_REQUIRED
= DDSD_HEIGHT
| DDSD_WIDTH
;
229 if ( (lpDDSurfaceDesc
->dwFlags
& FLAGS_REQUIRED
) == FLAGS_REQUIRED
)
231 wxArrayVideoModes
* const modes
= (wxArrayVideoModes
*)lpContext
;
233 modes
->Add(wxVideoMode(lpDDSurfaceDesc
->dwWidth
,
234 lpDDSurfaceDesc
->dwHeight
,
235 lpDDSurfaceDesc
->ddpfPixelFormat
.dwRGBBitCount
,
236 lpDDSurfaceDesc
->dwRefreshRate
));
239 // continue the enumeration
243 // ----------------------------------------------------------------------------
245 // ----------------------------------------------------------------------------
247 // initialize gs_displays using DirectX functions
248 static bool DoInitDirectX()
251 // suppress the errors if ddraw.dll is not found
252 wxLog::EnableLogging(false);
255 wxDynamicLibrary
dllDX(_T("ddraw.dll"));
258 wxLog::EnableLogging();
261 if ( !dllDX
.IsLoaded() )
264 DirectDrawEnumerateEx_t pDDEnumEx
= (DirectDrawEnumerateEx_t
)
265 dllDX
.GetSymbol(WINFUNC(DirectDrawEnumerateEx
));
269 // we'll also need DirectDrawCreate() later, resolve it right now
270 gs_DirectDrawCreate
= (DirectDrawCreate_t
)
271 dllDX
.GetSymbol(_T("DirectDrawCreate"));
272 if ( !gs_DirectDrawCreate
)
275 if ( (*pDDEnumEx
)(wxDDEnumExCallback
,
277 DDENUM_ATTACHEDSECONDARYDEVICES
) != DD_OK
)
282 // ok, it seems like we're going to use DirectDraw and so we're going to
283 // need ddraw.dll all the time, don't unload it
289 // initialize gs_displays using the standard Windows functions
290 static void DoInitStdWindows()
292 // enumerate all displays
293 if ( !::EnumDisplayMonitors(NULL
, NULL
, wxmswMonitorEnumProc
, 0) )
295 wxLogLastError(wxT("EnumDisplayMonitors"));
297 // TODO: still create at least one (valid) entry in gs_displays for the
302 // this function must be called before accessing gs_displays array as it
303 // creates and initializes it
304 static void InitDisplays()
309 gs_displays
= new wxDisplayInfoArray();
311 if ( !gs_useDirectX
|| !DoInitDirectX() )
313 // either we were told not to try to use DirectX or fall back to std
314 // functions if DirectX method failed
315 gs_useDirectX
= false;
321 // convert a DEVMODE to our wxVideoMode
322 wxVideoMode
ConvertToVideoMode(const DEVMODE
& dm
)
324 // note that dmDisplayFrequency may be 0 or 1 meaning "standard one" and
325 // although 0 is ok for us we don't want to return modes with 1hz refresh
326 return wxVideoMode(dm
.dmPelsWidth
,
329 dm
.dmDisplayFrequency
> 1 ? dm
.dmDisplayFrequency
: 0);
332 // emulation of ChangeDisplaySettingsEx() for Win95
333 LONG WINAPI
ChangeDisplaySettingsExForWin95(LPCTSTR
WXUNUSED(lpszDeviceName
),
337 LPVOID
WXUNUSED(lParam
))
339 return ::ChangeDisplaySettings(lpDevMode
, dwFlags
);
342 // ----------------------------------------------------------------------------
344 // ----------------------------------------------------------------------------
346 void wxDisplayModule::OnExit()
351 // ---------------------------------------------------------------------------
353 // ---------------------------------------------------------------------------
356 void wxDisplay::UseDirectX(bool useDX
)
358 wxCHECK_RET( !gs_displays
, _T("it is too late to call UseDirectX") );
360 gs_useDirectX
= useDX
;
363 // helper of GetFromPoint() and GetFromWindow()
364 static int DisplayFromHMONITOR(HMONITOR hmon
)
368 const size_t count
= wxDisplay::GetCount();
370 for ( size_t n
= 0; n
< count
; n
++ )
372 if ( hmon
== (*gs_displays
)[n
].m_hmon
)
381 size_t wxDisplayBase::GetCount()
385 // I'm not sure if they really always return the same thing and if this is
386 // not true I'd like to know in which situation does it happen
387 wxASSERT_MSG( gs_displays
->GetCount() == (size_t)::GetSystemMetrics(SM_CMONITORS
),
388 _T("So how many displays does this system have?") );
390 return gs_displays
->GetCount();
394 int wxDisplayBase::GetFromPoint ( const wxPoint
& pt
)
400 return DisplayFromHMONITOR(::MonitorFromPoint(pt2
, MONITOR_DEFAULTTONULL
));
404 int wxDisplayBase::GetFromWindow(wxWindow
*window
)
406 return DisplayFromHMONITOR
408 ::MonitorFromWindow(GetHwndOf(window
), MONITOR_DEFAULTTONULL
)
412 // ----------------------------------------------------------------------------
413 // wxDisplay ctor/dtor
414 // ----------------------------------------------------------------------------
416 wxDisplay::wxDisplay ( size_t n
)
417 : wxDisplayBase ( n
)
419 // if we do this in ctor we won't have to call it from all the member
425 wxDisplayInfo
& dpyInfo
= (*gs_displays
)[n
];
427 LPDIRECTDRAW2
& pDD2
= dpyInfo
.m_pDD2
;
430 if ( !gs_DirectDrawCreate
)
437 HRESULT hr
= (*gs_DirectDrawCreate
)(&dpyInfo
.m_guid
, &pDD
, NULL
);
439 if ( FAILED(hr
) || !pDD
)
442 wxLogApiError(_T("DirectDrawCreate"), hr
);
444 else // got IDirectDraw, we want IDirectDraw2
446 hr
= pDD
->QueryInterface(wxIID_IDirectDraw2
, (void **)&pDD2
);
447 if ( FAILED(hr
) || !pDD2
)
449 wxLogApiError(_T("IDirectDraw::QueryInterface(IDD2)"), hr
);
455 //else: DirectDraw object corresponding to our display already exists
457 // increment its ref count to account for Release() in dtor
459 // NB: pDD2 will be only really Release()d when gs_displays is
460 // destroyed which is ok as we don't want to recreate DD objects
466 wxDisplay::~wxDisplay()
468 wxDisplayInfo
& dpyInfo
= (*gs_displays
)[m_index
];
470 LPDIRECTDRAW2
& pDD2
= dpyInfo
.m_pDD2
;
477 // ----------------------------------------------------------------------------
478 // wxDisplay simple accessors
479 // ----------------------------------------------------------------------------
481 bool wxDisplay::IsOk() const
483 return m_index
< GetCount() &&
484 (!gs_useDirectX
|| (*gs_displays
)[m_index
].m_pDD2
);
487 wxRect
wxDisplay::GetGeometry() const
489 wxDisplayInfo
& dpyInfo
= (*gs_displays
)[m_index
];
490 wxRect
& rect
= dpyInfo
.m_rect
;
494 wxZeroMemory(monInfo
);
495 monInfo
.cbSize
= sizeof(monInfo
);
497 if ( !::GetMonitorInfo(dpyInfo
.m_hmon
, &monInfo
) )
499 wxLogLastError(_T("GetMonitorInfo"));
503 wxCopyRECTToRect(monInfo
.rcMonitor
, rect
);
510 wxString
wxDisplay::GetName() const
512 wxDisplayInfo
& dpyInfo
= (*gs_displays
)[m_index
];
513 if ( dpyInfo
.m_devName
.empty() )
515 MONITORINFOEX monInfo
;
516 wxZeroMemory(monInfo
);
517 monInfo
.cbSize
= sizeof(monInfo
);
519 // NB: Cast from MONITORINFOEX* to MONITORINFO* is done because
520 // Mingw headers - unlike the ones from Microsoft's Platform SDK -
521 // don't derive the former from the latter in C++ mode and so
522 // the pointer's type is not converted implicitly.
523 if ( !::GetMonitorInfo(dpyInfo
.m_hmon
, (LPMONITORINFO
)&monInfo
) )
525 wxLogLastError(_T("GetMonitorInfo"));
529 dpyInfo
.m_devName
= monInfo
.szDevice
;
533 return dpyInfo
.m_devName
;
536 wxString
wxDisplay::GetNameForEnumSettings() const
539 const bool isWin95
= wxGetOsVersion(&major
, &minor
) == wxWIN95
&&
540 major
== 4 && minor
== 0;
542 // the first parameter of EnumDisplaySettings() must be NULL under Win95
543 // according to MSDN but GetMonitorInfo() stub in multimon.h still returns
544 // something even in this case, so we have to correct this manually
552 // ----------------------------------------------------------------------------
553 // determine if this is the primary display
554 // ----------------------------------------------------------------------------
556 bool wxDisplay::IsPrimary() const
558 wxDisplayInfo
& dpyInfo
= (*gs_displays
)[m_index
];
560 MONITORINFOEX monInfo
;
561 wxZeroMemory(monInfo
);
562 monInfo
.cbSize
= sizeof(monInfo
);
564 // NB: Cast from MONITORINFOEX* to MONITORINFO* is done because
565 // Mingw headers - unlike the ones from Microsoft's Platform SDK -
566 // don't derive the former from the latter in C++ mode and so
567 // the pointer's type is not converted implicitly.
568 if ( !::GetMonitorInfo(dpyInfo
.m_hmon
, (LPMONITORINFO
)&monInfo
) )
570 wxLogLastError(_T("GetMonitorInfo"));
573 return (monInfo
.dwFlags
& MONITORINFOF_PRIMARY
) == MONITORINFOF_PRIMARY
;
576 // ----------------------------------------------------------------------------
577 // video modes enumeration
578 // ----------------------------------------------------------------------------
581 wxDisplay::DoGetModesDirectX(const wxVideoMode
& WXUNUSED(modeMatch
)) const
583 wxArrayVideoModes modes
;
585 IDirectDraw2
*pDD
= (*gs_displays
)[m_index
].m_pDD2
;
589 HRESULT hr
= pDD
->EnumDisplayModes
592 NULL
, // all modes (TODO: use modeMatch!)
593 &modes
, // callback parameter
594 wxDDEnumModesCallback
599 wxLogApiError(_T("IDirectDraw::EnumDisplayModes"), hr
);
607 wxDisplay::DoGetModesWindows(const wxVideoMode
& modeMatch
) const
609 wxArrayVideoModes modes
;
611 const wxString name
= GetNameForEnumSettings();
613 const wxChar
* const deviceName
= name
.empty() ? NULL
: name
.c_str();
616 for ( int iModeNum
= 0;
617 ::EnumDisplaySettings(deviceName
, iModeNum
, &dm
);
620 const wxVideoMode mode
= ConvertToVideoMode(dm
);
621 if ( mode
.Matches(modeMatch
) )
630 wxArrayVideoModes
wxDisplay::GetModes(const wxVideoMode
& modeMatch
) const
632 return gs_useDirectX
? DoGetModesDirectX(modeMatch
)
633 : DoGetModesWindows(modeMatch
);
636 wxVideoMode
wxDisplay::GetCurrentMode() const
640 const wxString name
= GetNameForEnumSettings();
643 if ( !::EnumDisplaySettings(name
.empty() ? NULL
: name
.c_str(),
644 ENUM_CURRENT_SETTINGS
,
647 wxLogLastError(_T("EnumDisplaySettings(ENUM_CURRENT_SETTINGS)"));
651 mode
= ConvertToVideoMode(dm
);
657 // ----------------------------------------------------------------------------
658 // video mode switching
659 // ----------------------------------------------------------------------------
661 bool wxDisplay::DoChangeModeDirectX(const wxVideoMode
& mode
)
663 IDirectDraw2
*pDD
= (*gs_displays
)[m_index
].m_pDD2
;
667 wxWindow
*winTop
= wxTheApp
->GetTopWindow();
668 wxCHECK_MSG( winTop
, false, _T("top level window required for DirectX") );
670 HRESULT hr
= pDD
->SetCooperativeLevel
673 DDSCL_EXCLUSIVE
| DDSCL_FULLSCREEN
677 wxLogApiError(_T("IDirectDraw::SetCooperativeLevel"), hr
);
682 hr
= pDD
->SetDisplayMode(mode
.w
, mode
.h
, mode
.bpp
, mode
.refresh
, 0);
685 wxLogApiError(_T("IDirectDraw::SetDisplayMode"), hr
);
694 bool wxDisplay::DoChangeModeWindows(const wxVideoMode
& mode
)
696 // prepare ChangeDisplaySettingsEx() parameters
701 if ( mode
== wxDefaultVideoMode
)
703 // reset the video mode to default
707 else // change to the given mode
709 wxCHECK_MSG( mode
.w
&& mode
.h
, false,
710 _T("at least the width and height must be specified") );
713 dm
.dmSize
= sizeof(dm
);
714 dm
.dmFields
= DM_PELSWIDTH
| DM_PELSHEIGHT
;
715 dm
.dmPelsWidth
= mode
.w
;
716 dm
.dmPelsHeight
= mode
.h
;
720 dm
.dmFields
|= DM_BITSPERPEL
;
721 dm
.dmBitsPerPel
= mode
.bpp
;
726 dm
.dmFields
|= DM_DISPLAYFREQUENCY
;
727 dm
.dmDisplayFrequency
= mode
.refresh
;
732 flags
= CDS_FULLSCREEN
;
736 // get pointer to the function dynamically
738 // we're only called from the main thread, so it's ok to use static
740 static ChangeDisplaySettingsEx_t pfnChangeDisplaySettingsEx
= NULL
;
741 if ( !pfnChangeDisplaySettingsEx
)
743 wxDynamicLibrary
dllUser32(_T("user32.dll"));
744 if ( dllUser32
.IsLoaded() )
746 pfnChangeDisplaySettingsEx
= (ChangeDisplaySettingsEx_t
)
747 dllUser32
.GetSymbol(WINFUNC(ChangeDisplaySettingsEx
));
749 //else: huh, no user32.dll??
751 if ( !pfnChangeDisplaySettingsEx
)
753 // we must be under Win95 and so there is no multiple monitors
755 pfnChangeDisplaySettingsEx
= ChangeDisplaySettingsExForWin95
;
759 // do change the mode
760 switch ( pfnChangeDisplaySettingsEx
762 GetName(), // display name
763 pDevMode
, // dev mode or NULL to reset
766 NULL
// pointer to video parameters (not used)
769 case DISP_CHANGE_SUCCESSFUL
:
772 // If we have a top-level, full-screen frame, emulate
773 // the DirectX behavior and resize it. This makes this
774 // API quite a bit easier to use.
775 wxWindow
*winTop
= wxTheApp
->GetTopWindow();
776 wxFrame
*frameTop
= wxDynamicCast(winTop
, wxFrame
);
777 if (frameTop
&& frameTop
->IsFullScreen())
779 wxVideoMode current
= GetCurrentMode();
780 frameTop
->SetClientSize(current
.w
, current
.h
);
785 case DISP_CHANGE_BADMODE
:
786 // don't complain about this, this is the only "expected" error
790 wxFAIL_MSG( _T("unexpected ChangeDisplaySettingsEx() return value") );
796 bool wxDisplay::ChangeMode(const wxVideoMode
& mode
)
798 return gs_useDirectX
? DoChangeModeDirectX(mode
)
799 : DoChangeModeWindows(mode
);
802 #endif // wxUSE_DISPLAY