aboutsummaryrefslogtreecommitdiffstats
path: root/subprojects/puglutil
diff options
context:
space:
mode:
Diffstat (limited to 'subprojects/puglutil')
-rw-r--r--subprojects/puglutil/include/puglutil/test_utils.h571
-rw-r--r--subprojects/puglutil/meson.build14
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']),
+)