diff --git a/libc3/struct.c b/libc3/struct.c
new file mode 100644
index 0000000..10df4c1
--- /dev/null
+++ b/libc3/struct.c
@@ -0,0 +1,37 @@
+/* c3
+ * Copyright 2022,2023 kmx.io <contact@kmx.io>
+ *
+ * Permission is hereby granted to use this software granted the above
+ * copyright notice and this permission paragraph are included in all
+ * copies and substantial portions of this software.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS-IS" WITHOUT ANY GUARANTEE OF
+ * PURPOSE AND PERFORMANCE. IN NO EVENT WHATSOEVER SHALL THE
+ * AUTHOR BE CONSIDERED LIABLE FOR THE USE AND PERFORMANCE OF
+ * THIS SOFTWARE.
+ */
+#include "struct.h"
+
+void struct_clean (s_struct *s)
+{
+}
+
+void struct_delete (s_struct *s)
+{
+}
+
+s_struct * struct_init (s_struct *s)
+{
+}
+
+s_struct * struct_init_1 (s_struct *s, const s8 *p)
+{
+}
+
+s_struct * struct_new (void)
+{
+}
+
+s_struct * struct_new_1 (const s8 *p)
+{
+}
diff --git a/libc3/struct.h b/libc3/struct.h
index 2730d92..63384c2 100644
--- a/libc3/struct.h
+++ b/libc3/struct.h
@@ -12,28 +12,22 @@
*/
/**
* @file tag.h
- * @brief Struct type : %Module{key: value, ...}
+ * @brief Struct : %Module{key: value, ...}
*
- * A structure type is defined using defstruct.
- * There can be only one defstruct per module.
- * The struct type can be referred to by just the module name.
- * The rest of the module should contain operations on that struct.
*/
#ifndef LIBC3_STRUCT_H
#define LIBC3_STRUCT_H
-#include <stdarg.h>
-#include <stdio.h>
#include "types.h"
/* Stack-allocation compatible functions, call struct_clean after use. */
void struct_clean (s_struct *s);
-s_struct * struct_init (s_struct *s);
+s_struct * struct_init (s_struct *s, const s_sym *module);
s_struct * struct_init_1 (s_struct *s, const s8 *p);
/* Heap-allocation functions, call struct_delete after use. */
void struct_delete (s_struct *s);
-s_struct * struct_new (void);
+s_struct * struct_new (uw count);
s_struct * struct_new_1 (const s8 *p);
#endif /* LIBC3_STRUCT_H */
diff --git a/libc3/struct_type.h b/libc3/struct_type.h
new file mode 100644
index 0000000..4723017
--- /dev/null
+++ b/libc3/struct_type.h
@@ -0,0 +1,38 @@
+/* c3
+ * Copyright 2022,2023 kmx.io <contact@kmx.io>
+ *
+ * Permission is hereby granted to use this software granted the above
+ * copyright notice and this permission paragraph are included in all
+ * copies and substantial portions of this software.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS-IS" WITHOUT ANY GUARANTEE OF
+ * PURPOSE AND PERFORMANCE. IN NO EVENT WHATSOEVER SHALL THE
+ * AUTHOR BE CONSIDERED LIABLE FOR THE USE AND PERFORMANCE OF
+ * THIS SOFTWARE.
+ */
+/**
+ * @file struct_type.h
+ * @brief Struct type : [key: value, ...]
+ *
+ * A structure type is defined using defstruct.
+ * There can be only one defstruct per module.
+ * The struct type can be referred to by just the module name.
+ * The rest of the module should contain operations on that struct.
+ */
+#ifndef LIBC3_STRUCT_TYPE_H
+#define LIBC3_STRUCT_TYPE_H
+
+#include "types.h"
+
+/* Stack-allocation compatible functions, call struct_type_clean after
+ use. */
+void struct_type_clean (s_struct_type *s);
+s_struct_type * struct_type_init (s_struct_type *s, const s_sym *module);
+s_struct_type * struct_type_init_1 (s_struct_type *s, const s8 *p);
+
+/* Heap-allocation functions, call struct_type_delete after use. */
+void struct_type_delete (s_struct_type *s);
+s_struct_type * struct_type_new (const s_sym *module);
+s_struct_type * struct_type_new_1 (const s8 *p);
+
+#endif /* LIBC3_STRUCT_TYPE_H */
diff --git a/libc3/window/cairo/xcb/demo/Makefile b/libc3/window/cairo/xcb/demo/Makefile
index 34cde24..6565300 100644
--- a/libc3/window/cairo/xcb/demo/Makefile
+++ b/libc3/window/cairo/xcb/demo/Makefile
@@ -9,8 +9,6 @@
## PURPOSE AND PERFORMANCE. IN NO EVENT WHATSOEVER SHALL THE
## AUTHOR BE CONSIDERED LIABLE FOR THE USE AND PERFORMANCE OF
## THIS SOFTWARE.
-include config.mk
-include sources.mk
CLEANFILES = *.a ${PROG} ${PROG_ASAN} ${PROG_COV} ${PROG_DEBUG} *.css \
*.gcno *.html *.o .libs *.lo
@@ -20,7 +18,8 @@ CLEANFILES += ${CLEANFILES_COV}
DISTCLEANFILES = ${CLEANFILES} config.mk
-build: ${PROG}
+build:
+ ${MAKE} ${PROG}
all:
${MAKE} build
@@ -59,10 +58,15 @@ install:
.PHONY: \
all \
asan \
+ build \
clean \
clean_cov \
cov \
debug \
demo \
distclean \
- gdb_demo
+ gdb_demo \
+ install
+
+include config.mk
+include sources.mk
diff --git a/libc3/window/configure b/libc3/window/configure
index 92a6757..89fd38b 100755
--- a/libc3/window/configure
+++ b/libc3/window/configure
@@ -139,9 +139,19 @@ else
fi
echo "HAVE_CAIRO = $HAVE_CAIRO" >> ${CONFIG_MK}
+if pkg-config sdl2; then
+ HAVE_SDL2=true
+else
+ HAVE_SDL2=false
+fi
+echo "HAVE_SDL2 = $HAVE_SDL2" >> ${CONFIG_MK}
+
update_config_mk
env_reset
if ${HAVE_CAIRO}; then
( cd cairo && ./configure; )
fi
+if ${HAVE_SDL2}; then
+ ( cd sdl2 && ./configure; )
+fi
diff --git a/libc3/window/sdl2/Makefile b/libc3/window/sdl2/Makefile
new file mode 100644
index 0000000..315d5ae
--- /dev/null
+++ b/libc3/window/sdl2/Makefile
@@ -0,0 +1,85 @@
+## c3
+## Copyright 2022,2023 kmx.io <contact@kmx.io>
+##
+## Permission is hereby granted to use this software granted the above
+## copyright notice and this permission paragraph are included in all
+## copies and substantial portions of this software.
+##
+## THIS SOFTWARE IS PROVIDED "AS-IS" WITHOUT ANY GUARANTEE OF
+## PURPOSE AND PERFORMANCE. IN NO EVENT WHATSOEVER SHALL THE
+## AUTHOR BE CONSIDERED LIABLE FOR THE USE AND PERFORMANCE OF
+## THIS SOFTWARE.
+
+build:
+ ${MAKE} ${LIB}
+ ${MAKE} -C demo build
+
+all:
+ ${MAKE} build
+
+asan:
+ ${MAKE} ${LIB_ASAN}
+ ${MAKE} -C demo asan
+
+clean:
+ ${MAKE} -C demo clean
+
+clean_cov:
+ ${MAKE} -C demo clean_cov
+
+cov:
+ ${MAKE} ${LIB_COV}
+ ${MAKE} -C demo cov
+
+debug:
+ ${MAKE} ${LIB_DEBUG}
+ ${MAKE} -C demo debug
+
+demo: build
+ ${MAKE} -C demo demo
+
+demo_debug: debug
+ ${MAKE} -C demo demo_debug
+
+distclean:
+ rm -rf ${DISTCLEANFILES}
+ ${MAKE} -C demo distclean
+
+gcovr:
+ ${MAKE} -C demo gcovr
+
+gdb_demo: debug
+ ${MAKE} -C demo gdb_demo
+
+install:
+ ${INSTALL} -o ${OWNER} -g ${GROUP} -m 0755 -d ${prefix}/include/libc3/window/sdl2
+ ${LIBTOOL} --tag=CC --mode=install ${INSTALL} -o ${OWNER} -g ${GROUP} -m 0644 ${HEADERS} ${prefix}/include/libc3/window/sdl2
+ ${INSTALL} -o ${OWNER} -g ${GROUP} -m 0755 -d ${prefix}/lib
+ ${LIBTOOL} --tag=CC --mode=install ${INSTALL} -o ${OWNER} -g ${GROUP} -m 0644 ${LIB} ${prefix}/lib
+ ${LIBTOOL} --finish ${prefix}/lib
+
+lldb_demo: debug
+ ${MAKE} -C demo lldb_demo
+
+test:
+ ${MAKE} -C demo test
+
+update_sources:
+ ./update_sources
+
+.PHONY: \
+ all \
+ asan \
+ build \
+ clean \
+ cov \
+ debug \
+ demo \
+ distclean \
+ gdb_demo \
+ install \
+ test \
+ update_sources
+
+include config.mk
+include sources.mk
diff --git a/libc3/window/sdl2/configure b/libc3/window/sdl2/configure
new file mode 100755
index 0000000..7dee600
--- /dev/null
+++ b/libc3/window/sdl2/configure
@@ -0,0 +1,141 @@
+#!/bin/sh
+## c3
+## Copyright 2022,2023 kmx.io <contact@kmx.io>
+##
+## Permission is hereby granted to use this software granted the above
+## copyright notice and this permission paragraph are included in all
+## copies and substantial portions of this software.
+##
+## THIS SOFTWARE IS PROVIDED "AS-IS" WITHOUT ANY GUARANTEE OF
+## PURPOSE AND PERFORMANCE. IN NO EVENT WHATSOEVER SHALL THE
+## AUTHOR BE CONSIDERED LIABLE FOR THE USE AND PERFORMANCE OF
+## THIS SOFTWARE.
+
+set -e
+
+export SRC_TOP="$(dirname "$PWD")"
+
+. ../../../config.subr
+
+LIB=libc3_window_sdl2.la
+LIB_ASAN=libc3_window_sdl2_asan.la
+LIB_COV=libc3_window_sdl2_cov.la
+LIB_DEBUG=libc3_window_sdl2_debug.la
+
+echo "LIB = $LIB" >> ${CONFIG_MK}
+echo "LIB_ASAN = $LIB_ASAN" >> ${CONFIG_MK}
+echo "LIB_COV = $LIB_COV" >> ${CONFIG_MK}
+echo "LIB_DEBUG = $LIB_DEBUG" >> ${CONFIG_MK}
+
+. ./sources.sh
+
+OBJECTS="$(c2ext .main.lo "$SOURCES")"
+echo "OBJECTS = $OBJECTS" >> ${CONFIG_MK}
+OBJECTS_ASAN="$(c2ext .asan.lo "$SOURCES")"
+OBJECTS_COV="$(c2ext .cov.lo "$SOURCES")"
+OBJECTS_DEBUG="$(c2ext .debug.lo "$SOURCES")"
+
+# Common config for all targets
+CPPFLAGS="-I../../../libffi/include -I../../.. $CPPFLAGS"
+CPPFLAGS="$CPPFLAGS -DGL_SILENCE_DEPRECATION=1"
+CFLAGS="$CFLAGS -W -Wall -Werror -std=c99 -pedantic"
+LDFLAGS="--shared -no-undefined ${LDFLAGS}"
+LIBS="$LIBS -rpath ${PREFIX}/lib"
+config_asan
+config_gnu
+pkg_config glesv2
+pkg_config sdl2
+pkg_config libbsd-overlay
+config_define PREFIX "\"${PREFIX}\""
+
+# Address Sanitizer config
+CFLAGS_ASAN="$CFLAGS -O1 -fsanitize=address -fno-omit-frame-pointer -g"
+LDFLAGS_ASAN="$LDFLAGS"
+LIBS_LOCAL_ASAN="../libc3_window_asan.la"
+LIBS_ASAN="$LIBS_LOCAL_ASAN $LIBS"
+
+# Coverage config
+CFLAGS_COV="$CFLAGS -ftest-coverage -fprofile-arcs -fprofile-generate"
+LDFLAGS_COV="$LDFLAGS --coverage"
+LIBS_LOCAL_COV="../libc3_window_cov.la"
+LIBS_COV="$LIBS_LOCAL_COV $LIBS"
+
+# Debug config
+CFLAGS_DEBUG="$CFLAGS -DDEBUG -O0 -g"
+LDFLAGS_DEBUG="$LDFLAGS"
+LIBS_LOCAL_DEBUG="../libc3_window_debug.la"
+LIBS_DEBUG="$LIBS_LOCAL_DEBUG $LIBS"
+
+# Main config
+DEFAULT_CFLAGS="-O2 -pipe"
+if [ "x$ENV_CFLAGS" = "x" ]; then
+ CFLAGS="$CFLAGS $DEFAULT_CFLAGS"
+fi
+CFLAGS="$CFLAGS -DNDEBUG"
+LIBS_LOCAL="../libc3_window.la"
+LIBS="$LIBS_LOCAL $LIBS"
+
+echo "LIB = $LIB" >> ${CONFIG_MK}
+echo "HAVE_ASAN = $HAVE_ASAN" >> ${CONFIG_MK}
+echo "CPPFLAGS = $CPPFLAGS" >> ${CONFIG_MK}
+echo "CFLAGS = $CFLAGS" >> ${CONFIG_MK}
+echo "LDFLAGS = $LDFLAGS" >> ${CONFIG_MK}
+echo "LIBS = $LIBS" >> ${CONFIG_MK}
+echo >> ${CONFIG_MK}
+echo "LIB_ASAN = $LIB_ASAN" >> ${CONFIG_MK}
+echo "CFLAGS_ASAN = $CFLAGS_ASAN" >> ${CONFIG_MK}
+echo "LDFLAGS_ASAN = $LDFLAGS_ASAN" >> ${CONFIG_MK}
+echo "LIBS_ASAN = $LIBS_ASAN" >> ${CONFIG_MK}
+echo >> ${CONFIG_MK}
+echo "LIB_COV = $LIB_COV" >> ${CONFIG_MK}
+echo "CFLAGS_COV = $CFLAGS_COV" >> ${CONFIG_MK}
+echo "LDFLAGS_COV = $LDFLAGS_COV" >> ${CONFIG_MK}
+echo "LIBS_COV = $LIBS_COV" >> ${CONFIG_MK}
+echo >> ${CONFIG_MK}
+echo "LIB_DEBUG = $LIB_DEBUG" >> ${CONFIG_MK}
+echo "CFLAGS_DEBUG = $CFLAGS_DEBUG" >> ${CONFIG_MK}
+echo "LDFLAGS_DEBUG = $LDFLAGS_DEBUG" >> ${CONFIG_MK}
+echo "LIBS_DEBUG = $LIBS_DEBUG" >> ${CONFIG_MK}
+
+echo >> ${CONFIG_MK}
+echo "$LIB: $LIBS_LOCAL $OBJECTS" >> ${CONFIG_MK}
+echo " ${LIBTOOL} --tag=CC --mode=link \${CC} -shared \${LDFLAGS} ${OBJECTS} \${LIBS} -rpath ${LIBDIR} -o ${LIB}" >> ${CONFIG_MK}
+
+echo >> ${CONFIG_MK}
+echo "$LIB_ASAN: $LIBS_LOCAL_ASAN $OBJECTS_ASAN" >> ${CONFIG_MK}
+echo " ${LIBTOOL} --tag=CC --mode=link \${CC} -shared \${LDFLAGS_ASAN} ${OBJECTS_ASAN} \${LIBS_ASAN} -rpath ${LIBDIR} -o ${LIB_ASAN}" >> ${CONFIG_MK}
+
+echo >> ${CONFIG_MK}
+echo "$LIB_COV: $LIBS_LOCAL_COV $OBJECTS_COV" >> ${CONFIG_MK}
+echo " ${LIBTOOL} --tag=CC --mode=link \${CC} -shared \${LDFLAGS_COV} ${OBJECTS_COV} \${LIBS_COV} -rpath ${LIBDIR} -o ${LIB_COV}" >> ${CONFIG_MK}
+
+echo >> ${CONFIG_MK}
+echo "$LIB_DEBUG: $LIBS_LOCAL_DEBUG $OBJECTS_DEBUG" >> ${CONFIG_MK}
+echo " ${LIBTOOL} --tag=CC --mode=link \${CC} -shared \${LDFLAGS_DEBUG} ${OBJECTS_DEBUG} \${LIBS_DEBUG} -rpath ${LIBDIR} -o ${LIB_DEBUG}" >> ${CONFIG_MK}
+
+for SRC in $SOURCES; do
+ echo >> ${CONFIG_MK}
+ SRC_LO="$(c2ext .main.lo "$SRC")"
+ c_ext_rule .main.lo "$SRC" >> ${CONFIG_MK}
+ echo " ${LIBTOOL} --tag=CC --mode=compile \${CC} \${CPPFLAGS} \${CFLAGS} -c $SRC -o $SRC_LO" >> ${CONFIG_MK}
+
+ echo >> ${CONFIG_MK}
+ SRC_ASAN_LO="$(c2ext .asan.lo "$SRC")"
+ c_ext_rule .asan.lo "$SRC" >> ${CONFIG_MK}
+ echo " ${LIBTOOL} --tag=CC --mode=compile \${CC} \${CPPFLAGS} \${CFLAGS_ASAN} -c $SRC -o $SRC_ASAN_LO" >> ${CONFIG_MK}
+
+ echo >> ${CONFIG_MK}
+ SRC_COV_LO="$(c2ext .cov.lo "$SRC")"
+ c_ext_rule .cov.lo "$SRC" >> ${CONFIG_MK}
+ echo " ${LIBTOOL} --tag=CC --mode=compile \${CC} \${CPPFLAGS} \${CFLAGS_COV} -c $SRC -o $SRC_COV_LO" >> ${CONFIG_MK}
+
+ echo >> ${CONFIG_MK}
+ SRC_DEBUG_LO="$(c2ext .debug.lo "$SRC")"
+ c_ext_rule .debug.lo "$SRC" >> ${CONFIG_MK}
+ echo " ${LIBTOOL} --tag=CC --mode=compile \${CC} \${CPPFLAGS} \${CFLAGS_DEBUG} -c $SRC -o $SRC_DEBUG_LO" >> ${CONFIG_MK}
+done
+
+update_config_mk
+env_reset
+
+( cd demo && ./configure; )
diff --git a/libc3/window/sdl2/demo/Makefile b/libc3/window/sdl2/demo/Makefile
new file mode 100644
index 0000000..6565300
--- /dev/null
+++ b/libc3/window/sdl2/demo/Makefile
@@ -0,0 +1,72 @@
+## c3
+## Copyright 2022,2023 kmx.io <contact@kmx.io>
+##
+## Permission is hereby granted to use this software granted the above
+## copyright notice and this permission paragraph are included in all
+## copies and substantial portions of this software.
+##
+## THIS SOFTWARE IS PROVIDED "AS-IS" WITHOUT ANY GUARANTEE OF
+## PURPOSE AND PERFORMANCE. IN NO EVENT WHATSOEVER SHALL THE
+## AUTHOR BE CONSIDERED LIABLE FOR THE USE AND PERFORMANCE OF
+## THIS SOFTWARE.
+
+CLEANFILES = *.a ${PROG} ${PROG_ASAN} ${PROG_COV} ${PROG_DEBUG} *.css \
+ *.gcno *.html *.o .libs *.lo
+
+CLEANFILES_COV = *.css *.gcda *.html .libs/*.gcda
+CLEANFILES += ${CLEANFILES_COV}
+
+DISTCLEANFILES = ${CLEANFILES} config.mk
+
+build:
+ ${MAKE} ${PROG}
+
+all:
+ ${MAKE} build
+ if ${HAVE_GCOV}; then ${MAKE} cov; fi
+ ${MAKE} debug
+ if ${HAVE_ASAN}; then ${MAKE} asan; fi
+
+asan: ${PROG_ASAN}
+
+clean:
+ rm -rf ${CLEANFILES}
+
+clean_cov:
+ rm -rf ${CLEANFILES_COV}
+
+cov: ${PROG_COV}
+
+debug: ${PROG_DEBUG}
+
+demo: ${PROG}
+ time ./${PROG}
+
+distclean:
+ rm -rf ${DISTCLEANFILES}
+
+gcovr:
+ gcovr --gcov-executable ${GCOV} --html-details ${PROG}.html
+
+gdb_demo: debug
+ if [ -f ${PROG_DEBUG}.core ]; then gdb .libs/${PROG_DEBUG} ${PROG_DEBUG}.core; else gdb .libs/${PROG_DEBUG}; fi
+
+install:
+ install -m 755 -d ${prefix}/bin
+ install -m 755 ${PROG} ${prefix}/bin/${PROG}
+
+.PHONY: \
+ all \
+ asan \
+ build \
+ clean \
+ clean_cov \
+ cov \
+ debug \
+ demo \
+ distclean \
+ gdb_demo \
+ install
+
+include config.mk
+include sources.mk
diff --git a/libc3/window/sdl2/demo/bg_rect.c b/libc3/window/sdl2/demo/bg_rect.c
new file mode 100644
index 0000000..7c6256e
--- /dev/null
+++ b/libc3/window/sdl2/demo/bg_rect.c
@@ -0,0 +1,56 @@
+/* c3
+ * Copyright 2022,2023 kmx.io <contact@kmx.io>
+ *
+ * Permission is hereby granted to use this software granted the above
+ * copyright notice and this permission paragraph are included in all
+ * copies and substantial portions of this software.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS-IS" WITHOUT ANY GUARANTEE OF
+ * PURPOSE AND PERFORMANCE. IN NO EVENT WHATSOEVER SHALL THE
+ * AUTHOR BE CONSIDERED LIABLE FOR THE USE AND PERFORMANCE OF
+ * THIS SOFTWARE.
+ */
+#include <assert.h>
+#include <math.h>
+#include <libc3/c3.h>
+#include "../types.h"
+#include "bg_rect.h"
+
+bool bg_rect_load (s_sequence *seq, s_window_sdl2 *window)
+{
+ (void) seq;
+ (void) window;
+ return true;
+}
+
+bool bg_rect_render (s_sequence *seq, s_window_sdl2 *window,
+ SDL_GLContext context)
+{
+ (void) context;
+#define BG_RECT_COLOR_MAX 8
+ const s_rgb color[BG_RECT_COLOR_MAX] = {
+ {1, 1, 1},
+ {1, 0, 0},
+ {1, 1, 0},
+ {0, 1, 0},
+ {0, 1, 1},
+ {0, 0, 1},
+ {1, 0, 1},
+ {0, 0, 0}
+ };
+ u8 c1;
+ u8 c2;
+ double p;
+ double q;
+ s_rgb rgb;
+ c1 = (u8) trunc(seq->t) % BG_RECT_COLOR_MAX;
+ c2 = (u8) trunc(seq->t + 1.0) % BG_RECT_COLOR_MAX;
+ p = fmod(seq->t, 1.0);
+ q = 1.0 - p;
+ rgb.r = color[c1].r * q + color[c2].r * p;
+ rgb.g = color[c1].g * q + color[c2].g * p;
+ rgb.b = color[c1].b * q + color[c2].b * p;
+ glColor3d(rgb.r, rgb.g, rgb.b);
+ glRectd(0, 0, window->w, window->h);
+ return true;
+}
diff --git a/libc3/window/sdl2/demo/bg_rect.h b/libc3/window/sdl2/demo/bg_rect.h
new file mode 100644
index 0000000..6bc4a8b
--- /dev/null
+++ b/libc3/window/sdl2/demo/bg_rect.h
@@ -0,0 +1,22 @@
+/* c3
+ * Copyright 2022,2023 kmx.io <contact@kmx.io>
+ *
+ * Permission is hereby granted to use this software granted the above
+ * copyright notice and this permission paragraph are included in all
+ * copies and substantial portions of this software.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS-IS" WITHOUT ANY GUARANTEE OF
+ * PURPOSE AND PERFORMANCE. IN NO EVENT WHATSOEVER SHALL THE
+ * AUTHOR BE CONSIDERED LIABLE FOR THE USE AND PERFORMANCE OF
+ * THIS SOFTWARE.
+ */
+#ifndef BG_RECT_H
+#define BG_RECT_H
+
+#include "../types.h"
+
+bool bg_rect_load (s_sequence *seq, s_window_sdl2 *window);
+bool bg_rect_render (s_sequence *seq, s_window_sdl2 *window,
+ SDL_GLContext context);
+
+#endif /* BG_RECT_H */
diff --git a/libc3/window/sdl2/demo/c3_window_sdl2_demo b/libc3/window/sdl2/demo/c3_window_sdl2_demo
new file mode 100755
index 0000000..43522bc
--- /dev/null
+++ b/libc3/window/sdl2/demo/c3_window_sdl2_demo
@@ -0,0 +1,210 @@
+#! /bin/sh
+
+# c3_window_sdl2_demo - temporary wrapper script for .libs/c3_window_sdl2_demo
+# Generated by libtool (GNU libtool) 2.4.7
+#
+# The c3_window_sdl2_demo program cannot be directly executed until all the libtool
+# libraries that it depends on are installed.
+#
+# This wrapper script should never be moved out of the build directory.
+# If it is, it will not operate correctly.
+
+# Sed substitution that helps us do robust quoting. It backslashifies
+# metacharacters that are still active within double-quoted strings.
+sed_quote_subst='s|\([`"$\\]\)|\\\1|g'
+
+# Be Bourne compatible
+if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then
+ emulate sh
+ NULLCMD=:
+ # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which
+ # is contrary to our usage. Disable this feature.
+ alias -g '${1+"$@"}'='"$@"'
+ setopt NO_GLOB_SUBST
+else
+ case `(set -o) 2>/dev/null` in *posix*) set -o posix;; esac
+fi
+BIN_SH=xpg4; export BIN_SH # for Tru64
+DUALCASE=1; export DUALCASE # for MKS sh
+
+# The HP-UX ksh and POSIX shell print the target directory to stdout
+# if CDPATH is set.
+(unset CDPATH) >/dev/null 2>&1 && unset CDPATH
+
+relink_command=""
+
+# This environment variable determines our operation mode.
+if test "$libtool_install_magic" = "%%%MAGIC variable%%%"; then
+ # install mode needs the following variables:
+ generated_by_libtool_version='2.4.7'
+ notinst_deplibs=' ../../../libc3.la ../../libc3_window.la ../libc3_window_sdl2.la /Users/thodg/c/thodg/c3-lang/c3/libc3/window/libc3_window.la /Users/thodg/c/thodg/c3-lang/c3/libc3/libc3.la /Users/thodg/c/thodg/c3-lang/c3/libffi/libffi.la'
+else
+ # When we are sourced in execute mode, $file and $ECHO are already set.
+ if test "$libtool_execute_magic" != "%%%MAGIC variable%%%"; then
+ file="$0"
+
+# A function that is used when there is no print builtin or printf.
+func_fallback_echo ()
+{
+ eval 'cat <<_LTECHO_EOF
+$1
+_LTECHO_EOF'
+}
+ ECHO="printf %s\\n"
+ fi
+
+# Very basic option parsing. These options are (a) specific to
+# the libtool wrapper, (b) are identical between the wrapper
+# /script/ and the wrapper /executable/ that is used only on
+# windows platforms, and (c) all begin with the string --lt-
+# (application programs are unlikely to have options that match
+# this pattern).
+#
+# There are only two supported options: --lt-debug and
+# --lt-dump-script. There is, deliberately, no --lt-help.
+#
+# The first argument to this parsing function should be the
+# script's /opt/homebrew/bin/glibtool value, followed by no.
+lt_option_debug=
+func_parse_lt_options ()
+{
+ lt_script_arg0=$0
+ shift
+ for lt_opt
+ do
+ case "$lt_opt" in
+ --lt-debug) lt_option_debug=1 ;;
+ --lt-dump-script)
+ lt_dump_D=`$ECHO "X$lt_script_arg0" | /usr/bin/sed -e 's/^X//' -e 's%/[^/]*$%%'`
+ test "X$lt_dump_D" = "X$lt_script_arg0" && lt_dump_D=.
+ lt_dump_F=`$ECHO "X$lt_script_arg0" | /usr/bin/sed -e 's/^X//' -e 's%^.*/%%'`
+ cat "$lt_dump_D/$lt_dump_F"
+ exit 0
+ ;;
+ --lt-*)
+ $ECHO "Unrecognized --lt- option: '$lt_opt'" 1>&2
+ exit 1
+ ;;
+ esac
+ done
+
+ # Print the debug banner immediately:
+ if test -n "$lt_option_debug"; then
+ echo "c3_window_sdl2_demo:c3_window_sdl2_demo:$LINENO: libtool wrapper (GNU libtool) 2.4.7" 1>&2
+ fi
+}
+
+# Used when --lt-debug. Prints its arguments to stdout
+# (redirection is the responsibility of the caller)
+func_lt_dump_args ()
+{
+ lt_dump_args_N=1;
+ for lt_arg
+ do
+ $ECHO "c3_window_sdl2_demo:c3_window_sdl2_demo:$LINENO: newargv[$lt_dump_args_N]: $lt_arg"
+ lt_dump_args_N=`expr $lt_dump_args_N + 1`
+ done
+}
+
+# Core function for launching the target application
+func_exec_program_core ()
+{
+
+ if test -n "$lt_option_debug"; then
+ $ECHO "c3_window_sdl2_demo:c3_window_sdl2_demo:$LINENO: newargv[0]: $progdir/$program" 1>&2
+ func_lt_dump_args ${1+"$@"} 1>&2
+ fi
+ exec "$progdir/$program" ${1+"$@"}
+
+ $ECHO "$0: cannot exec $program $*" 1>&2
+ exit 1
+}
+
+# A function to encapsulate launching the target application
+# Strips options in the --lt-* namespace from $@ and
+# launches target application with the remaining arguments.
+func_exec_program ()
+{
+ case " $* " in
+ *\ --lt-*)
+ for lt_wr_arg
+ do
+ case $lt_wr_arg in
+ --lt-*) ;;
+ *) set x "$@" "$lt_wr_arg"; shift;;
+ esac
+ shift
+ done ;;
+ esac
+ func_exec_program_core ${1+"$@"}
+}
+
+ # Parse options
+ func_parse_lt_options "$0" ${1+"$@"}
+
+ # Find the directory that this script lives in.
+ thisdir=`$ECHO "$file" | /usr/bin/sed 's%/[^/]*$%%'`
+ test "x$thisdir" = "x$file" && thisdir=.
+
+ # Follow symbolic links until we get to the real thisdir.
+ file=`ls -ld "$file" | /usr/bin/sed -n 's/.*-> //p'`
+ while test -n "$file"; do
+ destdir=`$ECHO "$file" | /usr/bin/sed 's%/[^/]*$%%'`
+
+ # If there was a directory component, then change thisdir.
+ if test "x$destdir" != "x$file"; then
+ case "$destdir" in
+ [\\/]* | [A-Za-z]:[\\/]*) thisdir="$destdir" ;;
+ *) thisdir="$thisdir/$destdir" ;;
+ esac
+ fi
+
+ file=`$ECHO "$file" | /usr/bin/sed 's%^.*/%%'`
+ file=`ls -ld "$thisdir/$file" | /usr/bin/sed -n 's/.*-> //p'`
+ done
+
+ # Usually 'no', except on cygwin/mingw when embedded into
+ # the cwrapper.
+ WRAPPER_SCRIPT_BELONGS_IN_OBJDIR=no
+ if test "$WRAPPER_SCRIPT_BELONGS_IN_OBJDIR" = "yes"; then
+ # special case for '.'
+ if test "$thisdir" = "."; then
+ thisdir=`pwd`
+ fi
+ # remove .libs from thisdir
+ case "$thisdir" in
+ *[\\/].libs ) thisdir=`$ECHO "$thisdir" | /usr/bin/sed 's%[\\/][^\\/]*$%%'` ;;
+ .libs ) thisdir=. ;;
+ esac
+ fi
+
+ # Try to get the absolute directory name.
+ absdir=`cd "$thisdir" && pwd`
+ test -n "$absdir" && thisdir="$absdir"
+
+ program='c3_window_sdl2_demo'
+ progdir="$thisdir/.libs"
+
+
+ if test -f "$progdir/$program"; then
+ # Add our own library path to DYLD_LIBRARY_PATH
+ DYLD_LIBRARY_PATH="/Users/thodg/c/thodg/c3-lang/c3/libc3/.libs:/Users/thodg/c/thodg/c3-lang/c3/libc3/window/.libs:/Users/thodg/c/thodg/c3-lang/c3/libc3/window/sdl2/.libs:/Users/thodg/c/thodg/c3-lang/c3/libffi/.libs:$DYLD_LIBRARY_PATH"
+
+ # Some systems cannot cope with colon-terminated DYLD_LIBRARY_PATH
+ # The second colon is a workaround for a bug in BeOS R4 sed
+ DYLD_LIBRARY_PATH=`$ECHO "$DYLD_LIBRARY_PATH" | /usr/bin/sed 's/::*$//'`
+
+ export DYLD_LIBRARY_PATH
+
+ if test "$libtool_execute_magic" != "%%%MAGIC variable%%%"; then
+ # Run the actual program with our arguments.
+ func_exec_program ${1+"$@"}
+ fi
+ else
+ # The program doesn't exist.
+ $ECHO "$0: error: '$progdir/$program' does not exist" 1>&2
+ $ECHO "This script is just a wrapper for $program." 1>&2
+ $ECHO "See the libtool documentation for more information." 1>&2
+ exit 1
+ fi
+fi
diff --git a/libc3/window/sdl2/demo/configure b/libc3/window/sdl2/demo/configure
new file mode 100755
index 0000000..d62b277
--- /dev/null
+++ b/libc3/window/sdl2/demo/configure
@@ -0,0 +1,143 @@
+#!/bin/sh
+## c3
+## Copyright 2022,2023 kmx.io <contact@kmx.io>
+##
+## Permission is hereby granted to use this software granted the above
+## copyright notice and this permission paragraph are included in all
+## copies and substantial portions of this software.
+##
+## THIS SOFTWARE IS PROVIDED "AS-IS" WITHOUT ANY GUARANTEE OF
+## PURPOSE AND PERFORMANCE. IN NO EVENT WHATSOEVER SHALL THE
+## AUTHOR BE CONSIDERED LIABLE FOR THE USE AND PERFORMANCE OF
+## THIS SOFTWARE.
+
+set -e
+
+export SRC_TOP="$(dirname "$PWD")"
+
+. ../../../../config.subr
+
+PROG=c3_window_sdl2_demo
+PROG_ASAN=c3_window_sdl2_demo_asan
+PROG_COV=c3_window_sdl2_demo_cov
+PROG_DEBUG=c3_window_sdl2_demo_debug
+
+echo "PROG = $PROG" >> ${CONFIG_MK}
+echo "PROG_ASAN = $PROG_ASAN" >> ${CONFIG_MK}
+echo "PROG_COV = $PROG_COV" >> ${CONFIG_MK}
+echo "PROG_DEBUG = $PROG_DEBUG" >> ${CONFIG_MK}
+
+. ./sources.sh
+
+OBJECTS="$(c2ext .main.lo "$SOURCES")"
+echo "OBJECTS = $OBJECTS" >> ${CONFIG_MK}
+
+OBJECTS_ASAN="$(c2ext .asan.lo "$SOURCES")"
+echo "OBJECTS_ASAN = $OBJECTS_ASAN" >> ${CONFIG_MK}
+
+if $HAVE_GCOV; then
+ OBJECTS_COV="$(c2ext .cov.lo "$SOURCES")"
+ echo "OBJECTS_COV = $OBJECTS_COV" >> ${CONFIG_MK}
+fi
+
+OBJECTS_DEBUG="$(c2ext .debug.lo "$SOURCES")"
+echo "OBJECTS_DEBUG = $OBJECTS_DEBUG" >> ${CONFIG_MK}
+
+# Common config for all targets
+CPPFLAGS="$CPPFLAGS -I../../../../libffi/include -I../../../.."
+CPPFLAGS="$CPPFLAGS -DGL_SILENCE_DEPRECATION=1"
+CFLAGS="$CFLAGS -W -Wall -Werror -std=c99 -pedantic"
+config_asan
+config_gnu
+pkg_config gl
+pkg_config sdl2
+LIBS="$LIBS"
+
+# Asan config
+CFLAGS_ASAN="$CFLAGS -fsanitize=address -O1 -fno-omit-frame-pointer -g"
+LDFLAGS_ASAN="$LDFLAGS"
+LOCAL_LIBS_ASAN="../../../libc3_asan.la ../../libc3_window_asan.la ../libc3_window_sdl2_asan.la"
+LIBS_ASAN="$LOCAL_LIBS_ASAN $LIBS"
+
+# Coverage config
+CFLAGS_COV="$CFLAGS -ftest-coverage -fprofile-arcs -fprofile-generate"
+LDFLAGS_COV="$LDFLAGS --coverage"
+LOCAL_LIBS_COV="../../../libc3_cov.la ../../libc3_window_cov.la ../libc3_window_sdl2_cov.la"
+LIBS_COV="$LOCAL_LIBS_COV $LIBS"
+
+# Debug config
+CFLAGS_DEBUG="$CFLAGS -DDEBUG -O0 -g"
+LDFLAGS_DEBUG="$LDFLAGS"
+LOCAL_LIBS_DEBUG="../../../libc3_debug.la ../../libc3_window_debug.la ../libc3_window_sdl2_debug.la"
+LIBS_DEBUG="$LOCAL_LIBS_DEBUG $LIBS"
+
+# Main config
+DEFAULT_CFLAGS="-O2 -pipe -fPIC"
+if [ "x$ENV_CFLAGS" = "x" ]; then
+ CFLAGS="$CFLAGS $DEFAULT_CFLAGS"
+fi
+CFLAGS="$CFLAGS -DNDEBUG"
+LOCAL_LIBS="../../../libc3.la ../../libc3_window.la ../libc3_window_sdl2.la"
+LIBS="$LOCAL_LIBS $LIBS"
+
+echo "HAVE_ASAN = $HAVE_ASAN" >> ${CONFIG_MK}
+echo "CPPFLAGS = $CPPFLAGS" >> ${CONFIG_MK}
+echo "CFLAGS = $CFLAGS" >> ${CONFIG_MK}
+echo "LDFLAGS = $LDFLAGS" >> ${CONFIG_MK}
+echo "LIBS = $LIBS" >> ${CONFIG_MK}
+echo >> ${CONFIG_MK}
+echo "CFLAGS_ASAN = $CFLAGS_ASAN" >> ${CONFIG_MK}
+echo "LDFLAGS_ASAN = $LDFLAGS_ASAN" >> ${CONFIG_MK}
+echo "LIBS_ASAN = $LIBS_ASAN" >> ${CONFIG_MK}
+echo >> ${CONFIG_MK}
+echo "CFLAGS_COV = $CFLAGS_COV" >> ${CONFIG_MK}
+echo "LDFLAGS_COV = $LDFLAGS_COV" >> ${CONFIG_MK}
+echo "LIBS_COV = $LIBS_COV" >> ${CONFIG_MK}
+echo >> ${CONFIG_MK}
+echo "CFLAGS_DEBUG = $CFLAGS_DEBUG" >> ${CONFIG_MK}
+echo "LDFLAGS_DEBUG = $LDFLAGS_DEBUG" >> ${CONFIG_MK}
+echo "LIBS_DEBUG = $LIBS_DEBUG" >> ${CONFIG_MK}
+
+echo >> ${CONFIG_MK}
+echo "$PROG: $LOCAL_LIBS $OBJECTS" >> ${CONFIG_MK}
+echo " ${LIBTOOL} --tag=CC --mode=link \${CC} \${CFLAGS} \${LDFLAGS} ${OBJECTS} ${LIBS} -o $PROG" >> ${CONFIG_MK}
+
+echo >> ${CONFIG_MK}
+echo "$PROG_ASAN: $LOCAL_LIBS_ASAN $OBJECTS_ASAN" >> ${CONFIG_MK}
+echo " ${LIBTOOL} --tag=CC --mode=link \${CC} \${CFLAGS_ASAN} \${LDFLAGS_ASAN} ${OBJECTS_ASAN} ${LIBS_ASAN} -o $PROG_ASAN" >> ${CONFIG_MK}
+
+if $HAVE_GCOV; then
+ echo >> ${CONFIG_MK}
+ echo "$PROG_COV: $LOCAL_LIBS_COV $OBJECTS_COV" >> ${CONFIG_MK}
+ echo " ${LIBTOOL} --tag=CC --mode=link \${CC} \${CFLAGS_COV} \${LDFLAGS_COV} ${OBJECTS_COV} ${LIBS_COV} -o $PROG_COV" >> ${CONFIG_MK}
+fi
+
+echo >> ${CONFIG_MK}
+echo "$PROG_DEBUG: $LOCAL_LIBS_DEBUG $OBJECTS_DEBUG" >> ${CONFIG_MK}
+echo " ${LIBTOOL} --tag=CC --mode=link \${CC} \${CFLAGS_DEBUG} \${LDFLAGS_DEBUG} ${OBJECTS_DEBUG} ${LIBS_DEBUG} -o $PROG_DEBUG" >> ${CONFIG_MK}
+
+for SRC in $SOURCES; do
+ echo >> ${CONFIG_MK}
+ SRC_LO="$(c2ext .main.lo "$SRC")"
+ c_ext_rule .main.lo "$SRC" >> ${CONFIG_MK}
+ echo " ${LIBTOOL} --tag=CC --mode=compile \${CC} \${CPPFLAGS} \${CFLAGS} -c $SRC -o $SRC_LO" >> ${CONFIG_MK}
+
+ echo >> ${CONFIG_MK}
+ SRC_ASAN_LO="$(c2ext .asan.lo "$SRC")"
+ c_ext_rule .asan.lo "$SRC" >> ${CONFIG_MK}
+ echo " ${LIBTOOL} --tag=CC --mode=compile \${CC} \${CPPFLAGS} \${CFLAGS_ASAN} -c $SRC -o $SRC_ASAN_LO" >> ${CONFIG_MK}
+
+ if $HAVE_GCOV; then
+ echo >> ${CONFIG_MK}
+ SRC_COV_LO="$(c2ext .cov.lo "$SRC")"
+ c_ext_rule .cov.lo "$SRC" >> ${CONFIG_MK}
+ echo " ${LIBTOOL} --tag=CC --mode=compile \${CC} \${CPPFLAGS} \${CFLAGS_COV} -c $SRC -o $SRC_COV_LO" >> ${CONFIG_MK}
+ fi
+
+ echo >> ${CONFIG_MK}
+ SRC_DEBUG_LO="$(c2ext .debug.lo "$SRC")"
+ c_ext_rule .debug.lo "$SRC" >> ${CONFIG_MK}
+ echo " ${LIBTOOL} --tag=CC --mode=compile \${CC} \${CPPFLAGS} \${CFLAGS_DEBUG} -c $SRC -o $SRC_DEBUG_LO" >> ${CONFIG_MK}
+done
+
+update_config_mk
diff --git a/libc3/window/sdl2/demo/sources.mk b/libc3/window/sdl2/demo/sources.mk
new file mode 100644
index 0000000..23edeee
--- /dev/null
+++ b/libc3/window/sdl2/demo/sources.mk
@@ -0,0 +1,8 @@
+# sources.mk generated by update_sources
+HEADERS = \
+ bg_rect.h \
+
+SOURCES = \
+ bg_rect.c \
+ window_sdl2_demo.c \
+
diff --git a/libc3/window/sdl2/demo/sources.sh b/libc3/window/sdl2/demo/sources.sh
new file mode 100644
index 0000000..029f297
--- /dev/null
+++ b/libc3/window/sdl2/demo/sources.sh
@@ -0,0 +1,3 @@
+# sources.sh generated by update_sources
+HEADERS='bg_rect.h '
+SOURCES='bg_rect.c window_sdl2_demo.c '
diff --git a/libc3/window/sdl2/demo/update_sources b/libc3/window/sdl2/demo/update_sources
new file mode 100755
index 0000000..0afdc30
--- /dev/null
+++ b/libc3/window/sdl2/demo/update_sources
@@ -0,0 +1,26 @@
+#!/bin/sh
+## c3
+## Copyright 2022,2023 kmx.io <contact@kmx.io>
+##
+## Permission is hereby granted to use this software granted the above
+## copyright notice and this permission paragraph are included in all
+## copies and substantial portions of this software.
+##
+## THIS SOFTWARE IS PROVIDED "AS-IS" WITHOUT ANY GUARANTEE OF
+## PURPOSE AND PERFORMANCE. IN NO EVENT WHATSOEVER SHALL THE
+## AUTHOR BE CONSIDERED LIABLE FOR THE USE AND PERFORMANCE OF
+## THIS SOFTWARE.
+
+. ../../../../config.subr
+
+echo "# sources.mk generated by update_sources" > ${SOURCES_MK}
+echo "# sources.sh generated by update_sources" > ${SOURCES_SH}
+
+HEADERS="$(ls *.h | sort)"
+sources HEADERS "$HEADERS"
+
+SOURCES="$(ls *.c | sort)"
+sources SOURCES "$SOURCES"
+
+update_sources_mk
+update_sources_sh
diff --git a/libc3/window/sdl2/demo/window_sdl2_demo.c b/libc3/window/sdl2/demo/window_sdl2_demo.c
new file mode 100644
index 0000000..3189a79
--- /dev/null
+++ b/libc3/window/sdl2/demo/window_sdl2_demo.c
@@ -0,0 +1,168 @@
+/* c3
+ * Copyright 2022,2023 kmx.io <contact@kmx.io>
+ *
+ * Permission is hereby granted to use this software granted the above
+ * copyright notice and this permission paragraph are included in all
+ * copies and substantial portions of this software.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS-IS" WITHOUT ANY GUARANTEE OF
+ * PURPOSE AND PERFORMANCE. IN NO EVENT WHATSOEVER SHALL THE
+ * AUTHOR BE CONSIDERED LIABLE FOR THE USE AND PERFORMANCE OF
+ * THIS SOFTWARE.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <libc3/c3.h>
+#include "../../window.h"
+#include "../window_sdl2.h"
+#include "bg_rect.h"
+
+#define WINDOW_SDL2_DEMO_SEQUENCE_COUNT 0
+
+static bool window_sdl2_demo_button (s_window_sdl2 *window, u8 button,
+ sw x, sw y);
+static bool window_sdl2_demo_key (s_window_sdl2 *window,
+ SDL_Keysym *keysym);
+static bool window_sdl2_demo_load (s_window_sdl2 *window);
+static bool window_sdl2_demo_render (s_window_sdl2 *window,
+ SDL_GLContext context);
+bool window_sdl2_demo_resize (s_window_sdl2 *window,
+ uw w, uw h);
+
+int main (int argc, char **argv)
+{
+ s_window_sdl2 window;
+ if (! c3_init(NULL, argc, argv)) {
+ err_puts("c3_init");
+ return 1;
+ }
+ window_sdl2_init(&window, 0, 0, 800, 600,
+ "C3.Window.SDL2 demo",
+ WINDOW_SDL2_DEMO_SEQUENCE_COUNT);
+ window.button = window_sdl2_demo_button;
+ window.key = window_sdl2_demo_key;
+ window.load = window_sdl2_demo_load;
+ window.render = window_sdl2_demo_render;
+ window.resize = window_sdl2_demo_resize;
+ if (! window_sdl2_run(&window)) {
+ err_puts("window_sdl2_run -> false");
+ c3_clean(NULL);
+ return g_c3_exit_code;
+ }
+ c3_clean(NULL);
+ SDL_Quit();
+ return 0;
+}
+
+bool window_sdl2_demo_button (s_window_sdl2 *window, u8 button,
+ sw x, sw y)
+{
+ assert(window);
+ (void) window;
+ printf("c3_window_sdl2_demo_button: %lu (%ld, %ld)\n", (uw) button, x, y);
+ return true;
+}
+
+bool window_sdl2_demo_key (s_window_sdl2 *window, SDL_Keysym *keysym)
+{
+ assert(window);
+ assert(keysym);
+ (void) window;
+ switch (keysym->sym) {
+ case SDLK_ESCAPE:
+ case SDLK_q:
+ g_c3_exit_code = 0;
+ return false;
+ case SDLK_LEFT:
+ if (! window_set_sequence_pos((s_window *) window,
+ (window->sequence_pos +
+ window->sequence_count - 1) %
+ window->sequence_count))
+ return false;
+ break;
+ case SDLK_RIGHT:
+ if (! window_set_sequence_pos((s_window *) window,
+ (window->sequence_pos + 1) %
+ window->sequence_count))
+ return false;
+ break;
+ default:
+ printf("c3_window_sdl2_demo_key: %d\n", keysym->sym);
+ }
+ return true;
+}
+
+bool window_sdl2_demo_load (s_window_sdl2 *window)
+{
+ assert(window);
+ if (window->sequence_count != WINDOW_SDL2_DEMO_SEQUENCE_COUNT) {
+ fprintf(stderr, "window_sdl2_demo_load: "
+ "window->sequence_count = %lu\n", window->sequence_count);
+ assert(window->sequence_count == WINDOW_SDL2_DEMO_SEQUENCE_COUNT);
+ return false;
+ }
+ window_sdl2_sequence_init(window->sequence, 8.0,
+ "01. Background rectangles",
+ bg_rect_load, bg_rect_render);
+ /*
+ window_sdl2_sequence_init(window->sequence + 1, 20.0,
+ "02. Lightspeed",
+ lightspeed_load, lightspeed_render);
+ if (! sdl2_sprite_init(&g_toaster_sprite,
+ sdl2_png_1("img/flaps.png"),
+ 4, 1, 4))
+ return false;
+ if (! sdl2_sprite_init(&g_toast_sprite,
+ sdl2_png_1("img/toast.png"),
+ 1, 1, 1))
+ return false;
+ window_sdl2_sequence_init(window->sequence + 2, 60.0,
+ "03. Toasters",
+ toasters_load, toasters_render);
+ if (! sdl2_sprite_init(&g_fly_sprite,
+ sdl2_png_1("img/fly-noto.png"),
+ 1, 1, 1))
+ return false;
+ if (! sdl2_sprite_init(&g_dead_fly_sprite,
+ sdl2_png_1("img/fly-dead.png"),
+ 1, 1, 1))
+ return false;
+ window_sdl2_sequence_init(window->sequence + 3, 60.0,
+ "04. Flies",
+ flies_load, flies_render);
+ */
+ window_set_sequence_pos((s_window *) window, 0);
+ return true;
+}
+
+bool window_sdl2_demo_render (s_window_sdl2 *window,
+ SDL_GLContext context)
+{
+ s_sequence *seq;
+ assert(window);
+ if (! window_animate((s_window *) window))
+ return false;
+ seq = window->sequence + window->sequence_pos;
+ if (! seq->render(seq, window, context))
+ return false;
+ /* progress bar */
+ glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
+ glRectd(19, window->h - 12,
+ 19 + (window->w - 40.0) * seq->t / seq->duration + 2,
+ window->h - 12 + 4);
+ glColor4f(0.0f, 0.0f, 0.0f, 1.0f);
+ glRectd(20, window->h - 11,
+ 20 + (window->w - 40.0) * seq->t / seq->duration,
+ window->h - 11 + 2);
+ return true;
+}
+
+bool window_sdl2_demo_resize (s_window_sdl2 *window,
+ uw w, uw h)
+{
+ assert(window);
+ (void) window;
+ (void) w;
+ (void) h;
+ return true;
+}
diff --git a/libc3/window/sdl2/sources.mk b/libc3/window/sdl2/sources.mk
new file mode 100644
index 0000000..74b2d89
--- /dev/null
+++ b/libc3/window/sdl2/sources.mk
@@ -0,0 +1,8 @@
+# sources.mk generated by update_sources
+HEADERS = \
+ types.h \
+ window_sdl2.h \
+
+SOURCES = \
+ window_sdl2.c \
+
diff --git a/libc3/window/sdl2/sources.sh b/libc3/window/sdl2/sources.sh
new file mode 100644
index 0000000..5f8490d
--- /dev/null
+++ b/libc3/window/sdl2/sources.sh
@@ -0,0 +1,3 @@
+# sources.sh generated by update_sources
+HEADERS='types.h window_sdl2.h '
+SOURCES='window_sdl2.c '
diff --git a/libc3/window/sdl2/types.h b/libc3/window/sdl2/types.h
new file mode 100644
index 0000000..ae25d0e
--- /dev/null
+++ b/libc3/window/sdl2/types.h
@@ -0,0 +1,95 @@
+/* c3
+ * Copyright 2022,2023 kmx.io <contact@kmx.io>
+ *
+ * Permission is hereby granted to use this software granted the above
+ * copyright notice and this permission paragraph are included in all
+ * copies and substantial portions of this software.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS-IS" WITHOUT ANY GUARANTEE OF
+ * PURPOSE AND PERFORMANCE. IN NO EVENT WHATSOEVER SHALL THE
+ * AUTHOR BE CONSIDERED LIABLE FOR THE USE AND PERFORMANCE OF
+ * THIS SOFTWARE.
+ */
+/** @file types.h
+ * @brief Module C3.Window.Sdl2
+ *
+ * Struct for all GUI window Sdl2 graphics operations.
+ */
+#ifndef LIBC3_WINDOW_SDL2_TYPES_H
+#define LIBC3_WINDOW_SDL2_TYPES_H
+
+#include <SDL.h>
+#include <OpenGL/gl.h>
+#include <libc3/types.h>
+#include "../types.h"
+
+typedef struct sdl2_font s_sdl2_font;
+typedef struct sdl2_sprite s_sdl2_sprite;
+typedef struct rgb s_rgb;
+typedef struct rgba s_rgba;
+typedef struct window_sdl2 s_window_sdl2;
+
+/* return false to break event loop */
+typedef bool (*f_window_sdl2_button) (s_window_sdl2 *window,
+ u8 button, sw x, sw y);
+
+/* return false to break event loop */
+typedef bool (*f_window_sdl2_key) (s_window_sdl2 *window,
+ SDL_Keysym *keysym);
+
+/* return false to break event loop */
+typedef bool (*f_window_sdl2_load) (s_window_sdl2 *window);
+
+/* return false to break event loop */
+typedef bool (*f_window_sdl2_motion) (s_window_sdl2 *window, sw x,
+ sw y);
+
+/* return false to break event loop */
+typedef bool (*f_window_sdl2_render) (s_window_sdl2 *window,
+ SDL_GLContext context);
+
+/* return false to break event loop */
+typedef bool (*f_window_sdl2_resize) (s_window_sdl2 *window,
+ uw w, uw h);
+
+typedef bool (*f_window_sdl2_sequence_load) (s_sequence *seq,
+ s_window_sdl2 *window);
+
+typedef bool (*f_window_sdl2_sequence_render) (s_sequence *seq,
+ s_window_sdl2 *window,
+ SDL_GLContext context);
+
+struct rgb {
+ double r;
+ double g;
+ double b;
+};
+
+struct rgba {
+ double r;
+ double g;
+ double b;
+ double a;
+};
+
+/* Subtype of s_window. See libc3/window/types.h */
+struct window_sdl2 {
+ sw x;
+ sw y;
+ uw w;
+ uw h;
+ f_window_sdl2_button button;
+ f_window_sdl2_key key;
+ f_window_sdl2_load load;
+ f_window_sdl2_motion motion;
+ f_window_sdl2_render render;
+ SDL_GLContext context;
+ f_window_sdl2_resize resize;
+ s_sequence *sequence;
+ uw sequence_count;
+ uw sequence_pos;
+ const s8 *title;
+ SDL_Window *sdl_window;
+};
+
+#endif /* LIBC3_WINDOW_SDL2_TYPES_H */
diff --git a/libc3/window/sdl2/update_sources b/libc3/window/sdl2/update_sources
new file mode 100755
index 0000000..04b5076
--- /dev/null
+++ b/libc3/window/sdl2/update_sources
@@ -0,0 +1,28 @@
+#!/bin/sh
+## c3
+## Copyright 2022,2023 kmx.io <contact@kmx.io>
+##
+## Permission is hereby granted to use this software granted the above
+## copyright notice and this permission paragraph are included in all
+## copies and substantial portions of this software.
+##
+## THIS SOFTWARE IS PROVIDED "AS-IS" WITHOUT ANY GUARANTEE OF
+## PURPOSE AND PERFORMANCE. IN NO EVENT WHATSOEVER SHALL THE
+## AUTHOR BE CONSIDERED LIABLE FOR THE USE AND PERFORMANCE OF
+## THIS SOFTWARE.
+
+. ../../../config.subr
+
+echo "# sources.mk generated by update_sources" > ${SOURCES_MK}
+echo "# sources.sh generated by update_sources" > ${SOURCES_SH}
+
+HEADERS="$(ls *.h | sort)"
+sources HEADERS "$HEADERS"
+
+SOURCES="$(ls *.c | sort)"
+sources SOURCES "$SOURCES"
+
+update_sources_mk
+update_sources_sh
+
+( cd demo && ./update_sources; )
diff --git a/libc3/window/sdl2/window_sdl2.c b/libc3/window/sdl2/window_sdl2.c
new file mode 100644
index 0000000..33cb326
--- /dev/null
+++ b/libc3/window/sdl2/window_sdl2.c
@@ -0,0 +1,166 @@
+/* c3
+ * Copyright 2022,2023 kmx.io <contact@kmx.io>
+ *
+ * Permission is hereby granted to use this software granted the above
+ * copyright notice and this permission paragraph are included in all
+ * copies and substantial portions of this software.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS-IS" WITHOUT ANY GUARANTEE OF
+ * PURPOSE AND PERFORMANCE. IN NO EVENT WHATSOEVER SHALL THE
+ * AUTHOR BE CONSIDERED LIABLE FOR THE USE AND PERFORMANCE OF
+ * THIS SOFTWARE.
+ */
+#include <assert.h>
+#include <err.h>
+#include <stdlib.h>
+#include <xkbcommon/xkbcommon.h>
+#include <libc3/tag.h>
+#include "window_sdl2.h"
+
+static bool g_window_sdl2_initialized = false;
+
+s_window_sdl2 * window_sdl2_init (s_window_sdl2 *window,
+ sw x, sw y, uw w, uw h,
+ const s8 *title,
+ uw sequence_count)
+{
+ assert(window);
+ window->x = x;
+ window->y = y;
+ window->w = w;
+ window->h = h;
+ window->button = window_sdl2_button_default;
+ window->key = window_sdl2_key_default;
+ window->load = window_sdl2_load_default;
+ window->motion = window_sdl2_motion_default;
+ window->render = window_sdl2_render_default;
+ window->resize = window_sdl2_resize_default;
+ window->context = NULL;
+ window->sequence = calloc(sequence_count, sizeof(s_sequence));
+ window->sequence_count = sequence_count;
+ window->sequence_pos = 0;
+ window->title = title ? title : "C3.Window.Sdl2";
+ return window;
+}
+
+bool window_sdl2_button_default (s_window_sdl2 *window, u8 button,
+ sw x, sw y)
+{
+ assert(window);
+ (void) window;
+ (void) button;
+ (void) x;
+ (void) y;
+ printf("window_sdl2_button_default: %d (%ld, %ld)\n",
+ (int) button, x, y);
+ return true;
+}
+
+bool window_sdl2_key_default (s_window_sdl2 *window, SDL_Keysym *keysym)
+{
+ assert(window);
+ assert(keysym);
+ (void) window;
+ (void) keysym;
+ printf("window_sdl2_key_default: %d\n", keysym->sym);
+ return true;
+}
+
+bool window_sdl2_load_default (s_window_sdl2 *window)
+{
+ assert(window);
+ (void) window;
+ printf("window_sdl2_load_default\n");
+ return true;
+}
+
+bool window_sdl2_motion_default (s_window_sdl2 *window, sw x, sw y)
+{
+ assert(window);
+ (void) window;
+ (void) x;
+ (void) y;
+ /*
+ printf("window_sdl2_motion_default (%ld, %ld)\n", x, y);
+ */
+ return true;
+}
+
+bool window_sdl2_render_default (s_window_sdl2 *window,
+ SDL_GLContext context)
+{
+ (void) window;
+ (void) context;
+ assert(window);
+ printf("window_sdl2_render_default\n");
+ return true;
+}
+
+bool window_sdl2_resize_default (s_window_sdl2 *window, uw w, uw h)
+{
+ assert(window);
+ (void) window;
+ (void) w;
+ (void) h;
+ printf("window_sdl2_resize_default: %lu x %lu\n", w, h);
+ return true;
+}
+
+bool window_sdl2_run (s_window_sdl2 *window)
+{
+ int quit = 0;
+ SDL_Event sdl_event;
+ assert(window);
+ if (! g_window_sdl2_initialized) {
+ if (SDL_Init(SDL_INIT_VIDEO) < 0) {
+ warnx("window_sdl2_run: SDL initialization failed: %s",
+ SDL_GetError());
+ return false;
+ }
+ g_window_sdl2_initialized = true;
+ }
+ window->sdl_window = SDL_CreateWindow(window->title,
+ window->x, window->y,
+ window->w, window->h,
+ SDL_WINDOW_OPENGL);
+ if (! window->sdl_window) {
+ warnx("window_sdl2_run: failed to create window: %s",
+ SDL_GetError());
+ return false;
+ }
+ window->context = SDL_GL_CreateContext(window->sdl_window);
+ if (! window->context) {
+ warnx("window_sdl2_run: failed to create OpenGL context: %s",
+ SDL_GetError());
+ return false;
+ }
+ SDL_GL_SetSwapInterval(1);
+ while (! quit) {
+ while (SDL_PollEvent(&sdl_event) != 0) {
+ if (sdl_event.type == SDL_QUIT) {
+ quit = 1;
+ }
+ }
+ glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
+ glClear(GL_COLOR_BUFFER_BIT);
+ SDL_GL_SwapWindow(window->sdl_window);
+ }
+ SDL_GL_DeleteContext(window->context);
+ SDL_DestroyWindow(window->sdl_window);
+ return true;
+}
+
+s_sequence * window_sdl2_sequence_init
+(s_sequence *seq, f64 duration, const s8 *title,
+ f_window_sdl2_sequence_load load,
+ f_window_sdl2_sequence_render render)
+{
+ assert(seq);
+ seq->t = 0.0;
+ seq->duration = duration;
+ seq->title = title;
+ seq->load = (f_sequence_load) load;
+ seq->render = (f_sequence_render) render;
+ tag_init_void(&seq->tag);
+ return seq;
+}
diff --git a/libc3/window/sdl2/window_sdl2.h b/libc3/window/sdl2/window_sdl2.h
new file mode 100644
index 0000000..f9cdf9e
--- /dev/null
+++ b/libc3/window/sdl2/window_sdl2.h
@@ -0,0 +1,39 @@
+/* c3
+ * Copyright 2022,2023 kmx.io <contact@kmx.io>
+ *
+ * Permission is hereby granted to use this software granted the above
+ * copyright notice and this permission paragraph are included in all
+ * copies and substantial portions of this software.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS-IS" WITHOUT ANY GUARANTEE OF
+ * PURPOSE AND PERFORMANCE. IN NO EVENT WHATSOEVER SHALL THE
+ * AUTHOR BE CONSIDERED LIABLE FOR THE USE AND PERFORMANCE OF
+ * THIS SOFTWARE.
+ */
+#ifndef LIBC3_WINDOW_SDL2_H
+#define LIBC3_WINDOW_SDL2_H
+
+#include "types.h"
+
+s_window_sdl2 * window_sdl2_init (s_window_sdl2 *window,
+ sw x, sw y, uw w, uw h,
+ const s8 *title,
+ uw sequence_count);
+bool window_sdl2_run (s_window_sdl2 *window);
+
+s_sequence * window_sdl2_sequence_init
+(s_sequence *sequence, f64 duration, const s8 *title,
+ f_window_sdl2_sequence_load load,
+ f_window_sdl2_sequence_render render);
+
+/* callbacks */
+bool window_sdl2_button_default (s_window_sdl2 *window, u8 button,
+ sw x, sw y);
+bool window_sdl2_key_default (s_window_sdl2 *window, SDL_Keysym *keysym);
+bool window_sdl2_load_default (s_window_sdl2 *window);
+bool window_sdl2_motion_default (s_window_sdl2 *window, sw x, sw y);
+bool window_sdl2_render_default (s_window_sdl2 *window,
+ SDL_GLContext context);
+bool window_sdl2_resize_default (s_window_sdl2 *window, uw w, uw h);
+
+#endif /* LIBC3_WINDOW_SDL2_H */
diff --git a/libc3/window/update_sources b/libc3/window/update_sources
index fbe3a60..2d8a35d 100755
--- a/libc3/window/update_sources
+++ b/libc3/window/update_sources
@@ -26,3 +26,4 @@ update_sources_mk
update_sources_sh
( cd cairo && ./update_sources; )
+( cd sdl2 && ./update_sources; )