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