cocoa_monitor.m 22 KB

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