// Async processing thread for keyboard events:
static void *KbQueueWorkerThreadMain(void *inarg) {
    int deviceIndex = (int) inarg;
    int rc;

    // Switch ourselves (NULL) to RT scheduling: We promise to use / require at most (0+1) == 1 msec every
    // 10 msecs and allow for wakeup delay/jitter of up to 2 msecs -- perfectly reasonable, given that we
    // only do minimal << 1 msec processing, only at the timescale of human reaction times, and driven by
    // input devices with at least 4+/-4 msecs jitter at 8 msec USB polling frequency.
    if ((rc = PsychSetThreadPriority(NULL, 2, 0)) > 0) {
        printf("PsychHID: KbQueueCreate: Failed to switch to realtime priority [%s].\n", strerror(rc));
    }

    // Keep a global reference to the runloop, as we need it in KbQueueRelease to get this thread to exit:
    psychHIDKbQueueCFRunLoopRef[deviceIndex] = (CFRunLoopRef) CFRunLoopGetCurrent();
    CFRetain(psychHIDKbQueueCFRunLoopRef[deviceIndex]);

    // Add HID queue to current runloop:
    IOHIDQueueScheduleWithRunLoop(queue[deviceIndex], psychHIDKbQueueCFRunLoopRef[deviceIndex], kCFRunLoopDefaultMode);

    // Start the run loop, code execution will block here until run loop is stopped again by PsychHIDKbQueueRelease
    // Meanwhile, the run loop of this thread will be responsible for executing code below in PsychHIDKbQueueCalbackFunction
    while (CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.1, false) == kCFRunLoopRunTimedOut) {};

    // Remove HID queue from current runloop:
    IOHIDQueueUnscheduleFromRunLoop(queue[deviceIndex], psychHIDKbQueueCFRunLoopRef[deviceIndex], kCFRunLoopDefaultMode);

    // Done. Die peacefully:
    return(NULL);
}
  void
  osxHIDInputDevice::AddDevice(void *context,
			       IOReturn /*result*/, void */*sender*/, IOHIDDeviceRef device) {
    osxHIDInputDevice *self = (osxHIDInputDevice*)context ;

    URI devUri = hidDeviceURI(device) ;

    // std::cerr << std::endl << self->uri.asString() << std::endl << devUri.asString() << std::endl << std::endl ;

    bool match = self->theDevice==0 && (self->uri.isEmpty() || self->uri.scheme=="any" || self->uri.resemble(devUri)) ;
    if (self->debugLevel>0) {
      std::cerr << (match?"+ ":"  ") ;
      hidDebugDevice(device, std::cerr) ;
      std::cerr << std::endl ;
    }
    if (!match) return ;

    self->theDevice = new __device(device) ;
    self->uri = devUri ;

    CFDataRef descriptor = (CFDataRef)IOHIDDeviceGetProperty(self->theDevice->device, CFSTR(kIOHIDReportDescriptorKey)) ;
    if (descriptor) {
      const UInt8 *bytes = CFDataGetBytePtr(descriptor) ;
      CFIndex length = CFDataGetLength(descriptor) ;
      if (self->inputreport_callback && !self->parser->setDescriptor(bytes, length))
        std::cerr << "osxHIDInputDevice::AddDevice: unable to parse the HID report descriptor" << std::endl;
      if (self->debugLevel > 1) {
        std::cerr << "    HID report descriptor: [ " << std::flush ;
        for (int i=0; i<length; ++i)
          std::cerr << std::hex << std::setfill('0') << std::setw(2) << (int)bytes[i] << " " ;
        std::cerr << "]" << std::endl ;
      }
    }

#if DEBUG_MODE
      std::cerr << "Setting up callbacks" << std::endl ;
#endif

    // ----------------------------------------------------------------
    
    if (self->inputreport_callback) {
#if DEBUG_MODE
      std::cerr << "Setting up report callback" << std::endl ;
#endif
#if __MAC_OS_X_VERSION_MIN_REQUIRED >= 101000
      IOHIDDeviceRegisterInputReportWithTimeStampCallback(device,
					     self->theDevice->report, sizeof(self->theDevice->report),
					     self->inputreport_callback, self->inputreport_context) ;
#else
      IOHIDDeviceRegisterInputReportCallback(device,
					     self->theDevice->report, sizeof(self->theDevice->report),
					     self->inputreport_callback, self->inputreport_context) ;
#endif
    }

    // ----------------------------------------------------------------
    
    if (self->value_callback) {
#if DEBUG_MODE
      std::cerr << "Setting up value callback" << std::endl ;
#endif     
      IOHIDDeviceSetInputValueMatchingMultiple(device, self->elements_match) ; 
      IOHIDDeviceRegisterInputValueCallback(device, self->value_callback, self->value_context) ;
    }

    // ----------------------------------------------------------------
    
    if (self->queue_callback) {
#if DEBUG_MODE
      std::cerr << "Setting up queue callback" << std::endl ;
#endif
      self->theDevice->queue = IOHIDQueueCreate(kCFAllocatorDefault, device, queueSize, kIOHIDOptionsTypeNone) ;
      if (self->elements_match) {
#if DEBUG && DEBUG_MATCHING_ELEMENTS	
	std::cerr << "Queue, elements_match" << std::endl ;
#endif
	CFIndex mcount = CFArrayGetCount(self->elements_match) ;
	for (CFIndex mindex=0; mindex<mcount; ++mindex) {
	  CFDictionaryRef matching = (CFDictionaryRef)CFArrayGetValueAtIndex(self->elements_match, mindex) ;
	  CFArrayRef elements = IOHIDDeviceCopyMatchingElements(device, matching, kIOHIDOptionsTypeNone) ;
	  if (!elements) continue ;
	  CFIndex ecount = CFArrayGetCount(elements) ;
	  for (CFIndex eindex=0; eindex<ecount; ++eindex) {
	    IOHIDElementRef e = (IOHIDElementRef)CFArrayGetValueAtIndex(elements, eindex) ;
	    IOHIDQueueAddElement(self->theDevice->queue, e) ;
#if DEBUG && DEBUG_MATCHING_ELEMENTS
	    std::cerr << "elements_match EINDEX: " << eindex
		      << ", usagepage: " << IOHIDElementGetUsagePage(e)
		      << ", usage: " << IOHIDElementGetUsage(e)
		      << std::endl ;
#endif
	  }
	}
      } else {
#if DEBUG && DEBUG_MATCHING_ELEMENTS	
	std::cerr << "Queue, no elements_match" << std::endl ;
#endif
	CFArrayRef elements = IOHIDDeviceCopyMatchingElements(device, 0, kIOHIDOptionsTypeNone) ;
	if (elements) {
	  CFIndex ecount = CFArrayGetCount(elements) ;
	  for (CFIndex eindex=0; eindex<ecount; ++eindex) {
	    IOHIDElementRef e = (IOHIDElementRef)CFArrayGetValueAtIndex(elements, eindex) ;
	    IOHIDQueueAddElement(self->theDevice->queue, e) ;
#if DEBUG && DEBUG_MATCHING_ELEMENTS
	    std::cerr << "!elements_match EINDEX: " << eindex
		      << ", usagepage: " << IOHIDElementGetUsagePage(e)
		      << ", usage: " << IOHIDElementGetUsage(e)
		      << std::endl ;
#endif
	  }
	}
      }
      IOHIDQueueRegisterValueAvailableCallback(self->theDevice->queue, self->queue_callback, self->queue_context) ;
      IOHIDQueueScheduleWithRunLoop(self->theDevice->queue, CFRunLoopGetMain(), kCFRunLoopDefaultMode) ;
      IOHIDQueueStart(self->theDevice->queue) ;
    }

    // ----------------------------------------------------------------
  }