macOS模拟事件CGEvent
macOS模拟事件CGEvent

macOS模拟事件CGEvent

CGEvent 是 macOS 中 Core Graphics 框架提供的一个结构体,用于表示一个低层级的输入事件,比如键盘、鼠标、滚轮等事件。它既可以表示系统中捕捉到的真实用户事件,也可以用于创建和发送模拟事件(synthetic event),广泛用于辅助功能、自动化控制、截图软件等场景。

常见用途

1、创建虚拟键盘事件(例如自动输入);

2、读取和修改事件的具体属性(如位置、键码、按钮等);

3、获取当前系统或用户的真实事件数据;

4、监听/拦截事件(配合 tap,比如 CGEventTapCreate)。

构造方法

1、init(mouseEventSource:mouseType: mouseCursorPosition: mouseButton:)

init?(mouseEventSource: CGEventSource?, mouseType: CGEventType, mouseCursorPosition: CGPoint, mouseButton: CGMouseButton)

创建鼠标事件:

1)mouseType: .leftMouseDown、.leftMouseUp、.mouseMoved 等;

2)mouseCursorPosition: 屏幕坐标(0,0)在左下角;

3)mouseButton: .left、.right、.center。

2、init(keyboardEventSource:virtualKey:keyDown:)

init?(keyboardEventSource: CGEventSource?, virtualKey: CGKeyCode, keyDown: Bool)

创建键盘事件:

virtualKey: 键码(如 0 是 A);

keyDown: true 表示按下,false 表示松开。

3、init(tap:type:event:)

init?(tap: CGEventTapLocation, type: CGEventType, event: CGEvent?)

拷贝一个已有事件(较少使用)。

4、init?(scrollWheelEvent2Source:unit:wheelCount:…)

init?(scrollWheelEvent2Source source: CGEventSource?, 
      unit: CGScrollEventUnit, 
      wheelCount: Int32, 
      wheel1: Int32, 
      wheel2: Int32, 
      wheel3: Int32)

1)source:CGEventSource?类型,事件源,通常使用 .hidSystemState 创建;

2)unit:CGScrollEventUnit类型,滚动单位,分为像素(.pixel)或行(.line);

3)wheelCount:Int32类型,滚轮数量(1~3),对应后续的 wheel1/2/3;

4)wheel1:Int32类型,主垂直滚轮(正数表示向上,负数表示向下);

5)wheel2:Int32类型,水平滚轮(正数表示右滚,负数表示左滚);

6)wheel3:Int32类型,第三维度滚动,一般不使用,设为 0。

常用属性

1、type: 获取事件类型(CGEventType);

2、location: 获取事件的位置(CGPoint);

3、timestamp: 时间戳(CGEventTimestamp);

4、flags: 获取按键修饰符(如 .maskCommand, .maskShift 等)。

常用方法

1、func post(tap: CGEventTapLocation):将事件“发送/注入”到系统事件流中(例如模拟鼠标点击);

2、func setIntegerValueField(_ field: CGEventField, value: Int64):设置整数类型的字段(如鼠标按钮编号、滚动值、键码等);

3、func integerValueField(_ field: CGEventField) -> Int64:获取事件中的某个整数值;

4、func setDoubleValueField(_ field: CGEventField, value: Double):设置双精度字段(通常用于压感、鼠标速度等);

5、func doubleValueField(_ field: CGEventField) -> Double:获取 double 类型的字段值;

6、func setFlags(_ flags: CGEventFlags):设置事件的修饰符(Command、Shift 等);

7、func flags() -> CGEventFlags:获取事件包含的修饰符;

8、func setType(_ type: CGEventType):设置事件类型,例如 .leftMouseDown;

9、func type() -> CGEventType:获取事件类型;

10、func setLocation(_ point: CGPoint):设置事件的位置;

11、func location() -> CGPoint:获取事件的位置;

12、func setUnicodeString(…):设置输入文本内容(用于文本键入)。

使用示例:

if let event = CGEvent(mouseEventSource: nil,
                       mouseType: .leftMouseDown,
                       mouseCursorPosition: CGPoint(x: 100, y: 100),
                       mouseButton: .left) {
    
    event.setType(.leftMouseDown)
    event.setLocation(CGPoint(x: 200, y: 200))
    event.setFlags(.maskCommand) // 按下了 Command 键时的事件
    event.post(tap: .cghidEventTap)
}

CGEventType

这是表示事件类型的枚举,例如鼠标点击、移动、键盘事件等。

1、.mouseMoved:鼠标移动;

2、.leftMouseDown:左键按下;

3、.leftMouseUp:左键抬起;

4、.rightMouseDown:右键按下;

5、.rightMouseUp:右键抬起;

6、.otherMouseDown:其他按钮按下(如中键);

7、.otherMouseUp:其他按钮抬起;

