/*************************************************************************** * __________ __ ___. * Open \______ \ ____ ____ | | _\_ |__ _______ ___ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ * \/ \/ \/ \/ \/ * $Id$ * * Copyright (C) 2022 by Aidan MacDonald * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY * KIND, either express or implied. * ****************************************************************************/ #include "gesture.h" #include "button.h" #include "viewport.h" #include "system.h" /* could be a setting */ #define TOUCH_LONG_PRESS_TIME (30 * HZ / 100) void gesture_reset(struct gesture *g) { g->flags = 0; } void gesture_process(struct gesture *g, const struct touchevent *ev) { /* wait for the first press if we haven't seen it */ if (!gesture_is_pressed(g) && ev->type != TOUCHEVENT_PRESS) return; int dx, dy, dist_sqr; switch (ev->type) { case TOUCHEVENT_PRESS: g->flags |= GESTURE_F_PRESSED | GESTURE_F_VALID; g->id = GESTURE_NONE; g->ox = g->x = ev->x; g->oy = g->y = ev->y; g->start_tick = ev->tick; g->last_tick = ev->tick; break; case TOUCHEVENT_CONTACT: g->x = ev->x; g->y = ev->y; g->last_tick = ev->tick; if (g->id == GESTURE_LONG_PRESS) g->id = GESTURE_HOLD; else if (g->id == GESTURE_DRAGSTART) g->id = GESTURE_DRAG; else if (g->id != GESTURE_DRAG) { dx = ev->x - g->ox; dy = ev->y - g->oy; dist_sqr = dx*dx + dy*dy; /* if squared distance exceeds a threshold, report as a DRAG. */ const int thresh = touchscreen_get_scroll_threshold(); if (dist_sqr > thresh*thresh) g->id = GESTURE_DRAGSTART; /* report a LONG_PRESS if no motion occurs within a timeout */ if (g->id == GESTURE_NONE && TIME_AFTER(ev->tick, g->start_tick + TOUCH_LONG_PRESS_TIME)) g->id = GESTURE_LONG_PRESS; } break; case TOUCHEVENT_RELEASE: /* report a RELEASE event after a continuous HOLD or DRAG */ if (g->id == GESTURE_HOLD || g->id == GESTURE_DRAGSTART || g->id == GESTURE_DRAG) g->id = GESTURE_RELEASE; /* report a TAP event if we got a press & release without * triggering any other gestures */ else if (g->id == GESTURE_NONE) g->id = GESTURE_TAP; g->flags &= ~GESTURE_F_PRESSED; g->last_tick = ev->tick; break; } } bool gesture_get_event_in_vp(struct gesture *g, struct gesture_event *gevt, const struct viewport *vp) { if (!gesture_is_valid(g)) return false; gevt->id = g->id; gevt->x = g->x; gevt->y = g->y; gevt->ox = g->ox; gevt->oy = g->oy; gevt->start_tick = g->start_tick; gevt->last_tick = g->last_tick; if (vp) { gevt->x -= vp->x; gevt->y -= vp->y; gevt->ox -= vp->x; gevt->oy -= vp->y; } return !vp || viewport_point_within_vp(vp, g->ox, g->oy); } /* * gesture velocity helper */ void gesture_vel_reset(struct gesture_vel *gv) { gv->idx = 0; gv->cnt = 0; } void gesture_vel_process(struct gesture_vel *gv, const struct touchevent *ev) { if (ev->type != TOUCHEVENT_PRESS && ev->type != TOUCHEVENT_CONTACT) return; gv->xsamp[gv->idx] = ev->x; gv->ysamp[gv->idx] = ev->y; gv->tsamp[gv->idx] = ev->tick; gv->idx++; gv->cnt++; if (gv->idx >= ARRAYLEN(gv->xsamp)) gv->idx = 0; } bool gesture_vel_get(struct gesture_vel *gv, int *xvel, int *yvel) { if (gv->cnt <= 1) { *xvel = 0; *yvel = 0; return false; } int dx = 0, dy = 0, dt = 0; size_t n = MIN(gv->cnt, ARRAYLEN(gv->xsamp)) - 1; size_t i = gv->cnt < ARRAYLEN(gv->xsamp) ? 0 : gv->idx; size_t ip = i + 1; while (n-- > 0) { if (ip >= ARRAYLEN(gv->xsamp)) ip = 0; dx += gv->xsamp[ip] - gv->xsamp[i]; dy += gv->ysamp[ip] - gv->ysamp[i]; dt += gv->tsamp[ip] - gv->tsamp[i]; i = ip; ip++; } if (dt == 0) { *xvel = 0; *yvel = 0; } else { *xvel = dx * HZ / dt; *yvel = dy * HZ / dt; } return gv->cnt >= ARRAYLEN(gv->xsamp); }