SDL 2.0
SDL_cocoamodes.m
Go to the documentation of this file.
1/*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2019 Sam Lantinga <slouken@libsdl.org>
4
5 This software is provided 'as-is', without any express or implied
6 warranty. In no event will the authors be held liable for any damages
7 arising from the use of this software.
8
9 Permission is granted to anyone to use this software for any purpose,
10 including commercial applications, and to alter it and redistribute it
11 freely, subject to the following restrictions:
12
13 1. The origin of this software must not be misrepresented; you must not
14 claim that you wrote the original software. If you use this software
15 in a product, an acknowledgment in the product documentation would be
16 appreciated but is not required.
17 2. Altered source versions must be plainly marked as such, and must not be
18 misrepresented as being the original software.
19 3. This notice may not be removed or altered from any source distribution.
20*/
21#include "../../SDL_internal.h"
22#include "SDL_assert.h"
23
24#if SDL_VIDEO_DRIVER_COCOA
25
26#include "SDL_cocoavideo.h"
27
28/* We need this for IODisplayCreateInfoDictionary and kIODisplayOnlyPreferredName */
29#include <IOKit/graphics/IOGraphicsLib.h>
30
31/* We need this for CVDisplayLinkGetNominalOutputVideoRefreshPeriod */
32#include <CoreVideo/CVBase.h>
33#include <CoreVideo/CVDisplayLink.h>
34
35/* we need this for ShowMenuBar() and HideMenuBar(). */
36#include <Carbon/Carbon.h>
37
38/* This gets us MAC_OS_X_VERSION_MIN_REQUIRED... */
39#include <AvailabilityMacros.h>
40
41
42static void
43Cocoa_ToggleMenuBar(const BOOL show)
44{
45 /* !!! FIXME: keep an eye on this.
46 * ShowMenuBar/HideMenuBar is officially unavailable for 64-bit binaries.
47 * It happens to work, as of 10.7, but we're going to see if
48 * we can just simply do without it on newer OSes...
49 */
50#if (MAC_OS_X_VERSION_MIN_REQUIRED < 1070) && !defined(__LP64__)
51 if (show) {
52 ShowMenuBar();
53 } else {
54 HideMenuBar();
55 }
56#endif
57}
58
59static int
60CG_SetError(const char *prefix, CGDisplayErr result)
61{
62 const char *error;
63
64 switch (result) {
65 case kCGErrorFailure:
66 error = "kCGErrorFailure";
67 break;
68 case kCGErrorIllegalArgument:
69 error = "kCGErrorIllegalArgument";
70 break;
71 case kCGErrorInvalidConnection:
72 error = "kCGErrorInvalidConnection";
73 break;
74 case kCGErrorInvalidContext:
75 error = "kCGErrorInvalidContext";
76 break;
77 case kCGErrorCannotComplete:
78 error = "kCGErrorCannotComplete";
79 break;
80 case kCGErrorNotImplemented:
81 error = "kCGErrorNotImplemented";
82 break;
83 case kCGErrorRangeCheck:
84 error = "kCGErrorRangeCheck";
85 break;
86 case kCGErrorTypeCheck:
87 error = "kCGErrorTypeCheck";
88 break;
89 case kCGErrorInvalidOperation:
90 error = "kCGErrorInvalidOperation";
91 break;
92 case kCGErrorNoneAvailable:
93 error = "kCGErrorNoneAvailable";
94 break;
95 default:
96 error = "Unknown Error";
97 break;
98 }
99 return SDL_SetError("%s: %s", prefix, error);
100}
101
102static SDL_bool
103GetDisplayMode(_THIS, CGDisplayModeRef vidmode, CVDisplayLinkRef link, SDL_DisplayMode *mode)
104{
106 int width = 0;
107 int height = 0;
108 int bpp = 0;
109 int refreshRate = 0;
110 CFStringRef fmt;
111
112 data = (SDL_DisplayModeData *) SDL_malloc(sizeof(*data));
113 if (!data) {
114 return SDL_FALSE;
115 }
116 data->moderef = vidmode;
117
118 fmt = CGDisplayModeCopyPixelEncoding(vidmode);
119 width = (int) CGDisplayModeGetWidth(vidmode);
120 height = (int) CGDisplayModeGetHeight(vidmode);
121 refreshRate = (int) (CGDisplayModeGetRefreshRate(vidmode) + 0.5);
122
123 if (CFStringCompare(fmt, CFSTR(IO32BitDirectPixels),
124 kCFCompareCaseInsensitive) == kCFCompareEqualTo) {
125 bpp = 32;
126 } else if (CFStringCompare(fmt, CFSTR(IO16BitDirectPixels),
127 kCFCompareCaseInsensitive) == kCFCompareEqualTo) {
128 bpp = 16;
129 } else if (CFStringCompare(fmt, CFSTR(kIO30BitDirectPixels),
130 kCFCompareCaseInsensitive) == kCFCompareEqualTo) {
131 bpp = 30;
132 } else {
133 bpp = 0; /* ignore 8-bit and such for now. */
134 }
135
136 CFRelease(fmt);
137
138 /* CGDisplayModeGetRefreshRate returns 0 for many non-CRT displays. */
139 if (refreshRate == 0 && link != NULL) {
140 CVTime time = CVDisplayLinkGetNominalOutputVideoRefreshPeriod(link);
141 if ((time.flags & kCVTimeIsIndefinite) == 0 && time.timeValue != 0) {
142 refreshRate = (int) ((time.timeScale / (double) time.timeValue) + 0.5);
143 }
144 }
145
147 switch (bpp) {
148 case 16:
150 break;
151 case 30:
153 break;
154 case 32:
156 break;
157 case 8: /* We don't support palettized modes now */
158 default: /* Totally unrecognizable bit depth. */
159 SDL_free(data);
160 return SDL_FALSE;
161 }
162 mode->w = width;
163 mode->h = height;
164 mode->refresh_rate = refreshRate;
165 mode->driverdata = data;
166 return SDL_TRUE;
167}
168
169static const char *
170Cocoa_GetDisplayName(CGDirectDisplayID displayID)
171{
172 CFDictionaryRef deviceInfo = IODisplayCreateInfoDictionary(CGDisplayIOServicePort(displayID), kIODisplayOnlyPreferredName);
173 NSDictionary *localizedNames = [(NSDictionary *)deviceInfo objectForKey:[NSString stringWithUTF8String:kDisplayProductName]];
174 const char* displayName = NULL;
175
176 if ([localizedNames count] > 0) {
177 displayName = SDL_strdup([[localizedNames objectForKey:[[localizedNames allKeys] objectAtIndex:0]] UTF8String]);
178 }
179 CFRelease(deviceInfo);
180 return displayName;
181}
182
183void
185{ @autoreleasepool
186{
187 CGDisplayErr result;
188 CGDirectDisplayID *displays;
189 CGDisplayCount numDisplays;
190 SDL_bool isstack;
191 int pass, i;
192
193 result = CGGetOnlineDisplayList(0, NULL, &numDisplays);
194 if (result != kCGErrorSuccess) {
195 CG_SetError("CGGetOnlineDisplayList()", result);
196 return;
197 }
198 displays = SDL_small_alloc(CGDirectDisplayID, numDisplays, &isstack);
199 result = CGGetOnlineDisplayList(numDisplays, displays, &numDisplays);
200 if (result != kCGErrorSuccess) {
201 CG_SetError("CGGetOnlineDisplayList()", result);
202 SDL_small_free(displays, isstack);
203 return;
204 }
205
206 /* Pick up the primary display in the first pass, then get the rest */
207 for (pass = 0; pass < 2; ++pass) {
208 for (i = 0; i < numDisplays; ++i) {
209 SDL_VideoDisplay display;
210 SDL_DisplayData *displaydata;
212 CGDisplayModeRef moderef = NULL;
213 CVDisplayLinkRef link = NULL;
214
215 if (pass == 0) {
216 if (!CGDisplayIsMain(displays[i])) {
217 continue;
218 }
219 } else {
220 if (CGDisplayIsMain(displays[i])) {
221 continue;
222 }
223 }
224
225 if (CGDisplayMirrorsDisplay(displays[i]) != kCGNullDirectDisplay) {
226 continue;
227 }
228
229 moderef = CGDisplayCopyDisplayMode(displays[i]);
230
231 if (!moderef) {
232 continue;
233 }
234
235 displaydata = (SDL_DisplayData *) SDL_malloc(sizeof(*displaydata));
236 if (!displaydata) {
237 CGDisplayModeRelease(moderef);
238 continue;
239 }
240 displaydata->display = displays[i];
241
242 CVDisplayLinkCreateWithCGDisplay(displays[i], &link);
243
244 SDL_zero(display);
245 /* this returns a stddup'ed string */
246 display.name = (char *)Cocoa_GetDisplayName(displays[i]);
247 if (!GetDisplayMode(_this, moderef, link, &mode)) {
248 CVDisplayLinkRelease(link);
249 CGDisplayModeRelease(moderef);
250 SDL_free(display.name);
251 SDL_free(displaydata);
252 continue;
253 }
254
255 CVDisplayLinkRelease(link);
256
257 display.desktop_mode = mode;
258 display.current_mode = mode;
259 display.driverdata = displaydata;
260 SDL_AddVideoDisplay(&display);
261 SDL_free(display.name);
262 }
263 }
264 SDL_small_free(displays, isstack);
265}}
266
267int
269{
270 SDL_DisplayData *displaydata = (SDL_DisplayData *) display->driverdata;
271 CGRect cgrect;
272
273 cgrect = CGDisplayBounds(displaydata->display);
274 rect->x = (int)cgrect.origin.x;
275 rect->y = (int)cgrect.origin.y;
276 rect->w = (int)cgrect.size.width;
277 rect->h = (int)cgrect.size.height;
278 return 0;
279}
280
281int
283{
284 SDL_DisplayData *displaydata = (SDL_DisplayData *) display->driverdata;
285 const CGDirectDisplayID cgdisplay = displaydata->display;
286 NSArray *screens = [NSScreen screens];
287 NSScreen *screen = nil;
288
289 /* !!! FIXME: maybe track the NSScreen in SDL_DisplayData? */
290 for (NSScreen *i in screens) {
291 const CGDirectDisplayID thisDisplay = (CGDirectDisplayID) [[[i deviceDescription] objectForKey:@"NSScreenNumber"] unsignedIntValue];
292 if (thisDisplay == cgdisplay) {
293 screen = i;
294 break;
295 }
296 }
297
298 SDL_assert(screen != nil); /* didn't find it?! */
299 if (screen == nil) {
300 return -1;
301 }
302
303 const NSRect frame = [screen visibleFrame];
304 rect->x = (int)frame.origin.x;
305 rect->y = (int)(CGDisplayPixelsHigh(kCGDirectMainDisplay) - frame.origin.y - frame.size.height);
306 rect->w = (int)frame.size.width;
307 rect->h = (int)frame.size.height;
308
309 return 0;
310}
311
312int
313Cocoa_GetDisplayDPI(_THIS, SDL_VideoDisplay * display, float * ddpi, float * hdpi, float * vdpi)
314{
315 const float MM_IN_INCH = 25.4f;
316
318
319 CGSize displaySize = CGDisplayScreenSize(data->display);
320 int pixelWidth = (int) CGDisplayPixelsWide(data->display);
321 int pixelHeight = (int) CGDisplayPixelsHigh(data->display);
322
323 if (ddpi) {
324 *ddpi = SDL_ComputeDiagonalDPI(pixelWidth, pixelHeight, displaySize.width / MM_IN_INCH, displaySize.height / MM_IN_INCH);
325 }
326 if (hdpi) {
327 *hdpi = pixelWidth * MM_IN_INCH / displaySize.width;
328 }
329 if (vdpi) {
330 *vdpi = pixelHeight * MM_IN_INCH / displaySize.height;
331 }
332
333 return 0;
334}
335
336void
338{
340 CVDisplayLinkRef link = NULL;
341 CGDisplayModeRef desktopmoderef;
342 SDL_DisplayMode desktopmode;
343 CFArrayRef modes;
344
345 CVDisplayLinkCreateWithCGDisplay(data->display, &link);
346
347 desktopmoderef = CGDisplayCopyDisplayMode(data->display);
348
349 /* CopyAllDisplayModes won't always contain the desktop display mode (if
350 * NULL is passed in) - for example on a retina 15" MBP, System Preferences
351 * allows choosing 1920x1200 but it's not in the list. AddDisplayMode makes
352 * sure there are no duplicates so it's safe to always add the desktop mode
353 * even in cases where it is in the CopyAllDisplayModes list.
354 */
355 if (desktopmoderef && GetDisplayMode(_this, desktopmoderef, link, &desktopmode)) {
356 if (!SDL_AddDisplayMode(display, &desktopmode)) {
357 CGDisplayModeRelease(desktopmoderef);
358 SDL_free(desktopmode.driverdata);
359 }
360 } else {
361 CGDisplayModeRelease(desktopmoderef);
362 }
363
364 modes = CGDisplayCopyAllDisplayModes(data->display, NULL);
365
366 if (modes) {
367 CFIndex i;
368 const CFIndex count = CFArrayGetCount(modes);
369
370 for (i = 0; i < count; i++) {
371 CGDisplayModeRef moderef = (CGDisplayModeRef) CFArrayGetValueAtIndex(modes, i);
373
374 if (GetDisplayMode(_this, moderef, link, &mode)) {
375 if (SDL_AddDisplayMode(display, &mode)) {
376 CGDisplayModeRetain(moderef);
377 } else {
378 SDL_free(mode.driverdata);
379 }
380 }
381 }
382
383 CFRelease(modes);
384 }
385
386 CVDisplayLinkRelease(link);
387}
388
389int
391{
392 SDL_DisplayData *displaydata = (SDL_DisplayData *) display->driverdata;
394 CGDisplayFadeReservationToken fade_token = kCGDisplayFadeReservationInvalidToken;
395 CGError result;
396
397 /* Fade to black to hide resolution-switching flicker */
398 if (CGAcquireDisplayFadeReservation(5, &fade_token) == kCGErrorSuccess) {
399 CGDisplayFade(fade_token, 0.3, kCGDisplayBlendNormal, kCGDisplayBlendSolidColor, 0.0, 0.0, 0.0, TRUE);
400 }
401
402 if (data == display->desktop_mode.driverdata) {
403 /* Restoring desktop mode */
404 CGDisplaySetDisplayMode(displaydata->display, data->moderef, NULL);
405
406 if (CGDisplayIsMain(displaydata->display)) {
407 CGReleaseAllDisplays();
408 } else {
409 CGDisplayRelease(displaydata->display);
410 }
411
412 if (CGDisplayIsMain(displaydata->display)) {
413 Cocoa_ToggleMenuBar(YES);
414 }
415 } else {
416 /* Put up the blanking window (a window above all other windows) */
417 if (CGDisplayIsMain(displaydata->display)) {
418 /* If we don't capture all displays, Cocoa tries to rearrange windows... *sigh* */
419 result = CGCaptureAllDisplays();
420 } else {
421 result = CGDisplayCapture(displaydata->display);
422 }
423 if (result != kCGErrorSuccess) {
424 CG_SetError("CGDisplayCapture()", result);
425 goto ERR_NO_CAPTURE;
426 }
427
428 /* Do the physical switch */
429 result = CGDisplaySetDisplayMode(displaydata->display, data->moderef, NULL);
430 if (result != kCGErrorSuccess) {
431 CG_SetError("CGDisplaySwitchToMode()", result);
432 goto ERR_NO_SWITCH;
433 }
434
435 /* Hide the menu bar so it doesn't intercept events */
436 if (CGDisplayIsMain(displaydata->display)) {
437 Cocoa_ToggleMenuBar(NO);
438 }
439 }
440
441 /* Fade in again (asynchronously) */
442 if (fade_token != kCGDisplayFadeReservationInvalidToken) {
443 CGDisplayFade(fade_token, 0.5, kCGDisplayBlendSolidColor, kCGDisplayBlendNormal, 0.0, 0.0, 0.0, FALSE);
444 CGReleaseDisplayFadeReservation(fade_token);
445 }
446
447 return 0;
448
449 /* Since the blanking window covers *all* windows (even force quit) correct recovery is crucial */
450ERR_NO_SWITCH:
451 CGDisplayRelease(displaydata->display);
452ERR_NO_CAPTURE:
453 if (fade_token != kCGDisplayFadeReservationInvalidToken) {
454 CGDisplayFade (fade_token, 0.5, kCGDisplayBlendSolidColor, kCGDisplayBlendNormal, 0.0, 0.0, 0.0, FALSE);
455 CGReleaseDisplayFadeReservation(fade_token);
456 }
457 return -1;
458}
459
460void
462{
463 int i, j;
464
465 for (i = 0; i < _this->num_displays; ++i) {
466 SDL_VideoDisplay *display = &_this->displays[i];
468
469 if (display->current_mode.driverdata != display->desktop_mode.driverdata) {
470 Cocoa_SetDisplayMode(_this, display, &display->desktop_mode);
471 }
472
474 CGDisplayModeRelease(mode->moderef);
475
476 for (j = 0; j < display->num_display_modes; j++) {
478 CGDisplayModeRelease(mode->moderef);
479 }
480
481 }
482 Cocoa_ToggleMenuBar(YES);
483}
484
485#endif /* SDL_VIDEO_DRIVER_COCOA */
486
487/* vi: set ts=4 sw=4 expandtab: */
#define _THIS
#define SDL_assert(condition)
Definition: SDL_assert.h:169
void Cocoa_QuitModes(_THIS)
int Cocoa_GetDisplayDPI(_THIS, SDL_VideoDisplay *display, float *ddpi, float *hpdi, float *vdpi)
int Cocoa_SetDisplayMode(_THIS, SDL_VideoDisplay *display, SDL_DisplayMode *mode)
int Cocoa_GetDisplayUsableBounds(_THIS, SDL_VideoDisplay *display, SDL_Rect *rect)
void Cocoa_GetDisplayModes(_THIS, SDL_VideoDisplay *display)
void Cocoa_InitModes(_THIS)
int Cocoa_GetDisplayBounds(_THIS, SDL_VideoDisplay *display, SDL_Rect *rect)
#define SDL_SetError
#define SDL_malloc
#define SDL_free
#define SDL_strdup
#define SDL_small_alloc(type, count, pisstack)
Definition: SDL_internal.h:39
#define SDL_small_free(ptr, isstack)
Definition: SDL_internal.h:40
GLint GLint GLint GLint GLint GLint y
Definition: SDL_opengl.h:1574
GLuint GLuint GLsizei count
Definition: SDL_opengl.h:1571
GLint GLenum GLsizei GLsizei GLsizei GLint GLsizei const GLvoid * data
Definition: SDL_opengl.h:1974
GLint GLint GLsizei width
Definition: SDL_opengl.h:1572
GLint GLint GLsizei GLsizei height
Definition: SDL_opengl.h:1572
GLenum mode
GLuint64EXT * result
GLuint in
GLsizeiptr size
@ SDL_PIXELFORMAT_ARGB1555
Definition: SDL_pixels.h:212
@ SDL_PIXELFORMAT_ARGB8888
Definition: SDL_pixels.h:248
@ SDL_PIXELFORMAT_ARGB2101010
Definition: SDL_pixels.h:260
@ SDL_PIXELFORMAT_UNKNOWN
Definition: SDL_pixels.h:173
#define SDL_zero(x)
Definition: SDL_stdinc.h:416
SDL_bool
Definition: SDL_stdinc.h:162
@ SDL_TRUE
Definition: SDL_stdinc.h:164
@ SDL_FALSE
Definition: SDL_stdinc.h:163
int SDL_AddVideoDisplay(const SDL_VideoDisplay *display)
Definition: SDL_video.c:603
float SDL_ComputeDiagonalDPI(int hpix, int vpix, float hinches, float vinches)
Definition: SDL_video.c:4015
SDL_bool SDL_AddDisplayMode(SDL_VideoDisplay *display, const SDL_DisplayMode *mode)
Definition: SDL_video.c:751
static SDL_VideoDevice * _this
Definition: SDL_video.c:118
return Display return Display Bool Bool int int int return Display XEvent Bool(*) XPointer return Display return Display Drawable _Xconst char unsigned int unsigned int return Display Pixmap Pixmap XColor XColor unsigned int unsigned int return Display _Xconst char char int char return Display Visual unsigned int int int char unsigned int unsigned int int in j)
Definition: SDL_x11sym.h:50
return Display return Display Bool Bool int int int return Display XEvent Bool(*) XPointer return Display return Display Drawable _Xconst char unsigned int unsigned int return Display Pixmap Pixmap XColor XColor unsigned int unsigned int return Display _Xconst char char int char return Display Visual unsigned int int int char unsigned int unsigned int in i)
Definition: SDL_x11sym.h:50
#define NULL
Definition: begin_code.h:167
#define TRUE
Definition: edid-parse.c:33
#define FALSE
Definition: edid-parse.c:34
EGLSurface EGLnsecsANDROID time
Definition: eglext.h:518
CGDirectDisplayID display
The structure that defines a display mode.
Definition: SDL_video.h:54
void * driverdata
Definition: SDL_video.h:59
A rectangle, with the origin at the upper left (integer).
Definition: SDL_rect.h:78
int h
Definition: SDL_rect.h:80
int w
Definition: SDL_rect.h:80
int y
Definition: SDL_rect.h:79
int x
Definition: SDL_rect.h:79
SDL_VideoDisplay * displays
Definition: SDL_sysvideo.h:316
SDL_DisplayMode desktop_mode
Definition: SDL_sysvideo.h:131
SDL_DisplayMode * display_modes
Definition: SDL_sysvideo.h:130
SDL_DisplayMode current_mode
Definition: SDL_sysvideo.h:132
SDL_Renderer * screen
SDL_Rect rect
Definition: testrelative.c:27
int frame
Definition: teststreaming.c:60