]>
Commit | Line | Data |
---|---|---|
a536e411 JS |
1 | ///////////////////////////////////////////////////////////////////////////// |
2 | // Name: display.cpp | |
3 | // Purpose: MSW Implementation of wxDisplay class | |
4 | // Author: Royce Mitchell III | |
06efac1f | 5 | // Modified by: VZ (resolutions enumeration/change support, DirectDraw, ...) |
a536e411 JS |
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 | ||
14f355c2 | 20 | #if defined(__GNUG__) && !defined(NO_GCC_PRAGMA) |
a536e411 JS |
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 | ||
8585e2b5 VZ |
37 | #include "wx/dynload.h" |
38 | ||
a536e411 JS |
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 | |
a536e411 | 43 | #define COMPILE_MULTIMON_STUBS |
8585e2b5 VZ |
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 | |
06efac1f | 51 | // as (m)any standard header(s), this one doesn't compile without warnings |
8585e2b5 VZ |
52 | // with VC++ 6 <sigh> |
53 | #pragma warning(disable:4706) | |
a536e411 JS |
54 | #endif |
55 | ||
8585e2b5 | 56 | #include <multimon.h> |
a536e411 | 57 | |
8585e2b5 VZ |
58 | #ifdef _MSC_VER |
59 | #pragma warning(default:4706) | |
60 | #endif | |
a536e411 | 61 | |
06efac1f VZ |
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 | ||
8585e2b5 | 73 | #ifdef _UNICODE |
06efac1f | 74 | #define WINFUNC(x) _T(#x) L"W" |
8585e2b5 VZ |
75 | #else |
76 | #define WINFUNC(x) #x "A" | |
77 | #endif | |
a536e411 JS |
78 | |
79 | // ---------------------------------------------------------------------------- | |
06efac1f | 80 | // typedefs for dynamically loaded Windows functions |
a536e411 JS |
81 | // ---------------------------------------------------------------------------- |
82 | ||
8585e2b5 VZ |
83 | typedef LONG (WINAPI *ChangeDisplaySettingsEx_t)(LPCTSTR lpszDeviceName, |
84 | LPDEVMODE lpDevMode, | |
85 | HWND hwnd, | |
86 | DWORD dwFlags, | |
87 | LPVOID lParam); | |
a536e411 | 88 | |
06efac1f VZ |
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 | ||
8585e2b5 VZ |
103 | // ---------------------------------------------------------------------------- |
104 | // private classes | |
105 | // ---------------------------------------------------------------------------- | |
a536e411 | 106 | |
8585e2b5 | 107 | class wxDisplayInfo |
a536e411 JS |
108 | { |
109 | public: | |
8585e2b5 VZ |
110 | // handle of this monitor used by MonitorXXX() functions, never NULL |
111 | HMONITOR m_hmon; | |
112 | ||
06efac1f VZ |
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 | ||
8585e2b5 | 119 | // the entire area of this monitor in virtual screen coordinates |
a536e411 | 120 | wxRect m_rect; |
8585e2b5 VZ |
121 | |
122 | // the display device name for this monitor, empty initially and retrieved | |
123 | // on demand by DoGetName() | |
124 | wxString m_devName; | |
125 | ||
06efac1f VZ |
126 | wxDisplayInfo() { m_hmon = NULL; m_pDD2 = NULL; } |
127 | ~wxDisplayInfo() { if ( m_pDD2 ) m_pDD2->Release(); } | |
a536e411 JS |
128 | }; |
129 | ||
8585e2b5 VZ |
130 | WX_DECLARE_OBJARRAY(wxDisplayInfo, wxDisplayInfoArray); |
131 | #include "wx/arrimpl.cpp" | |
132 | WX_DEFINE_OBJARRAY(wxDisplayInfoArray); | |
a536e411 | 133 | |
06efac1f | 134 | // this module is used to cleanup gs_displays array |
8585e2b5 VZ |
135 | class wxDisplayModule : public wxModule |
136 | { | |
137 | public: | |
06efac1f | 138 | virtual bool OnInit() { return true; } |
8585e2b5 VZ |
139 | virtual void OnExit(); |
140 | ||
141 | DECLARE_DYNAMIC_CLASS(wxDisplayModule) | |
142 | }; | |
143 | ||
144 | IMPLEMENT_DYNAMIC_CLASS(wxDisplayModule, wxModule) | |
a536e411 | 145 | |
06efac1f VZ |
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 | ||
a536e411 JS |
160 | // =========================================================================== |
161 | // implementation | |
162 | // =========================================================================== | |
163 | ||
8585e2b5 | 164 | // ---------------------------------------------------------------------------- |
06efac1f | 165 | // callbacks for monitor/modes enumeration stuff |
8585e2b5 VZ |
166 | // ---------------------------------------------------------------------------- |
167 | ||
168 | static BOOL CALLBACK wxmswMonitorEnumProc ( | |
2eb10e2a VZ |
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) | |
a536e411 JS |
173 | ) |
174 | { | |
06efac1f | 175 | wxDisplayInfo *info = new wxDisplayInfo(); |
8585e2b5 VZ |
176 | |
177 | // we need hMonitor to be able to map display id to it which is needed for | |
178 | // MonitorXXX() functions, in particular MonitorFromPoint() | |
a536e411 | 179 | info->m_hmon = hMonitor; |
8585e2b5 VZ |
180 | |
181 | // we also store the display geometry | |
a536e411 JS |
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 ); | |
8585e2b5 | 186 | |
a536e411 | 187 | // now add this monitor to the array |
06efac1f | 188 | gs_displays->Add(info); |
a536e411 | 189 | |
8585e2b5 | 190 | // continue the enumeration |
06efac1f | 191 | return true; |
a536e411 JS |
192 | } |
193 | ||
06efac1f VZ |
194 | BOOL PASCAL |
195 | wxDDEnumExCallback(GUID *pGuid, | |
2eb10e2a | 196 | LPTSTR WXUNUSED(driverDescription), |
06efac1f | 197 | LPTSTR driverName, |
2eb10e2a | 198 | LPVOID WXUNUSED(lpContext), |
06efac1f | 199 | HMONITOR hmon) |
a536e411 | 200 | { |
06efac1f VZ |
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); | |
a536e411 | 245 | |
06efac1f | 246 | wxDynamicLibrary dllDX(_T("ddraw.dll")); |
a536e411 | 247 | |
06efac1f VZ |
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 | { | |
8585e2b5 VZ |
281 | // enumerate all displays |
282 | if ( !::EnumDisplayMonitors(NULL, NULL, wxmswMonitorEnumProc, 0) ) | |
a536e411 | 283 | { |
8585e2b5 VZ |
284 | wxLogLastError(wxT("EnumDisplayMonitors")); |
285 | ||
06efac1f | 286 | // TODO: still create at least one (valid) entry in gs_displays for the |
8585e2b5 | 287 | // primary display! |
a536e411 | 288 | } |
8585e2b5 | 289 | } |
a536e411 | 290 | |
06efac1f VZ |
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 | ||
82eba826 | 300 | if ( !gs_useDirectX || !DoInitDirectX() ) |
06efac1f VZ |
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 | ||
8585e2b5 VZ |
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 | } | |
a536e411 | 320 | |
8585e2b5 VZ |
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 | } | |
a536e411 | 330 | |
8585e2b5 VZ |
331 | // ---------------------------------------------------------------------------- |
332 | // wxDisplayModule | |
333 | // ---------------------------------------------------------------------------- | |
a536e411 | 334 | |
8585e2b5 VZ |
335 | void wxDisplayModule::OnExit() |
336 | { | |
06efac1f | 337 | delete gs_displays; |
8585e2b5 | 338 | } |
a536e411 | 339 | |
8585e2b5 VZ |
340 | // --------------------------------------------------------------------------- |
341 | // wxDisplay | |
342 | // --------------------------------------------------------------------------- | |
a536e411 | 343 | |
06efac1f VZ |
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 | ||
8585e2b5 VZ |
352 | // helper of GetFromPoint() and GetFromWindow() |
353 | static int DisplayFromHMONITOR(HMONITOR hmon) | |
354 | { | |
355 | if ( hmon ) | |
356 | { | |
357 | const size_t count = wxDisplay::GetCount(); | |
a536e411 | 358 | |
8585e2b5 | 359 | for ( size_t n = 0; n < count; n++ ) |
a536e411 | 360 | { |
06efac1f | 361 | if ( hmon == (*gs_displays)[n].m_hmon ) |
8585e2b5 | 362 | return n; |
a536e411 | 363 | } |
a536e411 | 364 | } |
a536e411 | 365 | |
8585e2b5 | 366 | return wxNOT_FOUND; |
a536e411 JS |
367 | } |
368 | ||
8585e2b5 | 369 | /* static */ |
a536e411 JS |
370 | size_t wxDisplayBase::GetCount() |
371 | { | |
8585e2b5 VZ |
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 | |
06efac1f | 376 | wxASSERT_MSG( gs_displays->GetCount() == (size_t)::GetSystemMetrics(SM_CMONITORS), |
8585e2b5 VZ |
377 | _T("So how many displays does this system have?") ); |
378 | ||
06efac1f | 379 | return gs_displays->GetCount(); |
a536e411 JS |
380 | } |
381 | ||
8585e2b5 | 382 | /* static */ |
a536e411 JS |
383 | int wxDisplayBase::GetFromPoint ( const wxPoint& pt ) |
384 | { | |
385 | POINT pt2; | |
386 | pt2.x = pt.x; | |
387 | pt2.y = pt.y; | |
388 | ||
8585e2b5 VZ |
389 | return DisplayFromHMONITOR(::MonitorFromPoint(pt2, MONITOR_DEFAULTTONULL)); |
390 | } | |
a536e411 | 391 | |
8585e2b5 VZ |
392 | /* static */ |
393 | int wxDisplayBase::GetFromWindow(wxWindow *window) | |
394 | { | |
395 | return DisplayFromHMONITOR | |
396 | ( | |
397 | ::MonitorFromWindow(GetHwndOf(window), MONITOR_DEFAULTTONULL) | |
398 | ); | |
399 | } | |
400 | ||
06efac1f VZ |
401 | // ---------------------------------------------------------------------------- |
402 | // wxDisplay ctor/dtor | |
403 | // ---------------------------------------------------------------------------- | |
404 | ||
8585e2b5 VZ |
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(); | |
06efac1f VZ |
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); | |
8585e2b5 VZ |
474 | } |
475 | ||
476 | wxRect wxDisplay::GetGeometry() const | |
477 | { | |
06efac1f VZ |
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; | |
8585e2b5 VZ |
497 | } |
498 | ||
499 | wxString wxDisplay::GetName() const | |
500 | { | |
06efac1f | 501 | wxDisplayInfo& dpyInfo = (*gs_displays)[m_index]; |
8585e2b5 | 502 | if ( dpyInfo.m_devName.empty() ) |
a536e411 | 503 | { |
8585e2b5 VZ |
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 | } | |
a536e411 JS |
516 | } |
517 | ||
8585e2b5 | 518 | return dpyInfo.m_devName; |
a536e411 JS |
519 | } |
520 | ||
8585e2b5 | 521 | wxString wxDisplay::GetNameForEnumSettings() const |
a536e411 | 522 | { |
8585e2b5 VZ |
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; | |
a536e411 JS |
535 | } |
536 | ||
06efac1f VZ |
537 | // ---------------------------------------------------------------------------- |
538 | // video modes enumeration | |
539 | // ---------------------------------------------------------------------------- | |
540 | ||
541 | wxArrayVideoModes | |
2eb10e2a | 542 | wxDisplay::DoGetModesDirectX(const wxVideoMode& WXUNUSED(modeMatch)) const |
06efac1f VZ |
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 | |
a536e411 | 569 | { |
8585e2b5 VZ |
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; | |
a536e411 JS |
589 | } |
590 | ||
06efac1f VZ |
591 | wxArrayVideoModes wxDisplay::GetModes(const wxVideoMode& modeMatch) const |
592 | { | |
593 | return gs_useDirectX ? DoGetModesDirectX(modeMatch) | |
594 | : DoGetModesWindows(modeMatch); | |
595 | } | |
596 | ||
8585e2b5 | 597 | wxVideoMode wxDisplay::GetCurrentMode() const |
a536e411 | 598 | { |
8585e2b5 VZ |
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; | |
a536e411 JS |
616 | } |
617 | ||
06efac1f VZ |
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) | |
a536e411 | 656 | { |
8585e2b5 VZ |
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 | { | |
06efac1f | 670 | wxCHECK_MSG( mode.w && mode.h, false, |
8585e2b5 VZ |
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 | |
920b9675 JS |
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 | } | |
06efac1f | 744 | return true; |
8585e2b5 VZ |
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 | ||
06efac1f VZ |
754 | return false; |
755 | } | |
756 | ||
757 | bool wxDisplay::ChangeMode(const wxVideoMode& mode) | |
758 | { | |
759 | return gs_useDirectX ? DoChangeModeDirectX(mode) | |
760 | : DoChangeModeWindows(mode); | |
a536e411 JS |
761 | } |
762 | ||
8585e2b5 VZ |
763 | #endif // wxUSE_DISPLAY |
764 |