void PsychGetPrecisionTimerSeconds(double *secs) { double timeDouble; AbsoluteTime timeAbsTime; Nanoseconds timeNanoseconds; UInt64 timeUInt64; //Get the time in an AbsolulteTime structure which expresses time as a ratio. timeAbsTime=UpTime(); //Convert the AbsoluteTime structure to nanoseconds stored in an UnsignedWide. //UnsignedWide is an opaque type. Depending on the compiler it is //implemented either as structure holding holding 32-bit high and low parts //or as a native long long. timeNanoseconds=AbsoluteToNanoseconds(timeAbsTime); //convert the opaque unsigned wide type into a UInt64. Variant forms //of the UnsignedWide type is why we need to use the UnsignedWideToUInt64() //macro instead of a cast. If GCC then UnsignedWideToUInt64 resolves to a type recast. timeUInt64=UnsignedWideToUInt64(timeNanoseconds); //cast nanoseconds in unsigned wide type to a double timeDouble=(double)timeUInt64; //divide down to seconds *secs= timeDouble / 1000000000; }
uint64_t uv_hrtime() { uint64_t time; Nanoseconds enano; time = mach_absolute_time(); enano = AbsoluteToNanoseconds(*(AbsoluteTime *)&time); return (*(uint64_t *)&enano); }
clockmark_t ktiming_getmark() { #ifdef __APPLE__ const uint64_t now = mach_absolute_time(); const Nanoseconds now_nanoseconds = AbsoluteToNanoseconds(*(AbsoluteTime *)&now); return *(uint64_t *)&now_nanoseconds; #else struct timespec now; uint64_t now_nanoseconds; int stat = clock_gettime(KTIMING_CLOCK_ID, &now); if (stat != 0) { // Whoops, we couldn't get hold of the clock. If we're on a // platform that supports it, we try again with // CLOCK_MONOTONIC. #ifndef __CYGWIN__ stat = clock_gettime(CLOCK_MONOTONIC , &now); #endif if (stat != 0) { // Wow, we /still/ couldn't get hold of the clock. // Better give up; without a clock, we can't give back // meaningful statistics. perror("ktiming_getmark()"); exit(-1); } } now_nanoseconds = now.tv_nsec; now_nanoseconds += ((uint64_t)now.tv_sec) * 1000 * 1000 * 1000; return now_nanoseconds; #endif }
double currentTime(){ #if OCCA_OS == LINUX_OS timespec ct; clock_gettime(CLOCK_MONOTONIC, &ct); return (double) (ct.tv_sec + (1.0e-9 * ct.tv_nsec)); #elif OCCA_OS == OSX_OS uint64_t ct; ct = mach_absolute_time(); const Nanoseconds ct2 = AbsoluteToNanoseconds(*(AbsoluteTime *) &ct); return ((double) 1.0e-9) * ((double) ( *((uint64_t*) &ct2) )); #elif OCCA_OS == WINDOWS_OS LARGE_INTEGER timestamp, timerfreq; QueryPerformanceFrequency(&timerfreq); QueryPerformanceCounter(×tamp); return ((double) timestamp.QuadPart) / ((double) timerfreq.QuadPart); #endif }
uint64_t os_gettime_ns(void) { uint64_t t = mach_absolute_time(); #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wdeprecated-declarations" Nanoseconds nano = AbsoluteToNanoseconds(*(AbsoluteTime*) &t); #pragma clang diagnostic pop return *(uint64_t*) &nano; }
//A quick little function for rapidly determining an accurate time double GetCurrentTime( void ) { union { Nanoseconds ns; UInt64 u64; }time; time.ns = AbsoluteToNanoseconds( UpTime() ); return double( time.u64 ) * 1e-9; }
uint64_t time_GetMilliseconds() { #if defined(HAVE_SDL) || defined(WINDOWS) #ifdef HAVE_SDL uint64_t i = SDL_GetTicks(); #else uint64_t i = GetTickCount(); #endif i += timeoffset; // add an offset we might have set if (i > oldtime) { // normal time difference oldtime = i; } else { // we wrapped around. set a time offset to avoid the wrap timeoffset = (oldtime - i) + 1; i += timeoffset; } #else // ifdef HAVE_SDL #ifdef MAC // MAC NO-SDL TIME if (!startinitialised) { lastunixtime = mach_absolute_time(); startinitialised = 1; return 0; } uint64_t elapsed = mach_absolute_time() - lastunixtime; Nanoseconds ns = AbsoluteToNanoseconds(*(AbsoluteTime*)&elapsed); return *(uint64_t)&ns; #else // ifdef MAC // UNIX/LINUX NO-SDL TIME if (!startinitialised) { clock_gettime(CLOCK_MONOTONIC, &lastunixtime); startinitialised = 1; lasttimestamp = 0; return 0; } struct timespec current; clock_gettime(CLOCK_MONOTONIC, ¤t); int64_t seconds = current.tv_sec - lastunixtime.tv_sec; int64_t nseconds = current.tv_nsec - lastunixtime.tv_nsec; uint64_t i = (uint64_t)((double)((seconds) * 1000 + nseconds / 1000000.0) + 0.5); // to avoid accuracy issues in our continuously increased timestamp due // to all the rounding business happening above, we only update our old // reference check time (lastunixtime/lasttimestamp) once a full second // has passed: if (i > 1000) { i += lasttimestamp; lasttimestamp = i; memcpy(&lastunixtime, ¤t, sizeof(struct timespec)); } else { i += lasttimestamp; } #endif // ifdef MAC #endif // ifdef HAVE_SDL return i; }
MWTime StandardClock::getCurrentTimeNS() { // first things first /*uint64_t now = mach_absolute_time(); return (MWTime)(now * tTBI.numer / tTBI.denom);*/ AbsoluteTime uptime; Nanoseconds nano; uptime = UpTime(); nano = AbsoluteToNanoseconds(uptime); return (MWTime)(UnsignedWideToUInt64( nano ) - baseTime); }
struct timeval *pa_rtclock_get(struct timeval *tv) { #if defined(OS_IS_DARWIN) uint64_t val, abs_time = mach_absolute_time(); Nanoseconds nanos; nanos = AbsoluteToNanoseconds(*(AbsoluteTime *) &abs_time); val = *(uint64_t *) &nanos; tv->tv_sec = val / PA_NSEC_PER_SEC; tv->tv_usec = (val % PA_NSEC_PER_SEC) / PA_NSEC_PER_USEC; return tv; #elif defined(HAVE_CLOCK_GETTIME) struct timespec ts; #ifdef CLOCK_MONOTONIC /* No locking or atomic ops for no_monotonic here */ static pa_bool_t no_monotonic = FALSE; if (!no_monotonic) if (clock_gettime(CLOCK_MONOTONIC, &ts) < 0) no_monotonic = TRUE; if (no_monotonic) #endif /* CLOCK_MONOTONIC */ pa_assert_se(clock_gettime(CLOCK_REALTIME, &ts) == 0); pa_assert(tv); tv->tv_sec = ts.tv_sec; tv->tv_usec = ts.tv_nsec / PA_NSEC_PER_USEC; return tv; #elif defined(OS_IS_WIN32) if (counter_freq > 0) { LARGE_INTEGER count; pa_assert_se(QueryPerformanceCounter(&count)); tv->tv_sec = count.QuadPart / counter_freq; tv->tv_usec = (count.QuadPart % counter_freq) * PA_USEC_PER_SEC / counter_freq; return tv; } #endif /* HAVE_CLOCK_GETTIME */ return pa_gettimeofday(tv); }
uint64_t getcurrenttime(void) { // in nanoseconds from system boot #ifdef __MACH__ uint64_t absolute = mach_absolute_time(); Nanoseconds nano = AbsoluteToNanoseconds(*(AbsoluteTime *)&absolute); return *(uint64_t *)&nano; #elif _POSIX_TIMERS struct timespec time; clock_gettime(CLOCK_MONOTONIC, &time); return ((uint64_t)time.tv_sec * 1000000000) + ((uint64_t)time.tv_nsec); #else struct timeval tp; gettimeofday(&tp, NULL); return ((uint64_t)tp.tv_sec * 1000000000) + ((uint64_t)tp.tv_usec * 1000); #endif }
DWORDLONG rdtsc() { DWORDLONG temp; AbsoluteTime now; Nanoseconds s; now = UpTime(); s = AbsoluteToNanoseconds(now); temp = s.hi; temp <<= 32; temp += s.lo; // temp contains timestamp in nanosecs // temp * processor_speed_to_nsecs = timestamp in cpu cycles return (DWORDLONG) (temp * processor_speed_to_nsecs); }
uint64_t uv_hrtime() { uint64_t now; Nanoseconds enano; uint64_t enano64; now = mach_absolute_time(); AbsoluteTime at; if (sizeof at < sizeof now) { memset(&at, 0, sizeof at); } (void) sizeof(char[sizeof at <= sizeof now ? 1 : -1]); memcpy(&at, &now, sizeof at); enano = AbsoluteToNanoseconds(at); (void) sizeof(char[sizeof enano <= sizeof enano64 ? 1 : -1]); memcpy(&enano64, &enano, sizeof enano); return enano64; }
double getTicks() { //return 40; #ifdef _WIN32 return (double) GetTickCount(); #else Nanoseconds nano = AbsoluteToNanoseconds( UpTime() ); // if you want that in (floating point) seconds: double seconds = ((double) UnsignedWideToUInt64( nano )) * 1e-9; return seconds * 1000.0; #endif }
nglTime::nglTime() { #ifdef _POSIX_WORLD_ struct timeval tv; gettimeofday (&tv, NULL); mValue = (double)tv.tv_sec + ((double)tv.tv_usec / 1000000.0); #endif // _POSIX_WORLD_ #if macintosh Nanoseconds t = AbsoluteToNanoseconds(UpTime()); mValue = ((double)UnsignedWideToUInt64(t)) / 1000000.0; #endif // macintosh #ifdef _WIN32_ mValue = GetTime(); #endif // _WIN32_ }
static void load_framework(const char *path) { char *error; uint64_t elapsed, start; Nanoseconds elapsedNano; element_count = 0; start = mach_absolute_time(); if (!bs_parse(path, 0, parse_cb, NULL, &error)) { printf("*** error : %s\n", error); free(error); } elapsed = mach_absolute_time() - start; elapsedNano = AbsoluteToNanoseconds( *(AbsoluteTime *) &elapsed ); printf("parsed %d elements, took %lld ns\n", element_count, *(uint64_t *)&elapsedNano); }
/* ================ Sys_Milliseconds ================ */ int Sys_Milliseconds (void) { #if 0 int c; c = clock(); // FIXME, make more accurate return c*1000/60; #else AbsoluteTime t; Nanoseconds nano; double doub; #define kTwoPower32 (4294967296.0) /* 2^32 */ t = UpTime(); nano = AbsoluteToNanoseconds( t ); doub = (((double) nano.hi) * kTwoPower32) + nano.lo; return doub * 0.000001; #endif }
int RDebug::stopTimer(int id, const QString& msg) { #ifdef Q_OS_MAC Nanoseconds elapsedNano; uint64_t end = mach_absolute_time(); uint64_t elapsed = end - timerMac[id]; elapsedNano = AbsoluteToNanoseconds( *(AbsoluteTime *) &elapsed ); int t = (unsigned int)((* (uint64_t *) &elapsedNano) / 1000000); timerMac.remove(id); #else int t = timer[id].elapsed(); timer.remove(id); #endif /* va_list varg; va_start(varg, format); fprintf(stream, "TIMER: %d ms ", t); printV(format, varg); va_end(varg); */ qDebug() << "TIMER: " << t << "ms - " << msg; return t; }
clockmark_t ktiming_getmark(void) { #ifdef __APPLE__ uint64_t abs_time = mach_absolute_time(); Nanoseconds nanos = AbsoluteToNanoseconds(*(AbsoluteTime*)&abs_time); return *(uint64_t*)&nanos; #else struct timespec temp; uint64_t nanos; /* Try the default clock first, if it fails, try again with CLOCK_MONOTONIC */ int stat = clock_gettime(KTIMING_CLOCK_ID, &temp); if (stat != 0) { stat = clock_gettime(CLOCK_MONOTONIC, &temp); if (stat != 0) { perror("ktiming_getmark()"); exit(-1); } } nanos = temp.tv_nsec; nanos += ((uint64_t) temp.tv_sec) * 1000 * 1000 * 1000; return nanos; #endif }
bigtime_t ZTicks::sNow() { Nanoseconds theTicks = AbsoluteToNanoseconds(UpTime()); return *reinterpret_cast<uint64*>(&theTicks) / 1000; }
unsigned long long GetTimeSinceBootInMilliseconds() { UnsignedWide uw = AbsoluteToNanoseconds(UpTime()); return ((((unsigned long long)uw.hi)<<32)|(uw.lo))/1000000; }
//--------------------------------------------------------------------------- // wxJoystickThread::HIDCallback (static) // // Callback for the native HID device when it recieves input. // // This is where the REAL dirty work gets done. // // 1) Loops through each event the queue has recieved // 2) First, checks if the thread that is running the loop for // the polling has ended - if so it breaks out // 3) Next, it checks if there was an error getting this event from // the HID queue, if there was, it logs an error and returns // 4) Now it does the real dirty work by getting the button states // from cookies 0-40 and axes positions/states from cookies 40-50 // in the native HID device by quering cookie values. // 5) Sends the event to the polling window (if any) // 6) Gets the next event and goes back to (1) //--------------------------------------------------------------------------- /*static*/ void wxJoystickThread::HIDCallback(void* target, IOReturn res, void* context, void* sender) { IOHIDEventStruct hidevent; AbsoluteTime bogustime = {0,0}; IOReturn ret; wxJoystickThread* pThis = (wxJoystickThread*) context; wxHIDJoystick* m_hid = pThis->m_hid; //Get the "first" event from the queue //bogustime tells it we don't care at what time to start //where it gets the next from ret = (*m_hid->GetQueue())->getNextEvent(m_hid->GetQueue(), &hidevent, bogustime, 0); while (ret != kIOReturnUnderrun) { if (pThis->TestDestroy()) break; if(ret != kIOReturnSuccess) { wxLogSysError(wxString::Format(wxT("wxJoystick Error:[%i]"), ret)); return; } wxJoystickEvent wxevent; //Find the cookie that changed int nIndex = 0; IOHIDElementCookie* pCookies = m_hid->GetCookies(); while(nIndex < 50) { if(hidevent.elementCookie == pCookies[nIndex]) break; ++nIndex; } //debugging stuff #if 0 if(nIndex == 50) { wxLogSysError(wxString::Format(wxT("wxJoystick Out Of Bounds Error"))); break; } #endif //is the cookie a button? if (nIndex < 40) { if (hidevent.value) { pThis->m_buttons |= (1 << nIndex); wxevent.SetEventType(wxEVT_JOY_BUTTON_DOWN); } else { pThis->m_buttons &= ~(1 << nIndex); wxevent.SetEventType(wxEVT_JOY_BUTTON_UP); } wxevent.SetButtonChange(nIndex+1); } else if (nIndex == wxJS_AXIS_X) { pThis->m_lastposition.x = hidevent.value; wxevent.SetEventType(wxEVT_JOY_MOVE); pThis->m_axe[0] = hidevent.value; } else if (nIndex == wxJS_AXIS_Y) { pThis->m_lastposition.y = hidevent.value; wxevent.SetEventType(wxEVT_JOY_MOVE); pThis->m_axe[1] = hidevent.value; } else if (nIndex == wxJS_AXIS_Z) { wxevent.SetEventType(wxEVT_JOY_ZMOVE); pThis->m_axe[2] = hidevent.value; } else wxevent.SetEventType(wxEVT_JOY_MOVE); Nanoseconds timestamp = AbsoluteToNanoseconds(hidevent.timestamp); wxULongLong llTime(timestamp.hi, timestamp.lo); llTime /= 1000000; wxevent.SetTimestamp(llTime.GetValue()); wxevent.SetJoystick(pThis->m_joystick); wxevent.SetButtonState(pThis->m_buttons); wxevent.SetPosition(pThis->m_lastposition); wxevent.SetZPosition(pThis->m_axe[2]); wxevent.SetEventObject(pThis->m_catchwin); if (pThis->m_catchwin) pThis->m_catchwin->AddPendingEvent(wxevent); ret = (*m_hid->GetQueue())->getNextEvent(m_hid->GetQueue(), &hidevent, bogustime, 0); } }
static inline double stopwatch_ClockCycleCounter_get_time_s () { const Nanoseconds ns = AbsoluteToNanoseconds (UpTime ()); return (ns.hi * 4294967296e-9 + ns.lo * 1e-9); }
inline float ConvertTimeDifferenceNowToSec( timerStruct *now, timerStruct *last ) { uint64_t elapsed = mach_absolute_time() - *begin; Nanoseconds elapsedNano = AbsoluteToNanoseconds( *(AbsoluteTime*)&elapsed ); return float(*(uint64_t*)&elapsedNano) * (1e-9); }
inline float ConvertTimeDifferenceToSec( timerStruct *end, timerStruct *begin ) { uint64_t elapsed = *end - *begin; Nanoseconds elapsedNano = AbsoluteToNanoseconds( *(AbsoluteTime*)&elapsed ); return float(*(uint64_t*)&elapsedNano) * (1e-9); }
// Get local time as milliseconds since 00:00:00, Jan 1st 1970 wxLongLong wxGetLocalTimeMillis() { wxLongLong val = 1000l; // If possible, use a function which avoids conversions from // broken-up time structures to milliseconds #if defined(__WXPALMOS__) DateTimeType thenst; thenst.second = 0; thenst.minute = 0; thenst.hour = 0; thenst.day = 1; thenst.month = 1; thenst.year = 1970; thenst.weekDay = 5; uint32_t now = TimGetSeconds(); uint32_t then = TimDateTimeToSeconds (&thenst); return SysTimeToMilliSecs(SysTimeInSecs(now - then)); #elif defined(__WXMSW__) && (defined(__WINE__) || defined(__MWERKS__)) // This should probably be the way all WXMSW compilers should do it // Go direct to the OS for time SYSTEMTIME thenst = { 1970, 1, 4, 1, 0, 0, 0, 0 }; // 00:00:00 Jan 1st 1970 FILETIME thenft; SystemTimeToFileTime( &thenst, &thenft ); wxLongLong then( thenft.dwHighDateTime, thenft.dwLowDateTime ); // time in 100 nanoseconds SYSTEMTIME nowst; GetLocalTime( &nowst ); FILETIME nowft; SystemTimeToFileTime( &nowst, &nowft ); wxLongLong now( nowft.dwHighDateTime, nowft.dwLowDateTime ); // time in 100 nanoseconds return ( now - then ) / 10000.0; // time from 00:00:00 Jan 1st 1970 to now in milliseconds #elif defined(HAVE_GETTIMEOFDAY) struct timeval tp; if ( wxGetTimeOfDay(&tp, (struct timezone *)NULL) != -1 ) { val *= tp.tv_sec; return (val + (tp.tv_usec / 1000)); } else { wxLogError(_("wxGetTimeOfDay failed.")); return 0; } #elif defined(HAVE_FTIME) struct timeb tp; // ftime() is void and not int in some mingw32 headers, so don't // test the return code (well, it shouldn't fail anyhow...) (void)::ftime(&tp); val *= tp.time; return (val + tp.millitm); #elif defined(__WXMAC__) static UInt64 gMilliAtStart = 0; Nanoseconds upTime = AbsoluteToNanoseconds( UpTime() ); if ( gMilliAtStart == 0 ) { time_t start = time(NULL); gMilliAtStart = ((UInt64) start) * 1000000L; gMilliAtStart -= upTime.lo / 1000 ; gMilliAtStart -= ( ( (UInt64) upTime.hi ) << 32 ) / (1000 * 1000); } UInt64 millival = gMilliAtStart; millival += upTime.lo / (1000 * 1000); millival += ( ( (UInt64) upTime.hi ) << 32 ) / (1000 * 1000); val = millival; return val; #else // no gettimeofday() nor ftime() // We use wxGetLocalTime() to get the seconds since // 00:00:00 Jan 1st 1970 and then whatever is available // to get millisecond resolution. // // NOTE that this might lead to a problem if the clocks // use different sources, so this approach should be // avoided where possible. val *= wxGetLocalTime(); // GRG: This will go soon as all WIN32 seem to have ftime // JACS: unfortunately not. WinCE doesn't have it. #if defined (__WIN32__) // If your platform/compiler needs to use two different functions // to get ms resolution, please do NOT just shut off these warnings, // drop me a line instead at <*****@*****.**> // FIXME #ifndef __WXWINCE__ #warning "Possible clock skew bug in wxGetLocalTimeMillis()!" #endif SYSTEMTIME st; ::GetLocalTime(&st); val += st.wMilliseconds; #else // !Win32 // If your platform/compiler does not support ms resolution please // do NOT just shut off these warnings, drop me a line instead at // <*****@*****.**> #if defined(__VISUALC__) || defined (__WATCOMC__) #pragma message("wxStopWatch will be up to second resolution!") #elif defined(__BORLANDC__) #pragma message "wxStopWatch will be up to second resolution!" #else #warning "wxStopWatch will be up to second resolution!" #endif // compiler #endif return val; #endif // time functions }
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); }