Я пытаюсь использовать 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) с идентификаторами использования для отдельных лиц. ключи. Может, это как-то связано?

9
dostende 25 Авг 2011 в 17:04

2 ответа

Лучший ответ

Я понял. Способ сделать это - использовать Generic Desktop Page (0x01) Keyboard (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)».

11
dostende 25 Авг 2011 в 21:13

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

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

5
Yevgeniy 28 Авг 2011 в 10:43