In Mac OS X, every display gets a unique CGDirectDisplayID
number assigned to it. You can use CGGetActiveDisplayList(
) or [NSScreen screens]
While I'm no pro, I believe the answer is to allow Apple to notify you when the user changes displays. The info inf the callback contains flags for adding and removing CGDirectDisplayID
s.
The user shouldn't be adding or removing graphics cards during operation, so I would play with making list at startup, and whenever you get the "remove" flag set the next "add" operation to replace that ID in the list.
I'd try just printing the information you get back each time CGDisplayRegisterReconfigurationCallback
calls your function. See if you get one with a DeviceUID with a 'remove' flag, and then a subsequent call another with an 'add' flag. Checking those id's against CGGetActiveDisplayList
would also aid in understanding what's going on.
That's my best bet, hope it helps!
Use CFUUIDRef which can be obtained using:
CGDisplayCreateUUIDFromDisplayID(CGDirectDisplayID displayID)
and you can get the display ID back using:
CGDisplayGetDisplayIDFromUUID(CFUUIDRef uuid)
This is what I'm using to uniquely identify displays even when their CGDirectDisplayID changes, for example was plugged into a different port. These functions aren't properly documented by Apple unfortunately, but my testing on multiple Macs with multiple displays shown that the CFUUIDRef obtained is unique and consistent -even after a reboot-, regardless of whether CGDirectDisplayID changed for whatever reason.
To check if a display is new/unique, take its CGDirectDisplayID and convert it to CFUUIDRef, and then compare the UUID, it is a many-to-one relationship, many CGDirectDisplayIDs will map to a single CFUUIDRef.
These API calls are available in ApplicationServices in 10.7 - 10.12, and ColorSync since 10.13.
I have found no conceputally better way than what you list as "Tried". But I found a solution for the ambiguity issue of comparing only vendor id and product id.
oldInfoDict
and newInfoDict
in your code contain an additional entry for key kIODisplayEDIDKey
(defined in IOGraphicsTypes.h) which contains the EDID of each connected display. My observations show that this data as a whole stays persistent between GPU switches. For example:
CGDirectDisplayID displayId = [[[screen deviceDescription] valueForKey:@"NSScreenNumber"] unsignedIntValue];
io_service_t displayPort = CGDisplayIOServicePort(displayId);
if (displayPort == MACH_PORT_NULL)
return nil; // No physical device to get a name from.
CFDictionaryRef infoDict = IODisplayCreateInfoDictionary(displayPort, kIODisplayOnlyPreferredName);
NSData *displayEdid = (NSData *)CFDictionaryGetValue(infoDict, CFSTR(kIODisplayEDIDKey));
NSLog(@"EDID: %@", displayEdid);
CFRelease(infoDict);
Looking at the data description of EDID in Wikipedia, this blob already contains manufacturer, product id and serial. So it's enough to compare displays by using the EDID data (or for example a hash of it if you only want to compare a shorter number).