src/video/cocoa/SDL_cocoamodes.m
author Ryan C. Gordon
Mon, 23 Jan 2017 12:06:10 -0500
changeset 10837 c2f241c2f6ad
parent 10737 3406a0f8b041
child 11142 1e26564c7288
permissions -rw-r--r--
audio: Fix same bug as last commit, but for _mm_bslli_si128 vs _mm_slli_si128.
     1 /*
     2   Simple DirectMedia Layer
     3   Copyright (C) 1997-2017 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 
    42 static void
    43 Cocoa_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 
    59 static int
    60 CG_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 
   102 static SDL_bool
   103 GetDisplayMode(_THIS, CGDisplayModeRef vidmode, CVDisplayLinkRef link, SDL_DisplayMode *mode)
   104 {
   105     SDL_DisplayModeData *data;
   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 
   146     mode->format = SDL_PIXELFORMAT_UNKNOWN;
   147     switch (bpp) {
   148     case 16:
   149         mode->format = SDL_PIXELFORMAT_ARGB1555;
   150         break;
   151     case 30:
   152         mode->format = SDL_PIXELFORMAT_ARGB2101010;
   153         break;
   154     case 32:
   155         mode->format = SDL_PIXELFORMAT_ARGB8888;
   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 
   169 static const char *
   170 Cocoa_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 
   183 void
   184 Cocoa_InitModes(_THIS)
   185 { @autoreleasepool
   186 {
   187     CGDisplayErr result;
   188     CGDirectDisplayID *displays;
   189     CGDisplayCount numDisplays;
   190     int pass, i;
   191 
   192     result = CGGetOnlineDisplayList(0, NULL, &numDisplays);
   193     if (result != kCGErrorSuccess) {
   194         CG_SetError("CGGetOnlineDisplayList()", result);
   195         return;
   196     }
   197     displays = SDL_stack_alloc(CGDirectDisplayID, numDisplays);
   198     result = CGGetOnlineDisplayList(numDisplays, displays, &numDisplays);
   199     if (result != kCGErrorSuccess) {
   200         CG_SetError("CGGetOnlineDisplayList()", result);
   201         SDL_stack_free(displays);
   202         return;
   203     }
   204 
   205     /* Pick up the primary display in the first pass, then get the rest */
   206     for (pass = 0; pass < 2; ++pass) {
   207         for (i = 0; i < numDisplays; ++i) {
   208             SDL_VideoDisplay display;
   209             SDL_DisplayData *displaydata;
   210             SDL_DisplayMode mode;
   211             CGDisplayModeRef moderef = NULL;
   212             CVDisplayLinkRef link = NULL;
   213 
   214             if (pass == 0) {
   215                 if (!CGDisplayIsMain(displays[i])) {
   216                     continue;
   217                 }
   218             } else {
   219                 if (CGDisplayIsMain(displays[i])) {
   220                     continue;
   221                 }
   222             }
   223 
   224             if (CGDisplayMirrorsDisplay(displays[i]) != kCGNullDirectDisplay) {
   225                 continue;
   226             }
   227 
   228             moderef = CGDisplayCopyDisplayMode(displays[i]);
   229 
   230             if (!moderef) {
   231                 continue;
   232             }
   233 
   234             displaydata = (SDL_DisplayData *) SDL_malloc(sizeof(*displaydata));
   235             if (!displaydata) {
   236                 CGDisplayModeRelease(moderef);
   237                 continue;
   238             }
   239             displaydata->display = displays[i];
   240 
   241             CVDisplayLinkCreateWithCGDisplay(displays[i], &link);
   242 
   243             SDL_zero(display);
   244             /* this returns a stddup'ed string */
   245             display.name = (char *)Cocoa_GetDisplayName(displays[i]);
   246             if (!GetDisplayMode(_this, moderef, link, &mode)) {
   247                 CVDisplayLinkRelease(link);
   248                 CGDisplayModeRelease(moderef);
   249                 SDL_free(display.name);
   250                 SDL_free(displaydata);
   251                 continue;
   252             }
   253 
   254             CVDisplayLinkRelease(link);
   255 
   256             display.desktop_mode = mode;
   257             display.current_mode = mode;
   258             display.driverdata = displaydata;
   259             SDL_AddVideoDisplay(&display);
   260             SDL_free(display.name);
   261         }
   262     }
   263     SDL_stack_free(displays);
   264 }}
   265 
   266 int
   267 Cocoa_GetDisplayBounds(_THIS, SDL_VideoDisplay * display, SDL_Rect * rect)
   268 {
   269     SDL_DisplayData *displaydata = (SDL_DisplayData *) display->driverdata;
   270     CGRect cgrect;
   271 
   272     cgrect = CGDisplayBounds(displaydata->display);
   273     rect->x = (int)cgrect.origin.x;
   274     rect->y = (int)cgrect.origin.y;
   275     rect->w = (int)cgrect.size.width;
   276     rect->h = (int)cgrect.size.height;
   277     return 0;
   278 }
   279 
   280 int
   281 Cocoa_GetDisplayUsableBounds(_THIS, SDL_VideoDisplay * display, SDL_Rect * rect)
   282 {
   283     SDL_DisplayData *displaydata = (SDL_DisplayData *) display->driverdata;
   284     const CGDirectDisplayID cgdisplay = displaydata->display;
   285     NSArray *screens = [NSScreen screens];
   286     NSScreen *screen = nil;
   287 
   288     /* !!! FIXME: maybe track the NSScreen in SDL_DisplayData? */
   289     for (NSScreen *i in screens) {
   290         const CGDirectDisplayID thisDisplay = (CGDirectDisplayID) [[[i deviceDescription] objectForKey:@"NSScreenNumber"] unsignedIntValue];
   291         if (thisDisplay == cgdisplay) {
   292             screen = i;
   293             break;
   294         }
   295     }
   296 
   297     SDL_assert(screen != nil);  /* didn't find it?! */
   298     if (screen == nil) {
   299         return -1;
   300     }
   301 
   302     const CGRect cgrect = CGDisplayBounds(cgdisplay);
   303     const NSRect frame = [screen visibleFrame];
   304 
   305     // !!! FIXME: I assume -[NSScreen visibleFrame] is relative to the origin of the screen in question and not the whole desktop.
   306     // !!! FIXME: The math vs CGDisplayBounds might be incorrect if that's not the case, though. Check this.
   307     rect->x = (int)(cgrect.origin.x + frame.origin.x);
   308     rect->y = (int)(cgrect.origin.y + frame.origin.y);
   309     rect->w = (int)frame.size.width;
   310     rect->h = (int)frame.size.height;
   311 
   312     return 0;
   313 }
   314 
   315 int
   316 Cocoa_GetDisplayDPI(_THIS, SDL_VideoDisplay * display, float * ddpi, float * hdpi, float * vdpi)
   317 {
   318     const float MM_IN_INCH = 25.4f;
   319 
   320     SDL_DisplayData *data = (SDL_DisplayData *) display->driverdata;
   321 
   322     CGSize displaySize = CGDisplayScreenSize(data->display);
   323     int pixelWidth =  (int) CGDisplayPixelsWide(data->display);
   324     int pixelHeight = (int) CGDisplayPixelsHigh(data->display);
   325 
   326     if (ddpi) {
   327         *ddpi = SDL_ComputeDiagonalDPI(pixelWidth, pixelHeight, displaySize.width / MM_IN_INCH, displaySize.height / MM_IN_INCH);
   328     }
   329     if (hdpi) {
   330         *hdpi = pixelWidth * MM_IN_INCH / displaySize.width;
   331     }
   332     if (vdpi) {
   333         *vdpi = pixelHeight * MM_IN_INCH / displaySize.height;
   334     }
   335 
   336     return 0;
   337 }
   338 
   339 void
   340 Cocoa_GetDisplayModes(_THIS, SDL_VideoDisplay * display)
   341 {
   342     SDL_DisplayData *data = (SDL_DisplayData *) display->driverdata;
   343     CFArrayRef modes = CGDisplayCopyAllDisplayModes(data->display, NULL);
   344 
   345     if (modes) {
   346         CVDisplayLinkRef link = NULL;
   347         const CFIndex count = CFArrayGetCount(modes);
   348         CFIndex i;
   349 
   350         CVDisplayLinkCreateWithCGDisplay(data->display, &link);
   351 
   352         for (i = 0; i < count; i++) {
   353             CGDisplayModeRef moderef = (CGDisplayModeRef) CFArrayGetValueAtIndex(modes, i);
   354             SDL_DisplayMode mode;
   355             if (GetDisplayMode(_this, moderef, link, &mode)) {
   356                 CGDisplayModeRetain(moderef);
   357                 SDL_AddDisplayMode(display, &mode);
   358             }
   359         }
   360 
   361         CVDisplayLinkRelease(link);
   362         CFRelease(modes);
   363     }
   364 }
   365 
   366 int
   367 Cocoa_SetDisplayMode(_THIS, SDL_VideoDisplay * display, SDL_DisplayMode * mode)
   368 {
   369     SDL_DisplayData *displaydata = (SDL_DisplayData *) display->driverdata;
   370     SDL_DisplayModeData *data = (SDL_DisplayModeData *) mode->driverdata;
   371     CGDisplayFadeReservationToken fade_token = kCGDisplayFadeReservationInvalidToken;
   372     CGError result;
   373 
   374     /* Fade to black to hide resolution-switching flicker */
   375     if (CGAcquireDisplayFadeReservation(5, &fade_token) == kCGErrorSuccess) {
   376         CGDisplayFade(fade_token, 0.3, kCGDisplayBlendNormal, kCGDisplayBlendSolidColor, 0.0, 0.0, 0.0, TRUE);
   377     }
   378 
   379     if (data == display->desktop_mode.driverdata) {
   380         /* Restoring desktop mode */
   381         CGDisplaySetDisplayMode(displaydata->display, data->moderef, NULL);
   382 
   383         if (CGDisplayIsMain(displaydata->display)) {
   384             CGReleaseAllDisplays();
   385         } else {
   386             CGDisplayRelease(displaydata->display);
   387         }
   388 
   389         if (CGDisplayIsMain(displaydata->display)) {
   390             Cocoa_ToggleMenuBar(YES);
   391         }
   392     } else {
   393         /* Put up the blanking window (a window above all other windows) */
   394         if (CGDisplayIsMain(displaydata->display)) {
   395             /* If we don't capture all displays, Cocoa tries to rearrange windows... *sigh* */
   396             result = CGCaptureAllDisplays();
   397         } else {
   398             result = CGDisplayCapture(displaydata->display);
   399         }
   400         if (result != kCGErrorSuccess) {
   401             CG_SetError("CGDisplayCapture()", result);
   402             goto ERR_NO_CAPTURE;
   403         }
   404 
   405         /* Do the physical switch */
   406         result = CGDisplaySetDisplayMode(displaydata->display, data->moderef, NULL);
   407         if (result != kCGErrorSuccess) {
   408             CG_SetError("CGDisplaySwitchToMode()", result);
   409             goto ERR_NO_SWITCH;
   410         }
   411 
   412         /* Hide the menu bar so it doesn't intercept events */
   413         if (CGDisplayIsMain(displaydata->display)) {
   414             Cocoa_ToggleMenuBar(NO);
   415         }
   416     }
   417 
   418     /* Fade in again (asynchronously) */
   419     if (fade_token != kCGDisplayFadeReservationInvalidToken) {
   420         CGDisplayFade(fade_token, 0.5, kCGDisplayBlendSolidColor, kCGDisplayBlendNormal, 0.0, 0.0, 0.0, FALSE);
   421         CGReleaseDisplayFadeReservation(fade_token);
   422     }
   423 
   424     return 0;
   425 
   426     /* Since the blanking window covers *all* windows (even force quit) correct recovery is crucial */
   427 ERR_NO_SWITCH:
   428     CGDisplayRelease(displaydata->display);
   429 ERR_NO_CAPTURE:
   430     if (fade_token != kCGDisplayFadeReservationInvalidToken) {
   431         CGDisplayFade (fade_token, 0.5, kCGDisplayBlendSolidColor, kCGDisplayBlendNormal, 0.0, 0.0, 0.0, FALSE);
   432         CGReleaseDisplayFadeReservation(fade_token);
   433     }
   434     return -1;
   435 }
   436 
   437 void
   438 Cocoa_QuitModes(_THIS)
   439 {
   440     int i, j;
   441 
   442     for (i = 0; i < _this->num_displays; ++i) {
   443         SDL_VideoDisplay *display = &_this->displays[i];
   444         SDL_DisplayModeData *mode;
   445 
   446         if (display->current_mode.driverdata != display->desktop_mode.driverdata) {
   447             Cocoa_SetDisplayMode(_this, display, &display->desktop_mode);
   448         }
   449 
   450         mode = (SDL_DisplayModeData *) display->desktop_mode.driverdata;
   451         CGDisplayModeRelease(mode->moderef);
   452 
   453         for (j = 0; j < display->num_display_modes; j++) {
   454             mode = (SDL_DisplayModeData*) display->display_modes[j].driverdata;
   455             CGDisplayModeRelease(mode->moderef);
   456         }
   457 
   458     }
   459     Cocoa_ToggleMenuBar(YES);
   460 }
   461 
   462 #endif /* SDL_VIDEO_DRIVER_COCOA */
   463 
   464 /* vi: set ts=4 sw=4 expandtab: */