macOS插入外接显示器后NSScreen显示器计数不更新问题
问题原因
NSScreen属于AppKit框架的上层接口,它的屏幕列表缓存更新依赖NSApplication实例的正常运行以及主RunLoop的事件分发:系统的显示器配置变更事件会通过NSApplicationDidChangeScreenParametersNotification通知给应用,AppKit收到通知后才会刷新[NSScreen screens]的返回值。你当前的程序是普通命令行程序,没有初始化NSApplication,也没有启动主RunLoop,AppKit没有运行环境接收系统事件,自然不会更新缓存。- 代码使用了无限死循环,即便初始化了
NSApplication,死循环也会完全阻塞主RunLoop,事件无法分发,缓存依旧无法更新。 - 而
CGGetOnlineDisplayList是CoreGraphics的底层接口,每次调用都会直接从内核查询最新的显示器状态,不依赖AppKit的事件机制,所以可以实时返回正确数值。
解决方法
方案1:适配AppKit运行规则(推荐)
- 初始化
NSApplication实例:在main函数入口最前面添加[NSApplication sharedApplication];,启动AppKit运行环境。 - 替换无限死循环为定时任务,让出RunLoop运行时间。可以用GCD的定时源实现,示例如下:
// 修正后的getNSScreenCoordinates实现 float getNSScreenCoordinates() { #define MAX_NUM_DISPLAYS 255 // 每500ms执行一次打印 dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_main_queue()); dispatch_source_set_timer(timer, DISPATCH_TIME_NOW, 0.5 * NSEC_PER_SEC, 0.1 * NSEC_PER_SEC); dispatch_source_set_event_handler(timer, ^{ NSArray<NSScreen *> *arr = [NSScreen screens]; NSUInteger numScreens = [arr count]; CGDirectDisplayID displays[MAX_NUM_DISPLAYS]; CGDisplayCount displayCount; CGGetOnlineDisplayList(MAX_NUM_DISPLAYS, displays, &displayCount); std::cout << "cg num displays " << displayCount << "\n"; std::cout << "numscreens " << numScreens << "\n"; }); dispatch_resume(timer); // 启动主RunLoop保证程序不退出 [[NSRunLoop mainRunLoop] run]; return 1; }
方案2:监听显示器变更通知(更高效)
不需要轮询,直接监听系统的显示器配置变更通知,收到通知后再获取最新的屏幕列表:
float getNSScreenCoordinates() { #define MAX_NUM_DISPLAYS 255 [NSApplication sharedApplication]; // 监听显示器变更通知 [[NSNotificationCenter defaultCenter] addObserverForName:NSApplicationDidChangeScreenParametersNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification * _Nonnull note) { NSArray<NSScreen *> *arr = [NSScreen screens]; NSUInteger numScreens = [arr count]; CGDirectDisplayID displays[MAX_NUM_DISPLAYS]; CGDisplayCount displayCount; CGGetOnlineDisplayList(MAX_NUM_DISPLAYS, displays, &displayCount); std::cout << "=== 显示器配置变更 ===" << "\n"; std::cout << "cg num displays " << displayCount << "\n"; std::cout << "numscreens " << numScreens << "\n"; }]; [[NSRunLoop mainRunLoop] run]; return 1; }
内容的提问来源于stack exchange,提问作者Pradeep Chakravarti
相关产品推荐
相关产品推荐

