void PsychHIDOSKbQueueStart(int deviceIndex)
{
    LPDIRECTINPUTDEVICE8 kb;
    DIPROPDWORD  dipdw;
	psych_bool queueActive;
	int i;
    
	if (deviceIndex < 0) {
		deviceIndex = PsychHIDGetDefaultKbQueueDevice();
		// Ok, deviceIndex now contains our default keyboard to use - The first suitable keyboard.
	}
    
	if ((deviceIndex < 0) || (deviceIndex >= ndevices)) {
		// Out of range index:
		PsychErrorExitMsg(PsychError_user, "Invalid 'deviceIndex' specified. No such device!");
	}
    
	// Does Keyboard queue for this deviceIndex already exist?
	if (NULL == psychHIDKbQueueFirstPress[deviceIndex]) {
		// No. Bad bad...
		printf("PsychHID-ERROR: Tried to start processing on non-existent keyboard queue for deviceIndex %i! Call KbQueueCreate first!\n", deviceIndex);
		PsychErrorExitMsg(PsychError_user, "Invalid keyboard 'deviceIndex' specified. No queue for that device yet!");
	}
    
	// Keyboard queue already stopped? Then we ain't nothing to do:
	if (psychHIDKbQueueActive[deviceIndex]) return;
    
	// Queue is inactive. Start it:
    
	// Will this be the first active queue, ie., aren't there any queues running so far?
	queueActive = FALSE;
	for (i = 0; i < PSYCH_HID_MAX_DEVICES; i++) {
		queueActive |= psychHIDKbQueueActive[i];
	}
    
	PsychLockMutex(&KbQueueMutex);
    
	// Clear out current state for this queue:
	memset(psychHIDKbQueueFirstPress[deviceIndex]   , 0, (256 * sizeof(double)));
	memset(psychHIDKbQueueFirstRelease[deviceIndex] , 0, (256 * sizeof(double)));
	memset(psychHIDKbQueueLastPress[deviceIndex]    , 0, (256 * sizeof(double)));
	memset(psychHIDKbQueueLastRelease[deviceIndex]  , 0, (256 * sizeof(double)));
    
	// Setup event mask, so events from our associated xinput device
	// get enqueued in our event queue:
    kb = GetXDevice(deviceIndex);
	
	// Device specific data format setup:
	switch (info[deviceIndex].dwDevType & 0xff) {
		case DI8DEVTYPE_KEYBOARD:
			if (DI_OK != kb->SetDataFormat(&c_dfDIKeyboard)) {
				PsychUnlockMutex(&KbQueueMutex);
				printf("PsychHID-ERROR: Tried to start processing on keyboard queue for deviceIndex %i, but setting dataformat failed!\n", deviceIndex);
				PsychErrorExitMsg(PsychError_user, "Starting keyboard queue failed!");
			}			
		break;
			
		case DI8DEVTYPE_MOUSE:
		case DI8DEVTYPE_SCREENPOINTER:
			if (DI_OK != kb->SetDataFormat(&c_dfDIMouse2)) {
				PsychUnlockMutex(&KbQueueMutex);
				printf("PsychHID-ERROR: Tried to start processing on keyboard queue for deviceIndex %i, but setting dataformat failed!\n", deviceIndex);
				PsychErrorExitMsg(PsychError_user, "Starting keyboard queue failed!");
			}			
		break;
			
		case DI8DEVTYPE_JOYSTICK:
			if (DI_OK != kb->SetDataFormat(&c_dfDIJoystick2)) {
				PsychUnlockMutex(&KbQueueMutex);
				printf("PsychHID-ERROR: Tried to start processing on keyboard queue for deviceIndex %i, but setting dataformat failed!\n", deviceIndex);
				PsychErrorExitMsg(PsychError_user, "Starting keyboard queue failed!");
			}			
		break;				
	}

    // Set device event buffer size to 256 elements:
    dipdw.diph.dwSize = sizeof(DIPROPDWORD);
    dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER);
    dipdw.diph.dwObj = 0;
    dipdw.diph.dwHow = DIPH_DEVICE;
    dipdw.dwData = 256;
    
    if (DI_OK != kb->SetProperty(DIPROP_BUFFERSIZE, &dipdw.diph)) {
        PsychUnlockMutex(&KbQueueMutex);
		printf("PsychHID-ERROR: Tried to start processing on keyboard queue for deviceIndex %i, but setting buffersize on device failed!\n", deviceIndex);
		PsychErrorExitMsg(PsychError_user, "Starting keyboard queue failed!");
    }
    
    // Enable state-change event notifications:
    if (DI_OK != kb->SetEventNotification(hEvent)) {
        PsychUnlockMutex(&KbQueueMutex);
		printf("PsychHID-ERROR: Tried to start processing on keyboard queue for deviceIndex %i, but setting device state notifications failed!\n", deviceIndex);
		PsychErrorExitMsg(PsychError_user, "Starting keyboard queue failed!");
    }
    
    if (DI_OK != kb->Acquire()) {
        PsychUnlockMutex(&KbQueueMutex);
		printf("PsychHID-ERROR: Tried to start processing on keyboard queue for deviceIndex %i, but acquiring device failed!\n", deviceIndex);
		PsychErrorExitMsg(PsychError_user, "Starting keyboard queue failed!");
    }
    
	// Mark this queue as logically started:
	psychHIDKbQueueActive[deviceIndex] = TRUE;
    
	// Queue started.
	PsychUnlockMutex(&KbQueueMutex);
    
	// If other queues are already active then we're done:
	if (queueActive) return;
    
	// No other active queues. We are the first one.
    
	// Start the common processing thread for all queues:
	PsychLockMutex(&KbQueueMutex);
	KbQueueThreadTerminate = FALSE;
    
	if (PsychCreateThread(&KbQueueThread, NULL, KbQueueWorkerThreadMain, NULL)) {
		// We are soo screwed:
        
		// Cleanup the mess:
		psychHIDKbQueueActive[deviceIndex] = FALSE;
		PsychUnlockMutex(&KbQueueMutex);
        
		// Whine a little bit:
		printf("PsychHID-ERROR: Start of keyboard queue processing failed!\n");
		PsychErrorExitMsg(PsychError_system, "Creation of keyboard queue background processing thread failed!");
	}
    
	// Up and running, we're done!
	PsychUnlockMutex(&KbQueueMutex);
    
	return;
}
PsychError SCREENOpenMovie(void) 
{
        PsychWindowRecordType                   *windowRecord;
        char                                    *moviefile;
        int                                     moviehandle = -1;
        int                                     framecount;
        double                                  durationsecs;
        double                                  framerate;
        double                                  aspectRatio;
        int                                     width;
        int                                     height;
        int                                     asyncFlag = 0;
		int                                     specialFlags1 = 0;
        static psych_bool                       firstTime = TRUE;
        double                                  preloadSecs = 1;
        int                                     rc;
        int                                     pixelFormat = 4;
        int                                     maxNumberThreads = -1;

        if (firstTime) {
            // Setup asyncopeninfo on first invocation:
            firstTime = FALSE;
            asyncmovieinfo.asyncstate = 0; // State = No async open in progress.
        }
        
        // All sub functions should have these two lines
        PsychPushHelp(useString, synopsisString, seeAlsoString);
        if(PsychIsGiveHelp()) {PsychGiveHelp(); return(PsychError_none);};

        PsychErrorExit(PsychCapNumInputArgs(7));            // Max. 7 input args.
        PsychErrorExit(PsychRequireNumInputArgs(1));        // Min. 1 input args required.
        PsychErrorExit(PsychCapNumOutputArgs(7));           // Max. 7 output args.
        
        // Get the window record from the window record argument and get info from the window record
        windowRecord = NULL;
        PsychAllocInWindowRecordArg(kPsychUseDefaultArgPosition, FALSE, &windowRecord);
        // Only onscreen windows allowed:
        if(windowRecord && !PsychIsOnscreenWindow(windowRecord)) {
            PsychErrorExitMsg(PsychError_user, "OpenMovie called on something else than an onscreen window.");
        }
        
        // Get the movie name string:
        moviefile = NULL;
        PsychAllocInCharArg(2, kPsychArgRequired, &moviefile);

        // Get the (optional) asyncFlag:
        PsychCopyInIntegerArg(3, FALSE, &asyncFlag);

        PsychCopyInDoubleArg(4, FALSE, &preloadSecs);
        if (preloadSecs < 0 && preloadSecs!= -1 && preloadSecs!= -2) PsychErrorExitMsg(PsychError_user, "OpenMovie called with invalid (negative, but not equal -1) 'preloadSecs' argument!");

        // Get the (optional) specialFlags1:
        PsychCopyInIntegerArg(5, FALSE, &specialFlags1);
		if (specialFlags1 < 0) PsychErrorExitMsg(PsychError_user, "OpenMovie called with invalid 'specialFlags1' setting! Only positive values allowed.");

        // Get the (optional) pixelFormat:
        PsychCopyInIntegerArg(6, FALSE, &pixelFormat);
        if (pixelFormat < 1 || pixelFormat > 8) PsychErrorExitMsg(PsychError_user, "OpenMovie called with invalid 'pixelFormat' setting! Only values 1 to 8 are allowed.");

        // Get the (optional) maxNumberThreads:
        PsychCopyInIntegerArg(7, FALSE, &maxNumberThreads);
        if (maxNumberThreads < -1) PsychErrorExitMsg(PsychError_user, "OpenMovie called with invalid 'maxNumberThreads' setting! Only values of -1 or greater are allowed.");
    
        // Queueing of a new movie for seamless playback requested?
        if (asyncFlag & 2) {
            // Yes. Do a special call, just passing the moviename of the next
            // movie to play. Pass the relevant moviehandle as retrieved from
            // preloadSecs:
            moviehandle = (int) preloadSecs;
            preloadSecs = 0;
            PsychCreateMovie(windowRecord, moviefile, preloadSecs, &moviehandle, asyncFlag, specialFlags1, pixelFormat, maxNumberThreads);
            if (moviehandle == -1) PsychErrorExitMsg(PsychError_user, "Could not queue new moviefile for gapless playback.");
            return(PsychError_none);
        }

        // Asynchronous Open operation in progress or requested?
        if ((asyncmovieinfo.asyncstate == 0) && !(asyncFlag & 1)) {
            // No. We should just synchronously open the movie:

            // Try to open the named 'moviefile' and create & initialize a corresponding movie object.
            // A MATLAB handle to the movie object is returned upon successfull operation.
            PsychCreateMovie(windowRecord, moviefile, preloadSecs, &moviehandle, asyncFlag, specialFlags1, pixelFormat, maxNumberThreads);
        }
        else {
            // Asynchronous open operation requested or running:
            switch(asyncmovieinfo.asyncstate) {
                case 0: // No async open running, but async open requested
                    // Fill all information needed for opening the movie into the info struct:
                    asyncmovieinfo.asyncstate = 1; // Mark state as "Operation in progress"
                    asyncmovieinfo.moviename = strdup(moviefile);
                    asyncmovieinfo.preloadSecs = preloadSecs;
                    asyncmovieinfo.asyncFlag = asyncFlag;
                    asyncmovieinfo.specialFlags1 = specialFlags1;
                    asyncmovieinfo.pixelFormat = pixelFormat;
					asyncmovieinfo.maxNumberThreads = maxNumberThreads;
                    
                    if (windowRecord) {
                        memcpy(&asyncmovieinfo.windowRecord, windowRecord, sizeof(PsychWindowRecordType));
                    } else {
                        memcpy(&asyncmovieinfo.windowRecord, 0, sizeof(PsychWindowRecordType));
                    }

                    asyncmovieinfo.moviehandle = -1;

                    // Increase our scheduling priority to basic RT priority: This way we should get
                    // more cpu time for our PTB main thread than the async. background prefetch-thread:
					// On Windows we must not go higher than basePriority 1 (HIGH PRIORITY) or bad interference can happen.
					// On OS/X we use basePriority 2 for robust realtime, using up to (4+1) == 5 msecs of time in every 10 msecs slice, allowing for up to 1 msec jitter/latency for ops.
					// On Linux we just use standard basePriority 2 RT-FIFO scheduling and trust the os to do the right thing.
                    if ((rc=PsychSetThreadPriority(NULL, ((PSYCH_SYSTEM == PSYCH_WINDOWS) ? 1 : 2), ((PSYCH_SYSTEM == PSYCH_OSX) ? 4 : 0)))!=0) {
                        printf("PTB-WARNING: In OpenMovie(): Failed to raise priority of main thread [System error %i]. Expect movie timing problems.\n", rc);
                    }

                    // Start our own movie loader Posix-Thread:
                    PsychCreateThread(&asyncmovieinfo.pid, NULL, PsychAsyncCreateMovie, &asyncmovieinfo);
                    
                    // Async movie open initiated. We return control to host environment:
                    return(PsychError_none);
                break;
                    
                case 1: // Async open operation in progress, but not yet finished.
                    // Should we wait for completion or just return?
                    if (asyncFlag & 1) {
                        // Async poll requested. We just return -1 to signal that open isn't finished yet:
                        PsychCopyOutDoubleArg(1, TRUE, -1);
                        return(PsychError_none);
                    }
                    // We fall through to case 2 - Wait for "Load operation successfully finished."
                
                case 2: // Async open operation successfully finished. Parse asyncinfo struct and return it to host environment:
                    // We need to join our terminated worker thread to release its ressources. If the worker-thread
                    // isn't done yet (fallthrough from case 1 for sync. wait), this join will block us until worker
                    // completes:
                    PsychDeleteThread(&asyncmovieinfo.pid);

                    asyncmovieinfo.asyncstate = 0; // Reset state to idle:
                    moviehandle = asyncmovieinfo.moviehandle;
                    
                    // Movie successfully opened?
                    if (moviehandle < 0) {
                        // Movie loading failed for some reason.
                        printf("PTB-ERROR: When trying to asynchronously load movie %s, the operation failed: ", asyncmovieinfo.moviename);
                        #if PSYCH_SYSTEM == PSYCH_OSX
                        switch(moviehandle) {
                            case -2000:
                            case -50:
                            case -43:
                                printf("File not found.");
                                break;
                                
                            case -2048:
                                printf("This is not a file that Quicktime understands.");
                                break;
                                
                            case -2003:
                                printf("Can't find media handler (codec) for this movie.");
                                break;
                                
                            case -2:
                                printf("Maximum allowed number of simultaneously open movie files exceeded!");
                                break;
                                
                            case -1:
                                printf("Internal error: Failure in PTB's movie playback engine!");
                                break;
                                
                            default:
                                printf("Unknown error (Quicktime error %i): Check http://developer.apple.com/documentation/QuickTime/APIREF/ErrorCodes.htm#//apple_ref/doc/constant_group/Error_Codes", moviehandle);
                        }
                        printf("\n\n");
                        #endif

                        PsychErrorExitMsg(PsychError_user, "Asynchronous loading of the Quicktime movie failed.");
                    }
                    
                    // We can fall out of the switch statement and continue with the standard synchronous load code as if
                    // the movie had been loaded synchronously.
                break;
                default:
                    PsychErrorExitMsg(PsychError_internal, "Unhandled async movie state condition encountered! BUG!!");
            }
        }

        // Upon sucessfull completion, we'll have a valid handle in 'moviehandle'.
        // Return it to Matlab-world:
        PsychCopyOutDoubleArg(1, TRUE, (double) moviehandle);

        // Retrieve infos about new movie:
        
        // Is the "count" output argument (total number of frames) requested by user?
        if (PsychGetNumOutputArgs() > 5) {
            // Yes. Query the framecount (expensive!) and return it:
            PsychGetMovieInfos(moviehandle, &width, &height, &framecount, &durationsecs, &framerate, NULL, &aspectRatio);
            PsychCopyOutDoubleArg(6, TRUE, (double) framecount);
        }
        else {
            // No. Don't compute and return it.
            PsychGetMovieInfos(moviehandle, &width, &height, NULL, &durationsecs, &framerate, NULL, &aspectRatio);
        }

        PsychCopyOutDoubleArg(2, FALSE, (double) durationsecs);
        PsychCopyOutDoubleArg(3, FALSE, (double) framerate);
        PsychCopyOutDoubleArg(4, FALSE, (double) width);
        PsychCopyOutDoubleArg(5, FALSE, (double) height);
        PsychCopyOutDoubleArg(7, FALSE, (double) aspectRatio);

	// Ready!
	return(PsychError_none);
}
PsychError PsychHIDOSKbQueueCreate(int deviceIndex, int numScankeys, int* scanKeys)
{
    pRecDevice deviceRecord;

	// Valid number of keys?
	if (scanKeys && (numScankeys != 256)) {
		PsychErrorExitMsg(PsychError_user, "Second argument to KbQueueCreate must be a vector with 256 elements.");
	}

    // Do we finally have a valid keyboard or other suitable input device?
    // PsychHIDOSGetKbQueueDevice() will error out if no suitable device
    // for deviceIndex can be found. Otherwise it will return the HID
    // device record and remapped deviceIndex for use with our KbQueues:
    deviceIndex = PsychHIDOSGetKbQueueDevice(deviceIndex, &deviceRecord);

	// Keyboard queue for this deviceIndex already created?
	if (psychHIDKbQueueFirstPress[deviceIndex]) {
		// Yep. Release it, so we can start from scratch:
		PsychHIDOSKbQueueRelease(deviceIndex);
	}

	// Allocate and zero-init memory for tracking key presses and key releases:
	psychHIDKbQueueFirstPress[deviceIndex]   = calloc(256, sizeof(double));
	psychHIDKbQueueFirstRelease[deviceIndex] = calloc(256, sizeof(double));
	psychHIDKbQueueLastPress[deviceIndex]    = calloc(256, sizeof(double));
	psychHIDKbQueueLastRelease[deviceIndex]  = calloc(256, sizeof(double));
	psychHIDKbQueueScanKeys[deviceIndex]     = calloc(256, sizeof(int));
    
	// Assign scanKeys vector, if any:
	if (scanKeys) {
		// Copy it:
		memcpy(psychHIDKbQueueScanKeys[deviceIndex], scanKeys, 256 * sizeof(int));
	} else {
		// None provided. Enable all keys by default:
		memset(psychHIDKbQueueScanKeys[deviceIndex], 1, 256 * sizeof(int));        
	}
    
    // Create HIDQueue for device:
    queue[deviceIndex] = IOHIDQueueCreate(kCFAllocatorDefault, deviceRecord, 30, 0);
    if (NULL == queue[deviceIndex]) PsychErrorExitMsg(PsychError_system, "Failed to create event queue for detecting key press.");

    // Mark as a non-keyboard device, to start with:
    queueIsAKeyboard[deviceIndex] = FALSE;

    // Parse HID device to add all detected and selected keys:
    {
        // Add deviceRecord's elements to our queue, filtering unwanted keys via 'scanList'.
        // This code is almost identical to the enumeration code in PsychHIDKbCheck, to make sure we get
        // matching performance and behaviour and hopefully that it works on the latest problematic Apple
        // hardware, e.g., late 2013 MacBookAir and OSX 10.9:
        {
            uint32_t usage, usagePage;
            pRecElement currentElement, lastElement = NULL;
            
            // Step through the elements of the device and add matching ones:
            for (currentElement = HIDGetFirstDeviceElement(deviceRecord, kHIDElementTypeInput | kHIDElementTypeCollection);
                 (currentElement != NULL) && (currentElement != lastElement);
                 currentElement = HIDGetNextDeviceElement(currentElement, kHIDElementTypeInput | kHIDElementTypeCollection))
            {
                // Keep track of last queried element:
                lastElement = currentElement;
                
                usage     = IOHIDElementGetUsage(currentElement);
                usagePage = IOHIDElementGetUsagePage(currentElement);
                if (getenv("PSYCHHID_TELLME")) {
                    printf("PTB-DEBUG: [KbQueueCreate]: ce %p page %d usage: %d isArray: %d\n", currentElement, usagePage, usage, IOHIDElementIsArray(currentElement));
                }
                
                if (IOHIDElementGetType(currentElement) == kIOHIDElementTypeCollection) {
                    CFArrayRef children = IOHIDElementGetChildren(currentElement);
                    if (!children) continue;
                    
                    CFIndex idx, cnt = CFArrayGetCount(children);
                    for (idx = 0; idx < cnt; idx++) {
                        IOHIDElementRef tIOHIDElementRef = (IOHIDElementRef) CFArrayGetValueAtIndex(children, idx);
                        if (tIOHIDElementRef && ((IOHIDElementGetType(tIOHIDElementRef) == kIOHIDElementTypeInput_Button) ||
                                                 (IOHIDElementGetType(tIOHIDElementRef) == kIOHIDElementTypeInput_ScanCodes))) {
                            usage = IOHIDElementGetUsage(tIOHIDElementRef);
                            if ((usage <= 256) && (usage >= 1) && ( (scanKeys == NULL) || (scanKeys[usage - 1] > 0) )) {
                                // Add it for use in keyboard queue:
                                PsychHIDOSKbElementAdd(tIOHIDElementRef, queue[deviceIndex], deviceIndex);
                            }
                        }
                    }
                    
                    // Done with this currentElement, which was a collection of buttons/keys.
                    // Iterate to next currentElement:
                    continue;
                }
                
                // Classic path for non-collection elements:
                if(((usagePage == kHIDPage_KeyboardOrKeypad) || (usagePage == kHIDPage_Button)) && (usage <= 256) && (usage >= 1) &&
                   ( (scanKeys == NULL) || (scanKeys[usage - 1] > 0) ) ) {
                    // Add it for use in keyboard queue:
                    PsychHIDOSKbElementAdd(currentElement, queue[deviceIndex], deviceIndex);
                }
            }
        }
    }
    
    // Register "queue empty -> non-empty transition" callback: TODO Replace queue by reference to our keyboard queue struct:
    IOHIDQueueRegisterValueAvailableCallback(queue[deviceIndex], (IOHIDCallback) PsychHIDKbQueueCallbackFunction, (void*) (long) deviceIndex);

	// Create event buffer:
	PsychHIDCreateEventBuffer(deviceIndex);

    // Start the processing thread for this queue:
    PsychLockMutex(&KbQueueMutex);

    if (PsychCreateThread(&KbQueueThread[deviceIndex], NULL, KbQueueWorkerThreadMain, (void*) (long) deviceIndex)) {
        // We are so screwed:

        // Cleanup the mess:
        psychHIDKbQueueActive[deviceIndex] = FALSE;
        PsychUnlockMutex(&KbQueueMutex);

        // Whine a little bit:
        printf("PsychHID-ERROR: Start of keyboard queue processing for deviceIndex %i failed!\n", deviceIndex);
        PsychErrorExitMsg(PsychError_system, "Creation of keyboard queue background processing thread failed!");
    }

    PsychUnlockMutex(&KbQueueMutex);

	// Ready to use this keybord queue.
	return(PsychError_none);
}