I am finding that NSScreen is returning the same number of monitors even after additional monitors are plugged in.
Made a simple test app that can replicate the issue. Basically infinite loops and prints NSScreen counts and CGDisplay counts.
- Start the app
- printing “NSScreen = 1 CGDisplay = 1”
- Without stopping the app, plug in an additional monitor
- printing “NSScreen = 1 CGDisplay = 2”
However the code should be printing “NSScreen = 2 CGDisplay = 2”
On OS X 11, we see the same issue (NSScreen = 1 CGDisplay = 2) after plugging in the additional monitor.
Test Code is here:
main.cpp
#include <iostream>
#include "macScreen.h"
int main(int argc, const char * argv[]) {
getNSScreenCoordinates();
return 0;
}
macScreen.h
#ifndef macScreen_h
#define macScreen_h
float getNSScreenCoordinates();
#endif /* macScreen_h */
macScreen.mm
#import <Foundation/Foundation.h>
#include <iostream>
#include <Cocoa/Cocoa.h>
#import <AppKit/NSScreen.h>
#define MAX_NUM_DISPLAYS 255
float getNSScreenCoordinates() {
NSArray<NSScreen *> *arr = [NSScreen screens];
NSUInteger numScreens = [arr count];
CGDirectDisplayID displays[MAX_NUM_DISPLAYS];
CGDisplayCount displayCount;
CGGetOnlineDisplayList(MAX_NUM_DISPLAYS, displays, &displayCount);
while(1) {
std::cout << "cg num displays " << displayCount << "\n";
std::cout << "numscreens " << numScreens << "\n";
arr = [NSScreen screens];
numScreens = [arr count];
CGGetOnlineDisplayList(MAX_NUM_DISPLAYS, displays, &displayCount);
}
return 1;
}
In my testing on macOS 11.6 (Big Sur), you need to do two things to get
[NSScreen screens]to update:NSApplication.sharedobject exists.NSRunLoop.Here's an example (in Swift) that works:
But if you comment out the
NSApplication.sharedline, it no longer works. If you replace the use of theRunLoopwith awhileloop that never invokes theRunLoop, it also no longer works. For example, the following does not work:So you should really try to arrange your program so that
RunLoop.mainis in control. But if you really need to have your own main loop, it suffices to run one pass throughRunLoop.mainbefore checkingNSScreen.screens. This version works: