cscg22-gearboy

CSCG 2022 Challenge 'Gearboy'
git clone https://git.sinitax.com/sinitax/cscg22-gearboy
Log | Files | Refs | sfeed.txt

SDL_cocoamodes.m (15674B)


      1/*
      2  Simple DirectMedia Layer
      3  Copyright (C) 1997-2014 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
     23#if SDL_VIDEO_DRIVER_COCOA
     24
     25#include "SDL_cocoavideo.h"
     26
     27/* We need this for IODisplayCreateInfoDictionary and kIODisplayOnlyPreferredName */
     28#include <IOKit/graphics/IOGraphicsLib.h>
     29
     30/* We need this for CVDisplayLinkGetNominalOutputVideoRefreshPeriod */
     31#include <CoreVideo/CVBase.h>
     32#include <CoreVideo/CVDisplayLink.h>
     33
     34/* we need this for ShowMenuBar() and HideMenuBar(). */
     35#include <Carbon/Carbon.h>
     36
     37/* This gets us MAC_OS_X_VERSION_MIN_REQUIRED... */
     38#include <AvailabilityMacros.h>
     39
     40
     41static void
     42Cocoa_ToggleMenuBar(const BOOL show)
     43{
     44    /* !!! FIXME: keep an eye on this.
     45     * ShowMenuBar/HideMenuBar is officially unavailable for 64-bit binaries.
     46     *  It happens to work, as of 10.7, but we're going to see if
     47     *  we can just simply do without it on newer OSes...
     48     */
     49#if (MAC_OS_X_VERSION_MIN_REQUIRED < 1070) && !defined(__LP64__)
     50    if (show) {
     51        ShowMenuBar();
     52    } else {
     53        HideMenuBar();
     54    }
     55#endif
     56}
     57
     58
     59/* !!! FIXME: clean out the pre-10.6 code when it makes sense to do so. */
     60#define FORCE_OLD_API 0
     61
     62#if FORCE_OLD_API
     63#undef MAC_OS_X_VERSION_MIN_REQUIRED
     64#define MAC_OS_X_VERSION_MIN_REQUIRED 1050
     65#endif
     66
     67static BOOL
     68IS_SNOW_LEOPARD_OR_LATER()
     69{
     70#if FORCE_OLD_API
     71    return NO;
     72#else
     73    return floor(NSAppKitVersionNumber) > NSAppKitVersionNumber10_5;
     74#endif
     75}
     76
     77static int
     78CG_SetError(const char *prefix, CGDisplayErr result)
     79{
     80    const char *error;
     81
     82    switch (result) {
     83    case kCGErrorFailure:
     84        error = "kCGErrorFailure";
     85        break;
     86    case kCGErrorIllegalArgument:
     87        error = "kCGErrorIllegalArgument";
     88        break;
     89    case kCGErrorInvalidConnection:
     90        error = "kCGErrorInvalidConnection";
     91        break;
     92    case kCGErrorInvalidContext:
     93        error = "kCGErrorInvalidContext";
     94        break;
     95    case kCGErrorCannotComplete:
     96        error = "kCGErrorCannotComplete";
     97        break;
     98    case kCGErrorNotImplemented:
     99        error = "kCGErrorNotImplemented";
    100        break;
    101    case kCGErrorRangeCheck:
    102        error = "kCGErrorRangeCheck";
    103        break;
    104    case kCGErrorTypeCheck:
    105        error = "kCGErrorTypeCheck";
    106        break;
    107    case kCGErrorInvalidOperation:
    108        error = "kCGErrorInvalidOperation";
    109        break;
    110    case kCGErrorNoneAvailable:
    111        error = "kCGErrorNoneAvailable";
    112        break;
    113    default:
    114        error = "Unknown Error";
    115        break;
    116    }
    117    return SDL_SetError("%s: %s", prefix, error);
    118}
    119
    120static SDL_bool
    121GetDisplayMode(_THIS, const void *moderef, CVDisplayLinkRef link, SDL_DisplayMode *mode)
    122{
    123    SDL_DisplayModeData *data;
    124    long width = 0;
    125    long height = 0;
    126    long bpp = 0;
    127    long refreshRate = 0;
    128
    129    data = (SDL_DisplayModeData *) SDL_malloc(sizeof(*data));
    130    if (!data) {
    131        return SDL_FALSE;
    132    }
    133    data->moderef = moderef;
    134
    135    if (IS_SNOW_LEOPARD_OR_LATER()) {
    136        CGDisplayModeRef vidmode = (CGDisplayModeRef) moderef;
    137        CFStringRef fmt = CGDisplayModeCopyPixelEncoding(vidmode);
    138        width = (long) CGDisplayModeGetWidth(vidmode);
    139        height = (long) CGDisplayModeGetHeight(vidmode);
    140        refreshRate = (long) (CGDisplayModeGetRefreshRate(vidmode) + 0.5);
    141
    142        if (CFStringCompare(fmt, CFSTR(IO32BitDirectPixels),
    143                            kCFCompareCaseInsensitive) == kCFCompareEqualTo) {
    144            bpp = 32;
    145        } else if (CFStringCompare(fmt, CFSTR(IO16BitDirectPixels),
    146                            kCFCompareCaseInsensitive) == kCFCompareEqualTo) {
    147            bpp = 16;
    148        } else if (CFStringCompare(fmt, CFSTR(kIO30BitDirectPixels),
    149                            kCFCompareCaseInsensitive) == kCFCompareEqualTo) {
    150            bpp = 30;
    151        } else {
    152            bpp = 0;  /* ignore 8-bit and such for now. */
    153        }
    154
    155        CFRelease(fmt);
    156    }
    157
    158    #if MAC_OS_X_VERSION_MIN_REQUIRED < 1060
    159    if (!IS_SNOW_LEOPARD_OR_LATER()) {
    160        CFNumberRef number;
    161        double refresh;
    162        CFDictionaryRef vidmode = (CFDictionaryRef) moderef;
    163        number = CFDictionaryGetValue(vidmode, kCGDisplayWidth);
    164        CFNumberGetValue(number, kCFNumberLongType, &width);
    165        number = CFDictionaryGetValue(vidmode, kCGDisplayHeight);
    166        CFNumberGetValue(number, kCFNumberLongType, &height);
    167        number = CFDictionaryGetValue(vidmode, kCGDisplayBitsPerPixel);
    168        CFNumberGetValue(number, kCFNumberLongType, &bpp);
    169        number = CFDictionaryGetValue(vidmode, kCGDisplayRefreshRate);
    170        CFNumberGetValue(number, kCFNumberDoubleType, &refresh);
    171        refreshRate = (long) (refresh + 0.5);
    172    }
    173    #endif
    174
    175    /* CGDisplayModeGetRefreshRate returns 0 for many non-CRT displays. */
    176    if (refreshRate == 0 && link != NULL) {
    177        CVTime time = CVDisplayLinkGetNominalOutputVideoRefreshPeriod(link);
    178        if ((time.flags & kCVTimeIsIndefinite) == 0 && time.timeValue != 0) {
    179            refreshRate = (long) ((time.timeScale / (double) time.timeValue) + 0.5);
    180        }
    181    }
    182
    183    mode->format = SDL_PIXELFORMAT_UNKNOWN;
    184    switch (bpp) {
    185    case 16:
    186        mode->format = SDL_PIXELFORMAT_ARGB1555;
    187        break;
    188    case 30:
    189        mode->format = SDL_PIXELFORMAT_ARGB2101010;
    190        break;
    191    case 32:
    192        mode->format = SDL_PIXELFORMAT_ARGB8888;
    193        break;
    194    case 8: /* We don't support palettized modes now */
    195    default: /* Totally unrecognizable bit depth. */
    196        return SDL_FALSE;
    197    }
    198    mode->w = width;
    199    mode->h = height;
    200    mode->refresh_rate = refreshRate;
    201    mode->driverdata = data;
    202    return SDL_TRUE;
    203}
    204
    205static void
    206Cocoa_ReleaseDisplayMode(_THIS, const void *moderef)
    207{
    208    if (IS_SNOW_LEOPARD_OR_LATER()) {
    209        CGDisplayModeRelease((CGDisplayModeRef) moderef);  /* NULL is ok */
    210    }
    211}
    212
    213static void
    214Cocoa_ReleaseDisplayModeList(_THIS, CFArrayRef modelist)
    215{
    216    if (IS_SNOW_LEOPARD_OR_LATER()) {
    217        CFRelease(modelist);  /* NULL is ok */
    218    }
    219}
    220
    221static const char *
    222Cocoa_GetDisplayName(CGDirectDisplayID displayID)
    223{
    224    CFDictionaryRef deviceInfo = IODisplayCreateInfoDictionary(CGDisplayIOServicePort(displayID), kIODisplayOnlyPreferredName);
    225    NSDictionary *localizedNames = [(NSDictionary *)deviceInfo objectForKey:[NSString stringWithUTF8String:kDisplayProductName]];
    226    const char* displayName = NULL;
    227
    228    if ([localizedNames count] > 0) {
    229        displayName = SDL_strdup([[localizedNames objectForKey:[[localizedNames allKeys] objectAtIndex:0]] UTF8String]);
    230    }
    231    CFRelease(deviceInfo);
    232    return displayName;
    233}
    234
    235void
    236Cocoa_InitModes(_THIS)
    237{ @autoreleasepool
    238{
    239    CGDisplayErr result;
    240    CGDirectDisplayID *displays;
    241    CGDisplayCount numDisplays;
    242    int pass, i;
    243
    244    result = CGGetOnlineDisplayList(0, NULL, &numDisplays);
    245    if (result != kCGErrorSuccess) {
    246        CG_SetError("CGGetOnlineDisplayList()", result);
    247        return;
    248    }
    249    displays = SDL_stack_alloc(CGDirectDisplayID, numDisplays);
    250    result = CGGetOnlineDisplayList(numDisplays, displays, &numDisplays);
    251    if (result != kCGErrorSuccess) {
    252        CG_SetError("CGGetOnlineDisplayList()", result);
    253        SDL_stack_free(displays);
    254        return;
    255    }
    256
    257    /* Pick up the primary display in the first pass, then get the rest */
    258    for (pass = 0; pass < 2; ++pass) {
    259        for (i = 0; i < numDisplays; ++i) {
    260            SDL_VideoDisplay display;
    261            SDL_DisplayData *displaydata;
    262            SDL_DisplayMode mode;
    263            const void *moderef = NULL;
    264            CVDisplayLinkRef link = NULL;
    265
    266            if (pass == 0) {
    267                if (!CGDisplayIsMain(displays[i])) {
    268                    continue;
    269                }
    270            } else {
    271                if (CGDisplayIsMain(displays[i])) {
    272                    continue;
    273                }
    274            }
    275
    276            if (CGDisplayMirrorsDisplay(displays[i]) != kCGNullDirectDisplay) {
    277                continue;
    278            }
    279
    280            if (IS_SNOW_LEOPARD_OR_LATER()) {
    281                moderef = CGDisplayCopyDisplayMode(displays[i]);
    282            }
    283
    284            #if MAC_OS_X_VERSION_MIN_REQUIRED < 1060
    285            if (!IS_SNOW_LEOPARD_OR_LATER()) {
    286                moderef = CGDisplayCurrentMode(displays[i]);
    287            }
    288            #endif
    289
    290            if (!moderef) {
    291                continue;
    292            }
    293
    294            displaydata = (SDL_DisplayData *) SDL_malloc(sizeof(*displaydata));
    295            if (!displaydata) {
    296                Cocoa_ReleaseDisplayMode(_this, moderef);
    297                continue;
    298            }
    299            displaydata->display = displays[i];
    300
    301            CVDisplayLinkCreateWithCGDisplay(displays[i], &link);
    302
    303            SDL_zero(display);
    304            /* this returns a stddup'ed string */
    305            display.name = (char *)Cocoa_GetDisplayName(displays[i]);
    306            if (!GetDisplayMode(_this, moderef, link, &mode)) {
    307                CVDisplayLinkRelease(link);
    308                Cocoa_ReleaseDisplayMode(_this, moderef);
    309                SDL_free(display.name);
    310                SDL_free(displaydata);
    311                continue;
    312            }
    313
    314            CVDisplayLinkRelease(link);
    315
    316            display.desktop_mode = mode;
    317            display.current_mode = mode;
    318            display.driverdata = displaydata;
    319            SDL_AddVideoDisplay(&display);
    320            SDL_free(display.name);
    321        }
    322    }
    323    SDL_stack_free(displays);
    324}}
    325
    326int
    327Cocoa_GetDisplayBounds(_THIS, SDL_VideoDisplay * display, SDL_Rect * rect)
    328{
    329    SDL_DisplayData *displaydata = (SDL_DisplayData *) display->driverdata;
    330    CGRect cgrect;
    331
    332    cgrect = CGDisplayBounds(displaydata->display);
    333    rect->x = (int)cgrect.origin.x;
    334    rect->y = (int)cgrect.origin.y;
    335    rect->w = (int)cgrect.size.width;
    336    rect->h = (int)cgrect.size.height;
    337    return 0;
    338}
    339
    340void
    341Cocoa_GetDisplayModes(_THIS, SDL_VideoDisplay * display)
    342{
    343    SDL_DisplayData *data = (SDL_DisplayData *) display->driverdata;
    344    CFArrayRef modes = NULL;
    345
    346    if (IS_SNOW_LEOPARD_OR_LATER()) {
    347        modes = CGDisplayCopyAllDisplayModes(data->display, NULL);
    348    }
    349
    350    #if MAC_OS_X_VERSION_MIN_REQUIRED < 1060
    351    if (!IS_SNOW_LEOPARD_OR_LATER()) {
    352        modes = CGDisplayAvailableModes(data->display);
    353    }
    354    #endif
    355
    356    if (modes) {
    357        CVDisplayLinkRef link = NULL;
    358        const CFIndex count = CFArrayGetCount(modes);
    359        CFIndex i;
    360
    361        CVDisplayLinkCreateWithCGDisplay(data->display, &link);
    362
    363        for (i = 0; i < count; i++) {
    364            const void *moderef = CFArrayGetValueAtIndex(modes, i);
    365            SDL_DisplayMode mode;
    366            if (GetDisplayMode(_this, moderef, link, &mode)) {
    367                if (IS_SNOW_LEOPARD_OR_LATER()) {
    368                    CGDisplayModeRetain((CGDisplayModeRef) moderef);
    369                }
    370                SDL_AddDisplayMode(display, &mode);
    371            }
    372        }
    373
    374        CVDisplayLinkRelease(link);
    375        Cocoa_ReleaseDisplayModeList(_this, modes);
    376    }
    377}
    378
    379static CGError
    380Cocoa_SwitchMode(_THIS, CGDirectDisplayID display, const void *mode)
    381{
    382    if (IS_SNOW_LEOPARD_OR_LATER()) {
    383        return CGDisplaySetDisplayMode(display, (CGDisplayModeRef) mode, NULL);
    384    }
    385 
    386    #if MAC_OS_X_VERSION_MIN_REQUIRED < 1060
    387    if (!IS_SNOW_LEOPARD_OR_LATER()) {
    388        return CGDisplaySwitchToMode(display, (CFDictionaryRef) mode);
    389    }
    390    #endif
    391
    392    return kCGErrorFailure;
    393}
    394
    395int
    396Cocoa_SetDisplayMode(_THIS, SDL_VideoDisplay * display, SDL_DisplayMode * mode)
    397{
    398    SDL_DisplayData *displaydata = (SDL_DisplayData *) display->driverdata;
    399    SDL_DisplayModeData *data = (SDL_DisplayModeData *) mode->driverdata;
    400    CGDisplayFadeReservationToken fade_token = kCGDisplayFadeReservationInvalidToken;
    401    CGError result;
    402
    403    /* Fade to black to hide resolution-switching flicker */
    404    if (CGAcquireDisplayFadeReservation(5, &fade_token) == kCGErrorSuccess) {
    405        CGDisplayFade(fade_token, 0.3, kCGDisplayBlendNormal, kCGDisplayBlendSolidColor, 0.0, 0.0, 0.0, TRUE);
    406    }
    407
    408    if (data == display->desktop_mode.driverdata) {
    409        /* Restoring desktop mode */
    410        Cocoa_SwitchMode(_this, displaydata->display, data->moderef);
    411
    412        if (CGDisplayIsMain(displaydata->display)) {
    413            CGReleaseAllDisplays();
    414        } else {
    415            CGDisplayRelease(displaydata->display);
    416        }
    417
    418        if (CGDisplayIsMain(displaydata->display)) {
    419            Cocoa_ToggleMenuBar(YES);
    420        }
    421    } else {
    422        /* Put up the blanking window (a window above all other windows) */
    423        if (CGDisplayIsMain(displaydata->display)) {
    424            /* If we don't capture all displays, Cocoa tries to rearrange windows... *sigh* */
    425            result = CGCaptureAllDisplays();
    426        } else {
    427            result = CGDisplayCapture(displaydata->display);
    428        }
    429        if (result != kCGErrorSuccess) {
    430            CG_SetError("CGDisplayCapture()", result);
    431            goto ERR_NO_CAPTURE;
    432        }
    433
    434        /* Do the physical switch */
    435        result = Cocoa_SwitchMode(_this, displaydata->display, data->moderef);
    436        if (result != kCGErrorSuccess) {
    437            CG_SetError("CGDisplaySwitchToMode()", result);
    438            goto ERR_NO_SWITCH;
    439        }
    440
    441        /* Hide the menu bar so it doesn't intercept events */
    442        if (CGDisplayIsMain(displaydata->display)) {
    443            Cocoa_ToggleMenuBar(NO);
    444        }
    445    }
    446
    447    /* Fade in again (asynchronously) */
    448    if (fade_token != kCGDisplayFadeReservationInvalidToken) {
    449        CGDisplayFade(fade_token, 0.5, kCGDisplayBlendSolidColor, kCGDisplayBlendNormal, 0.0, 0.0, 0.0, FALSE);
    450        CGReleaseDisplayFadeReservation(fade_token);
    451    }
    452
    453    return 0;
    454
    455    /* Since the blanking window covers *all* windows (even force quit) correct recovery is crucial */
    456ERR_NO_SWITCH:
    457    CGDisplayRelease(displaydata->display);
    458ERR_NO_CAPTURE:
    459    if (fade_token != kCGDisplayFadeReservationInvalidToken) {
    460        CGDisplayFade (fade_token, 0.5, kCGDisplayBlendSolidColor, kCGDisplayBlendNormal, 0.0, 0.0, 0.0, FALSE);
    461        CGReleaseDisplayFadeReservation(fade_token);
    462    }
    463    return -1;
    464}
    465
    466void
    467Cocoa_QuitModes(_THIS)
    468{
    469    int i, j;
    470
    471    for (i = 0; i < _this->num_displays; ++i) {
    472        SDL_VideoDisplay *display = &_this->displays[i];
    473        SDL_DisplayModeData *mode;
    474
    475        if (display->current_mode.driverdata != display->desktop_mode.driverdata) {
    476            Cocoa_SetDisplayMode(_this, display, &display->desktop_mode);
    477        }
    478
    479        mode = (SDL_DisplayModeData *) display->desktop_mode.driverdata;
    480        Cocoa_ReleaseDisplayMode(_this, mode->moderef);
    481
    482        for (j = 0; j < display->num_display_modes; j++) {
    483            mode = (SDL_DisplayModeData*) display->display_modes[j].driverdata;
    484            Cocoa_ReleaseDisplayMode(_this, mode->moderef);
    485        }
    486
    487    }
    488    Cocoa_ToggleMenuBar(YES);
    489}
    490
    491#endif /* SDL_VIDEO_DRIVER_COCOA */
    492
    493/* vi: set ts=4 sw=4 expandtab: */