Skip to content

Commit e6e4809

Browse files
authored
Patch GLFW to improve macOS monitor name detection (#2398)
This patch fixes #2384 by porting some of the Cocoa monitor name handling changes from GLFW release 3.4 to the copy of GLFW in the Graph Editor source tree. Specifically, it uses `NSScreen` APIs where possible, instead of older IOKit APIs that do not work with AirPlay connected displays or on Apple Silicon. I chose to only bring over the essential changes from GLFW 3.4, since a full update to 3.4 is much more extensive, harder to review, and probably not merited for such small benefit. Nonetheless, if and when a full update to 3.4 or later is necessary, this patch should merge pretty cleanly. Tested on a MacBook Pro M2 Max running macOS Sequoia 15.4.1 and a MacBook Pro (2019) running macOS Monterey 12.7.5. Gracefully handles dynamic connection/disconnection of external displays and AirPlay connected displays, and removes the spurious log message noted in #2384.
1 parent 830a9a0 commit e6e4809

1 file changed

Lines changed: 41 additions & 13 deletions

File tree

source/MaterialXGraphEditor/External/Glfw/src/cocoa_monitor.m

Lines changed: 41 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -39,18 +39,31 @@
3939

4040
// Get the name of the specified display, or NULL
4141
//
42-
static char* getDisplayName(CGDirectDisplayID displayID)
42+
static char* getMonitorName(CGDirectDisplayID displayID, NSScreen* screen)
4343
{
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+
4457
io_iterator_t it;
4558
io_service_t service;
4659
CFDictionaryRef info;
4760

48-
if (IOServiceGetMatchingServices(kIOMasterPortDefault,
61+
if (IOServiceGetMatchingServices(MACH_PORT_NULL,
4962
IOServiceMatching("IODisplayConnect"),
5063
&it) != 0)
5164
{
5265
// This may happen if a desktop Mac is running headless
53-
return NULL;
66+
return _glfw_strdup("Display");
5467
}
5568

5669
while ((service = IOIteratorNext(it)) != 0)
@@ -85,11 +98,7 @@
8598
IOObjectRelease(it);
8699

87100
if (!service)
88-
{
89-
_glfwInputError(GLFW_PLATFORM_ERROR,
90-
"Cocoa: Failed to find service port for display");
91-
return NULL;
92-
}
101+
return _glfw_strdup("Display");
93102

94103
CFDictionaryRef names =
95104
CFDictionaryGetValue(info, CFSTR(kDisplayProductName));
@@ -101,7 +110,7 @@
101110
{
102111
// This may happen if a desktop Mac is running headless
103112
CFRelease(info);
104-
return NULL;
113+
return _glfw_strdup("Display");
105114
}
106115

107116
const CFIndex size =
@@ -328,27 +337,46 @@ void _glfwPollMonitorsNS(void)
328337
if (CGDisplayIsAsleep(displays[i]))
329338
continue;
330339

340+
const uint32_t unitNumber = CGDisplayUnitNumber(displays[i]);
341+
NSScreen* screen = nil;
342+
343+
for (screen in [NSScreen screens])
344+
{
345+
NSNumber* screenNumber = [screen deviceDescription][@"NSScreenNumber"];
346+
347+
// HACK: Compare unit numbers instead of display IDs to work around
348+
// display replacement on machines with automatic graphics
349+
// switching
350+
if (CGDisplayUnitNumber([screenNumber unsignedIntValue]) == unitNumber)
351+
break;
352+
}
353+
331354
// HACK: Compare unit numbers instead of display IDs to work around
332355
// display replacement on machines with automatic graphics
333356
// switching
334-
const uint32_t unitNumber = CGDisplayUnitNumber(displays[i]);
335-
for (uint32_t j = 0; j < disconnectedCount; j++)
357+
uint32_t j;
358+
for (j = 0; j < disconnectedCount; j++)
336359
{
337360
if (disconnected[j] && disconnected[j]->ns.unitNumber == unitNumber)
338361
{
362+
disconnected[j]->ns.screen = screen;
339363
disconnected[j] = NULL;
340364
break;
341365
}
342366
}
343367

368+
if (j < disconnectedCount)
369+
continue;
370+
344371
const CGSize size = CGDisplayScreenSize(displays[i]);
345-
char* name = getDisplayName(displays[i]);
372+
char* name = getMonitorName(displays[i], screen);
346373
if (!name)
347-
name = _glfw_strdup("Unknown");
374+
continue;
348375

349376
_GLFWmonitor* monitor = _glfwAllocMonitor(name, size.width, size.height);
350377
monitor->ns.displayID = displays[i];
351378
monitor->ns.unitNumber = unitNumber;
379+
monitor->ns.screen = screen;
352380

353381
free(name);
354382

0 commit comments

Comments
 (0)