medfall

A super great game engine
Log | Files | Refs

cocoa_monitor.m (14569B)


      1 //========================================================================
      2 // GLFW 3.3 macOS - www.glfw.org
      3 //------------------------------------------------------------------------
      4 // Copyright (c) 2002-2006 Marcus Geelnard
      5 // Copyright (c) 2006-2016 Camilla Löwy <elmindreda@glfw.org>
      6 //
      7 // This software is provided 'as-is', without any express or implied
      8 // warranty. In no event will the authors be held liable for any damages
      9 // arising from the use of this software.
     10 //
     11 // Permission is granted to anyone to use this software for any purpose,
     12 // including commercial applications, and to alter it and redistribute it
     13 // freely, subject to the following restrictions:
     14 //
     15 // 1. The origin of this software must not be misrepresented; you must not
     16 //    claim that you wrote the original software. If you use this software
     17 //    in a product, an acknowledgment in the product documentation would
     18 //    be appreciated but is not required.
     19 //
     20 // 2. Altered source versions must be plainly marked as such, and must not
     21 //    be misrepresented as being the original software.
     22 //
     23 // 3. This notice may not be removed or altered from any source
     24 //    distribution.
     25 //
     26 //========================================================================
     27 
     28 #include "internal.h"
     29 
     30 #include <stdlib.h>
     31 #include <limits.h>
     32 
     33 #include <IOKit/graphics/IOGraphicsLib.h>
     34 #include <CoreVideo/CVBase.h>
     35 #include <CoreVideo/CVDisplayLink.h>
     36 #include <ApplicationServices/ApplicationServices.h>
     37 
     38 
     39 // Get the name of the specified display, or NULL
     40 //
     41 static char* getDisplayName(CGDirectDisplayID displayID)
     42 {
     43     io_iterator_t it;
     44     io_service_t service;
     45     CFDictionaryRef info;
     46 
     47     if (IOServiceGetMatchingServices(kIOMasterPortDefault,
     48                                      IOServiceMatching("IODisplayConnect"),
     49                                      &it) != 0)
     50     {
     51         // This may happen if a desktop Mac is running headless
     52         return NULL;
     53     }
     54 
     55     while ((service = IOIteratorNext(it)) != 0)
     56     {
     57         info = IODisplayCreateInfoDictionary(service, kIODisplayOnlyPreferredName);
     58 
     59         CFNumberRef vendorIDRef =
     60             CFDictionaryGetValue(info, CFSTR(kDisplayVendorID));
     61         CFNumberRef productIDRef =
     62             CFDictionaryGetValue(info, CFSTR(kDisplayProductID));
     63         if (!vendorIDRef || !productIDRef)
     64         {
     65             CFRelease(info);
     66             continue;
     67         }
     68 
     69         unsigned int vendorID, productID;
     70         CFNumberGetValue(vendorIDRef, kCFNumberIntType, &vendorID);
     71         CFNumberGetValue(productIDRef, kCFNumberIntType, &productID);
     72 
     73         if (CGDisplayVendorNumber(displayID) == vendorID &&
     74             CGDisplayModelNumber(displayID) == productID)
     75         {
     76             // Info dictionary is used and freed below
     77             break;
     78         }
     79 
     80         CFRelease(info);
     81     }
     82 
     83     IOObjectRelease(it);
     84 
     85     if (!service)
     86     {
     87         _glfwInputError(GLFW_PLATFORM_ERROR,
     88                         "Cocoa: Failed to find service port for display");
     89         return NULL;
     90     }
     91 
     92     CFDictionaryRef names =
     93         CFDictionaryGetValue(info, CFSTR(kDisplayProductName));
     94 
     95     CFStringRef nameRef;
     96 
     97     if (!names || !CFDictionaryGetValueIfPresent(names, CFSTR("en_US"),
     98                                                  (const void**) &nameRef))
     99     {
    100         // This may happen if a desktop Mac is running headless
    101         CFRelease(info);
    102         return NULL;
    103     }
    104 
    105     const CFIndex size =
    106         CFStringGetMaximumSizeForEncoding(CFStringGetLength(nameRef),
    107                                           kCFStringEncodingUTF8);
    108     char* name = calloc(size + 1, 1);
    109     CFStringGetCString(nameRef, name, size, kCFStringEncodingUTF8);
    110 
    111     CFRelease(info);
    112     return name;
    113 }
    114 
    115 // Check whether the display mode should be included in enumeration
    116 //
    117 static GLFWbool modeIsGood(CGDisplayModeRef mode)
    118 {
    119     uint32_t flags = CGDisplayModeGetIOFlags(mode);
    120 
    121     if (!(flags & kDisplayModeValidFlag) || !(flags & kDisplayModeSafeFlag))
    122         return GLFW_FALSE;
    123     if (flags & kDisplayModeInterlacedFlag)
    124         return GLFW_FALSE;
    125     if (flags & kDisplayModeStretchedFlag)
    126         return GLFW_FALSE;
    127 
    128 #if MAC_OS_X_VERSION_MAX_ALLOWED <= 101100
    129     CFStringRef format = CGDisplayModeCopyPixelEncoding(mode);
    130     if (CFStringCompare(format, CFSTR(IO16BitDirectPixels), 0) &&
    131         CFStringCompare(format, CFSTR(IO32BitDirectPixels), 0))
    132     {
    133         CFRelease(format);
    134         return GLFW_FALSE;
    135     }
    136 
    137     CFRelease(format);
    138 #endif /* MAC_OS_X_VERSION_MAX_ALLOWED */
    139     return GLFW_TRUE;
    140 }
    141 
    142 // Convert Core Graphics display mode to GLFW video mode
    143 //
    144 static GLFWvidmode vidmodeFromCGDisplayMode(CGDisplayModeRef mode,
    145                                             CVDisplayLinkRef link)
    146 {
    147     GLFWvidmode result;
    148     result.width = (int) CGDisplayModeGetWidth(mode);
    149     result.height = (int) CGDisplayModeGetHeight(mode);
    150     result.refreshRate = (int) CGDisplayModeGetRefreshRate(mode);
    151 
    152     if (result.refreshRate == 0)
    153     {
    154         const CVTime time = CVDisplayLinkGetNominalOutputVideoRefreshPeriod(link);
    155         if (!(time.flags & kCVTimeIsIndefinite))
    156             result.refreshRate = (int) (time.timeScale / (double) time.timeValue);
    157     }
    158 
    159 #if MAC_OS_X_VERSION_MAX_ALLOWED <= 101100
    160     CFStringRef format = CGDisplayModeCopyPixelEncoding(mode);
    161     if (CFStringCompare(format, CFSTR(IO16BitDirectPixels), 0) == 0)
    162     {
    163         result.redBits = 5;
    164         result.greenBits = 5;
    165         result.blueBits = 5;
    166     }
    167     else
    168 #endif /* MAC_OS_X_VERSION_MAX_ALLOWED */
    169     {
    170         result.redBits = 8;
    171         result.greenBits = 8;
    172         result.blueBits = 8;
    173     }
    174 
    175 #if MAC_OS_X_VERSION_MAX_ALLOWED <= 101100
    176     CFRelease(format);
    177 #endif /* MAC_OS_X_VERSION_MAX_ALLOWED */
    178     return result;
    179 }
    180 
    181 // Starts reservation for display fading
    182 //
    183 static CGDisplayFadeReservationToken beginFadeReservation(void)
    184 {
    185     CGDisplayFadeReservationToken token = kCGDisplayFadeReservationInvalidToken;
    186 
    187     if (CGAcquireDisplayFadeReservation(5, &token) == kCGErrorSuccess)
    188         CGDisplayFade(token, 0.3, kCGDisplayBlendNormal, kCGDisplayBlendSolidColor, 0.0, 0.0, 0.0, TRUE);
    189 
    190     return token;
    191 }
    192 
    193 // Ends reservation for display fading
    194 //
    195 static void endFadeReservation(CGDisplayFadeReservationToken token)
    196 {
    197     if (token != kCGDisplayFadeReservationInvalidToken)
    198     {
    199         CGDisplayFade(token, 0.5, kCGDisplayBlendSolidColor, kCGDisplayBlendNormal, 0.0, 0.0, 0.0, FALSE);
    200         CGReleaseDisplayFadeReservation(token);
    201     }
    202 }
    203 
    204 
    205 //////////////////////////////////////////////////////////////////////////
    206 //////                       GLFW internal API                      //////
    207 //////////////////////////////////////////////////////////////////////////
    208 
    209 // Poll for changes in the set of connected monitors
    210 //
    211 void _glfwPollMonitorsNS(void)
    212 {
    213     uint32_t i, j, displayCount, disconnectedCount;
    214     CGDirectDisplayID* displays;
    215     _GLFWmonitor** disconnected = NULL;
    216 
    217     CGGetOnlineDisplayList(0, NULL, &displayCount);
    218     displays = calloc(displayCount, sizeof(CGDirectDisplayID));
    219     CGGetOnlineDisplayList(displayCount, displays, &displayCount);
    220 
    221     disconnectedCount = _glfw.monitorCount;
    222     if (disconnectedCount)
    223     {
    224         disconnected = calloc(_glfw.monitorCount, sizeof(_GLFWmonitor*));
    225         memcpy(disconnected,
    226                _glfw.monitors,
    227                _glfw.monitorCount * sizeof(_GLFWmonitor*));
    228     }
    229 
    230     for (i = 0;  i < displayCount;  i++)
    231     {
    232         _GLFWmonitor* monitor;
    233         const uint32_t unitNumber = CGDisplayUnitNumber(displays[i]);
    234 
    235         if (CGDisplayIsAsleep(displays[i]))
    236             continue;
    237 
    238         for (j = 0;  j < disconnectedCount;  j++)
    239         {
    240             // HACK: Compare unit numbers instead of display IDs to work around
    241             //       display replacement on machines with automatic graphics
    242             //       switching
    243             if (disconnected[j] && disconnected[j]->ns.unitNumber == unitNumber)
    244             {
    245                 disconnected[j] = NULL;
    246                 break;
    247             }
    248         }
    249 
    250         const CGSize size = CGDisplayScreenSize(displays[i]);
    251         char* name = getDisplayName(displays[i]);
    252         if (!name)
    253             name = strdup("Unknown");
    254 
    255         monitor = _glfwAllocMonitor(name, size.width, size.height);
    256         monitor->ns.displayID  = displays[i];
    257         monitor->ns.unitNumber = unitNumber;
    258 
    259         free(name);
    260 
    261         _glfwInputMonitor(monitor, GLFW_CONNECTED, _GLFW_INSERT_LAST);
    262     }
    263 
    264     for (i = 0;  i < disconnectedCount;  i++)
    265     {
    266         if (disconnected[i])
    267             _glfwInputMonitor(disconnected[i], GLFW_DISCONNECTED, 0);
    268     }
    269 
    270     free(disconnected);
    271     free(displays);
    272 }
    273 
    274 // Change the current video mode
    275 //
    276 GLFWbool _glfwSetVideoModeNS(_GLFWmonitor* monitor, const GLFWvidmode* desired)
    277 {
    278     CFArrayRef modes;
    279     CFIndex count, i;
    280     CVDisplayLinkRef link;
    281     CGDisplayModeRef native = NULL;
    282     GLFWvidmode current;
    283     const GLFWvidmode* best;
    284 
    285     best = _glfwChooseVideoMode(monitor, desired);
    286     _glfwPlatformGetVideoMode(monitor, &current);
    287     if (_glfwCompareVideoModes(&current, best) == 0)
    288         return GLFW_TRUE;
    289 
    290     CVDisplayLinkCreateWithCGDisplay(monitor->ns.displayID, &link);
    291 
    292     modes = CGDisplayCopyAllDisplayModes(monitor->ns.displayID, NULL);
    293     count = CFArrayGetCount(modes);
    294 
    295     for (i = 0;  i < count;  i++)
    296     {
    297         CGDisplayModeRef dm = (CGDisplayModeRef) CFArrayGetValueAtIndex(modes, i);
    298         if (!modeIsGood(dm))
    299             continue;
    300 
    301         const GLFWvidmode mode = vidmodeFromCGDisplayMode(dm, link);
    302         if (_glfwCompareVideoModes(best, &mode) == 0)
    303         {
    304             native = dm;
    305             break;
    306         }
    307     }
    308 
    309     if (native)
    310     {
    311         if (monitor->ns.previousMode == NULL)
    312             monitor->ns.previousMode = CGDisplayCopyDisplayMode(monitor->ns.displayID);
    313 
    314         CGDisplayFadeReservationToken token = beginFadeReservation();
    315         CGDisplaySetDisplayMode(monitor->ns.displayID, native, NULL);
    316         endFadeReservation(token);
    317     }
    318 
    319     CFRelease(modes);
    320     CVDisplayLinkRelease(link);
    321 
    322     if (!native)
    323     {
    324         _glfwInputError(GLFW_PLATFORM_ERROR,
    325                         "Cocoa: Monitor mode list changed");
    326         return GLFW_FALSE;
    327     }
    328 
    329     return GLFW_TRUE;
    330 }
    331 
    332 // Restore the previously saved (original) video mode
    333 //
    334 void _glfwRestoreVideoModeNS(_GLFWmonitor* monitor)
    335 {
    336     if (monitor->ns.previousMode)
    337     {
    338         CGDisplayFadeReservationToken token = beginFadeReservation();
    339         CGDisplaySetDisplayMode(monitor->ns.displayID,
    340                                 monitor->ns.previousMode, NULL);
    341         endFadeReservation(token);
    342 
    343         CGDisplayModeRelease(monitor->ns.previousMode);
    344         monitor->ns.previousMode = NULL;
    345     }
    346 }
    347 
    348 
    349 //////////////////////////////////////////////////////////////////////////
    350 //////                       GLFW platform API                      //////
    351 //////////////////////////////////////////////////////////////////////////
    352 
    353 void _glfwPlatformGetMonitorPos(_GLFWmonitor* monitor, int* xpos, int* ypos)
    354 {
    355     const CGRect bounds = CGDisplayBounds(monitor->ns.displayID);
    356 
    357     if (xpos)
    358         *xpos = (int) bounds.origin.x;
    359     if (ypos)
    360         *ypos = (int) bounds.origin.y;
    361 }
    362 
    363 GLFWvidmode* _glfwPlatformGetVideoModes(_GLFWmonitor* monitor, int* count)
    364 {
    365     CFArrayRef modes;
    366     CFIndex found, i, j;
    367     GLFWvidmode* result;
    368     CVDisplayLinkRef link;
    369 
    370     *count = 0;
    371 
    372     CVDisplayLinkCreateWithCGDisplay(monitor->ns.displayID, &link);
    373 
    374     modes = CGDisplayCopyAllDisplayModes(monitor->ns.displayID, NULL);
    375     found = CFArrayGetCount(modes);
    376     result = calloc(found, sizeof(GLFWvidmode));
    377 
    378     for (i = 0;  i < found;  i++)
    379     {
    380         CGDisplayModeRef dm = (CGDisplayModeRef) CFArrayGetValueAtIndex(modes, i);
    381         if (!modeIsGood(dm))
    382             continue;
    383 
    384         const GLFWvidmode mode = vidmodeFromCGDisplayMode(dm, link);
    385 
    386         for (j = 0;  j < *count;  j++)
    387         {
    388             if (_glfwCompareVideoModes(result + j, &mode) == 0)
    389                 break;
    390         }
    391 
    392         // Skip duplicate modes
    393         if (i < *count)
    394             continue;
    395 
    396         (*count)++;
    397         result[*count - 1] = mode;
    398     }
    399 
    400     CFRelease(modes);
    401     CVDisplayLinkRelease(link);
    402     return result;
    403 }
    404 
    405 void _glfwPlatformGetVideoMode(_GLFWmonitor* monitor, GLFWvidmode *mode)
    406 {
    407     CGDisplayModeRef displayMode;
    408     CVDisplayLinkRef link;
    409 
    410     CVDisplayLinkCreateWithCGDisplay(monitor->ns.displayID, &link);
    411 
    412     displayMode = CGDisplayCopyDisplayMode(monitor->ns.displayID);
    413     *mode = vidmodeFromCGDisplayMode(displayMode, link);
    414     CGDisplayModeRelease(displayMode);
    415 
    416     CVDisplayLinkRelease(link);
    417 }
    418 
    419 void _glfwPlatformGetGammaRamp(_GLFWmonitor* monitor, GLFWgammaramp* ramp)
    420 {
    421     uint32_t i, size = CGDisplayGammaTableCapacity(monitor->ns.displayID);
    422     CGGammaValue* values = calloc(size * 3, sizeof(CGGammaValue));
    423 
    424     CGGetDisplayTransferByTable(monitor->ns.displayID,
    425                                 size,
    426                                 values,
    427                                 values + size,
    428                                 values + size * 2,
    429                                 &size);
    430 
    431     _glfwAllocGammaArrays(ramp, size);
    432 
    433     for (i = 0; i < size; i++)
    434     {
    435         ramp->red[i]   = (unsigned short) (values[i] * 65535);
    436         ramp->green[i] = (unsigned short) (values[i + size] * 65535);
    437         ramp->blue[i]  = (unsigned short) (values[i + size * 2] * 65535);
    438     }
    439 
    440     free(values);
    441 }
    442 
    443 void _glfwPlatformSetGammaRamp(_GLFWmonitor* monitor, const GLFWgammaramp* ramp)
    444 {
    445     int i;
    446     CGGammaValue* values = calloc(ramp->size * 3, sizeof(CGGammaValue));
    447 
    448     for (i = 0;  i < ramp->size;  i++)
    449     {
    450         values[i]                  = ramp->red[i] / 65535.f;
    451         values[i + ramp->size]     = ramp->green[i] / 65535.f;
    452         values[i + ramp->size * 2] = ramp->blue[i] / 65535.f;
    453     }
    454 
    455     CGSetDisplayTransferByTable(monitor->ns.displayID,
    456                                 ramp->size,
    457                                 values,
    458                                 values + ramp->size,
    459                                 values + ramp->size * 2);
    460 
    461     free(values);
    462 }
    463 
    464 
    465 //////////////////////////////////////////////////////////////////////////
    466 //////                        GLFW native API                       //////
    467 //////////////////////////////////////////////////////////////////////////
    468 
    469 GLFWAPI CGDirectDisplayID glfwGetCocoaMonitor(GLFWmonitor* handle)
    470 {
    471     _GLFWmonitor* monitor = (_GLFWmonitor*) handle;
    472     _GLFW_REQUIRE_INIT_OR_RETURN(kCGNullDirectDisplay);
    473     return monitor->ns.displayID;
    474 }
    475