2011-08-25 57 views
7

我想使用IOHIDManager获取修饰键事件,因为Cocoa flagsChanged事件缺乏(难以区分按下/释放,左/右,如果两者都关闭等)。代码,我创建经理并注册回调。使用IOHIDManager获取修饰键事件

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)或键盘/键盘页面(0x07)上使用通用桌面页面(0x01)各个键的ID。也许这跟它有关系?

回答

9

我想通了。要做到这一点的方法是使用通用桌面页面(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超出正常范围,这就是“如果(扫描码< 4 ||扫描码> 231)”是。

+0

你在哪里找到了关于如何解析回调中的'value'的信息,以获得像扫描码这样的东西?你有一些(可读的)参考,或者你是如何弄清楚的? – jalf

4

THX提供的回答你的问题。

代替if语句中myHIDKeyboardCallback,检查扫描码< 4或扫描码> 231你可以使用IOHIDManagerSetInputValueMatching

// 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); 

更LOC那么一个简单的if语句,但你最终有一个更清洁的回调。

+0

很高兴知道。我想你也可以使用kIOHIDElementUsagePageKey将元素使用页面限制为0x07。这是必要的,但?键盘/键盘设备是否曾生成非0x07元素?我想如果你有一个带触控板或内置游戏杆的外部键盘或其他东西,这是可能的。 – dostende

+0

nope,从来没有一个非0x07 :) – Yevgeniy

+0

我从来没有使用过这样的键盘(即用一个游戏杆或类似的内置),但我希望第二个设备和键盘的事件被分开。但我无法确定。 – Yevgeniy