1 /////////////////////////////////////////////////////////////////////////////
3 // Purpose: Mac implementation of wxDisplay class
4 // Author: Ryan Norton & Brian Victor
5 // Modified by: Royce Mitchell III
8 // Copyright: (c) wxWidgets team
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
12 #if defined(__GNUG__) && !defined(NO_GCC_PRAGMA)
13 #pragma implementation "display.h"
16 // For compilers that support precompilation, includes "wx.h".
17 #include "wx/wxprec.h"
26 #include "wx/dynarray.h"
28 #include "wx/msgdlg.h"
32 #include <Carbon/Carbon.h>
36 #include <Quickdraw.h>
37 #include <Video.h> //for VDSwitchInfoRec
41 #include "wx/display.h"
42 #include "wx/gdicmn.h"
43 #include "wx/string.h"
45 // ----------------------------------------------------------------------------
47 // ----------------------------------------------------------------------------
51 class wxDisplayMacPriv
54 CGDirectDisplayID m_id
;
57 size_t wxDisplayBase::GetCount()
63 CGGetActiveDisplayList(0, NULL
, &count
);
65 wxASSERT(err
== CGDisplayNoErr
);
69 int wxDisplayBase::GetFromPoint(const wxPoint
&p
)
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
);
80 theCount
= GetCount();
81 CGDirectDisplayID
* theIDs
= new CGDirectDisplayID
[theCount
];
82 err
= CGGetActiveDisplayList(theCount
, theIDs
, &theCount
);
83 wxASSERT(err
== CGDisplayNoErr
);
85 for(nWhich
= 0; nWhich
< (int) theCount
; ++nWhich
)
87 if(theIDs
[nWhich
] == theID
)
93 if(nWhich
== (int) theCount
)
95 wxFAIL_MSG(wxT("Failed to find display in display list"));
101 }//CFUserNotification[NSBundle bundleForClass:[self class]]
103 wxDisplay::wxDisplay(size_t index
) : wxDisplayBase ( index
) ,
104 m_priv ( new wxDisplayMacPriv() )
106 CGDisplayCount theCount
= GetCount();
107 CGDirectDisplayID
* theIDs
= new CGDirectDisplayID
[theCount
];
111 CGGetActiveDisplayList(theCount
, theIDs
, &theCount
);
113 wxASSERT(err
== CGDisplayNoErr
);
114 wxASSERT(index
< theCount
);
116 m_priv
->m_id
= theIDs
[index
];
121 wxRect
wxDisplay::GetGeometry() const
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
130 int wxDisplay::GetDepth() const
132 return (int) CGDisplayBitsPerPixel(m_priv
->m_id
); //size_t
135 wxString
wxDisplay::GetName() const
137 // Macs don't name their displays...
138 return wxEmptyString
;
141 static int wxCFDictKeyToInt( CFDictionaryRef desc
, CFStringRef key
)
146 if ( (value
= (CFNumberRef
) CFDictionaryGetValue(desc
, key
)) == NULL
)
148 CFNumberGetValue(value
, kCFNumberIntType
, &num
);
153 wxDisplay::GetModes(const wxVideoMode
& mode
) const
155 wxArrayVideoModes Modes
;
157 CFArrayRef theArray
= CGDisplayAvailableModes(m_priv
->m_id
);
159 for(CFIndex i
= 0; i
< CFArrayGetCount(theArray
); ++i
)
161 CFDictionaryRef theValue
= (CFDictionaryRef
) CFArrayGetValueAtIndex(theArray
, i
);
163 wxVideoMode
theMode(wxCFDictKeyToInt(theValue
, kCGDisplayWidth
),
164 wxCFDictKeyToInt(theValue
, kCGDisplayHeight
),
165 wxCFDictKeyToInt(theValue
, kCGDisplayBitsPerPixel
),
166 wxCFDictKeyToInt(theValue
, kCGDisplayRefreshRate
));
168 if (theMode
.Matches(mode
))
175 wxVideoMode
wxDisplay::GetCurrentMode() const
177 CFDictionaryRef theValue
= CGDisplayCurrentMode (m_priv
->m_id
);
179 return wxVideoMode(wxCFDictKeyToInt(theValue
, kCGDisplayWidth
),
180 wxCFDictKeyToInt(theValue
, kCGDisplayHeight
),
181 wxCFDictKeyToInt(theValue
, kCGDisplayBitsPerPixel
),
182 wxCFDictKeyToInt(theValue
, kCGDisplayRefreshRate
));
185 bool wxDisplay::ChangeMode(const wxVideoMode
& mode
)
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 (
197 (double)mode
.refresh
,
200 bool bOK
= bExactMatch
;
203 bOK
= CGDisplaySwitchToMode(m_priv
->m_id
, theCGMode
) == CGDisplayNoErr
;
208 wxDisplay::~wxDisplay()
219 class wxDisplayMacPriv
225 size_t wxDisplayBase::GetCount()
229 hndl
= DMGetFirstScreenDevice(true);
233 hndl
= DMGetNextScreenDevice(hndl
, true);
238 int wxDisplayBase::GetFromPoint(const wxPoint
&p
)
242 hndl
= DMGetFirstScreenDevice(true);
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
)
254 hndl
= DMGetNextScreenDevice(hndl
, true);
259 wxDisplay::wxDisplay(size_t index
) : wxDisplayBase ( index
),
260 m_priv ( new wxDisplayMacPriv() )
263 hndl
= DMGetFirstScreenDevice(true);
264 m_priv
->m_hndl
= NULL
;
269 m_priv
->m_hndl
= hndl
;
272 hndl
= DMGetNextScreenDevice(hndl
, true);
276 wxRect
wxDisplay::GetGeometry() const
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
);
285 int wxDisplay::GetDepth() const
287 if (!(m_priv
)) return 0;
288 if (!(m_priv
->m_hndl
)) return 0;
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
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;
300 wxString
wxDisplay::GetName() const
302 // Macs don't name their displays...
303 return wxEmptyString
;
306 struct DMModeIteratorRec
308 wxArrayVideoModes
* pModes
;
309 const wxVideoMode
* pMatchMode
;
312 pascal void DMModeListIteratorProc ( void* pData
,
313 DMListIndexType nIndex
,
314 DMDisplayModeListEntryPtr pInfo
)
316 DMModeIteratorRec
* pInfoData
= (DMModeIteratorRec
*) pData
;
318 //Note that in testing the refresh rate is always 0 on my ibook - RN
319 int refresh
= (int) Fix2Long(pInfo
->displayModeResolutionInfo
->csRefreshRate
);
321 for(unsigned long i
= 0; i
< pInfo
->displayModeDepthBlockInfo
->depthBlockCount
; ++i
)
323 #define pDBI pInfo->displayModeDepthBlockInfo->depthVPBlock[i].depthVPBlock
325 if (wxVideoMode((int) pInfo
->displayModeResolutionInfo
->csHorizontalPixels
,
326 (int) pInfo
->displayModeResolutionInfo
->csVerticalLines
,
327 (int) pDBI
->vpPixelSize
,
328 refresh
).Matches(*pInfoData
->pMatchMode
) )
330 pInfoData
->pModes
->Add(wxVideoMode((int) pInfo
->displayModeResolutionInfo
->csHorizontalPixels
,
331 (int) pInfo
->displayModeResolutionInfo
->csVerticalLines
,
332 (int) pDBI
->vpPixelSize
,
341 const wxVideoMode
* pMode
;
342 VDSwitchInfoRec sMode
;
346 pascal void DMModeInfoProc ( void* pData
,
347 DMListIndexType nIndex
,
348 DMDisplayModeListEntryPtr pInfo
)
350 DMModeInfoRec
* pInfoData
= (DMModeInfoRec
*) pData
;
351 Fixed refresh
= Long2Fix(pInfoData
->pMode
->refresh
);
353 for(unsigned long i
= 0; i
< pInfo
->displayModeDepthBlockInfo
->depthBlockCount
; ++i
)
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
)
361 memcpy(&pInfoData
->sMode
, pInfo
->displayModeDepthBlockInfo
->depthVPBlock
[i
].depthSwitchInfo
,
362 sizeof(VDSwitchInfoRec
));
363 pInfoData
->sMode
.csMode
= pDBI
->vpPixelSize
;
364 pInfoData
->bMatched
= true;
371 struct DMModeTransRec
374 const VDSwitchInfoRec
* psMode
;
378 pascal void DMModeTransProc ( void* pData
,
379 DMListIndexType nIndex
,
380 DMDisplayModeListEntryPtr pInfo
)
382 DMModeTransRec
* pInfoData
= (DMModeTransRec
*) pData
;
384 for(unsigned long i
= 0; i
< pInfo
->displayModeDepthBlockInfo
->depthBlockCount
; ++i
)
386 #define pDBI pInfo->displayModeDepthBlockInfo->depthVPBlock[i].depthVPBlock
387 if (pInfoData
->psMode
->csData
== pInfo
->displayModeDepthBlockInfo
->depthVPBlock
[i
].depthSwitchInfo
->csData
)
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;
401 wxDisplay::GetModes(const wxVideoMode
& mode
) const
404 wxArrayVideoModes Modes
;
406 unsigned long dwDMVer
;
407 Gestalt(gestaltDisplayMgrVers
, (long*) &dwDMVer
);
409 //Check DM version (for backward compatibility only - 7.5.3+ use 2.0)
410 if (dwDMVer
>= 0x020000) //version 2?
413 DMListIndexType nNumModes
;
415 DMDisplayModeListIteratorUPP uppMLI
;
416 DisplayIDType nDisplayID
;
419 err
= DMGetDisplayIDByGDevice(m_priv
->m_hndl
, &nDisplayID
, false);
420 wxASSERT(err
== noErr
);
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") );
426 uppMLI
= NewDMDisplayModeListIteratorUPP(DMModeListIteratorProc
);
429 DMModeIteratorRec sModeInfo
;
430 sModeInfo
.pModes
= &Modes
;
431 sModeInfo
.pMatchMode
= &mode
;
432 for (DMListIndexType i
= 0; i
< nNumModes
; ++i
)
434 err
= DMGetIndexedDisplayModeFromList(pModes
, i
, NULL
, uppMLI
, &sModeInfo
);
435 wxASSERT(err
== noErr
);
437 DisposeDMDisplayModeListIteratorUPP(uppMLI
);
439 err
= DMDisposeList(pModes
);
440 wxASSERT(err
== noErr
);
442 else //DM 1.0, 1.2, 1.x
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")) )
453 wxVideoMode
wxDisplay::GetCurrentMode() const
455 unsigned long dwDMVer
;
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?
462 VDSwitchInfoRec sMode
; //Note - csMode member also contains the bit depth
463 if (DMGetDisplayMode(m_priv
->m_hndl
, &sMode
) == noErr
)
465 DMListIndexType nNumModes
;
467 DMDisplayModeListIteratorUPP uppMLI
;
468 DisplayIDType nDisplayID
;
471 err
= DMGetDisplayIDByGDevice(m_priv
->m_hndl
, &nDisplayID
, false);
472 wxASSERT(err
== noErr
);
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") );
478 uppMLI
= NewDMDisplayModeListIteratorUPP(DMModeTransProc
);
481 DMModeTransRec sModeInfo
;
482 sModeInfo
.bMatched
= false;
483 sModeInfo
.psMode
= &sMode
;
484 for (DMListIndexType i
= 0; i
< nNumModes
; ++i
)
486 err
= DMGetIndexedDisplayModeFromList(pModes
, i
, NULL
, uppMLI
, &sModeInfo
);
487 wxASSERT(err
== noErr
);
489 if ( sModeInfo
.bMatched
)
491 RetMode
= sModeInfo
.Mode
;
496 DisposeDMDisplayModeListIteratorUPP(uppMLI
);
498 err
= DMDisposeList(pModes
);
499 wxASSERT(err
== noErr
);
501 else //Can't get current mode?
503 wxLogSysError(wxString::Format(wxT("Couldn't obtain current display mode!!!\ndwDMVer:%u"),
504 (unsigned int) dwDMVer
));
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")) )
518 bool wxDisplay::ChangeMode(const wxVideoMode
& mode
)
520 unsigned long dwDMVer
;
521 Gestalt(gestaltDisplayMgrVers
, (long*)&dwDMVer
);
522 if (GetCount() == 1 || dwDMVer
>= 0x020000)
524 if (mode
== wxDefaultVideoMode
)
527 // Handle hDisplayState;
528 // if (DMBeginConfigureDisplays(&hDisplayState) != noErr)
530 // wxLogSysError(wxT("Could not lock display for display mode changing!"));
533 // wxASSERT( DMUseScreenPrefs(true, hDisplayState) == noErr);
534 // DMEndConfigureDisplays(hDisplayState);
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
) );
548 DMListIndexType nNumModes
;
550 DMDisplayModeListIteratorUPP uppMLI
;
551 DisplayIDType nDisplayID
;
554 err
= DMGetDisplayIDByGDevice(m_priv
->m_hndl
, &nDisplayID
, false);
555 wxASSERT(err
== noErr
);
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") );
561 uppMLI
= NewDMDisplayModeListIteratorUPP(DMModeInfoProc
);
564 DMModeInfoRec sModeInfo
;
565 sModeInfo
.bMatched
= false;
566 sModeInfo
.pMode
= &mode
;
568 for(i
= 0; i
< nNumModes
; ++i
)
570 err
= DMGetIndexedDisplayModeFromList(pModes
, i
, NULL
, uppMLI
, &sModeInfo
);
571 wxASSERT(err
== noErr
);
573 if (sModeInfo
.bMatched
)
575 sMode
= sModeInfo
.sMode
;
582 DisposeDMDisplayModeListIteratorUPP(uppMLI
);
584 err
= DMDisposeList(pModes
);
585 wxASSERT(err
== noErr
);
587 // For the really paranoid -
588 // unsigned long flags;
590 // wxASSERT(noErr == DMCheckDisplayMode(m_priv->m_hndl, sMode.csData,
591 // sMode.csMode, &flags, NULL, &bok));
594 Handle hDisplayState
;
595 if (DMBeginConfigureDisplays(&hDisplayState
) != noErr
)
597 wxLogSysError(wxT("Could not lock display for display mode changing!"));
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
608 DMEndConfigureDisplays(hDisplayState
);
609 wxMessageBox(wxString::Format(wxT("Could not set the display mode")));
612 DMEndConfigureDisplays(hDisplayState
);
614 else //DM 1.0, 1.2, 1.x
616 wxLogSysError(wxString::Format(wxT("Monitor gravitation not supported yet. dwDMVer:%u"),
617 (unsigned int) dwDMVer
));
624 wxDisplay::~wxDisplay()
635 #endif // wxUSE_DISPLAY