8、.scrollWheel:鼠标滚轮滚动;

9、.mouseDragged:拖动(左键按下后移动);

10、.rightMouseDragged:右键拖动;

11、.otherMouseDragged:其他键拖动。、

这类值用于指定事件的类型,例如:

let event = CGEvent(mouseEventSource: nil, mouseType: .leftMouseDown, mouseCursorPosition: point, mouseButton: .left)

CGMouseButton

这是表示具体使用了哪一个鼠标按钮的枚举,和 mouseType 组合使用。

.left (0):左键;

.right (1):右键;

.center (2):中键或滚轮键。

例如:

let event = CGEvent(mouseEventSource: nil,
    mouseType: .otherMouseDown,
    mouseCursorPosition: CGPoint(x: 100, y: 100),
    mouseButton: .center)

搭配 CGEventSource 使用

CGEventSource 是 macOS 中 Core Graphics 框架(CoreGraphics.framework)中的一个结构体类型,代表一个事件源(event source),主要用于创建和发送“合成的输入事件”(如模拟键盘按键、鼠标移动/点击等)。

let eventSource = CGEventSource(stateID: .hidSystemState)

.hidSystemState:表示来源于人类输入设备(Human Interface Device),用于模拟真实用户行为;

.combinedSessionState:用于模拟多个用户会话共享的事件。

构造模拟事件时通常提供一个 CGEventSource:

let src = CGEventSource(stateID: .hidSystemState)

let mouseDown = CGEvent(mouseEventSource: src, mouseType: .leftMouseDown, mouseCursorPosition: pos, mouseButton: .left)
let mouseUp = CGEvent(mouseEventSource: src, mouseType: .leftMouseUp, mouseCursorPosition: pos, mouseButton: .left)

使用场景

1、模拟鼠标点击

let source = CGEventSource(stateID: .hidSystemState)!

let point = CGPoint(x: 300, y: 300)
let mouseDown = CGEvent(mouseEventSource: source, mouseType: .leftMouseDown, mouseCursorPosition: point, mouseButton: .left)
let mouseUp = CGEvent(mouseEventSource: source, mouseType: .leftMouseUp, mouseCursorPosition: point, mouseButton: .left)

mouseDown?.post(tap: .cghidEventTap)
mouseUp?.post(tap: .cghidEventTap)

2、模拟按键输入

let source = CGEventSource(stateID: .hidSystemState)!
let keyDown = CGEvent(keyboardEventSource: source, virtualKey: 0x0, keyDown: true)  // 'A' 键
let keyUp = CGEvent(keyboardEventSource: source, virtualKey: 0x0, keyDown: false)

keyDown?.post(tap: .cghidEventTap)
keyUp?.post(tap: .cghidEventTap)

3、模拟滚轮向下滚动3行

let source = CGEventSource(stateID: .hidSystemState)
let scrollEvent = CGEvent(scrollWheelEvent2Source: source,
                          unit: .line,
                          wheelCount: 1,
                          wheel1: -3,
                          wheel2: 0,
                          wheel3: 0)

scrollEvent?.post(tap: .cghidEventTap)

事件发送方法

CGEvent的post方法用于将事件发送给系统。

func post(tap: .cghidEventTap)

其中参数 tap 指的是事件注入位置:

.cghidEventTap: 直接注入到系统硬件事件流中(更自然、推荐)。

.cgSessionEventTap: 注入到当前用户 session(某些权限较低的操作可能用它)。

.cgAnnotatedSessionEventTap: 用于辅助功能系统(通常不使用)。

示例:模拟点击

mouseDown?.post(tap: .cghidEventTap)
mouseUp?.post(tap: .cghidEventTap)

意思是:先模拟鼠标按下,再模拟松开,从而完成一次点击。

注意事项

所有 CGEvent 模拟的输入事件默认没有 UI 权限限制,但部分应用(沙盒内或需要辅助功能权限)可能拦截失败;

如果开发的是沙盒应用(App Sandbox 开启),必须请求辅助功能权限(Accessibility);

post(tap:) 并不会触发自己代码内部的 NSEvent.addGlobalMonitor 监听器。

总结

CGEvent可以模拟鼠标、键盘和滚轮的事件,不能用来监听用户输入。

在大多数情况下 CGEventType 与 CGMouseButton 是一一对应的(比如 .leftMouseDown 对应 .left)。

对于 CGEventType.otherMouseDown / otherMouseDragged / otherMouseUp,必须通过 CGMouseButton 告诉系统是哪个「非左/右」键触发了事件(中键、侧键等等)。

   

如果您认为这篇文章给您带来了帮助,您可以在此通过支付宝或者微信打赏网站开发者。

欢迎加入我们的 微信交流群QQ交流群,交流更多精彩内容!
微信交流群二维码 QQ交流群二维码

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注