使用IOHIDManager无法在部分macOS设备检测触控板事件
macOS 检测鼠标/触控板事件与设备详情的问题
问题概述
需要在macOS上检测鼠标事件并获取对应设备的详细信息(如厂商ID、产品ID)。当前基于IOHIDManager的代码能检测外接鼠标事件,但无法识别内置触控板事件,且该问题仅出现在部分新机型的Sonoma系统上。
设备兼容性情况
代码正常运行的设备
- Intel MacBook Pro(Ventura系统)
- M1 MacBook Pro(Ventura系统)
- M1 MacBook Pro(Sonoma 14.3系统)
代码失效的设备
- M3 MacBook Pro(Sonoma 14.6系统)
- M2 MacBook Air(Sonoma 14.4系统)
现有IOHIDManager代码及问题
以下代码仅能捕获外接鼠标事件,无法响应内置触控板操作:
#include <iostream> #include <IOKit/hid/IOHIDManager.h> #include <CoreFoundation/CoreFoundation.h> void mouseEventCallback(void *context, IOReturn result, void *sender, IOHIDValueRef value) { std::cout << "Mouse event" << std::endl; // 仅打印外接鼠标事件,内置触控板无响应 } int main() { IOHIDManagerRef manager = IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDOptionsTypeNone); // 创建鼠标和触控板的匹配字典 CFMutableDictionaryRef matchingDict = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); // 添加匹配条件:通用桌面控制页,鼠标(2)和触控板(5) CFDictionaryAddValue(matchingDict, CFSTR(kIOHIDDeviceUsagePageKey), CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, (const int[]){1})); CFDictionaryAddValue(matchingDict, CFSTR(kIOHIDDeviceUsageKey), CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, (const int[]){2, 5})); // 设置匹配规则 IOHIDManagerSetDeviceMatching(manager, matchingDict); // 注册输入值变化回调 IOHIDManagerRegisterInputValueCallback(manager, mouseEventCallback, NULL); // 打开HID管理器 IOHIDManagerOpen(manager, kIOHIDOptionsTypeNone); // 将HID管理器加入当前运行循环 IOHIDManagerScheduleWithRunLoop(manager, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode); // 启动循环监听事件 CFRunLoopRun(); // 资源清理 CFRelease(matchingDict); CFRelease(manager); return 0; }
编译命令:
clang++ -o mouse mouse.cpp -framework IOKit -framework CoreFoundation
尝试过的其他方案
- manymouse库:问题依旧,无法捕获内置触控板事件。
- IOHIDManagerRegisterDeviceMatchingCallback:能识别外接鼠标和触控板设备,但无法捕获触控板的事件。
- CGEventTapCreate:可在所有设备上检测到触控板和外接鼠标的事件,但无法获取事件对应的设备详情(如厂商ID、产品ID),代码如下:
// 此代码可捕获所有鼠标/触控板事件,但无法获取设备信息 #include <iostream> #include <ApplicationServices/ApplicationServices.h> // 事件回调函数 CGEventRef eventTapCallback(CGEventTapProxy proxy, CGEventType type, CGEventRef event, void* userInfo) { if (type == kCGEventLeftMouseDown || type == kCGEventRightMouseDown || type == kCGEventOtherMouseDown) { CGPoint mouseLocation = CGEventGetLocation(event); std::cout << "鼠标点击位置: (" << mouseLocation.x << ", " << mouseLocation.y << ")" << std::endl; } else if (type == kCGEventScrollWheel) { // 滚轮事件 CGPoint scrollDelta = CGEventGetUnflippedLocation(event); std::cout << "鼠标滚动位置: (" << scrollDelta.x << ", " << scrollDelta.y << ")" << std::endl; } return event; } int main() { // 创建事件监听掩码,包含点击和滚轮事件 CGEventMask eventMask = CGEventMaskBit(kCGEventLeftMouseDown) | CGEventMaskBit(kCGEventRightMouseDown) | CGEventMaskBit(kCGEventOtherMouseDown) | CGEventMaskBit(kCGEventScrollWheel); CFMachPortRef eventTap = CGEventTapCreate(kCGSessionEventTap, kCGHeadInsertEventTap, kCGEventTapOptionListenOnly, eventMask, eventTapCallback, nullptr); if (!eventTap) { std::cerr << "创建事件监听失败。" << std::endl; return 1; } // 创建运行循环源并加入当前循环 CFRunLoopSourceRef runLoopSource = CFMachPortCreateRunLoopSource(kCFAllocatorDefault, eventTap, 0); CFRunLoopAddSource(CFRunLoopGetCurrent(), runLoopSource, kCFRunLoopCommonModes); CFRelease(runLoopSource); // 启用事件监听 CGEventTapEnable(eventTap, true); // 启动运行循环处理事件 CFRunLoopRun(); return 0; }
解决思路
针对新机型Sonoma系统上内置触控板无法被IOHIDManager捕获的问题,可尝试以下方向:
- 调整HID设备匹配规则:Sonoma对内置触控板的HID报告可能有变更,尝试单独匹配触控板的Usage Page/Usage,或添加额外的匹配条件(如设备类型、厂商ID)。
- 设备枚举与事件监听结合:先通过
IOHIDManagerRegisterDeviceMatchingCallback枚举到内置触控板设备,再针对该设备单独注册输入回调,而非全局注册。 - 检查权限设置:确保应用已获得“输入监控”权限(系统设置-隐私与安全性-输入监控),新系统对权限的管控更严格,可能导致内置设备事件无法被捕获。
- CGEvent与IOHID信息关联:用CGEventTap捕获事件,同时提前枚举所有HID设备并缓存其信息,尝试通过事件属性关联到对应的设备。
内容的提问来源于stack exchange,提问作者Himaprasoon
相关产品推荐
相关产品推荐

