diff options
Diffstat (limited to 'subprojects/puglutil')
-rw-r--r-- | subprojects/puglutil/include/puglutil/test_utils.h | 571 | ||||
-rw-r--r-- | subprojects/puglutil/meson.build | 14 |
2 files changed, 585 insertions, 0 deletions
diff --git a/subprojects/puglutil/include/puglutil/test_utils.h b/subprojects/puglutil/include/puglutil/test_utils.h new file mode 100644 index 0000000..4ebd985 --- /dev/null +++ b/subprojects/puglutil/include/puglutil/test_utils.h @@ -0,0 +1,571 @@ +// Copyright 2012-2023 David Robillard <d@drobilla.net> +// SPDX-License-Identifier: ISC + +#ifndef TEST_TEST_UTILS_H +#define TEST_TEST_UTILS_H + +#include "pugl/pugl.h" + +#include <inttypes.h> +#include <stdarg.h> +#include <stdbool.h> +#include <stdio.h> +#include <string.h> + +#ifdef __GNUC__ +# define PUGL_LOG_FUNC(fmt, arg1) __attribute__((format(printf, fmt, arg1))) +#else +# define PUGL_LOG_FUNC(fmt, arg1) +#endif + +typedef struct { + int samples; + int doubleBuffer; + int sync; + int glApi; + int glMajorVersion; + int glMinorVersion; + bool continuous; + bool help; + bool ignoreKeyRepeat; + bool resizable; + bool verbose; + bool errorChecking; +} PuglTestOptions; + +PUGL_LOG_FUNC(1, 2) +static int +logError(const char* fmt, ...) +{ + fprintf(stderr, "error: "); + + va_list args; // NOLINT + va_start(args, fmt); + vfprintf(stderr, fmt, args); + va_end(args); + + return 1; +} + +static inline int +printModifiers(const uint32_t mods) +{ + return fprintf(stderr, + "Modifiers:%s%s%s%s%s%s%s\n", + (mods & PUGL_MOD_SHIFT) ? " Shift" : "", + (mods & PUGL_MOD_CTRL) ? " Ctrl" : "", + (mods & PUGL_MOD_ALT) ? " Alt" : "", + (mods & PUGL_MOD_SUPER) ? " Super" : "", + (mods & PUGL_MOD_NUM_LOCK) ? " Num" : "", + (mods & PUGL_MOD_SCROLL_LOCK) ? " Scroll" : "", + (mods & PUGL_MOD_CAPS_LOCK) ? " Caps" : ""); +} + +static inline const char* +crossingModeString(const PuglCrossingMode mode) +{ + switch (mode) { + case PUGL_CROSSING_NORMAL: + return "normal"; + case PUGL_CROSSING_GRAB: + return "grab"; + case PUGL_CROSSING_UNGRAB: + return "ungrab"; + } + + return "unknown"; +} + +static inline const char* +scrollDirectionString(const PuglScrollDirection direction) +{ + switch (direction) { + case PUGL_SCROLL_UP: + return "up"; + case PUGL_SCROLL_DOWN: + return "down"; + case PUGL_SCROLL_LEFT: + return "left"; + case PUGL_SCROLL_RIGHT: + return "right"; + case PUGL_SCROLL_SMOOTH: + return "smooth"; + } + + return "unknown"; +} + +static inline const char* +viewStyleFlagString(const PuglViewStyleFlag state) +{ + switch (state) { + case PUGL_VIEW_STYLE_MODAL: + return "modal"; + case PUGL_VIEW_STYLE_TALL: + return "tall"; + case PUGL_VIEW_STYLE_WIDE: + return "wide"; + case PUGL_VIEW_STYLE_HIDDEN: + return "hidden"; + case PUGL_VIEW_STYLE_FULLSCREEN: + return "fullscreen"; + case PUGL_VIEW_STYLE_ABOVE: + return "above"; + case PUGL_VIEW_STYLE_BELOW: + return "below"; + case PUGL_VIEW_STYLE_DEMANDING: + return "demanding"; + case PUGL_VIEW_STYLE_RESIZING: + return "resizing"; + case PUGL_VIEW_STYLE_MAPPED: + return "mapped"; + } + + return "unknown"; +} + +static inline const char* +keyString(const uint32_t key) +{ + switch (key) { + case PUGL_KEY_BACKSPACE: + return "BACKSPACE"; + case PUGL_KEY_TAB: + return "TAB"; + case PUGL_KEY_ENTER: + return "ENTER"; + case PUGL_KEY_ESCAPE: + return "ESCAPE"; + case PUGL_KEY_DELETE: + return "DELETE"; + case PUGL_KEY_SPACE: + return "SPACE"; + + case PUGL_KEY_F1: + return "F1"; + case PUGL_KEY_F2: + return "F2"; + case PUGL_KEY_F3: + return "F3"; + case PUGL_KEY_F4: + return "F4"; + case PUGL_KEY_F5: + return "F5"; + case PUGL_KEY_F6: + return "F6"; + case PUGL_KEY_F7: + return "F7"; + case PUGL_KEY_F8: + return "F8"; + case PUGL_KEY_F9: + return "F9"; + case PUGL_KEY_F10: + return "F10"; + case PUGL_KEY_F11: + return "F11"; + case PUGL_KEY_F12: + return "F12"; + + case PUGL_KEY_PAGE_UP: + return "PAGE_UP"; + case PUGL_KEY_PAGE_DOWN: + return "PAGE_DOWN"; + case PUGL_KEY_END: + return "END"; + case PUGL_KEY_HOME: + return "HOME"; + case PUGL_KEY_LEFT: + return "LEFT"; + case PUGL_KEY_UP: + return "UP"; + case PUGL_KEY_RIGHT: + return "RIGHT"; + case PUGL_KEY_DOWN: + return "DOWN"; + + case PUGL_KEY_PRINT_SCREEN: + return "PRINT_SCREEN"; + case PUGL_KEY_INSERT: + return "INSERT"; + case PUGL_KEY_PAUSE: + return "PAUSE"; + case PUGL_KEY_MENU: + return "MENU"; + case PUGL_KEY_NUM_LOCK: + return "NUM_LOCK"; + case PUGL_KEY_SCROLL_LOCK: + return "SCROLL_LOCK"; + case PUGL_KEY_CAPS_LOCK: + return "CAPS_LOCK"; + + case PUGL_KEY_SHIFT_L: + return "SHIFT_L"; + case PUGL_KEY_SHIFT_R: + return "SHIFT_R"; + case PUGL_KEY_CTRL_L: + return "CTRL_L"; + case PUGL_KEY_CTRL_R: + return "CTRL_R"; + case PUGL_KEY_ALT_L: + return "ALT_L"; + case PUGL_KEY_ALT_R: + return "ALT_R"; + case PUGL_KEY_SUPER_L: + return "SUPER_L"; + case PUGL_KEY_SUPER_R: + return "SUPER_R"; + + case PUGL_KEY_PAD_0: + return "PAD_0"; + case PUGL_KEY_PAD_1: + return "PAD_1"; + case PUGL_KEY_PAD_2: + return "PAD_2"; + case PUGL_KEY_PAD_3: + return "PAD_3"; + case PUGL_KEY_PAD_4: + return "PAD_4"; + case PUGL_KEY_PAD_5: + return "PAD_5"; + case PUGL_KEY_PAD_6: + return "PAD_6"; + case PUGL_KEY_PAD_7: + return "PAD_7"; + case PUGL_KEY_PAD_8: + return "PAD_8"; + case PUGL_KEY_PAD_9: + return "PAD_9"; + case PUGL_KEY_PAD_ENTER: + return "PAD_ENTER"; + + case PUGL_KEY_PAD_PAGE_UP: + return "PAD_PAGE_UP"; + case PUGL_KEY_PAD_PAGE_DOWN: + return "PAD_PAGE_DOWN"; + case PUGL_KEY_PAD_END: + return "PAD_END"; + case PUGL_KEY_PAD_HOME: + return "PAD_HOME"; + case PUGL_KEY_PAD_LEFT: + return "PAD_LEFT"; + case PUGL_KEY_PAD_UP: + return "PAD_UP"; + case PUGL_KEY_PAD_RIGHT: + return "PAD_RIGHT"; + case PUGL_KEY_PAD_DOWN: + return "PAD_DOWN"; + + case PUGL_KEY_PAD_CLEAR: + return "PAD_CLEAR"; + case PUGL_KEY_PAD_INSERT: + return "PAD_INSERT"; + case PUGL_KEY_PAD_DELETE: + return "PAD_DELETE"; + case PUGL_KEY_PAD_EQUAL: + return "PAD_EQUAL"; + + case PUGL_KEY_PAD_MULTIPLY: + return "PAD_MULTIPLY"; + case PUGL_KEY_PAD_ADD: + return "PAD_ADD"; + case PUGL_KEY_PAD_SEPARATOR: + return "PAD_SEPARATOR"; + case PUGL_KEY_PAD_SUBTRACT: + return "PAD_SUBTRACT"; + case PUGL_KEY_PAD_DECIMAL: + return "PAD_DECIMAL"; + case PUGL_KEY_PAD_DIVIDE: + return "PAD_DIVIDE"; + } + + return ""; +} + +static inline int +printEvent(const PuglEvent* event, const char* prefix, const bool verbose) +{ +#define PFFMT "%6.1f %6.1f" +#define PIFMT "%5d %5d" +#define PUFMT "%5u %5u" + +#define PRINT(fmt, ...) fprintf(stderr, fmt, __VA_ARGS__) + + switch (event->type) { + case PUGL_NOTHING: + return 0; + case PUGL_REALIZE: + return fprintf(stderr, "%sRealize\n", prefix); + case PUGL_UNREALIZE: + return fprintf(stderr, "%sUnrealize\n", prefix); + case PUGL_KEY_PRESS: + return (PRINT("%sKey press code %3u key U+%04X (%s) ", + prefix, + event->key.keycode, + event->key.key, + keyString(event->key.key)) + + printModifiers(event->scroll.state)); + case PUGL_KEY_RELEASE: + return (PRINT("%sKey release code %3u key U+%04X (%s) ", + prefix, + event->key.keycode, + event->key.key, + keyString(event->key.key)) + + printModifiers(event->scroll.state)); + case PUGL_TEXT: + return PRINT("%sText entry code %3u char U+%04X (%s)\n", + prefix, + event->text.keycode, + event->text.character, + event->text.string); + case PUGL_BUTTON_PRESS: + case PUGL_BUTTON_RELEASE: + return (PRINT("%sMouse %u %s at " PFFMT " ", + prefix, + event->button.button, + (event->type == PUGL_BUTTON_PRESS) ? "down" : "up ", + event->button.x, + event->button.y) + + printModifiers(event->scroll.state)); + case PUGL_SCROLL: + return (PRINT("%sScroll %5.1f %5.1f (%s) at " PFFMT " ", + prefix, + event->scroll.dx, + event->scroll.dy, + scrollDirectionString(event->scroll.direction), + event->scroll.x, + event->scroll.y) + + printModifiers(event->scroll.state)); + case PUGL_POINTER_IN: + return PRINT("%sMouse enter at " PFFMT " (%s)\n", + prefix, + event->crossing.x, + event->crossing.y, + crossingModeString(event->crossing.mode)); + case PUGL_POINTER_OUT: + return PRINT("%sMouse leave at " PFFMT " (%s)\n", + prefix, + event->crossing.x, + event->crossing.y, + crossingModeString(event->crossing.mode)); + case PUGL_FOCUS_IN: + return PRINT( + "%sFocus in (%s)\n", prefix, crossingModeString(event->crossing.mode)); + case PUGL_FOCUS_OUT: + return PRINT( + "%sFocus out (%s)\n", prefix, crossingModeString(event->crossing.mode)); + case PUGL_CLIENT: + return PRINT("%sClient %" PRIXPTR " %" PRIXPTR "\n", + prefix, + event->client.data1, + event->client.data2); + case PUGL_LOOP_ENTER: + return PRINT("%sLoop enter\n", prefix); + case PUGL_LOOP_LEAVE: + return PRINT("%sLoop leave\n", prefix); + case PUGL_DATA_OFFER: + return PRINT("%sData offer\n", prefix); + case PUGL_DATA: + return PRINT("%sData\n", prefix); + default: + break; + } + + if (verbose) { + switch (event->type) { + case PUGL_UPDATE: + return fprintf(stderr, "%sUpdate\n", prefix); + case PUGL_CONFIGURE: + PRINT("%sConfigure " PIFMT " " PUFMT " (", + prefix, + event->configure.x, + event->configure.y, + event->configure.width, + event->configure.height); + for (PuglViewStyleFlags mask = 1U; mask <= PUGL_MAX_VIEW_STYLE_FLAG; + mask <<= 1U) { + if (event->configure.style & mask) { + PRINT(" %s", viewStyleFlagString((PuglViewStyleFlag)mask)); + } + } + PRINT("%s\n", " )"); + return 0; + case PUGL_EXPOSE: + return PRINT("%sExpose " PIFMT " " PUFMT "\n", + prefix, + event->expose.x, + event->expose.y, + event->expose.width, + event->expose.height); + case PUGL_CLOSE: + return PRINT("%sClose\n", prefix); + case PUGL_MOTION: + return PRINT("%sMouse motion at " PFFMT "\n", + prefix, + event->motion.x, + event->motion.y); + case PUGL_TIMER: + return PRINT("%sTimer %" PRIuPTR "\n", prefix, event->timer.id); + default: + return PRINT("%sUnknown event type %d\n", prefix, (int)event->type); + } + } + +#undef PRINT +#undef PUFMT +#undef PIFMT +#undef PFFMT + + return 0; +} + +static inline const char* +puglViewHintString(const PuglViewHint hint) +{ + switch (hint) { + case PUGL_CONTEXT_API: + return "Context API"; + case PUGL_CONTEXT_VERSION_MAJOR: + return "Context major version"; + case PUGL_CONTEXT_VERSION_MINOR: + return "Context minor version"; + case PUGL_CONTEXT_PROFILE: + return "Context profile"; + case PUGL_CONTEXT_DEBUG: + return "Context debug"; + case PUGL_RED_BITS: + return "Red bits"; + case PUGL_GREEN_BITS: + return "Green bits"; + case PUGL_BLUE_BITS: + return "Blue bits"; + case PUGL_ALPHA_BITS: + return "Alpha bits"; + case PUGL_DEPTH_BITS: + return "Depth bits"; + case PUGL_STENCIL_BITS: + return "Stencil bits"; + case PUGL_SAMPLE_BUFFERS: + return "Sample buffers"; + case PUGL_SAMPLES: + return "Samples"; + case PUGL_DOUBLE_BUFFER: + return "Double buffer"; + case PUGL_SWAP_INTERVAL: + return "Swap interval"; + case PUGL_RESIZABLE: + return "Resizable"; + case PUGL_IGNORE_KEY_REPEAT: + return "Ignore key repeat"; + case PUGL_REFRESH_RATE: + return "Refresh rate"; + case PUGL_VIEW_TYPE: + return "View type"; + case PUGL_DARK_FRAME: + return "Dark frame"; + } + + return "Unknown"; +} + +static inline void +printViewHints(const PuglView* view) +{ + for (unsigned i = 0; i < PUGL_NUM_VIEW_HINTS; ++i) { + fprintf(stderr, + "%s: %d\n", + puglViewHintString((PuglViewHint)i), + puglGetViewHint(view, (PuglViewHint)i)); + } +} + +static inline void +puglPrintTestUsage(const char* prog, const char* posHelp) +{ + printf("Usage: %s [OPTION]... %s\n\n" + " -E Use OpenGL ES\n" + " -G OpenGL context version\n" + " -a Enable anti-aliasing\n" + " -b Block and only update on user input\n" + " -d Directly draw to window (no double-buffering)\n" + " -e Enable platform error-checking\n" + " -f Fast drawing, explicitly disable vertical sync\n" + " -h Display this help\n" + " -i Ignore key repeat\n" + " -v Print verbose output\n" + " -r Resizable window\n" + " -s Explicitly enable vertical sync\n", + prog, + posHelp); +} + +static inline PuglTestOptions +puglParseTestOptions(int* pargc, char*** pargv) +{ + PuglTestOptions opts = { + 0, + PUGL_TRUE, + PUGL_DONT_CARE, + PUGL_OPENGL_API, + 3, + 3, + true, + false, + false, + false, + false, + false, + }; + + char** const argv = *pargv; + int i = 1; + for (; i < *pargc; ++i) { + if (!strcmp(argv[i], "-E")) { + opts.glApi = PUGL_OPENGL_ES_API; + } else if (!strcmp(argv[i], "-G")) { + if (++i == *pargc) { + fprintf(stderr, "error: Missing OpenGL version argument\n"); + return opts; + } + + const int matches = + sscanf(argv[i], "%d.%d", &opts.glMajorVersion, &opts.glMinorVersion); + if (matches != 2) { + fprintf(stderr, "error: Invalid OpenGL version argument\n"); + return opts; + } + } else if (!strcmp(argv[i], "-a")) { + opts.samples = 4; + } else if (!strcmp(argv[i], "-b")) { + opts.continuous = false; + } else if (!strcmp(argv[i], "-d")) { + opts.doubleBuffer = PUGL_FALSE; + } else if (!strcmp(argv[i], "-e")) { + opts.errorChecking = PUGL_TRUE; + } else if (!strcmp(argv[i], "-f")) { + opts.sync = PUGL_FALSE; + } else if (!strcmp(argv[i], "-h")) { + opts.help = true; + return opts; + } else if (!strcmp(argv[i], "-i")) { + opts.ignoreKeyRepeat = true; + } else if (!strcmp(argv[i], "-r")) { + opts.resizable = true; + } else if (!strcmp(argv[i], "-s")) { + opts.sync = PUGL_TRUE; + } else if (!strcmp(argv[i], "-v")) { + opts.verbose = true; + } else if (argv[i][0] != '-') { + break; + } else { + opts.help = true; + logError("Unknown option: %s\n", argv[i]); + } + } + + *pargc -= i; + *pargv += i; + + return opts; +} + +#endif // TEST_TEST_UTILS_H diff --git a/subprojects/puglutil/meson.build b/subprojects/puglutil/meson.build new file mode 100644 index 0000000..e7ffece --- /dev/null +++ b/subprojects/puglutil/meson.build @@ -0,0 +1,14 @@ +# Copyright 2025 David Robillard <d@drobilla.net> +# SPDX-License-Identifier: 0BSD OR ISC + +project( + 'puglutil', + ['c'], + license: 'ISC', + meson_version: '>= 0.54.0', + version: '0.0.0', +) + +puglutil_dep = declare_dependency( + include_directories: include_directories(['include']), +) |