You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.23 11:29:11