Использование IOHIDManager для получения событий ключа модификатора - PullRequest
8 голосов
/ 25 августа 2011

Я пытаюсь использовать 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, поэтому я не знал, следует ли использовать универсальную страницу рабочего стола (0x01) с идентификатором использования клавиатуры (06) или страницу клавиатуры / клавиатуры (0x07) с идентификаторами использования для отдельных ключей. Может быть, это как-то связано с этим?

Ответы [ 2 ]

10 голосов
/ 26 августа 2011

Я понял это. Способ сделать это - использовать клавиатуру (0x01) Generic Desktop Page (06) (и Keypad (07) для полноты) для использования с IOHIDManagerSetDeviceMatchingMultiple, а затем обратный вызов входного значения получает содержимое Keyboard / Keypad Usage Page (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 ...
}

Обратите внимание, что обратный вызов, кажется, вызывается несколько раз за одно нажатие или выпуск, но с идентификаторами использования, выходящими за пределы нормального диапазона, для чего и используется "if (scancode <4 || scancode> 231)".

4 голосов
/ 28 августа 2011

спасибо за ответ на ваш вопрос.

вместо оператора 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, но в итоге вы получите более чистый обратный вызов.

...