use wxDynamicLibrary::GetSymbolAorW() instead of duplicating its functionality with...
[wxWidgets.git] / src / msw / display.cpp
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)
6 // Created: 06/21/02
7 // RCS-ID: $Id$
8 // Copyright: (c) wxWidgets team
9 // Copyright: (c) 2002-2006 wxWidgets team
10 // Licence: wxWindows licence
11 /////////////////////////////////////////////////////////////////////////////
12
13 // ===========================================================================
14 // declarations
15 // ===========================================================================
16
17 // ---------------------------------------------------------------------------
18 // headers
19 // ---------------------------------------------------------------------------
20
21 // For compilers that support precompilation, includes "wx.h".
22 #include "wx/wxprec.h"
23
24 #ifdef __BORLANDC__
25 #pragma hdrstop
26 #endif
27
28 #if wxUSE_DISPLAY
29
30 #ifndef WX_PRECOMP
31 #include "wx/app.h"
32 #include "wx/dynarray.h"
33 #include "wx/frame.h"
34 #endif
35
36 #include "wx/dynload.h"
37
38 #include "wx/display.h"
39
40 // Mingw's w32api headers don't include ddraw.h, though the user may have
41 // installed it. If using configure, we actually probe for ddraw.h there
42 // and set HAVE_DDRAW_H. Otherwise, assume we don't have it if using
43 // the w32api headers, and that we do otherwise.
44 #if !defined HAVE_W32API_H && !defined HAVE_DDRAW_H
45 #define HAVE_DDRAW_H 1
46 #endif
47
48 #ifndef __WXWINCE__
49 // Older versions of windef.h don't define HMONITOR. Unfortunately, we
50 // can't directly test whether HMONITOR is defined or not in windef.h as
51 // it's not a macro but a typedef, so we test for an unrelated symbol which
52 // is only defined in winuser.h if WINVER >= 0x0500
53 #if !defined(HMONITOR_DECLARED) && !defined(MNS_NOCHECK)
54 DECLARE_HANDLE(HMONITOR);
55 #define HMONITOR_DECLARED
56 #endif
57 #endif // !__WXWINCE__
58
59 #ifdef HAVE_DDRAW_H
60 #include <ddraw.h>
61
62 // we don't want to link with ddraw.lib which contains the real
63 // IID_IDirectDraw2 definition
64 const GUID wxIID_IDirectDraw2 =
65 { 0xB3A6F3E0, 0x2B43, 0x11CF, { 0xA2,0xDE,0x00,0xAA,0x00,0xB9,0x33,0x56 } };
66 #endif
67
68 // ----------------------------------------------------------------------------
69 // typedefs for dynamically loaded Windows functions
70 // ----------------------------------------------------------------------------
71
72 typedef LONG (WINAPI *ChangeDisplaySettingsEx_t)(LPCTSTR lpszDeviceName,
73 LPDEVMODE lpDevMode,
74 HWND hwnd,
75 DWORD dwFlags,
76 LPVOID lParam);
77
78 #ifdef HAVE_DDRAW_H
79 typedef BOOL (PASCAL *DDEnumExCallback_t)(GUID *pGuid,
80 LPTSTR driverDescription,
81 LPTSTR driverName,
82 LPVOID lpContext,
83 HMONITOR hmon);
84
85 typedef HRESULT (WINAPI *DirectDrawEnumerateEx_t)(DDEnumExCallback_t lpCallback,
86 LPVOID lpContext,
87 DWORD dwFlags);
88
89 typedef HRESULT (WINAPI *DirectDrawCreate_t)(GUID *lpGUID,
90 LPDIRECTDRAW *lplpDD,
91 IUnknown *pUnkOuter);
92 #endif
93
94 typedef BOOL (WINAPI *EnumDisplayMonitors_t)(HDC,LPCRECT,MONITORENUMPROC,LPARAM);
95 typedef HMONITOR (WINAPI *MonitorFromPoint_t)(POINT,DWORD);
96 typedef HMONITOR (WINAPI *MonitorFromWindow_t)(HWND,DWORD);
97 typedef BOOL (WINAPI *GetMonitorInfo_t)(HMONITOR,LPMONITORINFO);
98
99 static EnumDisplayMonitors_t gs_EnumDisplayMonitors = NULL;
100 static MonitorFromPoint_t gs_MonitorFromPoint = NULL;
101 static MonitorFromWindow_t gs_MonitorFromWindow = NULL;
102 static GetMonitorInfo_t gs_GetMonitorInfo = NULL;
103
104 // ----------------------------------------------------------------------------
105 // private classes
106 // ----------------------------------------------------------------------------
107
108 class wxDisplayInfo
109 {
110 public:
111 // handle of this monitor used by MonitorXXX() functions, never NULL
112 HMONITOR m_hmon;
113
114 // IDirectDraw object used to control this display, may be NULL
115 #ifdef HAVE_DDRAW_H
116 IDirectDraw2 *m_pDD2;
117 #else
118 void *m_pDD2;
119 #endif
120
121 // DirectDraw GUID for this display, only valid when using DirectDraw
122 GUID m_guid;
123
124 // the entire area of this monitor in virtual screen coordinates
125 wxRect m_rect;
126
127 // the display device name for this monitor, empty initially and retrieved
128 // on demand by DoGetName()
129 wxString m_devName;
130
131 wxDisplayInfo() { m_hmon = NULL; m_pDD2 = NULL; }
132 ~wxDisplayInfo() {
133 #ifdef HAVE_DDRAW_H
134 if ( m_pDD2 ) m_pDD2->Release();
135 #endif
136 }
137 };
138
139 WX_DECLARE_OBJARRAY(wxDisplayInfo, wxDisplayInfoArray);
140 #include "wx/arrimpl.cpp"
141 WX_DEFINE_OBJARRAY(wxDisplayInfoArray);
142
143 // this module is used to cleanup gs_displays array
144 class wxDisplayModule : public wxModule
145 {
146 public:
147 virtual bool OnInit() { return true; }
148 virtual void OnExit();
149
150 DECLARE_DYNAMIC_CLASS(wxDisplayModule)
151 };
152
153 IMPLEMENT_DYNAMIC_CLASS(wxDisplayModule, wxModule)
154
155 // ----------------------------------------------------------------------------
156 // globals
157 // ----------------------------------------------------------------------------
158
159 #ifdef HAVE_DDRAW_H
160 // do we use DirectX?
161 static bool gs_useDirectX = false;
162 #endif
163
164 // Try to look up the functions needed for supporting multiple monitors. If
165 // they aren't available (probably because we're running on Win95 or NT4 which
166 // predate this API), set a flag which makes wxDisplay return results for a
167 // single screen.
168 static bool OsSupportsMultipleMonitors()
169 {
170 static int isNewEnough = -1;
171 if ( isNewEnough == -1 )
172 {
173 isNewEnough = 0;
174 wxDynamicLibrary dllUser32(_T("user32.dll"));
175 // Check for one of the symbols to avoid logging errors just because
176 // we happen to be running on Win95 or NT4.
177 if ( dllUser32.IsLoaded() &&
178 dllUser32.HasSymbol(wxT("EnumDisplayMonitors")) )
179 {
180 // GetMonitorInfo has Unicode/ANSI variants, the others don't.
181 gs_EnumDisplayMonitors = (EnumDisplayMonitors_t)
182 dllUser32.GetSymbol(wxT("EnumDisplayMonitors"));
183 gs_MonitorFromPoint = (MonitorFromPoint_t)
184 dllUser32.GetSymbol(wxT("MonitorFromPoint"));
185 gs_MonitorFromWindow = (MonitorFromWindow_t)
186 dllUser32.GetSymbol(wxT("MonitorFromWindow"));
187 gs_GetMonitorInfo = (GetMonitorInfo_t)
188 dllUser32.GetSymbolAorW(wxT("GetMonitorInfo"));
189 if ( gs_EnumDisplayMonitors != NULL &&
190 gs_MonitorFromPoint != NULL &&
191 gs_MonitorFromWindow != NULL &&
192 gs_GetMonitorInfo != NULL )
193 {
194 isNewEnough = 1;
195 }
196 }
197 }
198 return (isNewEnough != 0);
199 }
200
201 #ifdef HAVE_DDRAW_H
202 // dynamically resolved DirectDrawCreate()
203 static DirectDrawCreate_t gs_DirectDrawCreate = NULL;
204 #endif
205
206 // this is not really MT-unsafe as wxDisplay is only going to be used from the
207 // main thread, i.e. we consider that it's a GUI class and so don't protect it
208 static wxDisplayInfoArray *gs_displays = NULL;
209
210 // ===========================================================================
211 // implementation
212 // ===========================================================================
213
214 // ----------------------------------------------------------------------------
215 // callbacks for monitor/modes enumeration stuff
216 // ----------------------------------------------------------------------------
217
218 static BOOL CALLBACK wxmswMonitorEnumProc (
219 HMONITOR hMonitor, // handle to display monitor
220 HDC WXUNUSED(hdcMonitor), // handle to monitor-appropriate device context
221 LPRECT lprcMonitor, // pointer to monitor intersection rectangle
222 LPARAM WXUNUSED(dwData) // data passed from EnumDisplayMonitors (unused)
223 )
224 {
225 wxDisplayInfo *info = new wxDisplayInfo();
226
227 // we need hMonitor to be able to map display id to it which is needed for
228 // MonitorXXX() functions, in particular MonitorFromPoint()
229 info->m_hmon = hMonitor;
230
231 // we also store the display geometry
232 info->m_rect.SetX ( lprcMonitor->left );
233 info->m_rect.SetY ( lprcMonitor->top );
234 info->m_rect.SetWidth ( lprcMonitor->right - lprcMonitor->left );
235 info->m_rect.SetHeight ( lprcMonitor->bottom - lprcMonitor->top );
236
237 // now add this monitor to the array
238 gs_displays->Add(info);
239
240 // continue the enumeration
241 return true;
242 }
243
244 #ifdef HAVE_DDRAW_H
245 BOOL PASCAL
246 wxDDEnumExCallback(GUID *pGuid,
247 LPTSTR WXUNUSED(driverDescription),
248 LPTSTR driverName,
249 LPVOID WXUNUSED(lpContext),
250 HMONITOR hmon)
251 {
252 if ( pGuid )
253 {
254 wxDisplayInfo *info = new wxDisplayInfo();
255
256 info->m_hmon = hmon;
257 info->m_guid = *pGuid;
258 info->m_devName = driverName;
259
260 gs_displays->Add(info);
261 }
262 //else: we're called for the primary monitor, skip it
263
264 // continue the enumeration
265 return true;
266 }
267
268 HRESULT WINAPI wxDDEnumModesCallback(LPDDSURFACEDESC lpDDSurfaceDesc,
269 LPVOID lpContext)
270 {
271 // we need at least the mode size
272 static const DWORD FLAGS_REQUIRED = DDSD_HEIGHT | DDSD_WIDTH;
273 if ( (lpDDSurfaceDesc->dwFlags & FLAGS_REQUIRED) == FLAGS_REQUIRED )
274 {
275 wxArrayVideoModes * const modes = (wxArrayVideoModes *)lpContext;
276
277 modes->Add(wxVideoMode(lpDDSurfaceDesc->dwWidth,
278 lpDDSurfaceDesc->dwHeight,
279 lpDDSurfaceDesc->ddpfPixelFormat.dwRGBBitCount,
280 lpDDSurfaceDesc->dwRefreshRate));
281 }
282
283 // continue the enumeration
284 return DDENUMRET_OK;
285 }
286 #endif
287
288 // ----------------------------------------------------------------------------
289 // local functions
290 // ----------------------------------------------------------------------------
291
292 #ifdef HAVE_DDRAW_H
293 // initialize gs_displays using DirectX functions
294 static bool DoInitDirectX()
295 {
296 #if wxUSE_LOG
297 // suppress the errors if ddraw.dll is not found
298 wxLog::EnableLogging(false);
299 #endif
300
301 wxDynamicLibrary dllDX(_T("ddraw.dll"));
302
303 #if wxUSE_LOG
304 wxLog::EnableLogging();
305 #endif
306
307 if ( !dllDX.IsLoaded() )
308 return false;
309
310 DirectDrawEnumerateEx_t pDDEnumEx = (DirectDrawEnumerateEx_t)
311 dllDX.GetSymbolAorW(_T("DirectDrawEnumerateEx"));
312 if ( !pDDEnumEx )
313 return false;
314
315 // we'll also need DirectDrawCreate() later, resolve it right now
316 gs_DirectDrawCreate = (DirectDrawCreate_t)
317 dllDX.GetSymbol(_T("DirectDrawCreate"));
318 if ( !gs_DirectDrawCreate )
319 return false;
320
321 if ( (*pDDEnumEx)(wxDDEnumExCallback,
322 NULL,
323 DDENUM_ATTACHEDSECONDARYDEVICES) != DD_OK )
324 {
325 return false;
326 }
327
328 // ok, it seems like we're going to use DirectDraw and so we're going to
329 // need ddraw.dll all the time, don't unload it
330 dllDX.Detach();
331
332 return true;
333 }
334 #endif
335
336 // initialize gs_displays using the standard Windows functions
337 static void DoInitStdWindows()
338 {
339 // enumerate all displays
340 if ( !gs_EnumDisplayMonitors(NULL, NULL, wxmswMonitorEnumProc, 0) )
341 {
342 wxLogLastError(wxT("EnumDisplayMonitors"));
343
344 // TODO: still create at least one (valid) entry in gs_displays for the
345 // primary display!
346 }
347 }
348
349 // this function must be called before accessing gs_displays array as it
350 // creates and initializes it
351 static void InitDisplays()
352 {
353 if ( gs_displays )
354 return;
355
356 gs_displays = new wxDisplayInfoArray();
357
358 #ifdef HAVE_DDRAW_H
359 if ( !gs_useDirectX || !DoInitDirectX() )
360 {
361 // either we were told not to try to use DirectX or fall back to std
362 // functions if DirectX method failed
363 gs_useDirectX = false;
364
365 DoInitStdWindows();
366 }
367 #else
368 DoInitStdWindows();
369 #endif
370 }
371
372 // convert a DEVMODE to our wxVideoMode
373 wxVideoMode ConvertToVideoMode(const DEVMODE& dm)
374 {
375 // note that dmDisplayFrequency may be 0 or 1 meaning "standard one" and
376 // although 0 is ok for us we don't want to return modes with 1hz refresh
377 return wxVideoMode(dm.dmPelsWidth,
378 dm.dmPelsHeight,
379 dm.dmBitsPerPel,
380 dm.dmDisplayFrequency > 1 ? dm.dmDisplayFrequency : 0);
381 }
382
383 #ifndef __WXWINCE__
384 // emulation of ChangeDisplaySettingsEx() for Win95
385 LONG WINAPI ChangeDisplaySettingsExForWin95(LPCTSTR WXUNUSED(lpszDeviceName),
386 LPDEVMODE lpDevMode,
387 HWND WXUNUSED(hwnd),
388 DWORD dwFlags,
389 LPVOID WXUNUSED(lParam))
390 {
391 return ::ChangeDisplaySettings(lpDevMode, dwFlags);
392 }
393 #endif // !__WXWINCE__
394
395 // ----------------------------------------------------------------------------
396 // wxDisplayModule
397 // ----------------------------------------------------------------------------
398
399 void wxDisplayModule::OnExit()
400 {
401 delete gs_displays;
402 }
403
404 // ---------------------------------------------------------------------------
405 // wxDisplay
406 // ---------------------------------------------------------------------------
407
408 /* static */
409 void wxDisplay::UseDirectX(bool useDX)
410 {
411 wxCHECK_RET( !gs_displays, _T("it is too late to call UseDirectX") );
412
413 #ifdef HAVE_DDRAW_H
414 // DirectDrawEnumerateEx requires Win98 or Win2k anyway.
415 if ( OsSupportsMultipleMonitors() ) gs_useDirectX = useDX;
416 #else
417 wxUnusedVar(useDX);
418 #endif
419 }
420
421 // helper for GetFromPoint() and GetFromWindow()
422 static int DisplayFromHMONITOR(HMONITOR hmon)
423 {
424 if ( hmon )
425 {
426 const size_t count = wxDisplay::GetCount();
427
428 for ( size_t n = 0; n < count; n++ )
429 {
430 if ( hmon == (*gs_displays)[n].m_hmon )
431 return n;
432 }
433 }
434
435 return wxNOT_FOUND;
436 }
437
438 /* static */
439 size_t wxDisplayBase::GetCount()
440 {
441 if ( !OsSupportsMultipleMonitors() ) return 1;
442
443 InitDisplays();
444
445 //RN: FIXME: This is wrong - the display info array should reload after every call
446 //to GetCount() - otherwise it will not be accurate.
447 //The user can change the number of displays in display properties/settings
448 //after GetCount or similar is called and really mess this up...
449 //wxASSERT_MSG( gs_displays->GetCount() == (size_t)::GetSystemMetrics(SM_CMONITORS),
450 // _T("So how many displays does this system have?") );
451
452 return gs_displays->GetCount();
453 }
454
455 /* static */
456 int wxDisplayBase::GetFromPoint ( const wxPoint& pt )
457 {
458 if ( !OsSupportsMultipleMonitors() )
459 {
460 const wxSize size = wxGetDisplaySize();
461 if (pt.x >= 0 && pt.x < size.GetWidth() &&
462 pt.y >= 0 && pt.y < size.GetHeight())
463 {
464 return 0;
465 }
466 return wxNOT_FOUND;
467 }
468
469 POINT pt2;
470 pt2.x = pt.x;
471 pt2.y = pt.y;
472
473 return DisplayFromHMONITOR(gs_MonitorFromPoint(pt2, MONITOR_DEFAULTTONULL));
474 }
475
476 /* static */
477 int wxDisplayBase::GetFromWindow(wxWindow *window)
478 {
479 if ( !OsSupportsMultipleMonitors() )
480 {
481 const wxRect r(window->GetRect());
482 const wxSize size = wxGetDisplaySize();
483 if (r.x < size.GetWidth() && r.x + r.width >= 0 &&
484 r.y < size.GetHeight() && r.y + r.height >= 0)
485 {
486 return 0;
487 }
488 return wxNOT_FOUND;
489 }
490
491 return DisplayFromHMONITOR
492 (
493 gs_MonitorFromWindow(GetHwndOf(window), MONITOR_DEFAULTTONULL)
494 );
495 }
496
497 // ----------------------------------------------------------------------------
498 // wxDisplay ctor/dtor
499 // ----------------------------------------------------------------------------
500
501 wxDisplay::wxDisplay ( size_t n )
502 : wxDisplayBase ( n )
503 {
504 if ( !OsSupportsMultipleMonitors() ) return;
505
506 // if we do this in ctor we won't have to call it from all the member
507 // functions
508 InitDisplays();
509
510 #ifdef HAVE_DDRAW_H
511 if ( gs_useDirectX )
512 {
513 wxDisplayInfo& dpyInfo = (*gs_displays)[n];
514
515 LPDIRECTDRAW2& pDD2 = dpyInfo.m_pDD2;
516 if ( !pDD2 )
517 {
518 if ( !gs_DirectDrawCreate )
519 {
520 // what to do??
521 return;
522 }
523
524 IDirectDraw *pDD;
525 HRESULT hr = (*gs_DirectDrawCreate)(&dpyInfo.m_guid, &pDD, NULL);
526
527 if ( FAILED(hr) || !pDD )
528 {
529 // what to do??
530 wxLogApiError(_T("DirectDrawCreate"), hr);
531 }
532 else // got IDirectDraw, we want IDirectDraw2
533 {
534 hr = pDD->QueryInterface(wxIID_IDirectDraw2, (void **)&pDD2);
535 if ( FAILED(hr) || !pDD2 )
536 {
537 wxLogApiError(_T("IDirectDraw::QueryInterface(IDD2)"), hr);
538 }
539
540 pDD->Release();
541 }
542 }
543 //else: DirectDraw object corresponding to our display already exists
544
545 // increment its ref count to account for Release() in dtor
546 //
547 // NB: pDD2 will be only really Release()d when gs_displays is
548 // destroyed which is ok as we don't want to recreate DD objects
549 // all the time
550 pDD2->AddRef();
551 }
552 #endif
553 }
554
555 wxDisplay::~wxDisplay()
556 {
557 #ifdef HAVE_DDRAW_H
558 if ( !OsSupportsMultipleMonitors() ) return;
559
560 wxDisplayInfo& dpyInfo = (*gs_displays)[m_index];
561
562 LPDIRECTDRAW2& pDD2 = dpyInfo.m_pDD2;
563 if ( pDD2 )
564 {
565 pDD2->Release();
566 }
567 #endif
568 }
569
570 // ----------------------------------------------------------------------------
571 // wxDisplay simple accessors
572 // ----------------------------------------------------------------------------
573
574 bool wxDisplay::IsOk() const
575 {
576 #ifdef HAVE_DDRAW_H
577 return m_index < GetCount() &&
578 (!gs_useDirectX || (*gs_displays)[m_index].m_pDD2);
579 #else
580 return m_index < GetCount();
581 #endif
582 }
583
584 wxRect wxDisplay::GetGeometry() const
585 {
586 if ( !OsSupportsMultipleMonitors() )
587 {
588 wxSize size = wxGetDisplaySize();
589 return wxRect(0, 0, size.GetWidth(), size.GetHeight());
590 }
591
592 wxDisplayInfo& dpyInfo = (*gs_displays)[m_index];
593 wxRect& rect = dpyInfo.m_rect;
594 if ( !rect.width )
595 {
596 MONITORINFO monInfo;
597 wxZeroMemory(monInfo);
598 monInfo.cbSize = sizeof(monInfo);
599
600 if ( !gs_GetMonitorInfo(dpyInfo.m_hmon, &monInfo) )
601 {
602 wxLogLastError(_T("GetMonitorInfo"));
603 }
604 else
605 {
606 wxCopyRECTToRect(monInfo.rcMonitor, rect);
607 }
608 }
609
610 return rect;
611 }
612
613 wxString wxDisplay::GetName() const
614 {
615 if ( !OsSupportsMultipleMonitors() ) return wxT("");
616
617 wxDisplayInfo& dpyInfo = (*gs_displays)[m_index];
618 if ( dpyInfo.m_devName.empty() )
619 {
620 MONITORINFOEX monInfo;
621 wxZeroMemory(monInfo);
622 monInfo.cbSize = sizeof(monInfo);
623
624 // NB: Cast from MONITORINFOEX* to MONITORINFO* is done because
625 // Mingw headers - unlike the ones from Microsoft's Platform SDK -
626 // don't derive the former from the latter in C++ mode and so
627 // the pointer's type is not converted implicitly.
628 if ( !gs_GetMonitorInfo(dpyInfo.m_hmon, (LPMONITORINFO)&monInfo) )
629 {
630 wxLogLastError(_T("GetMonitorInfo"));
631 }
632 else
633 {
634 dpyInfo.m_devName = monInfo.szDevice;
635 }
636 }
637
638 return dpyInfo.m_devName;
639 }
640
641 // ----------------------------------------------------------------------------
642 // determine if this is the primary display
643 // ----------------------------------------------------------------------------
644
645 bool wxDisplay::IsPrimary() const
646 {
647 if ( !OsSupportsMultipleMonitors() ) return true;
648
649 wxDisplayInfo& dpyInfo = (*gs_displays)[m_index];
650
651 MONITORINFOEX monInfo;
652 wxZeroMemory(monInfo);
653 monInfo.cbSize = sizeof(monInfo);
654
655 // NB: Cast from MONITORINFOEX* to MONITORINFO* is done because
656 // Mingw headers - unlike the ones from Microsoft's Platform SDK -
657 // don't derive the former from the latter in C++ mode and so
658 // the pointer's type is not converted implicitly.
659 if ( !gs_GetMonitorInfo(dpyInfo.m_hmon, (LPMONITORINFO)&monInfo) )
660 {
661 wxLogLastError(_T("GetMonitorInfo"));
662 }
663
664 return (monInfo.dwFlags & MONITORINFOF_PRIMARY) == MONITORINFOF_PRIMARY;
665 }
666
667 // ----------------------------------------------------------------------------
668 // video modes enumeration
669 // ----------------------------------------------------------------------------
670
671 #ifdef HAVE_DDRAW_H
672 wxArrayVideoModes
673 wxDisplay::DoGetModesDirectX(const wxVideoMode& WXUNUSED(modeMatch)) const
674 {
675 wxArrayVideoModes modes;
676
677 IDirectDraw2 *pDD = (*gs_displays)[m_index].m_pDD2;
678
679 if ( pDD )
680 {
681 HRESULT hr = pDD->EnumDisplayModes
682 (
683 DDEDM_REFRESHRATES,
684 NULL, // all modes (TODO: use modeMatch!)
685 &modes, // callback parameter
686 wxDDEnumModesCallback
687 );
688
689 if ( FAILED(hr) )
690 {
691 wxLogApiError(_T("IDirectDraw::EnumDisplayModes"), hr);
692 }
693 }
694
695 return modes;
696 }
697 #endif
698
699 wxArrayVideoModes
700 wxDisplay::DoGetModesWindows(const wxVideoMode& modeMatch) const
701 {
702 wxArrayVideoModes modes;
703
704 // The first parameter of EnumDisplaySettings() must be NULL under Win95
705 // according to MSDN. The version of GetName() we implement for Win95
706 // returns an empty string.
707 const wxString name = GetName();
708 const wxChar * const deviceName = name.empty() ? NULL : name.c_str();
709
710 DEVMODE dm;
711 dm.dmSize = sizeof(dm);
712 dm.dmDriverExtra = 0;
713 for ( int iModeNum = 0;
714 ::EnumDisplaySettings(deviceName, iModeNum, &dm);
715 iModeNum++ )
716 {
717 const wxVideoMode mode = ConvertToVideoMode(dm);
718 if ( mode.Matches(modeMatch) )
719 {
720 modes.Add(mode);
721 }
722 }
723
724 return modes;
725 }
726
727 wxArrayVideoModes wxDisplay::GetModes(const wxVideoMode& modeMatch) const
728 {
729 #ifdef HAVE_DDRAW_H
730 return gs_useDirectX ? DoGetModesDirectX(modeMatch)
731 : DoGetModesWindows(modeMatch);
732 #else
733 return DoGetModesWindows(modeMatch);
734 #endif
735 }
736
737 wxVideoMode wxDisplay::GetCurrentMode() const
738 {
739 wxVideoMode mode;
740
741 // The first parameter of EnumDisplaySettings() must be NULL under Win95
742 // according to MSDN. The version of GetName() we implement for Win95
743 // returns an empty string.
744 const wxString name = GetName();
745 const wxChar * const deviceName = name.empty() ? NULL : name.c_str();
746
747 DEVMODE dm;
748 dm.dmSize = sizeof(dm);
749 dm.dmDriverExtra = 0;
750 if ( !::EnumDisplaySettings(deviceName, ENUM_CURRENT_SETTINGS, &dm) )
751 {
752 wxLogLastError(_T("EnumDisplaySettings(ENUM_CURRENT_SETTINGS)"));
753 }
754 else
755 {
756 mode = ConvertToVideoMode(dm);
757 }
758
759 return mode;
760 }
761
762 // ----------------------------------------------------------------------------
763 // video mode switching
764 // ----------------------------------------------------------------------------
765
766 #ifdef HAVE_DDRAW_H
767 bool wxDisplay::DoChangeModeDirectX(const wxVideoMode& mode)
768 {
769 IDirectDraw2 *pDD = (*gs_displays)[m_index].m_pDD2;
770 if ( !pDD )
771 return false;
772
773 wxWindow *winTop = wxTheApp->GetTopWindow();
774 wxCHECK_MSG( winTop, false, _T("top level window required for DirectX") );
775
776 HRESULT hr = pDD->SetCooperativeLevel
777 (
778 GetHwndOf(winTop),
779 DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN
780 );
781 if ( FAILED(hr) )
782 {
783 wxLogApiError(_T("IDirectDraw::SetCooperativeLevel"), hr);
784
785 return false;
786 }
787
788 hr = pDD->SetDisplayMode(mode.w, mode.h, mode.bpp, mode.refresh, 0);
789 if ( FAILED(hr) )
790 {
791 wxLogApiError(_T("IDirectDraw::SetDisplayMode"), hr);
792
793 return false;
794 }
795
796
797 return true;
798 }
799 #endif
800
801 bool wxDisplay::DoChangeModeWindows(const wxVideoMode& mode)
802 {
803 // prepare ChangeDisplaySettingsEx() parameters
804 DEVMODE dm;
805 DEVMODE *pDevMode;
806
807 int flags;
808
809 if ( mode == wxDefaultVideoMode )
810 {
811 // reset the video mode to default
812 pDevMode = NULL;
813 flags = 0;
814 }
815 else // change to the given mode
816 {
817 wxCHECK_MSG( mode.w && mode.h, false,
818 _T("at least the width and height must be specified") );
819
820 wxZeroMemory(dm);
821 dm.dmSize = sizeof(dm);
822 dm.dmDriverExtra = 0;
823 dm.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT;
824 dm.dmPelsWidth = mode.w;
825 dm.dmPelsHeight = mode.h;
826
827 if ( mode.bpp )
828 {
829 dm.dmFields |= DM_BITSPERPEL;
830 dm.dmBitsPerPel = mode.bpp;
831 }
832
833 if ( mode.refresh )
834 {
835 dm.dmFields |= DM_DISPLAYFREQUENCY;
836 dm.dmDisplayFrequency = mode.refresh;
837 }
838
839 pDevMode = &dm;
840
841 #ifdef __WXWINCE__
842 flags = 0;
843 #else // !__WXWINCE__
844 flags = CDS_FULLSCREEN;
845 #endif // __WXWINCE__/!__WXWINCE__
846 }
847
848
849 // get pointer to the function dynamically
850 //
851 // we're only called from the main thread, so it's ok to use static
852 // variable
853 static ChangeDisplaySettingsEx_t pfnChangeDisplaySettingsEx = NULL;
854 if ( !pfnChangeDisplaySettingsEx )
855 {
856 wxDynamicLibrary dllUser32(_T("user32.dll"));
857 if ( dllUser32.IsLoaded() )
858 {
859 pfnChangeDisplaySettingsEx = (ChangeDisplaySettingsEx_t)
860 dllUser32.GetSymbolAorW(_T("ChangeDisplaySettingsEx"));
861 }
862 //else: huh, no user32.dll??
863
864 #ifndef __WXWINCE__
865 if ( !pfnChangeDisplaySettingsEx )
866 {
867 // we must be under Win95 and so there is no multiple monitors
868 // support anyhow
869 pfnChangeDisplaySettingsEx = ChangeDisplaySettingsExForWin95;
870 }
871 #endif // !__WXWINCE__
872 }
873
874 // do change the mode
875 switch ( pfnChangeDisplaySettingsEx
876 (
877 GetName(), // display name
878 pDevMode, // dev mode or NULL to reset
879 NULL, // reserved
880 flags,
881 NULL // pointer to video parameters (not used)
882 ) )
883 {
884 case DISP_CHANGE_SUCCESSFUL:
885 // ok
886 {
887 // If we have a top-level, full-screen frame, emulate
888 // the DirectX behavior and resize it. This makes this
889 // API quite a bit easier to use.
890 wxWindow *winTop = wxTheApp->GetTopWindow();
891 wxFrame *frameTop = wxDynamicCast(winTop, wxFrame);
892 if (frameTop && frameTop->IsFullScreen())
893 {
894 wxVideoMode current = GetCurrentMode();
895 frameTop->SetClientSize(current.w, current.h);
896 }
897 }
898 return true;
899
900 case DISP_CHANGE_BADMODE:
901 // don't complain about this, this is the only "expected" error
902 break;
903
904 default:
905 wxFAIL_MSG( _T("unexpected ChangeDisplaySettingsEx() return value") );
906 }
907
908 return false;
909 }
910
911 bool wxDisplay::ChangeMode(const wxVideoMode& mode)
912 {
913 #ifdef HAVE_DDRAW_H
914 return gs_useDirectX ? DoChangeModeDirectX(mode)
915 : DoChangeModeWindows(mode);
916 #else
917 return DoChangeModeWindows(mode);
918 #endif
919 }
920
921 #endif // wxUSE_DISPLAY