title: How to resolve CGDirectDisplayID changing issues on newer multi-GPU Apple laptops in Core Foundation/IO Kit?
body: <p>In Mac OS X, every display gets a unique <code>CGDirectDisplayID</code> number assigned to it.  You can use <code>CGGetActiveDisplayList(</code>) or <code>[NSScreen screens]</code> to access them, among others.  Per <a href="http://CGDisplayRegisterReconfigurationCallback" rel="noreferrer">Apple's docs</a>:</p>
  
  <blockquote>
    <p>A display ID can persist across
    processes and system reboot, and
    typically remains constant as long as
    certain display parameters do not
    change.</p>
  </blockquote>
  
  <p>On newer mid-2010 MacBook Pro's, Apple started using auto-switching Intel/nVidia graphics.  Laptops have two GPU's, a low-powered Intel, and a high-powered nVidia.  Previous dual-GPU laptops (2009 models) didn't have auto-GPU switching, and required the user to make a settings change, logoff, and then logon again to make a GPU switch occur.  Even older systems only had one GPU.</p>
  
  <p>There's an issue with the mid-2010 models where CGDirectDisplayID's don't remain the same when a display switches from one GPU to the next.  For example:</p>
  
  <ol>
  <li>Laptop powers on.</li>
  <li>Built-In <strong>LCD
  Screen</strong> is driven by Intel chipset. 
  Display ID:  <strong>30002</strong></li>
  <li><strong>External
  Display</strong> is plugged in.</li>
  <li>Built-In
  <strong>LCD Screen</strong> switches to nVidia
  chipset.  It's display ID changes: 
  <strong>30004</strong></li>
  <li><strong>External Display</strong> is driven
  by nVidia chipset.</li>
  <li>...at this point,
  the Intel chipset is dormant...</li>
  <li>User unplugs <strong>External Display</strong>.</li>
  <li>Built-In <strong>LCD Screen</strong> switches back to
  Intel chipset.  It's display ID
  changes back to original: <strong>30002</strong></li>
  </ol>
  
  <p><strong>My question is, how can I match an old display ID to a new display ID when they alter due to a GPU change?</strong></p>
  
  <hr>
  
  <p><strong>Thought about:</strong></p>
  
  <p>I've noticed that the display ID only changes by 2, but I don't have enough test Mac's available to determine if this is common to all new MacBook Pro's, or just mine.  Kind of a kludge if "just check for display ID's which are +/-2 from one another" works, anyway.</p>
  
  <hr>
  
  <p><strong>Tried:</strong></p>
  
  <p><code>CGDisplayRegisterReconfigurationCallback()</code>, which notifies before-and-after when displays are going to change, has no matching logic.  Putting something like this inside a method registered with it doesn't work:</p>
  
  <pre><code>// Run before display settings change:
  CGDirectDisplayID directDisplayID = ...;
  io_service_t    servicePort = CGDisplayIOServicePort(directDisplayID);
  CFDictionaryRef oldInfoDict = IODisplayCreateInfoDictionary(servicePort, kIODisplayMatchingInfo);
  
  // ...display settings change...
  
  // Run after display settings change:
  CGDirectDisplayID directDisplayID = ...;
  io_service_t    servicePort = CGDisplayIOServicePort(directDisplayID);
  CFDictionaryRef newInfoDict = IODisplayCreateInfoDictionary(servicePort, kIODisplayMatchingInfo);
  BOOL match = IODisplayMatchDictionaries(oldInfoDict, newInfoDict, 0);
  
  if (match)
      NSLog(@"Displays are a match");
  else
      NSLog(@"Displays are not a match");
  </code></pre>
  
  <p>What's happening above is:</p>
  
  <ol>
  <li>I'm caching <strong>oldInfoDict</strong> before display settings change.</li>
  <li>Waiting for display settings to change</li>
  <li>Then comparing <strong>oldInfoDict</strong> to <strong>newInfoDict</strong> by using <code>IODisplayMatchDictionaries()</code></li>
  <li><code>IODisplayMatchDictionaries()</code> returns a BOOL, either YES they're the same, or NO they're different.</li>
  </ol>
  
  <p>Unfortunately, <code>IODisplayMatchDictionaries()</code> doesn't return YES if the same display changed GPU's.  Here's an example of the dictionary's it's comparing (look at the <code>IODisplayLocation</code> key):</p>
  
  <pre><code>// oldInfoDict  (Display ID: 30002)
  oldInfoDict: {
      DisplayProductID = 40144;
      DisplayVendorID = 1552;
      IODisplayLocation = "IOService:/AppleACPIPlatformExpert/PCI0@0/AppleACPIPCI/IGPU@2/AppleIntelFramebuffer/display0/AppleBacklightDisplay";
  }
  
  // newInfoDict  (Display ID: 30004)
  newInfoDict: {
      DisplayProductID = 40144;
      DisplayVendorID = 1552;
      IODisplayLocation = "IOService:/AppleACPIPlatformExpert/PCI0@0/AppleACPIPCI/P0P2@1/IOPCI2PCIBridge/GFX0@0/NVDA,Display-A@0/NVDA/display0/AppleBacklightDisplay";
  }
  </code></pre>
  
  <p>As you can see, the <code>IODisplayLocation</code> key changes when GPU's are switched, hence <code>IODisplayMatchDictionaries()</code> doesn't work.</p>
  
  <p>I can, theoretically, compared just the <code>DisplayProductID</code> and <code>DisplayVendorID</code> keys, but I'm writing end-user software, and am worried of a situation where users have two or more identical monitors plugged in (meaning they'll both have the same DisplayProductID/DisplayVendorID).  In other words, it's a less-than-perfect solution open to potential glitches.</p>
  
  <hr>
  
  <p>Any help is greatly appreciated!  :)</p>