summaryrefslogtreecommitdiff
path: root/meme
diff options
context:
space:
mode:
authorWerner Almesberger <werner@almesberger.net>2014-09-12 14:01:13 -0300
committerWerner Almesberger <werner@almesberger.net>2014-09-12 14:01:13 -0300
commit29baa2745d7408a8bbe6088807e3467a11120855 (patch)
tree7ec7858a01791a1f133128079dcdf2e948801010 /meme
parent11871c02868983d0926ed2cb5445d447237b0d5b (diff)
downloadmisc-29baa2745d7408a8bbe6088807e3467a11120855.tar.gz
misc-29baa2745d7408a8bbe6088807e3467a11120855.tar.bz2
misc-29baa2745d7408a8bbe6088807e3467a11120855.zip
meme/gui.c: add idle timer mechanism and update cursor only when idle
Since motion updated come in faster than we can update the cursor (and this will get worse when we add projections), we need to rate-limit updates.
Diffstat (limited to 'meme')
-rw-r--r--meme/gui.c131
1 files changed, 129 insertions, 2 deletions
diff --git a/meme/gui.c b/meme/gui.c
index 49ed887..8b1476f 100644
--- a/meme/gui.c
+++ b/meme/gui.c
@@ -403,20 +403,147 @@ static void extrema(void)
}
+/* ----- Timers ------------------------------------------------------------ */
+
+
+#include <time.h>
+#include <sys/time.h>
+
+
+static struct timer {
+ struct timeval expires;
+ struct timer *next;
+ void (*fn)(void *user);
+ void *user;
+} *timers = NULL;
+
+
+static void cancel_timer(const struct timer *t)
+{
+ struct timer **anchor;
+
+ for (anchor = &timers; *anchor; anchor = &(*anchor)->next)
+ if (*anchor == t) {
+ *anchor = t->next;
+ return;
+ }
+}
+
+
+static int timeval_cmp(const struct timeval *a, const struct timeval *b)
+{
+ if (a->tv_sec < b->tv_sec)
+ return -1;
+ if (a->tv_sec > b->tv_sec)
+ return 1;
+ if (a->tv_usec < b->tv_usec)
+ return -1;
+ if (a->tv_usec > b->tv_usec)
+ return 1;
+ return 0;
+}
+
+
+static void start_timer(struct timer *t,
+ void (*fn)(void *user), void *user, unsigned delta_ms)
+{
+ struct timer **anchor;
+
+ cancel_timer(t);
+ gettimeofday(&t->expires, NULL);
+
+ t->expires.tv_usec += 1000*delta_ms;
+ t->expires.tv_sec -= t->expires.tv_usec/1000000;
+ t->expires.tv_usec %= 1000000;
+
+ t->fn = fn;
+ t->user = user;
+
+ for (anchor = &timers;
+ *anchor && timeval_cmp(&t->expires, &(*anchor)->expires) >= 0;
+ anchor = &(*anchor)->next);
+ t->next = *anchor;
+ *anchor = t;
+}
+
+
+static void run_timers(void)
+{
+ struct timeval now;
+ struct timer *t;
+
+ while (timers) {
+ gettimeofday(&now, NULL);
+ if (timeval_cmp(&now, &timers->expires) < 0)
+ return;
+ t = timers;
+ timers = t->next;
+ t->fn(t->user);
+ }
+}
+
+
+/* ----- Event polling ----------------------------------------------------- */
+
+
+static void sdl_event(SDL_Event *ev)
+{
+ struct timespec ts = {
+ .tv_sec = 0,
+ .tv_nsec = 20*1000*1000, // 20 ms
+ };
+
+ while (1) {
+ if (!timers) {
+ SDL_WaitEvent(ev);
+ return;
+ }
+
+ if (SDL_PollEvent(ev))
+ return;
+ run_timers();
+ nanosleep(&ts, NULL);
+ }
+}
+
+
/* ----- Main loop --------------------------------------------------------- */
+static struct motion {
+ SDL_Surface *s;
+ SDL_MouseMotionEvent event;
+} motion;
+
+static struct timer motion_timer;
+
+
+static void delayed_motion(void *user)
+{
+ struct motion *m = user;
+
+ motion_event(m->s, &m->event);
+}
+
+
static bool event_loop(SDL_Surface *s)
{
SDL_Event event;
bool redraw = 0;
+ cancel_timer(&motion_timer);
while (!redraw) {
- SDL_WaitEvent(&event);
+ sdl_event(&event);
switch (event.type) {
case SDL_MOUSEMOTION:
- motion_event(s, &event.motion);
+ if (cursor)
+ hide_cursor(s);
+ cursor = NULL;
+ motion.s = s;
+ motion.event = event.motion;
+ start_timer(&motion_timer, delayed_motion, &motion,
+ 50);
break;
case SDL_MOUSEBUTTONDOWN:
button_event(&event.button);