Something went wrong. Try again.
Live STIB-MIVB departures on the Pebble Time 2. It covers the bus, tram, and metro network of Brussels
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223#include "touch.h"
#define TOUCH_ENTRY_MAX 4#define TOUCH_HOLD_MS 500#define TOUCH_HOLD_SLOP_PX 12#define TOUCH_LOCK_SLOP_PX 8#define TOUCH_TAP_MS 300#define TOUCH_TAP_SLOP_PX 12#define TOUCH_SWIPE_MIN_PX 50#define TOUCH_SWIPE_MAX_MS 400
typedef enum { GESTURE_NONE, GESTURE_PAN, GESTURE_SWIPE} GestureMode;
typedef struct { Window *window; TouchHandlers h;} TouchEntry;
static TouchEntry s_entries[TOUCH_ENTRY_MAX];static AppTimer *s_hold_timer;static TouchEntry *s_gesture_entry;static bool s_tracking;static bool s_hold_fired;static GestureMode s_mode;static GPoint s_down_point;static GPoint s_lock_point;static int64_t s_down_ms;
static int64_t now_ms(void) { time_t sec; uint16_t ms; time_ms(&sec, &ms); return (int64_t)sec * 1000 + ms;}
static int abs_delta(int16_t from, int16_t to) { int d = to - from; return d < 0 ? -d : d;}
static TouchEntry *find_entry(Window *window) { for (int i = 0; i < TOUCH_ENTRY_MAX; i++) { if (s_entries[i].window == window) { return &s_entries[i]; } } return NULL;}
static TouchEntry *top_entry(void) { Window *top = window_stack_get_top_window(); return top ? find_entry(top) : NULL;}
static void cancel_hold_timer(void) { if (s_hold_timer) { app_timer_cancel(s_hold_timer); s_hold_timer = NULL; }}
static void cancel_gesture(void) { cancel_hold_timer(); s_gesture_entry = NULL; s_tracking = false; s_mode = GESTURE_NONE;}
static TouchEntry *gesture_entry(void) { if (!s_gesture_entry) { return NULL; } if (s_gesture_entry->window != window_stack_get_top_window()) { cancel_gesture(); return NULL; } return s_gesture_entry;}
static void hold_timer_fired(void *data) { (void)data; s_hold_timer = NULL; TouchEntry *e = gesture_entry(); if (!e || !e->h.on_hold) { return; } s_hold_fired = true; e->h.on_hold(s_down_point, e->h.ctx);}
static void touchdown(const TouchEvent *event) { cancel_gesture(); if (event->non_navigational) { return; } s_gesture_entry = top_entry(); if (!s_gesture_entry) { return; } s_tracking = true; s_hold_fired = false; s_down_point = GPoint(event->x, event->y); s_down_ms = now_ms(); s_hold_timer = app_timer_register(TOUCH_HOLD_MS, hold_timer_fired, NULL);}
static void position_update(const TouchEvent *event) { if (s_hold_timer && (abs_delta(s_down_point.x, event->x) > TOUCH_HOLD_SLOP_PX || abs_delta(s_down_point.y, event->y) > TOUCH_HOLD_SLOP_PX)) { cancel_hold_timer(); } if (s_mode == GESTURE_SWIPE) { return; } TouchEntry *e = s_gesture_entry; if (s_mode == GESTURE_PAN) { if (e->h.on_pan_move) { e->h.on_pan_move(GPoint((int16_t)(event->x - s_lock_point.x), (int16_t)(event->y - s_lock_point.y)), e->h.ctx); } return; } int dx = abs_delta(s_down_point.x, event->x); int dy = abs_delta(s_down_point.y, event->y); if (dx <= TOUCH_LOCK_SLOP_PX && dy <= TOUCH_LOCK_SLOP_PX) { return; } if (dy >= dx) { s_mode = GESTURE_PAN; s_lock_point = GPoint(event->x, event->y); if (e->h.on_pan_move) { e->h.on_pan_move(GPoint(0, 0), e->h.ctx); } } else { s_mode = GESTURE_SWIPE; }}
static void liftoff(const TouchEvent *event) { cancel_hold_timer(); s_tracking = false; GestureMode mode = s_mode; s_mode = GESTURE_NONE; TouchEntry *e = s_gesture_entry; s_gesture_entry = NULL; if (!e) { return; } int64_t duration = now_ms() - s_down_ms; int dx = event->x - s_down_point.x; int adx = abs_delta(s_down_point.x, event->x); int ady = abs_delta(s_down_point.y, event->y); if (mode == GESTURE_PAN) { if (e->h.on_pan_end) { e->h.on_pan_end(false, e->h.ctx); } return; } if (mode == GESTURE_SWIPE) { if (e->h.on_swipe && adx >= TOUCH_SWIPE_MIN_PX && adx > 2 * ady && duration <= TOUCH_SWIPE_MAX_MS) { e->h.on_swipe(dx < 0 ? TOUCH_SWIPE_LEFT : TOUCH_SWIPE_RIGHT, e->h.ctx); } return; } if (e->h.on_tap && !s_hold_fired && duration <= TOUCH_TAP_MS && adx <= TOUCH_TAP_SLOP_PX && ady <= TOUCH_TAP_SLOP_PX) { e->h.on_tap(s_down_point, e->h.ctx); }}
static void touch_event(const TouchEvent *event, void *context) { (void)context; if (event->type == TouchEvent_Touchdown) { touchdown(event); } else if (!s_tracking || !gesture_entry()) { return; } else if (event->type == TouchEvent_PositionUpdate) { position_update(event); } else if (event->type == TouchEvent_Liftoff) { liftoff(event); }}
void touch_init(void) { touch_service_subscribe(touch_event, NULL);}
void touch_deinit(void) { cancel_gesture(); touch_service_unsubscribe();}
void touch_attach(Window *window, const TouchHandlers *handlers) { if (!window || !handlers) { return; } TouchEntry *e = find_entry(window); if (!e) { e = find_entry(NULL); } if (!e) { return; } e->window = window; e->h = *handlers;}
void touch_detach(Window *window) { TouchEntry *e = window ? find_entry(window) : NULL; if (!e) { return; } if (s_gesture_entry == e) { cancel_gesture(); } *e = (TouchEntry){ .window = NULL };}