1/*
2 Copyright (C) 1997-2021 Sam Lantinga <slouken@libsdl.org>
3
4 This software is provided 'as-is', without any express or implied
5 warranty. In no event will the authors be held liable for any damages
6 arising from the use of this software.
7
8 Permission is granted to anyone to use this software for any purpose,
9 including commercial applications, and to alter it and redistribute it
10 freely.
11*/
12
13/* Usage:
14 * Spacebar to begin recording a gesture on all touches.
15 * s to save all touches into "./gestureSave"
16 * l to load all touches from "./gestureSave"
17 */
18
19#include "SDL.h"
20#include <stdlib.h> /* for exit() */
21
22#ifdef __EMSCRIPTEN__
23#include <emscripten/emscripten.h>
24#endif
25
26#include "SDL_test.h"
27#include "SDL_test_common.h"
28
29#define WIDTH 640
30#define HEIGHT 480
31#define BPP 4
32
33/* MUST BE A POWER OF 2! */
34#define EVENT_BUF_SIZE 256
35
36#define VERBOSE 0
37
38static SDLTest_CommonState *state;
39static SDL_Event events[EVENT_BUF_SIZE];
40static int eventWrite;
41static int colors[7] = {0xFF,0xFF00,0xFF0000,0xFFFF00,0x00FFFF,0xFF00FF,0xFFFFFF};
42static int quitting = 0;
43
44typedef struct
45{
46 float x, y;
47} Point;
48
49typedef struct
50{
51 float ang, r;
52 Point p;
53} Knob;
54
55static Knob knob = { 0.0f, 0.1f, { 0.0f, 0.0f } };
56
57
58static void
59setpix(SDL_Surface *screen, float _x, float _y, unsigned int col)
60{
61 Uint32 *pixmem32;
62 Uint32 colour;
63 Uint8 r, g, b;
64 const int x = (int)_x;
65 const int y = (int)_y;
66 float a;
67
68 if ( (x < 0) || (x >= screen->w) || (y < 0) || (y >= screen->h) ) {
69 return;
70 }
71
72 pixmem32 = (Uint32 *) screen->pixels + y * screen->pitch / BPP + x;
73
74 SDL_memcpy(&colour, pixmem32, screen->format->BytesPerPixel);
75
76 SDL_GetRGB(colour,screen->format,&r,&g,&b);
77
78 /* r = 0;g = 0; b = 0; */
79 a = (float) ((col >> 24) & 0xFF);
80 if (a == 0) {
81 a = 0xFF; /* Hack, to make things easier. */
82 }
83
84 a = (a == 0.0f) ? 1 : (a / 255.0f);
85 r = (Uint8) (r * (1 - a) + ((col >> 16) & 0xFF) * a);
86 g = (Uint8) (g * (1 - a) + ((col >> 8) & 0xFF) * a);
87 b = (Uint8) (b * (1 - a) + ((col >> 0) & 0xFF) * a);
88 colour = SDL_MapRGB(screen->format, r, g, b);
89
90 *pixmem32 = colour;
91}
92
93#if 0 /* unused */
94static void
95drawLine(SDL_Surface *screen, float x0, float y0, float x1, float y1, unsigned int col)
96{
97 float t;
98 for (t = 0; t < 1; t += (float) (1.0f / SDL_max(SDL_fabs(x0 - x1), SDL_fabs(y0 - y1)))) {
99 setpix(screen, x1 + t * (x0 - x1), y1 + t * (y0 - y1), col);
100 }
101}
102#endif
103
104static void
105drawCircle(SDL_Surface *screen, float x, float y, float r, unsigned int c)
106{
107 float tx,ty, xr;
108 for (ty = (float) -SDL_fabs(r); ty <= (float) SDL_fabs((int) r); ty++) {
109 xr = (float) SDL_sqrt(r * r - ty * ty);
110 if (r > 0) { /* r > 0 ==> filled circle */
111 for(tx = -xr + 0.5f; tx <= xr - 0.5f; tx++) {
112 setpix(screen, x + tx, y + ty, c);
113 }
114 } else {
115 setpix(screen, x - xr + 0.5f, y + ty, c);
116 setpix(screen, x + xr - 0.5f, y + ty, c);
117 }
118 }
119}
120
121static void
122drawKnob(SDL_Surface *screen, const Knob *k)
123{
124 drawCircle(screen, k->p.x * screen->w, k->p.y * screen->h, k->r * screen->w, 0xFFFFFF);
125 drawCircle(screen, (k->p.x + k->r / 2 * SDL_cosf(k->ang)) * screen->w,
126 (k->p.y + k->r / 2 * SDL_sinf(k->ang)) * screen->h, k->r / 4 * screen->w, 0);
127}
128
129static void
130DrawScreen(SDL_Window *window)
131{
132 SDL_Surface *screen = SDL_GetWindowSurface(window);
133 int i;
134
135 if (!screen) {
136 return;
137 }
138
139 SDL_FillRect(screen, NULL, SDL_MapRGB(screen->format, 75, 75, 75));
140
141 /* draw Touch History */
142 for (i = eventWrite; i < eventWrite + EVENT_BUF_SIZE; ++i) {
143 const SDL_Event *event = &events[i & (EVENT_BUF_SIZE - 1)];
144 const float age = (float)(i - eventWrite) / EVENT_BUF_SIZE;
145 float x, y;
146 unsigned int c, col;
147
148 if ( (event->type == SDL_FINGERMOTION) ||
149 (event->type == SDL_FINGERDOWN) ||
150 (event->type == SDL_FINGERUP) ) {
151 x = event->tfinger.x;
152 y = event->tfinger.y;
153
154 /* draw the touch: */
155 c = colors[event->tfinger.fingerId % 7];
156 col = ((unsigned int) (c * (0.1f + 0.85f))) | (unsigned int) (0xFF * age) << 24;
157
158 if (event->type == SDL_FINGERMOTION) {
159 drawCircle(screen, x * screen->w, y * screen->h, 5, col);
160 } else if (event->type == SDL_FINGERDOWN) {
161 drawCircle(screen, x * screen->w, y * screen->h, -10, col);
162 }
163 }
164 }
165
166 if (knob.p.x > 0) {
167 drawKnob(screen, &knob);
168 }
169
170 SDL_UpdateWindowSurface(window);
171}
172
173static void
174loop(void)
175{
176 SDL_Event event;
177 SDL_RWops *stream;
178 int i;
179
180 while (SDL_PollEvent(&event)) {
181 SDLTest_CommonEvent(state, &event, &quitting);
182
183 /* Record _all_ events */
184 events[eventWrite & (EVENT_BUF_SIZE-1)] = event;
185 eventWrite++;
186
187 switch (event.type) {
188 case SDL_KEYDOWN:
189 switch (event.key.keysym.sym) {
190 case SDLK_i: {
191 for (i = 0; i < SDL_GetNumTouchDevices(); ++i) {
192 const SDL_TouchID id = SDL_GetTouchDevice(i);
193 SDL_Log("Fingers Down on device %"SDL_PRIs64": %d", id, SDL_GetNumTouchFingers(id));
194 }
195 break;
196 }
197
198 case SDLK_SPACE:
199 SDL_RecordGesture(-1);
200 break;
201
202 case SDLK_s:
203 stream = SDL_RWFromFile("gestureSave", "w");
204 SDL_Log("Wrote %i templates", SDL_SaveAllDollarTemplates(stream));
205 SDL_RWclose(stream);
206 break;
207
208 case SDLK_l:
209 stream = SDL_RWFromFile("gestureSave", "r");
210 SDL_Log("Loaded: %i", SDL_LoadDollarTemplates(-1, stream));
211 SDL_RWclose(stream);
212 break;
213 }
214 break;
215
216#if VERBOSE
217 case SDL_FINGERMOTION:
218 SDL_Log("Finger: %"SDL_PRIs64",x: %f, y: %f",event.tfinger.fingerId,
219 event.tfinger.x,event.tfinger.y);
220 break;
221
222 case SDL_FINGERDOWN:
223 SDL_Log("Finger: %"SDL_PRIs64" down - x: %f, y: %f",
224 event.tfinger.fingerId,event.tfinger.x,event.tfinger.y);
225 break;
226
227 case SDL_FINGERUP:
228 SDL_Log("Finger: %"SDL_PRIs64" up - x: %f, y: %f",
229 event.tfinger.fingerId,event.tfinger.x,event.tfinger.y);
230 break;
231#endif
232
233 case SDL_MULTIGESTURE:
234#if VERBOSE
235 SDL_Log("Multi Gesture: x = %f, y = %f, dAng = %f, dR = %f",
236 event.mgesture.x, event.mgesture.y,
237 event.mgesture.dTheta, event.mgesture.dDist);
238 SDL_Log("MG: numDownTouch = %i",event.mgesture.numFingers);
239#endif
240
241 knob.p.x = event.mgesture.x;
242 knob.p.y = event.mgesture.y;
243 knob.ang += event.mgesture.dTheta;
244 knob.r += event.mgesture.dDist;
245 break;
246
247 case SDL_DOLLARGESTURE:
248 SDL_Log("Gesture %"SDL_PRIs64" performed, error: %f",
249 event.dgesture.gestureId, event.dgesture.error);
250 break;
251
252 case SDL_DOLLARRECORD:
253 SDL_Log("Recorded gesture: %"SDL_PRIs64"",event.dgesture.gestureId);
254 break;
255 }
256 }
257
258 for (i = 0; i < state->num_windows; ++i) {
259 if (state->windows[i]) {
260 DrawScreen(state->windows[i]);
261 }
262 }
263
264#ifdef __EMSCRIPTEN__
265 if (quitting) {
266 emscripten_cancel_main_loop();
267 }
268#endif
269}
270
271int main(int argc, char* argv[])
272{
273 state = SDLTest_CommonCreateState(argv, SDL_INIT_VIDEO);
274 if (!state) {
275 return 1;
276 }
277
278 state->window_title = "Gesture Test";
279 state->window_w = WIDTH;
280 state->window_h = HEIGHT;
281 state->skip_renderer = SDL_TRUE;
282
283 if (!SDLTest_CommonDefaultArgs(state, argc, argv) || !SDLTest_CommonInit(state)) {
284 SDLTest_CommonQuit(state);
285 return 1;
286 }
287
288#ifdef __EMSCRIPTEN__
289 emscripten_set_main_loop(loop, 0, 1);
290#else
291 while (!quitting) {
292 loop();
293 }
294#endif
295
296 SDLTest_CommonQuit(state);
297 return 0;
298}
299
300