9

I need to uniquely identify a display to the extent that the displays are indeed characteristically different. Thus, the same models of displays, plugged into the same physical port would not be treated as different, but basically everything else would be.

When the system GPU changes, the CGDirectDisplayID also changes, but not in a documented way. Experimentation indicates the same screen ID will differ by 2 depending on which GPU is used.

CGDisplayID changing issues

A way to overcome this has been to get an IO registry string for the display:

io_service_t  servicePort = CGDisplayIOServicePort (cgDisplayID);
io_service_t  root = IODisplayForFramebuffer (servicePort, kNilOptions);
NSDictionary* ioRegistryDict = nil;
NSString*     displayKey = nil;

IORegistryEntryCreateCFProperties (root, (CFMutableDictionaryRef *)&ioRegistryDict, kCFAllocatorDefault, kNilOptions);

if (ioRegistryDict)
    displayKey = [ioRegistryDict objectForKey:@"IODisplayPrefsKey"];

This works well, except in 10.9, CGDisplayIOServicePort is deprecated.

Given all of this, and Apple's advice not to cache NSScreens (which don't really work for this purpose anyway), what is the best way to reliably identify screens so that I can tell (for example) the difference between a screen at home and one at work?

I don't want to have to rely on screen resolution because the user changing resolution should not be considered a different display. Nor should the same screen on different GPUs be considered different.

A secondary goal is to find a way that given a CGDirectDisplayID, how can I determine what the CGDirectDisplayID would be for the same screen if a GPU switch were to occur? This would at least allow me to track displays by CGDirectDisplayID as long as I could match the two results from the two GPU controllers.

Community
  • 1
  • 1
Trygve
  • 1,317
  • 10
  • 27
  • I'm not exactly sure what properties of a display you want to affect your notion of "same" vs. "different", but you should look at the Quartz Display Services functions such as `CGDisplayModelNumber()`, `CGDisplaySerialNumber()`, `CGDisplayUnitNumber()`, and `CGDisplayVendorNumber()`. – Ken Thomases Jun 22 '14 at 05:24
  • The Quartz Reference states that "When assigning a display ID, Quartz considers the following parameters:Vendor, Model, Serial Number and Position in the I/O Kit registry" When I run my MBP on the integrated GPU I get: displayID:2077805631. On the fast GPU (AMD Radeon) I get a displayID: 69678016. In both cases the vendor, model, s/n and unit are the same: 1552, 40143, 0, 0. So what else is Apple doing that is causing these IDs to be different? – Trygve Jun 22 '14 at 13:22
  • I don't know. I doubt that list was intended to be exhaustive. But why do you care? I thought you were looking for a way to identify a display that *didn't* change with the GPU. – Ken Thomases Jun 22 '14 at 17:16
  • It is true that this may work for the first issue. But for the second, I am looking for a way to determine that 69678016 and 2077805631 refer to the same screen… I'd like to be able to derive one of them from the other. This is because Apple uses the DisplayID to determine the desktop and spaces, but Apple will only use one of them (what I call the primary ID) even if the GPU has dictated that the "secondary ID" is what gets returned from any inquiries from NSScreen. – Trygve Jun 22 '14 at 18:02
  • I had another thought (although I'm still not sure I understand what you're looking for). Try using `CGDisplayIDToOpenGLDisplayMask()` with the two display IDs. They may map to the same bit in the OpenGL display mask. You could also try round-tripping back through `CGOpenGLDisplayMaskToDisplayID()` to see if that "normalizes" to a single display ID. – Ken Thomases Jul 14 '14 at 13:06

1 Answers1

4

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.

H. Al-Amri
  • 179
  • 2
  • 8
  • Calling `CGDisplayCreateUUIDFromDisplayID` during a display callback where the display is being removed does not work as this returned "invalid display" even during the initial part where `kCGDisplayBeginConfigurationFlag` is set. During this time one can get all `NSScreens` and one of them will have the displayID in question. `NSScreen` has a private ivar called `_UUIDString` that is correct. How is `NSScreen `getting this when `CGDisplayCreateUUIDFromDisplayID` can't? – Trygve Jul 26 '21 at 20:42