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