IOHIDManager не запускает обратный вызов для клавиатуры

Итак, я пытаюсь собрать простое полноэкранное приложение OpenGL, используя CGL и IOHIDManager, чтобы изучить API более низкого уровня. В настоящее время я создаю контекст OpenGL и запускаю его в полноэкранном режиме. Сейчас я пытаюсь добавить ввод с клавиатуры, чтобы выйти из приложения. Я нашел много подобных примеров использования IOHIDManager для чтения ключей, но независимо от того, что я делаю, мой обратный вызов не срабатывает.

Мой обратный вызов — это просто функция, которая печатает «здесь». Я не уверен, где я ошибаюсь - я пробовал и CFRunLoopGetCurrent(), и CFRunLoopMain(). Мой основной - это просто цикл while. Что дает?

CFMutableDictionaryRef CreateMatchingDictionary(UInt32 usage_page, UInt32 usage) {        
    CFMutableDictionaryRef dictionary = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);

    CFNumberRef page_number = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &usage_page);            
    CFDictionarySetValue(dictionary, CFSTR(kIOHIDDeviceUsagePageKey), page_number);
    CFRelease(page_number);

    CFNumberRef usage_number = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &usage);            
    CFDictionarySetValue(dictionary, CFSTR(kIOHIDDeviceUsageKey), usage_number);
    CFRelease(usage_number);

    return dictionary;
}

void CreateInputManager() {
    IOHIDManagerRef hid_manager = IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDOptionsTypeNone);
    CFMutableDictionaryRef matching_dictionary = CreateMatchingDictionary(kHIDPage_GenericDesktop, kHIDUsage_GD_Keyboard);
    IOHIDManagerSetDeviceMatching(hid_manager, matching_dictionary);
    IOHIDManagerRegisterInputValueCallback(hid_manager, KeyboardCallback, NULL);

    IOHIDManagerOpen(hid_manager, kIOHIDOptionsTypeNone);
    IOHIDManagerScheduleWithRunLoop(hid_manager, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
}

void KeyboardCallback(void *context, IOReturn result, void *sender, IOHIDValueRef value) {                                                                                                       
    puts("CALLBACK!");
}

int main() {
    // Commented out CGL context & fullscreen window creation
    CreateInputManager();
    while(true) {
        ;   
    }   
}

ОБНОВЛЕНИЕ

Если я помещу CFRunLoopRun() в конец функции CreateInputManager, мой обратный вызов будет вызван, но функция никогда не вернется. Как это должно работать в однопоточном приложении CGL? Является ли это строгим требованием, чтобы для работы IOHIDManager требовался цикл выполнения?


person Community    schedule 19.02.2016    source источник


Ответы (2)


IOKit и HID работают через обмен сообщениями Mach, который, в свою очередь, глубоко интегрирован с механизмом цикла выполнения, как вы уже обнаружили. Если вы действительно хотите использовать опрос занятости, вы можете использовать функцию CFRunLoopRunInMode с нулевым временем ожидания для проверки событий.

Вместо этого вы можете рассмотреть возможность использования CVDisplayLink для вызова кода рендеринга при каждом вертикальном обновлении кадра. Обратный вызов отображаемой ссылки будет вызываться из цикла выполнения, поэтому вы можете оставить свой основной поток работающим в CFRunLoopRun().

См. https://developer.apple.com/library/mac/qa/qa1385/_index.html, чтобы узнать, как Apple рекомендует структурировать обработку событий в приложениях OpenGL.

person pmdj    schedule 20.02.2016
comment
Отлично, так что, если я выберу опрос занятости, настроив HID на использование CFRunLoopInMode, не будет ли в моей программе технически механизма выполнения цикла? - person ; 20.02.2016
comment
Кроме того, спасибо за совет CVDisplayLink, я знал об этой версии для iOS, но не для OSX. - person ; 20.02.2016
comment
Что ж, будет механизм цикла выполнения, но вы можете выбрать, когда цикл выполнения должен обрабатывать события, и как только он обработает все ожидающие события, он вернется из CFRunLoopInMode и вернет управление вашему приложению. - person pmdj; 20.02.2016
comment
На самом деле, разве IOHIDQueueCopyNextValueWithTimeout не позволит мне опрашивать состояние ключа в основном потоке без создания цикла выполнения? - person ; 20.02.2016
comment
Я не уверен, как IOHIDQueueCopyNextValueWithTimeout/IOHIDQueueCopyNextValue реализовано внутри, но вполне может работать. - person pmdj; 21.02.2016

Оказывается, мне нужно создать отдельный pthread с функцией CreateInputManager, указать, что IOHIDManager должен запланировать обратный вызов на CFRunLoopGetCurrent(), и запустить цикл выполнения в этом потоке, вызвав CFRunLoopRun().

Интересно, есть ли способ заставить IOHIDManager работать с обычным опросом вместо этих обратных вызовов...

person Community    schedule 19.02.2016