volviewer

Volumetric Data Toy Viewer
git clone anongit@rnpnr.xyz:volviewer.git
Log | Files | Refs | Feed | LICENSE

cocoa_monitor.m (19725B)


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