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