]> git.saurik.com Git - wxWidgets.git/blame_incremental - src/mac/carbon/display.cpp
wxUSE_TOOLTIPS fix (good grief I think I just did this a couple days ago...)
[wxWidgets.git] / src / mac / carbon / display.cpp
... / ...
CommitLineData
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
51class wxDisplayMacPriv
52{
53public:
54 CGDirectDisplayID m_id;
55};
56
57size_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
69int 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
103wxDisplay::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
121wxRect 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
130int wxDisplay::GetDepth() const
131{
132 return (int) CGDisplayBitsPerPixel(m_priv->m_id); //size_t
133}
134
135wxString wxDisplay::GetName() const
136{
137 // Macs don't name their displays...
138 return wxEmptyString;
139}
140
141static 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
152wxArrayVideoModes
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
175wxVideoMode 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
185bool 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
208wxDisplay::~wxDisplay()
209{
210 if ( m_priv )
211 {
212 delete m_priv;
213 m_priv = 0;
214 }
215}
216
217#else
218
219class wxDisplayMacPriv
220{
221public:
222 GDHandle m_hndl;
223};
224
225size_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
238int 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
259wxDisplay::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
276wxRect 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
285int 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
300wxString wxDisplay::GetName() const
301{
302 // Macs don't name their displays...
303 return wxEmptyString;
304}
305
306struct DMModeIteratorRec
307{
308 wxArrayVideoModes* pModes;
309 const wxVideoMode* pMatchMode;
310};
311
312pascal 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
339struct DMModeInfoRec
340{
341 const wxVideoMode* pMode;
342 VDSwitchInfoRec sMode;
343 bool bMatched;
344};
345
346pascal 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
371struct DMModeTransRec
372{
373 wxVideoMode Mode;
374 const VDSwitchInfoRec* psMode;
375 bool bMatched;
376};
377
378pascal 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
400wxArrayVideoModes
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
453wxVideoMode 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 == true )
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
518bool 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 == true)
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
624wxDisplay::~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