]> git.saurik.com Git - wxWidgets.git/blob - src/mac/carbon/display.cpp
moving and slightly extending the comment on CGState
[wxWidgets.git] / src / mac / carbon / display.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: display.cpp
3 // Purpose: Mac implementation of wxDisplay class
4 // Author: Ryan Norton & Brian Victor
5 // Modified by: Royce Mitchell III
6 // Created: 06/21/02
7 // RCS-ID: $Id$
8 // Copyright: (c) wxWidgets team
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
11
12 #if defined(__GNUG__) && !defined(NO_GCC_PRAGMA)
13 #pragma implementation "display.h"
14 #endif
15
16 // For compilers that support precompilation, includes "wx.h".
17 #include "wx/wxprec.h"
18
19 #ifdef __BORLANDC__
20 #pragma hdrstop
21 #endif
22
23 #if wxUSE_DISPLAY
24
25 #ifndef WX_PRECOMP
26 #include "wx/dynarray.h"
27 #include "wx/log.h"
28 #include "wx/msgdlg.h"
29 #endif
30
31 #ifdef __DARWIN__
32 #include <Carbon/Carbon.h>
33 #else
34 #include <Gestalt.h>
35 #include <Displays.h>
36 #include <Quickdraw.h>
37 #include <Video.h> //for VDSwitchInfoRec
38 #include <FixMath.h>
39 #endif
40
41 #include "wx/display.h"
42 #include "wx/gdicmn.h"
43 #include "wx/string.h"
44
45 // ----------------------------------------------------------------------------
46 // private classes
47 // ----------------------------------------------------------------------------
48
49 #ifdef __WXMAC_OSX__
50
51 class wxDisplayMacPriv
52 {
53 public:
54 CGDirectDisplayID m_id;
55 };
56
57 size_t wxDisplayBase::GetCount()
58 {
59 CGDisplayCount count;
60 #ifdef __WXDEBUG__
61 CGDisplayErr err =
62 #endif
63 CGGetActiveDisplayList(0, NULL, &count);
64
65 wxASSERT(err == CGDisplayNoErr);
66 return count;
67 }
68
69 int wxDisplayBase::GetFromPoint(const wxPoint &p)
70 {
71 CGPoint thePoint = {(float)p.x, (float)p.y};
72 CGDirectDisplayID theID;
73 CGDisplayCount theCount;
74 CGDisplayErr err = CGGetDisplaysWithPoint(thePoint, 1, &theID, &theCount);
75 wxASSERT(err == CGDisplayNoErr);
76 int nWhich = -1;
77
78 if (theCount)
79 {
80 theCount = GetCount();
81 CGDirectDisplayID* theIDs = new CGDirectDisplayID[theCount];
82 err = CGGetActiveDisplayList(theCount, theIDs, &theCount);
83 wxASSERT(err == CGDisplayNoErr);
84
85 for(nWhich = 0; nWhich < (int) theCount; ++nWhich)
86 {
87 if(theIDs[nWhich] == theID)
88 break;
89 }
90
91 delete[] theIDs;
92
93 if(nWhich == (int) theCount)
94 {
95 wxFAIL_MSG(wxT("Failed to find display in display list"));
96 nWhich = -1;
97 }
98 }
99
100 return nWhich;
101 }//CFUserNotification[NSBundle bundleForClass:[self class]]
102
103 wxDisplay::wxDisplay(size_t index) : wxDisplayBase ( index ) ,
104 m_priv ( new wxDisplayMacPriv() )
105 {
106 CGDisplayCount theCount = GetCount();
107 CGDirectDisplayID* theIDs = new CGDirectDisplayID[theCount];
108 #ifdef __WXDEBUG__
109 CGDisplayErr err =
110 #endif
111 CGGetActiveDisplayList(theCount, theIDs, &theCount);
112
113 wxASSERT(err == CGDisplayNoErr);
114 wxASSERT(index < theCount);
115
116 m_priv->m_id = theIDs[index];
117
118 delete[] theIDs;
119 }
120
121 wxRect wxDisplay::GetGeometry() const
122 {
123 CGRect theRect = CGDisplayBounds(m_priv->m_id);
124 return wxRect( (int)theRect.origin.x,
125 (int)theRect.origin.y,
126 (int)theRect.size.width,
127 (int)theRect.size.height ); //floats
128 }
129
130 int wxDisplay::GetDepth() const
131 {
132 return (int) CGDisplayBitsPerPixel(m_priv->m_id); //size_t
133 }
134
135 wxString wxDisplay::GetName() const
136 {
137 // Macs don't name their displays...
138 return wxEmptyString;
139 }
140
141 static int wxCFDictKeyToInt( CFDictionaryRef desc, CFStringRef key )
142 {
143 CFNumberRef value;
144 int num = 0;
145
146 if ( (value = (CFNumberRef) CFDictionaryGetValue(desc, key)) == NULL )
147 return 0;
148 CFNumberGetValue(value, kCFNumberIntType, &num);
149 return num;
150 }
151
152 wxArrayVideoModes
153 wxDisplay::GetModes(const wxVideoMode& mode) const
154 {
155 wxArrayVideoModes Modes;
156
157 CFArrayRef theArray = CGDisplayAvailableModes(m_priv->m_id);
158
159 for(CFIndex i = 0; i < CFArrayGetCount(theArray); ++i)
160 {
161 CFDictionaryRef theValue = (CFDictionaryRef) CFArrayGetValueAtIndex(theArray, i);
162
163 wxVideoMode theMode(wxCFDictKeyToInt(theValue, kCGDisplayWidth),
164 wxCFDictKeyToInt(theValue, kCGDisplayHeight),
165 wxCFDictKeyToInt(theValue, kCGDisplayBitsPerPixel),
166 wxCFDictKeyToInt(theValue, kCGDisplayRefreshRate));
167
168 if (theMode.Matches(mode))
169 Modes.Add(theMode);
170 }
171
172 return Modes;
173 }
174
175 wxVideoMode wxDisplay::GetCurrentMode() const
176 {
177 CFDictionaryRef theValue = CGDisplayCurrentMode (m_priv->m_id);
178
179 return wxVideoMode(wxCFDictKeyToInt(theValue, kCGDisplayWidth),
180 wxCFDictKeyToInt(theValue, kCGDisplayHeight),
181 wxCFDictKeyToInt(theValue, kCGDisplayBitsPerPixel),
182 wxCFDictKeyToInt(theValue, kCGDisplayRefreshRate));
183 }
184
185 bool wxDisplay::ChangeMode(const wxVideoMode& mode)
186 {
187 //Changing to default mode (wxDefualtVideoMode) doesn't
188 //work because we don't have access to the system's 'scrn'
189 //resource which holds the user's mode which the system
190 //will return to after this app is done
191 boolean_t bExactMatch;
192 CFDictionaryRef theCGMode = CGDisplayBestModeForParametersAndRefreshRate (
193 m_priv->m_id,
194 (size_t)mode.bpp,
195 (size_t)mode.w,
196 (size_t)mode.h,
197 (double)mode.refresh,
198 &bExactMatch);
199
200 bool bOK = bExactMatch;
201
202 if(bOK)
203 bOK = CGDisplaySwitchToMode(m_priv->m_id, theCGMode) == CGDisplayNoErr;
204
205 return bOK;
206 }
207
208 wxDisplay::~wxDisplay()
209 {
210 if ( m_priv )
211 {
212 delete m_priv;
213 m_priv = 0;
214 }
215 }
216
217 #else
218
219 class wxDisplayMacPriv
220 {
221 public:
222 GDHandle m_hndl;
223 };
224
225 size_t wxDisplayBase::GetCount()
226 {
227 GDHandle hndl;
228 size_t num = 0;
229 hndl = DMGetFirstScreenDevice(true);
230 while(hndl)
231 {
232 num++;
233 hndl = DMGetNextScreenDevice(hndl, true);
234 }
235 return num;
236 }
237
238 int wxDisplayBase::GetFromPoint(const wxPoint &p)
239 {
240 GDHandle hndl;
241 size_t num = 0;
242 hndl = DMGetFirstScreenDevice(true);
243 while(hndl)
244 {
245 Rect screenrect = (*hndl)->gdRect;
246 if (p.x >= screenrect.left &&
247 p.x <= screenrect.right &&
248 p.y >= screenrect.top &&
249 p.y <= screenrect.bottom)
250 {
251 return num;
252 }
253 num++;
254 hndl = DMGetNextScreenDevice(hndl, true);
255 }
256 return -1;
257 }
258
259 wxDisplay::wxDisplay(size_t index) : wxDisplayBase ( index ),
260 m_priv ( new wxDisplayMacPriv() )
261 {
262 GDHandle hndl;
263 hndl = DMGetFirstScreenDevice(true);
264 m_priv->m_hndl = NULL;
265 while(hndl)
266 {
267 if (index == 0)
268 {
269 m_priv->m_hndl = hndl;
270 }
271 index--;
272 hndl = DMGetNextScreenDevice(hndl, true);
273 }
274 }
275
276 wxRect wxDisplay::GetGeometry() const
277 {
278 if (!(m_priv)) return wxRect(0, 0, 0, 0);
279 if (!(m_priv->m_hndl)) return wxRect(0, 0, 0, 0);
280 Rect screenrect = (*(m_priv->m_hndl))->gdRect;
281 return wxRect( screenrect.left, screenrect.top,
282 screenrect.right - screenrect.left, screenrect.bottom - screenrect.top);
283 }
284
285 int wxDisplay::GetDepth() const
286 {
287 if (!(m_priv)) return 0;
288 if (!(m_priv->m_hndl)) return 0;
289
290 // This cryptic looking code is based on Apple's sample code:
291 // http://developer.apple.com/samplecode/Sample_Code/Graphics_2D/GDevVideo/Gen.cp.htm
292
293 //RN - according to the docs
294 //gdPMap is a bitmap-type representation of the GDevice, and all
295 //0x0000FFFF does is get the lower 16 bits of pixelSize. However,
296 //since pixelSize is only 16 bits (a short)...
297 return ((*(*(m_priv->m_hndl))->gdPMap)->pixelSize) & 0x0000FFFF;
298 }
299
300 wxString wxDisplay::GetName() const
301 {
302 // Macs don't name their displays...
303 return wxEmptyString;
304 }
305
306 struct DMModeIteratorRec
307 {
308 wxArrayVideoModes* pModes;
309 const wxVideoMode* pMatchMode;
310 };
311
312 pascal void DMModeListIteratorProc ( void* pData,
313 DMListIndexType nIndex,
314 DMDisplayModeListEntryPtr pInfo)
315 {
316 DMModeIteratorRec* pInfoData = (DMModeIteratorRec*) pData;
317
318 //Note that in testing the refresh rate is always 0 on my ibook - RN
319 int refresh = (int) Fix2Long(pInfo->displayModeResolutionInfo->csRefreshRate);
320
321 for(unsigned long i = 0; i < pInfo->displayModeDepthBlockInfo->depthBlockCount; ++i)
322 {
323 #define pDBI pInfo->displayModeDepthBlockInfo->depthVPBlock[i].depthVPBlock
324
325 if (wxVideoMode((int) pInfo->displayModeResolutionInfo->csHorizontalPixels,
326 (int) pInfo->displayModeResolutionInfo->csVerticalLines,
327 (int) pDBI->vpPixelSize,
328 refresh).Matches(*pInfoData->pMatchMode) )
329 {
330 pInfoData->pModes->Add(wxVideoMode((int) pInfo->displayModeResolutionInfo->csHorizontalPixels,
331 (int) pInfo->displayModeResolutionInfo->csVerticalLines,
332 (int) pDBI->vpPixelSize,
333 refresh));
334 }
335 #undef pDBI
336 }
337 }
338
339 struct DMModeInfoRec
340 {
341 const wxVideoMode* pMode;
342 VDSwitchInfoRec sMode;
343 bool bMatched;
344 };
345
346 pascal void DMModeInfoProc ( void* pData,
347 DMListIndexType nIndex,
348 DMDisplayModeListEntryPtr pInfo)
349 {
350 DMModeInfoRec* pInfoData = (DMModeInfoRec*) pData;
351 Fixed refresh = Long2Fix(pInfoData->pMode->refresh);
352
353 for(unsigned long i = 0; i < pInfo->displayModeDepthBlockInfo->depthBlockCount; ++i)
354 {
355 #define pDBI pInfo->displayModeDepthBlockInfo->depthVPBlock[i].depthVPBlock
356 if (pInfoData->pMode->w == (int&) pInfo->displayModeResolutionInfo->csHorizontalPixels &&
357 pInfoData->pMode->h == (int&) pInfo->displayModeResolutionInfo->csVerticalLines &&
358 pInfoData->pMode->bpp == (int) pDBI->vpPixelSize &&
359 refresh == pInfo->displayModeResolutionInfo->csRefreshRate)
360 {
361 memcpy(&pInfoData->sMode, pInfo->displayModeDepthBlockInfo->depthVPBlock[i].depthSwitchInfo,
362 sizeof(VDSwitchInfoRec));
363 pInfoData->sMode.csMode = pDBI->vpPixelSize;
364 pInfoData->bMatched = true;
365 break;
366 }
367 #undef pDBI
368 }
369 }
370
371 struct DMModeTransRec
372 {
373 wxVideoMode Mode;
374 const VDSwitchInfoRec* psMode;
375 bool bMatched;
376 };
377
378 pascal void DMModeTransProc ( void* pData,
379 DMListIndexType nIndex,
380 DMDisplayModeListEntryPtr pInfo)
381 {
382 DMModeTransRec* pInfoData = (DMModeTransRec*) pData;
383
384 for(unsigned long i = 0; i < pInfo->displayModeDepthBlockInfo->depthBlockCount; ++i)
385 {
386 #define pDBI pInfo->displayModeDepthBlockInfo->depthVPBlock[i].depthVPBlock
387 if (pInfoData->psMode->csData == pInfo->displayModeDepthBlockInfo->depthVPBlock[i].depthSwitchInfo->csData)
388 {
389 pInfoData->Mode = wxVideoMode((int) pInfo->displayModeResolutionInfo->csHorizontalPixels,
390 (int) pInfo->displayModeResolutionInfo->csVerticalLines,
391 (int) pDBI->vpPixelSize,
392 (int) Fix2Long(pInfo->displayModeResolutionInfo->csRefreshRate) );
393 pInfoData->bMatched = true;
394 break;
395 }
396 #undef pDBI
397 }
398 }
399
400 wxArrayVideoModes
401 wxDisplay::GetModes(const wxVideoMode& mode) const
402 {
403
404 wxArrayVideoModes Modes;
405
406 unsigned long dwDMVer;
407 Gestalt(gestaltDisplayMgrVers, (long*) &dwDMVer);
408
409 //Check DM version (for backward compatibility only - 7.5.3+ use 2.0)
410 if (dwDMVer >= 0x020000) //version 2?
411 {
412
413 DMListIndexType nNumModes;
414 DMListType pModes;
415 DMDisplayModeListIteratorUPP uppMLI;
416 DisplayIDType nDisplayID;
417 OSErr err;
418
419 err = DMGetDisplayIDByGDevice(m_priv->m_hndl, &nDisplayID, false);
420 wxASSERT(err == noErr);
421
422 //Create a new list...
423 err = DMNewDisplayModeList(nDisplayID, NULL, NULL, &nNumModes, &pModes);
424 wxASSERT_MSG(err == noErr, wxT("Could not create a new display mode list") );
425
426 uppMLI = NewDMDisplayModeListIteratorUPP(DMModeListIteratorProc);
427 wxASSERT(uppMLI);
428
429 DMModeIteratorRec sModeInfo;
430 sModeInfo.pModes = &Modes;
431 sModeInfo.pMatchMode = &mode;
432 for (DMListIndexType i = 0; i < nNumModes; ++i)
433 {
434 err = DMGetIndexedDisplayModeFromList(pModes, i, NULL, uppMLI, &sModeInfo);
435 wxASSERT(err == noErr);
436 }
437 DisposeDMDisplayModeListIteratorUPP(uppMLI);
438
439 err = DMDisposeList(pModes);
440 wxASSERT(err == noErr);
441 }
442 else //DM 1.0, 1.2, 1.x
443 {
444 wxLogSysError(wxString::Format(wxT("Display Manager Version %u Not Supported! Present? %s"),
445 (unsigned int) dwDMVer / 0x10000,
446 (dwDMVer & (1 << gestaltDisplayMgrPresent) ? wxT("Yes") : wxT("No")) )
447 );
448 }
449
450 return Modes;
451 }
452
453 wxVideoMode wxDisplay::GetCurrentMode() const
454 {
455 unsigned long dwDMVer;
456 wxVideoMode RetMode;
457
458 Gestalt(gestaltDisplayMgrVers, (long*) &dwDMVer);
459 //Check DM version (for backward compatibility only - 7.5.3+ use 2.0)
460 if (dwDMVer >= 0x020000) //version 2?
461 {
462 VDSwitchInfoRec sMode; //Note - csMode member also contains the bit depth
463 if (DMGetDisplayMode(m_priv->m_hndl, &sMode) == noErr)
464 {
465 DMListIndexType nNumModes;
466 DMListType pModes;
467 DMDisplayModeListIteratorUPP uppMLI;
468 DisplayIDType nDisplayID;
469 OSErr err;
470
471 err = DMGetDisplayIDByGDevice(m_priv->m_hndl, &nDisplayID, false);
472 wxASSERT(err == noErr);
473
474 //Create a new list...
475 err = DMNewDisplayModeList(nDisplayID, NULL, NULL, &nNumModes, &pModes);
476 wxASSERT_MSG(err == noErr, wxT("Could not create a new display mode list") );
477
478 uppMLI = NewDMDisplayModeListIteratorUPP(DMModeTransProc);
479 wxASSERT(uppMLI);
480
481 DMModeTransRec sModeInfo;
482 sModeInfo.bMatched = false;
483 sModeInfo.psMode = &sMode;
484 for (DMListIndexType i = 0; i < nNumModes; ++i)
485 {
486 err = DMGetIndexedDisplayModeFromList(pModes, i, NULL, uppMLI, &sModeInfo);
487 wxASSERT(err == noErr);
488
489 if ( sModeInfo.bMatched )
490 {
491 RetMode = sModeInfo.Mode;
492 break;
493 }
494 }
495
496 DisposeDMDisplayModeListIteratorUPP(uppMLI);
497
498 err = DMDisposeList(pModes);
499 wxASSERT(err == noErr);
500 }
501 else //Can't get current mode?
502 {
503 wxLogSysError(wxString::Format(wxT("Couldn't obtain current display mode!!!\ndwDMVer:%u"),
504 (unsigned int) dwDMVer));
505 }
506 }
507 else //DM ver 1
508 {
509 wxLogSysError(wxString::Format(wxT("Display Manager Version %u Not Supported! Present? %s"),
510 (unsigned int) dwDMVer / 0x10000,
511 (dwDMVer & (1 << gestaltDisplayMgrPresent) ? wxT("Yes") : wxT("No")) )
512 );
513 }
514
515 return RetMode;
516 }
517
518 bool wxDisplay::ChangeMode(const wxVideoMode& mode)
519 {
520 unsigned long dwDMVer;
521 Gestalt(gestaltDisplayMgrVers, (long*)&dwDMVer);
522 if (GetCount() == 1 || dwDMVer >= 0x020000)
523 {
524 if (mode == wxDefaultVideoMode)
525 {
526 //#ifndef __DARWIN__
527 // Handle hDisplayState;
528 // if (DMBeginConfigureDisplays(&hDisplayState) != noErr)
529 // {
530 // wxLogSysError(wxT("Could not lock display for display mode changing!"));
531 // return false;
532 // }
533 // wxASSERT( DMUseScreenPrefs(true, hDisplayState) == noErr);
534 // DMEndConfigureDisplays(hDisplayState);
535 // return true;
536 //#else
537 //hmmmmm....
538 return true;
539 //#endif
540 }
541
542 //0 & NULL for params 2 & 3 of DMSetVideoMode signal it to use defaults (current mode)
543 //DM 2.0+ doesn't use params 2 & 3 of DMSetDisplayMode
544 //so we have to use this icky structure
545 VDSwitchInfoRec sMode;
546 memset(&sMode, 0, sizeof(VDSwitchInfoRec) );
547
548 DMListIndexType nNumModes;
549 DMListType pModes;
550 DMDisplayModeListIteratorUPP uppMLI;
551 DisplayIDType nDisplayID;
552 OSErr err;
553
554 err = DMGetDisplayIDByGDevice(m_priv->m_hndl, &nDisplayID, false);
555 wxASSERT(err == noErr);
556
557 //Create a new list...
558 err = DMNewDisplayModeList(nDisplayID, NULL, NULL, &nNumModes, &pModes);
559 wxASSERT_MSG(err == noErr, wxT("Could not create a new display mode list") );
560
561 uppMLI = NewDMDisplayModeListIteratorUPP(DMModeInfoProc);
562 wxASSERT(uppMLI);
563
564 DMModeInfoRec sModeInfo;
565 sModeInfo.bMatched = false;
566 sModeInfo.pMode = &mode;
567 unsigned int i;
568 for(i = 0; i < nNumModes; ++i)
569 {
570 err = DMGetIndexedDisplayModeFromList(pModes, i, NULL, uppMLI, &sModeInfo);
571 wxASSERT(err == noErr);
572
573 if (sModeInfo.bMatched)
574 {
575 sMode = sModeInfo.sMode;
576 break;
577 }
578 }
579 if(i == nNumModes)
580 return false;
581
582 DisposeDMDisplayModeListIteratorUPP(uppMLI);
583
584 err = DMDisposeList(pModes);
585 wxASSERT(err == noErr);
586
587 // For the really paranoid -
588 // unsigned long flags;
589 // Boolean bok;
590 // wxASSERT(noErr == DMCheckDisplayMode(m_priv->m_hndl, sMode.csData,
591 // sMode.csMode, &flags, NULL, &bok));
592 // wxASSERT(bok);
593
594 Handle hDisplayState;
595 if (DMBeginConfigureDisplays(&hDisplayState) != noErr)
596 {
597 wxLogSysError(wxT("Could not lock display for display mode changing!"));
598 return false;
599 }
600
601 unsigned long dwBPP = (unsigned long) mode.bpp;
602 if (DMSetDisplayMode(m_priv->m_hndl, sMode.csData,
603 (unsigned long*) &(dwBPP), NULL
604 //(unsigned long) &sMode
605 , hDisplayState
606 ) != noErr)
607 {
608 DMEndConfigureDisplays(hDisplayState);
609 wxMessageBox(wxString::Format(wxT("Could not set the display mode")));
610 return false;
611 }
612 DMEndConfigureDisplays(hDisplayState);
613 }
614 else //DM 1.0, 1.2, 1.x
615 {
616 wxLogSysError(wxString::Format(wxT("Monitor gravitation not supported yet. dwDMVer:%u"),
617 (unsigned int) dwDMVer));
618 return false;
619 }
620
621 return true;
622 }
623
624 wxDisplay::~wxDisplay()
625 {
626 if ( m_priv )
627 {
628 delete m_priv;
629 m_priv = 0;
630 }
631 }
632
633 #endif // !OSX
634
635 #endif // wxUSE_DISPLAY