IOHIDManager 不触发键盘回调

IOHIDManager not triggering callback for keyboard

因此,我正在尝试使用 CGL 和 IOHIDManager 组合一个简单的全屏 OpenGL 应用程序,以学习较低级别的 API。目前,我正在创建一个 OpenGL 上下文并全屏启动它。我现在正在尝试添加键盘输入,以便我可以退出该应用程序。我发现了许多使用 IOHIDManager 读取密钥的类似示例,但无论我做什么,我的回调都不会触发。

我的回调只是一个打印 "here" 的函数。我不确定哪里出错了——我已经尝试了 CFRunLoopGetCurrent() 和 CFRunLoopMain()。我的 main 只是一个 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 的末尾,我的回调会被调用,但函数永远不会 returns。这应该如何在单线程 CGL 应用程序中工作? IOHIDManager 需要 运行 循环才能运行,这是一个严格的要求吗?

原来我需要用 CreateInputManager 函数创建一个单独的 pthread,指定 IOHIMDanager 是在 CFRunLoopGetCurrent() 上安排回调并在其上启动一个 运行 循环通过调用 CFRunLoopRun().

线程

我想知道是否有办法让 IOHIDManager 使用普通的轮询而不是这些回调...

IOKit 和 HID 通过 Mach 消息传递工作,而这又与运行循环机制深度集成,如您所见。如果您确实想要忙轮询,可以使用零超时的 CFRunLoopRunInMode 函数来检查事件。

您可能希望考虑使用 CVDisplayLink 在每个垂直帧刷新时调用您的渲染代码。显示 link 的回调将从运行循环中调用,因此您可以将主线程 运行 留在 CFRunLoopRun() 中。

有关 Apple 建议您在 OpenGL 应用程序中构建事件处理的方式,请参阅 https://developer.apple.com/library/mac/qa/qa1385/_index.html