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 | |
38 | static SDLTest_CommonState *state; |
39 | static SDL_Event events[EVENT_BUF_SIZE]; |
40 | static int eventWrite; |
41 | static int colors[7] = {0xFF,0xFF00,0xFF0000,0xFFFF00,0x00FFFF,0xFF00FF,0xFFFFFF}; |
42 | static int quitting = 0; |
43 | |
44 | typedef struct |
45 | { |
46 | float x, y; |
47 | } Point; |
48 | |
49 | typedef struct |
50 | { |
51 | float ang, r; |
52 | Point p; |
53 | } Knob; |
54 | |
55 | static Knob knob = { 0.0f, 0.1f, { 0.0f, 0.0f } }; |
56 | |
57 | |
58 | static void |
59 | setpix(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 */ |
94 | static void |
95 | drawLine(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 | |
104 | static void |
105 | drawCircle(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 | |
121 | static void |
122 | drawKnob(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 | |
129 | static void |
130 | DrawScreen(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 | |
173 | static void |
174 | loop(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 | |
271 | int 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 | |