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

macOS SwiftUI应用如何拦截电源键按压事件?已实现键盘拦截

拦截MacBook电源键事件的解决方案

你的问题核心在于电源键属于系统级HID(人机接口设备)事件,CGEventTap仅能捕获常规键盘/鼠标输入事件,无法触及这类底层硬件事件。要实现电源键拦截,需要借助IOKit框架直接与HID设备交互。

实现步骤

1. 添加IOKit框架依赖

在Xcode项目中,进入「Build Phases」>「Link Binary With Libraries」,添加IOKit.framework。

2. 实现HID电源键监视器

在你的EventMonitor类中添加以下代码,用于捕获电源键事件并阻止系统响应:

import IOKit.hid

private var hidManager: IOHIDManager?

private func startPowerKeyMonitoring() {
    hidManager = IOHIDManagerCreate(kCFAllocatorDefault, IOOptionBits(kIOHIDOptionsTypeNone))
    
    // 匹配MacBook电源键对应的HID设备
    let matchingDict: [CFString: Any] = [
        kIOHIDDeviceUsagePageKey: kHIDPage_GenericDesktop,
        kIOHIDDeviceUsageKey: kHIDUsage_GD_SystemPowerDown
    ]
    IOHIDManagerSetDeviceMatching(hidManager!, matchingDict as CFDictionary)
    
    // 注册设备连接回调
    IOHIDManagerRegisterDeviceMatchingCallback(hidManager!, { manager, _, sender in
        let devices = IOHIDManagerCopyDevices(manager).takeRetainedValue() as [IOHIDDevice]
        guard let powerKeyDevice = devices.first else { return }
        
        // 注册电源键事件回调
        IOHIDDeviceRegisterInputValueCallback(powerKeyDevice, { _, context, _, _, value in
            guard let event = value?.pointee else { return }
            // 取消事件,阻止系统处理睡眠/关机逻辑
            IOHIDEventSetCanceled(event, true)
            
            // 结合你的监控状态判断是否拦截
            guard let selfRef = Unmanaged<EventMonitor>.fromOpaque(context!).takeUnretainedValue() else { return }
            if selfRef.isMonitoring {
                // 电源键已被拦截,可添加自定义处理逻辑
            }
        }, Unmanaged.passUnretained(self).toOpaque())
        
        // 打开设备以接收事件
        IOHIDDeviceOpen(powerKeyDevice, IOOptionBits(kIOHIDOptionsTypeNone))
    }, Unmanaged.passUnretained(self).toOpaque())
    
    // 将HID管理器绑定到当前RunLoop
    IOHIDManagerScheduleWithRunLoop(hidManager!, CFRunLoopGetCurrent(), CFRunLoopMode.defaultMode.rawValue)
    IOHIDManagerOpen(hidManager!, IOOptionBits(kIOHIDOptionsTypeNone))
}

private func stopPowerKeyMonitoring() {
    guard let manager = hidManager else { return }
    IOHIDManagerClose(manager, IOOptionBits(kIOHIDOptionsTypeNone))
    hidManager = nil
}

3. 整合现有监控逻辑

修改你的startKeyboardMonitoring方法,同步启动电源键监控:

private func startKeyboardMonitoring() {
    // 原有的CGEventTap代码保持不变...
    
    // 启动电源键监控
    startPowerKeyMonitoring()
}

同时在停止监控的逻辑中添加电源键监视器的停止操作:

if isKeyComboPressed && !lastKeyComboState {
    DispatchQueue.main.async {
        self.isMonitoring.toggle()
        if !self.isMonitoring {
            self.stopPowerKeyMonitoring()
        } else {
            self.startPowerKeyMonitoring()
        }
    }
}

关键注意事项

  • 权限要求:应用必须获得「辅助功能」权限(系统偏好设置 > 安全性与隐私 > 隐私 > 辅助功能,勾选你的应用),否则无法拦截任何系统级事件。
  • 设备兼容性:部分Mac机型的电源键HID匹配规则可能略有差异,若上述代码无法捕获事件,可尝试将kHIDUsage_GD_SystemPowerDown替换为kHIDUsage_GD_SystemPower,或添加设备厂商ID过滤。
  • 沙箱限制:若应用开启了App Sandbox,需关闭沙箱功能,或向苹果申请com.apple.security.device.hid特殊权限(普通开发者较难通过)。

内容的提问来源于stack exchange,提问作者Lin Chen

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.20 08:14:54