This commit is contained in:
2026-06-14 19:09:18 +01:00
parent 14bd1a9271
commit 13fa90a0e9
3958 changed files with 999286 additions and 4 deletions
+966
View File
@@ -0,0 +1,966 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2022 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
/* Touch gestures were removed from SDL3, so this is the SDL2 implementation copied in here, and tweaked a little. */
#ifndef INCL_SDL_GESTURE_H
#define INCL_SDL_GESTURE_H
#if !defined(SDL_MAJOR_VERSION)
#error Please include SDL.h before including this header.
#elif SDL_MAJOR_VERSION < 2
#error This header requires SDL2 or later.
#elif SDL_MAJOR_VERSION == 2
/* building against SDL2? Just use the built-in SDL2 implementation. */
#define Gesture_Init() (0)
#define Gesture_Quit()
#define Gesture_ID SDL_GestureID
#define Gesture_LoadDollarTemplates SDL_LoadDollarTemplates
#define Gesture_RecordGesture SDL_RecordGesture
#define Gesture_SaveAllDollarTemplates SDL_SaveAllDollarTemplates
#define Gesture_SaveDollarTemplate SDL_SaveDollarTemplate
#define GESTURE_DOLLARGESTURE SDL_DOLLARGESTURE
#define GESTURE_DOLLARRECORD SDL_DOLLARRECORD
#define GESTURE_MULTIGESTURE SDL_MULTIGESTURE
#define Gesture_MultiGestureEvent SDL_MultiGestureEvent
#define Gesture_DollarGestureEvent SDL_DollarGestureEvent
#else
#include <cmath>
/* Set up for C function definitions, even when using C++ */
#ifdef __cplusplus
extern "C" {
#endif
typedef Sint64 Gesture_ID;
/* events... */
/* generally you shouldn't hardcode event type numbers--and doubly so in
the reserved range!--but these match SDL2 and SDL3 promises to preserve
these values to help sdl2-compat. */
#define GESTURE_DOLLARGESTURE 0x800
#define GESTURE_DOLLARRECORD 0x801
#define GESTURE_MULTIGESTURE 0x802
typedef struct Gesture_MultiGestureEvent
{
Uint32 type;
Uint32 reserved;
Uint64 timestamp;
SDL_TouchID touchID;
float dTheta;
float dDist;
float x;
float y;
Uint16 numFingers;
Uint16 padding;
} Gesture_MultiGestureEvent;
typedef struct Gesture_DollarGestureEvent
{
Uint32 type;
Uint32 reserved;
Uint64 timestamp;
SDL_TouchID touchID;
Gesture_ID gestureId;
Uint32 numFingers;
float error;
float x;
float y;
} Gesture_DollarGestureEvent;
/* Function prototypes */
/**
* Call this once, AFTER SDL_Init, to set up the Gesture API.
*
* \returns 0 on success, -1 on error. Call SDL_GetError() for specifics.
*/
extern int SDLCALL Gesture_Init(void);
/**
* Call this once, BEFORE SDL_Quit, to clean up the Gesture API.
*/
extern void SDLCALL Gesture_Quit(void);
/**
* Begin recording a gesture on a specified touch device or all touch devices.
*
* If the parameter `touchID` is -1 (i.e., all devices), this function will
* always return 1, regardless of whether there actually are any devices.
*
* \param touchID the touch device id, or -1 for all touch devices
* \returns 1 on success or 0 if the specified device could not be found.
*/
extern int SDLCALL Gesture_RecordGesture(SDL_TouchID touchID);
/**
* Save all currently loaded Dollar Gesture templates.
*
* \param dst a SDL_IOStream to save to
* \returns the number of saved templates on success or 0 on failure; call
* SDL_GetError() for more information.
*
* \since This function is available since SDL 2.0.0.
*
* \sa Gesture_LoadDollarTemplates
* \sa Gesture_SaveDollarTemplate
*/
extern int SDLCALL Gesture_SaveAllDollarTemplates(SDL_IOStream *dst);
/**
* Save a currently loaded Dollar Gesture template.
*
* \param gestureId a gesture id
* \param dst a SDL_IOStream to save to
* \returns 1 on success or 0 on failure; call SDL_GetError() for more
* information.
*
* \since This function is available since SDL 2.0.0.
*
* \sa SDL_LoadDollarTemplates
* \sa SDL_SaveAllDollarTemplates
*/
extern int SDLCALL Gesture_SaveDollarTemplate(Gesture_ID gestureId, SDL_IOStream *dst);
/**
* Load Dollar Gesture templates from a file.
*
* \param touchID a touch id
* \param src a SDL_IOStream to load from
* \returns the number of loaded templates on success or a negative error code
* (or 0) on failure; call SDL_GetError() for more information.
*
* \since This function is available since SDL 2.0.0.
*
* \sa SDL_SaveAllDollarTemplates
* \sa SDL_SaveDollarTemplate
*/
extern int SDLCALL Gesture_LoadDollarTemplates(SDL_TouchID touchID, SDL_IOStream *src);
/* Ends C function definitions when using C++ */
#ifdef __cplusplus
}
#endif
#if defined(SDL_GESTURE_IMPLEMENTATION)
#define GESTURE_MAX_DOLLAR_PATH_SIZE 1024
#define GESTURE_DOLLARNPOINTS 64
#define GESTURE_DOLLARSIZE 256
#define GESTURE_PHI 0.618033989
typedef struct
{
float length;
int numPoints;
SDL_FPoint p[GESTURE_MAX_DOLLAR_PATH_SIZE];
} GestureDollarPath;
typedef struct
{
SDL_FPoint path[GESTURE_DOLLARNPOINTS];
Sint64 hash;
} GestureDollarTemplate;
typedef struct
{
SDL_TouchID touchID;
SDL_FPoint centroid;
GestureDollarPath dollarPath;
int numDownFingers;
int numDollarTemplates;
GestureDollarTemplate *dollarTemplate;
bool recording;
} GestureTouch;
static GestureTouch *GestureTouches = NULL;
static int GestureNumTouches = 0;
static bool GestureRecordAll = false;
static void GestureProcessEvent(const SDL_Event *event);
static bool SDLCALL GestureEventWatch(void *, SDL_Event *event)
{
GestureProcessEvent(event);
return true;
}
int Gesture_Init(void)
{
Gesture_Quit();
SDL_AddEventWatch(GestureEventWatch, NULL);
return 0;
}
static GestureTouch *GestureAddTouch(const SDL_TouchID touchID)
{
GestureTouch *gestureTouch = (GestureTouch *)SDL_realloc(GestureTouches, (GestureNumTouches + 1) * sizeof(GestureTouch));
if (gestureTouch == NULL) {
SDL_OutOfMemory();
return NULL;
}
GestureTouches = gestureTouch;
SDL_zero(GestureTouches[GestureNumTouches]);
GestureTouches[GestureNumTouches].touchID = touchID;
return &GestureTouches[GestureNumTouches++];
}
#if 0
static int GestureDelTouch(const SDL_TouchID touchID)
{
int i;
for (i = 0; i < GestureNumTouches; i++) {
if (GestureTouches[i].touchID == touchID) {
break;
}
}
if (i == GestureNumTouches) {
/* not found */
return -1;
}
SDL_free(GestureTouches[i].dollarTemplate);
SDL_zero(GestureTouches[i]);
GestureNumTouches--;
if (i != GestureNumTouches) {
SDL_copyp(&GestureTouches[i], &GestureTouches[GestureNumTouches]);
}
return 0;
}
#endif
static GestureTouch *GestureGetTouch(const SDL_TouchID touchID)
{
int i;
for (i = 0; i < GestureNumTouches; i++) {
/* printf("%i ?= %i\n",GestureTouches[i].touchID,touchID); */
if (GestureTouches[i].touchID == touchID) {
return &GestureTouches[i];
}
}
return NULL;
}
int Gesture_RecordGesture(SDL_TouchID touchID)
{
SDL_TouchID *devices;
int i;
devices = SDL_GetTouchDevices(NULL);
if (devices) {
/* make sure we know about all the devices SDL3 knows about, since we aren't connected as tightly as we were in SDL2. */
for (i = 0; devices[i]; i++) {
if (!GestureGetTouch(devices[i])) {
GestureAddTouch(devices[i]);
}
}
SDL_free(devices);
}
if (touchID != 0) {
GestureRecordAll = true; /* !!! FIXME: this is never set back to false anywhere, that's probably a bug. */
for (i = 0; i < GestureNumTouches; i++) {
GestureTouches[i].recording = true;
}
} else {
GestureTouch *touch = GestureGetTouch(touchID);
if (!touch) {
return 0; /* bogus touchid */
}
touch->recording = true;
}
return 1;
}
void Gesture_Quit(void)
{
SDL_RemoveEventWatch(GestureEventWatch, NULL);
SDL_free(GestureTouches);
GestureTouches = NULL;
GestureNumTouches = 0;
GestureRecordAll = false;
}
static unsigned long GestureHashDollar(SDL_FPoint *points)
{
unsigned long hash = 5381;
int i;
for (i = 0; i < GESTURE_DOLLARNPOINTS; i++) {
hash = ((hash << 5) + hash) + (unsigned long)points[i].x;
hash = ((hash << 5) + hash) + (unsigned long)points[i].y;
}
return hash;
}
static int GestureSaveTemplate(GestureDollarTemplate *templ, SDL_IOStream *dst)
{
const size_t bytes = sizeof(templ->path[0]) * GESTURE_DOLLARNPOINTS;
if (dst == NULL) {
return 0;
}
/* No Longer storing the Hash, rehash on load */
/* if (SDL_IOWrite(dst, &(templ->hash), sizeof(templ->hash)) != sizeof(templ->hash)) return 0; */
#if SDL_BYTEORDER == SDL_LIL_ENDIAN
if (SDL_WriteIO(dst, templ->path, bytes) != bytes) {
return 0;
}
#else
{
GestureDollarTemplate copy = *templ;
SDL_FPoint *p = copy.path;
int i;
for (i = 0; i < GESTURE_DOLLARNPOINTS; i++, p++) {
p->x = SDL_SwapFloatLE(p->x);
p->y = SDL_SwapFloatLE(p->y);
}
if (SDL_WriteIO(dst, copy.path, bytes) != bytes) {
return 0;
}
}
#endif
return 1;
}
SDL_DECLSPEC int SDLCALL
Gesture_SaveAllDollarTemplates(SDL_IOStream *dst)
{
int i, j, rtrn = 0;
for (i = 0; i < GestureNumTouches; i++) {
GestureTouch *touch = &GestureTouches[i];
for (j = 0; j < touch->numDollarTemplates; j++) {
rtrn += GestureSaveTemplate(&touch->dollarTemplate[j], dst);
}
}
return rtrn;
}
SDL_DECLSPEC int SDLCALL
Gesture_SaveDollarTemplate(Gesture_ID gestureId, SDL_IOStream *dst)
{
int i, j;
for (i = 0; i < GestureNumTouches; i++) {
GestureTouch *touch = &GestureTouches[i];
for (j = 0; j < touch->numDollarTemplates; j++) {
if (touch->dollarTemplate[j].hash == gestureId) {
return GestureSaveTemplate(&touch->dollarTemplate[j], dst);
}
}
}
return SDL_SetError("Unknown gestureId");
}
/* path is an already sampled set of points
Returns the index of the gesture on success, or -1 */
static int GestureAddDollar_one(GestureTouch *inTouch, SDL_FPoint *path)
{
GestureDollarTemplate *dollarTemplate;
GestureDollarTemplate *templ;
int index;
index = inTouch->numDollarTemplates;
dollarTemplate = (GestureDollarTemplate *)SDL_realloc(inTouch->dollarTemplate, (index + 1) * sizeof(GestureDollarTemplate));
if (dollarTemplate == NULL) {
return SDL_OutOfMemory();
}
inTouch->dollarTemplate = dollarTemplate;
templ = &inTouch->dollarTemplate[index];
SDL_memcpy(templ->path, path, GESTURE_DOLLARNPOINTS * sizeof(SDL_FPoint));
templ->hash = GestureHashDollar(templ->path);
inTouch->numDollarTemplates++;
return index;
}
static int GestureAddDollar(GestureTouch *inTouch, SDL_FPoint *path)
{
int index = -1;
int i = 0;
if (inTouch == NULL) {
if (GestureNumTouches == 0) {
return SDL_SetError("no gesture touch devices registered");
}
for (i = 0; i < GestureNumTouches; i++) {
inTouch = &GestureTouches[i];
index = GestureAddDollar_one(inTouch, path);
if (index < 0) {
return -1;
}
}
/* Use the index of the last one added. */
return index;
}
return GestureAddDollar_one(inTouch, path);
}
SDL_DECLSPEC int SDLCALL
Gesture_LoadDollarTemplates(SDL_TouchID touchID, SDL_IOStream *src)
{
int i, loaded = 0;
GestureTouch *touch = NULL;
if (src == NULL) {
return 0;
}
/* In SDL2 this test was `touchID >= 0` leading to warnings from gcc
because SDL_TouchId is now Uint64. In SDL2 it was Sint64. The
documentation does not say what < 0 means here but the only defined
negative touchID was SDL_MOUSE_TOUCHID (-1). In SDL3 SDL_PEN_TOUCHID (-2)
has been added hence this test. Given the lack of documentation
it is impossible to say if this updated test is correct. */
if (touchID < SDL_PEN_TOUCHID) {
for (i = 0; i < GestureNumTouches; i++) {
if (GestureTouches[i].touchID == touchID) {
touch = &GestureTouches[i];
}
}
if (touch == NULL) {
return SDL_SetError("given touch id not found");
}
}
while (1) {
GestureDollarTemplate templ;
const size_t bytes = sizeof(templ.path[0]) * GESTURE_DOLLARNPOINTS;
if (SDL_ReadIO(src, templ.path, bytes) < bytes) {
if (loaded == 0) {
return SDL_SetError("could not read any dollar gesture from rwops");
}
break;
}
#if SDL_BYTEORDER != SDL_LIL_ENDIAN
for (i = 0; i < GESTURE_DOLLARNPOINTS; i++) {
SDL_FPoint *p = &templ.path[i];
p->x = SDL_SwapFloatLE(p->x);
p->y = SDL_SwapFloatLE(p->y);
}
#endif
// See comment at line 436.
if (touchID < SDL_PEN_TOUCHID) {
/* printf("Adding loaded gesture to 1 touch\n"); */
if (GestureAddDollar(touch, templ.path) >= 0) {
loaded++;
}
} else {
/* printf("Adding to: %i touches\n",GestureNumTouches); */
for (i = 0; i < GestureNumTouches; i++) {
touch = &GestureTouches[i];
/* printf("Adding loaded gesture to + touches\n"); */
/* TODO: What if this fails? */
GestureAddDollar(touch, templ.path);
}
loaded++;
}
}
return loaded;
}
static float GestureDollarDifference(SDL_FPoint *points, SDL_FPoint *templ, float ang)
{
/* SDL_FPoint p[GESTURE_DOLLARNPOINTS]; */
float dist = 0;
SDL_FPoint p;
int i;
for (i = 0; i < GESTURE_DOLLARNPOINTS; i++) {
p.x = points[i].x * SDL_cosf(ang) - points[i].y * SDL_sinf(ang);
p.y = points[i].x * SDL_sinf(ang) + points[i].y * SDL_cosf(ang);
dist += SDL_sqrtf((p.x - templ[i].x) * (p.x - templ[i].x) + (p.y - templ[i].y) * (p.y - templ[i].y));
}
return dist / GESTURE_DOLLARNPOINTS;
}
static float GestureBestDollarDifference(SDL_FPoint *points, SDL_FPoint *templ)
{
/*------------BEGIN DOLLAR BLACKBOX------------------
-TRANSLATED DIRECTLY FROM PSUDEO-CODE AVAILABLE AT-
-"http://depts.washington.edu/aimgroup/proj/dollar/"
*/
double ta = -SDL_PI_D / 4;
double tb = SDL_PI_D / 4;
double dt = SDL_PI_D / 90;
float x1 = (float)(GESTURE_PHI * ta + (1 - GESTURE_PHI) * tb);
float f1 = GestureDollarDifference(points, templ, x1);
float x2 = (float)((1 - GESTURE_PHI) * ta + GESTURE_PHI * tb);
float f2 = GestureDollarDifference(points, templ, x2);
while (SDL_fabs(ta - tb) > dt) {
if (f1 < f2) {
tb = x2;
x2 = x1;
f2 = f1;
x1 = (float)(GESTURE_PHI * ta + (1 - GESTURE_PHI) * tb);
f1 = GestureDollarDifference(points, templ, x1);
} else {
ta = x1;
x1 = x2;
f1 = f2;
x2 = (float)((1 - GESTURE_PHI) * ta + GESTURE_PHI * tb);
f2 = GestureDollarDifference(points, templ, x2);
}
}
/*
if (f1 <= f2)
printf("Min angle (x1): %f\n",x1);
else if (f1 > f2)
printf("Min angle (x2): %f\n",x2);
*/
return SDL_min(f1, f2);
}
/* `path` contains raw points, plus (possibly) the calculated length */
static int GestureDollarNormalize(const GestureDollarPath *path, SDL_FPoint *points, bool is_recording)
{
int i;
float interval;
float dist;
int numPoints = 0;
SDL_FPoint centroid;
float xmin, xmax, ymin, ymax;
float ang;
float w, h;
float length = path->length;
/* Calculate length if it hasn't already been done */
if (length <= 0) {
for (i = 1; i < path->numPoints; i++) {
const float dx = path->p[i].x - path->p[i - 1].x;
const float dy = path->p[i].y - path->p[i - 1].y;
length += SDL_sqrtf(dx * dx + dy * dy);
}
}
/* Resample */
interval = length / (GESTURE_DOLLARNPOINTS - 1);
dist = interval;
centroid.x = 0;
centroid.y = 0;
/* printf("(%f,%f)\n",path->p[path->numPoints-1].x,path->p[path->numPoints-1].y); */
for (i = 1; i < path->numPoints; i++) {
const float d = SDL_sqrtf((path->p[i - 1].x - path->p[i].x) * (path->p[i - 1].x - path->p[i].x) + (path->p[i - 1].y - path->p[i].y) * (path->p[i - 1].y - path->p[i].y));
/* printf("d = %f dist = %f/%f\n",d,dist,interval); */
while (dist + d > interval) {
points[numPoints].x = path->p[i - 1].x +
((interval - dist) / d) * (path->p[i].x - path->p[i - 1].x);
points[numPoints].y = path->p[i - 1].y +
((interval - dist) / d) * (path->p[i].y - path->p[i - 1].y);
centroid.x += points[numPoints].x;
centroid.y += points[numPoints].y;
numPoints++;
dist -= interval;
}
dist += d;
}
if (numPoints < GESTURE_DOLLARNPOINTS - 1) {
if (is_recording) {
SDL_SetError("ERROR: NumPoints = %i", numPoints);
}
return 0;
}
/* copy the last point */
points[GESTURE_DOLLARNPOINTS - 1] = path->p[path->numPoints - 1];
numPoints = GESTURE_DOLLARNPOINTS;
centroid.x /= numPoints;
centroid.y /= numPoints;
/* printf("Centroid (%f,%f)",centroid.x,centroid.y); */
/* Rotate Points so point 0 is left of centroid and solve for the bounding box */
xmin = centroid.x;
xmax = centroid.x;
ymin = centroid.y;
ymax = centroid.y;
ang = SDL_atan2f(centroid.y - points[0].y, centroid.x - points[0].x);
for (i = 0; i < numPoints; i++) {
const float px = points[i].x;
const float py = points[i].y;
points[i].x = (px - centroid.x) * SDL_cosf(ang) - (py - centroid.y) * SDL_sinf(ang) + centroid.x;
points[i].y = (px - centroid.x) * SDL_sinf(ang) + (py - centroid.y) * SDL_cosf(ang) + centroid.y;
if (points[i].x < xmin) {
xmin = points[i].x;
}
if (points[i].x > xmax) {
xmax = points[i].x;
}
if (points[i].y < ymin) {
ymin = points[i].y;
}
if (points[i].y > ymax) {
ymax = points[i].y;
}
}
/* Scale points to GESTURE_DOLLARSIZE, and translate to the origin */
w = xmax - xmin;
h = ymax - ymin;
for (i = 0; i < numPoints; i++) {
points[i].x = (points[i].x - centroid.x) * GESTURE_DOLLARSIZE / w;
points[i].y = (points[i].y - centroid.y) * GESTURE_DOLLARSIZE / h;
}
return numPoints;
}
static float GestureDollarRecognize(const GestureDollarPath *path, int *bestTempl, GestureTouch *touch)
{
SDL_FPoint points[GESTURE_DOLLARNPOINTS];
int i;
float bestDiff = 10000;
SDL_memset(points, 0, sizeof(points));
GestureDollarNormalize(path, points, false);
/* PrintPath(points); */
*bestTempl = -1;
for (i = 0; i < touch->numDollarTemplates; i++) {
const float diff = GestureBestDollarDifference(points, touch->dollarTemplate[i].path);
if (diff < bestDiff) {
bestDiff = diff;
*bestTempl = i;
}
}
return bestDiff;
}
static void GestureSendMulti(GestureTouch *touch, float dTheta, float dDist)
{
if (SDL_EventEnabled(GESTURE_MULTIGESTURE)) {
Gesture_MultiGestureEvent mgesture;
mgesture.type = GESTURE_MULTIGESTURE;
mgesture.timestamp = 0;
mgesture.touchID = touch->touchID;
mgesture.x = touch->centroid.x;
mgesture.y = touch->centroid.y;
mgesture.dTheta = dTheta;
mgesture.dDist = dDist;
mgesture.numFingers = (Uint16)touch->numDownFingers;
SDL_PushEvent((SDL_Event*)&mgesture);
}
}
static void GestureSendDollar(GestureTouch *touch, Gesture_ID gestureId, float error)
{
if (SDL_EventEnabled(GESTURE_DOLLARGESTURE)) {
Gesture_DollarGestureEvent dgesture;
dgesture.type = GESTURE_DOLLARGESTURE;
dgesture.timestamp = 0;
dgesture.touchID = touch->touchID;
dgesture.x = touch->centroid.x;
dgesture.y = touch->centroid.y;
dgesture.gestureId = gestureId;
dgesture.error = error;
/* A finger came up to trigger this event. */
dgesture.numFingers = touch->numDownFingers + 1;
SDL_PushEvent((SDL_Event*)&dgesture);
}
}
static void GestureSendDollarRecord(GestureTouch *touch, Gesture_ID gestureId)
{
if (SDL_EventEnabled(GESTURE_DOLLARRECORD)) {
Gesture_DollarGestureEvent dgesture;
dgesture.type = GESTURE_DOLLARRECORD;
dgesture.timestamp = 0;
dgesture.touchID = touch->touchID;
dgesture.gestureId = gestureId;
SDL_PushEvent((SDL_Event*)&dgesture);
}
}
#if !defined(GESTURE_LOG_UP_DOWN_EVENTS)
#define GESTURE_LOG_UP_DOWN_EVENTS 0
#endif
#if !defined(GESTURE_LOG_MOTION_EVENTS)
#define GESTURE_LOG_MOTION_EVENTS 0
#endif
static void GestureProcessEvent(const SDL_Event *event)
{
float x, y;
int index;
int i;
float pathDx, pathDy;
SDL_FPoint lastP;
SDL_FPoint lastCentroid;
float lDist;
float Dist;
float dtheta;
float dDist;
if (event->type == SDL_EVENT_FINGER_MOTION || event->type == SDL_EVENT_FINGER_DOWN || event->type == SDL_EVENT_FINGER_UP) {
GestureTouch *inTouch = GestureGetTouch(event->tfinger.touchID);
if (inTouch == NULL) { /* we maybe didn't see this one before. */
inTouch = GestureAddTouch(event->tfinger.touchID);
if (!inTouch) {
return; /* oh well. */
}
}
int numDownFingersReported;
SDL_Finger** fingers = SDL_GetTouchFingers(event->tfinger.touchID, &numDownFingersReported);
x = event->tfinger.x;
y = event->tfinger.y;
/* Finger Up */
if (event->type == SDL_EVENT_FINGER_UP) {
#if GESTURE_LOG_UP_DOWN_EVENTS
SDL_Log("GPE: Finger: %#" SDL_PRIx64 " UP. Device: %#" SDL_PRIx64 ", fingers: %i, x: %f, y: %f, press: %f",
event->tfinger.fingerID, event->tfinger.touchID, numDownFingersReported,
event->tfinger.x, event->tfinger.y, event->tfinger.pressure);
#endif
SDL_FPoint path[GESTURE_DOLLARNPOINTS];
#if SDL_PLATFORM_MACOS
/* Workaround issue https://github.com/libsdl-org/SDL/issues/13428,
Extra SDL_EVENT_FINGER_{UP,DOWN} with mouse button press, by
ignoring events with fingerID of SDL_BUTTON_LEFT.
N.B. If SDL_HINT_MOUSE_TOUCH_EVENTS is set to 0 no touch
events are received from the trackpad. */
if (event->tfinger.fingerID == SDL_BUTTON_LEFT) return;
#endif
/* Using the number of fingers returned by SDL_GetTouchFingers
is much more robust than counting finger up and down events.
With counting it is easy for the counted number to be higher
than the actual number. Unfortunately it has not been possible
to identify a sequence of actions that reliably reproduces
this but asserts have shown it happens often. Perhaps
sometimes a single UP or DOWN event is received for multiple
fingers.
Using the reported number is independent of how many events
are actually received. But, and this is a big one, in the
case of FINGER_UP SDL_GetTouchFingers reports the number of
fingers down *before* the up event.
N.B. In the case of a left button press on macOS,
SDL_GetTouchFingers reports 1 for the event that is not
ignored.
*/
inTouch->numDownFingers = numDownFingersReported - 1;
assert(inTouch->numDownFingers >= 0);
#if (GESTURE_LOG_UP_DOWN_EVENTS)
SDL_Log("GPE FINGER_UP, numDownFingers now = %i", inTouch->numDownFingers);
#endif
if (inTouch->recording) {
inTouch->recording = false;
GestureDollarNormalize(&inTouch->dollarPath, path, true);
/* PrintPath(path); */
if (GestureRecordAll) {
index = GestureAddDollar(NULL, path);
for (i = 0; i < GestureNumTouches; i++) {
GestureTouches[i].recording = false;
}
} else {
index = GestureAddDollar(inTouch, path);
}
if (index >= 0) {
GestureSendDollarRecord(inTouch, inTouch->dollarTemplate[index].hash);
} else {
GestureSendDollarRecord(inTouch, -1);
}
} else {
int bestTempl = -1;
const float error = GestureDollarRecognize(&inTouch->dollarPath, &bestTempl, inTouch);
if (bestTempl >= 0) {
/* Send Event */
const Gesture_ID gestureId = inTouch->dollarTemplate[bestTempl].hash;
GestureSendDollar(inTouch, gestureId, error);
/* printf ("%s\n",);("Dollar error: %f\n",error); */
}
}
/* inTouch->gestureLast[j] = inTouch->gestureLast[inTouch->numDownFingers]; */
if (inTouch->numDownFingers > 0) {
inTouch->centroid.x = (inTouch->centroid.x * (inTouch->numDownFingers + 1) - x) / inTouch->numDownFingers;
inTouch->centroid.y = (inTouch->centroid.y * (inTouch->numDownFingers + 1) - y) / inTouch->numDownFingers;
} else {
inTouch->centroid.x = inTouch->centroid.y = 0.0f;
}
} else if (event->type == SDL_EVENT_FINGER_MOTION) {
/* There is one FINGER_MOTION event per down finger. x,y gives
the position of the finger whose id is in the event. */
const float dx = event->tfinger.dx;
const float dy = event->tfinger.dy;
GestureDollarPath *path = &inTouch->dollarPath;
#if GESTURE_LOG_MOTION_EVENTS
SDL_Log("GPE: Finger: %#" SDL_PRIx64 " MOTION: device: %#" SDL_PRIx64 ", timestamp = %"
SDL_PRIu64 ", fingers: %i, x: %f, y: %f, press: %f, numDownFingers: %i",
event->tfinger.fingerID, event->tfinger.touchID, event->tfinger.timestamp,
numDownFingersReported, event->tfinger.x, event->tfinger.y, event->tfinger.pressure,
inTouch->numDownFingers);
#endif
assert(numDownFingersReported > 0);
#if SDL_PLATFORM_MACOS
/* Workaround issue https://github.com/libsdl-org/SDL/issues/13428.
See comment at line 753 for more details. */
if (event->tfinger.fingerID == SDL_BUTTON_LEFT) return;
/* SDL_GetTouchFingers reports 2 fingers down in the motion event
for the other finger during button press. Fix up the number of
fingers. */
uint32_t reportedNumFingers = numDownFingersReported;
for (uint32_t i = 0; i < reportedNumFingers; i++) {
if (fingers[i]->id == SDL_BUTTON_LEFT) {
numDownFingersReported--;
break;
}
}
#endif
/* See comment at line 762. One case where the count reliably
differs from reported is on iOS. When touching, dragging and
releasing 2 fingers, iOS sends a BUTTON_DOWN and BUTTON_UP
for one of the fingers. When the finger corresponding to the
button is raised, it sends the BUTTON_UP followed by the
FINGER_UP but FINGER_MOTION events can come before the
FINGER_UP and those events have only one finger down. */
inTouch->numDownFingers = numDownFingersReported;
if (path->numPoints < GESTURE_MAX_DOLLAR_PATH_SIZE) {
path->p[path->numPoints].x = inTouch->centroid.x;
path->p[path->numPoints].y = inTouch->centroid.y;
pathDx = (path->p[path->numPoints].x - path->p[path->numPoints - 1].x);
pathDy = (path->p[path->numPoints].y - path->p[path->numPoints - 1].y);
path->length += (float)SDL_sqrt(pathDx * pathDx + pathDy * pathDy);
path->numPoints++;
}
lastP.x = x - dx;
lastP.y = y - dy;
lastCentroid = inTouch->centroid;
inTouch->centroid.x += dx / inTouch->numDownFingers;
inTouch->centroid.y += dy / inTouch->numDownFingers;
/* printf("Centroid : (%f,%f)\n",inTouch->centroid.x,inTouch->centroid.y); */
if (inTouch->numDownFingers > 1) {
SDL_FPoint lv; /* Vector from centroid to last x,y position */
SDL_FPoint v; /* Vector from centroid to current x,y position */
/* lv = inTouch->gestureLast[j].cv; */
lv.x = lastP.x - lastCentroid.x;
lv.y = lastP.y - lastCentroid.y;
lDist = SDL_sqrtf(lv.x * lv.x + lv.y * lv.y);
/* printf("lDist = %f\n",lDist); */
v.x = x - inTouch->centroid.x;
v.y = y - inTouch->centroid.y;
/* inTouch->gestureLast[j].cv = v; */
Dist = SDL_sqrtf(v.x * v.x + v.y * v.y);
/* SDL_cosf(dTheta) = (v . lv)/(|v| * |lv|) */
/* Normalize Vectors to simplify angle calculation */
lv.x /= lDist;
lv.y /= lDist;
v.x /= Dist;
v.y /= Dist;
dtheta = SDL_atan2f(lv.x * v.y - lv.y * v.x, lv.x * v.x + lv.y * v.y);
dDist = (Dist - lDist);
if (lDist == 0) {
/* To avoid impossible values */
dDist = 0;
dtheta = 0;
}
/* inTouch->gestureLast[j].dDist = dDist;
inTouch->gestureLast[j].dtheta = dtheta;
printf("dDist = %f, dTheta = %f\n",dDist,dtheta);
gdtheta = gdtheta*.9 + dtheta*.1;
gdDist = gdDist*.9 + dDist*.1
knob.r += dDist/numDownFingers;
knob.ang += dtheta;
printf("thetaSum = %f, distSum = %f\n",gdtheta,gdDist);
printf("id: %i dTheta = %f, dDist = %f\n",j,dtheta,dDist); */
GestureSendMulti(inTouch, dtheta, dDist);
} else {
/* inTouch->gestureLast[j].dDist = 0;
inTouch->gestureLast[j].dtheta = 0;
inTouch->gestureLast[j].cv.x = 0;
inTouch->gestureLast[j].cv.y = 0; */
}
/* inTouch->gestureLast[j].f.p.x = x;
inTouch->gestureLast[j].f.p.y = y;
break;
pressure? */
} else if (event->type == SDL_EVENT_FINGER_DOWN) {
#if (GESTURE_LOG_UP_DOWN_EVENTS)
SDL_Log("GPE: Finger: %#" SDL_PRIx64 " DOWN. Device: %#" SDL_PRIx64 ", fingers: %i, x: %f, y: %f, press: %f",
event->tfinger.fingerID, event->tfinger.touchID, numDownFingersReported,
event->tfinger.x, event->tfinger.y, event->tfinger.pressure);
#endif
#if SDL_PLATFORM_MACOS
/* See comment starting at line 753. */
if (event->tfinger.fingerID == SDL_BUTTON_LEFT) return;
#endif
/* Using the number of fingers returned by SDL_GetTouchFingers
is much more robust than counting finger up and down events.
With counting it is easy for the counted number to be higher
than the actual number. Unfortunately it has not been possible
to identify a sequence of actions that reliably reproduces
this. Using the reported number is independent of how many
events are actually received. */
inTouch->numDownFingers = numDownFingersReported;
inTouch->centroid.x = inTouch->centroid.y = 0.0;
for (i = 0; i < numDownFingersReported; i++) {
inTouch->centroid.x += fingers[i]->x;
inTouch->centroid.y += fingers[i]->y;
}
inTouch->centroid.x /= numDownFingersReported;
inTouch->centroid.y /= numDownFingersReported;
//printf("Finger Down: (%f,%f). Centroid: (%f,%f\n",x,y,
// inTouch->centroid.x,inTouch->centroid.y);
inTouch->dollarPath.length = 0;
inTouch->dollarPath.p[0].x = x;
inTouch->dollarPath.p[0].y = y;
inTouch->dollarPath.numPoints = 1;
}
SDL_free(fingers);
}
}
#endif /* defined(SDL_GESTURE_IMPLEMENTATION) */
#endif /* SDL version > 2 */
#endif /* INCL_SDL_GESTURE_H */
/* vi: set sts=4 ts=4 sw=4 expandtab: */