macOS下如何向指定窗口发送Command+鼠标拖拽事件?
macOS下如何向指定窗口发送Command+鼠标拖拽事件?
我特别理解你现在的困扰——当多个窗口重叠在同一位置时,只想拖动你关心的那个窗口,用NSEvent和CGEvent尝试发送Command+拖拽事件却始终没效果,不管是用post(tap: .cghidEventTap)还是postToPid都不行对吧?
先梳理下你现有代码里可能存在的几个关键问题,再给你调整后的实现方案:
关键问题分析
- 事件时间戳无效:你用了
TimeInterval()(也就是0)作为事件时间戳,系统可能会认为这是过期或无效事件,直接忽略。 - 坐标系不匹配:
NSEvent.mouseEvent的location参数是相对于目标窗口的坐标,而你传入的是全局坐标,这会导致事件发送的位置不对。 - 窗口绑定不够精准:仅设置
mouseEventWindowUnderMousePointer这类字段可能不够,需要确保事件明确绑定到目标窗口所属的进程PID,同时正确设置事件源。
调整后的实现代码
import Cocoa // 目标窗口编号(替换为你实际需要的窗口号) let targetWindowNumber: CGWindowID = 20 // 1. 获取目标窗口对应的PID guard let targetPid = CGWindowListCopyWindowInfo([.optionOnScreenOnly], kCGNullWindowID)?.compactMap { info -> pid_t? in let windowID = info[kCGWindowNumber] as! CGWindowID if windowID == targetWindowNumber { return info[kCGWindowOwnerPID] as? pid_t } return nil }.first else { print("找不到目标窗口对应的进程PID") return } // 全局起始位置和目标位置(根据你的需求修改) let globalStartPos = CGPoint(x: 10, y: 10) let globalEndPos = CGPoint(x: 20, y: 20) // 2. 将全局坐标转换为目标窗口的局部坐标 guard let targetWindow = NSWindow.windowNumber(targetWindowNumber) else { print("无法获取目标窗口实例") return } let localStartPos = targetWindow.convertFromScreen(NSRect(origin: globalStartPos, size: .zero)).origin let localEndPos = targetWindow.convertFromScreen(NSRect(origin: globalEndPos, size: .zero)).origin // 3. 发送Command+鼠标按下事件 let downNSEvent = NSEvent.mouseEvent( with: .leftMouseDown, location: localStartPos, modifierFlags: .command, timestamp: Date.timeIntervalSinceReferenceDate, // 使用当前有效时间戳 windowNumber: Int(targetWindowNumber), context: nil, eventNumber: 0, clickCount: 1, pressure: 1.0 ) if let downCGEvent = downNSEvent?.cgEvent { downCGEvent.flags = .maskCommand downCGEvent.postToPid(targetPid) usleep(100_000) // 适当延迟,让系统处理完当前事件 } // 4. 发送鼠标拖拽事件 let dragNSEvent = NSEvent.mouseEvent( with: .leftMouseDragged, location: localEndPos, modifierFlags: .command, timestamp: Date.timeIntervalSinceReferenceDate, windowNumber: Int(targetWindowNumber), context: nil, eventNumber: 1, clickCount: 1, pressure: 1.0 ) if let dragCGEvent = dragNSEvent?.cgEvent { dragCGEvent.flags = .maskCommand dragCGEvent.postToPid(targetPid) usleep(100_000) } // 5. 发送鼠标抬起事件 let upNSEvent = NSEvent.mouseEvent( with: .leftMouseUp, location: localEndPos, modifierFlags: .command, timestamp: Date.timeIntervalSinceReferenceDate, windowNumber: Int(targetWindowNumber), context: nil, eventNumber: 2, clickCount: 1, pressure: 1.0 ) if let upCGEvent = upNSEvent?.cgEvent { upCGEvent.flags = .maskCommand upCGEvent.postToPid(targetPid) }
额外注意事项
- 权限问题:如果你的应用是沙盒应用,需要在系统设置的「隐私与安全性」-「输入监控」中添加你的应用,否则无法发送跨进程的事件。
- 坐标微调:如果目标窗口是带标题栏的窗口,可能需要对转换后的局部坐标进行微调(比如加上标题栏高度),确保点击位置落在可拖动区域。
- 延迟调整:不要设置过长的
usleep时间,过长的延迟可能导致事件序列被系统打断,100ms左右的延迟足够让系统处理完前一个事件。
备注:内容来源于stack exchange,提问作者chunyang.wen
相关产品推荐
相关产品推荐

