Wise&mystical  1.0
Project about Europe
Loading...
Searching...
No Matches
cocoa_monitor.m
Go to the documentation of this file.
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//
42static 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(kIOMasterPortDefault,
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 {
103 "Cocoa: Failed to find service port for display");
104 return _glfw_strdup("Display");
105 }
106
107 CFDictionaryRef names =
108 CFDictionaryGetValue(info, CFSTR(kDisplayProductName));
109
110 CFStringRef nameRef;
111
112 if (!names || !CFDictionaryGetValueIfPresent(names, CFSTR("en_US"),
113 (const void**) &nameRef))
114 {
115 // This may happen if a desktop Mac is running headless
116 CFRelease(info);
117 return _glfw_strdup("Display");
118 }
119
120 const CFIndex size =
121 CFStringGetMaximumSizeForEncoding(CFStringGetLength(nameRef),
122 kCFStringEncodingUTF8);
123 char* name = calloc(size + 1, 1);
124 CFStringGetCString(nameRef, name, size, kCFStringEncodingUTF8);
125
126 CFRelease(info);
127 return name;
128}
129
130// Check whether the display mode should be included in enumeration
131//
132static GLFWbool modeIsGood(CGDisplayModeRef mode)
133{
134 uint32_t flags = CGDisplayModeGetIOFlags(mode);
135
136 if (!(flags & kDisplayModeValidFlag) || !(flags & kDisplayModeSafeFlag))
137 return GLFW_FALSE;
138 if (flags & kDisplayModeInterlacedFlag)
139 return GLFW_FALSE;
140 if (flags & kDisplayModeStretchedFlag)
141 return GLFW_FALSE;
142
143#if MAC_OS_X_VERSION_MAX_ALLOWED <= 101100
144 CFStringRef format = CGDisplayModeCopyPixelEncoding(mode);
145 if (CFStringCompare(format, CFSTR(IO16BitDirectPixels), 0) &&
146 CFStringCompare(format, CFSTR(IO32BitDirectPixels), 0))
147 {
148 CFRelease(format);
149 return GLFW_FALSE;
150 }
151
152 CFRelease(format);
153#endif /* MAC_OS_X_VERSION_MAX_ALLOWED */
154 return GLFW_TRUE;
155}
156
157// Convert Core Graphics display mode to GLFW video mode
158//
159static GLFWvidmode vidmodeFromCGDisplayMode(CGDisplayModeRef mode,
160 double fallbackRefreshRate)
161{
162 GLFWvidmode result;
163 result.width = (int) CGDisplayModeGetWidth(mode);
164 result.height = (int) CGDisplayModeGetHeight(mode);
165 result.refreshRate = (int) round(CGDisplayModeGetRefreshRate(mode));
166
167 if (result.refreshRate == 0)
168 result.refreshRate = (int) round(fallbackRefreshRate);
169
170#if MAC_OS_X_VERSION_MAX_ALLOWED <= 101100
171 CFStringRef format = CGDisplayModeCopyPixelEncoding(mode);
172 if (CFStringCompare(format, CFSTR(IO16BitDirectPixels), 0) == 0)
173 {
174 result.redBits = 5;
175 result.greenBits = 5;
176 result.blueBits = 5;
177 }
178 else
179#endif /* MAC_OS_X_VERSION_MAX_ALLOWED */
180 {
181 result.redBits = 8;
182 result.greenBits = 8;
183 result.blueBits = 8;
184 }
185
186#if MAC_OS_X_VERSION_MAX_ALLOWED <= 101100
187 CFRelease(format);
188#endif /* MAC_OS_X_VERSION_MAX_ALLOWED */
189 return result;
190}
191
192// Starts reservation for display fading
193//
194static CGDisplayFadeReservationToken beginFadeReservation(void)
195{
196 CGDisplayFadeReservationToken token = kCGDisplayFadeReservationInvalidToken;
197
198 if (CGAcquireDisplayFadeReservation(5, &token) == kCGErrorSuccess)
199 {
200 CGDisplayFade(token, 0.3,
201 kCGDisplayBlendNormal,
202 kCGDisplayBlendSolidColor,
203 0.0, 0.0, 0.0,
204 TRUE);
205 }
206
207 return token;
208}
209
210// Ends reservation for display fading
211//
212static void endFadeReservation(CGDisplayFadeReservationToken token)
213{
214 if (token != kCGDisplayFadeReservationInvalidToken)
215 {
216 CGDisplayFade(token, 0.5,
217 kCGDisplayBlendSolidColor,
218 kCGDisplayBlendNormal,
219 0.0, 0.0, 0.0,
220 FALSE);
221 CGReleaseDisplayFadeReservation(token);
222 }
223}
224
225// Returns the display refresh rate queried from the I/O registry
226//
227static double getFallbackRefreshRate(CGDirectDisplayID displayID)
228{
229 double refreshRate = 60.0;
230
231 io_iterator_t it;
232 io_service_t service;
233
234 if (IOServiceGetMatchingServices(kIOMasterPortDefault,
235 IOServiceMatching("IOFramebuffer"),
236 &it) != 0)
237 {
238 return refreshRate;
239 }
240
241 while ((service = IOIteratorNext(it)) != 0)
242 {
243 const CFNumberRef indexRef =
244 IORegistryEntryCreateCFProperty(service,
245 CFSTR("IOFramebufferOpenGLIndex"),
246 kCFAllocatorDefault,
247 kNilOptions);
248 if (!indexRef)
249 continue;
250
251 uint32_t index = 0;
252 CFNumberGetValue(indexRef, kCFNumberIntType, &index);
253 CFRelease(indexRef);
254
255 if (CGOpenGLDisplayMaskToDisplayID(1 << index) != displayID)
256 continue;
257
258 const CFNumberRef clockRef =
259 IORegistryEntryCreateCFProperty(service,
260 CFSTR("IOFBCurrentPixelClock"),
261 kCFAllocatorDefault,
262 kNilOptions);
263 const CFNumberRef countRef =
264 IORegistryEntryCreateCFProperty(service,
265 CFSTR("IOFBCurrentPixelCount"),
266 kCFAllocatorDefault,
267 kNilOptions);
268
269 uint32_t clock = 0, count = 0;
270
271 if (clockRef)
272 {
273 CFNumberGetValue(clockRef, kCFNumberIntType, &clock);
274 CFRelease(clockRef);
275 }
276
277 if (countRef)
278 {
279 CFNumberGetValue(countRef, kCFNumberIntType, &count);
280 CFRelease(countRef);
281 }
282
283 if (clock > 0 && count > 0)
284 refreshRate = clock / (double) count;
285
286 break;
287 }
288
289 IOObjectRelease(it);
290 return refreshRate;
291}
292
293
297
298// Poll for changes in the set of connected monitors
299//
301{
302 uint32_t displayCount;
303 CGGetOnlineDisplayList(0, NULL, &displayCount);
304 CGDirectDisplayID* displays = calloc(displayCount, sizeof(CGDirectDisplayID));
305 CGGetOnlineDisplayList(displayCount, displays, &displayCount);
306
307 for (int i = 0; i < _glfw.monitorCount; i++)
308 _glfw.monitors[i]->ns.screen = nil;
309
310 _GLFWmonitor** disconnected = NULL;
311 uint32_t disconnectedCount = _glfw.monitorCount;
312 if (disconnectedCount)
313 {
314 disconnected = calloc(_glfw.monitorCount, sizeof(_GLFWmonitor*));
315 memcpy(disconnected,
317 _glfw.monitorCount * sizeof(_GLFWmonitor*));
318 }
319
320 for (uint32_t i = 0; i < displayCount; i++)
321 {
322 if (CGDisplayIsAsleep(displays[i]))
323 continue;
324
325 const uint32_t unitNumber = CGDisplayUnitNumber(displays[i]);
326 NSScreen* screen = nil;
327
328 for (screen in [NSScreen screens])
329 {
330 NSNumber* screenNumber = [screen deviceDescription][@"NSScreenNumber"];
331
332 // HACK: Compare unit numbers instead of display IDs to work around
333 // display replacement on machines with automatic graphics
334 // switching
335 if (CGDisplayUnitNumber([screenNumber unsignedIntValue]) == unitNumber)
336 break;
337 }
338
339 // HACK: Compare unit numbers instead of display IDs to work around
340 // display replacement on machines with automatic graphics
341 // switching
342 uint32_t j;
343 for (j = 0; j < disconnectedCount; j++)
344 {
345 if (disconnected[j] && disconnected[j]->ns.unitNumber == unitNumber)
346 {
347 disconnected[j]->ns.screen = screen;
348 disconnected[j] = NULL;
349 break;
350 }
351 }
352
353 if (j < disconnectedCount)
354 continue;
355
356 const CGSize size = CGDisplayScreenSize(displays[i]);
357 char* name = getMonitorName(displays[i], screen);
358 if (!name)
359 continue;
360
361 _GLFWmonitor* monitor = _glfwAllocMonitor(name, size.width, size.height);
362 monitor->ns.displayID = displays[i];
363 monitor->ns.unitNumber = unitNumber;
364 monitor->ns.screen = screen;
365
366 free(name);
367
368 CGDisplayModeRef mode = CGDisplayCopyDisplayMode(displays[i]);
369 if (CGDisplayModeGetRefreshRate(mode) == 0.0)
370 monitor->ns.fallbackRefreshRate = getFallbackRefreshRate(displays[i]);
371 CGDisplayModeRelease(mode);
372
374 }
375
376 for (uint32_t i = 0; i < disconnectedCount; i++)
377 {
378 if (disconnected[i])
379 _glfwInputMonitor(disconnected[i], GLFW_DISCONNECTED, 0);
380 }
381
382 free(disconnected);
383 free(displays);
384}
385
386// Change the current video mode
387//
388void _glfwSetVideoModeNS(_GLFWmonitor* monitor, const GLFWvidmode* desired)
389{
390 GLFWvidmode current;
391 _glfwPlatformGetVideoMode(monitor, &current);
392
393 const GLFWvidmode* best = _glfwChooseVideoMode(monitor, desired);
394 if (_glfwCompareVideoModes(&current, best) == 0)
395 return;
396
397 CFArrayRef modes = CGDisplayCopyAllDisplayModes(monitor->ns.displayID, NULL);
398 const CFIndex count = CFArrayGetCount(modes);
399 CGDisplayModeRef native = NULL;
400
401 for (CFIndex i = 0; i < count; i++)
402 {
403 CGDisplayModeRef dm = (CGDisplayModeRef) CFArrayGetValueAtIndex(modes, i);
404 if (!modeIsGood(dm))
405 continue;
406
407 const GLFWvidmode mode =
408 vidmodeFromCGDisplayMode(dm, monitor->ns.fallbackRefreshRate);
409 if (_glfwCompareVideoModes(best, &mode) == 0)
410 {
411 native = dm;
412 break;
413 }
414 }
415
416 if (native)
417 {
418 if (monitor->ns.previousMode == NULL)
419 monitor->ns.previousMode = CGDisplayCopyDisplayMode(monitor->ns.displayID);
420
421 CGDisplayFadeReservationToken token = beginFadeReservation();
422 CGDisplaySetDisplayMode(monitor->ns.displayID, native, NULL);
423 endFadeReservation(token);
424 }
425
426 CFRelease(modes);
427}
428
429// Restore the previously saved (original) video mode
430//
432{
433 if (monitor->ns.previousMode)
434 {
435 CGDisplayFadeReservationToken token = beginFadeReservation();
436 CGDisplaySetDisplayMode(monitor->ns.displayID,
437 monitor->ns.previousMode, NULL);
438 endFadeReservation(token);
439
440 CGDisplayModeRelease(monitor->ns.previousMode);
441 monitor->ns.previousMode = NULL;
442 }
443}
444
445
449
451{
452}
453
454void _glfwPlatformGetMonitorPos(_GLFWmonitor* monitor, int* xpos, int* ypos)
455{
456 @autoreleasepool {
457
458 const CGRect bounds = CGDisplayBounds(monitor->ns.displayID);
459
460 if (xpos)
461 *xpos = (int) bounds.origin.x;
462 if (ypos)
463 *ypos = (int) bounds.origin.y;
464
465 } // autoreleasepool
466}
467
469 float* xscale, float* yscale)
470{
471 @autoreleasepool {
472
473 if (!monitor->ns.screen)
474 {
476 "Cocoa: Cannot query content scale without screen");
477 }
478
479 const NSRect points = [monitor->ns.screen frame];
480 const NSRect pixels = [monitor->ns.screen convertRectToBacking:points];
481
482 if (xscale)
483 *xscale = (float) (pixels.size.width / points.size.width);
484 if (yscale)
485 *yscale = (float) (pixels.size.height / points.size.height);
486
487 } // autoreleasepool
488}
489
491 int* xpos, int* ypos,
492 int* width, int* height)
493{
494 @autoreleasepool {
495
496 if (!monitor->ns.screen)
497 {
499 "Cocoa: Cannot query workarea without screen");
500 }
501
502 const NSRect frameRect = [monitor->ns.screen visibleFrame];
503
504 if (xpos)
505 *xpos = frameRect.origin.x;
506 if (ypos)
507 *ypos = _glfwTransformYNS(frameRect.origin.y + frameRect.size.height - 1);
508 if (width)
509 *width = frameRect.size.width;
510 if (height)
511 *height = frameRect.size.height;
512
513 } // autoreleasepool
514}
515
517{
518 @autoreleasepool {
519
520 *count = 0;
521
522 CFArrayRef modes = CGDisplayCopyAllDisplayModes(monitor->ns.displayID, NULL);
523 const CFIndex found = CFArrayGetCount(modes);
524 GLFWvidmode* result = calloc(found, sizeof(GLFWvidmode));
525
526 for (CFIndex i = 0; i < found; i++)
527 {
528 CGDisplayModeRef dm = (CGDisplayModeRef) CFArrayGetValueAtIndex(modes, i);
529 if (!modeIsGood(dm))
530 continue;
531
532 const GLFWvidmode mode =
533 vidmodeFromCGDisplayMode(dm, monitor->ns.fallbackRefreshRate);
534 CFIndex j;
535
536 for (j = 0; j < *count; j++)
537 {
538 if (_glfwCompareVideoModes(result + j, &mode) == 0)
539 break;
540 }
541
542 // Skip duplicate modes
543 if (j < *count)
544 continue;
545
546 (*count)++;
547 result[*count - 1] = mode;
548 }
549
550 CFRelease(modes);
551 return result;
552
553 } // autoreleasepool
554}
555
557{
558 @autoreleasepool {
559
560 CGDisplayModeRef native = CGDisplayCopyDisplayMode(monitor->ns.displayID);
561 *mode = vidmodeFromCGDisplayMode(native, monitor->ns.fallbackRefreshRate);
562 CGDisplayModeRelease(native);
563
564 } // autoreleasepool
565}
566
568{
569 @autoreleasepool {
570
571 uint32_t size = CGDisplayGammaTableCapacity(monitor->ns.displayID);
572 CGGammaValue* values = calloc(size * 3, sizeof(CGGammaValue));
573
574 CGGetDisplayTransferByTable(monitor->ns.displayID,
575 size,
576 values,
577 values + size,
578 values + size * 2,
579 &size);
580
581 _glfwAllocGammaArrays(ramp, size);
582
583 for (uint32_t i = 0; i < size; i++)
584 {
585 ramp->red[i] = (unsigned short) (values[i] * 65535);
586 ramp->green[i] = (unsigned short) (values[i + size] * 65535);
587 ramp->blue[i] = (unsigned short) (values[i + size * 2] * 65535);
588 }
589
590 free(values);
591 return GLFW_TRUE;
592
593 } // autoreleasepool
594}
595
597{
598 @autoreleasepool {
599
600 CGGammaValue* values = calloc(ramp->size * 3, sizeof(CGGammaValue));
601
602 for (unsigned int i = 0; i < ramp->size; i++)
603 {
604 values[i] = ramp->red[i] / 65535.f;
605 values[i + ramp->size] = ramp->green[i] / 65535.f;
606 values[i + ramp->size * 2] = ramp->blue[i] / 65535.f;
607 }
608
609 CGSetDisplayTransferByTable(monitor->ns.displayID,
610 ramp->size,
611 values,
612 values + ramp->size,
613 values + ramp->size * 2);
614
615 free(values);
616
617 } // autoreleasepool
618}
619
620
624
625GLFWAPI CGDirectDisplayID glfwGetCocoaMonitor(GLFWmonitor* handle)
626{
627 _GLFWmonitor* monitor = (_GLFWmonitor*) handle;
628 _GLFW_REQUIRE_INIT_OR_RETURN(kCGNullDirectDisplay);
629 return monitor->ns.displayID;
630}
631
void _glfwPlatformFreeMonitor(_GLFWmonitor *monitor)
void _glfwRestoreVideoModeNS(_GLFWmonitor *monitor)
GLFWvidmode * _glfwPlatformGetVideoModes(_GLFWmonitor *monitor, int *count)
GLFWAPI CGDirectDisplayID glfwGetCocoaMonitor(GLFWmonitor *handle)
void _glfwPlatformGetVideoMode(_GLFWmonitor *monitor, GLFWvidmode *mode)
void _glfwSetVideoModeNS(_GLFWmonitor *monitor, const GLFWvidmode *desired)
void _glfwPlatformSetGammaRamp(_GLFWmonitor *monitor, const GLFWgammaramp *ramp)
void _glfwPlatformGetMonitorPos(_GLFWmonitor *monitor, int *xpos, int *ypos)
GLFWbool _glfwPlatformGetGammaRamp(_GLFWmonitor *monitor, GLFWgammaramp *ramp)
void _glfwPlatformGetMonitorContentScale(_GLFWmonitor *monitor, float *xscale, float *yscale)
void _glfwPlatformGetMonitorWorkarea(_GLFWmonitor *monitor, int *xpos, int *ypos, int *width, int *height)
void _glfwPollMonitorsNS(void)
float _glfwTransformYNS(float y)
Definition: cocoa_window.m:889
#define GLFWAPI
Definition: glfw3.h:269
#define GLFW_DISCONNECTED
Definition: glfw3.h:1231
#define GLFW_CONNECTED
Definition: glfw3.h:1230
#define GLFW_PLATFORM_ERROR
A platform-specific error occurred that does not match any of the more specific categories.
Definition: glfw3.h:758
#define GLFW_TRUE
One.
Definition: glfw3.h:310
#define GLFW_FALSE
Zero.
Definition: glfw3.h:319
struct GLFWmonitor GLFWmonitor
Opaque monitor object.
Definition: glfw3.h:1307
_GLFWlibrary _glfw
Definition: init.c:46
void _glfwInputError(int code, const char *format,...)
Definition: init.c:160
char * _glfw_strdup(const char *source)
Definition: init.c:121
void _glfwInputMonitor(_GLFWmonitor *monitor, int action, int placement)
Definition: monitor.c:97
const GLFWvidmode * _glfwChooseVideoMode(_GLFWmonitor *monitor, const GLFWvidmode *desired)
Definition: monitor.c:217
#define _GLFW_REQUIRE_INIT_OR_RETURN(x)
Definition: internal.h:214
int _glfwCompareVideoModes(const GLFWvidmode *first, const GLFWvidmode *second)
Definition: monitor.c:269
_GLFWmonitor * _glfwAllocMonitor(const char *name, int widthMM, int heightMM)
Definition: monitor.c:167
#define _GLFW_INSERT_LAST
Definition: internal.h:52
int GLFWbool
Definition: internal.h:61
void _glfwAllocGammaArrays(GLFWgammaramp *ramp, unsigned int size)
Definition: monitor.c:196
#define NULL
Definition: miniaudio.h:3718
unsigned int uint32_t
Definition: stdint.h:80
_GLFWmonitor ** monitors
Definition: internal.h:543
int monitorCount
Definition: internal.h:544
Gamma ramp.
Definition: glfw3.h:1826
unsigned short * red
Definition: glfw3.h:1829
unsigned short * blue
Definition: glfw3.h:1835
unsigned int size
Definition: glfw3.h:1838
unsigned short * green
Definition: glfw3.h:1832
Video mode type.
Definition: glfw3.h:1792
int greenBits
Definition: glfw3.h:1804
int redBits
Definition: glfw3.h:1801
int width
Definition: glfw3.h:1795
int refreshRate
Definition: glfw3.h:1810
int height
Definition: glfw3.h:1798
int blueBits
Definition: glfw3.h:1807