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;  
}
Esempio n. 2
0
uint64_t uv_hrtime() {
  uint64_t time;
  Nanoseconds enano;
  time = mach_absolute_time(); 
  enano = AbsoluteToNanoseconds(*(AbsoluteTime *)&time);
  return (*(uint64_t *)&enano);
}
Esempio n. 3
0
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
}
Esempio n. 4
0
  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(&timestamp);

    return ((double) timestamp.QuadPart) / ((double) timerfreq.QuadPart);
#endif
  }
Esempio n. 5
0
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;
}
Esempio n. 6
0
//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;
}
Esempio n. 7
0
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, &current);
    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, &current, sizeof(struct timespec));
    } else {
        i += lasttimestamp;
    }
#endif // ifdef MAC
#endif // ifdef HAVE_SDL
    return i;
}
Esempio n. 8
0
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);
}
Esempio n. 9
0
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);
}
Esempio n. 10
0
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
}
Esempio n. 11
0
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);
}
Esempio n. 12
0
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;
}
Esempio n. 13
0
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

}
Esempio n. 14
0
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_
}
Esempio n. 15
0
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);
}
Esempio n. 16
0
/*
================
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
}
Esempio n. 17
0
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
}
Esempio n. 19
0
bigtime_t ZTicks::sNow()
	{
	Nanoseconds theTicks = AbsoluteToNanoseconds(UpTime());
	return *reinterpret_cast<uint64*>(&theTicks) / 1000;
	}
Esempio n. 20
0
unsigned long long GetTimeSinceBootInMilliseconds()
{
	UnsignedWide uw = AbsoluteToNanoseconds(UpTime());
	return ((((unsigned long long)uw.hi)<<32)|(uw.lo))/1000000;
}
Esempio n. 21
0
//---------------------------------------------------------------------------
// 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);
}
Esempio n. 23
0
	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); }
Esempio n. 24
0
	inline float ConvertTimeDifferenceToSec( timerStruct *end, timerStruct *begin )
		{ uint64_t elapsed = *end - *begin; Nanoseconds elapsedNano = AbsoluteToNanoseconds( *(AbsoluteTime*)&elapsed );
			return float(*(uint64_t*)&elapsedNano) * (1e-9); }
Esempio n. 25
0
// 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);	
}