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 // For compilers that support precompilation, includes "wx.h".
13 #include "wx/wxprec.h"
22 #include "wx/dynarray.h"
24 #include "wx/msgdlg.h"
28 #include <Carbon/Carbon.h>
32 #include <Quickdraw.h>
33 #include <Video.h> //for VDSwitchInfoRec
37 #include "wx/display.h"
38 #include "wx/gdicmn.h"
39 #include "wx/string.h"
41 // ----------------------------------------------------------------------------
43 // ----------------------------------------------------------------------------
47 class wxDisplayMacPriv
50 CGDirectDisplayID m_id
;
53 size_t wxDisplayBase::GetCount()
59 CGGetActiveDisplayList(0, NULL
, &count
);
61 wxASSERT(err
== CGDisplayNoErr
);
65 int wxDisplayBase::GetFromPoint(const wxPoint
&p
)
67 CGPoint thePoint
= {(float)p
.x
, (float)p
.y
};
68 CGDirectDisplayID theID
;
69 CGDisplayCount theCount
;
70 CGDisplayErr err
= CGGetDisplaysWithPoint(thePoint
, 1, &theID
, &theCount
);
71 wxASSERT(err
== CGDisplayNoErr
);
76 theCount
= GetCount();
77 CGDirectDisplayID
* theIDs
= new CGDirectDisplayID
[theCount
];
78 err
= CGGetActiveDisplayList(theCount
, theIDs
, &theCount
);
79 wxASSERT(err
== CGDisplayNoErr
);
81 for(nWhich
= 0; nWhich
< (int) theCount
; ++nWhich
)
83 if(theIDs
[nWhich
] == theID
)
89 if(nWhich
== (int) theCount
)
91 wxFAIL_MSG(wxT("Failed to find display in display list"));
97 }//CFUserNotification[NSBundle bundleForClass:[self class]]
99 wxDisplay::wxDisplay(size_t index
) : wxDisplayBase ( index
) ,
100 m_priv ( new wxDisplayMacPriv() )
102 CGDisplayCount theCount
= GetCount();
103 CGDirectDisplayID
* theIDs
= new CGDirectDisplayID
[theCount
];
107 CGGetActiveDisplayList(theCount
, theIDs
, &theCount
);
109 wxASSERT(err
== CGDisplayNoErr
);
110 wxASSERT(index
< theCount
);
112 m_priv
->m_id
= theIDs
[index
];
117 wxRect
wxDisplay::GetGeometry() const
119 CGRect theRect
= CGDisplayBounds(m_priv
->m_id
);
120 return wxRect( (int)theRect
.origin
.x
,
121 (int)theRect
.origin
.y
,
122 (int)theRect
.size
.width
,
123 (int)theRect
.size
.height
); //floats
126 int wxDisplay::GetDepth() const
128 return (int) CGDisplayBitsPerPixel(m_priv
->m_id
); //size_t
131 wxString
wxDisplay::GetName() const
133 // Macs don't name their displays...
134 return wxEmptyString
;
137 static int wxCFDictKeyToInt( CFDictionaryRef desc
, CFStringRef key
)
142 if ( (value
= (CFNumberRef
) CFDictionaryGetValue(desc
, key
)) == NULL
)
144 CFNumberGetValue(value
, kCFNumberIntType
, &num
);
149 wxDisplay::GetModes(const wxVideoMode
& mode
) const
151 wxArrayVideoModes Modes
;
153 CFArrayRef theArray
= CGDisplayAvailableModes(m_priv
->m_id
);
155 for(CFIndex i
= 0; i
< CFArrayGetCount(theArray
); ++i
)
157 CFDictionaryRef theValue
= (CFDictionaryRef
) CFArrayGetValueAtIndex(theArray
, i
);
159 wxVideoMode
theMode(wxCFDictKeyToInt(theValue
, kCGDisplayWidth
),
160 wxCFDictKeyToInt(theValue
, kCGDisplayHeight
),
161 wxCFDictKeyToInt(theValue
, kCGDisplayBitsPerPixel
),
162 wxCFDictKeyToInt(theValue
, kCGDisplayRefreshRate
));
164 if (theMode
.Matches(mode
))
171 wxVideoMode
wxDisplay::GetCurrentMode() const
173 CFDictionaryRef theValue
= CGDisplayCurrentMode (m_priv
->m_id
);
175 return wxVideoMode(wxCFDictKeyToInt(theValue
, kCGDisplayWidth
),
176 wxCFDictKeyToInt(theValue
, kCGDisplayHeight
),
177 wxCFDictKeyToInt(theValue
, kCGDisplayBitsPerPixel
),
178 wxCFDictKeyToInt(theValue
, kCGDisplayRefreshRate
));
181 bool wxDisplay::ChangeMode(const wxVideoMode
& mode
)
183 //Changing to default mode (wxDefualtVideoMode) 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 (
193 (double)mode
.refresh
,
196 bool bOK
= bExactMatch
;
199 bOK
= CGDisplaySwitchToMode(m_priv
->m_id
, theCGMode
) == CGDisplayNoErr
;
204 wxDisplay::~wxDisplay()
215 class wxDisplayMacPriv
221 size_t wxDisplayBase::GetCount()
225 hndl
= DMGetFirstScreenDevice(true);
229 hndl
= DMGetNextScreenDevice(hndl
, true);
234 int wxDisplayBase::GetFromPoint(const wxPoint
&p
)
238 hndl
= DMGetFirstScreenDevice(true);
241 Rect screenrect
= (*hndl
)->gdRect
;
242 if (p
.x
>= screenrect
.left
&&
243 p
.x
<= screenrect
.right
&&
244 p
.y
>= screenrect
.top
&&
245 p
.y
<= screenrect
.bottom
)
250 hndl
= DMGetNextScreenDevice(hndl
, true);
255 wxDisplay::wxDisplay(size_t index
) : wxDisplayBase ( index
),
256 m_priv ( new wxDisplayMacPriv() )
259 hndl
= DMGetFirstScreenDevice(true);
260 m_priv
->m_hndl
= NULL
;
265 m_priv
->m_hndl
= hndl
;
268 hndl
= DMGetNextScreenDevice(hndl
, true);
272 wxRect
wxDisplay::GetGeometry() const
274 if (!(m_priv
)) return wxRect(0, 0, 0, 0);
275 if (!(m_priv
->m_hndl
)) return wxRect(0, 0, 0, 0);
276 Rect screenrect
= (*(m_priv
->m_hndl
))->gdRect
;
277 return wxRect( screenrect
.left
, screenrect
.top
,
278 screenrect
.right
- screenrect
.left
, screenrect
.bottom
- screenrect
.top
);
281 int wxDisplay::GetDepth() const
283 if (!(m_priv
)) return 0;
284 if (!(m_priv
->m_hndl
)) return 0;
286 // This cryptic looking code is based on Apple's sample code:
287 // http://developer.apple.com/samplecode/Sample_Code/Graphics_2D/GDevVideo/Gen.cp.htm
289 //RN - according to the docs
290 //gdPMap is a bitmap-type representation of the GDevice, and all
291 //0x0000FFFF does is get the lower 16 bits of pixelSize. However,
292 //since pixelSize is only 16 bits (a short)...
293 return ((*(*(m_priv
->m_hndl
))->gdPMap
)->pixelSize
) & 0x0000FFFF;
296 wxString
wxDisplay::GetName() const
298 // Macs don't name their displays...
299 return wxEmptyString
;
302 struct DMModeIteratorRec
304 wxArrayVideoModes
* pModes
;
305 const wxVideoMode
* pMatchMode
;
308 pascal void DMModeListIteratorProc ( void* pData
,
309 DMListIndexType nIndex
,
310 DMDisplayModeListEntryPtr pInfo
)
312 DMModeIteratorRec
* pInfoData
= (DMModeIteratorRec
*) pData
;
314 //Note that in testing the refresh rate is always 0 on my ibook - RN
315 int refresh
= (int) Fix2Long(pInfo
->displayModeResolutionInfo
->csRefreshRate
);
317 for(unsigned long i
= 0; i
< pInfo
->displayModeDepthBlockInfo
->depthBlockCount
; ++i
)
319 #define pDBI pInfo->displayModeDepthBlockInfo->depthVPBlock[i].depthVPBlock
321 if (wxVideoMode((int) pInfo
->displayModeResolutionInfo
->csHorizontalPixels
,
322 (int) pInfo
->displayModeResolutionInfo
->csVerticalLines
,
323 (int) pDBI
->vpPixelSize
,
324 refresh
).Matches(*pInfoData
->pMatchMode
) )
326 pInfoData
->pModes
->Add(wxVideoMode((int) pInfo
->displayModeResolutionInfo
->csHorizontalPixels
,
327 (int) pInfo
->displayModeResolutionInfo
->csVerticalLines
,
328 (int) pDBI
->vpPixelSize
,
337 const wxVideoMode
* pMode
;
338 VDSwitchInfoRec sMode
;
342 pascal void DMModeInfoProc ( void* pData
,
343 DMListIndexType nIndex
,
344 DMDisplayModeListEntryPtr pInfo
)
346 DMModeInfoRec
* pInfoData
= (DMModeInfoRec
*) pData
;
347 Fixed refresh
= Long2Fix(pInfoData
->pMode
->refresh
);
349 for(unsigned long i
= 0; i
< pInfo
->displayModeDepthBlockInfo
->depthBlockCount
; ++i
)
351 #define pDBI pInfo->displayModeDepthBlockInfo->depthVPBlock[i].depthVPBlock
352 if (pInfoData
->pMode
->w
== (int&) pInfo
->displayModeResolutionInfo
->csHorizontalPixels
&&
353 pInfoData
->pMode
->h
== (int&) pInfo
->displayModeResolutionInfo
->csVerticalLines
&&
354 pInfoData
->pMode
->bpp
== (int) pDBI
->vpPixelSize
&&
355 refresh
== pInfo
->displayModeResolutionInfo
->csRefreshRate
)
357 memcpy(&pInfoData
->sMode
, pInfo
->displayModeDepthBlockInfo
->depthVPBlock
[i
].depthSwitchInfo
,
358 sizeof(VDSwitchInfoRec
));
359 pInfoData
->sMode
.csMode
= pDBI
->vpPixelSize
;
360 pInfoData
->bMatched
= true;
367 struct DMModeTransRec
370 const VDSwitchInfoRec
* psMode
;
374 pascal void DMModeTransProc ( void* pData
,
375 DMListIndexType nIndex
,
376 DMDisplayModeListEntryPtr pInfo
)
378 DMModeTransRec
* pInfoData
= (DMModeTransRec
*) pData
;
380 for(unsigned long i
= 0; i
< pInfo
->displayModeDepthBlockInfo
->depthBlockCount
; ++i
)
382 #define pDBI pInfo->displayModeDepthBlockInfo->depthVPBlock[i].depthVPBlock
383 if (pInfoData
->psMode
->csData
== pInfo
->displayModeDepthBlockInfo
->depthVPBlock
[i
].depthSwitchInfo
->csData
)
385 pInfoData
->Mode
= wxVideoMode((int) pInfo
->displayModeResolutionInfo
->csHorizontalPixels
,
386 (int) pInfo
->displayModeResolutionInfo
->csVerticalLines
,
387 (int) pDBI
->vpPixelSize
,
388 (int) Fix2Long(pInfo
->displayModeResolutionInfo
->csRefreshRate
) );
389 pInfoData
->bMatched
= true;
397 wxDisplay::GetModes(const wxVideoMode
& mode
) const
400 wxArrayVideoModes Modes
;
402 unsigned long dwDMVer
;
403 Gestalt(gestaltDisplayMgrVers
, (long*) &dwDMVer
);
405 //Check DM version (for backward compatibility only - 7.5.3+ use 2.0)
406 if (dwDMVer
>= 0x020000) //version 2?
409 DMListIndexType nNumModes
;
411 DMDisplayModeListIteratorUPP uppMLI
;
412 DisplayIDType nDisplayID
;
415 err
= DMGetDisplayIDByGDevice(m_priv
->m_hndl
, &nDisplayID
, false);
416 wxASSERT(err
== noErr
);
418 //Create a new list...
419 err
= DMNewDisplayModeList(nDisplayID
, NULL
, NULL
, &nNumModes
, &pModes
);
420 wxASSERT_MSG(err
== noErr
, wxT("Could not create a new display mode list") );
422 uppMLI
= NewDMDisplayModeListIteratorUPP(DMModeListIteratorProc
);
425 DMModeIteratorRec sModeInfo
;
426 sModeInfo
.pModes
= &Modes
;
427 sModeInfo
.pMatchMode
= &mode
;
428 for (DMListIndexType i
= 0; i
< nNumModes
; ++i
)
430 err
= DMGetIndexedDisplayModeFromList(pModes
, i
, NULL
, uppMLI
, &sModeInfo
);
431 wxASSERT(err
== noErr
);
433 DisposeDMDisplayModeListIteratorUPP(uppMLI
);
435 err
= DMDisposeList(pModes
);
436 wxASSERT(err
== noErr
);
438 else //DM 1.0, 1.2, 1.x
440 wxLogSysError(wxString::Format(wxT("Display Manager Version %u Not Supported! Present? %s"),
441 (unsigned int) dwDMVer
/ 0x10000,
442 (dwDMVer
& (1 << gestaltDisplayMgrPresent
) ? wxT("Yes") : wxT("No")) )
449 wxVideoMode
wxDisplay::GetCurrentMode() const
451 unsigned long dwDMVer
;
454 Gestalt(gestaltDisplayMgrVers
, (long*) &dwDMVer
);
455 //Check DM version (for backward compatibility only - 7.5.3+ use 2.0)
456 if (dwDMVer
>= 0x020000) //version 2?
458 VDSwitchInfoRec sMode
; //Note - csMode member also contains the bit depth
459 if (DMGetDisplayMode(m_priv
->m_hndl
, &sMode
) == noErr
)
461 DMListIndexType nNumModes
;
463 DMDisplayModeListIteratorUPP uppMLI
;
464 DisplayIDType nDisplayID
;
467 err
= DMGetDisplayIDByGDevice(m_priv
->m_hndl
, &nDisplayID
, false);
468 wxASSERT(err
== noErr
);
470 //Create a new list...
471 err
= DMNewDisplayModeList(nDisplayID
, NULL
, NULL
, &nNumModes
, &pModes
);
472 wxASSERT_MSG(err
== noErr
, wxT("Could not create a new display mode list") );
474 uppMLI
= NewDMDisplayModeListIteratorUPP(DMModeTransProc
);
477 DMModeTransRec sModeInfo
;
478 sModeInfo
.bMatched
= false;
479 sModeInfo
.psMode
= &sMode
;
480 for (DMListIndexType i
= 0; i
< nNumModes
; ++i
)
482 err
= DMGetIndexedDisplayModeFromList(pModes
, i
, NULL
, uppMLI
, &sModeInfo
);
483 wxASSERT(err
== noErr
);
485 if ( sModeInfo
.bMatched
)
487 RetMode
= sModeInfo
.Mode
;
492 DisposeDMDisplayModeListIteratorUPP(uppMLI
);
494 err
= DMDisposeList(pModes
);
495 wxASSERT(err
== noErr
);
497 else //Can't get current mode?
499 wxLogSysError(wxString::Format(wxT("Couldn't obtain current display mode!!!\ndwDMVer:%u"),
500 (unsigned int) dwDMVer
));
505 wxLogSysError(wxString::Format(wxT("Display Manager Version %u Not Supported! Present? %s"),
506 (unsigned int) dwDMVer
/ 0x10000,
507 (dwDMVer
& (1 << gestaltDisplayMgrPresent
) ? wxT("Yes") : wxT("No")) )
514 bool wxDisplay::ChangeMode(const wxVideoMode
& mode
)
516 unsigned long dwDMVer
;
517 Gestalt(gestaltDisplayMgrVers
, (long*)&dwDMVer
);
518 if (GetCount() == 1 || dwDMVer
>= 0x020000)
520 if (mode
== wxDefaultVideoMode
)
523 // Handle hDisplayState;
524 // if (DMBeginConfigureDisplays(&hDisplayState) != noErr)
526 // wxLogSysError(wxT("Could not lock display for display mode changing!"));
529 // wxASSERT( DMUseScreenPrefs(true, hDisplayState) == noErr);
530 // DMEndConfigureDisplays(hDisplayState);
538 //0 & NULL for params 2 & 3 of DMSetVideoMode signal it to use defaults (current mode)
539 //DM 2.0+ doesn't use params 2 & 3 of DMSetDisplayMode
540 //so we have to use this icky structure
541 VDSwitchInfoRec sMode
;
542 memset(&sMode
, 0, sizeof(VDSwitchInfoRec
) );
544 DMListIndexType nNumModes
;
546 DMDisplayModeListIteratorUPP uppMLI
;
547 DisplayIDType nDisplayID
;
550 err
= DMGetDisplayIDByGDevice(m_priv
->m_hndl
, &nDisplayID
, false);
551 wxASSERT(err
== noErr
);
553 //Create a new list...
554 err
= DMNewDisplayModeList(nDisplayID
, NULL
, NULL
, &nNumModes
, &pModes
);
555 wxASSERT_MSG(err
== noErr
, wxT("Could not create a new display mode list") );
557 uppMLI
= NewDMDisplayModeListIteratorUPP(DMModeInfoProc
);
560 DMModeInfoRec sModeInfo
;
561 sModeInfo
.bMatched
= false;
562 sModeInfo
.pMode
= &mode
;
564 for(i
= 0; i
< nNumModes
; ++i
)
566 err
= DMGetIndexedDisplayModeFromList(pModes
, i
, NULL
, uppMLI
, &sModeInfo
);
567 wxASSERT(err
== noErr
);
569 if (sModeInfo
.bMatched
)
571 sMode
= sModeInfo
.sMode
;
578 DisposeDMDisplayModeListIteratorUPP(uppMLI
);
580 err
= DMDisposeList(pModes
);
581 wxASSERT(err
== noErr
);
583 // For the really paranoid -
584 // unsigned long flags;
586 // wxASSERT(noErr == DMCheckDisplayMode(m_priv->m_hndl, sMode.csData,
587 // sMode.csMode, &flags, NULL, &bok));
590 Handle hDisplayState
;
591 if (DMBeginConfigureDisplays(&hDisplayState
) != noErr
)
593 wxLogSysError(wxT("Could not lock display for display mode changing!"));
597 unsigned long dwBPP
= (unsigned long) mode
.bpp
;
598 if (DMSetDisplayMode(m_priv
->m_hndl
, sMode
.csData
,
599 (unsigned long*) &(dwBPP
), NULL
600 //(unsigned long) &sMode
604 DMEndConfigureDisplays(hDisplayState
);
605 wxMessageBox(wxString::Format(wxT("Could not set the display mode")));
608 DMEndConfigureDisplays(hDisplayState
);
610 else //DM 1.0, 1.2, 1.x
612 wxLogSysError(wxString::Format(wxT("Monitor gravitation not supported yet. dwDMVer:%u"),
613 (unsigned int) dwDMVer
));
620 wxDisplay::~wxDisplay()
631 #endif // wxUSE_DISPLAY