Использование IOHIDManager для получения событий ключа модификатора
Я пытаюсь использовать 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 ответа
Я понял. Способ сделать это - использовать Keyboard (06x) Generic Desktop Page (0) (и 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)".
Спасибо за ответ на ваш вопрос.
вместо оператора 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, но в итоге вы получите более чистый обратный вызов.