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