PsychError GETSECSGetSecs(void) { double *returnValue; //check to see if the user supplied superfluous arguments PsychErrorExit(PsychCapNumOutputArgs(1)); PsychErrorExit(PsychCapNumInputArgs(0)); //Allocate a return matrix and load it with the depth values. PsychAllocOutDoubleArg(1, FALSE, &returnValue); PsychGetAdjustedPrecisionTimerSeconds(returnValue); return(PsychError_none); }
PsychError PRIORITYPriority(void) { double *returnValue; pid_t processID; int oldPriority, newPriority; //check to see if the user supplied superfluous arguments PsychErrorExit(PsychCapNumOutputArgs(1)); PsychErrorExit(PsychCapNumInputArgs(1)); //Allocate a return matrix and load it with the current process priority. PsychAllocOutDoubleArg(1, FALSE, &returnValue); processID= getpid(); oldPriority=getpriority(PRIO_PROCESS, processID); *returnValue=(double)oldPriority; //Fetch the input argument and set the priority if(PsychCopyInIntegerArg(1, FALSE, &newPriority)) setpriority(PRIO_PROCESS, processID, newPriority); return(PsychError_none); }
PsychError SCREENPreloadTextures(void) { PsychWindowRecordType *windowRecord, *texwin; psych_bool isArgThere; int *texhandles; PsychWindowRecordType **windowRecordArray; int i, n, numWindows, myhandle; double *success; psych_bool* residency; GLuint* texids; GLboolean* texresident; psych_bool failed = false; GLclampf maxprio = 1.0f; GLenum target; //all sub functions should have these two lines PsychPushHelp(useString, synopsisString,seeAlsoString); if(PsychIsGiveHelp()){PsychGiveHelp();return(PsychError_none);}; //check for superfluous arguments PsychErrorExit(PsychCapNumInputArgs(2)); //The maximum number of inputs PsychErrorExit(PsychRequireNumInputArgs(1)); //The minimum number of inputs PsychErrorExit(PsychCapNumOutputArgs(2)); //The maximum number of outputs //get the window record from the window record argument and get info from the window record PsychAllocInWindowRecordArg(1, kPsychArgRequired, &windowRecord); // Get optional texids vector: isArgThere = PsychIsArgPresent(PsychArgIn, 2); PsychAllocInIntegerListArg(2, FALSE, &n, &texhandles); if (n < 1) isArgThere=FALSE; // Enable this windowRecords framebuffer as current drawingtarget: PsychSetDrawingTarget(windowRecord); // Disable shader: PsychSetShader(windowRecord, 0); glDisable(GL_TEXTURE_2D); // Fetch global texturing mode: target=PsychGetTextureTarget(windowRecord); glEnable(target); glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); glColor4f(0, 0, 0, 0); // Setup identity modelview matrix: glMatrixMode(GL_MODELVIEW); glPushMatrix(); glLoadIdentity(); PsychCreateVolatileWindowRecordPointerList(&numWindows, &windowRecordArray); // Process vector of all texids for all requested textures: if (!isArgThere) { // No handles provided: In this case, we preload all textures: n=0; for(i=0; i<numWindows; i++) { if (windowRecordArray[i]->windowType==kPsychTexture) { n++; // Prioritize this texture: glPrioritizeTextures(1, (GLuint*) &(windowRecordArray[i]->textureNumber), &maxprio); // Bind this texture: glBindTexture(target, windowRecordArray[i]->textureNumber); // Render a single textured point, thereby enforcing a texture upload: glBegin(GL_QUADS); glTexCoord2f(0,0); glVertex2i(10,10); glTexCoord2f(0,1); glVertex2i(10,11); glTexCoord2f(1,1); glVertex2i(11,11); glTexCoord2f(1,0); glVertex2i(11,10); glEnd(); } } texids = (GLuint*) PsychMallocTemp(sizeof(GLuint) * n); texresident = (GLboolean*) PsychMallocTemp(sizeof(GLboolean) * n); n=0; for(i=0; i<numWindows; i++) { if (windowRecordArray[i]->windowType==kPsychTexture) { texids[n] = (GLuint) windowRecordArray[i]->textureNumber; n++; } } } else { // Vector with texture handles provided: Just preload them. texids = (GLuint*) PsychMallocTemp(sizeof(GLuint) * n); texresident = (GLboolean*) PsychMallocTemp(sizeof(GLboolean) * n); myhandle=0; for (i=0; i<n; i++) { myhandle = texhandles[i]; texwin = NULL; if (IsWindowIndex(myhandle)) FindWindowRecord(myhandle, &texwin); if (texwin && texwin->windowType==kPsychTexture) { // Prioritize this texture: glPrioritizeTextures(1, (GLuint*) &(texwin->textureNumber), &maxprio); // Bind this texture: glBindTexture(target, texwin->textureNumber); // Render a single textured point, thereby enforcing a texture upload: glBegin(GL_QUADS); glTexCoord2f(0,0); glVertex2i(10,10); glTexCoord2f(0,1); glVertex2i(10,11); glTexCoord2f(1,1); glVertex2i(11,11); glTexCoord2f(1,0); glVertex2i(11,10); glEnd(); texids[i] = (GLuint) texwin->textureNumber; } else { // This handle is invalid or at least no texture handle: printf("PTB-ERROR! Screen('PreloadTextures'): Entry %i of texture handle vector (handle %i) is not a texture handle!\n", i, myhandle); failed = true; } } } // Restore old matrix from backup copy, undoing the global translation: glPopMatrix(); // Disable texture engine: glDisable(GL_TEXTURE_2D); glDisable(target); // Wait for prefetch completion: glFinish(); // We don't need these anymore: PsychDestroyVolatileWindowRecordPointerList(windowRecordArray); if (failed) { PsychErrorExitMsg(PsychError_user, "At least one texture handle in texids-vector was invalid! Aborted."); } // Query residency state of all preloaded textures: success = NULL; PsychAllocOutDoubleArg(1, FALSE, &success); *success = (double) glAreTexturesResident(n, texids, texresident); // Sync pipe again, just to be safe... glFinish(); // Count them and copy them into output vector: PsychAllocOutBooleanMatArg(2, FALSE, n, 1, 1, &residency); for (i=0; i<n; i++) { residency[i] = (psych_bool) ((*success) ? TRUE : texresident[i]); } PsychTestForGLErrors(); // Done. Our PsychMallocTemp'ed arrays will be auto-released... return(PsychError_none); }
PsychError PSYCHHIDKbTriggerWait(void) { pRecDevice deviceRecord; int i, deviceIndex, numDeviceIndices; long KeysUsagePage=0x07; // This is the keyboard usage page long KeysUsage; // This will contain the key code of the trigger key long KbDeviceUsagePages[NUMDEVICEUSAGES]= {0x01,0x01}, KbDeviceUsages[NUMDEVICEUSAGES]={0x06,0x07}; // Generic Desktop page (0x01), keyboard (0x06), keypad (0x07) int numDeviceUsages=NUMDEVICEUSAGES; int deviceIndices[PSYCH_HID_MAX_KEYBOARD_DEVICES]; pRecDevice deviceRecords[PSYCH_HID_MAX_KEYBOARD_DEVICES]; psych_bool isDeviceSpecified, foundUserSpecifiedDevice; double *timeValueOutput; IOHIDQueueInterface **queue; HRESULT result; IOHIDDeviceInterface122** interface=NULL; // This requires Mac OS X 10.3 or higher IOReturn success; IOHIDElementCookie triggerCookie; PsychPushHelp(useString, synopsisString, seeAlsoString); if(PsychIsGiveHelp()){PsychGiveHelp();return(PsychError_none);}; PsychErrorExit(PsychCapNumOutputArgs(1)); PsychErrorExit(PsychCapNumInputArgs(2)); //Specify trigger key code and the deviceNumber of the keyboard or keypad to scan. PsychHIDVerifyInit(); if(hidDataRef!=NULL) PsychErrorExitMsg(PsychError_user, "A queue is already running, you must call KbQueueRelease() before invoking KbTriggerWait."); //Identify the trigger { int KeysUsageInteger; if(!PsychCopyInIntegerArg(1, TRUE, &KeysUsageInteger)){ PsychErrorExitMsg(PsychError_user, "Keycode is required."); } KeysUsage=KeysUsageInteger; } //Choose the device index and its record PsychHIDGetDeviceListByUsages(numDeviceUsages, KbDeviceUsagePages, KbDeviceUsages, &numDeviceIndices, deviceIndices, deviceRecords); isDeviceSpecified=PsychCopyInIntegerArg(2, FALSE, &deviceIndex); if(isDeviceSpecified){ //make sure that the device number provided by the user is really a keyboard or keypad. for(i=0;i<numDeviceIndices;i++){ if(foundUserSpecifiedDevice=(deviceIndices[i]==deviceIndex)) break; } if(!foundUserSpecifiedDevice) PsychErrorExitMsg(PsychError_user, "Specified device number is not a keyboard or keypad device."); }else{ // set the keyboard or keypad device to be the first keyboard device or, if no keyboard, the first keypad i=0; if(numDeviceIndices==0) PsychErrorExitMsg(PsychError_user, "No keyboard or keypad devices detected."); else{ deviceIndex=deviceIndices[i]; } } deviceRecord=deviceRecords[i]; //Allocate and init out return arguments. PsychAllocOutDoubleArg(1, FALSE, &timeValueOutput); if(!timeValueOutput) PsychErrorExitMsg(PsychError_system, "Failed to allocate memory for output."); interface=deviceRecord->interface; if(!interface) PsychErrorExitMsg(PsychError_system, "Could not get interface to device."); // The following bracketed clause will get a cookie corresponding to the // trigger. If multiple keys were of interest, the code could be modified // trivially to iterate over an array of KeysUsage to generate an array of // corresponding cookies { CFArrayRef elements; psych_bool usedDictionary=FALSE; { CFDictionaryRef dict=NULL; // The next few lines define a dictionary describing the key of interest // If they are omitted, the code will still work, but all elements will match // initially rather than just the one key of interest, so the code will be // slower since it will iterate through hundreds of keys CFStringRef keys[2] = {CFSTR("UsagePage"), CFSTR("Usage")}; CFNumberRef values[2]; values[0] = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &KeysUsagePage); values[1] = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &KeysUsage); if(values[0]!=NULL && values[1]!=NULL){ // Even if they are NULL, it will be ok since dict can be NULL at the expense of some loss of efficiency dict = CFDictionaryCreate(kCFAllocatorDefault, (const void**)keys, (const void**)values, 2, &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); } // copyMatchinfElements requires IOHIDDeviceInterface122, thus Mac OS X 10.3 or higher // elements would have to be obtained directly from IORegistry for 10.2 or earlier // If dict==NULL, all elements will match, leading to some inefficiency success = (*interface)->copyMatchingElements(interface, dict, &elements); if(dict){ usedDictionary=TRUE; CFRelease(dict); } if(values[0]) CFRelease(values[0]); if(values[1]) CFRelease(values[1]); if(!elements){ PsychErrorExitMsg(PsychError_user, "Specified key code not found on device."); } } { // elements will only contain one element in this implementation, but has the // advantage of generalizing to future derived implementations that listen // for multiple keys CFIndex i; for (i=0; i<CFArrayGetCount(elements); i++) { long number; CFDictionaryRef element= CFArrayGetValueAtIndex(elements, i); CFTypeRef object; if(!element) continue; if(!usedDictionary){ // Verify tht we are dealing with a keypad or keyboard object = CFDictionaryGetValue(element, CFSTR(kIOHIDElementUsageKey)); if (object == 0 || CFGetTypeID(object) != CFNumberGetTypeID()) continue; if (!CFNumberGetValue((CFNumberRef) object, kCFNumberLongType,&number)) continue; if(number!=KeysUsage) continue; // See if element corresponds to the desired key object = CFDictionaryGetValue(element, CFSTR(kIOHIDElementUsagePageKey)); if (object == 0 || CFGetTypeID(object) != CFNumberGetTypeID()) continue; if (!CFNumberGetValue((CFNumberRef) object, kCFNumberLongType, &number)) continue; if(number!=KeysUsagePage) continue; } // Get the cookie for this element object= (CFDictionaryGetValue(element, CFSTR(kIOHIDElementCookieKey))); if (object == 0 || CFGetTypeID(object) != CFNumberGetTypeID()) continue; if(!CFNumberGetValue((CFNumberRef) object, kCFNumberLongType, &number)) continue; triggerCookie = (IOHIDElementCookie) number; break; } if(CFArrayGetCount(elements)==i){ CFRelease(elements); PsychErrorExitMsg(PsychError_user, "Specified key code not found on device."); } CFRelease(elements); } } // Allocate for the queue queue=(*interface)->allocQueue(interface); if(!queue){ PsychErrorExitMsg(PsychError_system, "Failed to allocate event queue for detecting key press."); } // Create the queue result = (*queue)->create(queue, 0, 8); // 8 events can be stored before the earliest event is lost if (kIOReturnSuccess != result){ (*queue)->Release(queue); (*queue)=NULL; PsychErrorExitMsg(PsychError_system, "Failed to create event queue for detecting key press."); } // Add the trigger to the queue // If multiple keys were of interest, their cookies could be added in turn result = (*queue)->addElement(queue, triggerCookie, 0); if (kIOReturnSuccess != result){ result = (*queue)->dispose(queue); (*queue)->Release(queue); (*queue)=NULL; PsychErrorExitMsg(PsychError_system, "Failed to add trigger key to event queues."); } // Start the queue result = (*queue)->start(queue); if (kIOReturnSuccess != result){ result = (*queue)->dispose(queue); (*queue)->Release(queue); (*queue)=NULL; PsychErrorExitMsg(PsychError_system, "Failed to start event queues."); } // Watch for the trigger { IOHIDEventStruct event; while(1){ AbsoluteTime zeroTime = {0,0}; result = (*queue)->getNextEvent(queue, &event, zeroTime, 0); if(kIOReturnSuccess==result) break; PsychWaitIntervalSeconds((double)0.004); //surrender some time to other processes // If it were of interest to trigger selectively on key press or key release, // this could be evaluated by checking event.value (zero versus non-zero) // but this would put more code inside the loop // If multiple keys are registered via addElement (not the case here), the // cookie for the key responsible for the event can be obtained from // event.elementCookie } // If event.longValue is not NULL, the documentation indicates that it is up // to the caller to deallocate it. The circumstances under which a non-NULL // value would be generated are not specified. My guess is that some devices // might return a 64-bit value (e.g., a tracking device coordinate). // Keys, having only two states, shouldn't need this, but check and free to // be safe if ((event.longValueSize != 0) && (event.longValue != NULL)) free(event.longValue); // Set the time, using the same strategy as PsychTimeGlue's PsychGetPrecisionTimerSeconds // For code maintainability, it would be better if this conversion were performed // by a function in PsychTimeGlue { Nanoseconds timeNanoseconds=AbsoluteToNanoseconds(event.timestamp); UInt64 timeUInt64=UnsignedWideToUInt64(timeNanoseconds); double timeDouble=(double)timeUInt64; *timeValueOutput=timeDouble / 1000000000; } } // Clean up result = (*queue)->stop(queue); // Code from Apple sometimes removes elements from queue before disposing and sometimes does not // I can't see any reason to do so for a queue that's one line of code away from destruction // and therefore haven't result = (*queue)->dispose(queue); (*queue)->Release(queue); (*queue)=NULL; // Just in case queue is redefined as static in the future // PsychGetPrecisionTimerSeconds(timeValueOutput); // Less precise strategy than using event.timestamp return(PsychError_none); }
void PsychHIDOSKbQueueCheck(int deviceIndex) { double *hasKeyBeenDownOutput, *firstPressTimeOutput, *firstReleaseTimeOutput, *lastPressTimeOutput, *lastReleaseTimeOutput; psych_bool isFirstPressSpecified, isFirstReleaseSpecified, isLastPressSpecified, isLastReleaseSpecified; 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 check non-existent keyboard queue for deviceIndex %i! Call KbQueueCreate first!\n", deviceIndex); PsychErrorExitMsg(PsychError_user, "Invalid 'deviceIndex' specified. No queue for that device yet!"); } // Allocate output PsychAllocOutDoubleArg(1, kPsychArgOptional, &hasKeyBeenDownOutput); isFirstPressSpecified = PsychAllocOutDoubleMatArg(2, kPsychArgOptional, 1, 256, 1, &firstPressTimeOutput); isFirstReleaseSpecified = PsychAllocOutDoubleMatArg(3, kPsychArgOptional, 1, 256, 1, &firstReleaseTimeOutput); isLastPressSpecified = PsychAllocOutDoubleMatArg(4, kPsychArgOptional, 1, 256, 1, &lastPressTimeOutput); isLastReleaseSpecified = PsychAllocOutDoubleMatArg(5, kPsychArgOptional, 1, 256, 1, &lastReleaseTimeOutput); // Initialize output if(isFirstPressSpecified) memset((void*) firstPressTimeOutput, 0, sizeof(double) * 256); if(isFirstReleaseSpecified) memset((void*) firstReleaseTimeOutput, 0, sizeof(double) * 256); if(isLastPressSpecified) memset((void*) lastPressTimeOutput, 0, sizeof(double) * 256); if(isLastReleaseSpecified) memset((void*) lastReleaseTimeOutput, 0, sizeof(double) * 256); *hasKeyBeenDownOutput=0; // Compute and assign output: PsychLockMutex(&KbQueueMutex); for (i = 0; i < 256; i++) { double lastRelease = psychHIDKbQueueLastRelease[deviceIndex][i]; double lastPress = psychHIDKbQueueLastPress[deviceIndex][i]; double firstRelease = psychHIDKbQueueFirstRelease[deviceIndex][i]; double firstPress = psychHIDKbQueueFirstPress[deviceIndex][i]; if (firstPress) { *hasKeyBeenDownOutput=1; if(isFirstPressSpecified) firstPressTimeOutput[i] = firstPress; psychHIDKbQueueFirstPress[deviceIndex][i] = 0; } if (firstRelease) { if(isFirstReleaseSpecified) firstReleaseTimeOutput[i] = firstRelease; psychHIDKbQueueFirstRelease[deviceIndex][i] = 0; } if (lastPress) { if(isLastPressSpecified) lastPressTimeOutput[i] = lastPress; psychHIDKbQueueLastPress[deviceIndex][i] = 0; } if (lastRelease) { if(isLastReleaseSpecified) lastReleaseTimeOutput[i] = lastRelease; psychHIDKbQueueLastRelease[deviceIndex][i] = 0; } } PsychUnlockMutex(&KbQueueMutex); return; }
void PsychHIDOSKbQueueCheck(int deviceIndex) { double *hasKeyBeenDownOutput, *firstPressTimeOutput, *firstReleaseTimeOutput, *lastPressTimeOutput, *lastReleaseTimeOutput; psych_bool isFirstPressSpecified, isFirstReleaseSpecified, isLastPressSpecified, isLastReleaseSpecified; int i; // Get true keyboardqueue index assigned to deviceIndex from original user provided deviceIndex: deviceIndex = PsychHIDOSGetKbQueueDevice(deviceIndex, NULL); // Does Keyboard queue for this deviceIndex already exist? if (NULL == psychHIDKbQueueFirstPress[deviceIndex]) { // No. Bad bad... printf("PsychHID-ERROR: Tried to check 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!"); } // Allocate output PsychAllocOutDoubleArg(1, FALSE, &hasKeyBeenDownOutput); isFirstPressSpecified = PsychAllocOutDoubleMatArg(2, FALSE, 1, 256, 1, &firstPressTimeOutput); isFirstReleaseSpecified = PsychAllocOutDoubleMatArg(3, FALSE, 1, 256, 1, &firstReleaseTimeOutput); isLastPressSpecified = PsychAllocOutDoubleMatArg(4, FALSE, 1, 256, 1, &lastPressTimeOutput); isLastReleaseSpecified = PsychAllocOutDoubleMatArg(5, FALSE, 1, 256, 1, &lastReleaseTimeOutput); // Initialize output if(isFirstPressSpecified) memset((void*) firstPressTimeOutput, 0, sizeof(double) * 256); if(isFirstReleaseSpecified) memset((void*) firstReleaseTimeOutput, 0, sizeof(double) * 256); if(isLastPressSpecified) memset((void*) lastPressTimeOutput, 0, sizeof(double) * 256); if(isLastReleaseSpecified) memset((void*) lastReleaseTimeOutput, 0, sizeof(double) * 256); *hasKeyBeenDownOutput=0; // Compute and assign output: PsychLockMutex(&KbQueueMutex); for (i = 0; i < 256; i++) { double lastRelease = psychHIDKbQueueLastRelease[deviceIndex][i]; double lastPress = psychHIDKbQueueLastPress[deviceIndex][i]; double firstRelease = psychHIDKbQueueFirstRelease[deviceIndex][i]; double firstPress = psychHIDKbQueueFirstPress[deviceIndex][i]; if (firstPress) { *hasKeyBeenDownOutput=1; if(isFirstPressSpecified) firstPressTimeOutput[i] = firstPress; psychHIDKbQueueFirstPress[deviceIndex][i] = 0; } if (firstRelease) { if(isFirstReleaseSpecified) firstReleaseTimeOutput[i] = firstRelease; psychHIDKbQueueFirstRelease[deviceIndex][i] = 0; } if (lastPress) { if(isLastPressSpecified) lastPressTimeOutput[i] = lastPress; psychHIDKbQueueLastPress[deviceIndex][i] = 0; } if (lastRelease) { if(isLastReleaseSpecified) lastReleaseTimeOutput[i] = lastRelease; psychHIDKbQueueLastRelease[deviceIndex][i] = 0; } } PsychUnlockMutex(&KbQueueMutex); return; }
PsychError PsychHIDOSKbCheck(int deviceIndex, double* scanList) { pRecDevice deviceRecord; pRecElement currentElement, lastElement = NULL; int i, debuglevel = 0; static int numDeviceIndices = -1; int numDeviceUsages=NUMDEVICEUSAGES; long KbDeviceUsagePages[NUMDEVICEUSAGES]= {kHIDPage_GenericDesktop, kHIDPage_GenericDesktop, kHIDPage_GenericDesktop, kHIDPage_GenericDesktop, kHIDPage_GenericDesktop, kHIDPage_GenericDesktop, kHIDPage_GenericDesktop}; long KbDeviceUsages[NUMDEVICEUSAGES]={kHIDUsage_GD_Keyboard, kHIDUsage_GD_Keypad, kHIDUsage_GD_Mouse, kHIDUsage_GD_Pointer, kHIDUsage_GD_Joystick, kHIDUsage_GD_GamePad, kHIDUsage_GD_MultiAxisController}; static int deviceIndices[PSYCH_HID_MAX_KEYBOARD_DEVICES]; static pRecDevice deviceRecords[PSYCH_HID_MAX_KEYBOARD_DEVICES]; psych_bool isDeviceSpecified, foundUserSpecifiedDevice; double *timeValueOutput, *isKeyDownOutput, *keyArrayOutput; int m, n, p, nout; double dummyKeyDown; double dummykeyArrayOutput[256]; uint32_t usage, usagePage; int value; // We query keyboard and keypad devices only on first invocation, then cache and recycle the data: if (numDeviceIndices == -1) { PsychHIDVerifyInit(); PsychHIDGetDeviceListByUsages(numDeviceUsages, KbDeviceUsagePages, KbDeviceUsages, &numDeviceIndices, deviceIndices, deviceRecords); } // Choose the device index and its record isDeviceSpecified = (deviceIndex != INT_MAX); if(isDeviceSpecified){ //make sure that the device number provided by the user is really a keyboard or keypad. if (deviceIndex < 0) { debuglevel = 1; deviceIndex = -deviceIndex; } for(i=0;i<numDeviceIndices;i++){ if ((foundUserSpecifiedDevice=(deviceIndices[i]==deviceIndex))) break; } if(!foundUserSpecifiedDevice) PsychErrorExitMsg(PsychError_user, "Specified device number is not a keyboard or keypad device."); } else { // set the keyboard or keypad device to be the first keyboard device or, if no keyboard, the first keypad i=0; if(numDeviceIndices==0) PsychErrorExitMsg(PsychError_user, "No keyboard or keypad devices detected."); else{ deviceIndex=deviceIndices[i]; } } deviceRecord=deviceRecords[i]; // Allocate and init out return arguments. // Either alloc out the arguments, or redirect to // internal dummy variables. This to avoid mxMalloc() call overhead // inside the PsychAllocOutXXX() routines: nout = PsychGetNumNamedOutputArgs(); // keyDown flag: if (nout >= 1) { PsychAllocOutDoubleArg(1, FALSE, &isKeyDownOutput); } else { isKeyDownOutput = &dummyKeyDown; } *isKeyDownOutput= (double) FALSE; // key state vector: if (nout >= 3) { PsychAllocOutDoubleMatArg(3, FALSE, 1, 256, 1, &keyArrayOutput); } else { keyArrayOutput = &dummykeyArrayOutput[0]; } memset((void*) keyArrayOutput, 0, sizeof(double) * 256); // Query timestamp: if (nout >= 2) { PsychAllocOutDoubleArg(2, FALSE, &timeValueOutput); // Get query timestamp: PsychGetPrecisionTimerSeconds(timeValueOutput); } // Make sure our keyboard query mechanism is not blocked for security reasons, e.g., // secure password entry field active in another process, i.e., EnableSecureEventInput() active. if (PsychHIDWarnInputDisabled("PsychHID('KbCheck')")) return(PsychError_none); //step through the elements of the device. Set flags in the return array for down keys. for(currentElement=HIDGetFirstDeviceElement(deviceRecord, kHIDElementTypeInput | kHIDElementTypeCollection); (currentElement != NULL) && (currentElement != lastElement); currentElement=HIDGetNextDeviceElement(currentElement, kHIDElementTypeInput | kHIDElementTypeCollection)) { // Keep track of last queried element: lastElement = currentElement; #ifndef __LP64__ usage = currentElement->usage; usagePage = currentElement->usagePage; #else usage = IOHIDElementGetUsage(currentElement); usagePage = IOHIDElementGetUsagePage(currentElement); // printf("PTB-DEBUG: [KbCheck]: 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) && ( (scanList == NULL) || (scanList[usage - 1] > 0) )) { value = (int) IOHIDElement_GetValue(tIOHIDElementRef, kIOHIDValueScaleTypePhysical); if (debuglevel > 0) printf("PTB-DEBUG: [KbCheck]: usage: %x value: %d \n", usage, value); keyArrayOutput[usage - 1] = (value || (int) keyArrayOutput[usage - 1]); *isKeyDownOutput = keyArrayOutput[usage - 1] || *isKeyDownOutput; } } } // Done with this currentElement, which was a collection of buttons/keys. // Iterate to next currentElement: continue; } #endif // Classic path, or 64-Bit path for non-collection elements: if(((usagePage == kHIDPage_KeyboardOrKeypad) || (usagePage == kHIDPage_Button)) && (usage <= 256) && (usage >= 1) && ( (scanList == NULL) || (scanList[usage - 1] > 0) ) ) { #ifndef __LP64__ value = (int) HIDGetElementValue(deviceRecord, currentElement); #else value = (int) IOHIDElement_GetValue(currentElement, kIOHIDValueScaleTypePhysical); #endif if (debuglevel > 0) printf("PTB-DEBUG: [KbCheck]: usage: %x value: %d \n", usage, value); keyArrayOutput[usage - 1]=(value || (int) keyArrayOutput[usage - 1]); *isKeyDownOutput= keyArrayOutput[usage - 1] || *isKeyDownOutput; } } return(PsychError_none); }
PsychError PSYCHHIDKbCheck(void) { pRecDevice deviceRecord; pRecElement currentElement; int i, deviceIndex, numDeviceIndices; long KeysUsagePage=7; long KbDeviceUsagePage= 1, KbDeviceUsage=6; int deviceIndices[PSYCH_HID_MAX_KEYBOARD_DEVICES]; pRecDevice deviceRecords[PSYCH_HID_MAX_KEYBOARD_DEVICES]; boolean isDeviceSpecified, foundUserSpecifiedDevice, isKeyArgPresent, isTimeArgPresent; double *timeValueOutput, *isKeyDownOutput; PsychNativeBooleanType *keyArrayOutput; PsychPushHelp(useString, synopsisString, seeAlsoString); if(PsychIsGiveHelp()){PsychGiveHelp();return(PsychError_none);}; PsychErrorExit(PsychCapNumOutputArgs(3)); PsychErrorExit(PsychCapNumInputArgs(1)); //Specifies the number of the keyboard to scan. PsychHIDVerifyInit(); //Choose the device index and its record PsychHIDGetDeviceListByUsage(KbDeviceUsagePage, KbDeviceUsage, &numDeviceIndices, deviceIndices, deviceRecords); isDeviceSpecified=PsychCopyInIntegerArg(1, FALSE, &deviceIndex); if(isDeviceSpecified){ //make sure that the device number provided by the user is really a keyboard. for(i=0;i<numDeviceIndices;i++){ if(foundUserSpecifiedDevice=(deviceIndices[i]==deviceIndex)) break; } if(!foundUserSpecifiedDevice) PsychErrorExitMsg(PsychError_user, "Specified device number is not a keyboard device."); }else{ // set the keyboard device to be the first keyboard device i=0; if(numDeviceIndices==0) PsychErrorExitMsg(PsychError_user, "No keyboard devices detected."); else{ deviceIndex=deviceIndices[i]; } } deviceRecord=deviceRecords[i]; //Allocate and init out return arguments. isKeyArgPresent = PsychAllocOutBooleanMatArg(3, FALSE, 1, 256, 1, &keyArrayOutput); isTimeArgPresent = PsychAllocOutDoubleArg(2, FALSE, &timeValueOutput); PsychGetPrecisionTimerSeconds(timeValueOutput); PsychAllocOutDoubleArg(1, FALSE, &isKeyDownOutput); *isKeyDownOutput=(double)FALSE; //step through the elements of the device. Set flags in the return array for down keys. for(currentElement=HIDGetFirstDeviceElement(deviceRecord, kHIDElementTypeInput); currentElement != NULL; currentElement=HIDGetNextDeviceElement(currentElement, kHIDElementTypeInput)) { if(currentElement->usagePage==KeysUsagePage && currentElement->usage <= 256 && currentElement->usage >=1){ //printf("usage: %x value: %d \n", currentElement->usage, HIDGetElementValue(deviceRecord, currentElement)); keyArrayOutput[currentElement->usage - 1]=(PsychNativeBooleanType)(HIDGetElementValue(deviceRecord, currentElement) || keyArrayOutput[currentElement->usage - 1]); *isKeyDownOutput= keyArrayOutput[currentElement->usage - 1] || *isKeyDownOutput; } } return(PsychError_none); }