Utilizzo di IOHIDManager per ottenere eventi chiave modificatori

Sto cercando di usare IOHIDManager per ottenere eventi chiave modificatore perché mancano gli eventi Cocoa flagsChanged (difficile da distinguere tra stampa / rilascio, sinistra / destra se entrambi sono giù, ecc.) Ecco il codice dove creo il gestore e registro il callback .

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

Tuttavia, il callback non viene mai eseguito. Mi sto perdendo qualcosa?

Non comprendo appieno le pagine di utilizzo HID, quindi non sapevo se utilizzare o meno Generic Desktop Page (0x01) con l’ID di utilizzo della tastiera (06) o la pagina Tastiera / tastiera (0x07) con ID di utilizzo per l’individuo chiavi. Forse ha qualcosa a che fare con questo?

L’avevo capito. Il modo per farlo è usare la tastiera Generic Desktop Page (0x01) (06) (e Keypad (07) per completezza) per l’uso con IOHIDManagerSetDeviceMatchingMultiple, e quindi il callback del valore di input ottiene la pagina di utilizzo della tastiera / tastiera (0x07).

Ad esempio, per configurare un HIDManager per tutte le tastiere / tastiere, si potrebbe fare qualcosa come:

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

Dove myCreateDeviceMatchingDictionary è qualcosa del tipo:

 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; } 

E myHIDKeyboardCallback è qualcosa del tipo:

 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 ... } 

Si noti che la richiamata sembra essere chiamata più volte per stampa o rilascio, ma con ID di utilizzo al di fuori dell’intervallo normale, che è ciò per cui è “if (scancode <4 || scancode> 231)”.

grazie per aver fornito la risposta alla tua domanda.

invece dell’istruzione if in myHIDKeyboardCallback , che controlla scancode <4 o scancode> 231, è ansible utilizzare 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); 

è più LOC quindi una semplice istruzione if, ma si finisce con un callback più pulito.