【问题标题】:Using IOHIDManager to Get Modifier Key Events使用 IOHIDManager 获取修饰符键事件
【发布时间】: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)对于各个键。也许这与它有关?

【问题讨论】:

    标签: macos keyboard core-foundation iokit


    【解决方案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 有一定的经验,那么您知道有什么好的参考资料可以解决这些问题吗?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-03-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多