多显示器场景下使用CGDisplayMoveCursor移动鼠标位置不符合预期
问题根源
AppKit(NS)和Quartz(CG)两套框架的坐标系规则不同,是导致落点偏移的核心原因:
- NS坐标系全局以主屏幕左下角为原点,y轴向上,你从
NSScreen.frame拿到的每个屏幕位置都是基于这个全局坐标系计算的 CGDisplayMoveCursorToPoint要求传入目标点在指定显示器的局部CG坐标,这类坐标系以当前屏幕左上角为原点,y轴向下
坐标转换步骤
你已经完成了目标点归属显示器的判断,只要按以下两步转换坐标即可适配接口要求:
- 把全局NS坐标转换为目标显示器的局部NS坐标:用目标点坐标减去目标显示器frame的origin值
- 翻转y轴适配CG坐标系:用显示器的高度减去上一步得到的局部NS坐标y值,得到CG局部坐标的y值
适配后的代码实现
let point = NSPoint(x: x, y: y) guard let monitor = Monitor.displayIdWithPoint(point) else { SendlogCallbackEvent("D", "", "Found nil in monitor with point", #function, #file, #line) } // 注:如果你的Monitor结构体没有关联对应NSScreen实例,可以在查找归属显示器的逻辑里同步返回匹配到的NSScreen frame let monitorFrame = monitor.screen.frame // 转换为显示器局部NS坐标 let localX = point.x - monitorFrame.origin.x let localNSY = point.y - monitorFrame.origin.y // 翻转y轴得到CG接口可识别的局部坐标 let localCGY = monitorFrame.height - localNSY let targetCGPoint = CGPoint(x: localX, y: localCGY) if CGDisplayMoveCursorToPoint(monitor.directDisplayId, targetCGPoint) == CGError.failure { SendlogCallbackEvent("D", "", "CGError.failer to move cursor", #function, #file, #line) }
更简单的可选实现方案
如果不需要强制绑定指定显示器移动,你可以跳过显示器归属判断逻辑,直接转换全局坐标后调用CGWarpMouseCursorPosition实现:
let point = NSPoint(x: x, y: y) // 计算全局NS坐标系的总高度(所有屏幕的最大y值) let globalMaxY = NSScreen.screens.map { $0.frame.maxY }.max() ?? 0 // 全局NS坐标转全局CG坐标 let globalCGY = globalMaxY - point.y let globalCGPoint = CGPoint(x: point.x, y: globalCGY) CGWarpMouseCursorPosition(globalCGPoint)
内容的提问来源于stack exchange,提问作者Gama
相关产品推荐
相关产品推荐

