]> git.sesse.net Git - pistorm/blob - raylib_pi4_test/external/glfw/src/cocoa_monitor.m
Update raylib files and Makefile for Pi 4 testing
[pistorm] / raylib_pi4_test / external / glfw / src / cocoa_monitor.m
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* 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
281         uint32_t clock = 0, count = 0;
282
283         if (clockRef)
284         {
285             CFNumberGetValue(clockRef, kCFNumberIntType, &clock);
286             CFRelease(clockRef);
287         }
288
289         if (countRef)
290         {
291             CFNumberGetValue(countRef, kCFNumberIntType, &count);
292             CFRelease(countRef);
293         }
294
295         if (clock > 0 && count > 0)
296             refreshRate = clock / (double) count;
297
298         break;
299     }
300
301     IOObjectRelease(it);
302     return refreshRate;
303 }
304
305
306 //////////////////////////////////////////////////////////////////////////
307 //////                       GLFW internal API                      //////
308 //////////////////////////////////////////////////////////////////////////
309
310 // Poll for changes in the set of connected monitors
311 //
312 void _glfwPollMonitorsNS(void)
313 {
314     uint32_t displayCount;
315     CGGetOnlineDisplayList(0, NULL, &displayCount);
316     CGDirectDisplayID* displays = calloc(displayCount, sizeof(CGDirectDisplayID));
317     CGGetOnlineDisplayList(displayCount, displays, &displayCount);
318
319     for (int i = 0;  i < _glfw.monitorCount;  i++)
320         _glfw.monitors[i]->ns.screen = nil;
321
322     _GLFWmonitor** disconnected = NULL;
323     uint32_t disconnectedCount = _glfw.monitorCount;
324     if (disconnectedCount)
325     {
326         disconnected = calloc(_glfw.monitorCount, sizeof(_GLFWmonitor*));
327         memcpy(disconnected,
328                _glfw.monitors,
329                _glfw.monitorCount * sizeof(_GLFWmonitor*));
330     }
331
332     for (uint32_t i = 0;  i < displayCount;  i++)
333     {
334         if (CGDisplayIsAsleep(displays[i]))
335             continue;
336
337         // HACK: Compare unit numbers instead of display IDs to work around
338         //       display replacement on machines with automatic graphics
339         //       switching
340         const uint32_t unitNumber = CGDisplayUnitNumber(displays[i]);
341         for (uint32_t j = 0;  j < disconnectedCount;  j++)
342         {
343             if (disconnected[j] && disconnected[j]->ns.unitNumber == unitNumber)
344             {
345                 disconnected[j] = NULL;
346                 break;
347             }
348         }
349
350         const CGSize size = CGDisplayScreenSize(displays[i]);
351         char* name = getDisplayName(displays[i]);
352         if (!name)
353             name = _glfw_strdup("Unknown");
354
355         _GLFWmonitor* monitor = _glfwAllocMonitor(name, size.width, size.height);
356         monitor->ns.displayID  = displays[i];
357         monitor->ns.unitNumber = unitNumber;
358
359         free(name);
360
361         CGDisplayModeRef mode = CGDisplayCopyDisplayMode(displays[i]);
362         if (CGDisplayModeGetRefreshRate(mode) == 0.0)
363             monitor->ns.fallbackRefreshRate = getFallbackRefreshRate(displays[i]);
364         CGDisplayModeRelease(mode);
365
366         _glfwInputMonitor(monitor, GLFW_CONNECTED, _GLFW_INSERT_LAST);
367     }
368
369     for (uint32_t i = 0;  i < disconnectedCount;  i++)
370     {
371         if (disconnected[i])
372             _glfwInputMonitor(disconnected[i], GLFW_DISCONNECTED, 0);
373     }
374
375     free(disconnected);
376     free(displays);
377 }
378
379 // Change the current video mode
380 //
381 void _glfwSetVideoModeNS(_GLFWmonitor* monitor, const GLFWvidmode* desired)
382 {
383     GLFWvidmode current;
384     _glfwPlatformGetVideoMode(monitor, &current);
385
386     const GLFWvidmode* best = _glfwChooseVideoMode(monitor, desired);
387     if (_glfwCompareVideoModes(&current, best) == 0)
388         return;
389
390     CFArrayRef modes = CGDisplayCopyAllDisplayModes(monitor->ns.displayID, NULL);
391     const CFIndex count = CFArrayGetCount(modes);
392     CGDisplayModeRef native = NULL;
393
394     for (CFIndex i = 0;  i < count;  i++)
395     {
396         CGDisplayModeRef dm = (CGDisplayModeRef) CFArrayGetValueAtIndex(modes, i);
397         if (!modeIsGood(dm))
398             continue;
399
400         const GLFWvidmode mode =
401             vidmodeFromCGDisplayMode(dm, monitor->ns.fallbackRefreshRate);
402         if (_glfwCompareVideoModes(best, &mode) == 0)
403         {
404             native = dm;
405             break;
406         }
407     }
408
409     if (native)
410     {
411         if (monitor->ns.previousMode == NULL)
412             monitor->ns.previousMode = CGDisplayCopyDisplayMode(monitor->ns.displayID);
413
414         CGDisplayFadeReservationToken token = beginFadeReservation();
415         CGDisplaySetDisplayMode(monitor->ns.displayID, native, NULL);
416         endFadeReservation(token);
417     }
418
419     CFRelease(modes);
420 }
421
422 // Restore the previously saved (original) video mode
423 //
424 void _glfwRestoreVideoModeNS(_GLFWmonitor* monitor)
425 {
426     if (monitor->ns.previousMode)
427     {
428         CGDisplayFadeReservationToken token = beginFadeReservation();
429         CGDisplaySetDisplayMode(monitor->ns.displayID,
430                                 monitor->ns.previousMode, NULL);
431         endFadeReservation(token);
432
433         CGDisplayModeRelease(monitor->ns.previousMode);
434         monitor->ns.previousMode = NULL;
435     }
436 }
437
438
439 //////////////////////////////////////////////////////////////////////////
440 //////                       GLFW platform API                      //////
441 //////////////////////////////////////////////////////////////////////////
442
443 void _glfwPlatformFreeMonitor(_GLFWmonitor* monitor)
444 {
445 }
446
447 void _glfwPlatformGetMonitorPos(_GLFWmonitor* monitor, int* xpos, int* ypos)
448 {
449     @autoreleasepool {
450
451     const CGRect bounds = CGDisplayBounds(monitor->ns.displayID);
452
453     if (xpos)
454         *xpos = (int) bounds.origin.x;
455     if (ypos)
456         *ypos = (int) bounds.origin.y;
457
458     } // autoreleasepool
459 }
460
461 void _glfwPlatformGetMonitorContentScale(_GLFWmonitor* monitor,
462                                          float* xscale, float* yscale)
463 {
464     @autoreleasepool {
465
466     if (!refreshMonitorScreen(monitor))
467         return;
468
469     const NSRect points = [monitor->ns.screen frame];
470     const NSRect pixels = [monitor->ns.screen convertRectToBacking:points];
471
472     if (xscale)
473         *xscale = (float) (pixels.size.width / points.size.width);
474     if (yscale)
475         *yscale = (float) (pixels.size.height / points.size.height);
476
477     } // autoreleasepool
478 }
479
480 void _glfwPlatformGetMonitorWorkarea(_GLFWmonitor* monitor,
481                                      int* xpos, int* ypos,
482                                      int* width, int* height)
483 {
484     @autoreleasepool {
485
486     if (!refreshMonitorScreen(monitor))
487         return;
488
489     const NSRect frameRect = [monitor->ns.screen visibleFrame];
490
491     if (xpos)
492         *xpos = frameRect.origin.x;
493     if (ypos)
494         *ypos = _glfwTransformYNS(frameRect.origin.y + frameRect.size.height - 1);
495     if (width)
496         *width = frameRect.size.width;
497     if (height)
498         *height = frameRect.size.height;
499
500     } // autoreleasepool
501 }
502
503 GLFWvidmode* _glfwPlatformGetVideoModes(_GLFWmonitor* monitor, int* count)
504 {
505     @autoreleasepool {
506
507     *count = 0;
508
509     CFArrayRef modes = CGDisplayCopyAllDisplayModes(monitor->ns.displayID, NULL);
510     const CFIndex found = CFArrayGetCount(modes);
511     GLFWvidmode* result = calloc(found, sizeof(GLFWvidmode));
512
513     for (CFIndex i = 0;  i < found;  i++)
514     {
515         CGDisplayModeRef dm = (CGDisplayModeRef) CFArrayGetValueAtIndex(modes, i);
516         if (!modeIsGood(dm))
517             continue;
518
519         const GLFWvidmode mode =
520             vidmodeFromCGDisplayMode(dm, monitor->ns.fallbackRefreshRate);
521         CFIndex j;
522
523         for (j = 0;  j < *count;  j++)
524         {
525             if (_glfwCompareVideoModes(result + j, &mode) == 0)
526                 break;
527         }
528
529         // Skip duplicate modes
530         if (i < *count)
531             continue;
532
533         (*count)++;
534         result[*count - 1] = mode;
535     }
536
537     CFRelease(modes);
538     return result;
539
540     } // autoreleasepool
541 }
542
543 void _glfwPlatformGetVideoMode(_GLFWmonitor* monitor, GLFWvidmode *mode)
544 {
545     @autoreleasepool {
546
547     CGDisplayModeRef native = CGDisplayCopyDisplayMode(monitor->ns.displayID);
548     *mode = vidmodeFromCGDisplayMode(native, monitor->ns.fallbackRefreshRate);
549     CGDisplayModeRelease(native);
550
551     } // autoreleasepool
552 }
553
554 GLFWbool _glfwPlatformGetGammaRamp(_GLFWmonitor* monitor, GLFWgammaramp* ramp)
555 {
556     @autoreleasepool {
557
558     uint32_t size = CGDisplayGammaTableCapacity(monitor->ns.displayID);
559     CGGammaValue* values = calloc(size * 3, sizeof(CGGammaValue));
560
561     CGGetDisplayTransferByTable(monitor->ns.displayID,
562                                 size,
563                                 values,
564                                 values + size,
565                                 values + size * 2,
566                                 &size);
567
568     _glfwAllocGammaArrays(ramp, size);
569
570     for (uint32_t i = 0; i < size; i++)
571     {
572         ramp->red[i]   = (unsigned short) (values[i] * 65535);
573         ramp->green[i] = (unsigned short) (values[i + size] * 65535);
574         ramp->blue[i]  = (unsigned short) (values[i + size * 2] * 65535);
575     }
576
577     free(values);
578     return GLFW_TRUE;
579
580     } // autoreleasepool
581 }
582
583 void _glfwPlatformSetGammaRamp(_GLFWmonitor* monitor, const GLFWgammaramp* ramp)
584 {
585     @autoreleasepool {
586
587     CGGammaValue* values = calloc(ramp->size * 3, sizeof(CGGammaValue));
588
589     for (unsigned int i = 0;  i < ramp->size;  i++)
590     {
591         values[i]                  = ramp->red[i] / 65535.f;
592         values[i + ramp->size]     = ramp->green[i] / 65535.f;
593         values[i + ramp->size * 2] = ramp->blue[i] / 65535.f;
594     }
595
596     CGSetDisplayTransferByTable(monitor->ns.displayID,
597                                 ramp->size,
598                                 values,
599                                 values + ramp->size,
600                                 values + ramp->size * 2);
601
602     free(values);
603
604     } // autoreleasepool
605 }
606
607
608 //////////////////////////////////////////////////////////////////////////
609 //////                        GLFW native API                       //////
610 //////////////////////////////////////////////////////////////////////////
611
612 GLFWAPI CGDirectDisplayID glfwGetCocoaMonitor(GLFWmonitor* handle)
613 {
614     _GLFWmonitor* monitor = (_GLFWmonitor*) handle;
615     _GLFW_REQUIRE_INIT_OR_RETURN(kCGNullDirectDisplay);
616     return monitor->ns.displayID;
617 }
618