github.com/Konstantin8105/c4go@v0.0.0-20240505174241-768bb1c65a51/tests/raylib/external/glfw/src/cocoa_monitor.m (about)

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