]> git.saurik.com Git - wxWidgets.git/blob - src/mac/display.cpp
safe destruction, even if redraws might be triggered
[wxWidgets.git] / src / mac / display.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: display.cpp
3 // Purpose: Mac implementation of wxDisplay class
4 // Author: Brian Victor
5 // Modified by: Royce Mitchell III & Ryan Norton
6 // Created: 06/21/02
7 // RCS-ID: $Id$
8 // Copyright: (c) wxWindows team
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
11
12 #ifdef __GNUG__
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 #endif
29
30 #ifdef __DARWIN__
31 #include <Carbon/Carbon.h>
32 #else
33 #include <Gestalt.h>
34 #include <Displays.h>
35 #include <Quickdraw.h>
36 #include <Video.h> //for VDSwitchInfoRec
37 #include <FixMath.h>
38 #endif
39
40 #include "wx/display.h"
41 #include "wx/gdicmn.h"
42 #include "wx/string.h"
43
44 // ----------------------------------------------------------------------------
45 // private classes
46 // ----------------------------------------------------------------------------
47
48 class wxDisplayMacPriv
49 {
50 public:
51 GDHandle m_hndl;
52 };
53
54 size_t wxDisplayBase::GetCount()
55 {
56 GDHandle hndl;
57 size_t num = 0;
58 hndl = DMGetFirstScreenDevice(true);
59 while(hndl)
60 {
61 num++;
62 hndl = DMGetNextScreenDevice(hndl, true);
63 }
64 return num;
65 }
66
67 int wxDisplayBase::GetFromPoint(const wxPoint &p)
68 {
69 GDHandle hndl;
70 size_t num = 0;
71 hndl = DMGetFirstScreenDevice(true);
72 while(hndl)
73 {
74 Rect screenrect = (*hndl)->gdRect;
75 if (p.x >= screenrect.left &&
76 p.x <= screenrect.right &&
77 p.y >= screenrect.top &&
78 p.y <= screenrect.bottom)
79 {
80 return num;
81 }
82 num++;
83 hndl = DMGetNextScreenDevice(hndl, true);
84 }
85 return -1;
86 }
87
88 wxDisplay::wxDisplay(size_t index) : wxDisplayBase ( index ),
89 m_priv ( new wxDisplayMacPriv() )
90 {
91 GDHandle hndl;
92 hndl = DMGetFirstScreenDevice(true);
93 m_priv->m_hndl = NULL;
94 while(hndl)
95 {
96 if (index == 0)
97 {
98 m_priv->m_hndl = hndl;
99 }
100 index--;
101 hndl = DMGetNextScreenDevice(hndl, true);
102 }
103 }
104
105 wxRect wxDisplay::GetGeometry() const
106 {
107 if (!(m_priv)) return wxRect(0, 0, 0, 0);
108 if (!(m_priv->m_hndl)) return wxRect(0, 0, 0, 0);
109 Rect screenrect = (*(m_priv->m_hndl))->gdRect;
110 return wxRect( screenrect.left, screenrect.top,
111 screenrect.right - screenrect.left, screenrect.bottom - screenrect.top);
112 }
113
114 int wxDisplay::GetDepth() const
115 {
116 if (!(m_priv)) return 0;
117 if (!(m_priv->m_hndl)) return 0;
118
119 // This cryptic looking code is based on Apple's sample code:
120 // http://developer.apple.com/samplecode/Sample_Code/Graphics_2D/GDevVideo/Gen.cp.htm
121
122 //RN - according to the docs
123 //gdPMap is a bitmap-type representation of the GDevice, and all
124 //0x0000FFFF does is get the lower 16 bits of pixelSize. However,
125 //since pixelSize is only 16 bits (a short)...
126 return ((*(*(m_priv->m_hndl))->gdPMap)->pixelSize) & 0x0000FFFF;
127 }
128
129 wxString wxDisplay::GetName() const
130 {
131 // Macs don't name their displays...
132 return wxEmptyString;
133 }
134
135 struct DMModeIteratorRec
136 {
137 wxArrayVideoModes* pModes;
138 const wxVideoMode* pMatchMode;
139 };
140
141 pascal void DMModeListIteratorProc ( void* pData,
142 DMListIndexType nIndex,
143 DMDisplayModeListEntryPtr pInfo)
144 {
145 DMModeIteratorRec* pInfoData = (DMModeIteratorRec*) pData;
146
147 //Note that in testing the refresh rate is always 0 on my ibook - RN
148 int refresh = (int) Fix2Long(pInfo->displayModeResolutionInfo->csRefreshRate);
149
150 for(unsigned long i = 0; i < pInfo->displayModeDepthBlockInfo->depthBlockCount; ++i)
151 {
152 #define pDBI pInfo->displayModeDepthBlockInfo->depthVPBlock[i].depthVPBlock
153
154 if (wxVideoMode((int) pInfo->displayModeResolutionInfo->csHorizontalPixels,
155 (int) pInfo->displayModeResolutionInfo->csVerticalLines,
156 (int) pDBI->vpPixelSize,
157 refresh).Matches(*pInfoData->pMatchMode) )
158 {
159 pInfoData->pModes->Add(wxVideoMode((int) pInfo->displayModeResolutionInfo->csHorizontalPixels,
160 (int) pInfo->displayModeResolutionInfo->csVerticalLines,
161 (int) pDBI->vpPixelSize,
162 refresh));
163 }
164 #undef pDBI
165 }
166 }
167
168 struct DMModeInfoRec
169 {
170 const wxVideoMode* pMode;
171 VDSwitchInfoRec sMode;
172 bool bMatched;
173 };
174
175 pascal void DMModeInfoProc ( void* pData,
176 DMListIndexType nIndex,
177 DMDisplayModeListEntryPtr pInfo)
178 {
179 DMModeInfoRec* pInfoData = (DMModeInfoRec*) pData;
180 Fixed refresh = Long2Fix(pInfoData->pMode->refresh);
181
182 for(unsigned long i = 0; i < pInfo->displayModeDepthBlockInfo->depthBlockCount; ++i)
183 {
184 #define pDBI pInfo->displayModeDepthBlockInfo->depthVPBlock[i].depthVPBlock
185 if (pInfoData->pMode->w == (int&) pInfo->displayModeResolutionInfo->csHorizontalPixels &&
186 pInfoData->pMode->h == (int&) pInfo->displayModeResolutionInfo->csVerticalLines &&
187 pInfoData->pMode->bpp == (int) pDBI->vpPixelSize &&
188 refresh == pInfo->displayModeResolutionInfo->csRefreshRate)
189 {
190 memcpy(&pInfoData->sMode, pInfo->displayModeDepthBlockInfo->depthVPBlock[i].depthSwitchInfo,
191 sizeof(VDSwitchInfoRec));
192 pInfoData->sMode.csMode = pDBI->vpPixelSize;
193 pInfoData->bMatched = true;
194 break;
195 }
196 #undef pDBI
197 }
198 }
199
200 struct DMModeTransRec
201 {
202 wxVideoMode Mode;
203 const VDSwitchInfoRec* psMode;
204 bool bMatched;
205 };
206
207 pascal void DMModeTransProc ( void* pData,
208 DMListIndexType nIndex,
209 DMDisplayModeListEntryPtr pInfo)
210 {
211 DMModeTransRec* pInfoData = (DMModeTransRec*) pData;
212
213 for(unsigned long i = 0; i < pInfo->displayModeDepthBlockInfo->depthBlockCount; ++i)
214 {
215 #define pDBI pInfo->displayModeDepthBlockInfo->depthVPBlock[i].depthVPBlock
216 if (pInfoData->psMode->csData == pInfo->displayModeDepthBlockInfo->depthVPBlock[i].depthSwitchInfo->csData)
217 {
218 pInfoData->Mode = wxVideoMode((int) pInfo->displayModeResolutionInfo->csHorizontalPixels,
219 (int) pInfo->displayModeResolutionInfo->csVerticalLines,
220 (int) pDBI->vpPixelSize,
221 (int) Fix2Long(pInfo->displayModeResolutionInfo->csRefreshRate) );
222 pInfoData->bMatched = true;
223 break;
224 }
225 #undef pDBI
226 }
227 }
228
229 wxArrayVideoModes
230 wxDisplay::GetModes(const wxVideoMode& mode) const
231 {
232
233 wxArrayVideoModes Modes;
234
235 unsigned long dwDMVer;
236 Gestalt(gestaltDisplayMgrVers, (long*) &dwDMVer);
237
238 //Check DM version (for backward compatibility only - 7.5.3+ use 2.0)
239 if (dwDMVer >= 0x020000) //version 2?
240 {
241
242 DMListIndexType nNumModes;
243 DMListType pModes;
244 DMDisplayModeListIteratorUPP uppMLI;
245 DisplayIDType nDisplayID;
246
247 wxASSERT(DMGetDisplayIDByGDevice(m_priv->m_hndl, &nDisplayID, false) == noErr);
248 //Create a new list...
249 wxASSERT_MSG(DMNewDisplayModeList(nDisplayID, NULL, NULL, &nNumModes, &pModes) == noErr, wxT("Could not create a new display mode list") );
250
251 uppMLI = NewDMDisplayModeListIteratorUPP(DMModeListIteratorProc);
252 wxASSERT(uppMLI);
253
254 DMModeIteratorRec sModeInfo;
255 sModeInfo.pModes = &Modes;
256 sModeInfo.pMatchMode = &mode;
257 for (DMListIndexType i = 0; i < nNumModes; ++i)
258 {
259 wxASSERT(DMGetIndexedDisplayModeFromList(pModes, i, NULL,
260 uppMLI, &sModeInfo) == noErr);
261 }
262 DisposeDMDisplayModeListIteratorUPP(uppMLI);
263 wxASSERT(DMDisposeList(pModes) == noErr);
264 }
265 else //DM 1.0, 1.2, 1.x
266 {
267 wxLogSysError(wxString::Format(wxT("Display Manager Version %u Not Supported! Present? %s"),
268 (unsigned int) dwDMVer / 0x10000,
269 (dwDMVer & (1 << gestaltDisplayMgrPresent) ? wxT("Yes") : wxT("No")) )
270 );
271 }
272
273 return Modes;
274 }
275
276 wxVideoMode wxDisplay::GetCurrentMode() const
277 {
278 unsigned long dwDMVer;
279 wxVideoMode RetMode;
280
281 Gestalt(gestaltDisplayMgrVers, (long*) &dwDMVer);
282 //Check DM version (for backward compatibility only - 7.5.3+ use 2.0)
283 if (dwDMVer >= 0x020000) //version 2?
284 {
285 VDSwitchInfoRec sMode; //Note - csMode member also contains the bit depth
286 if (DMGetDisplayMode(m_priv->m_hndl, &sMode) == noErr)
287 {
288 DMListIndexType nNumModes;
289 DMListType pModes;
290 DMDisplayModeListIteratorUPP uppMLI;
291 DisplayIDType nDisplayID;
292
293 wxASSERT(DMGetDisplayIDByGDevice(m_priv->m_hndl, &nDisplayID, false) == noErr);
294 //Create a new list...
295 wxASSERT_MSG(DMNewDisplayModeList(nDisplayID, NULL, NULL, &nNumModes, &pModes) == noErr,
296 wxT("Could not create a new display mode list") );
297
298 uppMLI = NewDMDisplayModeListIteratorUPP(DMModeTransProc);
299 wxASSERT(uppMLI);
300
301 DMModeTransRec sModeInfo;
302 sModeInfo.bMatched = false;
303 sModeInfo.psMode = &sMode;
304 for (DMListIndexType i = 0; i < nNumModes; ++i)
305 {
306 wxASSERT(DMGetIndexedDisplayModeFromList(pModes, i, NULL,
307 uppMLI, &sModeInfo) == noErr);
308
309 if ( sModeInfo.bMatched == true )
310 {
311 RetMode = sModeInfo.Mode;
312 break;
313 }
314 }
315
316 DisposeDMDisplayModeListIteratorUPP(uppMLI);
317 wxASSERT(DMDisposeList(pModes) == noErr);
318 }
319 else //Can't get current mode?
320 {
321 wxLogSysError(wxString::Format(wxT("Couldn't obtain current display mode!!!\ndwDMVer:%u"),
322 (unsigned int) dwDMVer));
323 }
324 }
325 else //DM ver 1
326 {
327 wxLogSysError(wxString::Format(wxT("Display Manager Version %u Not Supported! Present? %s"),
328 (unsigned int) dwDMVer / 0x10000,
329 (dwDMVer & (1 << gestaltDisplayMgrPresent) ? wxT("Yes") : wxT("No")) )
330 );
331 }
332
333 return RetMode;
334 }
335
336 bool wxDisplay::ChangeMode(const wxVideoMode& mode)
337 {
338 unsigned long dwDMVer;
339 Gestalt(gestaltDisplayMgrVers, (long*)&dwDMVer);
340 if (GetCount() == 1 || dwDMVer >= 0x020000)
341 {
342 if (mode == wxDefaultVideoMode)
343 {
344 //#ifndef __DARWIN__
345 // Handle hDisplayState;
346 // if (DMBeginConfigureDisplays(&hDisplayState) != noErr)
347 // {
348 // wxLogSysError(wxT("Could not lock display for display mode changing!"));
349 // return false;
350 // }
351 // wxASSERT( DMUseScreenPrefs(true, hDisplayState) == noErr);
352 // DMEndConfigureDisplays(hDisplayState);
353 // return true;
354 //#else
355 //hmmmmm....
356 return true;
357 //#endif
358 }
359
360 //0 & NULL for params 2 & 3 of DMSetVideoMode signal it to use defaults (current mode)
361 //DM 2.0+ doesn't use params 2 & 3 of DMSetDisplayMode
362 //so we have to use this icky structure
363 VDSwitchInfoRec sMode;
364 memset(&sMode, 0, sizeof(VDSwitchInfoRec) );
365
366 DMListIndexType nNumModes;
367 DMListType pModes;
368 DMDisplayModeListIteratorUPP uppMLI;
369 DisplayIDType nDisplayID;
370
371 wxASSERT(DMGetDisplayIDByGDevice(m_priv->m_hndl, &nDisplayID, false) == noErr);
372 //Create a new list...
373 wxASSERT_MSG(DMNewDisplayModeList(nDisplayID, NULL, NULL, &nNumModes, &pModes) == noErr,
374 wxT("Could not create a new display mode list") );
375
376 uppMLI = NewDMDisplayModeListIteratorUPP(DMModeInfoProc);
377 wxASSERT(uppMLI);
378
379 DMModeInfoRec sModeInfo;
380 sModeInfo.bMatched = false;
381 sModeInfo.pMode = &mode;
382 unsigned int i;
383 for(i = 0; i < nNumModes; ++i)
384 {
385 wxASSERT(DMGetIndexedDisplayModeFromList(pModes, i, NULL,
386 uppMLI, &sModeInfo) == noErr);
387 if (sModeInfo.bMatched == true)
388 {
389 sMode = sModeInfo.sMode;
390 break;
391 }
392 }
393 if(i == nNumModes)
394 return false;
395
396 DisposeDMDisplayModeListIteratorUPP(uppMLI);
397 wxASSERT(DMDisposeList(pModes) == noErr);
398
399 // For the really paranoid -
400 // unsigned long flags;
401 // Boolean bok;
402 // wxASSERT(noErr == DMCheckDisplayMode(m_priv->m_hndl, sMode.csData,
403 // sMode.csMode, &flags, NULL, &bok));
404 // wxASSERT(bok);
405
406 Handle hDisplayState;
407 if (DMBeginConfigureDisplays(&hDisplayState) != noErr)
408 {
409 wxLogSysError(wxT("Could not lock display for display mode changing!"));
410 return false;
411 }
412
413 unsigned long dwBPP = (unsigned long) mode.bpp;
414 if (DMSetDisplayMode(m_priv->m_hndl, sMode.csData,
415 (unsigned long*) &(dwBPP), NULL
416 //(unsigned long) &sMode
417 , hDisplayState
418 ) != noErr)
419 {
420 DMEndConfigureDisplays(hDisplayState);
421 wxMessageBox(wxString::Format(wxT("Could not set the display mode")));
422 return false;
423 }
424 DMEndConfigureDisplays(hDisplayState);
425 }
426 else //DM 1.0, 1.2, 1.x
427 {
428 wxLogSysError(wxString::Format(wxT("Monitor gravitation not supported yet. dwDMVer:%u"),
429 (unsigned int) dwDMVer));
430 return false;
431 }
432
433 return true;
434 }
435
436 wxDisplay::~wxDisplay()
437 {
438 if ( m_priv )
439 {
440 delete m_priv;
441 m_priv = 0;
442 }
443 }
444
445 #endif // wxUSE_DISPLAY