1 /////////////////////////////////////////////////////////////////////////////
2 // Name:        src/osx/core/display.cpp
3 // Purpose:     Mac implementation of wxDisplay class
4 // Author:      Ryan Norton & Brian Victor
5 // Modified by: Royce Mitchell III, Vadim Zeitlin
6 // Created:     06/21/02
7 // Copyright:   (c) wxWidgets team
8 // Licence:     wxWindows licence
9 /////////////////////////////////////////////////////////////////////////////
10 
11 // ============================================================================
12 // declarations
13 // ============================================================================
14 
15 // ----------------------------------------------------------------------------
16 // headers
17 // ----------------------------------------------------------------------------
18 
19 #include "wx/wxprec.h"
20 
21 
22 #include "wx/private/display.h"
23 
24 #ifndef WX_PRECOMP
25     #include "wx/dynarray.h"
26     #include "wx/log.h"
27     #include "wx/string.h"
28     #include "wx/gdicmn.h"
29     #include "wx/nonownedwnd.h"
30 #endif
31 
32 #include "wx/osx/private.h"
33 
34 // ----------------------------------------------------------------------------
35 // common helpers compiled even in wxUSE_DISPLAY==0 case
36 // ----------------------------------------------------------------------------
37 
38 // This one is defined in Objective C++ code.
39 extern wxRect wxOSXGetMainDisplayClientArea();
40 extern wxRect wxOSXGetDisplayClientArea(CGDirectDisplayID id);
41 
42 namespace
43 {
44 
wxGetScaleFactor(CGDirectDisplayID ID)45 double wxGetScaleFactor( CGDirectDisplayID ID)
46 {
47     wxCFRef<CGDisplayModeRef> mode = CGDisplayCopyDisplayMode(ID);
48     size_t width = CGDisplayModeGetWidth(mode);
49     size_t pixelsw = CGDisplayModeGetPixelWidth(mode);
50     return (double)pixelsw/width;
51 }
52 
wxGetDisplayGeometry(CGDirectDisplayID id)53 wxRect wxGetDisplayGeometry(CGDirectDisplayID id)
54 {
55     CGRect theRect = CGDisplayBounds(id);
56     return wxRect( (int)theRect.origin.x,
57                    (int)theRect.origin.y,
58                    (int)theRect.size.width,
59                    (int)theRect.size.height ); //floats
60 }
61 
wxGetDisplayDepth(CGDirectDisplayID id)62 int wxGetDisplayDepth(CGDirectDisplayID id)
63 {
64     CGDisplayModeRef currentMode = CGDisplayCopyDisplayMode(id);
65     CFStringRef encoding = CGDisplayModeCopyPixelEncoding(currentMode);
66 
67     int theDepth = 32; // some reasonable default
68     if(encoding)
69     {
70         if(CFStringCompare(encoding, CFSTR(IO32BitDirectPixels), kCFCompareCaseInsensitive) == kCFCompareEqualTo)
71             theDepth = 32;
72         else if(CFStringCompare(encoding, CFSTR(IO16BitDirectPixels), kCFCompareCaseInsensitive) == kCFCompareEqualTo)
73             theDepth = 16;
74         else if(CFStringCompare(encoding, CFSTR(IO8BitIndexedPixels), kCFCompareCaseInsensitive) == kCFCompareEqualTo)
75             theDepth = 8;
76 
77         CFRelease(encoding);
78     }
79 
80     CGDisplayModeRelease(currentMode);
81 
82     return theDepth;
83 }
84 
85 } // anonymous namespace
86 
87 #if wxUSE_DISPLAY
88 
89 #include "wx/scopedarray.h"
90 
91 // ----------------------------------------------------------------------------
92 // display classes implementation
93 // ----------------------------------------------------------------------------
94 
95 class wxDisplayImplMacOSX : public wxDisplayImpl
96 {
97 public:
wxDisplayImplMacOSX(unsigned n,CGDirectDisplayID id)98     wxDisplayImplMacOSX(unsigned n, CGDirectDisplayID id)
99         : wxDisplayImpl(n),
100           m_id(id)
101     {
102     }
103 
104     virtual wxRect GetGeometry() const wxOVERRIDE;
105     virtual wxRect GetClientArea() const wxOVERRIDE;
106     virtual int GetDepth() const wxOVERRIDE;
107     virtual double GetScaleFactor() const wxOVERRIDE;
108 
109     virtual wxArrayVideoModes GetModes(const wxVideoMode& mode) const wxOVERRIDE;
110     virtual wxVideoMode GetCurrentMode() const wxOVERRIDE;
111     virtual bool ChangeMode(const wxVideoMode& mode) wxOVERRIDE;
112 
113     virtual bool IsPrimary() const wxOVERRIDE;
114 
115 private:
116     CGDirectDisplayID m_id;
117 
118     wxDECLARE_NO_COPY_CLASS(wxDisplayImplMacOSX);
119 };
120 
121 class wxDisplayFactoryMacOSX : public wxDisplayFactory
122 {
123 public:
wxDisplayFactoryMacOSX()124     wxDisplayFactoryMacOSX() {}
125 
126     virtual wxDisplayImpl *CreateDisplay(unsigned n) wxOVERRIDE;
127     virtual unsigned GetCount() wxOVERRIDE;
128     virtual int GetFromPoint(const wxPoint& pt) wxOVERRIDE;
129     virtual int GetFromWindow(const wxWindow *window) wxOVERRIDE;
130 
131 protected:
132     wxDECLARE_NO_COPY_CLASS(wxDisplayFactoryMacOSX);
133 };
134 
135 // ============================================================================
136 // wxDisplayFactoryMacOSX implementation
137 // ============================================================================
138 
139 // gets all displays that are not mirror displays
140 
wxOSXGetDisplayList(CGDisplayCount maxDisplays,CGDirectDisplayID * displays,CGDisplayCount * displayCount)141 static CGDisplayErr wxOSXGetDisplayList(CGDisplayCount maxDisplays,
142                                    CGDirectDisplayID *displays,
143                                    CGDisplayCount *displayCount)
144 {
145     CGDisplayErr error = kCGErrorSuccess;
146     CGDisplayCount onlineCount;
147 
148     error = CGGetOnlineDisplayList(0,NULL,&onlineCount);
149     if ( error == kCGErrorSuccess )
150     {
151         *displayCount = 0;
152         if ( onlineCount > 0 )
153         {
154             CGDirectDisplayID *onlineDisplays = new CGDirectDisplayID[onlineCount];
155             error = CGGetOnlineDisplayList(onlineCount,onlineDisplays,&onlineCount);
156             if ( error == kCGErrorSuccess )
157             {
158                 for ( CGDisplayCount i = 0; i < onlineCount; ++i )
159                 {
160                     if ( CGDisplayMirrorsDisplay(onlineDisplays[i]) != kCGNullDirectDisplay )
161                         continue;
162 
163                     if ( displays == NULL )
164                         *displayCount += 1;
165                     else
166                     {
167                         if ( *displayCount < maxDisplays )
168                         {
169                             displays[*displayCount] = onlineDisplays[i];
170                             *displayCount += 1;
171                         }
172                     }
173                 }
174             }
175             delete[] onlineDisplays;
176         }
177 
178     }
179     return error;
180 }
181 
wxOSXGetDisplayFromID(CGDirectDisplayID theID)182 static int wxOSXGetDisplayFromID( CGDirectDisplayID theID )
183 {
184     int nWhich = wxNOT_FOUND;
185     CGDisplayCount theCount;
186     CGDisplayErr err = wxOSXGetDisplayList(0, NULL, &theCount);
187 
188     if (err == CGDisplayNoErr && theCount > 0 )
189     {
190         CGDirectDisplayID* theIDs = new CGDirectDisplayID[theCount];
191         err = wxOSXGetDisplayList(theCount, theIDs, &theCount);
192         wxASSERT(err == CGDisplayNoErr);
193 
194         for (nWhich = 0; nWhich < (int) theCount; ++nWhich)
195         {
196             if (theIDs[nWhich] == theID)
197                 break;
198         }
199 
200         delete [] theIDs;
201 
202         if (nWhich == (int) theCount)
203         {
204             wxFAIL_MSG(wxT("Failed to find display in display list"));
205             nWhich = wxNOT_FOUND;
206         }
207     }
208 
209     return nWhich;
210 }
211 
GetCount()212 unsigned wxDisplayFactoryMacOSX::GetCount()
213 {
214     CGDisplayCount count;
215     CGDisplayErr err = wxOSXGetDisplayList(0, NULL, &count);
216 
217     wxCHECK_MSG( err == CGDisplayNoErr, 0, "wxOSXGetDisplayList() failed" );
218 
219     return count;
220 }
221 
GetFromPoint(const wxPoint & p)222 int wxDisplayFactoryMacOSX::GetFromPoint(const wxPoint& p)
223 {
224     CGPoint thePoint = { CGFloat(p.x), CGFloat(p.y) };
225     CGDirectDisplayID theID;
226     CGDisplayCount theCount;
227     CGDisplayErr err = CGGetDisplaysWithPoint(thePoint, 1, &theID, &theCount);
228     wxASSERT(err == CGDisplayNoErr);
229     wxUnusedVar(err); // suppress "unused" warning in non-debug builds
230 
231     if (theCount)
232         return wxOSXGetDisplayFromID(theID);
233 
234     return wxNOT_FOUND;
235 }
236 
GetFromWindow(const wxWindow * window)237 int wxDisplayFactoryMacOSX::GetFromWindow(const wxWindow *window)
238 {
239     wxCHECK_MSG( window, wxNOT_FOUND, "window can't be NULL" );
240 
241     wxNonOwnedWindow* const tlw = window->MacGetTopLevelWindow();
242     // not yet instantiated
243     if ( tlw->GetWXWindow() == NULL )
244         return wxNOT_FOUND;
245 
246     int x,y,w,h;
247 
248     tlw->GetPosition(&x, &y);
249     tlw->GetSize(&w, &h);
250 
251     CGRect r = CGRectMake(x, y, w, h);
252     CGDisplayCount theCount;
253     CGDisplayErr err = CGGetDisplaysWithRect(r, 0, NULL, &theCount);
254     wxASSERT(err == CGDisplayNoErr);
255 
256     wxScopedArray<CGDirectDisplayID> theIDs(theCount);
257     err = CGGetDisplaysWithRect(r, theCount, theIDs.get(), &theCount);
258     wxASSERT(err == CGDisplayNoErr);
259 
260     const double scaleWindow = tlw->GetContentScaleFactor();
261     for ( unsigned i = 0; i < theCount; ++i )
262     {
263         // find a screen intersecting having the same contentScale as the window itself
264         double scale = wxGetScaleFactor(theIDs[i]);
265         if ( fabs(scale - scaleWindow) < 0.01 )
266         {
267             return wxOSXGetDisplayFromID(theIDs[i]);
268         }
269     }
270 
271     return wxNOT_FOUND;
272 }
273 
CreateDisplay(unsigned n)274 wxDisplayImpl *wxDisplayFactoryMacOSX::CreateDisplay(unsigned n)
275 {
276     CGDisplayCount theCount = GetCount();
277     wxScopedArray<CGDirectDisplayID> theIDs(theCount);
278 
279     CGDisplayErr err = wxOSXGetDisplayList(theCount, theIDs.get(), &theCount);
280     wxCHECK_MSG( err == CGDisplayNoErr, NULL, "wxOSXGetDisplayList() failed" );
281 
282     wxCHECK_MSG( n < theCount, NULL, wxS("Invalid display index") );
283 
284     return new wxDisplayImplMacOSX(n, theIDs[n]);
285 }
286 
287 // ============================================================================
288 // wxDisplayImplMacOSX implementation
289 // ============================================================================
290 
IsPrimary() const291 bool wxDisplayImplMacOSX::IsPrimary() const
292 {
293     return CGDisplayIsMain(m_id);
294 }
295 
GetGeometry() const296 wxRect wxDisplayImplMacOSX::GetGeometry() const
297 {
298     return wxGetDisplayGeometry(m_id);
299 }
300 
GetClientArea() const301 wxRect wxDisplayImplMacOSX::GetClientArea() const
302 {
303     return wxOSXGetDisplayClientArea(m_id);
304 }
305 
GetDepth() const306 int wxDisplayImplMacOSX::GetDepth() const
307 {
308     return wxGetDisplayDepth(m_id);
309 }
310 
GetScaleFactor() const311 double wxDisplayImplMacOSX::GetScaleFactor() const
312 {
313     return wxGetScaleFactor(m_id);
314 }
315 
wxOSXCGDisplayModeGetBitsPerPixel(CGDisplayModeRef theValue)316 static int wxOSXCGDisplayModeGetBitsPerPixel( CGDisplayModeRef theValue )
317 {
318     wxCFRef<CFStringRef> pixelEncoding( CGDisplayModeCopyPixelEncoding(theValue) );
319     int depth = 0;
320     if ( CFStringCompare( pixelEncoding, CFSTR(IO32BitDirectPixels) , kCFCompareCaseInsensitive) == kCFCompareEqualTo )
321         depth = 32;
322     else if ( CFStringCompare( pixelEncoding, CFSTR(IO16BitDirectPixels) , kCFCompareCaseInsensitive) == kCFCompareEqualTo )
323         depth = 16;
324     else if ( CFStringCompare( pixelEncoding, CFSTR(IO8BitIndexedPixels) , kCFCompareCaseInsensitive) == kCFCompareEqualTo )
325         depth = 8;
326 
327     return depth;
328 }
329 
GetModes(const wxVideoMode & mode) const330 wxArrayVideoModes wxDisplayImplMacOSX::GetModes(const wxVideoMode& mode) const
331 {
332     wxArrayVideoModes resultModes;
333 
334     wxCFRef<CFArrayRef> theArray(CGDisplayCopyAllDisplayModes( m_id ,NULL ) );
335 
336     for (CFIndex i = 0; i < CFArrayGetCount(theArray); ++i)
337     {
338         CGDisplayModeRef theValue = static_cast<CGDisplayModeRef>(const_cast<void*>(CFArrayGetValueAtIndex(theArray, i)));
339 
340         wxVideoMode theMode(
341                             CGDisplayModeGetWidth(theValue),
342                             CGDisplayModeGetHeight(theValue),
343                             wxOSXCGDisplayModeGetBitsPerPixel(theValue),
344                             CGDisplayModeGetRefreshRate(theValue));
345 
346         if (theMode.Matches( mode ))
347             resultModes.Add( theMode );
348     }
349 
350     return resultModes;
351 }
352 
GetCurrentMode() const353 wxVideoMode wxDisplayImplMacOSX::GetCurrentMode() const
354 {
355     wxCFRef<CGDisplayModeRef> theValue( CGDisplayCopyDisplayMode( m_id ) );
356 
357     return wxVideoMode(
358                        CGDisplayModeGetWidth(theValue),
359                        CGDisplayModeGetHeight(theValue),
360                        wxOSXCGDisplayModeGetBitsPerPixel(theValue),
361                        CGDisplayModeGetRefreshRate(theValue));
362 }
363 
ChangeMode(const wxVideoMode & mode)364 bool wxDisplayImplMacOSX::ChangeMode( const wxVideoMode& mode )
365 {
366 #ifndef __WXOSX_IPHONE__
367     if (mode == wxDefaultVideoMode)
368     {
369         CGRestorePermanentDisplayConfiguration();
370         return true;
371     }
372 #endif
373 
374     wxCHECK_MSG( mode.GetWidth() && mode.GetHeight(), false,
375                 wxT("at least the width and height must be specified") );
376 
377     bool bOK = false;
378     wxCFRef<CFArrayRef> theArray(CGDisplayCopyAllDisplayModes( m_id ,NULL ) );
379 
380     for (CFIndex i = 0; i < CFArrayGetCount(theArray); ++i)
381     {
382         CGDisplayModeRef theValue = static_cast<CGDisplayModeRef>(const_cast<void*>(CFArrayGetValueAtIndex(theArray, i)));
383 
384         wxVideoMode theMode(
385                             CGDisplayModeGetWidth(theValue),
386                             CGDisplayModeGetHeight(theValue),
387                             wxOSXCGDisplayModeGetBitsPerPixel(theValue),
388                             CGDisplayModeGetRefreshRate(theValue));
389 
390         if ( theMode.GetWidth() == mode.GetWidth() && theMode.GetHeight() == mode.GetHeight() &&
391             ( mode.GetDepth() == 0 || theMode.GetDepth() == mode.GetDepth() ) &&
392             ( mode.GetRefresh() == 0 || theMode.GetRefresh() == mode.GetRefresh() ) )
393         {
394             CGDisplaySetDisplayMode( m_id, theValue , NULL );
395             bOK = true;
396             break;
397         }
398     }
399 
400     return bOK;
401 }
402 
403 // ============================================================================
404 // wxDisplay::CreateFactory()
405 // ============================================================================
406 
CreateFactory()407 /* static */ wxDisplayFactory *wxDisplay::CreateFactory()
408 {
409     return new wxDisplayFactoryMacOSX;
410 }
411 
412 #else // !wxUSE_DISPLAY
413 
414 class wxDisplayImplSingleMacOSX : public wxDisplayImplSingle
415 {
416 public:
GetGeometry() const417     virtual wxRect GetGeometry() const wxOVERRIDE
418     {
419         return wxGetDisplayGeometry(CGMainDisplayID());
420     }
421 
GetClientArea() const422     virtual wxRect GetClientArea() const wxOVERRIDE
423     {
424         return wxOSXGetMainDisplayClientArea();
425     }
426 
GetDepth() const427     virtual int GetDepth() const wxOVERRIDE
428     {
429         return wxGetDisplayDepth(CGMainDisplayID());
430     }
431 };
432 
433 class wxDisplayFactorySingleMacOSX : public wxDisplayFactorySingle
434 {
435 protected:
CreateSingleDisplay()436     virtual wxDisplayImpl *CreateSingleDisplay() wxOVERRIDE
437     {
438         return new wxDisplayImplSingleMacOSX;
439     }
440 };
441 
CreateFactory()442 /* static */ wxDisplayFactory *wxDisplay::CreateFactory()
443 {
444     return new wxDisplayFactorySingleMacOSX;
445 }
446 
447 #endif // wxUSE_DISPLAY
448