summaryrefslogtreecommitdiff
path: root/contrib/SDL-3.2.8/src/test
diff options
context:
space:
mode:
author3gg <3gg@shellblade.net>2025-12-27 12:03:39 -0800
committer3gg <3gg@shellblade.net>2025-12-27 12:03:39 -0800
commit5a079a2d114f96d4847d1ee305d5b7c16eeec50e (patch)
tree8926ab44f168acf787d8e19608857b3af0f82758 /contrib/SDL-3.2.8/src/test
Initial commit
Diffstat (limited to 'contrib/SDL-3.2.8/src/test')
-rw-r--r--contrib/SDL-3.2.8/src/test/SDL_test_assert.c157
-rw-r--r--contrib/SDL-3.2.8/src/test/SDL_test_common.c2862
-rw-r--r--contrib/SDL-3.2.8/src/test/SDL_test_compare.c218
-rw-r--r--contrib/SDL-3.2.8/src/test/SDL_test_crc32.c160
-rw-r--r--contrib/SDL-3.2.8/src/test/SDL_test_font.c159
-rw-r--r--contrib/SDL-3.2.8/src/test/SDL_test_fuzzer.c494
-rw-r--r--contrib/SDL-3.2.8/src/test/SDL_test_harness.c864
-rw-r--r--contrib/SDL-3.2.8/src/test/SDL_test_log.c211
-rw-r--r--contrib/SDL-3.2.8/src/test/SDL_test_md5.c342
-rw-r--r--contrib/SDL-3.2.8/src/test/SDL_test_memory.c457
10 files changed, 5924 insertions, 0 deletions
diff --git a/contrib/SDL-3.2.8/src/test/SDL_test_assert.c b/contrib/SDL-3.2.8/src/test/SDL_test_assert.c
new file mode 100644
index 0000000..970e986
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/test/SDL_test_assert.c
@@ -0,0 +1,157 @@
1/*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
4
5 This software is provided 'as-is', without any express or implied
6 warranty. In no event will the authors be held liable for any damages
7 arising from the use of this software.
8
9 Permission is granted to anyone to use this software for any purpose,
10 including commercial applications, and to alter it and redistribute it
11 freely, subject to the following restrictions:
12
13 1. The origin of this software must not be misrepresented; you must not
14 claim that you wrote the original software. If you use this software
15 in a product, an acknowledgment in the product documentation would be
16 appreciated but is not required.
17 2. Altered source versions must be plainly marked as such, and must not be
18 misrepresented as being the original software.
19 3. This notice may not be removed or altered from any source distribution.
20*/
21
22/*
23
24 Used by the test framework and test cases.
25
26*/
27#include <SDL3/SDL_test.h>
28
29/* Enable to have color in logs */
30#if 1
31#define COLOR_RED "\033[0;31m"
32#define COLOR_GREEN "\033[0;32m"
33#define COLOR_YELLOW "\033[0;93m"
34#define COLOR_BLUE "\033[0;94m"
35#define COLOR_END "\033[0m"
36#else
37#define COLOR_RED ""
38#define COLOR_GREEN ""
39#define COLOR_BLUE ""
40#define COLOR_YELLOW ""
41#define COLOR_END ""
42#endif
43
44/* Assert check message format */
45#define SDLTEST_ASSERT_CHECK_FORMAT "Assert '%s': %s"
46
47/* Assert summary message format */
48#define SDLTEST_ASSERT_SUMMARY_FORMAT "Assert Summary: Total=%d " COLOR_GREEN "Passed=%d" COLOR_END " " COLOR_RED "Failed=%d" COLOR_END
49#define SDLTEST_ASSERT_SUMMARY_FORMAT_OK "Assert Summary: Total=%d " COLOR_GREEN "Passed=%d" COLOR_END " " COLOR_GREEN "Failed=%d" COLOR_END
50
51/* ! counts the failed asserts */
52static int SDLTest_AssertsFailed = 0;
53
54/* ! counts the passed asserts */
55static int SDLTest_AssertsPassed = 0;
56
57/*
58 * Assert that logs and break execution flow on failures (i.e. for harness errors).
59 */
60void SDLTest_Assert(int assertCondition, SDL_PRINTF_FORMAT_STRING const char *assertDescription, ...)
61{
62 va_list list;
63 char logMessage[SDLTEST_MAX_LOGMESSAGE_LENGTH];
64
65 /* Print assert description into a buffer */
66 SDL_memset(logMessage, 0, SDLTEST_MAX_LOGMESSAGE_LENGTH);
67 va_start(list, assertDescription);
68 (void)SDL_vsnprintf(logMessage, SDLTEST_MAX_LOGMESSAGE_LENGTH - 1, assertDescription, list);
69 va_end(list);
70
71 /* Log, then assert and break on failure */
72 SDL_assert((SDLTest_AssertCheck(assertCondition, "%s", logMessage)));
73}
74
75/*
76 * Assert that logs but does not break execution flow on failures (i.e. for test cases).
77 */
78int SDLTest_AssertCheck(int assertCondition, SDL_PRINTF_FORMAT_STRING const char *assertDescription, ...)
79{
80 va_list list;
81 char logMessage[SDLTEST_MAX_LOGMESSAGE_LENGTH];
82
83 /* Print assert description into a buffer */
84 SDL_memset(logMessage, 0, SDLTEST_MAX_LOGMESSAGE_LENGTH);
85 va_start(list, assertDescription);
86 (void)SDL_vsnprintf(logMessage, SDLTEST_MAX_LOGMESSAGE_LENGTH - 1, assertDescription, list);
87 va_end(list);
88
89 /* Log pass or fail message */
90 if (assertCondition == ASSERT_FAIL) {
91 SDLTest_AssertsFailed++;
92 SDLTest_LogError(SDLTEST_ASSERT_CHECK_FORMAT, logMessage, COLOR_RED "Failed" COLOR_END);
93 } else {
94 SDLTest_AssertsPassed++;
95 SDLTest_Log(SDLTEST_ASSERT_CHECK_FORMAT, logMessage, COLOR_GREEN "Passed" COLOR_END);
96 }
97
98 return assertCondition;
99}
100
101/*
102 * Explicitly passing Assert that logs (i.e. for test cases).
103 */
104void SDLTest_AssertPass(SDL_PRINTF_FORMAT_STRING const char *assertDescription, ...)
105{
106 va_list list;
107 char logMessage[SDLTEST_MAX_LOGMESSAGE_LENGTH];
108
109 /* Print assert description into a buffer */
110 SDL_memset(logMessage, 0, SDLTEST_MAX_LOGMESSAGE_LENGTH);
111 va_start(list, assertDescription);
112 (void)SDL_vsnprintf(logMessage, SDLTEST_MAX_LOGMESSAGE_LENGTH - 1, assertDescription, list);
113 va_end(list);
114
115 /* Log pass message */
116 SDLTest_AssertsPassed++;
117 SDLTest_Log(SDLTEST_ASSERT_CHECK_FORMAT, logMessage, COLOR_GREEN "Passed" COLOR_END);
118}
119
120/*
121 * Resets the assert summary counters to zero.
122 */
123void SDLTest_ResetAssertSummary(void)
124{
125 SDLTest_AssertsPassed = 0;
126 SDLTest_AssertsFailed = 0;
127}
128
129/*
130 * Logs summary of all assertions (total, pass, fail) since last reset
131 * as INFO (failed==0) or ERROR (failed > 0).
132 */
133void SDLTest_LogAssertSummary(void)
134{
135 int totalAsserts = SDLTest_AssertsPassed + SDLTest_AssertsFailed;
136 if (SDLTest_AssertsFailed == 0) {
137 SDLTest_Log(SDLTEST_ASSERT_SUMMARY_FORMAT_OK, totalAsserts, SDLTest_AssertsPassed, SDLTest_AssertsFailed);
138 } else {
139 SDLTest_LogError(SDLTEST_ASSERT_SUMMARY_FORMAT, totalAsserts, SDLTest_AssertsPassed, SDLTest_AssertsFailed);
140 }
141}
142
143/*
144 * Converts the current assert state into a test result
145 */
146int SDLTest_AssertSummaryToTestResult(void)
147{
148 if (SDLTest_AssertsFailed > 0) {
149 return TEST_RESULT_FAILED;
150 } else {
151 if (SDLTest_AssertsPassed > 0) {
152 return TEST_RESULT_PASSED;
153 } else {
154 return TEST_RESULT_NO_ASSERT;
155 }
156 }
157}
diff --git a/contrib/SDL-3.2.8/src/test/SDL_test_common.c b/contrib/SDL-3.2.8/src/test/SDL_test_common.c
new file mode 100644
index 0000000..3203dde
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/test/SDL_test_common.c
@@ -0,0 +1,2862 @@
1/*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
4
5 This software is provided 'as-is', without any express or implied
6 warranty. In no event will the authors be held liable for any damages
7 arising from the use of this software.
8
9 Permission is granted to anyone to use this software for any purpose,
10 including commercial applications, and to alter it and redistribute it
11 freely, subject to the following restrictions:
12
13 1. The origin of this software must not be misrepresented; you must not
14 claim that you wrote the original software. If you use this software
15 in a product, an acknowledgment in the product documentation would be
16 appreciated but is not required.
17 2. Altered source versions must be plainly marked as such, and must not be
18 misrepresented as being the original software.
19 3. This notice may not be removed or altered from any source distribution.
20*/
21
22/* Ported from original test/common.c file. */
23#include <SDL3/SDL_test.h>
24
25#define SDL_MAIN_NOIMPL
26#define SDL_MAIN_USE_CALLBACKS
27#include <SDL3/SDL_main.h>
28
29static const char *common_usage[] = {
30 "[-h | --help]",
31 "[--trackmem]",
32 "[--randmem]",
33 "[--info all|video|modes|render|event|event_motion]",
34 "[--log all|error|system|audio|video|render|input]",
35 NULL
36};
37
38static const char *video_usage[] = {
39 "[--always-on-top]",
40 "[--aspect min-max]",
41 "[--auto-scale-content]",
42 "[--center | --position X,Y]",
43 "[--confine-cursor X,Y,W,H]",
44 "[--depth N]",
45 "[--display N]",
46 "[--flash-on-focus-loss]",
47 "[--fullscreen | --fullscreen-desktop | --windows N]",
48 "[--geometry WxH]",
49 "[--gldebug]",
50 "[--grab]",
51 "[--hidden]",
52 "[--hide-cursor]",
53 "[--high-pixel-density]",
54 "[--icon icon.bmp]",
55 "[--input-focus]",
56 "[--keyboard-grab]",
57 "[--logical-presentation disabled|match|stretch|letterbox|overscan|integer_scale]",
58 "[--logical-scale-quality nearest|linear|best]",
59 "[--logical WxH]",
60 "[--max-geometry WxH]",
61 "[--maximize]",
62 "[--metal-window | --opengl-window | --vulkan-window]",
63 "[--min-geometry WxH]",
64 "[--minimize]",
65 "[--mouse-focus]",
66 "[--noframe]",
67 "[--refresh R]",
68 "[--renderer driver]",
69 "[--resizable]",
70 "[--scale N]",
71 "[--title title]",
72 "[--transparent]",
73 "[--usable-bounds]",
74 "[--utility]",
75 "[--video driver]",
76 "[--gpu driver]",
77 "[--vsync]",
78 NULL
79};
80
81/* !!! FIXME: Float32? Sint32? */
82static const char *audio_usage[] = {
83 "[--audio driver]",
84 "[--rate N]",
85 "[--format U8|S8|S16|S16LE|S16BE|S32|S32LE|S32BE|F32|F32LE|F32BE]",
86 "[--channels N]",
87 NULL
88};
89
90static void SDL_snprintfcat(SDL_OUT_Z_CAP(maxlen) char *text, size_t maxlen, SDL_PRINTF_FORMAT_STRING const char *fmt, ...)
91{
92 size_t length = SDL_strlen(text);
93 va_list ap;
94
95 va_start(ap, fmt);
96 text += length;
97 maxlen -= length;
98 (void)SDL_vsnprintf(text, maxlen, fmt, ap);
99 va_end(ap);
100}
101
102static void SDLCALL SDLTest_CommonArgParserFinalize(void *data)
103{
104 SDLTest_CommonState *state = data;
105
106 if (!(state->flags & SDL_INIT_VIDEO)) {
107 state->video_argparser.usage = NULL;
108 }
109 if (!(state->flags & SDL_INIT_AUDIO)) {
110 state->audio_argparser.usage = NULL;
111 }
112}
113
114#define SEARCHARG(dim) \
115 while (*(dim) && *(dim) != ',') { \
116 ++(dim); \
117 } \
118 if (!*(dim)) { \
119 return -1; \
120 } \
121 *(dim)++ = '\0';
122
123static int SDLCALL SDLTest_CommonStateParseCommonArguments(void *data, char **argv, int index)
124{
125 SDLTest_CommonState *state = data;
126
127 if ((SDL_strcasecmp(argv[index], "-h") == 0) || (SDL_strcasecmp(argv[index], "--help") == 0)) {
128 /* Print the usage message */
129 return -1;
130 }
131 if (SDL_strcasecmp(argv[index], "--trackmem") == 0) {
132 /* Already handled in SDLTest_CommonCreateState() */
133 return 1;
134 }
135 if (SDL_strcasecmp(argv[index], "--randmem") == 0) {
136 /* Already handled in SDLTest_CommonCreateState() */
137 return 1;
138 }
139 if (SDL_strcasecmp(argv[index], "--log") == 0) {
140 ++index;
141 if (!argv[index]) {
142 return -1;
143 }
144 if (SDL_strcasecmp(argv[index], "all") == 0) {
145 SDL_SetLogPriorities(SDL_LOG_PRIORITY_VERBOSE);
146 return 2;
147 }
148 if (SDL_strcasecmp(argv[index], "system") == 0) {
149 SDL_SetLogPriority(SDL_LOG_CATEGORY_SYSTEM, SDL_LOG_PRIORITY_VERBOSE);
150 return 2;
151 }
152 if (SDL_strcasecmp(argv[index], "audio") == 0) {
153 SDL_SetLogPriority(SDL_LOG_CATEGORY_AUDIO, SDL_LOG_PRIORITY_VERBOSE);
154 return 2;
155 }
156 if (SDL_strcasecmp(argv[index], "video") == 0) {
157 SDL_SetLogPriority(SDL_LOG_CATEGORY_VIDEO, SDL_LOG_PRIORITY_VERBOSE);
158 return 2;
159 }
160 if (SDL_strcasecmp(argv[index], "render") == 0) {
161 SDL_SetLogPriority(SDL_LOG_CATEGORY_RENDER, SDL_LOG_PRIORITY_VERBOSE);
162 return 2;
163 }
164 if (SDL_strcasecmp(argv[index], "input") == 0) {
165 SDL_SetLogPriority(SDL_LOG_CATEGORY_INPUT, SDL_LOG_PRIORITY_VERBOSE);
166 return 2;
167 }
168 return -1;
169 }
170
171 if (SDL_strcasecmp(argv[index], "--info") == 0) {
172 ++index;
173 if (!argv[index]) {
174 return -1;
175 }
176 if (SDL_strcasecmp(argv[index], "all") == 0) {
177 state->verbose |=
178 (VERBOSE_VIDEO | VERBOSE_MODES | VERBOSE_RENDER |
179 VERBOSE_EVENT);
180 return 2;
181 }
182 if (SDL_strcasecmp(argv[index], "video") == 0) {
183 state->verbose |= VERBOSE_VIDEO;
184 return 2;
185 }
186 if (SDL_strcasecmp(argv[index], "modes") == 0) {
187 state->verbose |= VERBOSE_MODES;
188 return 2;
189 }
190 if (SDL_strcasecmp(argv[index], "render") == 0) {
191 state->verbose |= VERBOSE_RENDER;
192 return 2;
193 }
194 if (SDL_strcasecmp(argv[index], "event") == 0) {
195 state->verbose |= VERBOSE_EVENT;
196 return 2;
197 }
198 if (SDL_strcasecmp(argv[index], "event_motion") == 0) {
199 state->verbose |= (VERBOSE_EVENT | VERBOSE_MOTION);
200 return 2;
201 }
202 return -1;
203 }
204 if (SDL_strcmp(argv[index], "-NSDocumentRevisionsDebugMode") == 0) {
205 /* Debug flag sent by Xcode */
206 return 2;
207 }
208 return 0;
209}
210
211static int SDLCALL SDLTest_CommonStateParseVideoArguments(void *data, char **argv, int index)
212{
213 SDLTest_CommonState *state = data;
214
215 if (!(state->flags & SDL_INIT_VIDEO)) {
216 return 0;
217 }
218
219 if (SDL_strcasecmp(argv[index], "--video") == 0) {
220 ++index;
221 if (!argv[index]) {
222 return -1;
223 }
224 state->videodriver = argv[index];
225 SDL_SetHint(SDL_HINT_VIDEO_DRIVER, state->videodriver);
226 return 2;
227 }
228 if (SDL_strcasecmp(argv[index], "--renderer") == 0) {
229 ++index;
230 if (!argv[index]) {
231 return -1;
232 }
233 state->renderdriver = argv[index];
234 SDL_SetHint(SDL_HINT_RENDER_DRIVER, state->renderdriver);
235 return 2;
236 }
237 if (SDL_strcasecmp(argv[index], "--gldebug") == 0) {
238 state->gl_debug = 1;
239 return 1;
240 }
241 if (SDL_strcasecmp(argv[index], "--display") == 0) {
242 ++index;
243 if (!argv[index]) {
244 return -1;
245 }
246 state->display_index = SDL_atoi(argv[index]);
247 return 2;
248 }
249 if (SDL_strcasecmp(argv[index], "--metal-window") == 0) {
250 state->window_flags |= SDL_WINDOW_METAL;
251 return 1;
252 }
253 if (SDL_strcasecmp(argv[index], "--opengl-window") == 0) {
254 state->window_flags |= SDL_WINDOW_OPENGL;
255 return 1;
256 }
257 if (SDL_strcasecmp(argv[index], "--vulkan-window") == 0) {
258 state->window_flags |= SDL_WINDOW_VULKAN;
259 return 1;
260 }
261 if (SDL_strcasecmp(argv[index], "--fullscreen") == 0) {
262 state->window_flags |= SDL_WINDOW_FULLSCREEN;
263 state->fullscreen_exclusive = true;
264 state->num_windows = 1;
265 return 1;
266 }
267 if (SDL_strcasecmp(argv[index], "--fullscreen-desktop") == 0) {
268 state->window_flags |= SDL_WINDOW_FULLSCREEN;
269 state->fullscreen_exclusive = false;
270 state->num_windows = 1;
271 return 1;
272 }
273 if (SDL_strcasecmp(argv[index], "--windows") == 0) {
274 ++index;
275 if (!argv[index] || !SDL_isdigit((unsigned char) *argv[index])) {
276 return -1;
277 }
278 if (!(state->window_flags & SDL_WINDOW_FULLSCREEN)) {
279 state->num_windows = SDL_atoi(argv[index]);
280 }
281 return 2;
282 }
283 if (SDL_strcasecmp(argv[index], "--title") == 0) {
284 ++index;
285 if (!argv[index]) {
286 return -1;
287 }
288 state->window_title = argv[index];
289 return 2;
290 }
291 if (SDL_strcasecmp(argv[index], "--icon") == 0) {
292 ++index;
293 if (!argv[index]) {
294 return -1;
295 }
296 state->window_icon = argv[index];
297 return 2;
298 }
299 if (SDL_strcasecmp(argv[index], "--center") == 0) {
300 state->window_x = SDL_WINDOWPOS_CENTERED;
301 state->window_y = SDL_WINDOWPOS_CENTERED;
302 return 1;
303 }
304 if (SDL_strcasecmp(argv[index], "--position") == 0) {
305 char *x, *y;
306 ++index;
307 if (!argv[index]) {
308 return -1;
309 }
310 x = argv[index];
311 y = argv[index];
312 while (*y && *y != ',') {
313 ++y;
314 }
315 if (!*y) {
316 return -1;
317 }
318 *y++ = '\0';
319 state->window_x = SDL_atoi(x);
320 state->window_y = SDL_atoi(y);
321 return 2;
322 }
323 if (SDL_strcasecmp(argv[index], "--confine-cursor") == 0) {
324 char *x, *y, *w, *h;
325 ++index;
326 if (!argv[index]) {
327 return -1;
328 }
329 x = argv[index];
330 y = argv[index];
331 SEARCHARG(y)
332 w = y;
333 SEARCHARG(w)
334 h = w;
335 SEARCHARG(h)
336 state->confine.x = SDL_atoi(x);
337 state->confine.y = SDL_atoi(y);
338 state->confine.w = SDL_atoi(w);
339 state->confine.h = SDL_atoi(h);
340 return 2;
341 }
342 if (SDL_strcasecmp(argv[index], "--usable-bounds") == 0) {
343 state->fill_usable_bounds = true;
344 return 1;
345 }
346 if (SDL_strcasecmp(argv[index], "--geometry") == 0) {
347 char *w, *h;
348 ++index;
349 if (!argv[index]) {
350 return -1;
351 }
352 w = argv[index];
353 h = argv[index];
354 while (*h && *h != 'x') {
355 ++h;
356 }
357 if (!*h) {
358 return -1;
359 }
360 *h++ = '\0';
361 state->window_w = SDL_atoi(w);
362 state->window_h = SDL_atoi(h);
363 return 2;
364 }
365 if (SDL_strcasecmp(argv[index], "--min-geometry") == 0) {
366 char *w, *h;
367 ++index;
368 if (!argv[index]) {
369 return -1;
370 }
371 w = argv[index];
372 h = argv[index];
373 while (*h && *h != 'x') {
374 ++h;
375 }
376 if (!*h) {
377 return -1;
378 }
379 *h++ = '\0';
380 state->window_minW = SDL_atoi(w);
381 state->window_minH = SDL_atoi(h);
382 return 2;
383 }
384 if (SDL_strcasecmp(argv[index], "--max-geometry") == 0) {
385 char *w, *h;
386 ++index;
387 if (!argv[index]) {
388 return -1;
389 }
390 w = argv[index];
391 h = argv[index];
392 while (*h && *h != 'x') {
393 ++h;
394 }
395 if (!*h) {
396 return -1;
397 }
398 *h++ = '\0';
399 state->window_maxW = SDL_atoi(w);
400 state->window_maxH = SDL_atoi(h);
401 return 2;
402 }
403 if (SDL_strcasecmp(argv[index], "--aspect") == 0) {
404 char *min_aspect, *max_aspect;
405 ++index;
406 if (!argv[index]) {
407 return -1;
408 }
409 min_aspect = argv[index];
410 max_aspect = argv[index];
411 while (*max_aspect && *max_aspect != '-') {
412 ++max_aspect;
413 }
414 if (*max_aspect) {
415 *max_aspect++ = '\0';
416 } else {
417 max_aspect = min_aspect;
418 }
419 state->window_min_aspect = (float)SDL_atof(min_aspect);
420 state->window_max_aspect = (float)SDL_atof(max_aspect);
421 return 2;
422 }
423 if (SDL_strcasecmp(argv[index], "--logical") == 0) {
424 char *w, *h;
425 ++index;
426 if (!argv[index]) {
427 return -1;
428 }
429 w = argv[index];
430 h = argv[index];
431 while (*h && *h != 'x') {
432 ++h;
433 }
434 if (!*h) {
435 return -1;
436 }
437 *h++ = '\0';
438 state->logical_w = SDL_atoi(w);
439 state->logical_h = SDL_atoi(h);
440 return 2;
441 }
442 if (SDL_strcasecmp(argv[index], "--high-pixel-density") == 0) {
443 state->window_flags |= SDL_WINDOW_HIGH_PIXEL_DENSITY;
444 return 1;
445 }
446 if (SDL_strcasecmp(argv[index], "--auto-scale-content") == 0) {
447 state->auto_scale_content = true;
448
449 if (state->logical_presentation == SDL_LOGICAL_PRESENTATION_DISABLED) {
450 state->logical_presentation = SDL_LOGICAL_PRESENTATION_STRETCH;
451 }
452 return 1;
453 }
454 if (SDL_strcasecmp(argv[index], "--logical-presentation") == 0) {
455 ++index;
456 if (!argv[index]) {
457 return -1;
458 }
459 if (SDL_strcasecmp(argv[index], "disabled") == 0) {
460 state->logical_presentation = SDL_LOGICAL_PRESENTATION_DISABLED;
461 return 2;
462 }
463 if (SDL_strcasecmp(argv[index], "stretch") == 0) {
464 state->logical_presentation = SDL_LOGICAL_PRESENTATION_STRETCH;
465 return 2;
466 }
467 if (SDL_strcasecmp(argv[index], "letterbox") == 0) {
468 state->logical_presentation = SDL_LOGICAL_PRESENTATION_LETTERBOX;
469 return 2;
470 }
471 if (SDL_strcasecmp(argv[index], "overscan") == 0) {
472 state->logical_presentation = SDL_LOGICAL_PRESENTATION_OVERSCAN;
473 return 2;
474 }
475 if (SDL_strcasecmp(argv[index], "integer_scale") == 0) {
476 state->logical_presentation = SDL_LOGICAL_PRESENTATION_INTEGER_SCALE;
477 return 2;
478 }
479 return -1;
480 }
481 if (SDL_strcasecmp(argv[index], "--scale") == 0) {
482 ++index;
483 if (!argv[index]) {
484 return -1;
485 }
486 state->scale = (float) SDL_atof(argv[index]);
487 return 2;
488 }
489 if (SDL_strcasecmp(argv[index], "--depth") == 0) {
490 ++index;
491 if (!argv[index]) {
492 return -1;
493 }
494 state->depth = SDL_atoi(argv[index]);
495 return 2;
496 }
497 if (SDL_strcasecmp(argv[index], "--refresh") == 0) {
498 ++index;
499 if (!argv[index]) {
500 return -1;
501 }
502 state->refresh_rate = (float) SDL_atof(argv[index]);
503 return 2;
504 }
505 if (SDL_strcasecmp(argv[index], "--vsync") == 0) {
506 state->render_vsync = 1;
507 return 1;
508 }
509 if (SDL_strcasecmp(argv[index], "--noframe") == 0) {
510 state->window_flags |= SDL_WINDOW_BORDERLESS;
511 return 1;
512 }
513 if (SDL_strcasecmp(argv[index], "--resizable") == 0) {
514 state->window_flags |= SDL_WINDOW_RESIZABLE;
515 return 1;
516 }
517 if (SDL_strcasecmp(argv[index], "--transparent") == 0) {
518 state->window_flags |= SDL_WINDOW_TRANSPARENT;
519 return 1;
520 }
521 if (SDL_strcasecmp(argv[index], "--always-on-top") == 0) {
522 state->window_flags |= SDL_WINDOW_ALWAYS_ON_TOP;
523 return 1;
524 }
525 if (SDL_strcasecmp(argv[index], "--minimize") == 0) {
526 state->window_flags |= SDL_WINDOW_MINIMIZED;
527 return 1;
528 }
529 if (SDL_strcasecmp(argv[index], "--maximize") == 0) {
530 state->window_flags |= SDL_WINDOW_MAXIMIZED;
531 return 1;
532 }
533 if (SDL_strcasecmp(argv[index], "--hidden") == 0) {
534 state->window_flags |= SDL_WINDOW_HIDDEN;
535 return 1;
536 }
537 if (SDL_strcasecmp(argv[index], "--input-focus") == 0) {
538 state->window_flags |= SDL_WINDOW_INPUT_FOCUS;
539 return 1;
540 }
541 if (SDL_strcasecmp(argv[index], "--mouse-focus") == 0) {
542 state->window_flags |= SDL_WINDOW_MOUSE_FOCUS;
543 return 1;
544 }
545 if (SDL_strcasecmp(argv[index], "--flash-on-focus-loss") == 0) {
546 state->flash_on_focus_loss = true;
547 return 1;
548 }
549 if (SDL_strcasecmp(argv[index], "--grab") == 0) {
550 state->window_flags |= SDL_WINDOW_MOUSE_GRABBED;
551 return 1;
552 }
553 if (SDL_strcasecmp(argv[index], "--keyboard-grab") == 0) {
554 state->window_flags |= SDL_WINDOW_KEYBOARD_GRABBED;
555 return 1;
556 }
557 if (SDL_strcasecmp(argv[index], "--utility") == 0) {
558 state->window_flags |= SDL_WINDOW_UTILITY;
559 return 1;
560 }
561 if (SDL_strcasecmp(argv[index], "--hide-cursor") == 0) {
562 state->hide_cursor = true;
563 return 1;
564 }
565 if (SDL_strcasecmp(argv[index], "--gpu") == 0) {
566 ++index;
567 if (!argv[index]) {
568 return -1;
569 }
570 state->gpudriver = argv[index];
571 SDL_SetHint(SDL_HINT_GPU_DRIVER, state->gpudriver);
572 return 2;
573 }
574 return 0;
575}
576
577static int SDLCALL SDLTest_CommonStateParseAudioArguments(void *data, char **argv, int index)
578{
579 SDLTest_CommonState *state = data;
580
581 if (!(state->flags & SDL_INIT_AUDIO)) {
582 return 0;
583 }
584 if (SDL_strcasecmp(argv[index], "--audio") == 0) {
585 ++index;
586 if (!argv[index]) {
587 return -1;
588 }
589 state->audiodriver = argv[index];
590 SDL_SetHint(SDL_HINT_AUDIO_DRIVER, state->audiodriver);
591 return 2;
592 }
593 if (SDL_strcasecmp(argv[index], "--rate") == 0) {
594 ++index;
595 if (!argv[index]) {
596 return -1;
597 }
598 state->audio_freq = SDL_atoi(argv[index]);
599 return 2;
600 }
601 if (SDL_strcasecmp(argv[index], "--format") == 0) {
602 ++index;
603 if (!argv[index]) {
604 return -1;
605 }
606 if (SDL_strcasecmp(argv[index], "U8") == 0) {
607 state->audio_format = SDL_AUDIO_U8;
608 return 2;
609 }
610 if (SDL_strcasecmp(argv[index], "S8") == 0) {
611 state->audio_format = SDL_AUDIO_S8;
612 return 2;
613 }
614 if (SDL_strcasecmp(argv[index], "S16") == 0) {
615 state->audio_format = SDL_AUDIO_S16;
616 return 2;
617 }
618 if (SDL_strcasecmp(argv[index], "S16LE") == 0) {
619 state->audio_format = SDL_AUDIO_S16LE;
620 return 2;
621 }
622 if (SDL_strcasecmp(argv[index], "S16BE") == 0) {
623 state->audio_format = SDL_AUDIO_S16BE;
624 return 2;
625 }
626 if (SDL_strcasecmp(argv[index], "S32") == 0) {
627 state->audio_format = SDL_AUDIO_S32;
628 return 2;
629 }
630 if (SDL_strcasecmp(argv[index], "S32LE") == 0) {
631 state->audio_format = SDL_AUDIO_S32LE;
632 return 2;
633 }
634 if (SDL_strcasecmp(argv[index], "S32BE") == 0) {
635 state->audio_format = SDL_AUDIO_S32BE;
636 return 2;
637 }
638 if (SDL_strcasecmp(argv[index], "F32") == 0) {
639 state->audio_format = SDL_AUDIO_F32;
640 return 2;
641 }
642 if (SDL_strcasecmp(argv[index], "F32LE") == 0) {
643 state->audio_format = SDL_AUDIO_F32LE;
644 return 2;
645 }
646 if (SDL_strcasecmp(argv[index], "F32BE") == 0) {
647 state->audio_format = SDL_AUDIO_F32BE;
648 return 2;
649 }
650 return -1;
651 }
652 if (SDL_strcasecmp(argv[index], "--channels") == 0) {
653 ++index;
654 if (!argv[index]) {
655 return -1;
656 }
657 state->audio_channels = (Uint8) SDL_atoi(argv[index]);
658 return 2;
659 }
660 return 0;
661}
662
663SDLTest_CommonState *SDLTest_CommonCreateState(char **argv, SDL_InitFlags flags)
664{
665 int i;
666 SDLTest_CommonState *state;
667
668 /* Do this first so we catch all allocations */
669 for (i = 1; argv[i]; ++i) {
670 if (SDL_strcasecmp(argv[i], "--trackmem") == 0) {
671 SDLTest_TrackAllocations();
672 } else if (SDL_strcasecmp(argv[i], "--randmem") == 0) {
673 SDLTest_RandFillAllocations();
674 }
675 }
676
677 state = (SDLTest_CommonState *)SDL_calloc(1, sizeof(*state));
678 if (!state) {
679 return NULL;
680 }
681
682 /* Initialize some defaults */
683 state->argv = argv;
684 state->flags = flags;
685 state->window_title = argv[0];
686 state->window_flags = SDL_WINDOW_HIDDEN;
687 state->window_x = SDL_WINDOWPOS_UNDEFINED;
688 state->window_y = SDL_WINDOWPOS_UNDEFINED;
689 state->window_w = DEFAULT_WINDOW_WIDTH;
690 state->window_h = DEFAULT_WINDOW_HEIGHT;
691 state->logical_presentation = SDL_LOGICAL_PRESENTATION_DISABLED;
692 state->num_windows = 1;
693 state->audio_freq = 22050;
694 state->audio_format = SDL_AUDIO_S16;
695 state->audio_channels = 2;
696
697 /* Set some very sane GL defaults */
698 state->gl_red_size = 8;
699 state->gl_green_size = 8;
700 state->gl_blue_size = 8;
701 state->gl_alpha_size = 8;
702 state->gl_buffer_size = 0;
703 state->gl_depth_size = 16;
704 state->gl_stencil_size = 0;
705 state->gl_double_buffer = 1;
706 state->gl_accum_red_size = 0;
707 state->gl_accum_green_size = 0;
708 state->gl_accum_blue_size = 0;
709 state->gl_accum_alpha_size = 0;
710 state->gl_stereo = 0;
711 state->gl_multisamplebuffers = 0;
712 state->gl_multisamplesamples = 0;
713 state->gl_retained_backing = 1;
714 state->gl_accelerated = -1;
715 state->gl_debug = 0;
716
717 state->common_argparser.parse_arguments = SDLTest_CommonStateParseCommonArguments;
718 state->common_argparser.finalize = SDLTest_CommonArgParserFinalize;
719 state->common_argparser.usage = common_usage;
720 state->common_argparser.data = state;
721 state->common_argparser.next = &state->video_argparser;
722
723 state->video_argparser.parse_arguments = SDLTest_CommonStateParseVideoArguments;
724 state->video_argparser.finalize = NULL;
725 state->video_argparser.usage = video_usage;
726 state->video_argparser.data = state;
727 state->video_argparser.next = &state->audio_argparser;
728
729 state->audio_argparser.parse_arguments = SDLTest_CommonStateParseAudioArguments;
730 state->audio_argparser.finalize = NULL;
731 state->audio_argparser.usage = audio_usage;
732 state->audio_argparser.data = state;
733
734 state->argparser = &state->common_argparser;
735
736 return state;
737}
738
739void SDLTest_CommonDestroyState(SDLTest_CommonState *state) {
740 SDL_free(state);
741 SDLTest_LogAllocations();
742}
743
744int SDLTest_CommonArg(SDLTest_CommonState *state, int index)
745{
746 SDLTest_ArgumentParser *argparser = state->argparser;
747
748 /* Go back and parse arguments as we go */
749 while (argparser) {
750 if (argparser->parse_arguments) {
751 int consumed = argparser->parse_arguments(argparser->data, state->argv, index);
752 if (consumed != 0) {
753 return consumed;
754 }
755 }
756 argparser = argparser->next;
757 }
758 return 0;
759}
760
761void SDLTest_CommonLogUsage(SDLTest_CommonState *state, const char *argv0, const char **options)
762{
763 SDLTest_ArgumentParser *argparser;
764
765 SDL_Log("USAGE: %s", argv0);
766
767 for (argparser = state->argparser; argparser; argparser = argparser->next) {
768 if (argparser->finalize) {
769 argparser->finalize(argparser->data);
770 }
771 if (argparser->usage) {
772 int i;
773 for (i = 0; argparser->usage[i] != NULL; i++) {
774 SDL_Log(" %s", argparser->usage[i]);
775 }
776 }
777 }
778 if (options) {
779 int i;
780 for (i = 0; options[i] != NULL; i++) {
781 SDL_Log(" %s", options[i]);
782 }
783 }
784}
785
786bool SDLTest_CommonDefaultArgs(SDLTest_CommonState *state, int argc, char **argv)
787{
788 int i = 1;
789 while (i < argc) {
790 const int consumed = SDLTest_CommonArg(state, i);
791 if (consumed <= 0) {
792 SDLTest_CommonLogUsage(state, argv[0], NULL);
793 return false;
794 }
795 i += consumed;
796 }
797 return true;
798}
799
800static void SDLTest_PrintDisplayOrientation(char *text, size_t maxlen, SDL_DisplayOrientation orientation)
801{
802 switch (orientation) {
803 case SDL_ORIENTATION_UNKNOWN:
804 SDL_snprintfcat(text, maxlen, "UNKNOWN");
805 break;
806 case SDL_ORIENTATION_LANDSCAPE:
807 SDL_snprintfcat(text, maxlen, "LANDSCAPE");
808 break;
809 case SDL_ORIENTATION_LANDSCAPE_FLIPPED:
810 SDL_snprintfcat(text, maxlen, "LANDSCAPE_FLIPPED");
811 break;
812 case SDL_ORIENTATION_PORTRAIT:
813 SDL_snprintfcat(text, maxlen, "PORTRAIT");
814 break;
815 case SDL_ORIENTATION_PORTRAIT_FLIPPED:
816 SDL_snprintfcat(text, maxlen, "PORTRAIT_FLIPPED");
817 break;
818 default:
819 SDL_snprintfcat(text, maxlen, "0x%8.8x", orientation);
820 break;
821 }
822}
823
824static void SDLTest_PrintWindowFlag(char *text, size_t maxlen, SDL_WindowFlags flag)
825{
826 switch (flag) {
827 case SDL_WINDOW_FULLSCREEN:
828 SDL_snprintfcat(text, maxlen, "FULLSCREEN");
829 break;
830 case SDL_WINDOW_OPENGL:
831 SDL_snprintfcat(text, maxlen, "OPENGL");
832 break;
833 case SDL_WINDOW_OCCLUDED:
834 SDL_snprintfcat(text, maxlen, "OCCLUDED");
835 break;
836 case SDL_WINDOW_HIDDEN:
837 SDL_snprintfcat(text, maxlen, "HIDDEN");
838 break;
839 case SDL_WINDOW_BORDERLESS:
840 SDL_snprintfcat(text, maxlen, "BORDERLESS");
841 break;
842 case SDL_WINDOW_RESIZABLE:
843 SDL_snprintfcat(text, maxlen, "RESIZABLE");
844 break;
845 case SDL_WINDOW_MINIMIZED:
846 SDL_snprintfcat(text, maxlen, "MINIMIZED");
847 break;
848 case SDL_WINDOW_MAXIMIZED:
849 SDL_snprintfcat(text, maxlen, "MAXIMIZED");
850 break;
851 case SDL_WINDOW_MOUSE_GRABBED:
852 SDL_snprintfcat(text, maxlen, "MOUSE_GRABBED");
853 break;
854 case SDL_WINDOW_INPUT_FOCUS:
855 SDL_snprintfcat(text, maxlen, "INPUT_FOCUS");
856 break;
857 case SDL_WINDOW_MOUSE_FOCUS:
858 SDL_snprintfcat(text, maxlen, "MOUSE_FOCUS");
859 break;
860 case SDL_WINDOW_EXTERNAL:
861 SDL_snprintfcat(text, maxlen, "EXTERNAL");
862 break;
863 case SDL_WINDOW_MODAL:
864 SDL_snprintfcat(text, maxlen, "MODAL");
865 break;
866 case SDL_WINDOW_HIGH_PIXEL_DENSITY:
867 SDL_snprintfcat(text, maxlen, "HIGH_PIXEL_DENSITY");
868 break;
869 case SDL_WINDOW_MOUSE_CAPTURE:
870 SDL_snprintfcat(text, maxlen, "MOUSE_CAPTURE");
871 break;
872 case SDL_WINDOW_MOUSE_RELATIVE_MODE:
873 SDL_snprintfcat(text, maxlen, "MOUSE_RELATIVE_MODE");
874 break;
875 case SDL_WINDOW_ALWAYS_ON_TOP:
876 SDL_snprintfcat(text, maxlen, "ALWAYS_ON_TOP");
877 break;
878 case SDL_WINDOW_UTILITY:
879 SDL_snprintfcat(text, maxlen, "UTILITY");
880 break;
881 case SDL_WINDOW_TOOLTIP:
882 SDL_snprintfcat(text, maxlen, "TOOLTIP");
883 break;
884 case SDL_WINDOW_POPUP_MENU:
885 SDL_snprintfcat(text, maxlen, "POPUP_MENU");
886 break;
887 case SDL_WINDOW_KEYBOARD_GRABBED:
888 SDL_snprintfcat(text, maxlen, "KEYBOARD_GRABBED");
889 break;
890 case SDL_WINDOW_VULKAN:
891 SDL_snprintfcat(text, maxlen, "VULKAN");
892 break;
893 case SDL_WINDOW_METAL:
894 SDL_snprintfcat(text, maxlen, "METAL");
895 break;
896 case SDL_WINDOW_TRANSPARENT:
897 SDL_snprintfcat(text, maxlen, "TRANSPARENT");
898 break;
899 case SDL_WINDOW_NOT_FOCUSABLE:
900 SDL_snprintfcat(text, maxlen, "NOT_FOCUSABLE");
901 break;
902 default:
903 SDL_snprintfcat(text, maxlen, "0x%16.16" SDL_PRIx64, flag);
904 break;
905 }
906}
907
908static void SDLTest_PrintWindowFlags(char *text, size_t maxlen, SDL_WindowFlags flags)
909{
910 const SDL_WindowFlags window_flags[] = {
911 SDL_WINDOW_FULLSCREEN,
912 SDL_WINDOW_OPENGL,
913 SDL_WINDOW_OCCLUDED,
914 SDL_WINDOW_HIDDEN,
915 SDL_WINDOW_BORDERLESS,
916 SDL_WINDOW_RESIZABLE,
917 SDL_WINDOW_MINIMIZED,
918 SDL_WINDOW_MAXIMIZED,
919 SDL_WINDOW_MOUSE_GRABBED,
920 SDL_WINDOW_INPUT_FOCUS,
921 SDL_WINDOW_MOUSE_FOCUS,
922 SDL_WINDOW_EXTERNAL,
923 SDL_WINDOW_MODAL,
924 SDL_WINDOW_HIGH_PIXEL_DENSITY,
925 SDL_WINDOW_MOUSE_CAPTURE,
926 SDL_WINDOW_MOUSE_RELATIVE_MODE,
927 SDL_WINDOW_ALWAYS_ON_TOP,
928 SDL_WINDOW_UTILITY,
929 SDL_WINDOW_TOOLTIP,
930 SDL_WINDOW_POPUP_MENU,
931 SDL_WINDOW_KEYBOARD_GRABBED,
932 SDL_WINDOW_VULKAN,
933 SDL_WINDOW_METAL,
934 SDL_WINDOW_TRANSPARENT,
935 SDL_WINDOW_NOT_FOCUSABLE
936 };
937
938 int i;
939 int count = 0;
940 for (i = 0; i < (sizeof(window_flags) / sizeof(window_flags[0])); ++i) {
941 const SDL_WindowFlags flag = window_flags[i];
942 if ((flags & flag) == flag) {
943 if (count > 0) {
944 SDL_snprintfcat(text, maxlen, " | ");
945 }
946 SDLTest_PrintWindowFlag(text, maxlen, flag);
947 ++count;
948 }
949 }
950}
951
952static void SDLTest_PrintModStateFlag(char *text, size_t maxlen, SDL_Keymod flag)
953{
954 switch (flag) {
955 case SDL_KMOD_LSHIFT:
956 SDL_snprintfcat(text, maxlen, "LSHIFT");
957 break;
958 case SDL_KMOD_RSHIFT:
959 SDL_snprintfcat(text, maxlen, "RSHIFT");
960 break;
961 case SDL_KMOD_LEVEL5:
962 SDL_snprintfcat(text, maxlen, "LEVEL5");
963 break;
964 case SDL_KMOD_LCTRL:
965 SDL_snprintfcat(text, maxlen, "LCTRL");
966 break;
967 case SDL_KMOD_RCTRL:
968 SDL_snprintfcat(text, maxlen, "RCTRL");
969 break;
970 case SDL_KMOD_LALT:
971 SDL_snprintfcat(text, maxlen, "LALT");
972 break;
973 case SDL_KMOD_RALT:
974 SDL_snprintfcat(text, maxlen, "RALT");
975 break;
976 case SDL_KMOD_LGUI:
977 SDL_snprintfcat(text, maxlen, "LGUI");
978 break;
979 case SDL_KMOD_RGUI:
980 SDL_snprintfcat(text, maxlen, "RGUI");
981 break;
982 case SDL_KMOD_NUM:
983 SDL_snprintfcat(text, maxlen, "NUM");
984 break;
985 case SDL_KMOD_CAPS:
986 SDL_snprintfcat(text, maxlen, "CAPS");
987 break;
988 case SDL_KMOD_MODE:
989 SDL_snprintfcat(text, maxlen, "MODE");
990 break;
991 case SDL_KMOD_SCROLL:
992 SDL_snprintfcat(text, maxlen, "SCROLL");
993 break;
994 default:
995 SDL_snprintfcat(text, maxlen, "0x%8.8x", (unsigned int) flag);
996 break;
997 }
998}
999
1000static void SDLTest_PrintModState(char *text, size_t maxlen, SDL_Keymod keymod)
1001{
1002 const SDL_Keymod kmod_flags[] = {
1003 SDL_KMOD_LSHIFT,
1004 SDL_KMOD_RSHIFT,
1005 SDL_KMOD_LEVEL5,
1006 SDL_KMOD_LCTRL,
1007 SDL_KMOD_RCTRL,
1008 SDL_KMOD_LALT,
1009 SDL_KMOD_RALT,
1010 SDL_KMOD_LGUI,
1011 SDL_KMOD_RGUI,
1012 SDL_KMOD_NUM,
1013 SDL_KMOD_CAPS,
1014 SDL_KMOD_MODE,
1015 SDL_KMOD_SCROLL
1016 };
1017
1018 int i;
1019 int count = 0;
1020 for (i = 0; i < SDL_arraysize(kmod_flags); ++i) {
1021 const SDL_Keymod flag = kmod_flags[i];
1022 if ((keymod & flag) == flag) {
1023 if (count > 0) {
1024 SDL_snprintfcat(text, maxlen, " | ");
1025 }
1026 SDLTest_PrintModStateFlag(text, maxlen, flag);
1027 ++count;
1028 }
1029 }
1030}
1031
1032static void SDLTest_PrintButtonMask(char *text, size_t maxlen, SDL_MouseButtonFlags flags)
1033{
1034 int i;
1035 int count = 0;
1036 for (i = 1; i <= 32; ++i) {
1037 const Uint32 flag = SDL_BUTTON_MASK(i);
1038 if ((flags & flag) == flag) {
1039 if (count > 0) {
1040 SDL_snprintfcat(text, maxlen, " | ");
1041 }
1042 SDL_snprintfcat(text, maxlen, "SDL_BUTTON_MASK(%d)", i);
1043 ++count;
1044 }
1045 }
1046}
1047
1048static void SDLTest_PrintPixelFormat(char *text, size_t maxlen, Uint32 format)
1049{
1050 const char *name = SDL_GetPixelFormatName(format);
1051 if (name) {
1052 if (SDL_strncmp(name, "SDL_PIXELFORMAT_", 16) == 0) {
1053 name += 16;
1054 }
1055 SDL_snprintfcat(text, maxlen, name);
1056 } else {
1057 SDL_snprintfcat(text, maxlen, "0x%8.8x", format);
1058 }
1059}
1060
1061static void SDLTest_PrintLogicalPresentation(char *text, size_t maxlen, SDL_RendererLogicalPresentation logical_presentation)
1062{
1063 switch (logical_presentation) {
1064 case SDL_LOGICAL_PRESENTATION_DISABLED:
1065 SDL_snprintfcat(text, maxlen, "DISABLED");
1066 break;
1067 case SDL_LOGICAL_PRESENTATION_STRETCH:
1068 SDL_snprintfcat(text, maxlen, "STRETCH");
1069 break;
1070 case SDL_LOGICAL_PRESENTATION_LETTERBOX:
1071 SDL_snprintfcat(text, maxlen, "LETTERBOX");
1072 break;
1073 case SDL_LOGICAL_PRESENTATION_OVERSCAN:
1074 SDL_snprintfcat(text, maxlen, "OVERSCAN");
1075 break;
1076 case SDL_LOGICAL_PRESENTATION_INTEGER_SCALE:
1077 SDL_snprintfcat(text, maxlen, "INTEGER_SCALE");
1078 break;
1079 default:
1080 SDL_snprintfcat(text, maxlen, "0x%8.8x", logical_presentation);
1081 break;
1082 }
1083}
1084
1085static void SDLTest_PrintRenderer(SDL_Renderer *renderer)
1086{
1087 const char *name;
1088 int i;
1089 char text[1024];
1090 int max_texture_size;
1091 const SDL_PixelFormat *texture_formats;
1092
1093 name = SDL_GetRendererName(renderer);
1094
1095 SDL_Log(" Renderer %s:", name);
1096 if (SDL_strcmp(name, "gpu") == 0) {
1097 SDL_GPUDevice *device = SDL_GetPointerProperty(SDL_GetRendererProperties(renderer), SDL_PROP_RENDERER_GPU_DEVICE_POINTER, NULL);
1098 SDL_Log(" Driver: %s", SDL_GetGPUDeviceDriver(device));
1099 }
1100 SDL_Log(" VSync: %d", (int)SDL_GetNumberProperty(SDL_GetRendererProperties(renderer), SDL_PROP_RENDERER_VSYNC_NUMBER, 0));
1101
1102 texture_formats = (const SDL_PixelFormat *)SDL_GetPointerProperty(SDL_GetRendererProperties(renderer), SDL_PROP_RENDERER_TEXTURE_FORMATS_POINTER, NULL);
1103 if (texture_formats) {
1104 (void)SDL_snprintf(text, sizeof(text), " Texture formats: ");
1105 for (i = 0; texture_formats[i]; ++i) {
1106 if (i > 0) {
1107 SDL_snprintfcat(text, sizeof(text), ", ");
1108 }
1109 SDLTest_PrintPixelFormat(text, sizeof(text), texture_formats[i]);
1110 }
1111 SDL_Log("%s", text);
1112 }
1113
1114 max_texture_size = (int)SDL_GetNumberProperty(SDL_GetRendererProperties(renderer), SDL_PROP_RENDERER_MAX_TEXTURE_SIZE_NUMBER, 0);
1115 if (max_texture_size) {
1116 SDL_Log(" Max Texture Size: %dx%d", max_texture_size, max_texture_size);
1117 }
1118}
1119
1120static SDL_Surface *SDLTest_LoadIcon(const char *file)
1121{
1122 SDL_Surface *icon;
1123
1124 /* Load the icon surface */
1125 icon = SDL_LoadBMP(file);
1126 if (!icon) {
1127 SDL_Log("Couldn't load %s: %s", file, SDL_GetError());
1128 return NULL;
1129 }
1130
1131 if (icon->format == SDL_PIXELFORMAT_INDEX8) {
1132 /* Set the colorkey */
1133 SDL_SetSurfaceColorKey(icon, 1, *((Uint8 *)icon->pixels));
1134 }
1135
1136 return icon;
1137}
1138
1139static SDL_HitTestResult SDLCALL SDLTest_ExampleHitTestCallback(SDL_Window *win, const SDL_Point *area, void *data)
1140{
1141 int w, h;
1142 const int RESIZE_BORDER = 8;
1143 const int DRAGGABLE_TITLE = 32;
1144
1145 /*SDL_Log("Hit test point %d,%d", area->x, area->y);*/
1146
1147 SDL_GetWindowSize(win, &w, &h);
1148
1149 if (area->x < RESIZE_BORDER) {
1150 if (area->y < RESIZE_BORDER) {
1151 SDL_Log("SDL_HITTEST_RESIZE_TOPLEFT");
1152 return SDL_HITTEST_RESIZE_TOPLEFT;
1153 } else if (area->y >= (h - RESIZE_BORDER)) {
1154 SDL_Log("SDL_HITTEST_RESIZE_BOTTOMLEFT");
1155 return SDL_HITTEST_RESIZE_BOTTOMLEFT;
1156 } else {
1157 SDL_Log("SDL_HITTEST_RESIZE_LEFT");
1158 return SDL_HITTEST_RESIZE_LEFT;
1159 }
1160 } else if (area->x >= (w - RESIZE_BORDER)) {
1161 if (area->y < RESIZE_BORDER) {
1162 SDL_Log("SDL_HITTEST_RESIZE_TOPRIGHT");
1163 return SDL_HITTEST_RESIZE_TOPRIGHT;
1164 } else if (area->y >= (h - RESIZE_BORDER)) {
1165 SDL_Log("SDL_HITTEST_RESIZE_BOTTOMRIGHT");
1166 return SDL_HITTEST_RESIZE_BOTTOMRIGHT;
1167 } else {
1168 SDL_Log("SDL_HITTEST_RESIZE_RIGHT");
1169 return SDL_HITTEST_RESIZE_RIGHT;
1170 }
1171 } else if (area->y >= (h - RESIZE_BORDER)) {
1172 SDL_Log("SDL_HITTEST_RESIZE_BOTTOM");
1173 return SDL_HITTEST_RESIZE_BOTTOM;
1174 } else if (area->y < RESIZE_BORDER) {
1175 SDL_Log("SDL_HITTEST_RESIZE_TOP");
1176 return SDL_HITTEST_RESIZE_TOP;
1177 } else if (area->y < DRAGGABLE_TITLE) {
1178 SDL_Log("SDL_HITTEST_DRAGGABLE");
1179 return SDL_HITTEST_DRAGGABLE;
1180 }
1181 return SDL_HITTEST_NORMAL;
1182}
1183
1184bool SDLTest_CommonInit(SDLTest_CommonState *state)
1185{
1186 int i, j, m, n, w, h;
1187 char text[1024];
1188
1189 if (state->flags & SDL_INIT_VIDEO) {
1190 if (state->verbose & VERBOSE_VIDEO) {
1191 n = SDL_GetNumVideoDrivers();
1192 if (n == 0) {
1193 SDL_Log("No built-in video drivers");
1194 } else {
1195 (void)SDL_snprintf(text, sizeof(text), "Built-in video drivers:");
1196 for (i = 0; i < n; ++i) {
1197 if (i > 0) {
1198 SDL_snprintfcat(text, sizeof(text), ",");
1199 }
1200 SDL_snprintfcat(text, sizeof(text), " %s", SDL_GetVideoDriver(i));
1201 }
1202 SDL_Log("%s", text);
1203 }
1204 }
1205 if (!SDL_InitSubSystem(SDL_INIT_VIDEO)) {
1206 SDL_Log("Couldn't initialize video driver: %s",
1207 SDL_GetError());
1208 return false;
1209 }
1210 if (state->verbose & VERBOSE_VIDEO) {
1211 SDL_Log("Video driver: %s",
1212 SDL_GetCurrentVideoDriver());
1213 }
1214
1215 /* Upload GL settings */
1216 SDL_GL_SetAttribute(SDL_GL_RED_SIZE, state->gl_red_size);
1217 SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, state->gl_green_size);
1218 SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, state->gl_blue_size);
1219 SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, state->gl_alpha_size);
1220 SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, state->gl_double_buffer);
1221 SDL_GL_SetAttribute(SDL_GL_BUFFER_SIZE, state->gl_buffer_size);
1222 SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, state->gl_depth_size);
1223 SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, state->gl_stencil_size);
1224 SDL_GL_SetAttribute(SDL_GL_ACCUM_RED_SIZE, state->gl_accum_red_size);
1225 SDL_GL_SetAttribute(SDL_GL_ACCUM_GREEN_SIZE, state->gl_accum_green_size);
1226 SDL_GL_SetAttribute(SDL_GL_ACCUM_BLUE_SIZE, state->gl_accum_blue_size);
1227 SDL_GL_SetAttribute(SDL_GL_ACCUM_ALPHA_SIZE, state->gl_accum_alpha_size);
1228 SDL_GL_SetAttribute(SDL_GL_STEREO, state->gl_stereo);
1229 SDL_GL_SetAttribute(SDL_GL_CONTEXT_RELEASE_BEHAVIOR, state->gl_release_behavior);
1230 SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, state->gl_multisamplebuffers);
1231 SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, state->gl_multisamplesamples);
1232 if (state->gl_accelerated >= 0) {
1233 SDL_GL_SetAttribute(SDL_GL_ACCELERATED_VISUAL,
1234 state->gl_accelerated);
1235 }
1236 SDL_GL_SetAttribute(SDL_GL_RETAINED_BACKING, state->gl_retained_backing);
1237 if (state->gl_major_version) {
1238 SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, state->gl_major_version);
1239 SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, state->gl_minor_version);
1240 }
1241 if (state->gl_debug) {
1242 SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_DEBUG_FLAG);
1243 }
1244 if (state->gl_profile_mask) {
1245 SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, state->gl_profile_mask);
1246 }
1247
1248 if (state->verbose & VERBOSE_MODES) {
1249 SDL_DisplayID *displays;
1250 SDL_Rect bounds, usablebounds;
1251 SDL_DisplayMode **modes;
1252 const SDL_DisplayMode *mode;
1253 int bpp;
1254 Uint32 Rmask, Gmask, Bmask, Amask;
1255#ifdef SDL_VIDEO_DRIVER_WINDOWS
1256 int adapterIndex = 0;
1257 int outputIndex = 0;
1258#endif
1259 displays = SDL_GetDisplays(&n);
1260 SDL_Log("Number of displays: %d", n);
1261 for (i = 0; i < n; ++i) {
1262 SDL_DisplayID displayID = displays[i];
1263 SDL_Log("Display %" SDL_PRIu32 ": %s", displayID, SDL_GetDisplayName(displayID));
1264
1265 SDL_zero(bounds);
1266 SDL_GetDisplayBounds(displayID, &bounds);
1267
1268 SDL_zero(usablebounds);
1269 SDL_GetDisplayUsableBounds(displayID, &usablebounds);
1270
1271 SDL_Log("Bounds: %dx%d at %d,%d", bounds.w, bounds.h, bounds.x, bounds.y);
1272 SDL_Log("Usable bounds: %dx%d at %d,%d", usablebounds.w, usablebounds.h, usablebounds.x, usablebounds.y);
1273
1274 mode = SDL_GetDesktopDisplayMode(displayID);
1275 SDL_GetMasksForPixelFormat(mode->format, &bpp, &Rmask, &Gmask,
1276 &Bmask, &Amask);
1277 SDL_Log(" Desktop mode: %dx%d@%gx %gHz, %d bits-per-pixel (%s)",
1278 mode->w, mode->h, mode->pixel_density, mode->refresh_rate, bpp,
1279 SDL_GetPixelFormatName(mode->format));
1280 if (Rmask || Gmask || Bmask) {
1281 SDL_Log(" Red Mask = 0x%.8" SDL_PRIx32, Rmask);
1282 SDL_Log(" Green Mask = 0x%.8" SDL_PRIx32, Gmask);
1283 SDL_Log(" Blue Mask = 0x%.8" SDL_PRIx32, Bmask);
1284 if (Amask) {
1285 SDL_Log(" Alpha Mask = 0x%.8" SDL_PRIx32, Amask);
1286 }
1287 }
1288
1289 /* Print available fullscreen video modes */
1290 modes = SDL_GetFullscreenDisplayModes(displayID, &m);
1291 if (m == 0) {
1292 SDL_Log("No available fullscreen video modes");
1293 } else {
1294 SDL_Log(" Fullscreen video modes:");
1295 for (j = 0; j < m; ++j) {
1296 mode = modes[j];
1297 SDL_GetMasksForPixelFormat(mode->format, &bpp, &Rmask,
1298 &Gmask, &Bmask, &Amask);
1299 SDL_Log(" Mode %d: %dx%d@%gx %gHz, %d bits-per-pixel (%s)",
1300 j, mode->w, mode->h, mode->pixel_density, mode->refresh_rate, bpp,
1301 SDL_GetPixelFormatName(mode->format));
1302 if (Rmask || Gmask || Bmask) {
1303 SDL_Log(" Red Mask = 0x%.8" SDL_PRIx32,
1304 Rmask);
1305 SDL_Log(" Green Mask = 0x%.8" SDL_PRIx32,
1306 Gmask);
1307 SDL_Log(" Blue Mask = 0x%.8" SDL_PRIx32,
1308 Bmask);
1309 if (Amask) {
1310 SDL_Log(" Alpha Mask = 0x%.8" SDL_PRIx32, Amask);
1311 }
1312 }
1313 }
1314 }
1315 SDL_free(modes);
1316
1317#if defined(SDL_VIDEO_DRIVER_WINDOWS) && !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES)
1318 /* Print the D3D9 adapter index */
1319 adapterIndex = SDL_GetDirect3D9AdapterIndex(displayID);
1320 SDL_Log("D3D9 Adapter Index: %d", adapterIndex);
1321
1322 /* Print the DXGI adapter and output indices */
1323 SDL_GetDXGIOutputInfo(displayID, &adapterIndex, &outputIndex);
1324 SDL_Log("DXGI Adapter Index: %d Output Index: %d", adapterIndex, outputIndex);
1325#endif
1326 }
1327 SDL_free(displays);
1328 }
1329
1330 if (state->verbose & VERBOSE_RENDER) {
1331 n = SDL_GetNumRenderDrivers();
1332 if (n == 0) {
1333 SDL_Log("No built-in render drivers");
1334 } else {
1335 SDL_Log("Built-in render drivers:");
1336 for (i = 0; i < n; ++i) {
1337 SDL_Log(" %s", SDL_GetRenderDriver(i));
1338 }
1339 }
1340 }
1341
1342 state->displayID = SDL_GetPrimaryDisplay();
1343 if (state->display_index > 0) {
1344 SDL_DisplayID *displays = SDL_GetDisplays(&n);
1345 if (state->display_index < n) {
1346 state->displayID = displays[state->display_index];
1347 }
1348 SDL_free(displays);
1349
1350 if (SDL_WINDOWPOS_ISUNDEFINED(state->window_x)) {
1351 state->window_x = SDL_WINDOWPOS_UNDEFINED_DISPLAY(state->displayID);
1352 state->window_y = SDL_WINDOWPOS_UNDEFINED_DISPLAY(state->displayID);
1353 } else if (SDL_WINDOWPOS_ISCENTERED(state->window_x)) {
1354 state->window_x = SDL_WINDOWPOS_CENTERED_DISPLAY(state->displayID);
1355 state->window_y = SDL_WINDOWPOS_CENTERED_DISPLAY(state->displayID);
1356 }
1357 }
1358
1359 {
1360 bool include_high_density_modes = false;
1361 if (state->window_flags & SDL_WINDOW_HIGH_PIXEL_DENSITY) {
1362 include_high_density_modes = true;
1363 }
1364 SDL_GetClosestFullscreenDisplayMode(state->displayID, state->window_w, state->window_h, state->refresh_rate, include_high_density_modes, &state->fullscreen_mode);
1365 }
1366
1367 state->windows =
1368 (SDL_Window **)SDL_calloc(state->num_windows,
1369 sizeof(*state->windows));
1370 state->renderers =
1371 (SDL_Renderer **)SDL_calloc(state->num_windows,
1372 sizeof(*state->renderers));
1373 state->targets =
1374 (SDL_Texture **)SDL_calloc(state->num_windows,
1375 sizeof(*state->targets));
1376 if (!state->windows || !state->renderers) {
1377 SDL_Log("Out of memory!");
1378 return false;
1379 }
1380 for (i = 0; i < state->num_windows; ++i) {
1381 char title[1024];
1382 SDL_Rect r;
1383 SDL_PropertiesID props;
1384
1385 if (state->fill_usable_bounds) {
1386 SDL_GetDisplayUsableBounds(state->displayID, &r);
1387 } else {
1388 r.x = state->window_x;
1389 r.y = state->window_y;
1390 r.w = state->window_w;
1391 r.h = state->window_h;
1392 if (state->auto_scale_content) {
1393 float scale = SDL_GetDisplayContentScale(state->displayID);
1394 r.w = (int)SDL_ceilf(r.w * scale);
1395 r.h = (int)SDL_ceilf(r.h * scale);
1396 }
1397 }
1398
1399 if (state->num_windows > 1) {
1400 (void)SDL_snprintf(title, SDL_arraysize(title), "%s %d",
1401 state->window_title, i + 1);
1402 } else {
1403 SDL_strlcpy(title, state->window_title, SDL_arraysize(title));
1404 }
1405 props = SDL_CreateProperties();
1406 SDL_SetStringProperty(props, SDL_PROP_WINDOW_CREATE_TITLE_STRING, title);
1407 SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_X_NUMBER, r.x);
1408 SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_Y_NUMBER, r.y);
1409 SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_WIDTH_NUMBER, r.w);
1410 SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_HEIGHT_NUMBER, r.h);
1411 SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_FLAGS_NUMBER, state->window_flags);
1412 state->windows[i] = SDL_CreateWindowWithProperties(props);
1413 SDL_DestroyProperties(props);
1414 if (!state->windows[i]) {
1415 SDL_Log("Couldn't create window: %s",
1416 SDL_GetError());
1417 return false;
1418 }
1419 if (state->window_minW || state->window_minH) {
1420 SDL_SetWindowMinimumSize(state->windows[i], state->window_minW, state->window_minH);
1421 }
1422 if (state->window_maxW || state->window_maxH) {
1423 SDL_SetWindowMaximumSize(state->windows[i], state->window_maxW, state->window_maxH);
1424 }
1425 if (state->window_min_aspect != 0.f || state->window_max_aspect != 0.f) {
1426 SDL_SetWindowAspectRatio(state->windows[i], state->window_min_aspect, state->window_max_aspect);
1427 }
1428 SDL_GetWindowSize(state->windows[i], &w, &h);
1429 if (!(state->window_flags & SDL_WINDOW_RESIZABLE) && (w != r.w || h != r.h)) {
1430 SDL_Log("Window requested size %dx%d, got %dx%d", r.w, r.h, w, h);
1431 state->window_w = w;
1432 state->window_h = h;
1433 }
1434 if (state->window_flags & SDL_WINDOW_FULLSCREEN) {
1435 if (state->fullscreen_exclusive) {
1436 SDL_SetWindowFullscreenMode(state->windows[i], &state->fullscreen_mode);
1437 }
1438 SDL_SetWindowFullscreen(state->windows[i], true);
1439 }
1440
1441 /* Add resize/drag areas for windows that are borderless and resizable */
1442 if ((state->window_flags & (SDL_WINDOW_RESIZABLE | SDL_WINDOW_BORDERLESS)) ==
1443 (SDL_WINDOW_RESIZABLE | SDL_WINDOW_BORDERLESS)) {
1444 SDL_SetWindowHitTest(state->windows[i], SDLTest_ExampleHitTestCallback, NULL);
1445 }
1446
1447 if (state->window_icon) {
1448 SDL_Surface *icon = SDLTest_LoadIcon(state->window_icon);
1449 if (icon) {
1450 SDL_SetWindowIcon(state->windows[i], icon);
1451 SDL_DestroySurface(icon);
1452 }
1453 }
1454
1455 if (!SDL_RectEmpty(&state->confine)) {
1456 SDL_SetWindowMouseRect(state->windows[i], &state->confine);
1457 }
1458
1459 if (!state->skip_renderer && (state->renderdriver || !(state->window_flags & (SDL_WINDOW_OPENGL | SDL_WINDOW_VULKAN | SDL_WINDOW_METAL)))) {
1460 state->renderers[i] = SDL_CreateRenderer(state->windows[i], state->renderdriver);
1461 if (!state->renderers[i]) {
1462 SDL_Log("Couldn't create renderer: %s",
1463 SDL_GetError());
1464 return false;
1465 }
1466 if (state->logical_w == 0 || state->logical_h == 0) {
1467 state->logical_w = state->window_w;
1468 state->logical_h = state->window_h;
1469 }
1470 if (state->render_vsync) {
1471 SDL_SetRenderVSync(state->renderers[i], state->render_vsync);
1472 }
1473 if (!SDL_SetRenderLogicalPresentation(state->renderers[i], state->logical_w, state->logical_h, state->logical_presentation)) {
1474 SDL_Log("Couldn't set logical presentation: %s", SDL_GetError());
1475 return false;
1476 }
1477 if (state->scale != 0.0f) {
1478 SDL_SetRenderScale(state->renderers[i], state->scale, state->scale);
1479 }
1480 if (state->verbose & VERBOSE_RENDER) {
1481 SDL_Log("Current renderer:");
1482 SDLTest_PrintRenderer(state->renderers[i]);
1483 }
1484 }
1485
1486 SDL_ShowWindow(state->windows[i]);
1487 }
1488 if (state->hide_cursor) {
1489 SDL_HideCursor();
1490 }
1491 }
1492
1493 if (state->flags & SDL_INIT_AUDIO) {
1494 if (state->verbose & VERBOSE_AUDIO) {
1495 n = SDL_GetNumAudioDrivers();
1496 if (n == 0) {
1497 SDL_Log("No built-in audio drivers");
1498 } else {
1499 (void)SDL_snprintf(text, sizeof(text), "Built-in audio drivers:");
1500 for (i = 0; i < n; ++i) {
1501 if (i > 0) {
1502 SDL_snprintfcat(text, sizeof(text), ",");
1503 }
1504 SDL_snprintfcat(text, sizeof(text), " %s", SDL_GetAudioDriver(i));
1505 }
1506 SDL_Log("%s", text);
1507 }
1508 }
1509 if (!SDL_InitSubSystem(SDL_INIT_AUDIO)) {
1510 SDL_Log("Couldn't initialize audio driver: %s",
1511 SDL_GetError());
1512 return false;
1513 }
1514 if (state->verbose & VERBOSE_AUDIO) {
1515 SDL_Log("Audio driver: %s",
1516 SDL_GetCurrentAudioDriver());
1517 }
1518
1519 const SDL_AudioSpec spec = { state->audio_format, state->audio_channels, state->audio_freq };
1520 state->audio_id = SDL_OpenAudioDevice(SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, &spec);
1521 if (!state->audio_id) {
1522 SDL_Log("Couldn't open audio: %s", SDL_GetError());
1523 return false;
1524 }
1525 }
1526
1527 if (state->flags & SDL_INIT_CAMERA) {
1528 SDL_InitSubSystem(SDL_INIT_CAMERA);
1529 }
1530
1531 return true;
1532}
1533
1534static const char *SystemThemeName(void)
1535{
1536 switch (SDL_GetSystemTheme()) {
1537#define CASE(X) \
1538 case SDL_SYSTEM_THEME_##X: \
1539 return #X
1540 CASE(UNKNOWN);
1541 CASE(LIGHT);
1542 CASE(DARK);
1543#undef CASE
1544 default:
1545 return "???";
1546 }
1547}
1548
1549static const char *DisplayOrientationName(int orientation)
1550{
1551 switch (orientation) {
1552#define CASE(X) \
1553 case SDL_ORIENTATION_##X: \
1554 return #X
1555 CASE(UNKNOWN);
1556 CASE(LANDSCAPE);
1557 CASE(LANDSCAPE_FLIPPED);
1558 CASE(PORTRAIT);
1559 CASE(PORTRAIT_FLIPPED);
1560#undef CASE
1561 default:
1562 return "???";
1563 }
1564}
1565
1566static const char *GamepadAxisName(const SDL_GamepadAxis axis)
1567{
1568 switch (axis) {
1569#define AXIS_CASE(ax) \
1570 case SDL_GAMEPAD_AXIS_##ax: \
1571 return #ax
1572 AXIS_CASE(INVALID);
1573 AXIS_CASE(LEFTX);
1574 AXIS_CASE(LEFTY);
1575 AXIS_CASE(RIGHTX);
1576 AXIS_CASE(RIGHTY);
1577 AXIS_CASE(LEFT_TRIGGER);
1578 AXIS_CASE(RIGHT_TRIGGER);
1579#undef AXIS_CASE
1580 default:
1581 return "???";
1582 }
1583}
1584
1585static const char *GamepadButtonName(const SDL_GamepadButton button)
1586{
1587 switch (button) {
1588#define BUTTON_CASE(btn) \
1589 case SDL_GAMEPAD_BUTTON_##btn: \
1590 return #btn
1591 BUTTON_CASE(INVALID);
1592 BUTTON_CASE(SOUTH);
1593 BUTTON_CASE(EAST);
1594 BUTTON_CASE(WEST);
1595 BUTTON_CASE(NORTH);
1596 BUTTON_CASE(BACK);
1597 BUTTON_CASE(GUIDE);
1598 BUTTON_CASE(START);
1599 BUTTON_CASE(LEFT_STICK);
1600 BUTTON_CASE(RIGHT_STICK);
1601 BUTTON_CASE(LEFT_SHOULDER);
1602 BUTTON_CASE(RIGHT_SHOULDER);
1603 BUTTON_CASE(DPAD_UP);
1604 BUTTON_CASE(DPAD_DOWN);
1605 BUTTON_CASE(DPAD_LEFT);
1606 BUTTON_CASE(DPAD_RIGHT);
1607#undef BUTTON_CASE
1608 default:
1609 return "???";
1610 }
1611}
1612
1613void SDLTest_PrintEvent(const SDL_Event *event)
1614{
1615 switch (event->type) {
1616 case SDL_EVENT_SYSTEM_THEME_CHANGED:
1617 SDL_Log("SDL EVENT: System theme changed to %s", SystemThemeName());
1618 break;
1619 case SDL_EVENT_DISPLAY_ADDED:
1620 SDL_Log("SDL EVENT: Display %" SDL_PRIu32 " attached",
1621 event->display.displayID);
1622 break;
1623 case SDL_EVENT_DISPLAY_CONTENT_SCALE_CHANGED:
1624 {
1625 float scale = SDL_GetDisplayContentScale(event->display.displayID);
1626 SDL_Log("SDL EVENT: Display %" SDL_PRIu32 " changed content scale to %d%%",
1627 event->display.displayID, (int)(scale * 100.0f));
1628 }
1629 break;
1630 case SDL_EVENT_DISPLAY_DESKTOP_MODE_CHANGED:
1631 SDL_Log("SDL EVENT: Display %" SDL_PRIu32 " desktop mode changed to %" SDL_PRIs32 "x%" SDL_PRIs32,
1632 event->display.displayID, event->display.data1, event->display.data2);
1633 break;
1634 case SDL_EVENT_DISPLAY_CURRENT_MODE_CHANGED:
1635 SDL_Log("SDL EVENT: Display %" SDL_PRIu32 " current mode changed to %" SDL_PRIs32 "x%" SDL_PRIs32,
1636 event->display.displayID, event->display.data1, event->display.data2);
1637 break;
1638 case SDL_EVENT_DISPLAY_MOVED:
1639 SDL_Log("SDL EVENT: Display %" SDL_PRIu32 " changed position",
1640 event->display.displayID);
1641 break;
1642 case SDL_EVENT_DISPLAY_ORIENTATION:
1643 SDL_Log("SDL EVENT: Display %" SDL_PRIu32 " changed orientation to %s",
1644 event->display.displayID, DisplayOrientationName(event->display.data1));
1645 break;
1646 case SDL_EVENT_DISPLAY_REMOVED:
1647 SDL_Log("SDL EVENT: Display %" SDL_PRIu32 " removed",
1648 event->display.displayID);
1649 break;
1650 case SDL_EVENT_WINDOW_SHOWN:
1651 SDL_Log("SDL EVENT: Window %" SDL_PRIu32 " shown", event->window.windowID);
1652 break;
1653 case SDL_EVENT_WINDOW_HIDDEN:
1654 SDL_Log("SDL EVENT: Window %" SDL_PRIu32 " hidden", event->window.windowID);
1655 break;
1656 case SDL_EVENT_WINDOW_EXPOSED:
1657 SDL_Log("SDL EVENT: Window %" SDL_PRIu32 " exposed", event->window.windowID);
1658 break;
1659 case SDL_EVENT_WINDOW_MOVED:
1660 SDL_Log("SDL EVENT: Window %" SDL_PRIu32 " moved to %" SDL_PRIs32 ",%" SDL_PRIs32,
1661 event->window.windowID, event->window.data1, event->window.data2);
1662 break;
1663 case SDL_EVENT_WINDOW_RESIZED:
1664 SDL_Log("SDL EVENT: Window %" SDL_PRIu32 " resized to %" SDL_PRIs32 "x%" SDL_PRIs32,
1665 event->window.windowID, event->window.data1, event->window.data2);
1666 break;
1667 case SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED:
1668 SDL_Log("SDL EVENT: Window %" SDL_PRIu32 " changed pixel size to %" SDL_PRIs32 "x%" SDL_PRIs32,
1669 event->window.windowID, event->window.data1, event->window.data2);
1670 break;
1671 case SDL_EVENT_WINDOW_METAL_VIEW_RESIZED:
1672 SDL_Log("SDL EVENT: Window %" SDL_PRIu32 " changed metal view size",
1673 event->window.windowID);
1674 break;
1675 case SDL_EVENT_WINDOW_SAFE_AREA_CHANGED: {
1676 SDL_Rect rect;
1677
1678 SDL_GetWindowSafeArea(SDL_GetWindowFromEvent(event), &rect);
1679 SDL_Log("SDL EVENT: Window %" SDL_PRIu32 " changed safe area to: %d,%d %dx%d",
1680 event->window.windowID, rect.x, rect.y, rect.w, rect.h);
1681 break;
1682 }
1683 case SDL_EVENT_WINDOW_MINIMIZED:
1684 SDL_Log("SDL EVENT: Window %" SDL_PRIu32 " minimized", event->window.windowID);
1685 break;
1686 case SDL_EVENT_WINDOW_MAXIMIZED:
1687 SDL_Log("SDL EVENT: Window %" SDL_PRIu32 " maximized", event->window.windowID);
1688 break;
1689 case SDL_EVENT_WINDOW_RESTORED:
1690 SDL_Log("SDL EVENT: Window %" SDL_PRIu32 " restored", event->window.windowID);
1691 break;
1692 case SDL_EVENT_WINDOW_MOUSE_ENTER:
1693 SDL_Log("SDL EVENT: Mouse entered window %" SDL_PRIu32, event->window.windowID);
1694 break;
1695 case SDL_EVENT_WINDOW_MOUSE_LEAVE:
1696 SDL_Log("SDL EVENT: Mouse left window %" SDL_PRIu32, event->window.windowID);
1697 break;
1698 case SDL_EVENT_WINDOW_FOCUS_GAINED:
1699 SDL_Log("SDL EVENT: Window %" SDL_PRIu32 " gained keyboard focus",
1700 event->window.windowID);
1701 break;
1702 case SDL_EVENT_WINDOW_FOCUS_LOST:
1703 SDL_Log("SDL EVENT: Window %" SDL_PRIu32 " lost keyboard focus",
1704 event->window.windowID);
1705 break;
1706 case SDL_EVENT_WINDOW_CLOSE_REQUESTED:
1707 SDL_Log("SDL EVENT: Window %" SDL_PRIu32 " closed", event->window.windowID);
1708 break;
1709 case SDL_EVENT_WINDOW_HIT_TEST:
1710 SDL_Log("SDL EVENT: Window %" SDL_PRIu32 " hit test", event->window.windowID);
1711 break;
1712 case SDL_EVENT_WINDOW_ICCPROF_CHANGED:
1713 SDL_Log("SDL EVENT: Window %" SDL_PRIu32 " ICC profile changed", event->window.windowID);
1714 break;
1715 case SDL_EVENT_WINDOW_DISPLAY_CHANGED:
1716 SDL_Log("SDL EVENT: Window %" SDL_PRIu32 " display changed to %" SDL_PRIs32, event->window.windowID, event->window.data1);
1717 break;
1718 case SDL_EVENT_WINDOW_DISPLAY_SCALE_CHANGED:
1719 SDL_Log("SDL EVENT: Window %" SDL_PRIu32 " display scale changed to %d%%", event->window.windowID, (int)(SDL_GetWindowDisplayScale(SDL_GetWindowFromEvent(event)) * 100.0f));
1720 break;
1721 case SDL_EVENT_WINDOW_OCCLUDED:
1722 SDL_Log("SDL EVENT: Window %" SDL_PRIu32 " occluded", event->window.windowID);
1723 break;
1724 case SDL_EVENT_WINDOW_ENTER_FULLSCREEN:
1725 SDL_Log("SDL EVENT: Window %" SDL_PRIu32 " entered fullscreen", event->window.windowID);
1726 break;
1727 case SDL_EVENT_WINDOW_LEAVE_FULLSCREEN:
1728 SDL_Log("SDL EVENT: Window %" SDL_PRIu32 " left fullscreen", event->window.windowID);
1729 break;
1730 case SDL_EVENT_WINDOW_DESTROYED:
1731 SDL_Log("SDL EVENT: Window %" SDL_PRIu32 " destroyed", event->window.windowID);
1732 break;
1733 case SDL_EVENT_WINDOW_HDR_STATE_CHANGED:
1734 SDL_Log("SDL EVENT: Window %" SDL_PRIu32 " HDR %s", event->window.windowID, event->window.data1 ? "enabled" : "disabled");
1735 break;
1736 case SDL_EVENT_KEYBOARD_ADDED:
1737 SDL_Log("SDL EVENT: Keyboard %" SDL_PRIu32 " attached",
1738 event->kdevice.which);
1739 break;
1740 case SDL_EVENT_KEYBOARD_REMOVED:
1741 SDL_Log("SDL EVENT: Keyboard %" SDL_PRIu32 " removed",
1742 event->kdevice.which);
1743 break;
1744 case SDL_EVENT_KEY_DOWN:
1745 case SDL_EVENT_KEY_UP: {
1746 char modstr[64];
1747 if (event->key.mod) {
1748 modstr[0] = '\0';
1749 SDLTest_PrintModState(modstr, sizeof (modstr), event->key.mod);
1750 } else {
1751 SDL_strlcpy(modstr, "NONE", sizeof (modstr));
1752 }
1753
1754 SDL_Log("SDL EVENT: Keyboard: key %s in window %" SDL_PRIu32 ": scancode 0x%08X = %s, keycode 0x%08" SDL_PRIX32 " = %s, mods = %s",
1755 (event->type == SDL_EVENT_KEY_DOWN) ? "pressed" : "released",
1756 event->key.windowID,
1757 event->key.scancode,
1758 SDL_GetScancodeName(event->key.scancode),
1759 event->key.key, SDL_GetKeyName(event->key.key),
1760 modstr);
1761 break;
1762 }
1763 case SDL_EVENT_TEXT_EDITING:
1764 SDL_Log("SDL EVENT: Keyboard: text editing \"%s\" in window %" SDL_PRIu32,
1765 event->edit.text, event->edit.windowID);
1766 break;
1767 case SDL_EVENT_TEXT_EDITING_CANDIDATES:
1768 SDL_Log("SDL EVENT: Keyboard: text editing candidates in window %" SDL_PRIu32,
1769 event->edit.windowID);
1770 break;
1771 case SDL_EVENT_TEXT_INPUT:
1772 SDL_Log("SDL EVENT: Keyboard: text input \"%s\" in window %" SDL_PRIu32,
1773 event->text.text, event->text.windowID);
1774 break;
1775 case SDL_EVENT_KEYMAP_CHANGED:
1776 SDL_Log("SDL EVENT: Keymap changed");
1777 break;
1778 case SDL_EVENT_MOUSE_ADDED:
1779 SDL_Log("SDL EVENT: Mouse %" SDL_PRIu32 " attached",
1780 event->mdevice.which);
1781 break;
1782 case SDL_EVENT_MOUSE_REMOVED:
1783 SDL_Log("SDL EVENT: Mouse %" SDL_PRIu32 " removed",
1784 event->mdevice.which);
1785 break;
1786 case SDL_EVENT_MOUSE_MOTION:
1787 SDL_Log("SDL EVENT: Mouse: moved to %g,%g (%g,%g) in window %" SDL_PRIu32,
1788 event->motion.x, event->motion.y,
1789 event->motion.xrel, event->motion.yrel,
1790 event->motion.windowID);
1791 break;
1792 case SDL_EVENT_MOUSE_BUTTON_DOWN:
1793 SDL_Log("SDL EVENT: Mouse: button %d pressed at %g,%g with click count %d in window %" SDL_PRIu32,
1794 event->button.button, event->button.x, event->button.y, event->button.clicks,
1795 event->button.windowID);
1796 break;
1797 case SDL_EVENT_MOUSE_BUTTON_UP:
1798 SDL_Log("SDL EVENT: Mouse: button %d released at %g,%g with click count %d in window %" SDL_PRIu32,
1799 event->button.button, event->button.x, event->button.y, event->button.clicks,
1800 event->button.windowID);
1801 break;
1802 case SDL_EVENT_MOUSE_WHEEL:
1803 SDL_Log("SDL EVENT: Mouse: wheel scrolled %g in x and %g in y (reversed: %d) in window %" SDL_PRIu32,
1804 event->wheel.x, event->wheel.y, event->wheel.direction, event->wheel.windowID);
1805 break;
1806 case SDL_EVENT_JOYSTICK_ADDED:
1807 SDL_Log("SDL EVENT: Joystick %" SDL_PRIu32 " attached",
1808 event->jdevice.which);
1809 break;
1810 case SDL_EVENT_JOYSTICK_REMOVED:
1811 SDL_Log("SDL EVENT: Joystick %" SDL_PRIu32 " removed",
1812 event->jdevice.which);
1813 break;
1814 case SDL_EVENT_JOYSTICK_AXIS_MOTION:
1815 SDL_Log("SDL EVENT: Joystick %" SDL_PRIu32 " axis %d value: %d",
1816 event->jaxis.which,
1817 event->jaxis.axis,
1818 event->jaxis.value);
1819 break;
1820 case SDL_EVENT_JOYSTICK_BALL_MOTION:
1821 SDL_Log("SDL EVENT: Joystick %" SDL_PRIs32 ": ball %d moved by %d,%d",
1822 event->jball.which, event->jball.ball, event->jball.xrel,
1823 event->jball.yrel);
1824 break;
1825 case SDL_EVENT_JOYSTICK_HAT_MOTION:
1826 {
1827 const char *position = "UNKNOWN";
1828 switch (event->jhat.value) {
1829 case SDL_HAT_CENTERED:
1830 position = "CENTER";
1831 break;
1832 case SDL_HAT_UP:
1833 position = "UP";
1834 break;
1835 case SDL_HAT_RIGHTUP:
1836 position = "RIGHTUP";
1837 break;
1838 case SDL_HAT_RIGHT:
1839 position = "RIGHT";
1840 break;
1841 case SDL_HAT_RIGHTDOWN:
1842 position = "RIGHTDOWN";
1843 break;
1844 case SDL_HAT_DOWN:
1845 position = "DOWN";
1846 break;
1847 case SDL_HAT_LEFTDOWN:
1848 position = "LEFTDOWN";
1849 break;
1850 case SDL_HAT_LEFT:
1851 position = "LEFT";
1852 break;
1853 case SDL_HAT_LEFTUP:
1854 position = "LEFTUP";
1855 break;
1856 }
1857 SDL_Log("SDL EVENT: Joystick %" SDL_PRIu32 ": hat %d moved to %s",
1858 event->jhat.which, event->jhat.hat, position);
1859 } break;
1860 case SDL_EVENT_JOYSTICK_BUTTON_DOWN:
1861 SDL_Log("SDL EVENT: Joystick %" SDL_PRIu32 ": button %d pressed",
1862 event->jbutton.which, event->jbutton.button);
1863 break;
1864 case SDL_EVENT_JOYSTICK_BUTTON_UP:
1865 SDL_Log("SDL EVENT: Joystick %" SDL_PRIu32 ": button %d released",
1866 event->jbutton.which, event->jbutton.button);
1867 break;
1868 case SDL_EVENT_JOYSTICK_BATTERY_UPDATED:
1869 SDL_Log("SDL EVENT: Joystick %" SDL_PRIu32 ": battery at %d percent",
1870 event->jbattery.which, event->jbattery.percent);
1871 break;
1872 case SDL_EVENT_GAMEPAD_ADDED:
1873 SDL_Log("SDL EVENT: Gamepad %" SDL_PRIu32 " attached",
1874 event->gdevice.which);
1875 break;
1876 case SDL_EVENT_GAMEPAD_REMOVED:
1877 SDL_Log("SDL EVENT: Gamepad %" SDL_PRIu32 " removed",
1878 event->gdevice.which);
1879 break;
1880 case SDL_EVENT_GAMEPAD_REMAPPED:
1881 SDL_Log("SDL EVENT: Gamepad %" SDL_PRIu32 " mapping changed",
1882 event->gdevice.which);
1883 break;
1884 case SDL_EVENT_GAMEPAD_AXIS_MOTION:
1885 SDL_Log("SDL EVENT: Gamepad %" SDL_PRIu32 " axis %d ('%s') value: %d",
1886 event->gaxis.which,
1887 event->gaxis.axis,
1888 GamepadAxisName((SDL_GamepadAxis)event->gaxis.axis),
1889 event->gaxis.value);
1890 break;
1891 case SDL_EVENT_GAMEPAD_BUTTON_DOWN:
1892 SDL_Log("SDL EVENT: Gamepad %" SDL_PRIu32 "button %d ('%s') down",
1893 event->gbutton.which, event->gbutton.button,
1894 GamepadButtonName((SDL_GamepadButton)event->gbutton.button));
1895 break;
1896 case SDL_EVENT_GAMEPAD_BUTTON_UP:
1897 SDL_Log("SDL EVENT: Gamepad %" SDL_PRIu32 " button %d ('%s') up",
1898 event->gbutton.which, event->gbutton.button,
1899 GamepadButtonName((SDL_GamepadButton)event->gbutton.button));
1900 break;
1901 case SDL_EVENT_CLIPBOARD_UPDATE:
1902 SDL_Log("SDL EVENT: Clipboard updated");
1903 break;
1904
1905 case SDL_EVENT_FINGER_MOTION:
1906 SDL_Log("SDL EVENT: Finger: motion touch=%" SDL_PRIu64 ", finger=%" SDL_PRIu64 ", x=%f, y=%f, dx=%f, dy=%f, pressure=%f",
1907 event->tfinger.touchID,
1908 event->tfinger.fingerID,
1909 event->tfinger.x, event->tfinger.y,
1910 event->tfinger.dx, event->tfinger.dy, event->tfinger.pressure);
1911 break;
1912 case SDL_EVENT_FINGER_DOWN:
1913 case SDL_EVENT_FINGER_UP:
1914 case SDL_EVENT_FINGER_CANCELED:
1915 SDL_Log("SDL EVENT: Finger: %s touch=%" SDL_PRIu64 ", finger=%" SDL_PRIu64 ", x=%f, y=%f, dx=%f, dy=%f, pressure=%f",
1916 (event->type == SDL_EVENT_FINGER_DOWN) ? "down" :
1917 (event->type == SDL_EVENT_FINGER_UP) ? "up" : "cancel",
1918 event->tfinger.touchID,
1919 event->tfinger.fingerID,
1920 event->tfinger.x, event->tfinger.y,
1921 event->tfinger.dx, event->tfinger.dy, event->tfinger.pressure);
1922 break;
1923
1924 case SDL_EVENT_RENDER_TARGETS_RESET:
1925 SDL_Log("SDL EVENT: render targets reset in window %" SDL_PRIu32, event->render.windowID);
1926 break;
1927 case SDL_EVENT_RENDER_DEVICE_RESET:
1928 SDL_Log("SDL EVENT: render device reset in window %" SDL_PRIu32, event->render.windowID);
1929 break;
1930 case SDL_EVENT_RENDER_DEVICE_LOST:
1931 SDL_Log("SDL EVENT: render device lost in window %" SDL_PRIu32, event->render.windowID);
1932 break;
1933
1934 case SDL_EVENT_TERMINATING:
1935 SDL_Log("SDL EVENT: App terminating");
1936 break;
1937 case SDL_EVENT_LOW_MEMORY:
1938 SDL_Log("SDL EVENT: App running low on memory");
1939 break;
1940 case SDL_EVENT_WILL_ENTER_BACKGROUND:
1941 SDL_Log("SDL EVENT: App will enter the background");
1942 break;
1943 case SDL_EVENT_DID_ENTER_BACKGROUND:
1944 SDL_Log("SDL EVENT: App entered the background");
1945 break;
1946 case SDL_EVENT_WILL_ENTER_FOREGROUND:
1947 SDL_Log("SDL EVENT: App will enter the foreground");
1948 break;
1949 case SDL_EVENT_DID_ENTER_FOREGROUND:
1950 SDL_Log("SDL EVENT: App entered the foreground");
1951 break;
1952 case SDL_EVENT_DROP_BEGIN:
1953 SDL_Log("SDL EVENT: Drag and drop beginning in window %" SDL_PRIu32, event->drop.windowID);
1954 break;
1955 case SDL_EVENT_DROP_POSITION:
1956 SDL_Log("SDL EVENT: Drag and drop moving in window %" SDL_PRIu32 ": %g,%g", event->drop.windowID, event->drop.x, event->drop.y);
1957 break;
1958 case SDL_EVENT_DROP_FILE:
1959 SDL_Log("SDL EVENT: Drag and drop file in window %" SDL_PRIu32 ": '%s'", event->drop.windowID, event->drop.data);
1960 break;
1961 case SDL_EVENT_DROP_TEXT:
1962 SDL_Log("SDL EVENT: Drag and drop text in window %" SDL_PRIu32 ": '%s'", event->drop.windowID, event->drop.data);
1963 break;
1964 case SDL_EVENT_DROP_COMPLETE:
1965 SDL_Log("SDL EVENT: Drag and drop ending");
1966 break;
1967 case SDL_EVENT_AUDIO_DEVICE_ADDED:
1968 SDL_Log("SDL EVENT: Audio %s device %" SDL_PRIu32 " available",
1969 event->adevice.recording ? "recording" : "playback",
1970 event->adevice.which);
1971 break;
1972 case SDL_EVENT_AUDIO_DEVICE_REMOVED:
1973 SDL_Log("SDL EVENT: Audio %s device %" SDL_PRIu32 " removed",
1974 event->adevice.recording ? "recording" : "playback",
1975 event->adevice.which);
1976 break;
1977 case SDL_EVENT_AUDIO_DEVICE_FORMAT_CHANGED:
1978 SDL_Log("SDL EVENT: Audio %s device %" SDL_PRIu32 " format changed",
1979 event->adevice.recording ? "recording" : "playback",
1980 event->adevice.which);
1981 break;
1982 case SDL_EVENT_CAMERA_DEVICE_ADDED:
1983 SDL_Log("SDL EVENT: Camera device %" SDL_PRIu32 " available",
1984 event->cdevice.which);
1985 break;
1986 case SDL_EVENT_CAMERA_DEVICE_REMOVED:
1987 SDL_Log("SDL EVENT: Camera device %" SDL_PRIu32 " removed",
1988 event->cdevice.which);
1989 break;
1990 case SDL_EVENT_CAMERA_DEVICE_APPROVED:
1991 SDL_Log("SDL EVENT: Camera device %" SDL_PRIu32 " permission granted",
1992 event->cdevice.which);
1993 break;
1994 case SDL_EVENT_CAMERA_DEVICE_DENIED:
1995 SDL_Log("SDL EVENT: Camera device %" SDL_PRIu32 " permission denied",
1996 event->cdevice.which);
1997 break;
1998 case SDL_EVENT_SENSOR_UPDATE:
1999 SDL_Log("SDL EVENT: Sensor update for %" SDL_PRIu32,
2000 event->sensor.which);
2001 break;
2002 case SDL_EVENT_PEN_PROXIMITY_IN:
2003 SDL_Log("SDL EVENT: Pen %" SDL_PRIu32 " entered proximity",
2004 event->pproximity.which);
2005 break;
2006 case SDL_EVENT_PEN_PROXIMITY_OUT:
2007 SDL_Log("SDL EVENT: Pen %" SDL_PRIu32 " left proximity",
2008 event->ptouch.which);
2009 break;
2010 case SDL_EVENT_PEN_DOWN:
2011 SDL_Log("SDL EVENT: Pen %" SDL_PRIu32 " touched down at %g,%g",
2012 event->ptouch.which, event->ptouch.x, event->ptouch.y);
2013 break;
2014 case SDL_EVENT_PEN_UP:
2015 SDL_Log("SDL EVENT: Pen %" SDL_PRIu32 " lifted off at %g,%g",
2016 event->ptouch.which, event->ptouch.x, event->ptouch.y);
2017 break;
2018 case SDL_EVENT_PEN_BUTTON_DOWN:
2019 SDL_Log("SDL EVENT: Pen %" SDL_PRIu32 " button %d pressed at %g,%g",
2020 event->pbutton.which, event->pbutton.button, event->pbutton.x, event->pbutton.y);
2021 break;
2022 case SDL_EVENT_PEN_BUTTON_UP:
2023 SDL_Log("SDL EVENT: Pen %" SDL_PRIu32 " button %d released at %g,%g",
2024 event->pbutton.which, event->pbutton.button, event->pbutton.x, event->pbutton.y);
2025 break;
2026 case SDL_EVENT_PEN_MOTION:
2027 SDL_Log("SDL EVENT: Pen %" SDL_PRIu32 " moved to %g,%g",
2028 event->pmotion.which, event->pmotion.x, event->pmotion.y);
2029 break;
2030 case SDL_EVENT_PEN_AXIS:
2031 SDL_Log("SDL EVENT: Pen %" SDL_PRIu32 " axis %d changed to %.2f",
2032 event->paxis.which, event->paxis.axis, event->paxis.value);
2033 break;
2034 case SDL_EVENT_LOCALE_CHANGED:
2035 SDL_Log("SDL EVENT: Locale changed");
2036 break;
2037 case SDL_EVENT_QUIT:
2038 SDL_Log("SDL EVENT: Quit requested");
2039 break;
2040 case SDL_EVENT_USER:
2041 SDL_Log("SDL EVENT: User event %" SDL_PRIs32, event->user.code);
2042 break;
2043 default:
2044 SDL_Log("Unknown event 0x%4.4" SDL_PRIx32, event->type);
2045 break;
2046 }
2047}
2048
2049#define SCREENSHOT_FILE "screenshot.bmp"
2050
2051typedef struct
2052{
2053 void *image;
2054 size_t size;
2055} SDLTest_ClipboardData;
2056
2057static void SDLCALL SDLTest_ScreenShotClipboardCleanup(void *context)
2058{
2059 SDLTest_ClipboardData *data = (SDLTest_ClipboardData *)context;
2060
2061 SDL_Log("Cleaning up screenshot image data");
2062
2063 if (data->image) {
2064 SDL_free(data->image);
2065 }
2066 SDL_free(data);
2067}
2068
2069static const void * SDLCALL SDLTest_ScreenShotClipboardProvider(void *context, const char *mime_type, size_t *size)
2070{
2071 SDLTest_ClipboardData *data = (SDLTest_ClipboardData *)context;
2072
2073 if (SDL_strncmp(mime_type, "text", 4) == 0) {
2074 SDL_Log("Providing screenshot title to clipboard!");
2075
2076 /* Return "Test screenshot" */
2077 *size = 15;
2078 return "Test screenshot (but this isn't part of it)";
2079 }
2080
2081 SDL_Log("Providing screenshot image to clipboard!");
2082
2083 if (!data->image) {
2084 SDL_IOStream *file;
2085
2086 file = SDL_IOFromFile(SCREENSHOT_FILE, "r");
2087 if (file) {
2088 size_t length = (size_t)SDL_GetIOSize(file);
2089 void *image = SDL_malloc(length);
2090 if (image) {
2091 if (SDL_ReadIO(file, image, length) != length) {
2092 SDL_Log("Couldn't read %s: %s", SCREENSHOT_FILE, SDL_GetError());
2093 SDL_free(image);
2094 image = NULL;
2095 }
2096 }
2097 SDL_CloseIO(file);
2098
2099 if (image) {
2100 data->image = image;
2101 data->size = length;
2102 }
2103 } else {
2104 SDL_Log("Couldn't load %s: %s", SCREENSHOT_FILE, SDL_GetError());
2105 }
2106 }
2107
2108 *size = data->size;
2109 return data->image;
2110}
2111
2112static void SDLTest_CopyScreenShot(SDL_Renderer *renderer)
2113{
2114 SDL_Surface *surface;
2115 const char *image_formats[] = {
2116 "text/plain;charset=utf-8",
2117 "image/bmp"
2118 };
2119 SDLTest_ClipboardData *clipboard_data;
2120
2121 if (!renderer) {
2122 return;
2123 }
2124
2125 surface = SDL_RenderReadPixels(renderer, NULL);
2126 if (!surface) {
2127 SDL_Log("Couldn't read screen: %s", SDL_GetError());
2128 return;
2129 }
2130
2131 if (!SDL_SaveBMP(surface, SCREENSHOT_FILE)) {
2132 SDL_Log("Couldn't save %s: %s", SCREENSHOT_FILE, SDL_GetError());
2133 SDL_DestroySurface(surface);
2134 return;
2135 }
2136 SDL_DestroySurface(surface);
2137
2138 clipboard_data = (SDLTest_ClipboardData *)SDL_calloc(1, sizeof(*clipboard_data));
2139 if (!clipboard_data) {
2140 SDL_Log("Couldn't allocate clipboard data");
2141 return;
2142 }
2143 SDL_SetClipboardData(SDLTest_ScreenShotClipboardProvider, SDLTest_ScreenShotClipboardCleanup, clipboard_data, image_formats, SDL_arraysize(image_formats));
2144 SDL_Log("Saved screenshot to %s and clipboard", SCREENSHOT_FILE);
2145}
2146
2147static void SDLTest_PasteScreenShot(void)
2148{
2149 const char *image_formats[] = {
2150 "image/bmp",
2151 "image/png",
2152 "image/tiff",
2153 };
2154 size_t i;
2155
2156 for (i = 0; i < SDL_arraysize(image_formats); ++i) {
2157 size_t size;
2158 void *data = SDL_GetClipboardData(image_formats[i], &size);
2159 if (data) {
2160 char filename[16];
2161 SDL_IOStream *file;
2162
2163 SDL_snprintf(filename, sizeof(filename), "clipboard.%s", image_formats[i] + 6);
2164 file = SDL_IOFromFile(filename, "w");
2165 if (file) {
2166 SDL_Log("Writing clipboard image to %s", filename);
2167 SDL_WriteIO(file, data, size);
2168 SDL_CloseIO(file);
2169 }
2170 SDL_free(data);
2171 return;
2172 }
2173 }
2174 SDL_Log("No supported screenshot data in the clipboard");
2175}
2176
2177static void FullscreenTo(SDLTest_CommonState *state, int index, int windowId)
2178{
2179 int num_displays;
2180 SDL_DisplayID *displays;
2181 SDL_Window *window;
2182 SDL_WindowFlags flags;
2183 const SDL_DisplayMode *mode;
2184 struct SDL_Rect rect = { 0, 0, 0, 0 };
2185
2186 displays = SDL_GetDisplays(&num_displays);
2187 if (displays && index < num_displays) {
2188 window = SDL_GetWindowFromID(windowId);
2189 if (window) {
2190 SDL_GetDisplayBounds(displays[index], &rect);
2191
2192 flags = SDL_GetWindowFlags(window);
2193 if (flags & SDL_WINDOW_FULLSCREEN) {
2194 SDL_SetWindowFullscreen(window, false);
2195 SDL_Delay(15);
2196 }
2197
2198 mode = SDL_GetWindowFullscreenMode(window);
2199 if (mode) {
2200 /* Try to set the existing mode on the new display */
2201 SDL_DisplayMode new_mode;
2202
2203 SDL_memcpy(&new_mode, mode, sizeof(new_mode));
2204 new_mode.displayID = displays[index];
2205 if (!SDL_SetWindowFullscreenMode(window, &new_mode)) {
2206 /* Try again with a default mode */
2207 bool include_high_density_modes = false;
2208 if (state->window_flags & SDL_WINDOW_HIGH_PIXEL_DENSITY) {
2209 include_high_density_modes = true;
2210 }
2211 if (SDL_GetClosestFullscreenDisplayMode(displays[index], state->window_w, state->window_h, state->refresh_rate, include_high_density_modes, &new_mode)) {
2212 SDL_SetWindowFullscreenMode(window, &new_mode);
2213 }
2214 }
2215 }
2216 if (!mode) {
2217 SDL_SetWindowPosition(window, rect.x, rect.y);
2218 }
2219 SDL_SetWindowFullscreen(window, true);
2220 }
2221 }
2222 SDL_free(displays);
2223}
2224
2225SDL_AppResult SDLTest_CommonEventMainCallbacks(SDLTest_CommonState *state, const SDL_Event *event)
2226{
2227 int i;
2228
2229 if (state->verbose & VERBOSE_EVENT) {
2230 if ((event->type != SDL_EVENT_MOUSE_MOTION &&
2231 event->type != SDL_EVENT_FINGER_MOTION &&
2232 event->type != SDL_EVENT_PEN_MOTION &&
2233 event->type != SDL_EVENT_PEN_AXIS &&
2234 event->type != SDL_EVENT_JOYSTICK_AXIS_MOTION) ||
2235 (state->verbose & VERBOSE_MOTION)) {
2236 SDLTest_PrintEvent(event);
2237 }
2238 }
2239
2240 switch (event->type) {
2241 case SDL_EVENT_WINDOW_DISPLAY_SCALE_CHANGED:
2242 if (state->auto_scale_content) {
2243 SDL_Window *window = SDL_GetWindowFromEvent(event);
2244 if (window) {
2245 float scale = SDL_GetDisplayContentScale(SDL_GetDisplayForWindow(window));
2246 int w = state->window_w;
2247 int h = state->window_h;
2248
2249 w = (int)SDL_ceilf(w * scale);
2250 h = (int)SDL_ceilf(h * scale);
2251 SDL_SetWindowSize(window, w, h);
2252 }
2253 }
2254 break;
2255 case SDL_EVENT_WINDOW_FOCUS_LOST:
2256 if (state->flash_on_focus_loss) {
2257 SDL_Window *window = SDL_GetWindowFromEvent(event);
2258 if (window) {
2259 SDL_FlashWindow(window, SDL_FLASH_UNTIL_FOCUSED);
2260 }
2261 }
2262 break;
2263 case SDL_EVENT_WINDOW_CLOSE_REQUESTED:
2264 {
2265 SDL_Window *window = SDL_GetWindowFromEvent(event);
2266 if (window) {
2267 SDL_HideWindow(window);
2268 }
2269 break;
2270 }
2271 case SDL_EVENT_KEY_DOWN:
2272 {
2273 bool withControl = !!(event->key.mod & SDL_KMOD_CTRL);
2274 bool withShift = !!(event->key.mod & SDL_KMOD_SHIFT);
2275 bool withAlt = !!(event->key.mod & SDL_KMOD_ALT);
2276
2277 switch (event->key.key) {
2278 /* Add hotkeys here */
2279 case SDLK_PRINTSCREEN:
2280 {
2281 SDL_Window *window = SDL_GetWindowFromEvent(event);
2282 if (window) {
2283 for (i = 0; i < state->num_windows; ++i) {
2284 if (window == state->windows[i]) {
2285 SDLTest_CopyScreenShot(state->renderers[i]);
2286 }
2287 }
2288 }
2289 } break;
2290 case SDLK_EQUALS:
2291 if (withControl) {
2292 /* Ctrl-+ double the size of the window */
2293 SDL_Window *window = SDL_GetWindowFromEvent(event);
2294 if (window) {
2295 int w, h;
2296 SDL_GetWindowSize(window, &w, &h);
2297 SDL_SetWindowSize(window, w * 2, h * 2);
2298 }
2299 }
2300 break;
2301 case SDLK_MINUS:
2302 if (withControl) {
2303 /* Ctrl-- half the size of the window */
2304 SDL_Window *window = SDL_GetWindowFromEvent(event);
2305 if (window) {
2306 int w, h;
2307 SDL_GetWindowSize(window, &w, &h);
2308 SDL_SetWindowSize(window, w / 2, h / 2);
2309 }
2310 }
2311 break;
2312 case SDLK_UP:
2313 case SDLK_DOWN:
2314 case SDLK_LEFT:
2315 case SDLK_RIGHT:
2316 if (withAlt) {
2317 /* Alt-Up/Down/Left/Right switches between displays */
2318 SDL_Window *window = SDL_GetWindowFromEvent(event);
2319 if (window) {
2320 int num_displays;
2321 const SDL_DisplayID *displays = SDL_GetDisplays(&num_displays);
2322 if (displays) {
2323 SDL_DisplayID displayID = SDL_GetDisplayForWindow(window);
2324 int current_index = -1;
2325
2326 for (i = 0; i < num_displays; ++i) {
2327 if (displayID == displays[i]) {
2328 current_index = i;
2329 break;
2330 }
2331 }
2332 if (current_index >= 0) {
2333 SDL_DisplayID dest;
2334 if (event->key.key == SDLK_UP || event->key.key == SDLK_LEFT) {
2335 dest = displays[(current_index + num_displays - 1) % num_displays];
2336 } else {
2337 dest = displays[(current_index + num_displays + 1) % num_displays];
2338 }
2339 SDL_Log("Centering on display (%" SDL_PRIu32 ")", dest);
2340 SDL_SetWindowPosition(window,
2341 SDL_WINDOWPOS_CENTERED_DISPLAY(dest),
2342 SDL_WINDOWPOS_CENTERED_DISPLAY(dest));
2343 }
2344 }
2345 }
2346 }
2347 if (withShift) {
2348 /* Shift-Up/Down/Left/Right shift the window by 100px */
2349 SDL_Window *window = SDL_GetWindowFromEvent(event);
2350 if (window) {
2351 const int delta = 100;
2352 int x, y;
2353 SDL_GetWindowPosition(window, &x, &y);
2354
2355 if (event->key.key == SDLK_UP) {
2356 y -= delta;
2357 }
2358 if (event->key.key == SDLK_DOWN) {
2359 y += delta;
2360 }
2361 if (event->key.key == SDLK_LEFT) {
2362 x -= delta;
2363 }
2364 if (event->key.key == SDLK_RIGHT) {
2365 x += delta;
2366 }
2367
2368 SDL_Log("Setting position to (%d, %d)", x, y);
2369 SDL_SetWindowPosition(window, x, y);
2370 }
2371 }
2372 break;
2373 case SDLK_O:
2374 if (withControl) {
2375 /* Ctrl-O (or Ctrl-Shift-O) changes window opacity. */
2376 SDL_Window *window = SDL_GetWindowFromEvent(event);
2377 if (window) {
2378 float opacity = SDL_GetWindowOpacity(window);
2379 if (withShift) {
2380 opacity += 0.20f;
2381 } else {
2382 opacity -= 0.20f;
2383 }
2384 SDL_SetWindowOpacity(window, opacity);
2385 }
2386 }
2387 break;
2388 case SDLK_H:
2389 if (withControl) {
2390 /* Ctrl-H changes cursor visibility. */
2391 if (SDL_CursorVisible()) {
2392 SDL_HideCursor();
2393 } else {
2394 SDL_ShowCursor();
2395 }
2396 }
2397 break;
2398 case SDLK_C:
2399 if (withAlt) {
2400 /* Alt-C copy awesome text to the primary selection! */
2401 SDL_SetPrimarySelectionText("SDL rocks!\nYou know it!");
2402 SDL_Log("Copied text to primary selection");
2403
2404 } else if (withControl) {
2405 if (withShift) {
2406 /* Ctrl-Shift-C copy screenshot! */
2407 SDL_Window *window = SDL_GetWindowFromEvent(event);
2408 if (window) {
2409 for (i = 0; i < state->num_windows; ++i) {
2410 if (window == state->windows[i]) {
2411 SDLTest_CopyScreenShot(state->renderers[i]);
2412 }
2413 }
2414 }
2415 } else {
2416 /* Ctrl-C copy awesome text! */
2417 SDL_SetClipboardText("SDL rocks!\nYou know it!");
2418 SDL_Log("Copied text to clipboard");
2419 }
2420 break;
2421 }
2422 break;
2423 case SDLK_V:
2424 if (withAlt) {
2425 /* Alt-V paste awesome text from the primary selection! */
2426 char *text = SDL_GetPrimarySelectionText();
2427 if (*text) {
2428 SDL_Log("Primary selection: %s", text);
2429 } else {
2430 SDL_Log("Primary selection is empty");
2431 }
2432 SDL_free(text);
2433
2434 } else if (withControl) {
2435 if (withShift) {
2436 /* Ctrl-Shift-V paste screenshot! */
2437 SDLTest_PasteScreenShot();
2438 } else {
2439 /* Ctrl-V paste awesome text! */
2440 char *text = SDL_GetClipboardText();
2441 if (*text) {
2442 SDL_Log("Clipboard: %s", text);
2443 } else {
2444 SDL_Log("Clipboard is empty");
2445 }
2446 SDL_free(text);
2447 }
2448 }
2449 break;
2450 case SDLK_F:
2451 if (withControl) {
2452 /* Ctrl-F flash the window */
2453 SDL_Window *window = SDL_GetWindowFromEvent(event);
2454 if (window) {
2455 SDL_FlashWindow(window, SDL_FLASH_BRIEFLY);
2456 }
2457 }
2458 break;
2459 case SDLK_G:
2460 if (withControl) {
2461 /* Ctrl-G toggle mouse grab */
2462 SDL_Window *window = SDL_GetWindowFromEvent(event);
2463 if (window) {
2464 SDL_SetWindowMouseGrab(window, !SDL_GetWindowMouseGrab(window));
2465 }
2466 }
2467 break;
2468 case SDLK_K:
2469 if (withControl) {
2470 /* Ctrl-K toggle keyboard grab */
2471 SDL_Window *window = SDL_GetWindowFromEvent(event);
2472 if (window) {
2473 SDL_SetWindowKeyboardGrab(window, !SDL_GetWindowKeyboardGrab(window));
2474 }
2475 }
2476 break;
2477 case SDLK_M:
2478 if (withControl) {
2479 /* Ctrl-M maximize */
2480 SDL_Window *window = SDL_GetWindowFromEvent(event);
2481 if (window) {
2482 SDL_WindowFlags flags = SDL_GetWindowFlags(window);
2483 if (!(flags & SDL_WINDOW_RESIZABLE)) {
2484 SDL_SetWindowResizable(window, true);
2485 }
2486 if (flags & SDL_WINDOW_MAXIMIZED) {
2487 SDL_RestoreWindow(window);
2488 } else {
2489 SDL_MaximizeWindow(window);
2490 }
2491 if (!(flags & SDL_WINDOW_RESIZABLE)) {
2492 SDL_SetWindowResizable(window, false);
2493 }
2494 }
2495 }
2496 if (withShift) {
2497 SDL_Window *window = SDL_GetWindowFromEvent(event);
2498 if (window) {
2499 const bool shouldCapture = !(SDL_GetWindowFlags(window) & SDL_WINDOW_MOUSE_CAPTURE);
2500 const bool rc = SDL_CaptureMouse(shouldCapture);
2501 SDL_Log("%sapturing mouse %s!", shouldCapture ? "C" : "Unc", rc ? "succeeded" : "failed");
2502 }
2503 }
2504 break;
2505 case SDLK_R:
2506 if (withControl) {
2507 /* Ctrl-R toggle mouse relative mode */
2508 SDL_Window *window = SDL_GetWindowFromEvent(event);
2509 if (window) {
2510 SDL_SetWindowRelativeMouseMode(window, !SDL_GetWindowRelativeMouseMode(window));
2511 }
2512 }
2513 break;
2514 case SDLK_T:
2515 if (withControl) {
2516 /* Ctrl-T toggle topmost mode */
2517 SDL_Window *window = SDL_GetWindowFromEvent(event);
2518 if (window) {
2519 SDL_WindowFlags flags = SDL_GetWindowFlags(window);
2520 if (flags & SDL_WINDOW_ALWAYS_ON_TOP) {
2521 SDL_SetWindowAlwaysOnTop(window, false);
2522 } else {
2523 SDL_SetWindowAlwaysOnTop(window, true);
2524 }
2525 }
2526 }
2527 break;
2528 case SDLK_Z:
2529 if (withControl) {
2530 /* Ctrl-Z minimize */
2531 SDL_Window *window = SDL_GetWindowFromEvent(event);
2532 if (window) {
2533 SDL_MinimizeWindow(window);
2534 }
2535 }
2536 break;
2537 case SDLK_RETURN:
2538 if (withControl) {
2539 /* Ctrl-Enter toggle fullscreen */
2540 SDL_Window *window = SDL_GetWindowFromEvent(event);
2541 if (window) {
2542 SDL_WindowFlags flags = SDL_GetWindowFlags(window);
2543 if (!(flags & SDL_WINDOW_FULLSCREEN) ||
2544 !SDL_GetWindowFullscreenMode(window)) {
2545 SDL_SetWindowFullscreenMode(window, &state->fullscreen_mode);
2546 SDL_SetWindowFullscreen(window, true);
2547 } else {
2548 SDL_SetWindowFullscreen(window, false);
2549 }
2550 }
2551 } else if (withAlt) {
2552 /* Alt-Enter toggle fullscreen desktop */
2553 SDL_Window *window = SDL_GetWindowFromEvent(event);
2554 if (window) {
2555 SDL_WindowFlags flags = SDL_GetWindowFlags(window);
2556 if (!(flags & SDL_WINDOW_FULLSCREEN) ||
2557 SDL_GetWindowFullscreenMode(window)) {
2558 SDL_SetWindowFullscreenMode(window, NULL);
2559 SDL_SetWindowFullscreen(window, true);
2560 } else {
2561 SDL_SetWindowFullscreen(window, false);
2562 }
2563 }
2564 }
2565
2566 break;
2567 case SDLK_B:
2568 if (withControl) {
2569 /* Ctrl-B toggle window border */
2570 SDL_Window *window = SDL_GetWindowFromEvent(event);
2571 if (window) {
2572 const SDL_WindowFlags flags = SDL_GetWindowFlags(window);
2573 const bool b = (flags & SDL_WINDOW_BORDERLESS) ? true : false;
2574 SDL_SetWindowBordered(window, b);
2575 }
2576 }
2577 break;
2578 case SDLK_A:
2579 if (withControl) {
2580 /* Ctrl-A toggle aspect ratio */
2581 SDL_Window *window = SDL_GetWindowFromEvent(event);
2582 if (window) {
2583 float min_aspect = 0.0f, max_aspect = 0.0f;
2584
2585 SDL_GetWindowAspectRatio(window, &min_aspect, &max_aspect);
2586 if (min_aspect > 0.0f || max_aspect > 0.0f) {
2587 min_aspect = 0.0f;
2588 max_aspect = 0.0f;
2589 } else {
2590 min_aspect = 1.0f;
2591 max_aspect = 1.0f;
2592 }
2593 SDL_SetWindowAspectRatio(window, min_aspect, max_aspect);
2594 }
2595 }
2596 break;
2597 case SDLK_0:
2598 if (withControl) {
2599 SDL_Window *window = SDL_GetWindowFromEvent(event);
2600 SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_INFORMATION, "Test Message", "You're awesome!", window);
2601 }
2602 break;
2603 case SDLK_1:
2604 if (withControl) {
2605 FullscreenTo(state, 0, event->key.windowID);
2606 }
2607 break;
2608 case SDLK_2:
2609 if (withControl) {
2610 FullscreenTo(state, 1, event->key.windowID);
2611 }
2612 break;
2613 case SDLK_ESCAPE:
2614 return SDL_APP_SUCCESS;
2615 default:
2616 break;
2617 }
2618 break;
2619 }
2620 case SDL_EVENT_QUIT:
2621 return SDL_APP_SUCCESS;
2622 default:
2623 break;
2624 }
2625
2626 return SDL_APP_CONTINUE;
2627}
2628
2629void SDLTest_CommonEvent(SDLTest_CommonState *state, SDL_Event *event, int *done)
2630{
2631 if (SDLTest_CommonEventMainCallbacks(state, event) != SDL_APP_CONTINUE) {
2632 *done = 1;
2633 }
2634}
2635
2636void SDLTest_CommonQuit(SDLTest_CommonState *state)
2637{
2638 if (state) {
2639 int i;
2640
2641 if (state->targets) {
2642 for (i = 0; i < state->num_windows; ++i) {
2643 if (state->targets[i]) {
2644 SDL_DestroyTexture(state->targets[i]);
2645 }
2646 }
2647 SDL_free(state->targets);
2648 }
2649 if (state->renderers) {
2650 for (i = 0; i < state->num_windows; ++i) {
2651 if (state->renderers[i]) {
2652 SDL_DestroyRenderer(state->renderers[i]);
2653 }
2654 }
2655 SDL_free(state->renderers);
2656 }
2657 if (state->windows) {
2658 for (i = 0; i < state->num_windows; i++) {
2659 SDL_DestroyWindow(state->windows[i]);
2660 }
2661 SDL_free(state->windows);
2662 }
2663 }
2664 SDL_Quit();
2665 SDLTest_CommonDestroyState(state);
2666}
2667
2668void SDLTest_CommonDrawWindowInfo(SDL_Renderer *renderer, SDL_Window *window, float *usedHeight)
2669{
2670 char text[1024];
2671 float textY = 0.0f;
2672 const int lineHeight = 10;
2673 int x, y, w, h;
2674 float fx, fy;
2675 SDL_Rect rect;
2676 const SDL_DisplayMode *mode;
2677 float scaleX, scaleY;
2678 SDL_MouseButtonFlags flags;
2679 SDL_DisplayID windowDisplayID = SDL_GetDisplayForWindow(window);
2680 const char *name;
2681 SDL_RendererLogicalPresentation logical_presentation;
2682
2683 /* Video */
2684
2685 SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255);
2686 SDLTest_DrawString(renderer, 0.0f, textY, "-- Video --");
2687 textY += lineHeight;
2688
2689 SDL_SetRenderDrawColor(renderer, 170, 170, 170, 255);
2690
2691 (void)SDL_snprintf(text, sizeof(text), "SDL_GetCurrentVideoDriver: %s", SDL_GetCurrentVideoDriver());
2692 SDLTest_DrawString(renderer, 0.0f, textY, text);
2693 textY += lineHeight;
2694
2695 /* Renderer */
2696
2697 SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255);
2698 SDLTest_DrawString(renderer, 0.0f, textY, "-- Renderer --");
2699 textY += lineHeight;
2700
2701 SDL_SetRenderDrawColor(renderer, 170, 170, 170, 255);
2702
2703 name = SDL_GetRendererName(renderer);
2704 (void)SDL_snprintf(text, sizeof(text), "SDL_GetRendererName: %s", name);
2705 SDLTest_DrawString(renderer, 0.0f, textY, text);
2706 textY += lineHeight;
2707
2708 if (SDL_GetRenderOutputSize(renderer, &w, &h)) {
2709 (void)SDL_snprintf(text, sizeof(text), "SDL_GetRenderOutputSize: %dx%d", w, h);
2710 SDLTest_DrawString(renderer, 0.0f, textY, text);
2711 textY += lineHeight;
2712 }
2713
2714 if (SDL_GetCurrentRenderOutputSize(renderer, &w, &h)) {
2715 (void)SDL_snprintf(text, sizeof(text), "SDL_GetCurrentRenderOutputSize: %dx%d", w, h);
2716 SDLTest_DrawString(renderer, 0.0f, textY, text);
2717 textY += lineHeight;
2718 }
2719
2720 SDL_GetRenderViewport(renderer, &rect);
2721 (void)SDL_snprintf(text, sizeof(text), "SDL_GetRenderViewport: %d,%d, %dx%d",
2722 rect.x, rect.y, rect.w, rect.h);
2723 SDLTest_DrawString(renderer, 0.0f, textY, text);
2724 textY += lineHeight;
2725
2726 SDL_GetRenderScale(renderer, &scaleX, &scaleY);
2727 (void)SDL_snprintf(text, sizeof(text), "SDL_GetRenderScale: %g,%g",
2728 scaleX, scaleY);
2729 SDLTest_DrawString(renderer, 0.0f, textY, text);
2730 textY += lineHeight;
2731
2732 SDL_GetRenderLogicalPresentation(renderer, &w, &h, &logical_presentation);
2733 (void)SDL_snprintf(text, sizeof(text), "SDL_GetRenderLogicalPresentation: %dx%d ", w, h);
2734 SDLTest_PrintLogicalPresentation(text, sizeof(text), logical_presentation);
2735 textY += lineHeight;
2736
2737 /* Window */
2738
2739 SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255);
2740 SDLTest_DrawString(renderer, 0.0f, textY, "-- Window --");
2741 textY += lineHeight;
2742
2743 SDL_SetRenderDrawColor(renderer, 170, 170, 170, 255);
2744
2745 SDL_GetWindowPosition(window, &x, &y);
2746 (void)SDL_snprintf(text, sizeof(text), "SDL_GetWindowPosition: %d,%d", x, y);
2747 SDLTest_DrawString(renderer, 0.0f, textY, text);
2748 textY += lineHeight;
2749
2750 SDL_GetWindowSize(window, &w, &h);
2751 (void)SDL_snprintf(text, sizeof(text), "SDL_GetWindowSize: %dx%d", w, h);
2752 SDLTest_DrawString(renderer, 0.0f, textY, text);
2753 textY += lineHeight;
2754
2755 SDL_GetWindowSafeArea(window, &rect);
2756 (void)SDL_snprintf(text, sizeof(text), "SDL_GetWindowSafeArea: %d,%d %dx%d", rect.x, rect.y, rect.w, rect.h);
2757 SDLTest_DrawString(renderer, 0.0f, textY, text);
2758 textY += lineHeight;
2759
2760 (void)SDL_snprintf(text, sizeof(text), "SDL_GetWindowFlags: ");
2761 SDLTest_PrintWindowFlags(text, sizeof(text), SDL_GetWindowFlags(window));
2762 SDLTest_DrawString(renderer, 0.0f, textY, text);
2763 textY += lineHeight;
2764
2765 mode = SDL_GetWindowFullscreenMode(window);
2766 if (mode) {
2767 (void)SDL_snprintf(text, sizeof(text), "SDL_GetWindowFullscreenMode: %dx%d@%gx %gHz, (%s)",
2768 mode->w, mode->h, mode->pixel_density, mode->refresh_rate, SDL_GetPixelFormatName(mode->format));
2769 SDLTest_DrawString(renderer, 0.0f, textY, text);
2770 textY += lineHeight;
2771 }
2772
2773 /* Display */
2774
2775 SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255);
2776 SDLTest_DrawString(renderer, 0.0f, textY, "-- Display --");
2777 textY += lineHeight;
2778
2779 SDL_SetRenderDrawColor(renderer, 170, 170, 170, 255);
2780
2781 (void)SDL_snprintf(text, sizeof(text), "SDL_GetDisplayForWindow: %" SDL_PRIu32, windowDisplayID);
2782 SDLTest_DrawString(renderer, 0.0f, textY, text);
2783 textY += lineHeight;
2784
2785 (void)SDL_snprintf(text, sizeof(text), "SDL_GetDisplayName: %s", SDL_GetDisplayName(windowDisplayID));
2786 SDLTest_DrawString(renderer, 0.0f, textY, text);
2787 textY += lineHeight;
2788
2789 if (SDL_GetDisplayBounds(windowDisplayID, &rect)) {
2790 (void)SDL_snprintf(text, sizeof(text), "SDL_GetDisplayBounds: %d,%d, %dx%d",
2791 rect.x, rect.y, rect.w, rect.h);
2792 SDLTest_DrawString(renderer, 0.0f, textY, text);
2793 textY += lineHeight;
2794 }
2795
2796 mode = SDL_GetCurrentDisplayMode(windowDisplayID);
2797 if (mode) {
2798 (void)SDL_snprintf(text, sizeof(text), "SDL_GetCurrentDisplayMode: %dx%d@%gx %gHz, (%s)",
2799 mode->w, mode->h, mode->pixel_density, mode->refresh_rate, SDL_GetPixelFormatName(mode->format));
2800 SDLTest_DrawString(renderer, 0.0f, textY, text);
2801 textY += lineHeight;
2802 }
2803
2804 mode = SDL_GetDesktopDisplayMode(windowDisplayID);
2805 if (mode) {
2806 (void)SDL_snprintf(text, sizeof(text), "SDL_GetDesktopDisplayMode: %dx%d@%gx %gHz, (%s)",
2807 mode->w, mode->h, mode->pixel_density, mode->refresh_rate, SDL_GetPixelFormatName(mode->format));
2808 SDLTest_DrawString(renderer, 0.0f, textY, text);
2809 textY += lineHeight;
2810 }
2811
2812 (void)SDL_snprintf(text, sizeof(text), "SDL_GetNaturalDisplayOrientation: ");
2813 SDLTest_PrintDisplayOrientation(text, sizeof(text), SDL_GetNaturalDisplayOrientation(windowDisplayID));
2814 SDLTest_DrawString(renderer, 0.0f, textY, text);
2815 textY += lineHeight;
2816
2817 (void)SDL_snprintf(text, sizeof(text), "SDL_GetCurrentDisplayOrientation: ");
2818 SDLTest_PrintDisplayOrientation(text, sizeof(text), SDL_GetCurrentDisplayOrientation(windowDisplayID));
2819 SDLTest_DrawString(renderer, 0.0f, textY, text);
2820 textY += lineHeight;
2821
2822 (void)SDL_snprintf(text, sizeof(text), "SDL_GetDisplayContentScale: %g", SDL_GetDisplayContentScale(windowDisplayID));
2823 SDLTest_DrawString(renderer, 0.0f, textY, text);
2824 textY += lineHeight;
2825
2826 /* Mouse */
2827
2828 SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255);
2829 SDLTest_DrawString(renderer, 0.0f, textY, "-- Mouse --");
2830 textY += lineHeight;
2831
2832 SDL_SetRenderDrawColor(renderer, 170, 170, 170, 255);
2833
2834 flags = SDL_GetMouseState(&fx, &fy);
2835 (void)SDL_snprintf(text, sizeof(text), "SDL_GetMouseState: %g,%g ", fx, fy);
2836 SDLTest_PrintButtonMask(text, sizeof(text), flags);
2837 SDLTest_DrawString(renderer, 0.0f, textY, text);
2838 textY += lineHeight;
2839
2840 flags = SDL_GetGlobalMouseState(&fx, &fy);
2841 (void)SDL_snprintf(text, sizeof(text), "SDL_GetGlobalMouseState: %g,%g ", fx, fy);
2842 SDLTest_PrintButtonMask(text, sizeof(text), flags);
2843 SDLTest_DrawString(renderer, 0.0f, textY, text);
2844 textY += lineHeight;
2845
2846 /* Keyboard */
2847
2848 SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255);
2849 SDLTest_DrawString(renderer, 0, textY, "-- Keyboard --");
2850 textY += lineHeight;
2851
2852 SDL_SetRenderDrawColor(renderer, 170, 170, 170, 255);
2853
2854 (void)SDL_snprintf(text, sizeof(text), "SDL_GetModState: ");
2855 SDLTest_PrintModState(text, sizeof(text), SDL_GetModState());
2856 SDLTest_DrawString(renderer, 0, textY, text);
2857 textY += lineHeight;
2858
2859 if (usedHeight) {
2860 *usedHeight = textY;
2861 }
2862}
diff --git a/contrib/SDL-3.2.8/src/test/SDL_test_compare.c b/contrib/SDL-3.2.8/src/test/SDL_test_compare.c
new file mode 100644
index 0000000..c3ee039
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/test/SDL_test_compare.c
@@ -0,0 +1,218 @@
1/*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
4
5 This software is provided 'as-is', without any express or implied
6 warranty. In no event will the authors be held liable for any damages
7 arising from the use of this software.
8
9 Permission is granted to anyone to use this software for any purpose,
10 including commercial applications, and to alter it and redistribute it
11 freely, subject to the following restrictions:
12
13 1. The origin of this software must not be misrepresented; you must not
14 claim that you wrote the original software. If you use this software
15 in a product, an acknowledgment in the product documentation would be
16 appreciated but is not required.
17 2. Altered source versions must be plainly marked as such, and must not be
18 misrepresented as being the original software.
19 3. This notice may not be removed or altered from any source distribution.
20*/
21
22/*
23
24 Based on automated SDL_Surface tests originally written by Edgar Simo 'bobbens'.
25
26 Rewritten for test lib by Andreas Schiffler.
27
28*/
29#include <SDL3/SDL_test.h>
30
31#define FILENAME_SIZE 128
32
33/* Counter for _CompareSurface calls; used for filename creation when comparisons fail */
34static int _CompareSurfaceCount = 0;
35
36/* Compare surfaces */
37int SDLTest_CompareSurfaces(SDL_Surface *surface, SDL_Surface *referenceSurface, int allowable_error)
38{
39 int ret;
40 int i, j;
41 int dist;
42 int sampleErrorX = 0, sampleErrorY = 0, sampleDist = 0;
43 SDL_Color sampleReference = { 0, 0, 0, 0 };
44 SDL_Color sampleActual = { 0, 0, 0, 0 };
45 Uint8 R, G, B, A;
46 Uint8 Rd, Gd, Bd, Ad;
47 char imageFilename[FILENAME_SIZE];
48 char referenceFilename[FILENAME_SIZE];
49
50 /* Validate input surfaces */
51 if (!surface) {
52 SDLTest_LogError("Cannot compare NULL surface");
53 return -1;
54 }
55
56 if (!referenceSurface) {
57 SDLTest_LogError("Cannot compare NULL reference surface");
58 return -1;
59 }
60
61 /* Make sure surface size is the same. */
62 if ((surface->w != referenceSurface->w) || (surface->h != referenceSurface->h)) {
63 SDLTest_LogError("Expected %dx%d surface, got %dx%d", referenceSurface->w, referenceSurface->h, surface->w, surface->h);
64 return -2;
65 }
66
67 /* Sanitize input value */
68 if (allowable_error < 0) {
69 allowable_error = 0;
70 }
71
72 SDL_LockSurface(surface);
73 SDL_LockSurface(referenceSurface);
74
75 ret = 0;
76 /* Compare image - should be same format. */
77 for (j = 0; j < surface->h; j++) {
78 for (i = 0; i < surface->w; i++) {
79 int temp;
80
81 temp = SDL_ReadSurfacePixel(surface, i, j, &R, &G, &B, &A);
82 if (!temp) {
83 SDLTest_LogError("Failed to retrieve pixel (%d,%d): %s", i, j, SDL_GetError());
84 ret++;
85 continue;
86 }
87
88 temp = SDL_ReadSurfacePixel(referenceSurface, i, j, &Rd, &Gd, &Bd, &Ad);
89 if (!temp) {
90 SDLTest_LogError("Failed to retrieve reference pixel (%d,%d): %s", i, j, SDL_GetError());
91 ret++;
92 continue;
93 }
94
95 dist = 0;
96 dist += (R - Rd) * (R - Rd);
97 dist += (G - Gd) * (G - Gd);
98 dist += (B - Bd) * (B - Bd);
99
100 /* Allow some difference in blending accuracy */
101 if (dist > allowable_error) {
102 ret++;
103 if (ret == 1) {
104 sampleErrorX = i;
105 sampleErrorY = j;
106 sampleDist = dist;
107 sampleReference.r = Rd;
108 sampleReference.g = Gd;
109 sampleReference.b = Bd;
110 sampleReference.a = Ad;
111 sampleActual.r = R;
112 sampleActual.g = G;
113 sampleActual.b = B;
114 sampleActual.a = A;
115 }
116 }
117 }
118 }
119
120 SDL_UnlockSurface(surface);
121 SDL_UnlockSurface(referenceSurface);
122
123 /* Save test image and reference for analysis on failures */
124 _CompareSurfaceCount++;
125 if (ret != 0) {
126 SDLTest_LogError("Comparison of pixels with allowable error of %i failed %i times.", allowable_error, ret);
127 SDLTest_LogError("Reference surface format: %s", SDL_GetPixelFormatName(referenceSurface->format));
128 SDLTest_LogError("Actual surface format: %s", SDL_GetPixelFormatName(surface->format));
129 SDLTest_LogError("First detected occurrence at position %i,%i with a squared RGB-difference of %i.", sampleErrorX, sampleErrorY, sampleDist);
130 SDLTest_LogError("Reference pixel: R=%u G=%u B=%u A=%u", sampleReference.r, sampleReference.g, sampleReference.b, sampleReference.a);
131 SDLTest_LogError("Actual pixel : R=%u G=%u B=%u A=%u", sampleActual.r, sampleActual.g, sampleActual.b, sampleActual.a);
132 (void)SDL_snprintf(imageFilename, FILENAME_SIZE - 1, "CompareSurfaces%04d_TestOutput.bmp", _CompareSurfaceCount);
133 SDL_SaveBMP(surface, imageFilename);
134 (void)SDL_snprintf(referenceFilename, FILENAME_SIZE - 1, "CompareSurfaces%04d_Reference.bmp", _CompareSurfaceCount);
135 SDL_SaveBMP(referenceSurface, referenceFilename);
136 SDLTest_LogError("Surfaces from failed comparison saved as '%s' and '%s'", imageFilename, referenceFilename);
137 }
138
139 return ret;
140}
141
142int SDLTest_CompareMemory(const void *actual, size_t size_actual, const void *reference, size_t size_reference) {
143#define WIDTH 16
144
145 const size_t size_max = SDL_max(size_actual, size_reference);
146 size_t i;
147 struct {
148 const char *header;
149 const Uint8 *data;
150 size_t size;
151 } columns[] = {
152 {
153 "actual",
154 actual,
155 size_actual,
156 },
157 {
158 "reference",
159 reference,
160 size_reference,
161 },
162 };
163 char line_buffer[16 + SDL_arraysize(columns) * (4 * WIDTH + 1) + (SDL_arraysize(columns) - 1) * 2 + 1];
164
165 SDLTest_AssertCheck(size_actual == size_reference, "Sizes of memory blocks must be equal (actual=%" SDL_PRIu64 " expected=%" SDL_PRIu64 ")", (Uint64)size_actual, (Uint64)size_reference);
166 if (size_actual == size_reference) {
167 int equals;
168 equals = SDL_memcmp(actual, reference, size_max) == 0;
169 SDLTest_AssertCheck(equals, "Memory blocks contain the same data");
170 if (equals) {
171 return 0;
172 }
173 }
174
175 SDL_memset(line_buffer, ' ', sizeof(line_buffer));
176 line_buffer[sizeof(line_buffer) - 1] = '\0';
177 for (i = 0; i < SDL_arraysize(columns); i++) {
178 SDL_memcpy(line_buffer + 16 + 1 + i * (4 * WIDTH + 3), columns[i].header, SDL_strlen(columns[i].header));
179 }
180 SDLTest_LogError("%s", line_buffer);
181
182 for (i = 0; i < size_max; i += WIDTH) {
183 size_t pos = 0;
184 size_t col;
185
186 pos += SDL_snprintf(line_buffer + pos, SDL_arraysize(line_buffer) - pos, "%016" SDL_PRIx64 , (Uint64)i);
187
188 for (col = 0; col < SDL_arraysize(columns); col++) {
189 size_t j;
190
191 for (j = 0; j < WIDTH; j++) {
192 if (i + j < columns[col].size) {
193 pos += SDL_snprintf(line_buffer + pos, SDL_arraysize(line_buffer) - pos, " %02x", columns[col].data[i + j]);
194 } else {
195 pos += SDL_snprintf(line_buffer + pos, SDL_arraysize(line_buffer) - pos, " ");
196 }
197 }
198 pos += SDL_snprintf(line_buffer + pos, SDL_arraysize(line_buffer) - pos, " ");
199 for (j = 0; j < WIDTH; j++) {
200 char c = ' ';
201 if (i + j < columns[col].size) {
202 c = columns[col].data[i + j];
203 if (!SDL_isprint(c)) {
204 c = '.';
205 }
206 }
207 pos += SDL_snprintf(line_buffer + pos, SDL_arraysize(line_buffer) - pos, "%c", c);
208 }
209 if (col < SDL_arraysize(columns) - 1) {
210 pos += SDL_snprintf(line_buffer + pos, SDL_arraysize(line_buffer), " |");
211 }
212 }
213 SDLTest_LogError("%s", line_buffer);
214 SDL_assert(pos == SDL_arraysize(line_buffer) - 1);
215 }
216#undef WIDTH
217 return 1;
218}
diff --git a/contrib/SDL-3.2.8/src/test/SDL_test_crc32.c b/contrib/SDL-3.2.8/src/test/SDL_test_crc32.c
new file mode 100644
index 0000000..f3d94b8
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/test/SDL_test_crc32.c
@@ -0,0 +1,160 @@
1/*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
4
5 This software is provided 'as-is', without any express or implied
6 warranty. In no event will the authors be held liable for any damages
7 arising from the use of this software.
8
9 Permission is granted to anyone to use this software for any purpose,
10 including commercial applications, and to alter it and redistribute it
11 freely, subject to the following restrictions:
12
13 1. The origin of this software must not be misrepresented; you must not
14 claim that you wrote the original software. If you use this software
15 in a product, an acknowledgment in the product documentation would be
16 appreciated but is not required.
17 2. Altered source versions must be plainly marked as such, and must not be
18 misrepresented as being the original software.
19 3. This notice may not be removed or altered from any source distribution.
20*/
21
22/*
23
24 Used by the test execution component.
25 Original source code contributed by A. Schiffler for GSOC project.
26
27*/
28#include <SDL3/SDL_test.h>
29
30bool SDLTest_Crc32Init(SDLTest_Crc32Context *crcContext)
31{
32 int i, j;
33 CrcUint32 c;
34
35 /* Sanity check context pointer */
36 if (!crcContext) {
37 return SDL_InvalidParamError("crcContext");
38 }
39
40 /*
41 * Build auxiliary table for parallel byte-at-a-time CRC-32
42 */
43#ifdef ORIGINAL_METHOD
44 for (i = 0; i < 256; ++i) {
45 for (c = i << 24, j = 8; j > 0; --j) {
46 c = c & 0x80000000 ? (c << 1) ^ CRC32_POLY : (c << 1);
47 }
48 crcContext->crc32_table[i] = c;
49 }
50#else
51 for (i = 0; i < 256; i++) {
52 c = i;
53 for (j = 8; j > 0; j--) {
54 if (c & 1) {
55 c = (c >> 1) ^ CRC32_POLY;
56 } else {
57 c >>= 1;
58 }
59 }
60 crcContext->crc32_table[i] = c;
61 }
62#endif
63
64 return true;
65}
66
67/* Complete CRC32 calculation on a memory block */
68bool SDLTest_Crc32Calc(SDLTest_Crc32Context *crcContext, CrcUint8 *inBuf, CrcUint32 inLen, CrcUint32 *crc32)
69{
70 if (!SDLTest_Crc32CalcStart(crcContext, crc32)) {
71 return false;
72 }
73
74 if (!SDLTest_Crc32CalcBuffer(crcContext, inBuf, inLen, crc32)) {
75 return false;
76 }
77
78 if (!SDLTest_Crc32CalcEnd(crcContext, crc32)) {
79 return false;
80 }
81
82 return true;
83}
84
85/* Start crc calculation */
86
87bool SDLTest_Crc32CalcStart(SDLTest_Crc32Context *crcContext, CrcUint32 *crc32)
88{
89 /* Sanity check pointers */
90 if (!crcContext) {
91 *crc32 = 0;
92 return SDL_InvalidParamError("crcContext");
93 }
94
95 /*
96 * Preload shift register, per CRC-32 spec
97 */
98 *crc32 = 0xffffffff;
99
100 return true;
101}
102
103/* Finish crc calculation */
104
105bool SDLTest_Crc32CalcEnd(SDLTest_Crc32Context *crcContext, CrcUint32 *crc32)
106{
107 /* Sanity check pointers */
108 if (!crcContext) {
109 *crc32 = 0;
110 return SDL_InvalidParamError("crcContext");
111 }
112
113 /*
114 * Return complement, per CRC-32 spec
115 */
116 *crc32 = (~(*crc32));
117
118 return true;
119}
120
121/* Include memory block in crc */
122
123bool SDLTest_Crc32CalcBuffer(SDLTest_Crc32Context *crcContext, CrcUint8 *inBuf, CrcUint32 inLen, CrcUint32 *crc32)
124{
125 CrcUint8 *p;
126 register CrcUint32 crc;
127
128 if (!crcContext) {
129 *crc32 = 0;
130 return SDL_InvalidParamError("crcContext");
131 }
132
133 if (!inBuf) {
134 return SDL_InvalidParamError("inBuf");
135 }
136
137 /*
138 * Calculate CRC from data
139 */
140 crc = *crc32;
141 for (p = inBuf; inLen > 0; ++p, --inLen) {
142#ifdef ORIGINAL_METHOD
143 crc = (crc << 8) ^ crcContext->crc32_table[(crc >> 24) ^ *p];
144#else
145 crc = ((crc >> 8) & 0x00FFFFFF) ^ crcContext->crc32_table[(crc ^ *p) & 0xFF];
146#endif
147 }
148 *crc32 = crc;
149
150 return true;
151}
152
153bool SDLTest_Crc32Done(SDLTest_Crc32Context *crcContext)
154{
155 if (!crcContext) {
156 return SDL_InvalidParamError("crcContext");
157 }
158
159 return true;
160}
diff --git a/contrib/SDL-3.2.8/src/test/SDL_test_font.c b/contrib/SDL-3.2.8/src/test/SDL_test_font.c
new file mode 100644
index 0000000..00268f6
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/test/SDL_test_font.c
@@ -0,0 +1,159 @@
1/*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
4
5 This software is provided 'as-is', without any express or implied
6 warranty. In no event will the authors be held liable for any damages
7 arising from the use of this software.
8
9 Permission is granted to anyone to use this software for any purpose,
10 including commercial applications, and to alter it and redistribute it
11 freely, subject to the following restrictions:
12
13 1. The origin of this software must not be misrepresented; you must not
14 claim that you wrote the original software. If you use this software
15 in a product, an acknowledgment in the product documentation would be
16 appreciated but is not required.
17 2. Altered source versions must be plainly marked as such, and must not be
18 misrepresented as being the original software.
19 3. This notice may not be removed or altered from any source distribution.
20*/
21#include <SDL3/SDL_test.h>
22
23#define UTF8_IsTrailingByte(c) ((c) >= 0x80 && (c) <= 0xBF)
24
25int FONT_CHARACTER_SIZE = SDL_DEBUG_TEXT_FONT_CHARACTER_SIZE;
26
27bool SDLTest_DrawCharacter(SDL_Renderer *renderer, float x, float y, Uint32 c)
28{
29 char str[5];
30 char *ptr = SDL_UCS4ToUTF8(c, str);
31 *ptr = '\0';
32 return SDL_RenderDebugText(renderer, x, y, str);
33}
34
35bool SDLTest_DrawString(SDL_Renderer *renderer, float x, float y, const char *s)
36{
37 return SDL_RenderDebugText(renderer, x, y, s);
38}
39
40SDLTest_TextWindow *SDLTest_TextWindowCreate(float x, float y, float w, float h)
41{
42 SDLTest_TextWindow *textwin = (SDLTest_TextWindow *)SDL_malloc(sizeof(*textwin));
43
44 if (!textwin) {
45 return NULL;
46 }
47
48 textwin->rect.x = x;
49 textwin->rect.y = y;
50 textwin->rect.w = w;
51 textwin->rect.h = h;
52 textwin->current = 0;
53 textwin->numlines = (int)SDL_ceilf(h / FONT_LINE_HEIGHT);
54 textwin->lines = (char **)SDL_calloc(textwin->numlines, sizeof(*textwin->lines));
55 if (!textwin->lines) {
56 SDL_free(textwin);
57 return NULL;
58 }
59 return textwin;
60}
61
62void SDLTest_TextWindowDisplay(SDLTest_TextWindow *textwin, SDL_Renderer *renderer)
63{
64 int i;
65 float y;
66
67 for (y = textwin->rect.y, i = 0; i < textwin->numlines; ++i, y += FONT_LINE_HEIGHT) {
68 if (textwin->lines[i]) {
69 SDLTest_DrawString(renderer, textwin->rect.x, y, textwin->lines[i]);
70 }
71 }
72}
73
74void SDLTest_TextWindowAddText(SDLTest_TextWindow *textwin, const char *fmt, ...)
75{
76 char text[1024];
77 va_list ap;
78
79 va_start(ap, fmt);
80 (void)SDL_vsnprintf(text, sizeof(text), fmt, ap);
81 va_end(ap);
82
83 SDLTest_TextWindowAddTextWithLength(textwin, text, SDL_strlen(text));
84}
85
86void SDLTest_TextWindowAddTextWithLength(SDLTest_TextWindow *textwin, const char *text, size_t len)
87{
88 size_t existing;
89 bool newline = false;
90 char *line;
91
92 if (len > 0 && text[len - 1] == '\n') {
93 --len;
94 newline = true;
95 }
96
97 if (textwin->lines[textwin->current]) {
98 existing = SDL_strlen(textwin->lines[textwin->current]);
99 } else {
100 existing = 0;
101 }
102
103 if (*text == '\b') {
104 if (existing) {
105 while (existing > 1 && UTF8_IsTrailingByte((Uint8)textwin->lines[textwin->current][existing - 1])) {
106 --existing;
107 }
108 --existing;
109 textwin->lines[textwin->current][existing] = '\0';
110 } else if (textwin->current > 0) {
111 SDL_free(textwin->lines[textwin->current]);
112 textwin->lines[textwin->current] = NULL;
113 --textwin->current;
114 }
115 return;
116 }
117
118 line = (char *)SDL_realloc(textwin->lines[textwin->current], existing + len + 1);
119 if (line) {
120 SDL_memcpy(&line[existing], text, len);
121 line[existing + len] = '\0';
122 textwin->lines[textwin->current] = line;
123 if (newline) {
124 if (textwin->current == textwin->numlines - 1) {
125 SDL_free(textwin->lines[0]);
126 SDL_memmove(&textwin->lines[0], &textwin->lines[1], (textwin->numlines - 1) * sizeof(textwin->lines[1]));
127 textwin->lines[textwin->current] = NULL;
128 } else {
129 ++textwin->current;
130 }
131 }
132 }
133}
134
135void SDLTest_TextWindowClear(SDLTest_TextWindow *textwin)
136{
137 int i;
138
139 for (i = 0; i < textwin->numlines; ++i) {
140 if (textwin->lines[i]) {
141 SDL_free(textwin->lines[i]);
142 textwin->lines[i] = NULL;
143 }
144 }
145 textwin->current = 0;
146}
147
148void SDLTest_TextWindowDestroy(SDLTest_TextWindow *textwin)
149{
150 if (textwin) {
151 SDLTest_TextWindowClear(textwin);
152 SDL_free(textwin->lines);
153 SDL_free(textwin);
154 }
155}
156
157void SDLTest_CleanupTextDrawing(void)
158{
159}
diff --git a/contrib/SDL-3.2.8/src/test/SDL_test_fuzzer.c b/contrib/SDL-3.2.8/src/test/SDL_test_fuzzer.c
new file mode 100644
index 0000000..67638b5
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/test/SDL_test_fuzzer.c
@@ -0,0 +1,494 @@
1/*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
4
5 This software is provided 'as-is', without any express or implied
6 warranty. In no event will the authors be held liable for any damages
7 arising from the use of this software.
8
9 Permission is granted to anyone to use this software for any purpose,
10 including commercial applications, and to alter it and redistribute it
11 freely, subject to the following restrictions:
12
13 1. The origin of this software must not be misrepresented; you must not
14 claim that you wrote the original software. If you use this software
15 in a product, an acknowledgment in the product documentation would be
16 appreciated but is not required.
17 2. Altered source versions must be plainly marked as such, and must not be
18 misrepresented as being the original software.
19 3. This notice may not be removed or altered from any source distribution.
20*/
21
22/*
23
24 Data generators for fuzzing test data in a reproducible way.
25
26*/
27#include <SDL3/SDL_test.h>
28
29#include <float.h> /* Needed for FLT_MAX and DBL_EPSILON */
30#include <limits.h> /* Needed for UCHAR_MAX, etc. */
31
32/**
33 * Counter for fuzzer invocations
34 */
35static int fuzzerInvocationCounter = 0;
36
37/**
38 * Context for shared random number generator
39 */
40static Uint64 rndContext;
41
42/*
43 * Note: doxygen documentation markup for functions is in the header file.
44 */
45
46void SDLTest_FuzzerInit(Uint64 execKey)
47{
48 rndContext = execKey;
49 fuzzerInvocationCounter = 0;
50}
51
52int SDLTest_GetFuzzerInvocationCount(void)
53{
54 return fuzzerInvocationCounter;
55}
56
57Uint8 SDLTest_RandomUint8(void)
58{
59 fuzzerInvocationCounter++;
60
61 return (Uint8)(SDL_rand_bits_r(&rndContext) >> 24);
62}
63
64Sint8 SDLTest_RandomSint8(void)
65{
66 fuzzerInvocationCounter++;
67
68 return (Sint8)(SDL_rand_bits_r(&rndContext) >> 24);
69}
70
71Uint16 SDLTest_RandomUint16(void)
72{
73 fuzzerInvocationCounter++;
74
75 return (Uint16)(SDL_rand_bits_r(&rndContext) >> 16);
76}
77
78Sint16 SDLTest_RandomSint16(void)
79{
80 fuzzerInvocationCounter++;
81
82 return (Sint16)(SDL_rand_bits_r(&rndContext) >> 16);
83}
84
85Uint32 SDLTest_RandomUint32(void)
86{
87 fuzzerInvocationCounter++;
88
89 return SDL_rand_bits_r(&rndContext);
90}
91
92Sint32 SDLTest_RandomSint32(void)
93{
94 fuzzerInvocationCounter++;
95
96 return (Sint32)SDL_rand_bits_r(&rndContext);
97}
98
99Uint64 SDLTest_RandomUint64(void)
100{
101 union
102 {
103 Uint64 v64;
104 Uint32 v32[2];
105 } value;
106
107 fuzzerInvocationCounter++;
108
109 value.v32[0] = SDLTest_RandomUint32();
110 value.v32[1] = SDLTest_RandomUint32();
111
112 return value.v64;
113}
114
115Sint64 SDLTest_RandomSint64(void)
116{
117 union
118 {
119 Uint64 v64;
120 Uint32 v32[2];
121 } value;
122
123 fuzzerInvocationCounter++;
124
125 value.v32[0] = SDLTest_RandomUint32();
126 value.v32[1] = SDLTest_RandomUint32();
127
128 return (Sint64)value.v64;
129}
130
131Sint32 SDLTest_RandomIntegerInRange(Sint32 min, Sint32 max)
132{
133 fuzzerInvocationCounter++;
134
135 if (min == max) {
136 return min;
137 }
138
139 if (min > max) {
140 Sint32 temp = min;
141 min = max;
142 max = temp;
143 }
144
145 Uint64 range = (Sint64)max - (Sint64)min;
146 if (range < SDL_MAX_SINT32) {
147 return min + SDL_rand_r(&rndContext, (Sint32) range + 1);
148 } else {
149 Uint64 add = SDL_rand_bits_r(&rndContext) | ((Uint64) SDL_rand_bits_r(&rndContext) << 32);
150 return (Sint32) (min + (Sint64) (add % (range + 1)));
151 }
152}
153
154/**
155 * Generates a unsigned boundary value between the given boundaries.
156 * Boundary values are inclusive. See the examples below.
157 * If boundary2 < boundary1, the values are swapped.
158 * If boundary1 == boundary2, value of boundary1 will be returned
159 *
160 * Generating boundary values for Uint8:
161 * BoundaryValues(UINT8_MAX, 10, 20, True) -> [10,11,19,20]
162 * BoundaryValues(UINT8_MAX, 10, 20, False) -> [9,21]
163 * BoundaryValues(UINT8_MAX, 0, 15, True) -> [0, 1, 14, 15]
164 * BoundaryValues(UINT8_MAX, 0, 15, False) -> [16]
165 * BoundaryValues(UINT8_MAX, 0, 0xFF, False) -> [0], error set
166 *
167 * Generator works the same for other types of unsigned integers.
168 *
169 * \param maxValue The biggest value that is acceptable for this data type.
170 * For instance, for Uint8 -> 255, Uint16 -> 65536 etc.
171 * \param boundary1 defines lower boundary
172 * \param boundary2 defines upper boundary
173 * \param validDomain Generate only for valid domain (for the data type)
174 *
175 * \returns Returns a random boundary value for the domain or 0 in case of error
176 */
177static Uint64 SDLTest_GenerateUnsignedBoundaryValues(const Uint64 maxValue, Uint64 boundary1, Uint64 boundary2, bool validDomain)
178{
179 Uint64 b1, b2;
180 Uint64 delta;
181 Uint64 tempBuf[4];
182 Uint8 index;
183
184 /* Maybe swap */
185 if (boundary1 > boundary2) {
186 b1 = boundary2;
187 b2 = boundary1;
188 } else {
189 b1 = boundary1;
190 b2 = boundary2;
191 }
192
193 index = 0;
194 if (validDomain == true) {
195 if (b1 == b2) {
196 return b1;
197 }
198
199 /* Generate up to 4 values within bounds */
200 delta = b2 - b1;
201 if (delta < 4) {
202 do {
203 tempBuf[index] = b1 + index;
204 index++;
205 } while (index < delta);
206 } else {
207 tempBuf[index] = b1;
208 index++;
209 tempBuf[index] = b1 + 1;
210 index++;
211 tempBuf[index] = b2 - 1;
212 index++;
213 tempBuf[index] = b2;
214 index++;
215 }
216 } else {
217 /* Generate up to 2 values outside of bounds */
218 if (b1 > 0) {
219 tempBuf[index] = b1 - 1;
220 index++;
221 }
222
223 if (b2 < maxValue) {
224 tempBuf[index] = b2 + 1;
225 index++;
226 }
227 }
228
229 if (index == 0) {
230 /* There are no valid boundaries */
231 SDL_Unsupported();
232 return 0;
233 }
234
235 return tempBuf[SDLTest_RandomUint8() % index];
236}
237
238Uint8 SDLTest_RandomUint8BoundaryValue(Uint8 boundary1, Uint8 boundary2, bool validDomain)
239{
240 /* max value for Uint8 */
241 const Uint64 maxValue = UCHAR_MAX;
242 return (Uint8)SDLTest_GenerateUnsignedBoundaryValues(maxValue,
243 (Uint64)boundary1, (Uint64)boundary2,
244 validDomain);
245}
246
247Uint16 SDLTest_RandomUint16BoundaryValue(Uint16 boundary1, Uint16 boundary2, bool validDomain)
248{
249 /* max value for Uint16 */
250 const Uint64 maxValue = USHRT_MAX;
251 return (Uint16)SDLTest_GenerateUnsignedBoundaryValues(maxValue,
252 (Uint64)boundary1, (Uint64)boundary2,
253 validDomain);
254}
255
256Uint32 SDLTest_RandomUint32BoundaryValue(Uint32 boundary1, Uint32 boundary2, bool validDomain)
257{
258/* max value for Uint32 */
259#if ((ULONG_MAX) == (UINT_MAX))
260 const Uint64 maxValue = ULONG_MAX;
261#else
262 const Uint64 maxValue = UINT_MAX;
263#endif
264 return (Uint32)SDLTest_GenerateUnsignedBoundaryValues(maxValue,
265 (Uint64)boundary1, (Uint64)boundary2,
266 validDomain);
267}
268
269Uint64 SDLTest_RandomUint64BoundaryValue(Uint64 boundary1, Uint64 boundary2, bool validDomain)
270{
271 /* max value for Uint64 */
272 const Uint64 maxValue = UINT64_MAX;
273 return SDLTest_GenerateUnsignedBoundaryValues(maxValue,
274 boundary1, boundary2,
275 validDomain);
276}
277
278/**
279 * Generates a signed boundary value between the given boundaries.
280 * Boundary values are inclusive. See the examples below.
281 * If boundary2 < boundary1, the values are swapped.
282 * If boundary1 == boundary2, value of boundary1 will be returned
283 *
284 * Generating boundary values for Sint8:
285 * SignedBoundaryValues(SCHAR_MIN, SCHAR_MAX, -10, 20, True) -> [-10,-9,19,20]
286 * SignedBoundaryValues(SCHAR_MIN, SCHAR_MAX, -10, 20, False) -> [-11,21]
287 * SignedBoundaryValues(SCHAR_MIN, SCHAR_MAX, -30, -15, True) -> [-30, -29, -16, -15]
288 * SignedBoundaryValues(SCHAR_MIN, SCHAR_MAX, -127, 15, False) -> [16]
289 * SignedBoundaryValues(SCHAR_MIN, SCHAR_MAX, -127, 127, False) -> [0], error set
290 *
291 * Generator works the same for other types of signed integers.
292 *
293 * \param minValue The smallest value that is acceptable for this data type.
294 * For instance, for Uint8 -> -127, etc.
295 * \param maxValue The biggest value that is acceptable for this data type.
296 * For instance, for Uint8 -> 127, etc.
297 * \param boundary1 defines lower boundary
298 * \param boundary2 defines upper boundary
299 * \param validDomain Generate only for valid domain (for the data type)
300 *
301 * \returns Returns a random boundary value for the domain or 0 in case of error
302 */
303static Sint64 SDLTest_GenerateSignedBoundaryValues(const Sint64 minValue, const Sint64 maxValue, Sint64 boundary1, Sint64 boundary2, bool validDomain)
304{
305 Sint64 b1, b2;
306 Sint64 delta;
307 Sint64 tempBuf[4];
308 Uint8 index;
309
310 /* Maybe swap */
311 if (boundary1 > boundary2) {
312 b1 = boundary2;
313 b2 = boundary1;
314 } else {
315 b1 = boundary1;
316 b2 = boundary2;
317 }
318
319 index = 0;
320 if (validDomain == true) {
321 if (b1 == b2) {
322 return b1;
323 }
324
325 /* Generate up to 4 values within bounds */
326 delta = b2 - b1;
327 if (delta < 4) {
328 do {
329 tempBuf[index] = b1 + index;
330 index++;
331 } while (index < delta);
332 } else {
333 tempBuf[index] = b1;
334 index++;
335 tempBuf[index] = b1 + 1;
336 index++;
337 tempBuf[index] = b2 - 1;
338 index++;
339 tempBuf[index] = b2;
340 index++;
341 }
342 } else {
343 /* Generate up to 2 values outside of bounds */
344 if (b1 > minValue) {
345 tempBuf[index] = b1 - 1;
346 index++;
347 }
348
349 if (b2 < maxValue) {
350 tempBuf[index] = b2 + 1;
351 index++;
352 }
353 }
354
355 if (index == 0) {
356 /* There are no valid boundaries */
357 SDL_Unsupported();
358 return minValue;
359 }
360
361 return tempBuf[SDLTest_RandomUint8() % index];
362}
363
364Sint8 SDLTest_RandomSint8BoundaryValue(Sint8 boundary1, Sint8 boundary2, bool validDomain)
365{
366 /* min & max values for Sint8 */
367 const Sint64 maxValue = SCHAR_MAX;
368 const Sint64 minValue = SCHAR_MIN;
369 return (Sint8)SDLTest_GenerateSignedBoundaryValues(minValue, maxValue,
370 (Sint64)boundary1, (Sint64)boundary2,
371 validDomain);
372}
373
374Sint16 SDLTest_RandomSint16BoundaryValue(Sint16 boundary1, Sint16 boundary2, bool validDomain)
375{
376 /* min & max values for Sint16 */
377 const Sint64 maxValue = SHRT_MAX;
378 const Sint64 minValue = SHRT_MIN;
379 return (Sint16)SDLTest_GenerateSignedBoundaryValues(minValue, maxValue,
380 (Sint64)boundary1, (Sint64)boundary2,
381 validDomain);
382}
383
384Sint32 SDLTest_RandomSint32BoundaryValue(Sint32 boundary1, Sint32 boundary2, bool validDomain)
385{
386/* min & max values for Sint32 */
387#if ((ULONG_MAX) == (UINT_MAX))
388 const Sint64 maxValue = LONG_MAX;
389 const Sint64 minValue = LONG_MIN;
390#else
391 const Sint64 maxValue = INT_MAX;
392 const Sint64 minValue = INT_MIN;
393#endif
394 return (Sint32)SDLTest_GenerateSignedBoundaryValues(minValue, maxValue,
395 (Sint64)boundary1, (Sint64)boundary2,
396 validDomain);
397}
398
399Sint64 SDLTest_RandomSint64BoundaryValue(Sint64 boundary1, Sint64 boundary2, bool validDomain)
400{
401 /* min & max values for Sint64 */
402 const Sint64 maxValue = INT64_MAX;
403 const Sint64 minValue = INT64_MIN;
404 return SDLTest_GenerateSignedBoundaryValues(minValue, maxValue,
405 boundary1, boundary2,
406 validDomain);
407}
408
409float SDLTest_RandomUnitFloat(void)
410{
411 return SDL_randf_r(&rndContext);
412}
413
414float SDLTest_RandomFloat(void)
415{
416 union
417 {
418 float f;
419 Uint32 v32;
420 } value;
421
422 do {
423 value.v32 = SDLTest_RandomUint32();
424 } while (SDL_isnanf(value.f) || SDL_isinff(value.f));
425
426 return value.f;
427}
428
429double SDLTest_RandomUnitDouble(void)
430{
431 return (double)(SDLTest_RandomUint64() >> (64-53)) * 0x1.0p-53;
432}
433
434double SDLTest_RandomDouble(void)
435{
436 union
437 {
438 double d;
439 Uint64 v64;
440 } value;
441
442 do {
443 value.v64 = SDLTest_RandomUint64();
444 } while (SDL_isnan(value.d) || SDL_isinf(value.d));
445
446 return value.d;
447}
448
449char *SDLTest_RandomAsciiString(void)
450{
451 return SDLTest_RandomAsciiStringWithMaximumLength(255);
452}
453
454char *SDLTest_RandomAsciiStringWithMaximumLength(int maxLength)
455{
456 int size;
457
458 if (maxLength < 1) {
459 SDL_InvalidParamError("maxLength");
460 return NULL;
461 }
462
463 size = (SDLTest_RandomUint32() % (maxLength + 1));
464 if (size == 0) {
465 size = 1;
466 }
467 return SDLTest_RandomAsciiStringOfSize(size);
468}
469
470char *SDLTest_RandomAsciiStringOfSize(int size)
471{
472 char *string;
473 int counter;
474
475 if (size < 1) {
476 SDL_InvalidParamError("size");
477 return NULL;
478 }
479
480 string = (char *)SDL_malloc((size + 1) * sizeof(char));
481 if (!string) {
482 return NULL;
483 }
484
485 for (counter = 0; counter < size; ++counter) {
486 string[counter] = (char)SDLTest_RandomIntegerInRange(32, 126);
487 }
488
489 string[counter] = '\0';
490
491 fuzzerInvocationCounter++;
492
493 return string;
494}
diff --git a/contrib/SDL-3.2.8/src/test/SDL_test_harness.c b/contrib/SDL-3.2.8/src/test/SDL_test_harness.c
new file mode 100644
index 0000000..d6a3b8d
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/test/SDL_test_harness.c
@@ -0,0 +1,864 @@
1/*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
4
5 This software is provided 'as-is', without any express or implied
6 warranty. In no event will the authors be held liable for any damages
7 arising from the use of this software.
8
9 Permission is granted to anyone to use this software for any purpose,
10 including commercial applications, and to alter it and redistribute it
11 freely, subject to the following restrictions:
12
13 1. The origin of this software must not be misrepresented; you must not
14 claim that you wrote the original software. If you use this software
15 in a product, an acknowledgment in the product documentation would be
16 appreciated but is not required.
17 2. Altered source versions must be plainly marked as such, and must not be
18 misrepresented as being the original software.
19 3. This notice may not be removed or altered from any source distribution.
20*/
21#include <SDL3/SDL_test.h>
22
23#include <stdlib.h> /* Needed for exit() */
24
25/* Enable to have color in logs */
26#if 1
27#define COLOR_RED "\033[0;31m"
28#define COLOR_GREEN "\033[0;32m"
29#define COLOR_YELLOW "\033[0;93m"
30#define COLOR_BLUE "\033[0;94m"
31#define COLOR_END "\033[0m"
32#else
33#define COLOR_RED ""
34#define COLOR_GREEN ""
35#define COLOR_BLUE ""
36#define COLOR_YELLOW ""
37#define COLOR_END ""
38#endif
39
40/* Invalid test name/description message format */
41#define SDLTEST_INVALID_NAME_FORMAT "(Invalid)"
42
43/* Log summary message format */
44#define SDLTEST_LOG_SUMMARY_FORMAT "%s Summary: Total=%d " COLOR_GREEN "Passed=%d" COLOR_END " " COLOR_RED "Failed=%d" COLOR_END " " COLOR_BLUE "Skipped=%d" COLOR_END
45#define SDLTEST_LOG_SUMMARY_FORMAT_OK "%s Summary: Total=%d " COLOR_GREEN "Passed=%d" COLOR_END " " COLOR_GREEN "Failed=%d" COLOR_END " " COLOR_BLUE "Skipped=%d" COLOR_END
46
47/* Final result message format */
48#define SDLTEST_FINAL_RESULT_FORMAT COLOR_YELLOW ">>> %s '%s':" COLOR_END " %s\n"
49
50struct SDLTest_TestSuiteRunner {
51 struct
52 {
53 SDLTest_TestSuiteReference **testSuites;
54 char *runSeed;
55 Uint64 execKey;
56 char *filter;
57 int testIterations;
58 bool randomOrder;
59 } user;
60
61 SDLTest_ArgumentParser argparser;
62};
63
64/* ! Timeout for single test case execution */
65static Uint32 SDLTest_TestCaseTimeout = 3600;
66
67static const char *common_harness_usage[] = {
68 "[--iterations #]",
69 "[--execKey #]",
70 "[--seed string]",
71 "[--filter suite_name|test_name]",
72 "[--random-order]",
73 NULL
74};
75
76char *SDLTest_GenerateRunSeed(char *buffer, int length)
77{
78 Uint64 randomContext = SDL_GetPerformanceCounter();
79 int counter;
80
81 if (!buffer) {
82 SDLTest_LogError("Input buffer must not be NULL.");
83 return NULL;
84 }
85
86 /* Sanity check input */
87 if (length <= 0) {
88 SDLTest_LogError("The length of the harness seed must be >0.");
89 return NULL;
90 }
91
92 /* Generate a random string of alphanumeric characters */
93 for (counter = 0; counter < length; counter++) {
94 char ch;
95 int v = SDL_rand_r(&randomContext, 10 + 26);
96 if (v < 10) {
97 ch = (char)('0' + v);
98 } else {
99 ch = (char)('A' + v - 10);
100 }
101 buffer[counter] = ch;
102 }
103 buffer[length] = '\0';
104
105 return buffer;
106}
107
108/**
109 * Generates an execution key for the fuzzer.
110 *
111 * \param runSeed The run seed to use
112 * \param suiteName The name of the test suite
113 * \param testName The name of the test
114 * \param iteration The iteration count
115 *
116 * \returns The generated execution key to initialize the fuzzer with.
117 *
118 */
119static Uint64 SDLTest_GenerateExecKey(const char *runSeed, const char *suiteName, const char *testName, int iteration)
120{
121 SDLTest_Md5Context md5Context;
122 Uint64 *keys;
123 char iterationString[16];
124 size_t runSeedLength;
125 size_t suiteNameLength;
126 size_t testNameLength;
127 size_t iterationStringLength;
128 size_t entireStringLength;
129 char *buffer;
130
131 if (!runSeed || runSeed[0] == '\0') {
132 SDLTest_LogError("Invalid runSeed string.");
133 return 0;
134 }
135
136 if (!suiteName || suiteName[0] == '\0') {
137 SDLTest_LogError("Invalid suiteName string.");
138 return 0;
139 }
140
141 if (!testName || testName[0] == '\0') {
142 SDLTest_LogError("Invalid testName string.");
143 return 0;
144 }
145
146 if (iteration <= 0) {
147 SDLTest_LogError("Invalid iteration count.");
148 return 0;
149 }
150
151 /* Convert iteration number into a string */
152 SDL_memset(iterationString, 0, sizeof(iterationString));
153 (void)SDL_snprintf(iterationString, sizeof(iterationString) - 1, "%d", iteration);
154
155 /* Combine the parameters into single string */
156 runSeedLength = SDL_strlen(runSeed);
157 suiteNameLength = SDL_strlen(suiteName);
158 testNameLength = SDL_strlen(testName);
159 iterationStringLength = SDL_strlen(iterationString);
160 entireStringLength = runSeedLength + suiteNameLength + testNameLength + iterationStringLength + 1;
161 buffer = (char *)SDL_malloc(entireStringLength);
162 if (!buffer) {
163 SDLTest_LogError("Failed to allocate buffer for execKey generation.");
164 return 0;
165 }
166 (void)SDL_snprintf(buffer, entireStringLength, "%s%s%s%d", runSeed, suiteName, testName, iteration);
167
168 /* Hash string and use half of the digest as 64bit exec key */
169 SDLTest_Md5Init(&md5Context);
170 SDLTest_Md5Update(&md5Context, (unsigned char *)buffer, (unsigned int)entireStringLength);
171 SDLTest_Md5Final(&md5Context);
172 SDL_free(buffer);
173 keys = (Uint64 *)md5Context.digest;
174
175 return keys[0];
176}
177
178/**
179 * Set timeout handler for test.
180 *
181 * \param timeout Timeout interval in seconds.
182 * \param callback Function that will be called after timeout has elapsed.
183 *
184 * \return Timer id or -1 on failure.
185 */
186static SDL_TimerID SDLTest_SetTestTimeout(int timeout, SDL_TimerCallback callback)
187{
188 Uint32 timeoutInMilliseconds;
189 SDL_TimerID timerID;
190
191 if (!callback) {
192 SDLTest_LogError("Timeout callback can't be NULL");
193 return 0;
194 }
195
196 if (timeout < 0) {
197 SDLTest_LogError("Timeout value must be bigger than zero.");
198 return 0;
199 }
200
201 /* Set timer */
202 timeoutInMilliseconds = timeout * 1000;
203 timerID = SDL_AddTimer(timeoutInMilliseconds, callback, 0x0);
204 if (timerID == 0) {
205 SDLTest_LogError("Creation of SDL timer failed: %s", SDL_GetError());
206 return 0;
207 }
208
209 return timerID;
210}
211
212/**
213 * Timeout handler. Aborts test run and exits harness process.
214 */
215static Uint32 SDLCALL SDLTest_BailOut(void *userdata, SDL_TimerID timerID, Uint32 interval)
216{
217 SDLTest_LogError("TestCaseTimeout timer expired. Aborting test run.");
218 exit(TEST_ABORTED); /* bail out from the test */
219 return 0;
220}
221
222/**
223 * Execute a test using the given execution key.
224 *
225 * \param testSuite Suite containing the test case.
226 * \param testCase Case to execute.
227 * \param execKey Execution key for the fuzzer.
228 * \param forceTestRun Force test to run even if test was disabled in suite.
229 *
230 * \returns Test case result.
231 */
232static int SDLTest_RunTest(SDLTest_TestSuiteReference *testSuite, const SDLTest_TestCaseReference *testCase, Uint64 execKey, bool forceTestRun)
233{
234 SDL_TimerID timer = 0;
235 int testCaseResult = 0;
236 int testResult = 0;
237 int fuzzerCount;
238 void *data = NULL;
239
240 if (!testSuite || !testCase || !testSuite->name || !testCase->name) {
241 SDLTest_LogError("Setup failure: testSuite or testCase references NULL");
242 return TEST_RESULT_SETUP_FAILURE;
243 }
244
245 if (!testCase->enabled && forceTestRun == false) {
246 SDLTest_Log(SDLTEST_FINAL_RESULT_FORMAT, "Test", testCase->name, "Skipped (Disabled)");
247 return TEST_RESULT_SKIPPED;
248 }
249
250 /* Initialize fuzzer */
251 SDLTest_FuzzerInit(execKey);
252
253 /* Reset assert tracker */
254 SDLTest_ResetAssertSummary();
255
256 /* Set timeout timer */
257 timer = SDLTest_SetTestTimeout(SDLTest_TestCaseTimeout, SDLTest_BailOut);
258
259 /* Maybe run suite initializer function */
260 if (testSuite->testSetUp) {
261 testSuite->testSetUp(&data);
262 if (SDLTest_AssertSummaryToTestResult() == TEST_RESULT_FAILED) {
263 SDLTest_LogError(SDLTEST_FINAL_RESULT_FORMAT, "Suite Setup", testSuite->name, COLOR_RED "Failed" COLOR_END);
264 return TEST_RESULT_SETUP_FAILURE;
265 }
266 }
267
268 /* Run test case function */
269 testCaseResult = testCase->testCase(data);
270
271 /* Convert test execution result into harness result */
272 if (testCaseResult == TEST_SKIPPED) {
273 /* Test was programmatically skipped */
274 testResult = TEST_RESULT_SKIPPED;
275 } else if (testCaseResult == TEST_STARTED) {
276 /* Test did not return a TEST_COMPLETED value; assume it failed */
277 testResult = TEST_RESULT_FAILED;
278 } else if (testCaseResult == TEST_ABORTED) {
279 /* Test was aborted early; assume it failed */
280 testResult = TEST_RESULT_FAILED;
281 } else {
282 /* Perform failure analysis based on asserts */
283 testResult = SDLTest_AssertSummaryToTestResult();
284 }
285
286 /* Maybe run suite cleanup function (ignore failed asserts) */
287 if (testSuite->testTearDown) {
288 testSuite->testTearDown(data);
289 }
290
291 /* Cancel timeout timer */
292 if (timer) {
293 SDL_RemoveTimer(timer);
294 }
295
296 /* Report on asserts and fuzzer usage */
297 fuzzerCount = SDLTest_GetFuzzerInvocationCount();
298 if (fuzzerCount > 0) {
299 SDLTest_Log("Fuzzer invocations: %d", fuzzerCount);
300 }
301
302 /* Final log based on test execution result */
303 if (testCaseResult == TEST_SKIPPED) {
304 /* Test was programmatically skipped */
305 SDLTest_Log(SDLTEST_FINAL_RESULT_FORMAT, "Test", testCase->name, COLOR_BLUE "Skipped (Programmatically)" COLOR_END);
306 } else if (testCaseResult == TEST_STARTED) {
307 /* Test did not return a TEST_COMPLETED value; assume it failed */
308 SDLTest_LogError(SDLTEST_FINAL_RESULT_FORMAT, "Test", testCase->name, COLOR_RED "Failed (test started, but did not return TEST_COMPLETED)" COLOR_END);
309 } else if (testCaseResult == TEST_ABORTED) {
310 /* Test was aborted early; assume it failed */
311 SDLTest_LogError(SDLTEST_FINAL_RESULT_FORMAT, "Test", testCase->name, COLOR_RED "Failed (Aborted)" COLOR_END);
312 } else {
313 SDLTest_LogAssertSummary();
314 }
315
316 return testResult;
317}
318
319/* Prints summary of all suites/tests contained in the given reference */
320#if 0
321static void SDLTest_LogTestSuiteSummary(SDLTest_TestSuiteReference *testSuites)
322{
323 int suiteCounter;
324 int testCounter;
325 SDLTest_TestSuiteReference *testSuite;
326 SDLTest_TestCaseReference *testCase;
327
328 /* Loop over all suites */
329 suiteCounter = 0;
330 while (&testSuites[suiteCounter]) {
331 testSuite=&testSuites[suiteCounter];
332 suiteCounter++;
333 SDLTest_Log("Test Suite %i - %s\n", suiteCounter,
334 (testSuite->name) ? testSuite->name : SDLTEST_INVALID_NAME_FORMAT);
335
336 /* Loop over all test cases */
337 testCounter = 0;
338 while (testSuite->testCases[testCounter]) {
339 testCase=(SDLTest_TestCaseReference *)testSuite->testCases[testCounter];
340 testCounter++;
341 SDLTest_Log(" Test Case %i - %s: %s", testCounter,
342 (testCase->name) ? testCase->name : SDLTEST_INVALID_NAME_FORMAT,
343 (testCase->description) ? testCase->description : SDLTEST_INVALID_NAME_FORMAT);
344 }
345 }
346}
347#endif
348
349/* Gets a timer value in seconds */
350static float GetClock(void)
351{
352 float currentClock = SDL_GetPerformanceCounter() / (float)SDL_GetPerformanceFrequency();
353 return currentClock;
354}
355
356/**
357 * Execute a test suite using the given run seed and execution key.
358 *
359 * The filter string is matched to the suite name (full comparison) to select a single suite,
360 * or if no suite matches, it is matched to the test names (full comparison) to select a single test.
361 *
362 * \param runner The runner to execute.
363 *
364 * \returns Test run result; 0 when all tests passed, 1 if any tests failed.
365 */
366int SDLTest_ExecuteTestSuiteRunner(SDLTest_TestSuiteRunner *runner)
367{
368 int totalNumberOfTests = 0;
369 int failedNumberOfTests = 0;
370 int suiteCounter;
371 int testCounter;
372 int iterationCounter;
373 SDLTest_TestSuiteReference *testSuite;
374 const SDLTest_TestCaseReference *testCase;
375 const char *runSeed = NULL;
376 const char *currentSuiteName;
377 const char *currentTestName;
378 Uint64 execKey;
379 float runStartSeconds;
380 float suiteStartSeconds;
381 float testStartSeconds;
382 float runEndSeconds;
383 float suiteEndSeconds;
384 float testEndSeconds;
385 float runtime;
386 int suiteFilter = 0;
387 const char *suiteFilterName = NULL;
388 int testFilter = 0;
389 const char *testFilterName = NULL;
390 bool forceTestRun = false;
391 int testResult = 0;
392 int runResult = 0;
393 int totalTestFailedCount = 0;
394 int totalTestPassedCount = 0;
395 int totalTestSkippedCount = 0;
396 int testFailedCount = 0;
397 int testPassedCount = 0;
398 int testSkippedCount = 0;
399 int countSum = 0;
400 const SDLTest_TestCaseReference **failedTests;
401 char generatedSeed[16 + 1];
402 int nbSuites = 0;
403 int i = 0;
404 int *arraySuites = NULL;
405
406 /* Sanitize test iterations */
407 if (runner->user.testIterations < 1) {
408 runner->user.testIterations = 1;
409 }
410
411 /* Generate run see if we don't have one already */
412 if (!runner->user.runSeed || runner->user.runSeed[0] == '\0') {
413 runSeed = SDLTest_GenerateRunSeed(generatedSeed, 16);
414 if (!runSeed) {
415 SDLTest_LogError("Generating a random seed failed");
416 return 2;
417 }
418 } else {
419 runSeed = runner->user.runSeed;
420 }
421
422 /* Reset per-run counters */
423 totalTestFailedCount = 0;
424 totalTestPassedCount = 0;
425 totalTestSkippedCount = 0;
426
427 /* Take time - run start */
428 runStartSeconds = GetClock();
429
430 /* Log run with fuzzer parameters */
431 SDLTest_Log("::::: Test Run /w seed '%s' started\n", runSeed);
432
433 /* Count the total number of tests */
434 suiteCounter = 0;
435 while (runner->user.testSuites[suiteCounter]) {
436 testSuite = runner->user.testSuites[suiteCounter];
437 suiteCounter++;
438 testCounter = 0;
439 while (testSuite->testCases[testCounter]) {
440 testCounter++;
441 totalNumberOfTests++;
442 }
443 }
444
445 if (totalNumberOfTests == 0) {
446 SDLTest_LogError("No tests to run?");
447 return -1;
448 }
449
450 /* Pre-allocate an array for tracking failed tests (potentially all test cases) */
451 failedTests = (const SDLTest_TestCaseReference **)SDL_malloc(totalNumberOfTests * sizeof(SDLTest_TestCaseReference *));
452 if (!failedTests) {
453 SDLTest_LogError("Unable to allocate cache for failed tests");
454 return -1;
455 }
456
457 /* Initialize filtering */
458 if (runner->user.filter && runner->user.filter[0] != '\0') {
459 /* Loop over all suites to check if we have a filter match */
460 suiteCounter = 0;
461 while (runner->user.testSuites[suiteCounter] && suiteFilter == 0) {
462 testSuite = runner->user.testSuites[suiteCounter];
463 suiteCounter++;
464 if (testSuite->name && SDL_strcasecmp(runner->user.filter, testSuite->name) == 0) {
465 /* Matched a suite name */
466 suiteFilter = 1;
467 suiteFilterName = testSuite->name;
468 SDLTest_Log("Filtering: running only suite '%s'", suiteFilterName);
469 break;
470 }
471
472 /* Within each suite, loop over all test cases to check if we have a filter match */
473 testCounter = 0;
474 while (testSuite->testCases[testCounter] && testFilter == 0) {
475 testCase = testSuite->testCases[testCounter];
476 testCounter++;
477 if (testCase->name && SDL_strcasecmp(runner->user.filter, testCase->name) == 0) {
478 /* Matched a test name */
479 suiteFilter = 1;
480 suiteFilterName = testSuite->name;
481 testFilter = 1;
482 testFilterName = testCase->name;
483 SDLTest_Log("Filtering: running only test '%s' in suite '%s'", testFilterName, suiteFilterName);
484 break;
485 }
486 }
487 }
488
489 if (suiteFilter == 0 && testFilter == 0) {
490 SDLTest_LogError("Filter '%s' did not match any test suite/case.", runner->user.filter);
491 for (suiteCounter = 0; runner->user.testSuites[suiteCounter]; ++suiteCounter) {
492 testSuite = runner->user.testSuites[suiteCounter];
493 if (testSuite->name) {
494 SDLTest_Log("Test suite: %s", testSuite->name);
495 }
496
497 /* Within each suite, loop over all test cases to check if we have a filter match */
498 for (testCounter = 0; testSuite->testCases[testCounter]; ++testCounter) {
499 testCase = testSuite->testCases[testCounter];
500 SDLTest_Log(" test: %s%s", testCase->name, testCase->enabled ? "" : " (disabled)");
501 }
502 }
503 SDLTest_Log("Exit code: 2");
504 SDL_free((void *)failedTests);
505 return 2;
506 }
507
508 runner->user.randomOrder = false;
509 }
510
511 /* Number of test suites */
512 while (runner->user.testSuites[nbSuites]) {
513 nbSuites++;
514 }
515
516 arraySuites = SDL_malloc(nbSuites * sizeof(int));
517 if (!arraySuites) {
518 SDL_free((void *)failedTests);
519 return SDL_OutOfMemory();
520 }
521 for (i = 0; i < nbSuites; i++) {
522 arraySuites[i] = i;
523 }
524
525 /* Mix the list of suites to run them in random order */
526 {
527 /* Exclude last test "subsystemsTestSuite" which is said to interfere with other tests */
528 nbSuites--;
529
530 if (runner->user.execKey != 0) {
531 execKey = runner->user.execKey;
532 } else {
533 /* dummy values to have random numbers working */
534 execKey = SDLTest_GenerateExecKey(runSeed, "random testSuites", "initialisation", 1);
535 }
536
537 /* Initialize fuzzer */
538 SDLTest_FuzzerInit(execKey);
539
540 i = 100;
541 while (i--) {
542 int a, b;
543 int tmp;
544 a = SDLTest_RandomIntegerInRange(0, nbSuites - 1);
545 b = SDLTest_RandomIntegerInRange(0, nbSuites - 1);
546 /*
547 * NB: prevent swapping here to make sure the tests start with the same
548 * random seed (whether they are run in order or not).
549 * So we consume same number of SDLTest_RandomIntegerInRange() in all cases.
550 *
551 * If some random value were used at initialization before the tests start, the --seed wouldn't do the same with or without randomOrder.
552 */
553 /* Swap */
554 if (runner->user.randomOrder) {
555 tmp = arraySuites[b];
556 arraySuites[b] = arraySuites[a];
557 arraySuites[a] = tmp;
558 }
559 }
560
561 /* re-add last lest */
562 nbSuites++;
563 }
564
565 /* Loop over all suites */
566 for (i = 0; i < nbSuites; i++) {
567 suiteCounter = arraySuites[i];
568 testSuite = runner->user.testSuites[suiteCounter];
569 currentSuiteName = (testSuite->name ? testSuite->name : SDLTEST_INVALID_NAME_FORMAT);
570 suiteCounter++;
571
572 /* Filter suite if flag set and we have a name */
573 if (suiteFilter == 1 && suiteFilterName && testSuite->name &&
574 SDL_strcasecmp(suiteFilterName, testSuite->name) != 0) {
575 /* Skip suite */
576 SDLTest_Log("===== Test Suite %i: '%s' " COLOR_BLUE "skipped" COLOR_END "\n",
577 suiteCounter,
578 currentSuiteName);
579 } else {
580
581 int nbTestCases = 0;
582 int *arrayTestCases;
583 int j;
584 while (testSuite->testCases[nbTestCases]) {
585 nbTestCases++;
586 }
587
588 arrayTestCases = SDL_malloc(nbTestCases * sizeof(int));
589 if (!arrayTestCases) {
590 SDL_free(arraySuites);
591 SDL_free((void *)failedTests);
592 return SDL_OutOfMemory();
593 }
594 for (j = 0; j < nbTestCases; j++) {
595 arrayTestCases[j] = j;
596 }
597
598 /* Mix the list of testCases to run them in random order */
599 j = 100;
600 while (j--) {
601 int a, b;
602 int tmp;
603 a = SDLTest_RandomIntegerInRange(0, nbTestCases - 1);
604 b = SDLTest_RandomIntegerInRange(0, nbTestCases - 1);
605 /* Swap */
606 /* See previous note */
607 if (runner->user.randomOrder) {
608 tmp = arrayTestCases[b];
609 arrayTestCases[b] = arrayTestCases[a];
610 arrayTestCases[a] = tmp;
611 }
612 }
613
614 /* Reset per-suite counters */
615 testFailedCount = 0;
616 testPassedCount = 0;
617 testSkippedCount = 0;
618
619 /* Take time - suite start */
620 suiteStartSeconds = GetClock();
621
622 /* Log suite started */
623 SDLTest_Log("===== Test Suite %i: '%s' started\n",
624 suiteCounter,
625 currentSuiteName);
626
627 /* Loop over all test cases */
628 for (j = 0; j < nbTestCases; j++) {
629 testCounter = arrayTestCases[j];
630 testCase = testSuite->testCases[testCounter];
631 currentTestName = (testCase->name ? testCase->name : SDLTEST_INVALID_NAME_FORMAT);
632 testCounter++;
633
634 /* Filter tests if flag set and we have a name */
635 if (testFilter == 1 && testFilterName && testCase->name &&
636 SDL_strcasecmp(testFilterName, testCase->name) != 0) {
637 /* Skip test */
638 SDLTest_Log("===== Test Case %i.%i: '%s' " COLOR_BLUE "skipped" COLOR_END "\n",
639 suiteCounter,
640 testCounter,
641 currentTestName);
642 } else {
643 /* Override 'disabled' flag if we specified a test filter (i.e. force run for debugging) */
644 if (testFilter == 1 && !testCase->enabled) {
645 SDLTest_Log("Force run of disabled test since test filter was set");
646 forceTestRun = true;
647 }
648
649 /* Take time - test start */
650 testStartSeconds = GetClock();
651
652 /* Log test started */
653 SDLTest_Log(COLOR_YELLOW "----- Test Case %i.%i: '%s' started" COLOR_END,
654 suiteCounter,
655 testCounter,
656 currentTestName);
657 if (testCase->description && testCase->description[0] != '\0') {
658 SDLTest_Log("Test Description: '%s'",
659 (testCase->description) ? testCase->description : SDLTEST_INVALID_NAME_FORMAT);
660 }
661
662 /* Loop over all iterations */
663 iterationCounter = 0;
664 while (iterationCounter < runner->user.testIterations) {
665 iterationCounter++;
666
667 if (runner->user.execKey != 0) {
668 execKey = runner->user.execKey;
669 } else {
670 execKey = SDLTest_GenerateExecKey(runSeed, testSuite->name, testCase->name, iterationCounter);
671 }
672
673 SDLTest_Log("Test Iteration %i: execKey %" SDL_PRIu64, iterationCounter, execKey);
674 testResult = SDLTest_RunTest(testSuite, testCase, execKey, forceTestRun);
675
676 if (testResult == TEST_RESULT_PASSED) {
677 testPassedCount++;
678 totalTestPassedCount++;
679 } else if (testResult == TEST_RESULT_SKIPPED) {
680 testSkippedCount++;
681 totalTestSkippedCount++;
682 } else {
683 testFailedCount++;
684 totalTestFailedCount++;
685 }
686 }
687
688 /* Take time - test end */
689 testEndSeconds = GetClock();
690 runtime = testEndSeconds - testStartSeconds;
691 if (runtime < 0.0f) {
692 runtime = 0.0f;
693 }
694
695 if (runner->user.testIterations > 1) {
696 /* Log test runtime */
697 SDLTest_Log("Runtime of %i iterations: %.1f sec", runner->user.testIterations, runtime);
698 SDLTest_Log("Average Test runtime: %.5f sec", runtime / (float)runner->user.testIterations);
699 } else {
700 /* Log test runtime */
701 SDLTest_Log("Total Test runtime: %.1f sec", runtime);
702 }
703
704 /* Log final test result */
705 switch (testResult) {
706 case TEST_RESULT_PASSED:
707 SDLTest_Log(SDLTEST_FINAL_RESULT_FORMAT, "Test", currentTestName, COLOR_GREEN "Passed" COLOR_END);
708 break;
709 case TEST_RESULT_FAILED:
710 SDLTest_LogError(SDLTEST_FINAL_RESULT_FORMAT, "Test", currentTestName, COLOR_RED "Failed" COLOR_END);
711 break;
712 case TEST_RESULT_NO_ASSERT:
713 SDLTest_LogError(SDLTEST_FINAL_RESULT_FORMAT, "Test", currentTestName, COLOR_BLUE "No Asserts" COLOR_END);
714 break;
715 }
716
717 /* Collect failed test case references for repro-step display */
718 if (testResult == TEST_RESULT_FAILED) {
719 failedTests[failedNumberOfTests] = testCase;
720 failedNumberOfTests++;
721 }
722 }
723 }
724
725 /* Take time - suite end */
726 suiteEndSeconds = GetClock();
727 runtime = suiteEndSeconds - suiteStartSeconds;
728 if (runtime < 0.0f) {
729 runtime = 0.0f;
730 }
731
732 /* Log suite runtime */
733 SDLTest_Log("Total Suite runtime: %.1f sec", runtime);
734
735 /* Log summary and final Suite result */
736 countSum = testPassedCount + testFailedCount + testSkippedCount;
737 if (testFailedCount == 0) {
738 SDLTest_Log(SDLTEST_LOG_SUMMARY_FORMAT_OK, "Suite", countSum, testPassedCount, testFailedCount, testSkippedCount);
739 SDLTest_Log(SDLTEST_FINAL_RESULT_FORMAT, "Suite", currentSuiteName, COLOR_GREEN "Passed" COLOR_END);
740 } else {
741 SDLTest_LogError(SDLTEST_LOG_SUMMARY_FORMAT, "Suite", countSum, testPassedCount, testFailedCount, testSkippedCount);
742 SDLTest_LogError(SDLTEST_FINAL_RESULT_FORMAT, "Suite", currentSuiteName, COLOR_RED "Failed" COLOR_END);
743 }
744
745 SDL_free(arrayTestCases);
746 }
747 }
748
749 SDL_free(arraySuites);
750
751 /* Take time - run end */
752 runEndSeconds = GetClock();
753 runtime = runEndSeconds - runStartSeconds;
754 if (runtime < 0.0f) {
755 runtime = 0.0f;
756 }
757
758 /* Log total runtime */
759 SDLTest_Log("Total Run runtime: %.1f sec", runtime);
760
761 /* Log summary and final run result */
762 countSum = totalTestPassedCount + totalTestFailedCount + totalTestSkippedCount;
763 if (totalTestFailedCount == 0) {
764 runResult = 0;
765 SDLTest_Log(SDLTEST_LOG_SUMMARY_FORMAT_OK, "Run", countSum, totalTestPassedCount, totalTestFailedCount, totalTestSkippedCount);
766 SDLTest_Log(SDLTEST_FINAL_RESULT_FORMAT, "Run /w seed", runSeed, COLOR_GREEN "Passed" COLOR_END);
767 } else {
768 runResult = 1;
769 SDLTest_LogError(SDLTEST_LOG_SUMMARY_FORMAT, "Run", countSum, totalTestPassedCount, totalTestFailedCount, totalTestSkippedCount);
770 SDLTest_LogError(SDLTEST_FINAL_RESULT_FORMAT, "Run /w seed", runSeed, COLOR_RED "Failed" COLOR_END);
771 }
772
773 /* Print repro steps for failed tests */
774 if (failedNumberOfTests > 0) {
775 SDLTest_Log("Harness input to repro failures:");
776 for (testCounter = 0; testCounter < failedNumberOfTests; testCounter++) {
777 SDLTest_Log(COLOR_RED " --seed %s --filter %s" COLOR_END, runSeed, failedTests[testCounter]->name);
778 }
779 }
780 SDL_free((void *)failedTests);
781
782 SDLTest_Log("Exit code: %d", runResult);
783 return runResult;
784}
785
786static int SDLCALL SDLTest_TestSuiteCommonArg(void *data, char **argv, int index)
787{
788 SDLTest_TestSuiteRunner *runner = data;
789
790 if (SDL_strcasecmp(argv[index], "--iterations") == 0) {
791 if (argv[index + 1]) {
792 runner->user.testIterations = SDL_atoi(argv[index + 1]);
793 if (runner->user.testIterations < 1) {
794 runner->user.testIterations = 1;
795 }
796 return 2;
797 }
798 }
799 else if (SDL_strcasecmp(argv[index], "--execKey") == 0) {
800 if (argv[index + 1]) {
801 (void)SDL_sscanf(argv[index + 1], "%" SDL_PRIu64, &runner->user.execKey);
802 return 2;
803 }
804 }
805 else if (SDL_strcasecmp(argv[index], "--seed") == 0) {
806 if (argv[index + 1]) {
807 runner->user.runSeed = SDL_strdup(argv[index + 1]);
808 return 2;
809 }
810 }
811 else if (SDL_strcasecmp(argv[index], "--filter") == 0) {
812 if (argv[index + 1]) {
813 runner->user.filter = SDL_strdup(argv[index + 1]);
814 return 2;
815 }
816 }
817 else if (SDL_strcasecmp(argv[index], "--random-order") == 0) {
818 runner->user.randomOrder = true;
819 return 1;
820 }
821 return 0;
822}
823
824SDLTest_TestSuiteRunner *SDLTest_CreateTestSuiteRunner(SDLTest_CommonState *state, SDLTest_TestSuiteReference *testSuites[])
825{
826 SDLTest_TestSuiteRunner *runner;
827 SDLTest_ArgumentParser *argparser;
828
829 if (!state) {
830 SDLTest_LogError("SDL Test Suites require a common state");
831 return NULL;
832 }
833
834 runner = SDL_calloc(1, sizeof(SDLTest_TestSuiteRunner));
835 if (!runner) {
836 SDLTest_LogError("Failed to allocate memory for test suite runner");
837 return NULL;
838 }
839 runner->user.testSuites = testSuites;
840
841 runner->argparser.parse_arguments = SDLTest_TestSuiteCommonArg;
842 runner->argparser.usage = common_harness_usage;
843 runner->argparser.data = runner;
844
845 /* Find last argument description and append our description */
846 argparser = state->argparser;
847 for (;;) {
848 if (argparser->next == NULL) {
849 argparser->next = &runner->argparser;
850 break;
851 }
852 argparser = argparser->next;
853
854 }
855
856 return runner;
857}
858
859void SDLTest_DestroyTestSuiteRunner(SDLTest_TestSuiteRunner *runner) {
860
861 SDL_free(runner->user.filter);
862 SDL_free(runner->user.runSeed);
863 SDL_free(runner);
864}
diff --git a/contrib/SDL-3.2.8/src/test/SDL_test_log.c b/contrib/SDL-3.2.8/src/test/SDL_test_log.c
new file mode 100644
index 0000000..95bc2a1
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/test/SDL_test_log.c
@@ -0,0 +1,211 @@
1/*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
4
5 This software is provided 'as-is', without any express or implied
6 warranty. In no event will the authors be held liable for any damages
7 arising from the use of this software.
8
9 Permission is granted to anyone to use this software for any purpose,
10 including commercial applications, and to alter it and redistribute it
11 freely, subject to the following restrictions:
12
13 1. The origin of this software must not be misrepresented; you must not
14 claim that you wrote the original software. If you use this software
15 in a product, an acknowledgment in the product documentation would be
16 appreciated but is not required.
17 2. Altered source versions must be plainly marked as such, and must not be
18 misrepresented as being the original software.
19 3. This notice may not be removed or altered from any source distribution.
20*/
21
22/*
23
24 Used by the test framework and test cases.
25
26*/
27
28/* quiet windows compiler warnings */
29#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)
30#define _CRT_SECURE_NO_WARNINGS
31#endif
32#include <SDL3/SDL_test.h>
33
34#include <time.h> /* Needed for localtime() */
35
36/* work around compiler warning on older GCCs. */
37#if (defined(__GNUC__) && (__GNUC__ <= 2))
38static size_t strftime_gcc2_workaround(char *s, size_t max, const char *fmt, const struct tm *tm)
39{
40 return strftime(s, max, fmt, tm);
41}
42#ifdef strftime
43#undef strftime
44#endif
45#define strftime strftime_gcc2_workaround
46#endif
47
48/**
49 * Converts unix timestamp to its ascii representation in localtime
50 *
51 * Note: Uses a static buffer internally, so the return value
52 * isn't valid after the next call of this function. If you
53 * want to retain the return value, make a copy of it.
54 *
55 * \param timestamp A Timestamp, i.e. time(0)
56 *
57 * \return Ascii representation of the timestamp in localtime in the format '08/23/01 14:55:02'
58 */
59static const char *SDLTest_TimestampToString(const time_t timestamp)
60{
61 time_t copy;
62 static char buffer[64];
63 struct tm *local;
64 size_t result = 0;
65
66 SDL_memset(buffer, 0, sizeof(buffer));
67 copy = timestamp;
68 local = localtime(&copy);
69 result = strftime(buffer, sizeof(buffer), "%x %X", local);
70 if (result == 0) {
71 return "";
72 }
73
74 return buffer;
75}
76
77/*
78 * Prints given message with a timestamp in the TEST category and INFO priority.
79 */
80void SDLTest_Log(SDL_PRINTF_FORMAT_STRING const char *fmt, ...)
81{
82 va_list list;
83 char logMessage[SDLTEST_MAX_LOGMESSAGE_LENGTH];
84
85 /* Print log message into a buffer */
86 SDL_memset(logMessage, 0, SDLTEST_MAX_LOGMESSAGE_LENGTH);
87 va_start(list, fmt);
88 (void)SDL_vsnprintf(logMessage, SDLTEST_MAX_LOGMESSAGE_LENGTH - 1, fmt, list);
89 va_end(list);
90
91 /* Log with timestamp and newline */
92 SDL_LogMessage(SDL_LOG_CATEGORY_TEST, SDL_LOG_PRIORITY_INFO, " %s: %s", SDLTest_TimestampToString(time(0)), logMessage);
93}
94
95/*
96 * Prints given message with a timestamp in the TEST category and the ERROR priority.
97 */
98void SDLTest_LogError(SDL_PRINTF_FORMAT_STRING const char *fmt, ...)
99{
100 va_list list;
101 char logMessage[SDLTEST_MAX_LOGMESSAGE_LENGTH];
102
103 /* Print log message into a buffer */
104 SDL_memset(logMessage, 0, SDLTEST_MAX_LOGMESSAGE_LENGTH);
105 va_start(list, fmt);
106 (void)SDL_vsnprintf(logMessage, SDLTEST_MAX_LOGMESSAGE_LENGTH - 1, fmt, list);
107 va_end(list);
108
109 /* Log with timestamp and newline */
110 SDL_LogMessage(SDL_LOG_CATEGORY_TEST, SDL_LOG_PRIORITY_ERROR, "%s: %s", SDLTest_TimestampToString(time(0)), logMessage);
111}
112
113static char nibble_to_char(Uint8 nibble)
114{
115 if (nibble < 0xa) {
116 return '0' + nibble;
117 } else {
118 return 'a' + nibble - 10;
119 }
120}
121
122void SDLTest_LogEscapedString(const char *prefix, const void *buffer, size_t size)
123{
124 const Uint8 *data = buffer;
125 char logMessage[SDLTEST_MAX_LOGMESSAGE_LENGTH];
126
127 if (data) {
128 size_t i;
129 size_t pos = 0;
130 #define NEED_X_CHARS(N) \
131 if (pos + (N) > sizeof(logMessage) - 2) { \
132 break; \
133 }
134
135 logMessage[pos++] = '"';
136 for (i = 0; i < size; i++) {
137 Uint8 c = data[i];
138 size_t pos_start = pos;
139 switch (c) {
140 case '\0':
141 NEED_X_CHARS(2);
142 logMessage[pos++] = '\\';
143 logMessage[pos++] = '0';
144 break;
145 case '"':
146 NEED_X_CHARS(2);
147 logMessage[pos++] = '\\';
148 logMessage[pos++] = '"';
149 break;
150 case '\n':
151 NEED_X_CHARS(2);
152 logMessage[pos++] = '\\';
153 logMessage[pos++] = 'n';
154 break;
155 case '\r':
156 NEED_X_CHARS(2);
157 logMessage[pos++] = '\\';
158 logMessage[pos++] = 'r';
159 break;
160 case '\t':
161 NEED_X_CHARS(2);
162 logMessage[pos++] = '\\';
163 logMessage[pos++] = 't';
164 break;
165 case '\f':
166 NEED_X_CHARS(2);
167 logMessage[pos++] = '\\';
168 logMessage[pos++] = 'f';
169 break;
170 case '\b':
171 NEED_X_CHARS(2);
172 logMessage[pos++] = '\\';
173 logMessage[pos++] = 'b';
174 break;
175 case '\\':
176 NEED_X_CHARS(2);
177 logMessage[pos++] = '\\';
178 logMessage[pos++] = '\\';
179 break;
180 default:
181 if (SDL_isprint(c)) {
182 NEED_X_CHARS(1);
183 logMessage[pos++] = c;
184 } else {
185 NEED_X_CHARS(4);
186 logMessage[pos++] = '\\';
187 logMessage[pos++] = 'x';
188 logMessage[pos++] = nibble_to_char(c >> 4);
189 logMessage[pos++] = nibble_to_char(c & 0xf);
190 }
191 break;
192 }
193 if (pos == pos_start) {
194 break;
195 }
196 }
197 if (i < size) {
198 logMessage[sizeof(logMessage) - 4] = '.';
199 logMessage[sizeof(logMessage) - 3] = '.';
200 logMessage[sizeof(logMessage) - 2] = '.';
201 logMessage[sizeof(logMessage) - 1] = '\0';
202 } else {
203 logMessage[pos++] = '"';
204 logMessage[pos] = '\0';
205 }
206 } else {
207 SDL_strlcpy(logMessage, "(nil)", sizeof(logMessage));
208 }
209
210 SDLTest_Log("%s%s", prefix, logMessage);
211}
diff --git a/contrib/SDL-3.2.8/src/test/SDL_test_md5.c b/contrib/SDL-3.2.8/src/test/SDL_test_md5.c
new file mode 100644
index 0000000..0cc0896
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/test/SDL_test_md5.c
@@ -0,0 +1,342 @@
1/*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
4
5 This software is provided 'as-is', without any express or implied
6 warranty. In no event will the authors be held liable for any damages
7 arising from the use of this software.
8
9 Permission is granted to anyone to use this software for any purpose,
10 including commercial applications, and to alter it and redistribute it
11 freely, subject to the following restrictions:
12
13 1. The origin of this software must not be misrepresented; you must not
14 claim that you wrote the original software. If you use this software
15 in a product, an acknowledgment in the product documentation would be
16 appreciated but is not required.
17 2. Altered source versions must be plainly marked as such, and must not be
18 misrepresented as being the original software.
19 3. This notice may not be removed or altered from any source distribution.
20*/
21
22/*
23 ***********************************************************************
24 ** RSA Data Security, Inc. MD5 Message-Digest Algorithm **
25 ** Created: 2/17/90 RLR **
26 ** Revised: 1/91 SRD,AJ,BSK,JT Reference C ver., 7/10 constant corr. **
27 ***********************************************************************
28 */
29
30/*
31 ***********************************************************************
32 ** Copyright (C) 1990, RSA Data Security, Inc. All rights reserved. **
33 ** **
34 ** License to copy and use this software is granted provided that **
35 ** it is identified as the "RSA Data Security, Inc. MD5 Message- **
36 ** Digest Algorithm" in all material mentioning or referencing this **
37 ** software or this function. **
38 ** **
39 ** License is also granted to make and use derivative works **
40 ** provided that such works are identified as "derived from the RSA **
41 ** Data Security, Inc. MD5 Message-Digest Algorithm" in all **
42 ** material mentioning or referencing the derived work. **
43 ** **
44 ** RSA Data Security, Inc. makes no representations concerning **
45 ** either the merchantability of this software or the suitability **
46 ** of this software for any particular purpose. It is provided "as **
47 ** is" without express or implied warranty of any kind. **
48 ** **
49 ** These notices must be retained in any copies of any part of this **
50 ** documentation and/or software. **
51 ***********************************************************************
52 */
53#include <SDL3/SDL_test.h>
54
55/* Forward declaration of static helper function */
56static void SDLTest_Md5Transform(MD5UINT4 *buf, const MD5UINT4 *in);
57
58static unsigned char MD5PADDING[64] = {
59 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
60 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
61 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
62 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
63 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
64 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
65 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
66 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
67};
68
69/* F, G, H and I are basic MD5 functions */
70#define F(x, y, z) (((x) & (y)) | ((~(x)) & (z)))
71#define G(x, y, z) (((x) & (z)) | ((y) & (~(z))))
72#define H(x, y, z) ((x) ^ (y) ^ (z))
73#define I(x, y, z) ((y) ^ ((x) | (~(z))))
74
75/* ROTATE_LEFT rotates x left n bits */
76#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32 - (n))))
77
78/* FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4 */
79
80/* Rotation is separate from addition to prevent recomputation */
81#define FF(a, b, c, d, x, s, ac) \
82 { \
83 (a) += F((b), (c), (d)) + (x) + (MD5UINT4)(ac); \
84 (a) = ROTATE_LEFT((a), (s)); \
85 (a) += (b); \
86 }
87#define GG(a, b, c, d, x, s, ac) \
88 { \
89 (a) += G((b), (c), (d)) + (x) + (MD5UINT4)(ac); \
90 (a) = ROTATE_LEFT((a), (s)); \
91 (a) += (b); \
92 }
93#define HH(a, b, c, d, x, s, ac) \
94 { \
95 (a) += H((b), (c), (d)) + (x) + (MD5UINT4)(ac); \
96 (a) = ROTATE_LEFT((a), (s)); \
97 (a) += (b); \
98 }
99#define II(a, b, c, d, x, s, ac) \
100 { \
101 (a) += I((b), (c), (d)) + (x) + (MD5UINT4)(ac); \
102 (a) = ROTATE_LEFT((a), (s)); \
103 (a) += (b); \
104 }
105
106/*
107 The routine MD5Init initializes the message-digest context
108 mdContext. All fields are set to zero.
109*/
110
111void SDLTest_Md5Init(SDLTest_Md5Context *mdContext)
112{
113 if (!mdContext) {
114 return;
115 }
116
117 mdContext->i[0] = mdContext->i[1] = (MD5UINT4)0;
118
119 /*
120 * Load magic initialization constants.
121 */
122 mdContext->buf[0] = (MD5UINT4)0x67452301;
123 mdContext->buf[1] = (MD5UINT4)0xefcdab89;
124 mdContext->buf[2] = (MD5UINT4)0x98badcfe;
125 mdContext->buf[3] = (MD5UINT4)0x10325476;
126}
127
128/*
129 The routine MD5Update updates the message-digest context to
130 account for the presence of each of the characters inBuf[0..inLen-1]
131 in the message whose digest is being computed.
132*/
133
134void SDLTest_Md5Update(SDLTest_Md5Context *mdContext, unsigned char *inBuf,
135 unsigned int inLen)
136{
137 MD5UINT4 in[16];
138 int mdi;
139 unsigned int i, ii;
140
141 if (!mdContext) {
142 return;
143 }
144 if (!inBuf || inLen < 1) {
145 return;
146 }
147
148 /*
149 * compute number of bytes mod 64
150 */
151 mdi = (int)((mdContext->i[0] >> 3) & 0x3F);
152
153 /*
154 * update number of bits
155 */
156 if ((mdContext->i[0] + ((MD5UINT4)inLen << 3)) < mdContext->i[0]) {
157 mdContext->i[1]++;
158 }
159 mdContext->i[0] += ((MD5UINT4)inLen << 3);
160 mdContext->i[1] += ((MD5UINT4)inLen >> 29);
161
162 while (inLen--) {
163 /*
164 * add new character to buffer, increment mdi
165 */
166 mdContext->in[mdi++] = *inBuf++;
167
168 /*
169 * transform if necessary
170 */
171 if (mdi == 0x40) {
172 for (i = 0, ii = 0; i < 16; i++, ii += 4) {
173 in[i] = (((MD5UINT4)mdContext->in[ii + 3]) << 24) | (((MD5UINT4)mdContext->in[ii + 2]) << 16) | (((MD5UINT4)mdContext->in[ii + 1]) << 8) | ((MD5UINT4)mdContext->in[ii]);
174 }
175 SDLTest_Md5Transform(mdContext->buf, in);
176 mdi = 0;
177 }
178 }
179}
180
181/*
182 The routine MD5Final terminates the message-digest computation and
183 ends with the desired message digest in mdContext->digest[0...15].
184*/
185
186void SDLTest_Md5Final(SDLTest_Md5Context *mdContext)
187{
188 MD5UINT4 in[16];
189 int mdi;
190 unsigned int i, ii;
191 unsigned int padLen;
192
193 if (!mdContext) {
194 return;
195 }
196
197 /*
198 * save number of bits
199 */
200 in[14] = mdContext->i[0];
201 in[15] = mdContext->i[1];
202
203 /*
204 * compute number of bytes mod 64
205 */
206 mdi = (int)((mdContext->i[0] >> 3) & 0x3F);
207
208 /*
209 * pad out to 56 mod 64
210 */
211 padLen = (mdi < 56) ? (56 - mdi) : (120 - mdi);
212 SDLTest_Md5Update(mdContext, MD5PADDING, padLen);
213
214 /*
215 * append length in bits and transform
216 */
217 for (i = 0, ii = 0; i < 14; i++, ii += 4) {
218 in[i] = (((MD5UINT4)mdContext->in[ii + 3]) << 24) | (((MD5UINT4)mdContext->in[ii + 2]) << 16) | (((MD5UINT4)mdContext->in[ii + 1]) << 8) | ((MD5UINT4)mdContext->in[ii]);
219 }
220 SDLTest_Md5Transform(mdContext->buf, in);
221
222 /*
223 * store buffer in digest
224 */
225 for (i = 0, ii = 0; i < 4; i++, ii += 4) {
226 mdContext->digest[ii] = (unsigned char)(mdContext->buf[i] & 0xFF);
227 mdContext->digest[ii + 1] =
228 (unsigned char)((mdContext->buf[i] >> 8) & 0xFF);
229 mdContext->digest[ii + 2] =
230 (unsigned char)((mdContext->buf[i] >> 16) & 0xFF);
231 mdContext->digest[ii + 3] =
232 (unsigned char)((mdContext->buf[i] >> 24) & 0xFF);
233 }
234}
235
236/* Basic MD5 step. Transforms buf based on in.
237 */
238static void SDLTest_Md5Transform(MD5UINT4 *buf, const MD5UINT4 *in)
239{
240 MD5UINT4 a = buf[0], b = buf[1], c = buf[2], d = buf[3];
241
242 /*
243 * Round 1
244 */
245#define S11 7
246#define S12 12
247#define S13 17
248#define S14 22
249 FF(a, b, c, d, in[0], S11, 3614090360u); /* 1 */
250 FF(d, a, b, c, in[1], S12, 3905402710u); /* 2 */
251 FF(c, d, a, b, in[2], S13, 606105819u); /* 3 */
252 FF(b, c, d, a, in[3], S14, 3250441966u); /* 4 */
253 FF(a, b, c, d, in[4], S11, 4118548399u); /* 5 */
254 FF(d, a, b, c, in[5], S12, 1200080426u); /* 6 */
255 FF(c, d, a, b, in[6], S13, 2821735955u); /* 7 */
256 FF(b, c, d, a, in[7], S14, 4249261313u); /* 8 */
257 FF(a, b, c, d, in[8], S11, 1770035416u); /* 9 */
258 FF(d, a, b, c, in[9], S12, 2336552879u); /* 10 */
259 FF(c, d, a, b, in[10], S13, 4294925233u); /* 11 */
260 FF(b, c, d, a, in[11], S14, 2304563134u); /* 12 */
261 FF(a, b, c, d, in[12], S11, 1804603682u); /* 13 */
262 FF(d, a, b, c, in[13], S12, 4254626195u); /* 14 */
263 FF(c, d, a, b, in[14], S13, 2792965006u); /* 15 */
264 FF(b, c, d, a, in[15], S14, 1236535329u); /* 16 */
265
266 /*
267 * Round 2
268 */
269#define S21 5
270#define S22 9
271#define S23 14
272#define S24 20
273 GG(a, b, c, d, in[1], S21, 4129170786u); /* 17 */
274 GG(d, a, b, c, in[6], S22, 3225465664u); /* 18 */
275 GG(c, d, a, b, in[11], S23, 643717713u); /* 19 */
276 GG(b, c, d, a, in[0], S24, 3921069994u); /* 20 */
277 GG(a, b, c, d, in[5], S21, 3593408605u); /* 21 */
278 GG(d, a, b, c, in[10], S22, 38016083u); /* 22 */
279 GG(c, d, a, b, in[15], S23, 3634488961u); /* 23 */
280 GG(b, c, d, a, in[4], S24, 3889429448u); /* 24 */
281 GG(a, b, c, d, in[9], S21, 568446438u); /* 25 */
282 GG(d, a, b, c, in[14], S22, 3275163606u); /* 26 */
283 GG(c, d, a, b, in[3], S23, 4107603335u); /* 27 */
284 GG(b, c, d, a, in[8], S24, 1163531501u); /* 28 */
285 GG(a, b, c, d, in[13], S21, 2850285829u); /* 29 */
286 GG(d, a, b, c, in[2], S22, 4243563512u); /* 30 */
287 GG(c, d, a, b, in[7], S23, 1735328473u); /* 31 */
288 GG(b, c, d, a, in[12], S24, 2368359562u); /* 32 */
289
290 /*
291 * Round 3
292 */
293#define S31 4
294#define S32 11
295#define S33 16
296#define S34 23
297 HH(a, b, c, d, in[5], S31, 4294588738u); /* 33 */
298 HH(d, a, b, c, in[8], S32, 2272392833u); /* 34 */
299 HH(c, d, a, b, in[11], S33, 1839030562u); /* 35 */
300 HH(b, c, d, a, in[14], S34, 4259657740u); /* 36 */
301 HH(a, b, c, d, in[1], S31, 2763975236u); /* 37 */
302 HH(d, a, b, c, in[4], S32, 1272893353u); /* 38 */
303 HH(c, d, a, b, in[7], S33, 4139469664u); /* 39 */
304 HH(b, c, d, a, in[10], S34, 3200236656u); /* 40 */
305 HH(a, b, c, d, in[13], S31, 681279174u); /* 41 */
306 HH(d, a, b, c, in[0], S32, 3936430074u); /* 42 */
307 HH(c, d, a, b, in[3], S33, 3572445317u); /* 43 */
308 HH(b, c, d, a, in[6], S34, 76029189u); /* 44 */
309 HH(a, b, c, d, in[9], S31, 3654602809u); /* 45 */
310 HH(d, a, b, c, in[12], S32, 3873151461u); /* 46 */
311 HH(c, d, a, b, in[15], S33, 530742520u); /* 47 */
312 HH(b, c, d, a, in[2], S34, 3299628645u); /* 48 */
313
314 /*
315 * Round 4
316 */
317#define S41 6
318#define S42 10
319#define S43 15
320#define S44 21
321 II(a, b, c, d, in[0], S41, 4096336452u); /* 49 */
322 II(d, a, b, c, in[7], S42, 1126891415u); /* 50 */
323 II(c, d, a, b, in[14], S43, 2878612391u); /* 51 */
324 II(b, c, d, a, in[5], S44, 4237533241u); /* 52 */
325 II(a, b, c, d, in[12], S41, 1700485571u); /* 53 */
326 II(d, a, b, c, in[3], S42, 2399980690u); /* 54 */
327 II(c, d, a, b, in[10], S43, 4293915773u); /* 55 */
328 II(b, c, d, a, in[1], S44, 2240044497u); /* 56 */
329 II(a, b, c, d, in[8], S41, 1873313359u); /* 57 */
330 II(d, a, b, c, in[15], S42, 4264355552u); /* 58 */
331 II(c, d, a, b, in[6], S43, 2734768916u); /* 59 */
332 II(b, c, d, a, in[13], S44, 1309151649u); /* 60 */
333 II(a, b, c, d, in[4], S41, 4149444226u); /* 61 */
334 II(d, a, b, c, in[11], S42, 3174756917u); /* 62 */
335 II(c, d, a, b, in[2], S43, 718787259u); /* 63 */
336 II(b, c, d, a, in[9], S44, 3951481745u); /* 64 */
337
338 buf[0] += a;
339 buf[1] += b;
340 buf[2] += c;
341 buf[3] += d;
342}
diff --git a/contrib/SDL-3.2.8/src/test/SDL_test_memory.c b/contrib/SDL-3.2.8/src/test/SDL_test_memory.c
new file mode 100644
index 0000000..b90c4a5
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/test/SDL_test_memory.c
@@ -0,0 +1,457 @@
1/*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
4
5 This software is provided 'as-is', without any express or implied
6 warranty. In no event will the authors be held liable for any damages
7 arising from the use of this software.
8
9 Permission is granted to anyone to use this software for any purpose,
10 including commercial applications, and to alter it and redistribute it
11 freely, subject to the following restrictions:
12
13 1. The origin of this software must not be misrepresented; you must not
14 claim that you wrote the original software. If you use this software
15 in a product, an acknowledgment in the product documentation would be
16 appreciated but is not required.
17 2. Altered source versions must be plainly marked as such, and must not be
18 misrepresented as being the original software.
19 3. This notice may not be removed or altered from any source distribution.
20*/
21#include <SDL3/SDL_test.h>
22
23#ifdef HAVE_LIBUNWIND_H
24#define UNW_LOCAL_ONLY
25#include <libunwind.h>
26#ifndef unw_get_proc_name_by_ip
27#define SDLTEST_UNWIND_NO_PROC_NAME_BY_IP
28static bool s_unwind_symbol_names = true;
29#endif
30#endif
31
32#ifdef SDL_PLATFORM_WIN32
33#include <windows.h>
34#include <dbghelp.h>
35
36static struct {
37 SDL_SharedObject *module;
38 BOOL (WINAPI *pSymInitialize)(HANDLE hProcess, PCSTR UserSearchPath, BOOL fInvadeProcess);
39 BOOL (WINAPI *pSymFromAddr)(HANDLE hProcess, DWORD64 Address, PDWORD64 Displacement, PSYMBOL_INFO Symbol);
40 BOOL (WINAPI *pSymGetLineFromAddr64)(HANDLE hProcess, DWORD64 qwAddr, PDWORD pdwDisplacement, PIMAGEHLP_LINE64 Line);
41} dyn_dbghelp;
42
43/* older SDKs might not have this: */
44__declspec(dllimport) USHORT WINAPI RtlCaptureStackBackTrace(ULONG FramesToSkip, ULONG FramesToCapture, PVOID* BackTrace, PULONG BackTraceHash);
45#define CaptureStackBackTrace RtlCaptureStackBackTrace
46
47#endif
48
49/* This is a simple tracking allocator to demonstrate the use of SDL's
50 memory allocation replacement functionality.
51
52 It gets slow with large numbers of allocations and shouldn't be used
53 for production code.
54*/
55
56#define MAXIMUM_TRACKED_STACK_DEPTH 32
57
58typedef struct SDL_tracked_allocation
59{
60 void *mem;
61 size_t size;
62 Uint64 stack[MAXIMUM_TRACKED_STACK_DEPTH];
63 struct SDL_tracked_allocation *next;
64#ifdef SDLTEST_UNWIND_NO_PROC_NAME_BY_IP
65 char stack_names[MAXIMUM_TRACKED_STACK_DEPTH][256];
66#endif
67} SDL_tracked_allocation;
68
69static SDLTest_Crc32Context s_crc32_context;
70static SDL_malloc_func SDL_malloc_orig = NULL;
71static SDL_calloc_func SDL_calloc_orig = NULL;
72static SDL_realloc_func SDL_realloc_orig = NULL;
73static SDL_free_func SDL_free_orig = NULL;
74static int s_previous_allocations = 0;
75static int s_unknown_frees = 0;
76static SDL_tracked_allocation *s_tracked_allocations[256];
77static bool s_randfill_allocations = false;
78static SDL_AtomicInt s_lock;
79
80#define LOCK_ALLOCATOR() \
81 do { \
82 if (SDL_CompareAndSwapAtomicInt(&s_lock, 0, 1)) { \
83 break; \
84 } \
85 SDL_CPUPauseInstruction(); \
86 } while (true)
87#define UNLOCK_ALLOCATOR() do { SDL_SetAtomicInt(&s_lock, 0); } while (0)
88
89static unsigned int get_allocation_bucket(void *mem)
90{
91 CrcUint32 crc_value;
92 unsigned int index;
93 SDLTest_Crc32Calc(&s_crc32_context, (CrcUint8 *)&mem, sizeof(mem), &crc_value);
94 index = (crc_value & (SDL_arraysize(s_tracked_allocations) - 1));
95 return index;
96}
97
98static SDL_tracked_allocation* SDL_GetTrackedAllocation(void *mem)
99{
100 SDL_tracked_allocation *entry;
101 LOCK_ALLOCATOR();
102 int index = get_allocation_bucket(mem);
103 for (entry = s_tracked_allocations[index]; entry; entry = entry->next) {
104 if (mem == entry->mem) {
105 UNLOCK_ALLOCATOR();
106 return entry;
107 }
108 }
109 UNLOCK_ALLOCATOR();
110 return NULL;
111}
112
113static size_t SDL_GetTrackedAllocationSize(void *mem)
114{
115 SDL_tracked_allocation *entry = SDL_GetTrackedAllocation(mem);
116
117 return entry ? entry->size : SIZE_MAX;
118}
119
120static bool SDL_IsAllocationTracked(void *mem)
121{
122 return SDL_GetTrackedAllocation(mem) != NULL;
123}
124
125static void SDL_TrackAllocation(void *mem, size_t size)
126{
127 SDL_tracked_allocation *entry;
128 int index = get_allocation_bucket(mem);
129
130 if (SDL_IsAllocationTracked(mem)) {
131 return;
132 }
133 entry = (SDL_tracked_allocation *)SDL_malloc_orig(sizeof(*entry));
134 if (!entry) {
135 return;
136 }
137 LOCK_ALLOCATOR();
138 entry->mem = mem;
139 entry->size = size;
140
141 /* Generate the stack trace for the allocation */
142 SDL_zeroa(entry->stack);
143#ifdef HAVE_LIBUNWIND_H
144 {
145 int stack_index;
146 unw_cursor_t cursor;
147 unw_context_t context;
148
149 unw_getcontext(&context);
150 unw_init_local(&cursor, &context);
151
152 stack_index = 0;
153 while (unw_step(&cursor) > 0) {
154 unw_word_t pc;
155#ifdef SDLTEST_UNWIND_NO_PROC_NAME_BY_IP
156 unw_word_t offset;
157 char sym[236];
158#endif
159
160 unw_get_reg(&cursor, UNW_REG_IP, &pc);
161 entry->stack[stack_index] = pc;
162
163#ifdef SDLTEST_UNWIND_NO_PROC_NAME_BY_IP
164 if (s_unwind_symbol_names && unw_get_proc_name(&cursor, sym, sizeof(sym), &offset) == 0) {
165 SDL_snprintf(entry->stack_names[stack_index], sizeof(entry->stack_names[stack_index]), "%s+0x%llx", sym, (unsigned long long)offset);
166 }
167#endif
168 ++stack_index;
169
170 if (stack_index == SDL_arraysize(entry->stack)) {
171 break;
172 }
173 }
174 }
175#elif defined(SDL_PLATFORM_WIN32)
176 {
177 Uint32 count;
178 PVOID frames[63];
179 Uint32 i;
180
181 count = CaptureStackBackTrace(1, SDL_arraysize(frames), frames, NULL);
182
183 count = SDL_min(count, MAXIMUM_TRACKED_STACK_DEPTH);
184 for (i = 0; i < count; i++) {
185 entry->stack[i] = (Uint64)(uintptr_t)frames[i];
186 }
187 }
188#endif /* HAVE_LIBUNWIND_H */
189
190 entry->next = s_tracked_allocations[index];
191 s_tracked_allocations[index] = entry;
192 UNLOCK_ALLOCATOR();
193}
194
195static void SDL_UntrackAllocation(void *mem)
196{
197 SDL_tracked_allocation *entry, *prev;
198 int index = get_allocation_bucket(mem);
199
200 LOCK_ALLOCATOR();
201 prev = NULL;
202 for (entry = s_tracked_allocations[index]; entry; entry = entry->next) {
203 if (mem == entry->mem) {
204 if (prev) {
205 prev->next = entry->next;
206 } else {
207 s_tracked_allocations[index] = entry->next;
208 }
209 SDL_free_orig(entry);
210 UNLOCK_ALLOCATOR();
211 return;
212 }
213 prev = entry;
214 }
215 s_unknown_frees += 1;
216 UNLOCK_ALLOCATOR();
217}
218
219static void rand_fill_memory(void* ptr, size_t start, size_t end)
220{
221 Uint8* mem = (Uint8*) ptr;
222 size_t i;
223
224 if (!s_randfill_allocations)
225 return;
226
227 for (i = start; i < end; ++i) {
228 mem[i] = SDLTest_RandomUint8();
229 }
230}
231
232static void * SDLCALL SDLTest_TrackedMalloc(size_t size)
233{
234 void *mem;
235
236 mem = SDL_malloc_orig(size);
237 if (mem) {
238 SDL_TrackAllocation(mem, size);
239 rand_fill_memory(mem, 0, size);
240 }
241 return mem;
242}
243
244static void * SDLCALL SDLTest_TrackedCalloc(size_t nmemb, size_t size)
245{
246 void *mem;
247
248 mem = SDL_calloc_orig(nmemb, size);
249 if (mem) {
250 SDL_TrackAllocation(mem, nmemb * size);
251 }
252 return mem;
253}
254
255static void * SDLCALL SDLTest_TrackedRealloc(void *ptr, size_t size)
256{
257 void *mem;
258 size_t old_size = 0;
259 if (ptr) {
260 old_size = SDL_GetTrackedAllocationSize(ptr);
261 SDL_assert(old_size != SIZE_MAX);
262 }
263 mem = SDL_realloc_orig(ptr, size);
264 if (ptr) {
265 SDL_UntrackAllocation(ptr);
266 }
267 if (mem) {
268 SDL_TrackAllocation(mem, size);
269 if (size > old_size) {
270 rand_fill_memory(mem, old_size, size);
271 }
272 }
273 return mem;
274}
275
276static void SDLCALL SDLTest_TrackedFree(void *ptr)
277{
278 if (!ptr) {
279 return;
280 }
281
282 if (s_previous_allocations == 0) {
283 SDL_assert(SDL_IsAllocationTracked(ptr));
284 }
285 SDL_UntrackAllocation(ptr);
286 SDL_free_orig(ptr);
287}
288
289void SDLTest_TrackAllocations(void)
290{
291 if (SDL_malloc_orig) {
292 return;
293 }
294
295 SDLTest_Crc32Init(&s_crc32_context);
296
297 s_previous_allocations = SDL_GetNumAllocations();
298 if (s_previous_allocations < 0) {
299 SDL_Log("SDL was built without allocation count support, disabling free() validation");
300 } else if (s_previous_allocations != 0) {
301 SDL_Log("SDLTest_TrackAllocations(): There are %d previous allocations, disabling free() validation", s_previous_allocations);
302 }
303#ifdef SDLTEST_UNWIND_NO_PROC_NAME_BY_IP
304 do {
305 /* Don't use SDL_GetHint: SDL_malloc is off limits. */
306 const char *env_trackmem = SDL_getenv_unsafe("SDL_TRACKMEM_SYMBOL_NAMES");
307 if (env_trackmem) {
308 if (SDL_strcasecmp(env_trackmem, "1") == 0 || SDL_strcasecmp(env_trackmem, "yes") == 0 || SDL_strcasecmp(env_trackmem, "true") == 0) {
309 s_unwind_symbol_names = true;
310 } else if (SDL_strcasecmp(env_trackmem, "0") == 0 || SDL_strcasecmp(env_trackmem, "no") == 0 || SDL_strcasecmp(env_trackmem, "false") == 0) {
311 s_unwind_symbol_names = false;
312 }
313 }
314 } while (0);
315
316#elif defined(SDL_PLATFORM_WIN32)
317 do {
318 dyn_dbghelp.module = SDL_LoadObject("dbghelp.dll");
319 if (!dyn_dbghelp.module) {
320 goto dbghelp_failed;
321 }
322 dyn_dbghelp.pSymInitialize = (void *)SDL_LoadFunction(dyn_dbghelp.module, "SymInitialize");
323 dyn_dbghelp.pSymFromAddr = (void *)SDL_LoadFunction(dyn_dbghelp.module, "SymFromAddr");
324 dyn_dbghelp.pSymGetLineFromAddr64 = (void *)SDL_LoadFunction(dyn_dbghelp.module, "SymGetLineFromAddr64");
325 if (!dyn_dbghelp.pSymInitialize || !dyn_dbghelp.pSymFromAddr || !dyn_dbghelp.pSymGetLineFromAddr64) {
326 goto dbghelp_failed;
327 }
328 if (!dyn_dbghelp.pSymInitialize(GetCurrentProcess(), NULL, TRUE)) {
329 goto dbghelp_failed;
330 }
331 break;
332dbghelp_failed:
333 if (dyn_dbghelp.module) {
334 SDL_UnloadObject(dyn_dbghelp.module);
335 dyn_dbghelp.module = NULL;
336 }
337 } while (0);
338#endif
339
340 SDL_GetMemoryFunctions(&SDL_malloc_orig,
341 &SDL_calloc_orig,
342 &SDL_realloc_orig,
343 &SDL_free_orig);
344
345 SDL_SetMemoryFunctions(SDLTest_TrackedMalloc,
346 SDLTest_TrackedCalloc,
347 SDLTest_TrackedRealloc,
348 SDLTest_TrackedFree);
349}
350
351void SDLTest_RandFillAllocations(void)
352{
353 SDLTest_TrackAllocations();
354
355 s_randfill_allocations = true;
356}
357
358void SDLTest_LogAllocations(void)
359{
360 char *message = NULL;
361 size_t message_size = 0;
362 char line[256], *tmp;
363 SDL_tracked_allocation *entry;
364 int index, count, stack_index;
365 Uint64 total_allocated;
366
367 if (!SDL_malloc_orig) {
368 return;
369 }
370
371 message = SDL_realloc_orig(NULL, 1);
372 if (!message) {
373 return;
374 }
375 *message = 0;
376
377#define ADD_LINE() \
378 message_size += (SDL_strlen(line) + 1); \
379 tmp = (char *)SDL_realloc_orig(message, message_size); \
380 if (!tmp) { \
381 return; \
382 } \
383 message = tmp; \
384 SDL_strlcat(message, line, message_size)
385
386 SDL_strlcpy(line, "Memory allocations:\n", sizeof(line));
387 ADD_LINE();
388
389 count = 0;
390 total_allocated = 0;
391 for (index = 0; index < SDL_arraysize(s_tracked_allocations); ++index) {
392 for (entry = s_tracked_allocations[index]; entry; entry = entry->next) {
393 (void)SDL_snprintf(line, sizeof(line), "Allocation %d: %d bytes\n", count, (int)entry->size);
394 ADD_LINE();
395 /* Start at stack index 1 to skip our tracking functions */
396 for (stack_index = 1; stack_index < SDL_arraysize(entry->stack); ++stack_index) {
397 char stack_entry_description[256] = "???";
398
399 if (!entry->stack[stack_index]) {
400 break;
401 }
402#ifdef HAVE_LIBUNWIND_H
403 {
404#ifdef SDLTEST_UNWIND_NO_PROC_NAME_BY_IP
405 if (s_unwind_symbol_names) {
406 (void)SDL_snprintf(stack_entry_description, sizeof(stack_entry_description), "%s", entry->stack_names[stack_index]);
407 }
408#else
409 char name[256] = "???";
410 unw_word_t offset = 0;
411 unw_get_proc_name_by_ip(unw_local_addr_space, entry->stack[stack_index], name, sizeof(name), &offset, NULL);
412 (void)SDL_snprintf(stack_entry_description, sizeof(stack_entry_description), "%s+0x%llx", name, (long long unsigned int)offset);
413#endif
414 }
415#elif defined(SDL_PLATFORM_WIN32)
416 {
417 DWORD64 dwDisplacement = 0;
418 char symbol_buffer[sizeof(SYMBOL_INFO) + MAX_SYM_NAME * sizeof(TCHAR)];
419 PSYMBOL_INFO pSymbol = (PSYMBOL_INFO)symbol_buffer;
420 DWORD lineColumn = 0;
421 pSymbol->SizeOfStruct = sizeof(SYMBOL_INFO);
422 pSymbol->MaxNameLen = MAX_SYM_NAME;
423 IMAGEHLP_LINE64 dbg_line;
424 dbg_line.SizeOfStruct = sizeof(dbg_line);
425 dbg_line.FileName = "";
426 dbg_line.LineNumber = 0;
427
428 if (dyn_dbghelp.module) {
429 if (!dyn_dbghelp.pSymFromAddr(GetCurrentProcess(), entry->stack[stack_index], &dwDisplacement, pSymbol)) {
430 SDL_strlcpy(pSymbol->Name, "???", MAX_SYM_NAME);
431 dwDisplacement = 0;
432 }
433 dyn_dbghelp.pSymGetLineFromAddr64(GetCurrentProcess(), (DWORD64)entry->stack[stack_index], &lineColumn, &dbg_line);
434 }
435 SDL_snprintf(stack_entry_description, sizeof(stack_entry_description), "%s+0x%I64x %s:%u", pSymbol->Name, dwDisplacement, dbg_line.FileName, (Uint32)dbg_line.LineNumber);
436 }
437#endif
438 (void)SDL_snprintf(line, sizeof(line), "\t0x%" SDL_PRIx64 ": %s\n", entry->stack[stack_index], stack_entry_description);
439
440 ADD_LINE();
441 }
442 total_allocated += entry->size;
443 ++count;
444 }
445 }
446 (void)SDL_snprintf(line, sizeof(line), "Total: %.2f Kb in %d allocations", total_allocated / 1024.0, count);
447 ADD_LINE();
448 if (s_unknown_frees != 0) {
449 (void)SDL_snprintf(line, sizeof(line), ", %d unknown frees", s_unknown_frees);
450 ADD_LINE();
451 }
452 (void)SDL_snprintf(line, sizeof(line), "\n");
453 ADD_LINE();
454#undef ADD_LINE
455
456 SDL_Log("%s", message);
457}