使用 IOHIDManager 获取修饰符键事件
Posted
技术标签:
【中文标题】使用 IOHIDManager 获取修饰符键事件【英文标题】:Using IOHIDManager to Get Modifier Key Events 【发布时间】:2011-11-03 16:29:13 【问题描述】:我正在尝试使用 IOHIDManager 获取修饰键事件,因为缺少 Cocoa flagsChanged 事件(很难区分按下/释放、左/右是否都按下等)这是我创建管理器的代码和注册回调。
IOHIDManagerRef hidManager = IOHIDManagerCreate(kCFAllocatorDefault,
kIOHIDOptionsTypeNone);
if (CFGetTypeID(hidManager) != IOHIDManagerGetTypeID())
return 1;
CFMutableDictionaryRef capsLock =
myCreateDeviceMatchingDictionary(0x07, 0x39);
CFMutableDictionaryRef lctrl =
myCreateDeviceMatchingDictionary(0x07, 0xE0);
CFMutableDictionaryRef lshift =
myCreateDeviceMatchingDictionary(0x07, 0xE1);
CFMutableDictionaryRef lalt =
myCreateDeviceMatchingDictionary(0x07, 0xE2);
CFMutableDictionaryRef lsuper =
myCreateDeviceMatchingDictionary(0x07, 0xE3);
CFMutableDictionaryRef rctrl =
myCreateDeviceMatchingDictionary(0x07, 0xE4);
CFMutableDictionaryRef rshift =
myCreateDeviceMatchingDictionary(0x07, 0xE5);
CFMutableDictionaryRef ralt =
myCreateDeviceMatchingDictionary(0x07, 0xE6);
CFMutableDictionaryRef rsuper =
myCreateDeviceMatchingDictionary(0x07, 0xE7);
CFMutableDictionaryRef matchesList[] =
capsLock,
lctrl,
lshift,
lalt,
lsuper,
rctrl,
rshift,
ralt,
rsuper
;
CFArrayRef matches = CFArrayCreate(kCFAllocatorDefault,
(const void **)matchesList, 9, NULL);
IOHIDManagerSetDeviceMatchingMultiple(hidManager, matches);
IOHIDManagerRegisterInputValueCallback(hidManager,
myHandleModifiersCallback, NULL);
IOHIDManagerScheduleWithRunLoop(hidManager, CFRunLoopGetMain(),
kCFRunLoopDefaultMode);
IOHIDManagerOpen(hidManager, kIOHIDOptionsTypeNone);
但是,回调永远不会运行。我错过了什么吗?
我不完全了解 HID 使用页面,所以我不知道是使用带有键盘使用 ID (06) 的通用桌面页面 (0x01) 还是带有使用 ID 的键盘/键盘页面 (0x07)对于各个键。也许这与它有关?
【问题讨论】:
【参考方案1】:我想通了。这样做的方法是使用通用桌面页面 (0x01) 键盘 (06)(和小键盘 (07) 以确保完整性)与 IOHIDManagerSetDeviceMatchingMultiple 一起使用,然后输入值回调获取键盘/小键盘使用页面 (0x07) 的东西。
例如,要为所有键盘/小键盘设置一个 HIDManager,可以执行以下操作:
IOHIDManagerRef hidManager = IOHIDManagerCreate(kCFAllocatorDefault,
kIOHIDOptionsTypeNone);
CFMutableDictionaryRef keyboard =
myCreateDeviceMatchingDictionary(0x01, 6);
CFMutableDictionaryRef keypad =
myCreateDeviceMatchingDictionary(0x01, 7);
CFMutableDictionaryRef matchesList[] =
keyboard,
keypad,
;
CFArrayRef matches = CFArrayCreate(kCFAllocatorDefault,
(const void **)matchesList, 2, NULL);
IOHIDManagerSetDeviceMatchingMultiple(hidManager, matches);
IOHIDManagerRegisterInputValueCallback(hidManager,
myHIDKeyboardCallback, NULL);
IOHIDManagerScheduleWithRunLoop(hidManager, CFRunLoopGetMain(),
kCFRunLoopDefaultMode);
IOHIDManagerOpen(hidManager, kIOHIDOptionsTypeNone);
myCreateDeviceMatchingDictionary 类似于:
CFMutableDictionaryRef myCreateDeviceMatchingDictionary(UInt32 usagePage,
UInt32 usage)
CFMutableDictionaryRef ret = CFDictionaryCreateMutable(kCFAllocatorDefault,
0, &kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
if (!ret)
return NULL;
CFNumberRef pageNumberRef = CFNumberCreate(kCFAllocatorDefault,
kCFNumberIntType, &usagePage );
if (!pageNumberRef)
CFRelease(ret);
return NULL;
CFDictionarySetValue(ret, CFSTR(kIOHIDDeviceUsagePageKey), pageNumberRef);
CFRelease(pageNumberRef);
CFNumberRef usageNumberRef = CFNumberCreate(kCFAllocatorDefault,
kCFNumberIntType, &usage);
if (!usageNumberRef)
CFRelease(ret);
return NULL;
CFDictionarySetValue(ret, CFSTR(kIOHIDDeviceUsageKey), usageNumberRef);
CFRelease(usageNumberRef);
return ret;
而 myHIDKeyboardCallback 类似于:
void myHIDKeyboardCallback(void *context, IOReturn result, void *sender,
IOHIDValueRef value)
IOHIDElementRef elem = IOHIDValueGetElement(value);
if (IOHIDElementGetUsagePage(elem) != 0x07)
return;
uint32_t scancode = IOHIDElementGetUsage(elem);
if (scancode < 4 || scancode > 231)
return;
long pressed = IOHIDValueGetIntegerValue(value);
// ... Do something ...
请注意,每次按下或释放时似乎都会多次调用回调,但使用 ID 超出正常范围,这就是“if (scancode 231)”的用途。
【讨论】:
您在哪里找到有关如何解析回调中的value
以获取扫描码之类的信息的信息?你有一些(可读的)参考,或者你是怎么想出来的?【参考方案2】:
感谢您提供问题的答案。
您可以使用 IOHIDManagerSetInputValueMatching,而不是 myHIDKeyboardCallback 中检查扫描码231 的 if 语句。
// before IOHIDManagerOpen
int usageMin = 4;
CFNumberRef minNumberRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &usageMin);
CFDictionarySetValue(inputValueFilter, CFSTR(kIOHIDElementUsageMinKey), minNumberRef);
CFRelease(minNumberRef);
int usageMax = 231;
CFNumberRef maxNumberRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &usageMax);
CFDictionarySetValue(inputValueFilter, CFSTR(kIOHIDElementUsageMaxKey), maxNumberRef);
CFRelease(maxNumberRef);
IOHIDManagerSetInputValueMatching(hidManager, inputValueFilter);
它比简单的 if 语句更 LOC,但你最终会得到一个更清晰的回调。
【讨论】:
很高兴知道。我想您还可以使用 kIOHIDElementUsagePageKey 将元素使用页面限制为 0x07。不过,这有必要吗?键盘/小键盘设备是否会生成非 0x07 元素?我想如果你有一个带有内置触控板或操纵杆或其他东西的外部键盘,这是可能的。 我从来没有使用过这样的键盘(即带有操纵杆或类似的内置),但我希望第二个设备和键盘能够将它们的事件分开。但我不能确定。 你有关于如何使用 IOHID API 的好的资料吗? (我主要对阅读鼠标/键盘输入感兴趣。)只需按照 Apple 的文档创建 IOHIDManager 并查找匹配的设备等相当简单,但如何解析发送到回调的数据则不太清楚。听起来您对 API 有一定的经验,那么您知道有什么好的参考资料可以解决这些问题吗?以上是关于使用 IOHIDManager 获取修饰符键事件的主要内容,如果未能解决你的问题,请参考以下文章