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, ¤t); 388 389 const GLFWvidmode* best = _glfwChooseVideoMode(monitor, desired); 390 if (_glfwCompareVideoModes(¤t, 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