1 /*
2 Copyright 2012-2020 David Robillard <d@drobilla.net>
3
4 Permission to use, copy, modify, and/or distribute this software for any
5 purpose with or without fee is hereby granted, provided that the above
6 copyright notice and this permission notice appear in all copies.
7
8 THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
16
17 #ifndef TEST_TEST_UTILS_H
18 #define TEST_TEST_UTILS_H
19
20 #define __STDC_FORMAT_MACROS 1
21
22 #include "pugl/pugl.h"
23
24 #include <inttypes.h>
25 #include <stdarg.h>
26 #include <stdbool.h>
27 #include <stdint.h>
28 #include <stdio.h>
29 #include <string.h>
30
31 #ifdef __GNUC__
32 # define PUGL_LOG_FUNC(fmt, arg1) __attribute__((format(printf, fmt, arg1)))
33 #else
34 # define PUGL_LOG_FUNC(fmt, arg1)
35 #endif
36
37 typedef struct {
38 int samples;
39 int doubleBuffer;
40 int sync;
41 bool continuous;
42 bool help;
43 bool ignoreKeyRepeat;
44 bool resizable;
45 bool verbose;
46 bool errorChecking;
47 } PuglTestOptions;
48
49 PUGL_LOG_FUNC(1, 2)
50 static int
logError(const char * fmt,...)51 logError(const char* fmt, ...)
52 {
53 fprintf(stderr, "error: ");
54
55 va_list args; // NOLINT
56 va_start(args, fmt);
57 vfprintf(stderr, fmt, args);
58 va_end(args);
59
60 return 1;
61 }
62
63 static inline int
printModifiers(const uint32_t mods)64 printModifiers(const uint32_t mods)
65 {
66 return fprintf(stderr,
67 "Modifiers:%s%s%s%s\n",
68 (mods & PUGL_MOD_SHIFT) ? " Shift" : "",
69 (mods & PUGL_MOD_CTRL) ? " Ctrl" : "",
70 (mods & PUGL_MOD_ALT) ? " Alt" : "",
71 (mods & PUGL_MOD_SUPER) ? " Super" : "");
72 }
73
74 static inline const char*
crossingModeString(const PuglCrossingMode mode)75 crossingModeString(const PuglCrossingMode mode)
76 {
77 switch (mode) {
78 case PUGL_CROSSING_NORMAL:
79 return "normal";
80 case PUGL_CROSSING_GRAB:
81 return "grab";
82 case PUGL_CROSSING_UNGRAB:
83 return "ungrab";
84 }
85
86 return "unknown";
87 }
88
89 static inline const char*
scrollDirectionString(const PuglScrollDirection direction)90 scrollDirectionString(const PuglScrollDirection direction)
91 {
92 switch (direction) {
93 case PUGL_SCROLL_UP:
94 return "up";
95 case PUGL_SCROLL_DOWN:
96 return "down";
97 case PUGL_SCROLL_LEFT:
98 return "left";
99 case PUGL_SCROLL_RIGHT:
100 return "right";
101 case PUGL_SCROLL_SMOOTH:
102 return "smooth";
103 }
104
105 return "unknown";
106 }
107
108 static inline int
printEvent(const PuglEvent * event,const char * prefix,const bool verbose)109 printEvent(const PuglEvent* event, const char* prefix, const bool verbose)
110 {
111 #define FFMT "%6.1f"
112 #define PFMT FFMT " " FFMT
113 #define PRINT(fmt, ...) fprintf(stderr, fmt, __VA_ARGS__)
114
115 switch (event->type) {
116 case PUGL_NOTHING:
117 return 0;
118 case PUGL_KEY_PRESS:
119 return PRINT("%sKey press code %3u key U+%04X\n",
120 prefix,
121 event->key.keycode,
122 event->key.key);
123 case PUGL_KEY_RELEASE:
124 return PRINT("%sKey release code %3u key U+%04X\n",
125 prefix,
126 event->key.keycode,
127 event->key.key);
128 case PUGL_TEXT:
129 return PRINT("%sText entry code %3u char U+%04X (%s)\n",
130 prefix,
131 event->text.keycode,
132 event->text.character,
133 event->text.string);
134 case PUGL_BUTTON_PRESS:
135 case PUGL_BUTTON_RELEASE:
136 return (PRINT("%sMouse %u %s at " PFMT " ",
137 prefix,
138 event->button.button,
139 (event->type == PUGL_BUTTON_PRESS) ? "down" : "up ",
140 event->button.x,
141 event->button.y) +
142 printModifiers(event->scroll.state));
143 case PUGL_SCROLL:
144 return (PRINT("%sScroll %5.1f %5.1f (%s) at " PFMT " ",
145 prefix,
146 event->scroll.dx,
147 event->scroll.dy,
148 scrollDirectionString(event->scroll.direction),
149 event->scroll.x,
150 event->scroll.y) +
151 printModifiers(event->scroll.state));
152 case PUGL_POINTER_IN:
153 return PRINT("%sMouse enter at " PFMT " (%s)\n",
154 prefix,
155 event->crossing.x,
156 event->crossing.y,
157 crossingModeString(event->crossing.mode));
158 case PUGL_POINTER_OUT:
159 return PRINT("%sMouse leave at " PFMT " (%s)\n",
160 prefix,
161 event->crossing.x,
162 event->crossing.y,
163 crossingModeString(event->crossing.mode));
164 case PUGL_FOCUS_IN:
165 return PRINT(
166 "%sFocus in (%s)\n", prefix, crossingModeString(event->crossing.mode));
167 case PUGL_FOCUS_OUT:
168 return PRINT(
169 "%sFocus out (%s)\n", prefix, crossingModeString(event->crossing.mode));
170 case PUGL_CLIENT:
171 return PRINT("%sClient %" PRIXPTR " %" PRIXPTR "\n",
172 prefix,
173 event->client.data1,
174 event->client.data2);
175 case PUGL_LOOP_ENTER:
176 return PRINT("%sLoop enter\n", prefix);
177 case PUGL_LOOP_LEAVE:
178 return PRINT("%sLoop leave\n", prefix);
179 default:
180 break;
181 }
182
183 if (verbose) {
184 switch (event->type) {
185 case PUGL_CREATE:
186 return fprintf(stderr, "%sCreate\n", prefix);
187 case PUGL_DESTROY:
188 return fprintf(stderr, "%sDestroy\n", prefix);
189 case PUGL_MAP:
190 return fprintf(stderr, "%sMap\n", prefix);
191 case PUGL_UNMAP:
192 return fprintf(stderr, "%sUnmap\n", prefix);
193 case PUGL_UPDATE:
194 return fprintf(stderr, "%sUpdate\n", prefix);
195 case PUGL_CONFIGURE:
196 return PRINT("%sConfigure " PFMT " " PFMT "\n",
197 prefix,
198 event->configure.x,
199 event->configure.y,
200 event->configure.width,
201 event->configure.height);
202 case PUGL_EXPOSE:
203 return PRINT("%sExpose " PFMT " " PFMT "\n",
204 prefix,
205 event->expose.x,
206 event->expose.y,
207 event->expose.width,
208 event->expose.height);
209 case PUGL_CLOSE:
210 return PRINT("%sClose\n", prefix);
211 case PUGL_MOTION:
212 return PRINT("%sMouse motion at " PFMT "\n",
213 prefix,
214 event->motion.x,
215 event->motion.y);
216 case PUGL_TIMER:
217 return PRINT("%sTimer %" PRIuPTR "\n", prefix, event->timer.id);
218 default:
219 return PRINT("%sUnknown event type %d\n", prefix, (int)event->type);
220 }
221 }
222
223 #undef PRINT
224 #undef PFMT
225 #undef FFMT
226
227 return 0;
228 }
229
230 static inline const char*
puglViewHintString(const PuglViewHint hint)231 puglViewHintString(const PuglViewHint hint)
232 {
233 switch (hint) {
234 case PUGL_USE_COMPAT_PROFILE:
235 return "Use compatible profile";
236 case PUGL_USE_DEBUG_CONTEXT:
237 return "Use debug context";
238 case PUGL_CONTEXT_VERSION_MAJOR:
239 return "Context major version";
240 case PUGL_CONTEXT_VERSION_MINOR:
241 return "Context minor version";
242 case PUGL_RED_BITS:
243 return "Red bits";
244 case PUGL_GREEN_BITS:
245 return "Green bits";
246 case PUGL_BLUE_BITS:
247 return "Blue bits";
248 case PUGL_ALPHA_BITS:
249 return "Alpha bits";
250 case PUGL_DEPTH_BITS:
251 return "Depth bits";
252 case PUGL_STENCIL_BITS:
253 return "Stencil bits";
254 case PUGL_SAMPLES:
255 return "Samples";
256 case PUGL_DOUBLE_BUFFER:
257 return "Double buffer";
258 case PUGL_SWAP_INTERVAL:
259 return "Swap interval";
260 case PUGL_RESIZABLE:
261 return "Resizable";
262 case PUGL_IGNORE_KEY_REPEAT:
263 return "Ignore key repeat";
264 case PUGL_REFRESH_RATE:
265 return "Refresh rate";
266 case PUGL_NUM_VIEW_HINTS:
267 return "Unknown";
268 }
269
270 return "Unknown";
271 }
272
273 static inline void
printViewHints(const PuglView * view)274 printViewHints(const PuglView* view)
275 {
276 for (int i = 0; i < PUGL_NUM_VIEW_HINTS; ++i) {
277 const PuglViewHint hint = (PuglViewHint)i;
278 fprintf(stderr,
279 "%s: %d\n",
280 puglViewHintString(hint),
281 puglGetViewHint(view, hint));
282 }
283 }
284
285 static inline void
puglPrintTestUsage(const char * prog,const char * posHelp)286 puglPrintTestUsage(const char* prog, const char* posHelp)
287 {
288 printf("Usage: %s [OPTION]... %s\n\n"
289 " -a Enable anti-aliasing\n"
290 " -c Continuously animate and draw\n"
291 " -d Directly draw to window (no double-buffering)\n"
292 " -e Enable platform error-checking\n"
293 " -f Fast drawing, explicitly disable vertical sync\n"
294 " -h Display this help\n"
295 " -i Ignore key repeat\n"
296 " -v Print verbose output\n"
297 " -r Resizable window\n"
298 " -s Explicitly enable vertical sync\n",
299 prog,
300 posHelp);
301 }
302
303 static inline PuglTestOptions
puglParseTestOptions(int * pargc,char *** pargv)304 puglParseTestOptions(int* pargc, char*** pargv)
305 {
306 PuglTestOptions opts = {
307 0,
308 PUGL_TRUE,
309 PUGL_DONT_CARE,
310 false,
311 false,
312 false,
313 false,
314 false,
315 false,
316 };
317
318 char** const argv = *pargv;
319 int i = 1;
320 for (; i < *pargc; ++i) {
321 if (!strcmp(argv[i], "-a")) {
322 opts.samples = 4;
323 } else if (!strcmp(argv[i], "-c")) {
324 opts.continuous = true;
325 } else if (!strcmp(argv[i], "-d")) {
326 opts.doubleBuffer = PUGL_FALSE;
327 } else if (!strcmp(argv[i], "-e")) {
328 opts.errorChecking = PUGL_TRUE;
329 } else if (!strcmp(argv[i], "-f")) {
330 opts.sync = PUGL_FALSE;
331 } else if (!strcmp(argv[i], "-h")) {
332 opts.help = true;
333 return opts;
334 } else if (!strcmp(argv[i], "-i")) {
335 opts.ignoreKeyRepeat = true;
336 } else if (!strcmp(argv[i], "-r")) {
337 opts.resizable = true;
338 } else if (!strcmp(argv[i], "-s")) {
339 opts.sync = PUGL_TRUE;
340 } else if (!strcmp(argv[i], "-v")) {
341 opts.verbose = true;
342 } else if (argv[i][0] != '-') {
343 break;
344 } else {
345 opts.help = true;
346 logError("Unknown option: %s\n", argv[i]);
347 }
348 }
349
350 *pargc -= i;
351 *pargv += i;
352
353 return opts;
354 }
355
356 #endif // TEST_TEST_UTILS_H
357