]>
Commit | Line | Data |
---|---|---|
1 | ///////////////////////////////////////////////////////////////////////////// | |
2 | // Name: display.cpp | |
3 | // Purpose: MSW Implementation of wxDisplay class | |
4 | // Author: Royce Mitchell III | |
5 | // Modified by: VZ (resolutions enumeration/change support, DirectDraw, ...) | |
6 | // Created: 06/21/02 | |
7 | // RCS-ID: $Id$ | |
8 | // Copyright: (c) wxWindows team | |
9 | // Licence: wxWindows licence | |
10 | ///////////////////////////////////////////////////////////////////////////// | |
11 | ||
12 | // =========================================================================== | |
13 | // declarations | |
14 | // =========================================================================== | |
15 | ||
16 | // --------------------------------------------------------------------------- | |
17 | // headers | |
18 | // --------------------------------------------------------------------------- | |
19 | ||
20 | #if defined(__GNUG__) && !defined(NO_GCC_PRAGMA) | |
21 | #pragma implementation "display.h" | |
22 | #endif | |
23 | ||
24 | // For compilers that support precompilation, includes "wx.h". | |
25 | #include "wx/wxprec.h" | |
26 | ||
27 | #ifdef __BORLANDC__ | |
28 | #pragma hdrstop | |
29 | #endif | |
30 | ||
31 | #if wxUSE_DISPLAY | |
32 | ||
33 | #ifndef WX_PRECOMP | |
34 | #include "wx/dynarray.h" | |
35 | #endif | |
36 | ||
37 | #include "wx/dynload.h" | |
38 | ||
39 | #include "wx/display.h" | |
40 | ||
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 | |
44 | ||
45 | // if you don't have multimon.h you can download the file from: | |
46 | // | |
47 | // http://www.microsoft.com/msj/0697/monitor/monitortextfigs.htm#fig4 | |
48 | // | |
49 | ||
50 | #ifdef _MSC_VER | |
51 | // as (m)any standard header(s), this one doesn't compile without warnings | |
52 | // with VC++ 6 <sigh> | |
53 | #pragma warning(disable:4706) | |
54 | #endif | |
55 | ||
56 | #include <multimon.h> | |
57 | ||
58 | #ifdef _MSC_VER | |
59 | #pragma warning(default:4706) | |
60 | #endif | |
61 | ||
62 | #include <ddraw.h> | |
63 | ||
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 } }; | |
68 | ||
69 | // ---------------------------------------------------------------------------- | |
70 | // macros | |
71 | // ---------------------------------------------------------------------------- | |
72 | ||
73 | #ifdef _UNICODE | |
74 | #define WINFUNC(x) _T(#x) L"W" | |
75 | #else | |
76 | #define WINFUNC(x) #x "A" | |
77 | #endif | |
78 | ||
79 | // ---------------------------------------------------------------------------- | |
80 | // typedefs for dynamically loaded Windows functions | |
81 | // ---------------------------------------------------------------------------- | |
82 | ||
83 | typedef LONG (WINAPI *ChangeDisplaySettingsEx_t)(LPCTSTR lpszDeviceName, | |
84 | LPDEVMODE lpDevMode, | |
85 | HWND hwnd, | |
86 | DWORD dwFlags, | |
87 | LPVOID lParam); | |
88 | ||
89 | typedef BOOL (PASCAL *DDEnumExCallback_t)(GUID *pGuid, | |
90 | LPTSTR driverDescription, | |
91 | LPTSTR driverName, | |
92 | LPVOID lpContext, | |
93 | HMONITOR hmon); | |
94 | ||
95 | typedef HRESULT (WINAPI *DirectDrawEnumerateEx_t)(DDEnumExCallback_t lpCallback, | |
96 | LPVOID lpContext, | |
97 | DWORD dwFlags); | |
98 | ||
99 | typedef HRESULT (WINAPI *DirectDrawCreate_t)(GUID *lpGUID, | |
100 | LPDIRECTDRAW *lplpDD, | |
101 | IUnknown *pUnkOuter); | |
102 | ||
103 | // ---------------------------------------------------------------------------- | |
104 | // private classes | |
105 | // ---------------------------------------------------------------------------- | |
106 | ||
107 | class wxDisplayInfo | |
108 | { | |
109 | public: | |
110 | // handle of this monitor used by MonitorXXX() functions, never NULL | |
111 | HMONITOR m_hmon; | |
112 | ||
113 | // IDirectDraw object used to control this display, may be NULL | |
114 | IDirectDraw2 *m_pDD2; | |
115 | ||
116 | // DirectDraw GUID for this display, only valid when using DirectDraw | |
117 | GUID m_guid; | |
118 | ||
119 | // the entire area of this monitor in virtual screen coordinates | |
120 | wxRect m_rect; | |
121 | ||
122 | // the display device name for this monitor, empty initially and retrieved | |
123 | // on demand by DoGetName() | |
124 | wxString m_devName; | |
125 | ||
126 | wxDisplayInfo() { m_hmon = NULL; m_pDD2 = NULL; } | |
127 | ~wxDisplayInfo() { if ( m_pDD2 ) m_pDD2->Release(); } | |
128 | }; | |
129 | ||
130 | WX_DECLARE_OBJARRAY(wxDisplayInfo, wxDisplayInfoArray); | |
131 | #include "wx/arrimpl.cpp" | |
132 | WX_DEFINE_OBJARRAY(wxDisplayInfoArray); | |
133 | ||
134 | // this module is used to cleanup gs_displays array | |
135 | class wxDisplayModule : public wxModule | |
136 | { | |
137 | public: | |
138 | virtual bool OnInit() { return true; } | |
139 | virtual void OnExit(); | |
140 | ||
141 | DECLARE_DYNAMIC_CLASS(wxDisplayModule) | |
142 | }; | |
143 | ||
144 | IMPLEMENT_DYNAMIC_CLASS(wxDisplayModule, wxModule) | |
145 | ||
146 | // ---------------------------------------------------------------------------- | |
147 | // globals | |
148 | // ---------------------------------------------------------------------------- | |
149 | ||
150 | // do we use DirectX? | |
151 | static bool gs_useDirectX = false; | |
152 | ||
153 | // dynamically resolved DirectDrawCreate() | |
154 | static DirectDrawCreate_t gs_DirectDrawCreate = NULL; | |
155 | ||
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; | |
159 | ||
160 | // =========================================================================== | |
161 | // implementation | |
162 | // =========================================================================== | |
163 | ||
164 | // ---------------------------------------------------------------------------- | |
165 | // callbacks for monitor/modes enumeration stuff | |
166 | // ---------------------------------------------------------------------------- | |
167 | ||
168 | static BOOL CALLBACK wxmswMonitorEnumProc ( | |
169 | HMONITOR hMonitor, // handle to display monitor | |
170 | HDC WXUNUSED(hdcMonitor), // handle to monitor-appropriate device context | |
171 | LPRECT lprcMonitor, // pointer to monitor intersection rectangle | |
172 | LPARAM WXUNUSED(dwData) // data passed from EnumDisplayMonitors (unused) | |
173 | ) | |
174 | { | |
175 | wxDisplayInfo *info = new wxDisplayInfo(); | |
176 | ||
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; | |
180 | ||
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 ); | |
186 | ||
187 | // now add this monitor to the array | |
188 | gs_displays->Add(info); | |
189 | ||
190 | // continue the enumeration | |
191 | return true; | |
192 | } | |
193 | ||
194 | BOOL PASCAL | |
195 | wxDDEnumExCallback(GUID *pGuid, | |
196 | LPTSTR WXUNUSED(driverDescription), | |
197 | LPTSTR driverName, | |
198 | LPVOID WXUNUSED(lpContext), | |
199 | HMONITOR hmon) | |
200 | { | |
201 | if ( pGuid ) | |
202 | { | |
203 | wxDisplayInfo *info = new wxDisplayInfo(); | |
204 | ||
205 | info->m_hmon = hmon; | |
206 | info->m_guid = *pGuid; | |
207 | info->m_devName = driverName; | |
208 | ||
209 | gs_displays->Add(info); | |
210 | } | |
211 | //else: we're called for the primary monitor, skip it | |
212 | ||
213 | // continue the enumeration | |
214 | return true; | |
215 | } | |
216 | ||
217 | HRESULT WINAPI wxDDEnumModesCallback(LPDDSURFACEDESC lpDDSurfaceDesc, | |
218 | LPVOID lpContext) | |
219 | { | |
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 ) | |
223 | { | |
224 | wxArrayVideoModes * const modes = (wxArrayVideoModes *)lpContext; | |
225 | ||
226 | modes->Add(wxVideoMode(lpDDSurfaceDesc->dwWidth, | |
227 | lpDDSurfaceDesc->dwHeight, | |
228 | lpDDSurfaceDesc->ddpfPixelFormat.dwRGBBitCount, | |
229 | lpDDSurfaceDesc->dwRefreshRate)); | |
230 | } | |
231 | ||
232 | // continue the enumeration | |
233 | return DDENUMRET_OK; | |
234 | } | |
235 | ||
236 | // ---------------------------------------------------------------------------- | |
237 | // local functions | |
238 | // ---------------------------------------------------------------------------- | |
239 | ||
240 | // initialize gs_displays using DirectX functions | |
241 | static bool DoInitDirectX() | |
242 | { | |
243 | // suppress the errors if ddraw.dll is not found | |
244 | wxLog::EnableLogging(false); | |
245 | ||
246 | wxDynamicLibrary dllDX(_T("ddraw.dll")); | |
247 | ||
248 | wxLog::EnableLogging(true); | |
249 | ||
250 | if ( !dllDX.IsLoaded() ) | |
251 | return false; | |
252 | ||
253 | DirectDrawEnumerateEx_t pDDEnumEx = (DirectDrawEnumerateEx_t) | |
254 | dllDX.GetSymbol(WINFUNC(DirectDrawEnumerateEx)); | |
255 | if ( !pDDEnumEx ) | |
256 | return false; | |
257 | ||
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 ) | |
262 | return false; | |
263 | ||
264 | if ( (*pDDEnumEx)(wxDDEnumExCallback, | |
265 | NULL, | |
266 | DDENUM_ATTACHEDSECONDARYDEVICES) != DD_OK ) | |
267 | { | |
268 | return false; | |
269 | } | |
270 | ||
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 | |
273 | dllDX.Detach(); | |
274 | ||
275 | return true; | |
276 | } | |
277 | ||
278 | // initialize gs_displays using the standard Windows functions | |
279 | static void DoInitStdWindows() | |
280 | { | |
281 | // enumerate all displays | |
282 | if ( !::EnumDisplayMonitors(NULL, NULL, wxmswMonitorEnumProc, 0) ) | |
283 | { | |
284 | wxLogLastError(wxT("EnumDisplayMonitors")); | |
285 | ||
286 | // TODO: still create at least one (valid) entry in gs_displays for the | |
287 | // primary display! | |
288 | } | |
289 | } | |
290 | ||
291 | // this function must be called before accessing gs_displays array as it | |
292 | // creates and initializes it | |
293 | static void InitDisplays() | |
294 | { | |
295 | if ( gs_displays ) | |
296 | return; | |
297 | ||
298 | gs_displays = new wxDisplayInfoArray(); | |
299 | ||
300 | if ( !gs_useDirectX || !DoInitDirectX() ) | |
301 | { | |
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; | |
305 | ||
306 | DoInitStdWindows(); | |
307 | } | |
308 | } | |
309 | ||
310 | // convert a DEVMODE to our wxVideoMode | |
311 | wxVideoMode ConvertToVideoMode(const DEVMODE& dm) | |
312 | { | |
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, | |
316 | dm.dmPelsHeight, | |
317 | dm.dmBitsPerPel, | |
318 | dm.dmDisplayFrequency > 1 ? dm.dmDisplayFrequency : 0); | |
319 | } | |
320 | ||
321 | // emulation of ChangeDisplaySettingsEx() for Win95 | |
322 | LONG WINAPI ChangeDisplaySettingsExForWin95(LPCTSTR WXUNUSED(lpszDeviceName), | |
323 | LPDEVMODE lpDevMode, | |
324 | HWND WXUNUSED(hwnd), | |
325 | DWORD dwFlags, | |
326 | LPVOID WXUNUSED(lParam)) | |
327 | { | |
328 | return ::ChangeDisplaySettings(lpDevMode, dwFlags); | |
329 | } | |
330 | ||
331 | // ---------------------------------------------------------------------------- | |
332 | // wxDisplayModule | |
333 | // ---------------------------------------------------------------------------- | |
334 | ||
335 | void wxDisplayModule::OnExit() | |
336 | { | |
337 | delete gs_displays; | |
338 | } | |
339 | ||
340 | // --------------------------------------------------------------------------- | |
341 | // wxDisplay | |
342 | // --------------------------------------------------------------------------- | |
343 | ||
344 | /* static */ | |
345 | void wxDisplay::UseDirectX(bool useDX) | |
346 | { | |
347 | wxCHECK_RET( !gs_displays, _T("it is too late to call UseDirectX") ); | |
348 | ||
349 | gs_useDirectX = useDX; | |
350 | } | |
351 | ||
352 | // helper of GetFromPoint() and GetFromWindow() | |
353 | static int DisplayFromHMONITOR(HMONITOR hmon) | |
354 | { | |
355 | if ( hmon ) | |
356 | { | |
357 | const size_t count = wxDisplay::GetCount(); | |
358 | ||
359 | for ( size_t n = 0; n < count; n++ ) | |
360 | { | |
361 | if ( hmon == (*gs_displays)[n].m_hmon ) | |
362 | return n; | |
363 | } | |
364 | } | |
365 | ||
366 | return wxNOT_FOUND; | |
367 | } | |
368 | ||
369 | /* static */ | |
370 | size_t wxDisplayBase::GetCount() | |
371 | { | |
372 | InitDisplays(); | |
373 | ||
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?") ); | |
378 | ||
379 | return gs_displays->GetCount(); | |
380 | } | |
381 | ||
382 | /* static */ | |
383 | int wxDisplayBase::GetFromPoint ( const wxPoint& pt ) | |
384 | { | |
385 | POINT pt2; | |
386 | pt2.x = pt.x; | |
387 | pt2.y = pt.y; | |
388 | ||
389 | return DisplayFromHMONITOR(::MonitorFromPoint(pt2, MONITOR_DEFAULTTONULL)); | |
390 | } | |
391 | ||
392 | /* static */ | |
393 | int wxDisplayBase::GetFromWindow(wxWindow *window) | |
394 | { | |
395 | return DisplayFromHMONITOR | |
396 | ( | |
397 | ::MonitorFromWindow(GetHwndOf(window), MONITOR_DEFAULTTONULL) | |
398 | ); | |
399 | } | |
400 | ||
401 | // ---------------------------------------------------------------------------- | |
402 | // wxDisplay ctor/dtor | |
403 | // ---------------------------------------------------------------------------- | |
404 | ||
405 | wxDisplay::wxDisplay ( size_t n ) | |
406 | : wxDisplayBase ( n ) | |
407 | { | |
408 | // if we do this in ctor we won't have to call it from all the member | |
409 | // functions | |
410 | InitDisplays(); | |
411 | ||
412 | if ( gs_useDirectX ) | |
413 | { | |
414 | wxDisplayInfo& dpyInfo = (*gs_displays)[n]; | |
415 | ||
416 | LPDIRECTDRAW2& pDD2 = dpyInfo.m_pDD2; | |
417 | if ( !pDD2 ) | |
418 | { | |
419 | if ( !gs_DirectDrawCreate ) | |
420 | { | |
421 | // what to do?? | |
422 | return; | |
423 | } | |
424 | ||
425 | IDirectDraw *pDD; | |
426 | HRESULT hr = (*gs_DirectDrawCreate)(&dpyInfo.m_guid, &pDD, NULL); | |
427 | ||
428 | if ( FAILED(hr) || !pDD ) | |
429 | { | |
430 | // what to do?? | |
431 | wxLogApiError(_T("DirectDrawCreate"), hr); | |
432 | } | |
433 | else // got IDirectDraw, we want IDirectDraw2 | |
434 | { | |
435 | hr = pDD->QueryInterface(wxIID_IDirectDraw2, (void **)&pDD2); | |
436 | if ( FAILED(hr) || !pDD2 ) | |
437 | { | |
438 | wxLogApiError(_T("IDirectDraw::QueryInterface(IDD2)"), hr); | |
439 | } | |
440 | ||
441 | pDD->Release(); | |
442 | } | |
443 | } | |
444 | //else: DirectDraw object corresponding to our display already exists | |
445 | ||
446 | // increment its ref count to account for Release() in dtor | |
447 | // | |
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 | |
450 | // all the time | |
451 | pDD2->AddRef(); | |
452 | } | |
453 | } | |
454 | ||
455 | wxDisplay::~wxDisplay() | |
456 | { | |
457 | wxDisplayInfo& dpyInfo = (*gs_displays)[m_index]; | |
458 | ||
459 | LPDIRECTDRAW2& pDD2 = dpyInfo.m_pDD2; | |
460 | if ( pDD2 ) | |
461 | { | |
462 | pDD2->Release(); | |
463 | } | |
464 | } | |
465 | ||
466 | // ---------------------------------------------------------------------------- | |
467 | // wxDisplay simple accessors | |
468 | // ---------------------------------------------------------------------------- | |
469 | ||
470 | bool wxDisplay::IsOk() const | |
471 | { | |
472 | return m_index < GetCount() && | |
473 | (!gs_useDirectX || (*gs_displays)[m_index].m_pDD2); | |
474 | } | |
475 | ||
476 | wxRect wxDisplay::GetGeometry() const | |
477 | { | |
478 | wxDisplayInfo& dpyInfo = (*gs_displays)[m_index]; | |
479 | wxRect& rect = dpyInfo.m_rect; | |
480 | if ( !rect.width ) | |
481 | { | |
482 | MONITORINFO monInfo; | |
483 | wxZeroMemory(monInfo); | |
484 | monInfo.cbSize = sizeof(monInfo); | |
485 | ||
486 | if ( !::GetMonitorInfo(dpyInfo.m_hmon, &monInfo) ) | |
487 | { | |
488 | wxLogLastError(_T("GetMonitorInfo")); | |
489 | } | |
490 | else | |
491 | { | |
492 | wxCopyRECTToRect(monInfo.rcMonitor, rect); | |
493 | } | |
494 | } | |
495 | ||
496 | return rect; | |
497 | } | |
498 | ||
499 | wxString wxDisplay::GetName() const | |
500 | { | |
501 | wxDisplayInfo& dpyInfo = (*gs_displays)[m_index]; | |
502 | if ( dpyInfo.m_devName.empty() ) | |
503 | { | |
504 | MONITORINFOEX monInfo; | |
505 | wxZeroMemory(monInfo); | |
506 | monInfo.cbSize = sizeof(monInfo); | |
507 | ||
508 | if ( !::GetMonitorInfo(dpyInfo.m_hmon, &monInfo) ) | |
509 | { | |
510 | wxLogLastError(_T("GetMonitorInfo")); | |
511 | } | |
512 | else | |
513 | { | |
514 | dpyInfo.m_devName = monInfo.szDevice; | |
515 | } | |
516 | } | |
517 | ||
518 | return dpyInfo.m_devName; | |
519 | } | |
520 | ||
521 | wxString wxDisplay::GetNameForEnumSettings() const | |
522 | { | |
523 | int major, minor; | |
524 | const bool isWin95 = wxGetOsVersion(&major, &minor) == wxWIN95 && | |
525 | major == 4 && minor == 0; | |
526 | ||
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 | |
530 | wxString name; | |
531 | if ( !isWin95 ) | |
532 | name = GetName(); | |
533 | ||
534 | return name; | |
535 | } | |
536 | ||
537 | // ---------------------------------------------------------------------------- | |
538 | // video modes enumeration | |
539 | // ---------------------------------------------------------------------------- | |
540 | ||
541 | wxArrayVideoModes | |
542 | wxDisplay::DoGetModesDirectX(const wxVideoMode& WXUNUSED(modeMatch)) const | |
543 | { | |
544 | wxArrayVideoModes modes; | |
545 | ||
546 | IDirectDraw2 *pDD = (*gs_displays)[m_index].m_pDD2; | |
547 | ||
548 | if ( pDD ) | |
549 | { | |
550 | HRESULT hr = pDD->EnumDisplayModes | |
551 | ( | |
552 | DDEDM_REFRESHRATES, | |
553 | NULL, // all modes (TODO: use modeMatch!) | |
554 | &modes, // callback parameter | |
555 | wxDDEnumModesCallback | |
556 | ); | |
557 | ||
558 | if ( FAILED(hr) ) | |
559 | { | |
560 | wxLogApiError(_T("IDirectDraw::EnumDisplayModes"), hr); | |
561 | } | |
562 | } | |
563 | ||
564 | return modes; | |
565 | } | |
566 | ||
567 | wxArrayVideoModes | |
568 | wxDisplay::DoGetModesWindows(const wxVideoMode& modeMatch) const | |
569 | { | |
570 | wxArrayVideoModes modes; | |
571 | ||
572 | const wxString name = GetNameForEnumSettings(); | |
573 | ||
574 | const wxChar * const deviceName = name.empty() ? NULL : name.c_str(); | |
575 | ||
576 | DEVMODE dm; | |
577 | for ( int iModeNum = 0; | |
578 | ::EnumDisplaySettings(deviceName, iModeNum, &dm); | |
579 | iModeNum++ ) | |
580 | { | |
581 | const wxVideoMode mode = ConvertToVideoMode(dm); | |
582 | if ( mode.Matches(modeMatch) ) | |
583 | { | |
584 | modes.Add(mode); | |
585 | } | |
586 | } | |
587 | ||
588 | return modes; | |
589 | } | |
590 | ||
591 | wxArrayVideoModes wxDisplay::GetModes(const wxVideoMode& modeMatch) const | |
592 | { | |
593 | return gs_useDirectX ? DoGetModesDirectX(modeMatch) | |
594 | : DoGetModesWindows(modeMatch); | |
595 | } | |
596 | ||
597 | wxVideoMode wxDisplay::GetCurrentMode() const | |
598 | { | |
599 | wxVideoMode mode; | |
600 | ||
601 | const wxString name = GetNameForEnumSettings(); | |
602 | ||
603 | DEVMODE dm; | |
604 | if ( !::EnumDisplaySettings(name.empty() ? NULL : name.c_str(), | |
605 | ENUM_CURRENT_SETTINGS, | |
606 | &dm) ) | |
607 | { | |
608 | wxLogLastError(_T("EnumDisplaySettings(ENUM_CURRENT_SETTINGS)")); | |
609 | } | |
610 | else | |
611 | { | |
612 | mode = ConvertToVideoMode(dm); | |
613 | } | |
614 | ||
615 | return mode; | |
616 | } | |
617 | ||
618 | // ---------------------------------------------------------------------------- | |
619 | // video mode switching | |
620 | // ---------------------------------------------------------------------------- | |
621 | ||
622 | bool wxDisplay::DoChangeModeDirectX(const wxVideoMode& mode) | |
623 | { | |
624 | IDirectDraw2 *pDD = (*gs_displays)[m_index].m_pDD2; | |
625 | if ( !pDD ) | |
626 | return false; | |
627 | ||
628 | wxWindow *winTop = wxTheApp->GetTopWindow(); | |
629 | wxCHECK_MSG( winTop, false, _T("top level window required for DirectX") ); | |
630 | ||
631 | HRESULT hr = pDD->SetCooperativeLevel | |
632 | ( | |
633 | GetHwndOf(winTop), | |
634 | DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN | |
635 | ); | |
636 | if ( FAILED(hr) ) | |
637 | { | |
638 | wxLogApiError(_T("IDirectDraw::SetCooperativeLevel"), hr); | |
639 | ||
640 | return false; | |
641 | } | |
642 | ||
643 | hr = pDD->SetDisplayMode(mode.w, mode.h, mode.bpp, mode.refresh, 0); | |
644 | if ( FAILED(hr) ) | |
645 | { | |
646 | wxLogApiError(_T("IDirectDraw::SetDisplayMode"), hr); | |
647 | ||
648 | return false; | |
649 | } | |
650 | ||
651 | ||
652 | return true; | |
653 | } | |
654 | ||
655 | bool wxDisplay::DoChangeModeWindows(const wxVideoMode& mode) | |
656 | { | |
657 | // prepare ChangeDisplaySettingsEx() parameters | |
658 | DEVMODE dm, | |
659 | *pDevMode; | |
660 | int flags; | |
661 | ||
662 | if ( mode == wxDefaultVideoMode ) | |
663 | { | |
664 | // reset the video mode to default | |
665 | pDevMode = NULL; | |
666 | flags = 0; | |
667 | } | |
668 | else // change to the given mode | |
669 | { | |
670 | wxCHECK_MSG( mode.w && mode.h, false, | |
671 | _T("at least the width and height must be specified") ); | |
672 | ||
673 | wxZeroMemory(dm); | |
674 | dm.dmSize = sizeof(dm); | |
675 | dm.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT; | |
676 | dm.dmPelsWidth = mode.w; | |
677 | dm.dmPelsHeight = mode.h; | |
678 | ||
679 | if ( mode.bpp ) | |
680 | { | |
681 | dm.dmFields |= DM_BITSPERPEL; | |
682 | dm.dmBitsPerPel = mode.bpp; | |
683 | } | |
684 | ||
685 | if ( mode.refresh ) | |
686 | { | |
687 | dm.dmFields |= DM_DISPLAYFREQUENCY; | |
688 | dm.dmDisplayFrequency = mode.refresh; | |
689 | } | |
690 | ||
691 | pDevMode = &dm; | |
692 | ||
693 | flags = CDS_FULLSCREEN; | |
694 | } | |
695 | ||
696 | ||
697 | // get pointer to the function dynamically | |
698 | // | |
699 | // we're only called from the main thread, so it's ok to use static | |
700 | // variable | |
701 | static ChangeDisplaySettingsEx_t pfnChangeDisplaySettingsEx = NULL; | |
702 | if ( !pfnChangeDisplaySettingsEx ) | |
703 | { | |
704 | wxDynamicLibrary dllUser32(_T("user32.dll")); | |
705 | if ( dllUser32.IsLoaded() ) | |
706 | { | |
707 | pfnChangeDisplaySettingsEx = (ChangeDisplaySettingsEx_t) | |
708 | dllUser32.GetSymbol(WINFUNC(ChangeDisplaySettingsEx)); | |
709 | } | |
710 | //else: huh, no user32.dll?? | |
711 | ||
712 | if ( !pfnChangeDisplaySettingsEx ) | |
713 | { | |
714 | // we must be under Win95 and so there is no multiple monitors | |
715 | // support anyhow | |
716 | pfnChangeDisplaySettingsEx = ChangeDisplaySettingsExForWin95; | |
717 | } | |
718 | } | |
719 | ||
720 | // do change the mode | |
721 | switch ( pfnChangeDisplaySettingsEx | |
722 | ( | |
723 | GetName(), // display name | |
724 | pDevMode, // dev mode or NULL to reset | |
725 | NULL, // reserved | |
726 | flags, | |
727 | NULL // pointer to video parameters (not used) | |
728 | ) ) | |
729 | { | |
730 | case DISP_CHANGE_SUCCESSFUL: | |
731 | // ok | |
732 | { | |
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()) | |
739 | { | |
740 | wxVideoMode current = GetCurrentMode(); | |
741 | frameTop->SetClientSize(current.w, current.h); | |
742 | } | |
743 | } | |
744 | return true; | |
745 | ||
746 | case DISP_CHANGE_BADMODE: | |
747 | // don't complain about this, this is the only "expected" error | |
748 | break; | |
749 | ||
750 | default: | |
751 | wxFAIL_MSG( _T("unexpected ChangeDisplaySettingsEx() return value") ); | |
752 | } | |
753 | ||
754 | return false; | |
755 | } | |
756 | ||
757 | bool wxDisplay::ChangeMode(const wxVideoMode& mode) | |
758 | { | |
759 | return gs_useDirectX ? DoChangeModeDirectX(mode) | |
760 | : DoChangeModeWindows(mode); | |
761 | } | |
762 | ||
763 | #endif // wxUSE_DISPLAY | |
764 |