I have a function in my app to detect if screens are added or removed, watching for notifications from NSApplication.didChangeScreenParametersNotification. I am seeing some strange behavior when the screen attached to a Mac mini is turned off, macOS will spit out hundreds of the didChangeScreenParametersNotification, all relating to a 'ghost' screen being added and then subsequently replaced with the original screen a second later. This cycle will go on for hours until the screen is turned back on again.
I can confirm this also happens with the CoreGraphics equivalent, with flags .added and .removed being the only changes. I would imagine this creates immense churn for all apps watching for screen changes.
I've tried debouncing the notifications but even with a delay of 10 seconds this is still being called hundreds of times while the computer is idle and the screen is off.
One constant I can see is that the CGDisplayUnitNumber() for the 'ghost' display is always 0, while the logical unit number for the real screen is '1'. Is it safe to ignore screens with 0? I'm trying to find a reliable way to prevent heavy processing for 'false' screens. I'm afraid because this ghost screen has parameters so different to the actual screen, it's otherwise not possible to ignore it as it looks like a new screen.
See example below:
// Observe notification NotificationCenter.default.addObserver(self, selector: #selector(displaysDidChange), name: NSApplication.didChangeScreenParametersNotification, object: nil) // Function to update screens called from displaysDidChange func updateScreens() { let screens = NSScreen.screens for screen in screens { guard let screenDisplayID = screen.displayID() else { NSLog("Screen does not have a display ID: \(screen.localizedName)") continue } let screenIdentifier = "v\(CGDisplayVendorNumber(screenDisplayID)), m\(CGDisplayModelNumber(screenDisplayID)), sn\(CGDisplaySerialNumber(screenDisplayID)), u\(CGDisplayUnitNumber(screenDisplayID)), sz\(CGDisplayScreenSize(screenDisplayID))" } // -- Logic to determine if screen is new or already exists for window management -- NSLog("Found new screen display ID \(screenDisplayID) (\(screenIdentifier)): \(screen.localizedName)") }
And the logging I'll get:
Found new screen display ID 2 (v16652, m1219, sn16843009, u1, sz(1434.3529196346508, 806.823517294491)): Philips FTV
Found new screen display ID 10586 (v1970170734, m1986622068, sn0, u0, sz(677.3333231608074, 380.9999942779541)):
One constant I can see is that the CGDisplayUnitNumber() for the 'ghost' display is always 0, while the logical unit number for the real screen is '1'. Is it safe to ignore screens with 0?
Yes. More specifically, the value is actually kCGNullDirectDisplay
from CGDirectDisplay.h:
#define kCGNullDirectDisplay ((CGDirectDisplayID)0) #define kCGDirectMainDisplay CGMainDisplayID()
...and means exactly what you think it does.
I'm afraid because this ghost screen has parameters so different to the actual screen, it's otherwise not possible to ignore it as it looks like a new screen.
Interestingly, after a bit of digging, the numbers are more meaningful than they look, though it is a bit obscure. Many of implementation details of this area of the system are extremely old, as they were implemented as part of MacOS X's original implementation (pre-10.0) and, as is the case here, basically "copied" from macOS classic. Sometimes that was because code was directly copied, sometimes it was simply a matter of reusing the same general approach because there wasn't really anything "wrong" with the previous approach.
In this case, one of those patterns from Classic MacOS is using encoding 4 ASCII characters as a single 32 bit value (the way type/creator codes were encoded). Applying that here gets you:
v1970170734-> 0x75 6E 6B 6E -> 'unkn' -> "Unknown" m1986622068-> 0x76 69 72 74 -> 'virt' -> "Virtual"
In a similar vein, the resolutions is almost certainly derived from MacOS X's original resolution, 640x480*. I'm not sure how that lead to the specific values you're seeing, but my guess is that it's a case of having used the same value "forever" and simply not caring/noticing** what the resolution actually was, since the system was already going to ignore the display.
*I'm not sure the system will actually still run at that resolution but I am certain it won't work very well. It didn't work very well in 2001 and I doubt the situation has improved.
**Philosophically speaking, is it possible to incorrectly draw on a screen no one ever sees?
__
Kevin Elliott
DTS Engineer, CoreOS/Hardware