twitchapon-anim

Basic Twitchapon Receiver/Visuals
git clone git://bsandro.tech/twitchapon-anim
Log | Files | Refs | README | LICENSE

cocoa_monitor.m (18888B)


      1 //========================================================================
      2 // GLFW 3.3 macOS - www.glfw.org
      3 //------------------------------------------------------------------------
      4 // Copyright (c) 2002-2006 Marcus Geelnard
      5 // Copyright (c) 2006-2019 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 // It is fine to use C99 in this file because it will not be built with VS
     28 //========================================================================
     29 
     30 #include "internal.h"
     31 
     32 #include <stdlib.h>
     33 #include <limits.h>
     34 #include <math.h>
     35 
     36 #include <IOKit/graphics/IOGraphicsLib.h>
     37 #include <ApplicationServices/ApplicationServices.h>
     38 
     39 
     40 // Get the name of the specified display, or NULL
     41 //
     42 static char* getDisplayName(CGDirectDisplayID displayID)
     43 {
     44     io_iterator_t it;
     45     io_service_t service;
     46     CFDictionaryRef info;
     47 
     48     if (IOServiceGetMatchingServices(kIOMasterPortDefault,
     49                                      IOServiceMatching("IODisplayConnect"),
     50                                      &it) != 0)
     51     {
     52         // This may happen if a desktop Mac is running headless
     53         return NULL;
     54     }
     55 
     56     while ((service = IOIteratorNext(it)) != 0)
     57     {
     58         info = IODisplayCreateInfoDictionary(service,
     59                                              kIODisplayOnlyPreferredName);
     60 
     61         CFNumberRef vendorIDRef =
     62             CFDictionaryGetValue(info, CFSTR(kDisplayVendorID));
     63         CFNumberRef productIDRef =
     64             CFDictionaryGetValue(info, CFSTR(kDisplayProductID));
     65         if (!vendorIDRef || !productIDRef)
     66         {
     67             CFRelease(info);
     68             continue;
     69         }
     70 
     71         unsigned int vendorID, productID;
     72         CFNumberGetValue(vendorIDRef, kCFNumberIntType, &vendorID);
     73         CFNumberGetValue(productIDRef, kCFNumberIntType, &productID);
     74 
     75         if (CGDisplayVendorNumber(displayID) == vendorID &&
     76             CGDisplayModelNumber(displayID) == productID)
     77         {
     78             // Info dictionary is used and freed below
     79             break;
     80         }
     81 
     82         CFRelease(info);
     83     }
     84 
     85     IOObjectRelease(it);
     86 
     87     if (!service)
     88     {
     89         _glfwInputError(GLFW_PLATFORM_ERROR,
     90                         "Cocoa: Failed to find service port for display");
     91         return NULL;
     92     }
     93 
     94     CFDictionaryRef names =
     95         CFDictionaryGetValue(info, CFSTR(kDisplayProductName));
     96 
     97     CFStringRef nameRef;
     98 
     99     if (!names || !CFDictionaryGetValueIfPresent(names, CFSTR("en_US"),
    100                                                  (const void**) &nameRef))
    101     {
    102         // This may happen if a desktop Mac is running headless
    103         CFRelease(info);
    104         return NULL;
    105     }
    106 
    107     const CFIndex size =
    108         CFStringGetMaximumSizeForEncoding(CFStringGetLength(nameRef),
    109                                           kCFStringEncodingUTF8);
    110     char* name = calloc(size + 1, 1);
    111     CFStringGetCString(nameRef, name, size, kCFStringEncodingUTF8);
    112 
    113     CFRelease(info);
    114     return name;
    115 }
    116 
    117 // Check whether the display mode should be included in enumeration
    118 //
    119 static GLFWbool modeIsGood(CGDisplayModeRef mode)
    120 {
    121     uint32_t flags = CGDisplayModeGetIOFlags(mode);
    122 
    123     if (!(flags & kDisplayModeValidFlag) || !(flags & kDisplayModeSafeFlag))
    124         return GLFW_FALSE;
    125     if (flags & kDisplayModeInterlacedFlag)
    126         return GLFW_FALSE;
    127     if (flags & kDisplayModeStretchedFlag)
    128         return GLFW_FALSE;
    129 
    130 #if MAC_OS_X_VERSION_MAX_ALLOWED <= 101100
    131     CFStringRef format = CGDisplayModeCopyPixelEncoding(mode);
    132     if (CFStringCompare(format, CFSTR(IO16BitDirectPixels), 0) &&
    133         CFStringCompare(format, CFSTR(IO32BitDirectPixels), 0))
    134     {
    135         CFRelease(format);
    136         return GLFW_FALSE;
    137     }
    138 
    139     CFRelease(format);
    140 #endif /* MAC_OS_X_VERSION_MAX_ALLOWED */
    141     return GLFW_TRUE;
    142 }
    143 
    144 // Convert Core Graphics display mode to GLFW video mode
    145 //
    146 static GLFWvidmode vidmodeFromCGDisplayMode(CGDisplayModeRef mode,
    147                                             double fallbackRefreshRate)
    148 {
    149     GLFWvidmode result;
    150     result.width = (int) CGDisplayModeGetWidth(mode);
    151     result.height = (int) CGDisplayModeGetHeight(mode);
    152     result.refreshRate = (int) round(CGDisplayModeGetRefreshRate(mode));
    153 
    154     if (result.refreshRate == 0)
    155         result.refreshRate = (int) round(fallbackRefreshRate);
    156 
    157 #if MAC_OS_X_VERSION_MAX_ALLOWED <= 101100
    158     CFStringRef format = CGDisplayModeCopyPixelEncoding(mode);
    159     if (CFStringCompare(format, CFSTR(IO16BitDirectPixels), 0) == 0)
    160     {
    161         result.redBits = 5;
    162         result.greenBits = 5;
    163         result.blueBits = 5;
    164     }
    165     else
    166 #endif /* MAC_OS_X_VERSION_MAX_ALLOWED */
    167     {
    168         result.redBits = 8;
    169         result.greenBits = 8;
    170         result.blueBits = 8;
    171     }
    172 
    173 #if MAC_OS_X_VERSION_MAX_ALLOWED <= 101100
    174     CFRelease(format);
    175 #endif /* MAC_OS_X_VERSION_MAX_ALLOWED */
    176     return result;
    177 }
    178 
    179 // Starts reservation for display fading
    180 //
    181 static CGDisplayFadeReservationToken beginFadeReservation(void)
    182 {
    183     CGDisplayFadeReservationToken token = kCGDisplayFadeReservationInvalidToken;
    184 
    185     if (CGAcquireDisplayFadeReservation(5, &token) == kCGErrorSuccess)
    186     {
    187         CGDisplayFade(token, 0.3,
    188                       kCGDisplayBlendNormal,
    189                       kCGDisplayBlendSolidColor,
    190                       0.0, 0.0, 0.0,
    191                       TRUE);
    192     }
    193 
    194     return token;
    195 }
    196 
    197 // Ends reservation for display fading
    198 //
    199 static void endFadeReservation(CGDisplayFadeReservationToken token)
    200 {
    201     if (token != kCGDisplayFadeReservationInvalidToken)
    202     {
    203         CGDisplayFade(token, 0.5,
    204                       kCGDisplayBlendSolidColor,
    205                       kCGDisplayBlendNormal,
    206                       0.0, 0.0, 0.0,
    207                       FALSE);
    208         CGReleaseDisplayFadeReservation(token);
    209     }
    210 }
    211 
    212 // Finds and caches the NSScreen corresponding to the specified monitor
    213 //
    214 static GLFWbool refreshMonitorScreen(_GLFWmonitor* monitor)
    215 {
    216     if (monitor->ns.screen)
    217         return GLFW_TRUE;
    218 
    219     for (NSScreen* screen in [NSScreen screens])
    220     {
    221         NSNumber* displayID = [screen deviceDescription][@"NSScreenNumber"];
    222 
    223         // HACK: Compare unit numbers instead of display IDs to work around
    224         //       display replacement on machines with automatic graphics
    225         //       switching
    226         if (monitor->ns.unitNumber == CGDisplayUnitNumber([displayID unsignedIntValue]))
    227         {
    228             monitor->ns.screen = screen;
    229             return GLFW_TRUE;
    230         }
    231     }
    232 
    233     _glfwInputError(GLFW_PLATFORM_ERROR, "Cocoa: Failed to find a screen for monitor");
    234     return GLFW_FALSE;
    235 }
    236 
    237 // Returns the display refresh rate queried from the I/O registry
    238 //
    239 static double getFallbackRefreshRate(CGDirectDisplayID displayID)
    240 {
    241     double refreshRate = 60.0;
    242 
    243     io_iterator_t it;
    244     io_service_t service;
    245 
    246     if (IOServiceGetMatchingServices(kIOMasterPortDefault,
    247                                      IOServiceMatching("IOFramebuffer"),
    248                                      &it) != 0)
    249     {
    250         return refreshRate;
    251     }
    252 
    253     while ((service = IOIteratorNext(it)) != 0)
    254     {
    255         const CFNumberRef indexRef =
    256             IORegistryEntryCreateCFProperty(service,
    257                                             CFSTR("IOFramebufferOpenGLIndex"),
    258                                             kCFAllocatorDefault,
    259                                             kNilOptions);
    260         if (!indexRef)
    261             continue;
    262 
    263         uint32_t index = 0;
    264         CFNumberGetValue(indexRef, kCFNumberIntType, &index);
    265         CFRelease(indexRef);
    266 
    267         if (CGOpenGLDisplayMaskToDisplayID(1 << index) != displayID)
    268             continue;
    269 
    270         const CFNumberRef clockRef =
    271             IORegistryEntryCreateCFProperty(service,
    272                                             CFSTR("IOFBCurrentPixelClock"),
    273                                             kCFAllocatorDefault,
    274                                             kNilOptions);
    275         const CFNumberRef countRef =
    276             IORegistryEntryCreateCFProperty(service,
    277                                             CFSTR("IOFBCurrentPixelCount"),
    278                                             kCFAllocatorDefault,
    279                                             kNilOptions);
    280         if (!clockRef || !countRef)
    281             break;
    282 
    283         uint32_t clock = 0, count = 0;
    284         CFNumberGetValue(clockRef, kCFNumberIntType, &clock);
    285         CFNumberGetValue(countRef, kCFNumberIntType, &count);
    286         CFRelease(clockRef);
    287         CFRelease(countRef);
    288 
    289         if (clock > 0 && count > 0)
    290             refreshRate = clock / (double) count;
    291 
    292         break;
    293     }
    294 
    295     IOObjectRelease(it);
    296     return refreshRate;
    297 }
    298 
    299 
    300 //////////////////////////////////////////////////////////////////////////
    301 //////                       GLFW internal API                      //////
    302 //////////////////////////////////////////////////////////////////////////
    303 
    304 // Poll for changes in the set of connected monitors
    305 //
    306 void _glfwPollMonitorsNS(void)
    307 {
    308     uint32_t displayCount;
    309     CGGetOnlineDisplayList(0, NULL, &displayCount);
    310     CGDirectDisplayID* displays = calloc(displayCount, sizeof(CGDirectDisplayID));
    311     CGGetOnlineDisplayList(displayCount, displays, &displayCount);
    312 
    313     for (int i = 0;  i < _glfw.monitorCount;  i++)
    314         _glfw.monitors[i]->ns.screen = nil;
    315 
    316     _GLFWmonitor** disconnected = NULL;
    317     uint32_t disconnectedCount = _glfw.monitorCount;
    318     if (disconnectedCount)
    319     {
    320         disconnected = calloc(_glfw.monitorCount, sizeof(_GLFWmonitor*));
    321         memcpy(disconnected,
    322                _glfw.monitors,
    323                _glfw.monitorCount * sizeof(_GLFWmonitor*));
    324     }
    325 
    326     for (uint32_t i = 0;  i < displayCount;  i++)
    327     {
    328         if (CGDisplayIsAsleep(displays[i]))
    329             continue;
    330 
    331         // HACK: Compare unit numbers instead of display IDs to work around
    332         //       display replacement on machines with automatic graphics
    333         //       switching
    334         const uint32_t unitNumber = CGDisplayUnitNumber(displays[i]);
    335         for (uint32_t j = 0;  j < disconnectedCount;  j++)
    336         {
    337             if (disconnected[j] && disconnected[j]->ns.unitNumber == unitNumber)
    338             {
    339                 disconnected[j] = NULL;
    340                 break;
    341             }
    342         }
    343 
    344         const CGSize size = CGDisplayScreenSize(displays[i]);
    345         char* name = getDisplayName(displays[i]);
    346         if (!name)
    347             name = _glfw_strdup("Unknown");
    348 
    349         _GLFWmonitor* monitor = _glfwAllocMonitor(name, size.width, size.height);
    350         monitor->ns.displayID  = displays[i];
    351         monitor->ns.unitNumber = unitNumber;
    352 
    353         free(name);
    354 
    355         CGDisplayModeRef mode = CGDisplayCopyDisplayMode(displays[i]);
    356         if (CGDisplayModeGetRefreshRate(mode) == 0.0)
    357             monitor->ns.fallbackRefreshRate = getFallbackRefreshRate(displays[i]);
    358         CGDisplayModeRelease(mode);
    359 
    360         _glfwInputMonitor(monitor, GLFW_CONNECTED, _GLFW_INSERT_LAST);
    361     }
    362 
    363     for (uint32_t i = 0;  i < disconnectedCount;  i++)
    364     {
    365         if (disconnected[i])
    366             _glfwInputMonitor(disconnected[i], GLFW_DISCONNECTED, 0);
    367     }
    368 
    369     free(disconnected);
    370     free(displays);
    371 }
    372 
    373 // Change the current video mode
    374 //
    375 void _glfwSetVideoModeNS(_GLFWmonitor* monitor, const GLFWvidmode* desired)
    376 {
    377     GLFWvidmode current;
    378     _glfwPlatformGetVideoMode(monitor, &current);
    379 
    380     const GLFWvidmode* best = _glfwChooseVideoMode(monitor, desired);
    381     if (_glfwCompareVideoModes(&current, best) == 0)
    382         return;
    383 
    384     CFArrayRef modes = CGDisplayCopyAllDisplayModes(monitor->ns.displayID, NULL);
    385     const CFIndex count = CFArrayGetCount(modes);
    386     CGDisplayModeRef native = NULL;
    387 
    388     for (CFIndex i = 0;  i < count;  i++)
    389     {
    390         CGDisplayModeRef dm = (CGDisplayModeRef) CFArrayGetValueAtIndex(modes, i);
    391         if (!modeIsGood(dm))
    392             continue;
    393 
    394         const GLFWvidmode mode =
    395             vidmodeFromCGDisplayMode(dm, monitor->ns.fallbackRefreshRate);
    396         if (_glfwCompareVideoModes(best, &mode) == 0)
    397         {
    398             native = dm;
    399             break;
    400         }
    401     }
    402 
    403     if (native)
    404     {
    405         if (monitor->ns.previousMode == NULL)
    406             monitor->ns.previousMode = CGDisplayCopyDisplayMode(monitor->ns.displayID);
    407 
    408         CGDisplayFadeReservationToken token = beginFadeReservation();
    409         CGDisplaySetDisplayMode(monitor->ns.displayID, native, NULL);
    410         endFadeReservation(token);
    411     }
    412 
    413     CFRelease(modes);
    414 }
    415 
    416 // Restore the previously saved (original) video mode
    417 //
    418 void _glfwRestoreVideoModeNS(_GLFWmonitor* monitor)
    419 {
    420     if (monitor->ns.previousMode)
    421     {
    422         CGDisplayFadeReservationToken token = beginFadeReservation();
    423         CGDisplaySetDisplayMode(monitor->ns.displayID,
    424                                 monitor->ns.previousMode, NULL);
    425         endFadeReservation(token);
    426 
    427         CGDisplayModeRelease(monitor->ns.previousMode);
    428         monitor->ns.previousMode = NULL;
    429     }
    430 }
    431 
    432 
    433 //////////////////////////////////////////////////////////////////////////
    434 //////                       GLFW platform API                      //////
    435 //////////////////////////////////////////////////////////////////////////
    436 
    437 void _glfwPlatformFreeMonitor(_GLFWmonitor* monitor)
    438 {
    439 }
    440 
    441 void _glfwPlatformGetMonitorPos(_GLFWmonitor* monitor, int* xpos, int* ypos)
    442 {
    443     @autoreleasepool {
    444 
    445     const CGRect bounds = CGDisplayBounds(monitor->ns.displayID);
    446 
    447     if (xpos)
    448         *xpos = (int) bounds.origin.x;
    449     if (ypos)
    450         *ypos = (int) bounds.origin.y;
    451 
    452     } // autoreleasepool
    453 }
    454 
    455 void _glfwPlatformGetMonitorContentScale(_GLFWmonitor* monitor,
    456                                          float* xscale, float* yscale)
    457 {
    458     @autoreleasepool {
    459 
    460     if (!refreshMonitorScreen(monitor))
    461         return;
    462 
    463     const NSRect points = [monitor->ns.screen frame];
    464     const NSRect pixels = [monitor->ns.screen convertRectToBacking:points];
    465 
    466     if (xscale)
    467         *xscale = (float) (pixels.size.width / points.size.width);
    468     if (yscale)
    469         *yscale = (float) (pixels.size.height / points.size.height);
    470 
    471     } // autoreleasepool
    472 }
    473 
    474 void _glfwPlatformGetMonitorWorkarea(_GLFWmonitor* monitor,
    475                                      int* xpos, int* ypos,
    476                                      int* width, int* height)
    477 {
    478     @autoreleasepool {
    479 
    480     if (!refreshMonitorScreen(monitor))
    481         return;
    482 
    483     const NSRect frameRect = [monitor->ns.screen visibleFrame];
    484 
    485     if (xpos)
    486         *xpos = frameRect.origin.x;
    487     if (ypos)
    488         *ypos = _glfwTransformYNS(frameRect.origin.y + frameRect.size.height - 1);
    489     if (width)
    490         *width = frameRect.size.width;
    491     if (height)
    492         *height = frameRect.size.height;
    493 
    494     } // autoreleasepool
    495 }
    496 
    497 GLFWvidmode* _glfwPlatformGetVideoModes(_GLFWmonitor* monitor, int* count)
    498 {
    499     @autoreleasepool {
    500 
    501     *count = 0;
    502 
    503     CFArrayRef modes = CGDisplayCopyAllDisplayModes(monitor->ns.displayID, NULL);
    504     const CFIndex found = CFArrayGetCount(modes);
    505     GLFWvidmode* result = calloc(found, sizeof(GLFWvidmode));
    506 
    507     for (CFIndex i = 0;  i < found;  i++)
    508     {
    509         CGDisplayModeRef dm = (CGDisplayModeRef) CFArrayGetValueAtIndex(modes, i);
    510         if (!modeIsGood(dm))
    511             continue;
    512 
    513         const GLFWvidmode mode =
    514             vidmodeFromCGDisplayMode(dm, monitor->ns.fallbackRefreshRate);
    515         CFIndex j;
    516 
    517         for (j = 0;  j < *count;  j++)
    518         {
    519             if (_glfwCompareVideoModes(result + j, &mode) == 0)
    520                 break;
    521         }
    522 
    523         // Skip duplicate modes
    524         if (i < *count)
    525             continue;
    526 
    527         (*count)++;
    528         result[*count - 1] = mode;
    529     }
    530 
    531     CFRelease(modes);
    532     return result;
    533 
    534     } // autoreleasepool
    535 }
    536 
    537 void _glfwPlatformGetVideoMode(_GLFWmonitor* monitor, GLFWvidmode *mode)
    538 {
    539     @autoreleasepool {
    540 
    541     CGDisplayModeRef native = CGDisplayCopyDisplayMode(monitor->ns.displayID);
    542     *mode = vidmodeFromCGDisplayMode(native, monitor->ns.fallbackRefreshRate);
    543     CGDisplayModeRelease(native);
    544 
    545     } // autoreleasepool
    546 }
    547 
    548 GLFWbool _glfwPlatformGetGammaRamp(_GLFWmonitor* monitor, GLFWgammaramp* ramp)
    549 {
    550     @autoreleasepool {
    551 
    552     uint32_t size = CGDisplayGammaTableCapacity(monitor->ns.displayID);
    553     CGGammaValue* values = calloc(size * 3, sizeof(CGGammaValue));
    554 
    555     CGGetDisplayTransferByTable(monitor->ns.displayID,
    556                                 size,
    557                                 values,
    558                                 values + size,
    559                                 values + size * 2,
    560                                 &size);
    561 
    562     _glfwAllocGammaArrays(ramp, size);
    563 
    564     for (uint32_t i = 0; i < size; i++)
    565     {
    566         ramp->red[i]   = (unsigned short) (values[i] * 65535);
    567         ramp->green[i] = (unsigned short) (values[i + size] * 65535);
    568         ramp->blue[i]  = (unsigned short) (values[i + size * 2] * 65535);
    569     }
    570 
    571     free(values);
    572     return GLFW_TRUE;
    573 
    574     } // autoreleasepool
    575 }
    576 
    577 void _glfwPlatformSetGammaRamp(_GLFWmonitor* monitor, const GLFWgammaramp* ramp)
    578 {
    579     @autoreleasepool {
    580 
    581     CGGammaValue* values = calloc(ramp->size * 3, sizeof(CGGammaValue));
    582 
    583     for (unsigned int i = 0;  i < ramp->size;  i++)
    584     {
    585         values[i]                  = ramp->red[i] / 65535.f;
    586         values[i + ramp->size]     = ramp->green[i] / 65535.f;
    587         values[i + ramp->size * 2] = ramp->blue[i] / 65535.f;
    588     }
    589 
    590     CGSetDisplayTransferByTable(monitor->ns.displayID,
    591                                 ramp->size,
    592                                 values,
    593                                 values + ramp->size,
    594                                 values + ramp->size * 2);
    595 
    596     free(values);
    597 
    598     } // autoreleasepool
    599 }
    600 
    601 
    602 //////////////////////////////////////////////////////////////////////////
    603 //////                        GLFW native API                       //////
    604 //////////////////////////////////////////////////////////////////////////
    605 
    606 GLFWAPI CGDirectDisplayID glfwGetCocoaMonitor(GLFWmonitor* handle)
    607 {
    608     _GLFWmonitor* monitor = (_GLFWmonitor*) handle;
    609     _GLFW_REQUIRE_INIT_OR_RETURN(kCGNullDirectDisplay);
    610     return monitor->ns.displayID;
    611 }
    612