From 1454e47c755d10849f5871858ada83cb8d0942fd Mon Sep 17 00:00:00 2001 From: Drew Fisher Date: Sun, 30 Oct 2011 23:24:34 -0700 Subject: [PATCH] Make the timeouts behave as they ought. Each platform has its own API for high resolution timers, so I added an abstraction function that returns a monotonically increasing number in nanoseconds. On Windows, we use QueryPerformanceCounter() On OSX, we use mach_absolute_time() On Linux, we use clock_gettime() with CLOCK_MONOTONIC Signed-off-by: Drew Fisher --- CMakeLists.txt | 2 +- src/mono_timer.c | 52 ++++++++++++++++++++++++++++++++++++++++++++++++ src/mono_timer.h | 25 +++++++++++++++++++++++ src/touchmouse.c | 21 ++++++++++++++----- 4 files changed, 94 insertions(+), 6 deletions(-) create mode 100644 src/mono_timer.c create mode 100644 src/mono_timer.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 51853b4..a799fe4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -28,7 +28,7 @@ else() include_directories(/usr/include/libusb-1.0) list(APPEND LIBSRC hidapi/linux/hid-libusb.c) endif() -list(APPEND LIBSRC src/touchmouse.c) +list(APPEND LIBSRC src/touchmouse.c src/mono_timer.c) set(CMAKE_C_FLAGS "-Wall -ggdb") diff --git a/src/mono_timer.c b/src/mono_timer.c new file mode 100644 index 0000000..bdea504 --- /dev/null +++ b/src/mono_timer.c @@ -0,0 +1,52 @@ +/* This file is intended to make it easier to work with monotonic timers on + * multiple platforms, since there's no single API for doing so. + * + * On Windows, we use QueryPerformanceCounter and QueryPerformanceFrequency + * On OSX, we use mach_absolute_time() + * On Linux, we use clock_gettime() with CLOCK_MONOTONIC + * + * Since I find this problem worth solving somewhat permanently: + * + * I hereby release this file into the public domain. + */ + +#include "mono_timer.h" + +#ifdef _WIN32 +#include +#endif + +#ifdef __APPLE__ +#include +#endif + +#ifdef __linux__ +#include +#endif + +#ifdef _WIN32 +uint64_t mono_timer_nanos() { + LARGE_INTEGER freq; + LARGE_INTEGER count; + if (!QueryPerformanceFrequency(&freq)) return 0; + if (!QueryPerformanceCounter(&count)) return 0; + // count / freq = seconds, so count / freq * 1e9 = nanoseconds + return (uint64_t)(count.QuadPart * 1000000000 / freq.QuadPart); +} +#endif // _WIN32 +#ifdef __APPLE__ +uint64_t mono_timer_nanos() { + mach_timebase_info_data_t info; + mach_timebase_info(&info); + return mach_absolute_time() * info.numer / info.denom; +} +#endif // APPLE +#ifdef __linux__ +uint64_t mono_timer_nanos() { + struct timespec ts; + if(clock_gettime(CLOCK_MONOTONIC, &ts) != 0) return 0; + uint64_t nanos = (uint64_t)ts.tv_sec * 1000000000; + nanos += ts.tv_nsec; + return nanos; +} +#endif // __linux__ diff --git a/src/mono_timer.h b/src/mono_timer.h new file mode 100644 index 0000000..cc539b0 --- /dev/null +++ b/src/mono_timer.h @@ -0,0 +1,25 @@ +/* This file is intended to make it easier to work with monotonic timers on + * multiple platforms, since there's no single API for doing so. + * + * On Windows, we use QueryPerformanceCounter and QueryPerformanceFrequency + * On OSX, we use mach_absolute_time() + * On Linux, we use clock_gettime() with CLOCK_MONOTONIC + * Other platforms are currently unsupported. + * + * Since I find this problem worth solving somewhat permanently: + * + * I hereby release this file into the public domain. + */ +#ifndef __MONO_TIMER_H__ +#define __MONO_TIMER_H__ + +#include + +// Returns the current time in nanoseconds since an arbitrary epoch. This +// value is supposed to be stable and increase monotonically when called from +// the same process, up to the maximum guarantee that can be provided by the +// underlying OS. +// Returns 0 on error. +uint64_t mono_timer_nanos(); + +#endif // __MONO_TIMER_H__ diff --git a/src/touchmouse.c b/src/touchmouse.c index 17108bb..30c0189 100644 --- a/src/touchmouse.c +++ b/src/touchmouse.c @@ -31,6 +31,7 @@ #include #include "touchmouse-internal.h" +#include "mono_timer.h" #pragma pack(1) // The USB HID reports that contain our data are always 32 bytes, with the @@ -335,12 +336,21 @@ int touchmouse_set_device_userdata(touchmouse_device *dev, void *userdata) int touchmouse_process_events_timeout(touchmouse_device *dev, int milliseconds) { unsigned char data[256] = {}; int res; - int millisleft = milliseconds; uint8_t first_timestamp_read = 0; uint8_t last_timestamp = 0; - while(millisleft) { // TODO: make this TIME_NOT_YET_EXPIRED - res = hid_read_timeout(dev->dev, data, 255, millisleft); // TODO: fix this to be - // the right number of milliseconds + uint64_t deadline; + if(milliseconds == -1) { + deadline = (uint64_t)(-1); + } else { + deadline = mono_timer_nanos() + (milliseconds * 1000000); + } + uint64_t nanos = mono_timer_nanos(); + if (nanos == 0 || deadline == 0) { + fprintf(stderr, "timer function returned an error, erroring out since we have no timer\n"); + return -1; + } + do { + res = hid_read_timeout(dev->dev, data, 255, (deadline - nanos) / 1000000 ); if (res < 0 ) { fprintf(stderr, "hid_read() failed: %d\n", res); return -1; @@ -405,7 +415,8 @@ int touchmouse_process_events_timeout(touchmouse_device *dev, int milliseconds) } } } - } + nanos = mono_timer_nanos(); + } while(nanos < deadline); return 0; } -- 2.39.2