Rockbox open source high quality audio player as a Music Player Daemon
mpris rockbox mpd libadwaita audio rust zig deno
2
fork

Configure Feed

Select the types of activity you want to include in your feed.

touchscreen: Add gesture recognition API

Add a basic gesture API which can detect taps, long presses,
and dragging gestures.

Change-Id: Id10bf8d46b9195330ce951f9f108c81e87a8dad4

authored by

Aidan MacDonald and committed by
Solomon Peachy
7aa82321 0f4cc33d

+247
+3
apps/SOURCES
··· 11 11 debug_menu.c 12 12 filetypes.c 13 13 fileop.c 14 + #ifdef HAVE_TOUCHSCREEN 15 + gesture.c 16 + #endif 14 17 language.c 15 18 main.c 16 19 menu.c
+121
apps/gesture.c
··· 1 + /*************************************************************************** 2 + * __________ __ ___. 3 + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ 4 + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / 5 + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < 6 + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ 7 + * \/ \/ \/ \/ \/ 8 + * $Id$ 9 + * 10 + * Copyright (C) 2022 by Aidan MacDonald 11 + * 12 + * This program is free software; you can redistribute it and/or 13 + * modify it under the terms of the GNU General Public License 14 + * as published by the Free Software Foundation; either version 2 15 + * of the License, or (at your option) any later version. 16 + * 17 + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY 18 + * KIND, either express or implied. 19 + * 20 + ****************************************************************************/ 21 + 22 + #include "gesture.h" 23 + #include "button.h" 24 + #include "viewport.h" 25 + #include "system.h" 26 + 27 + /* could be a setting */ 28 + #define TOUCH_LONG_PRESS_TIME (30 * HZ / 100) 29 + 30 + void gesture_reset(struct gesture *g) 31 + { 32 + g->flags = 0; 33 + } 34 + 35 + void gesture_process(struct gesture *g, const struct touchevent *ev) 36 + { 37 + /* wait for the first press if we haven't seen it */ 38 + if (!gesture_is_pressed(g) && ev->type != TOUCHEVENT_PRESS) 39 + return; 40 + 41 + int dx, dy, dist_sqr; 42 + switch (ev->type) 43 + { 44 + case TOUCHEVENT_PRESS: 45 + g->flags |= GESTURE_F_PRESSED | GESTURE_F_VALID; 46 + g->id = GESTURE_NONE; 47 + g->ox = g->x = ev->x; 48 + g->oy = g->y = ev->y; 49 + g->start_tick = ev->tick; 50 + g->last_tick = ev->tick; 51 + break; 52 + 53 + case TOUCHEVENT_CONTACT: 54 + g->x = ev->x; 55 + g->y = ev->y; 56 + g->last_tick = ev->tick; 57 + 58 + if (g->id == GESTURE_LONG_PRESS) 59 + g->id = GESTURE_HOLD; 60 + else if (g->id == GESTURE_DRAGSTART) 61 + g->id = GESTURE_DRAG; 62 + else if (g->id != GESTURE_DRAG) 63 + { 64 + dx = ev->x - g->ox; 65 + dy = ev->y - g->oy; 66 + dist_sqr = dx*dx + dy*dy; 67 + 68 + /* if squared distance exceeds a threshold, report as a DRAG. */ 69 + const int thresh = touchscreen_get_scroll_threshold(); 70 + if (dist_sqr > thresh*thresh) 71 + g->id = GESTURE_DRAGSTART; 72 + 73 + /* report a LONG_PRESS if no motion occurs within a timeout */ 74 + if (g->id == GESTURE_NONE && 75 + TIME_AFTER(ev->tick, g->start_tick + TOUCH_LONG_PRESS_TIME)) 76 + g->id = GESTURE_LONG_PRESS; 77 + } 78 + 79 + break; 80 + 81 + case TOUCHEVENT_RELEASE: 82 + /* report a RELEASE event after a continuous HOLD or DRAG */ 83 + if (g->id == GESTURE_HOLD || 84 + g->id == GESTURE_DRAGSTART || 85 + g->id == GESTURE_DRAG) 86 + g->id = GESTURE_RELEASE; 87 + 88 + /* report a TAP event if we got a press & release without 89 + * triggering any other gestures */ 90 + else if (g->id == GESTURE_NONE) 91 + g->id = GESTURE_TAP; 92 + 93 + g->flags &= ~GESTURE_F_PRESSED; 94 + g->last_tick = ev->tick; 95 + break; 96 + } 97 + } 98 + 99 + bool gesture_get_event_in_vp(struct gesture *g, struct gesture_event *gevt, 100 + const struct viewport *vp) 101 + { 102 + if (!gesture_is_valid(g)) 103 + return false; 104 + 105 + gevt->id = g->id; 106 + gevt->x = g->x; 107 + gevt->y = g->y; 108 + gevt->ox = g->ox; 109 + gevt->oy = g->oy; 110 + gevt->start_tick = g->start_tick; 111 + gevt->last_tick = g->last_tick; 112 + 113 + if (vp) { 114 + gevt->x -= vp->x; 115 + gevt->y -= vp->y; 116 + gevt->ox -= vp->x; 117 + gevt->oy -= vp->y; 118 + } 119 + 120 + return !vp || viewport_point_within_vp(vp, g->ox, g->oy); 121 + }
+123
apps/gesture.h
··· 1 + /*************************************************************************** 2 + * __________ __ ___. 3 + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ 4 + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / 5 + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < 6 + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ 7 + * \/ \/ \/ \/ \/ 8 + * $Id$ 9 + * 10 + * Copyright (C) 2022 Aidan MacDonald 11 + * 12 + * This program is free software; you can redistribute it and/or 13 + * modify it under the terms of the GNU General Public License 14 + * as published by the Free Software Foundation; either version 2 15 + * of the License, or (at your option) any later version. 16 + * 17 + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY 18 + * KIND, either express or implied. 19 + * 20 + ****************************************************************************/ 21 + #ifndef _GESTURE_H_ 22 + #define _GESTURE_H_ 23 + 24 + #include <stddef.h> 25 + #include <stdbool.h> 26 + 27 + struct viewport; 28 + struct touchevent; 29 + 30 + /** Events which can be detected by the gesture API. 31 + * 32 + * The gesture state machine, informally, looks like this: 33 + * 34 + * NONE --+--> TAP ------------------------------------+ 35 + * | | 36 + * +--> LONG_PRESS -----------------------------+ 37 + * | | | 38 + * | +---> HOLD ---+ | 39 + * | | | | 40 + * +-------------+---> DRAG ---+--> RELEASE ----+--> NONE 41 + * 42 + * State transitions occur from gesture_process() in response to touch events. 43 + * 44 + * - The NONE "event" is returned prior to any other gesture being detected. 45 + * Although the graph above depicts a transition back to NONE at the end of 46 + * an event chain, that transition in fact happens on the TOUCHEVENT_PRESS 47 + * after the end of the last event. 48 + * 49 + * - TAP events are reported after getting a TOUCHEVENT_RELEASE event if no 50 + * other gestures were detected between the press and release. 51 + * 52 + * - LONG_PRESS events are reported on a TOUCHEVENT_CONTACT event if the long 53 + * press timeout has expired and the touch point hasn't moved too far from 54 + * its initial position. 55 + * 56 + * - HOLD events are reported on TOUCHEVENT_CONTACT events after a LONG_PRESS, 57 + * provided the touch point hasn't moved too far from its initial position. 58 + * 59 + * - DRAG events are reported on TOUCHEVENT_CONTACT events after the touch 60 + * point first moves a certain distance from its initial position. The first 61 + * time this happens, it is reported as DRAGSTART. 62 + * 63 + * - RELEASE events are reported on the TOUCHEVENT_RELEASE event following a 64 + * HOLD or DRAG gesture. 65 + */ 66 + enum gesture_id 67 + { 68 + GESTURE_NONE = 0, /** No gesture */ 69 + GESTURE_TAP, /** Quick press & release */ 70 + GESTURE_LONG_PRESS, /** Start of a long press */ 71 + GESTURE_HOLD, /** Continuation of a long press */ 72 + GESTURE_DRAGSTART, /** Start of a DRAG event */ 73 + GESTURE_DRAG, /** Press and drag on the screen */ 74 + GESTURE_RELEASE, /** End of a HOLD or DRAG event */ 75 + }; 76 + 77 + enum gesture_flags 78 + { 79 + GESTURE_F_VALID = 0x01, 80 + GESTURE_F_PRESSED = 0x02, 81 + }; 82 + 83 + struct gesture 84 + { 85 + unsigned int flags; 86 + int id; 87 + short x, y; 88 + short ox, oy; 89 + long start_tick; 90 + long last_tick; 91 + }; 92 + 93 + struct gesture_event 94 + { 95 + int id; 96 + short x, y; 97 + short ox, oy; 98 + long start_tick; 99 + long last_tick; 100 + }; 101 + 102 + void gesture_reset(struct gesture *g); 103 + void gesture_process(struct gesture *g, const struct touchevent *ev); 104 + bool gesture_get_event_in_vp(struct gesture *g, struct gesture_event *gevt, 105 + const struct viewport *vp); 106 + 107 + static inline bool gesture_get_event(struct gesture *g, 108 + struct gesture_event *gevt) 109 + { 110 + return gesture_get_event_in_vp(g, gevt, NULL); 111 + } 112 + 113 + static inline bool gesture_is_valid(struct gesture *g) 114 + { 115 + return !!(g->flags & GESTURE_F_VALID); 116 + } 117 + 118 + static inline bool gesture_is_pressed(struct gesture *g) 119 + { 120 + return !!(g->flags & GESTURE_F_PRESSED); 121 + } 122 + 123 + #endif /* _GESTURE_H_ */