changeset 414:6947c1fefe5c

misc: extreme cleanup - Remove of Javascript binding for many reasons. - Put examples and tests in GNU make. - Create a library with shared example code.
author David Demelier <markand@malikania.fr>
date Sun, 09 Oct 2022 13:51:03 +0200
parents 222045c513ec
children a5b98db4fd87
files .hgignore GNUmakefile README.md examples/assets/images/battle-background.png examples/assets/images/black-cat.png examples/assets/images/haunted-wood.png examples/assets/images/sword.png examples/assets/music/vabsounds-romance.ogg examples/assets/sounds/fire.wav examples/assets/sprites/chest.png examples/assets/sprites/explosion.png examples/assets/sprites/john-sword.png examples/assets/sprites/john-walk.png examples/assets/sprites/numbers.png examples/assets/sprites/people.png examples/assets/sprites/ui-cursor.png examples/example-action/assets/sprites/chest.png examples/example-action/assets/sprites/people.png examples/example-action/main.c examples/example-animation/assets/sprites/numbers.png examples/example-animation/main.c examples/example-audio/assets/music/vabsounds-romance.ogg examples/example-audio/assets/sounds/fire.wav examples/example-audio/main.c examples/example-battle/assets/images/battle-background.png examples/example-battle/assets/images/black-cat.png examples/example-battle/assets/images/haunted-wood.png examples/example-battle/assets/sounds/fire.wav examples/example-battle/assets/sprites/explosion.png examples/example-battle/assets/sprites/john-sword.png examples/example-battle/assets/sprites/john-walk.png examples/example-battle/assets/sprites/ui-cursor.png examples/example-battle/character-john.c examples/example-battle/character-john.h examples/example-battle/registry.c examples/example-battle/registry.h examples/example-battle/spell-fire.c examples/example-battle/spell-fire.h examples/example-cursor/main.c examples/example-debug/main.c examples/example-drawable/assets/sprites/explosion.png examples/example-drawable/main.c examples/example-font/main.c examples/example-gridmenu/main.c examples/example-label/main.c examples/example-message/main.c examples/example-notify/assets/images/sword.png examples/example-notify/main.c examples/example-sprite/assets/sprites/people.png examples/example-sprite/main.c examples/example-trace/main.c examples/example-trace/trace_hud.c examples/example-trace/trace_hud.h examples/example-ui/main.c examples/example/character-john.c examples/example/character-john.h examples/example/registry.c examples/example/registry.h examples/example/spell-fire.c examples/example/spell-fire.h examples/example/trace_hud.c examples/example/trace_hud.h src/libmlk-core-js/CMakeLists.txt src/libmlk-core-js/core/js-action-stack.c src/libmlk-core-js/core/js-action-stack.h src/libmlk-core-js/core/js-action.c src/libmlk-core-js/core/js-action.h src/libmlk-core-js/core/js-animation.c src/libmlk-core-js/core/js-animation.h src/libmlk-core-js/core/js-clock.c src/libmlk-core-js/core/js-clock.h src/libmlk-core-js/core/js-color.c src/libmlk-core-js/core/js-color.h src/libmlk-core-js/core/js-core.c src/libmlk-core-js/core/js-core.h src/libmlk-core-js/core/js-drawable-stack.c src/libmlk-core-js/core/js-drawable-stack.h src/libmlk-core-js/core/js-drawable.c src/libmlk-core-js/core/js-drawable.h src/libmlk-core-js/core/js-event.c src/libmlk-core-js/core/js-event.h src/libmlk-core-js/core/js-font.c src/libmlk-core-js/core/js-font.h src/libmlk-core-js/core/js-game.c src/libmlk-core-js/core/js-game.h src/libmlk-core-js/core/js-music.c src/libmlk-core-js/core/js-music.h src/libmlk-core-js/core/js-painter.c src/libmlk-core-js/core/js-painter.h src/libmlk-core-js/core/js-panic.c src/libmlk-core-js/core/js-panic.h src/libmlk-core-js/core/js-sound.c src/libmlk-core-js/core/js-sound.h src/libmlk-core-js/core/js-sprite.c src/libmlk-core-js/core/js-sprite.h src/libmlk-core-js/core/js-state.c src/libmlk-core-js/core/js-state.h src/libmlk-core-js/core/js-texture.c src/libmlk-core-js/core/js-texture.h src/libmlk-core-js/core/js-trace.c src/libmlk-core-js/core/js-trace.h src/libmlk-core-js/core/js-window.c src/libmlk-core-js/core/js-window.h src/mlk-run/CMakeLists.txt src/mlk-run/main.c src/tools/bcc/arg.h src/tools/bcc/main.c src/tools/map/main.c src/tools/map/mlk-map.c src/tools/tileset/main.c src/tools/tileset/mlk-tileset.c
diffstat 111 files changed, 1328 insertions(+), 8047 deletions(-) [+]
line wrap: on
line diff
--- a/.hgignore	Fri Oct 07 15:25:32 2022 +0200
+++ b/.hgignore	Sun Oct 09 13:51:03 2022 +0200
@@ -25,7 +25,48 @@
 # Generated files.
 \.exe$
 ^config\.h$
-^mlk-
-^example-
 ^src/libmlk-data/maps/.*\.map$
 ^src/libmlk-data/maps/.*\.tileset$
+
+# Examples.
+examples/example-action/example-action
+examples/example-animation/example-animation
+examples/example-audio/example-audio
+examples/example-battle/main
+examples/example-cursor/example-cursor
+examples/example-debug/example-debug
+examples/example-drawable/example-drawable
+examples/example-font/example-font
+examples/example-gridmenu/example-gridmenu
+examples/example-label/example-label
+examples/example-message/example-message
+examples/example-notify/example-notify
+examples/example-sprite/example-sprite
+examples/example-trace/example-trace
+examples/example-ui/example-ui
+examples/example/character-john
+examples/example/registry
+examples/example/spell-fire
+examples/example/trace_hud
+
+# Tests.
+tests/test-action-script
+tests/test-action
+tests/test-alloc
+tests/test-character
+tests/test-color
+tests/test-drawable
+tests/test-error
+tests/test-map
+tests/test-save-quest
+tests/test-save
+tests/test-state
+tests/test-tileset
+tests/test-util
+tests/test-vfs-directory
+tests/test-vfs-zip
+
+# Tools.
+src/tools/bcc/mlk-bcc
+src/tools/bcc/mlk-map
+src/tools/bcc/mlk-tileset
--- a/GNUmakefile	Fri Oct 07 15:25:32 2022 +0200
+++ b/GNUmakefile	Sun Oct 09 13:51:03 2022 +0200
@@ -3,7 +3,7 @@
 # Toolchain options.
 CC ?=                   clang
 CFLAGS ?=               -g -O0
-BCC ?=                  bcc
+BCC ?=                  src/tools/bcc/mlk-bcc
 BCC_OPTS ?=             -cs
 MD ?=                   -MD
 
@@ -26,6 +26,9 @@
 SNDFILE_INCS ?=         $(shell pkg-config --cflags sndfile)
 SNDFILE_LIBS ?=         $(shell pkg-config --libs sndfile)
 
+JANSSON_INCS ?=         $(shell pkg-config --cflags jansson)
+JANSSON_LIBS ?=         $(shell pkg-config --libs jansson)
+
 ifeq ($(WITH_ZSTD),yes)
 ZSTD_INCS ?=            $(shell pkg-config --cflags libzstd)
 ZSTD_LIBS ?=            $(shell pkg-config --libs libzstd)
@@ -67,7 +70,7 @@
 INCS +=                 $(ZIP_INCS)
 endif
 
-OPTS :=                 -Wall -Wextra -Wno-format-truncation -pipe
+OPTS :=                 -Wall -Wextra -pipe
 
 ifeq ($(OS),Darwin)
 OPTS +=                 -Wno-deprecated-declarations
@@ -79,18 +82,39 @@
 OPTS +=                 -DNDEBUG -O3
 endif
 
+# Meta variable that contains all libraries, used for executables and tests.
+LIBMLK =                $(LIBMLK_RPG) \
+                        $(LIBMLK_UI) \
+                        $(LIBMLK_CORE) \
+                        $(LIBMLK_PORT) \
+                        $(LIBMLK_SQLITE) \
+                        $(MATH_LIBS) \
+                        $(SDL2_LIBS) \
+                        $(SDL2_IMAGE_LIBS) \
+                        $(SDL2_TTF_LIBS) \
+                        $(OPENAL_LIBS) \
+                        $(SNDFILE_LIBS)
+
+ifeq ($(WITH_ZSTD),yes)
+LIBMLK +=               $(ZSTD_LIBS)
+endif
+
+ifeq ($(WITH_ZIP),yes)
+LIBMLK +=               $(LIBZIP_LIBS)
+endif
+
 .DEFAULT_GOAL :=        all
 
 .SUFFIXES:
-.SUFFIXES: .c .h .o .sql .ttf
+.SUFFIXES: .c .h .o .ogg .png .sql .ttf .wav
 
 .c.o:
 	$(CC) $(OPTS) $(INCS) $(DEFS) $(CFLAGS) $(MD) -c $< -o $@
 
 .c:
-	$(CC) $(OPTS) $(INCS) $(DEFS) $(CFLAGS) $< -o $@ $(LIBS) $(LDFLAGS)
+	$(CC) $(OPTS) $(INCS) $(DEFS) $(CFLAGS) $< -o $@ $(OBJS) $(LIBS) $(LDFLAGS)
 
-.ttf.h .sql.h:
+.ogg.h .png.h .ttf.h .sql.h .wav.h:
 	$(BCC) $(BCC_OPTS) $< assets_$(basename $(<F)) > $@
 
 %.a:
@@ -132,6 +156,39 @@
 
 # }}}
 
+# {{{ mlk-bcc
+
+MLK_BCC :=              src/tools/bcc/mlk-bcc
+
+$(MLK_BCC): INCS :=
+
+all: $(MLK_BCC)
+
+# }}}
+
+# {{{ mlk-tileset
+
+MLK_TILESET :=          src/tools/tileset/mlk-tileset
+
+$(MLK_TILESET): INCS := $(JANSSON_INCS)
+$(MLK_TILESET): LIBS := $(JANSSON_LIBS)
+
+all: $(MLK_TILESET)
+
+# }}}
+
+# {{{ mlk-map
+
+MLK_MAP :=              src/tools/map/mlk-map
+
+$(MLK_MAP): INCS := -Isrc/libmlk-port $(JANSSON_INCS)
+$(MLK_MAP): LIBS := $(LIBMLK_PORT) $(JANSSON_LIBS)
+$(MLK_MAP): $(LIBMLK_PORT)
+
+all: $(MLK_MAP)
+
+# }}}
+
 # {{{ libmlk-sqlite
 
 LIBMLK_SQLITE :=          libmlk-sqlite.a
@@ -141,8 +198,8 @@
 
 -include $(LIBMLK_SQLITE_DEPS)
 
-$(LIBMLK_SQLITE): INCS :=
-$(LIBMLK_SQLITE): OBJS := $(LIBMLK_SQLITE_OBJS)
+$(LIBMLK_SQLITE): private INCS :=
+$(LIBMLK_SQLITE): private OBJS := $(LIBMLK_SQLITE_OBJS)
 $(LIBMLK_SQLITE): $(LIBMLK_SQLITE_OBJS)
 
 all: $(LIBMLK_SQLITE)
@@ -189,7 +246,7 @@
 -include $(LIBMLK_CORE_DEPS)
 
 $(LIBMLK_CORE_OBJS): config.h
-$(LIBMLK_CORE): OBJS := $(LIBMLK_CORE_OBJS)
+$(LIBMLK_CORE): private OBJS := $(LIBMLK_CORE_OBJS)
 $(LIBMLK_CORE): $(LIBMLK_CORE_OBJS)
 
 all: $(LIBMLK_CORE)
@@ -217,9 +274,12 @@
                         src/libmlk-ui/assets/fonts/opensans-regular.ttf
 LIBMLK_UI_DATA_OBJS :=  $(addsuffix .h,$(basename $(LIBMLK_UI_DATA_SRCS)))
 
+$(LIBMLK_UI_DATA_OBJS): $(MLK_BCC)
+
 $(LIBMLK_UI_OBJS): $(LIBMLK_UI_DATA_OBJS)
+
 $(LIBMLK_UI): $(LIBMLK_UI_OBJS)
-$(LIBMLK_UI): OBJS := $(LIBMLK_UI_OBJS)
+$(LIBMLK_UI): private OBJS := $(LIBMLK_UI_OBJS)
 
 all: $(LIBMLK_UI)
 
@@ -268,9 +328,9 @@
                         src/libmlk-rpg/rpg/tileset-file.c \
                         src/libmlk-rpg/rpg/tileset.c \
                         src/libmlk-rpg/rpg/walksprite.c
-
 LIBMLK_RPG_OBJS :=      $(LIBMLK_RPG_SRCS:.c=.o)
 LIBMLK_RPG_DEPS :=      $(LIBMLK_RPG_SRCS:.c=.d)
+
 LIBMLK_RPG_DATA_SRCS := src/libmlk-rpg/assets/sql/character-load.sql \
                         src/libmlk-rpg/assets/sql/character-save.sql \
                         src/libmlk-rpg/assets/sql/init.sql \
@@ -284,34 +344,82 @@
 LIBMLK_RPG_DATA_OBJS := $(addsuffix .h,$(basename $(LIBMLK_RPG_DATA_SRCS)))
 
 $(LIBMLK_RPG_DATA_OBJS): BCC_OPTS := -cs0
+$(LIBMLK_RPG_DATA_OBJS): $(MLK_BCC)
+
 $(LIBMLK_RPG_OBJS): $(LIBMLK_RPG_DATA_OBJS)
+
 $(LIBMLK_RPG): $(LIBMLK_RPG_OBJS)
-$(LIBMLK_RPG): OBJS := $(LIBMLK_RPG_OBJS)
+$(LIBMLK_RPG): private OBJS := $(LIBMLK_RPG_OBJS)
 
 all: $(LIBMLK_RPG)
 
 # }}}
 
-# Meta variable that contains all libraries, used for executables and tests.
-LIBMLK :=       $(LIBMLK_RPG) \
-                $(LIBMLK_UI) \
-                $(LIBMLK_CORE) \
-                $(LIBMLK_PORT) \
-                $(LIBMLK_SQLITE) \
-                $(MATH_LIBS) \
-                $(SDL2_LIBS) \
-                $(SDL2_IMAGE_LIBS) \
-                $(SDL2_TTF_LIBS) \
-                $(OPENAL_LIBS) \
-                $(SNDFILE_LIBS)
+# {{{ libmlk-example
+
+LIBMLK_EXAMPLE :=               libmlk-example.a
+LIBMLK_EXAMPLE_SRCS :=          examples/example/character-john.c \
+                                examples/example/registry.c \
+                                examples/example/spell-fire.c \
+                                examples/example/trace_hud.c
+
+LIBMLK_EXAMPLE_DATA_SRCS :=     examples/assets/images/battle-background.png \
+                                examples/assets/images/black-cat.png \
+                                examples/assets/images/haunted-wood.png \
+                                examples/assets/images/sword.png \
+                                examples/assets/music/vabsounds-romance.ogg \
+                                examples/assets/sounds/fire.wav \
+                                examples/assets/sprites/chest.png \
+                                examples/assets/sprites/explosion.png \
+                                examples/assets/sprites/john-sword.png \
+                                examples/assets/sprites/john-walk.png \
+                                examples/assets/sprites/numbers.png \
+                                examples/assets/sprites/people.png \
+                                examples/assets/sprites/ui-cursor.png
+LIBMLK_EXAMPLE_DATA_OBJS :=     $(addsuffix .h,$(basename $(LIBMLK_EXAMPLE_DATA_SRCS)))
+
+LIBMLK_EXAMPLE_OBJS :=          $(LIBMLK_EXAMPLE_SRCS:.c=.o)
+LIBMLK_EXAMPLE_DEPS :=          $(LIBMLK_EXAMPLE_SRCS:.c=.d)
+
+-include $(LIBMLK_EXAMPLE_DEPS)
+
+$(LIBMLK_EXAMPLE_DATA_OBJS): $(MLK_BCC)
+
+$(LIBMLK_EXAMPLE_OBJS): private INCS += -Iexamples
+$(LIBMLK_EXAMPLE_OBJS): $(LIBMLK_EXAMPLE_DATA_OBJS)
 
-ifeq ($(WITH_ZSTD),yes)
-LIBMLK +=       $(ZSTD_LIBS)
-endif
+$(LIBMLK_EXAMPLE): private OBJS := $(LIBMLK_EXAMPLE_OBJS)
+$(LIBMLK_EXAMPLE): $(LIBMLK_EXAMPLE_OBJS)
+
+# }}}
+
+# {{{ examples
 
-ifeq ($(WITH_ZIP),yes)
-LIBMLK +=       $(LIBZIP_LIBS)
-endif
+EXAMPLES :=             examples/example-action/example-action.c \
+                        examples/example-animation/example-animation.c \
+                        examples/example-audio/example-audio.c \
+                        examples/example-cursor/example-cursor.c \
+                        examples/example-debug/example-debug.c \
+                        examples/example-drawable/example-drawable.c \
+                        examples/example-font/example-font.c \
+                        examples/example-gridmenu/example-gridmenu.c \
+                        examples/example-label/example-label.c \
+                        examples/example-message/example-message.c \
+                        examples/example-notify/example-notify.c \
+                        examples/example-sprite/example-sprite.c \
+                        examples/example-ui/example-ui.c
+EXAMPLES_EXE :=         $(EXAMPLES:.c=)
+EXAMPLES_OBJS :=        $(EXAMPLES_EXE)
+
+$(EXAMPLES_EXE): private LIBS += $(LIBMLK) $(LIBMLK_EXAMPLE)
+$(EXAMPLES_EXE): private INCS += -Iexamples
+$(EXAMPLES_EXE): $(LIBMLK_RPG) $(LIBMLK_UI) $(LIBMLK_CORE) $(LIBMLK_PORT) $(LIBMLK_SQLITE) $(LIBMLK_EXAMPLE)
+
+examples: $(EXAMPLES_EXE)
+
+# }}}
+
+# {{{ tests
 
 TESTS :=        tests/test-action-script.c \
                 tests/test-action.c \
@@ -341,13 +449,17 @@
 tests: $(TESTS_EXE)
 	for t in $(TESTS_EXE); do ./$$t; done
 
+# }}}
+
 clean:
 	rm -f config.h
+	rm -f $(MLK_BCC) $(MLK_MAP) $(MLK_TILESET)
 	rm -f $(LIBMLK_SQLITE) $(LIBMLK_SQLITE_DEPS) $(LIBMLK_SQLITE_OBJS)
 	rm -f $(LIBMLK_PORT) $(LIBMLK_PORT_DEPS) $(LIBMLK_PORT_OBJS)
 	rm -f $(LIBMLK_CORE) $(LIBMLK_CORE_DEPS) $(LIBMLK_CORE_OBJS)
 	rm -f $(LIBMLK_UI) $(LIBMLK_UI_DEPS) $(LIBMLK_UI_OBJS) $(LIBMLK_UI_DATA_OBJS)
 	rm -f $(LIBMLK_RPG) $(LIBMLK_RPG_DEPS) $(LIBMLK_RPG_OBJS) $(LIBMLK_RPG_DATA_OBJS)
-	rm -f $(TESTS_EXE)
+	rm -f $(LIBMLK_EXAMPLE) $(LIBMLK_EXAMPLE_DEPS) $(LIBMLK_EXAMPLE_OBJS) $(LIBMLK_EXAMPLE_DATA_OBJS)
+	rm -f $(TESTS_EXE) $(EXAMPLES_EXE)
 
-.PHONY: all clean tests
+.PHONY: all clean examples tests
--- a/README.md	Fri Oct 07 15:25:32 2022 +0200
+++ b/README.md	Sun Oct 09 13:51:03 2022 +0200
@@ -1,9 +1,8 @@
 Molko's Engine
 ==============
 
-The Molko's Engine is a C and Javascript framework to write 2D RPG games in a
-portable manner. It is designed in mind to be flexible, simple and convenient to
-use.
+The Molko's Engine is a C and framework to write 2D RPG games in a portable
+manner. It is designed in mind to be flexible, simple and convenient to use.
 
 It has the following features:
 
Binary file examples/assets/images/battle-background.png has changed
Binary file examples/assets/images/black-cat.png has changed
Binary file examples/assets/images/haunted-wood.png has changed
Binary file examples/assets/images/sword.png has changed
Binary file examples/assets/music/vabsounds-romance.ogg has changed
Binary file examples/assets/sounds/fire.wav has changed
Binary file examples/assets/sprites/chest.png has changed
Binary file examples/assets/sprites/explosion.png has changed
Binary file examples/assets/sprites/john-sword.png has changed
Binary file examples/assets/sprites/john-walk.png has changed
Binary file examples/assets/sprites/numbers.png has changed
Binary file examples/assets/sprites/people.png has changed
Binary file examples/assets/sprites/ui-cursor.png has changed
Binary file examples/example-action/assets/sprites/chest.png has changed
Binary file examples/example-action/assets/sprites/people.png has changed
--- a/examples/example-action/main.c	Fri Oct 07 15:25:32 2022 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,425 +0,0 @@
-/*
- * example-action.c -- example on how to use automatic actions
- *
- * Copyright (c) 2020-2022 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <assert.h>
-
-#include <core/action.h>
-#include <core/action-stack.h>
-#include <core/core.h>
-#include <core/event.h>
-#include <core/game.h>
-#include <core/image.h>
-#include <core/maths.h>
-#include <core/painter.h>
-#include <core/panic.h>
-#include <core/script.h>
-#include <core/sprite.h>
-#include <core/state.h>
-#include <core/sys.h>
-#include <core/texture.h>
-#include <core/util.h>
-#include <core/window.h>
-
-#include <ui/theme.h>
-#include <ui/ui.h>
-
-#include <rpg/message.h>
-#include <rpg/rpg.h>
-
-#include <assets/sprites/chest.h>
-#include <assets/sprites/people.h>
-
-#define W       1280
-#define H       720
-
-#define MW      (W * 0.75)
-#define MX      ((W / 2) - (MW / 2))
-#define MY      (100)
-
-/* This is a stack of "parallel" events. */
-static struct action *events_actions[32];
-static struct action_stack events;
-
-/* This is a stack of modal events. */
-static struct action *modal_actions[32];
-static struct action_stack modal;
-
-/* Maximum number of states. */
-static struct state *states[1];
-
-/*
- * Event object for the chest to click on.
- */
-static struct {
-	const char *text[2];
-	struct message msg;
-	struct action msg_act;
-	int x;
-	int y;
-	int opened;
-	struct texture image;
-	struct sprite sprite;
-	struct action event;
-} chest = {
-	.text = {
-		"100000 pièces.",
-		"Te voilà riche sale file de crevette."
-	},
-	.msg = {
-		.x = MX,
-		.y = MY,
-		.w = MW,
-		.lines = chest.text,
-		.linesz = 2
-	}
-};
-
-/*
- * Event object for the guide who ask dialog with you.
- *
- * It is a script spawned upon click like this.
- *
- * [guide.event]
- *       |           spawn
- *   (on click)------------------->[message 1]
- *                                      v
- *                                 [message 2]
- *                                      v
- *                                 [question 3]
- *                   spawn              v
- * [response]<---------------------[check result]
- *     v
- * [message 4/5]
- */
-static struct {
-	struct {
-		const char *text[6];
-		struct message msg;
-		struct action act;
-	} msgs[5];
-
-	/* This is the sprite and action to click on. */
-	int x;
-	int y;
-	struct texture image;
-	struct sprite sprite;
-	struct action event;
-
-	/* This is the event for the response. */
-	struct action response;
-
-	struct script script;
-	struct action script_act;
-} guide = {
-	.msgs = {
-		{
-			.text = {
-				"Bienvenue dans ce monde Molko."
-			},
-			.msg = {
-				.x = MX,
-				.y = MY,
-				.w = MW,
-				.delay = MESSAGE_DELAY_DEFAULT,
-				.flags = MESSAGE_FLAGS_FADEIN,
-				.lines = guide.msgs[0].text,
-				.linesz = 1
-			},
-		},
-		{
-			.text = {
-				"Penses tu vraiment pouvoir me battre ?"
-			},
-			.msg = {
-				.x = MX,
-				.y = MY,
-				.w = MW,
-				.lines = guide.msgs[1].text,
-				.linesz = 1
-			}
-		},
-		{
-			.text = {
-				"Non j'ai la trouille.",
-				"Bien sûr, je suis la légende."
-			},
-			.msg = {
-				.x = MX,
-				.y = MY,
-				.w = MW,
-				.flags = MESSAGE_FLAGS_QUESTION,
-				.lines = guide.msgs[2].text,
-				.linesz = 2
-			}
-		},
-
-		/* In case of NO. */
-		{
-			.text = {
-				"Poule mouillée."
-			},
-			.msg = {
-				.x = MX,
-				.y = MY,
-				.w = MW,
-				.lines = guide.msgs[3].text,
-				.linesz = 1
-			}
-		},
-
-		/* In case of YES. */
-		{
-			.text = {
-				"Prépare toi à souffrir."
-			},
-			.msg = {
-				.x = MX,
-				.y = MY,
-				.w = MW,
-				.lines = guide.msgs[4].text,
-				.linesz = 1
-			}
-		}
-	}
-};
-
-static int
-guide_response_update(struct action *act, unsigned int ticks)
-{
-	/* Immediately return to get access to end. */
-	(void)act;
-	(void)ticks;
-
-	return 1;
-}
-
-static void
-guide_response_end(struct action *act)
-{
-	(void)act;
-
-	/* We add a final response depending on the result. */
-	const int index = guide.msgs[2].msg.index + 3;
-
-	message_action(&guide.msgs[index].msg, &guide.msgs[index].act);
-	message_query(&guide.msgs[index].msg, NULL, &guide.msgs[index].msg.h);
-	action_stack_add(&modal, &guide.msgs[index].act);
-}
-
-static void
-guide_popup(void)
-{
-	/* Prepare the script with first 3 messages. */
-	for (size_t i = 0; i < 3; ++i) {
-		message_action(&guide.msgs[i].msg, &guide.msgs[i].act);
-		message_query(&guide.msgs[i].msg, NULL, &guide.msgs[i].msg.h);
-		script_append(&guide.script, &guide.msgs[i].act);
-	}
-
-	/* Reset the message index. */
-	guide.msgs[2].msg.index = 0;
-
-	/* This is final action that analyze user input. */
-	guide.response.update = guide_response_update;
-	guide.response.end = guide_response_end;
-	script_append(&guide.script, &guide.response);
-
-	script_action(&guide.script, &guide.script_act);
-	action_stack_add(&modal, &guide.script_act);
-}
-
-static void
-guide_handle(struct action *act, const union event *ev)
-{
-	(void)act;
-
-	if (!action_stack_completed(&modal))
-		return;
-
-	switch (ev->type) {
-	case EVENT_CLICKDOWN:
-		if (maths_is_boxed(guide.x, guide.y,
-		    guide.sprite.cellw, guide.sprite.cellh,
-		    ev->click.x, ev->click.y))
-			guide_popup();
-		break;
-	default:
-		break;
-	}
-}
-
-static void
-guide_draw(struct action *act)
-{
-	(void)act;
-
-	sprite_draw(&guide.sprite, 0, 0, guide.x, guide.y);
-}
-
-static void
-guide_init(void)
-{
-	if (image_openmem(&guide.image, assets_sprites_people, sizeof (assets_sprites_people)) < 0)
-		panic();
-
-	sprite_init(&guide.sprite, &guide.image, 48, 48);
-
-	/* This is the global guide action. */
-	guide.event.handle = guide_handle;
-	guide.event.draw = guide_draw;
-	guide.x = 200;
-	guide.y = 200;
-}
-
-static void
-chest_handle(struct action *act, const union event *ev)
-{
-	(void)act;
-
-	if (chest.opened || !action_stack_completed(&modal))
-		return;
-
-	switch (ev->type) {
-	case EVENT_CLICKDOWN:
-		if (maths_is_boxed(chest.x, chest.y, chest.sprite.cellw, chest.sprite.cellh,
-		    ev->click.x, ev->click.y)) {
-			chest.opened = 1;
-			message_action(&chest.msg, &chest.msg_act);
-			message_query(&chest.msg, NULL, &chest.msg.h);
-			action_stack_add(&modal, &chest.msg_act);
-		}
-	default:
-		break;
-	}
-}
-
-static void
-chest_draw(struct action *act)
-{
-	(void)act;
-
-	const int row = chest.opened ? chest.sprite.nrows - 1 : 0;
-	const int col = chest.opened ? chest.sprite.ncols - 1 : 0;
-
-	sprite_draw(&chest.sprite, row, col, chest.x, chest.y);
-}
-
-static void
-chest_init(void)
-{
-	if (image_openmem(&chest.image, assets_sprites_chest, sizeof (assets_sprites_chest)) < 0)
-		panic();
-
-	sprite_init(&chest.sprite, &chest.image, 32, 32);
-
-	chest.x = 100;
-	chest.y = 100;
-	chest.event.handle = chest_handle;
-	chest.event.draw = chest_draw;
-}
-
-static void
-init(void)
-{
-	if (core_init("fr.malikania", "example-action") < 0 || ui_init() < 0 || rpg_init() < 0)
-		panic();
-	if (window_open("Example - Action", W, H) < 0)
-		panic();
-
-	guide_init();
-	chest_init();
-}
-
-static void
-handle(struct state *st, const union event *ev)
-{
-	(void)st;
-
-	switch (ev->type) {
-	case EVENT_QUIT:
-		game_quit();
-		break;
-	default:
-		action_stack_handle(&events, ev);
-		action_stack_handle(&modal, ev);
-		break;
-	}
-}
-
-static void
-update(struct state *st, unsigned int ticks)
-{
-	(void)st;
-
-	action_stack_update(&events, ticks);
-	action_stack_update(&modal, ticks);
-}
-
-static void
-draw(struct state *st)
-{
-	(void)st;
-
-	painter_set_color(0xffffffff);
-	painter_clear();
-	action_stack_draw(&events);
-	action_stack_draw(&modal);
-	painter_present();
-}
-
-static void
-run(void)
-{
-	struct state state = {
-		.handle = handle,
-		.update = update,
-		.draw = draw
-	};
-
-	action_stack_init(&modal, modal_actions, UTIL_SIZE(modal_actions));
-
-	action_stack_init(&events, events_actions, UTIL_SIZE(events_actions));
-	action_stack_add(&events, &chest.event);
-	action_stack_add(&events, &guide.event);
-
-	game_init(states, UTIL_SIZE(states));
-	game_push(&state);
-	game_loop();
-}
-
-static void
-quit(void)
-{
-	window_finish();
-	rpg_finish();
-	ui_finish();
-	core_finish();
-}
-
-int
-main(int argc, char **argv)
-{
-	(void)argc;
-	(void)argv;
-
-	init();
-	run();
-	quit();
-
-	return 0;
-}
Binary file examples/example-animation/assets/sprites/numbers.png has changed
--- a/examples/example-animation/main.c	Fri Oct 07 15:25:32 2022 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,148 +0,0 @@
-/*
- * example-animation.c -- example on how to use animations
- *
- * Copyright (c) 2020-2022 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <core/animation.h>
-#include <core/core.h>
-#include <core/event.h>
-#include <core/game.h>
-#include <core/image.h>
-#include <core/sys.h>
-#include <core/window.h>
-#include <core/painter.h>
-#include <core/panic.h>
-#include <core/sprite.h>
-#include <core/state.h>
-#include <core/texture.h>
-#include <core/util.h>
-
-#include <ui/label.h>
-#include <ui/ui.h>
-
-#include <assets/sprites/numbers.h>
-
-#define W       1280
-#define H       720
-
-static struct label label = {
-	.text = "Keys: <Space> start or reset the animation.",
-	.x = 10,
-	.y = 10,
-	.flags = LABEL_FLAGS_SHADOW
-};
-
-static struct state *states[1];
-static struct texture numbers;
-static struct animation animation;
-static struct sprite sprite;
-static int completed = 1;
-
-static void
-init(void)
-{
-	if (core_init("fr.malikania", "example-animation") < 0 || ui_init() < 0)
-		panic();
-	if (window_open("Example - Animation", W, H) < 0)
-		panic();
-	if (image_openmem(&numbers, assets_sprites_numbers, sizeof (assets_sprites_numbers)) < 0)
-		panic();
-}
-
-static void
-handle(struct state *st, const union event *ev)
-{
-	(void)st;
-
-	switch (ev->type) {
-	case EVENT_KEYDOWN:
-		switch (ev->key.key) {
-		case KEY_SPACE:
-			animation_start(&animation);
-			completed = animation_completed(&animation);
-			break;
-		default:
-			break;
-		}
-		break;
-	case EVENT_QUIT:
-		game_quit();
-		break;
-	default:
-		break;
-	}
-}
-
-static void
-update(struct state *st, unsigned int ticks)
-{
-	(void)st;
-
-	if (!completed)
-		completed = animation_update(&animation, ticks);
-}
-
-static void
-draw(struct state *st)
-{
-	(void)st;
-
-	painter_set_color(0x4f8fbaff);
-	painter_clear();
-	label_draw(&label);
-
-	if (!completed)
-		animation_draw(&animation, (window.w - sprite.cellw) / 2, (window.h - sprite.cellh) / 2);
-
-	painter_present();
-}
-
-static void
-run(void)
-{
-	struct state state = {
-		.handle = handle,
-		.update = update,
-		.draw = draw
-	};
-
-	sprite_init(&sprite, &numbers, 48, 48);
-	animation_init(&animation, &sprite, 1000);
-
-	game_init(states, UTIL_SIZE(states));
-	game_push(&state);
-	game_loop();
-}
-
-static void
-quit(void)
-{
-	window_finish();
-	ui_finish();
-	core_finish();
-}
-
-int
-main(int argc, char **argv)
-{
-	(void)argc;
-	(void)argv;
-
-	init();
-	run();
-	quit();
-}
-
Binary file examples/example-audio/assets/music/vabsounds-romance.ogg has changed
Binary file examples/example-audio/assets/sounds/fire.wav has changed
--- a/examples/example-audio/main.c	Fri Oct 07 15:25:32 2022 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,155 +0,0 @@
-/*
- * example-audio.c -- show how to use sounds and music
- *
- * Copyright (c) 2020-2022 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <core/core.h>
-#include <core/event.h>
-#include <core/game.h>
-#include <core/music.h>
-#include <core/painter.h>
-#include <core/panic.h>
-#include <core/sound.h>
-#include <core/state.h>
-#include <core/sys.h>
-#include <core/util.h>
-#include <core/window.h>
-
-#include <ui/label.h>
-#include <ui/theme.h>
-#include <ui/ui.h>
-
-#include <assets/music/vabsounds-romance.h>
-#include <assets/sounds/fire.h>
-
-#define W       1280
-#define H       720
-
-static struct state *states[1];
-static struct music music;
-static struct sound sound;
-
-static struct label label_music = {
-	.text = "Music: <Space> play, <p> pause, <r> resume, <q> stop, <l> loop.",
-	.x = 10,
-	.y = 10,
-	.flags = LABEL_FLAGS_SHADOW
-};
-
-static struct label label_sound = {
-	.text = "Sound: click anywhere to pop a sound.",
-	.x = 10,
-	.y = 30,
-	.flags = LABEL_FLAGS_SHADOW
-};
-
-static void
-init(void)
-{
-	if (core_init("fr.malikania", "example-audio") < 0 || ui_init() < 0)
-		panic();
-	if (window_open("Example - Audio", W, H) < 0)
-		panic();
-	if (music_openmem(&music, assets_music_vabsounds_romance, sizeof (assets_music_vabsounds_romance)) < 0 ||
-	    sound_openmem(&sound, assets_sounds_fire, sizeof (assets_sounds_fire)) < 0)
-		panic();
-}
-
-static void
-quit(void)
-{
-	window_finish();
-	ui_finish();
-	core_finish();
-}
-
-static void
-handle(struct state *st, const union event *ev)
-{
-	(void)st;
-
-	switch (ev->type) {
-	case EVENT_CLICKDOWN:
-		if (sound_play(&sound) < 0)
-			panic();
-		break;
-	case EVENT_KEYDOWN:
-		switch (ev->key.key) {
-		case KEY_p:
-			music_pause(&music);
-			break;
-		case KEY_r:
-			music_resume(&music);
-			break;
-		case KEY_q:
-			music_stop(&music);
-			break;
-		case KEY_l:
-			music_play(&music, MUSIC_LOOP);
-			break;
-		case KEY_SPACE:
-			music_play(&music, 0);
-			break;
-		default:
-			break;
-		}
-		break;
-	case EVENT_QUIT:
-		game_quit();
-		break;
-	default:
-		break;
-	}
-}
-
-static void
-draw(struct state *st)
-{
-	(void)st;
-
-	painter_set_color(0x006554ff);
-	painter_clear();
-	label_draw(&label_music);
-	label_draw(&label_sound);
-	painter_present();
-}
-
-static void
-run(void)
-{
-	struct state state = {
-		.handle = handle,
-		.draw = draw
-	};
-
-	game_init(states, UTIL_SIZE(states));
-	game_push(&state);
-	game_loop();
-
-	music_finish(&music);
-	sound_finish(&sound);
-}
-
-int
-main(int argc, char **argv)
-{
-	(void)argc;
-	(void)argv;
-
-	init();
-	run();
-	quit();
-}
Binary file examples/example-battle/assets/images/battle-background.png has changed
Binary file examples/example-battle/assets/images/black-cat.png has changed
Binary file examples/example-battle/assets/images/haunted-wood.png has changed
Binary file examples/example-battle/assets/sounds/fire.wav has changed
Binary file examples/example-battle/assets/sprites/explosion.png has changed
Binary file examples/example-battle/assets/sprites/john-sword.png has changed
Binary file examples/example-battle/assets/sprites/john-walk.png has changed
Binary file examples/example-battle/assets/sprites/ui-cursor.png has changed
--- a/examples/example-battle/character-john.c	Fri Oct 07 15:25:32 2022 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,50 +0,0 @@
-/*
- * character-john.c -- john character
- *
- * Copyright (c) 2020-2022 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <rpg/character.h>
-
-#include "character-john.h"
-#include "spell-fire.h"
-#include "registry.h"
-
-static void
-adventurer_reset(struct character *ch)
-{
-	/* TODO: this function should compute the attack thanks to the level. */
-	ch->hpmax = 120;
-	ch->mpmax = 50;
-	ch->atk = 50;
-	ch->def = 50;
-	ch->agt = 50;
-	ch->luck = 50;
-}
-
-const struct character character_john = {
-	.name = "John ",
-	.level = 1,
-	.hp = 120,
-	.mp = 50,
-	.reset = adventurer_reset,
-	.sprites = {
-		[CHARACTER_SPRITE_NORMAL] = &registry_sprites[REGISTRY_TEXTURE_JOHN_WALK],
-		[CHARACTER_SPRITE_SWORD] = &registry_sprites[REGISTRY_TEXTURE_JOHN_SWORD]
-	},
-	.spells = {
-		&spell_fire
-	}
-};
--- a/examples/example-battle/character-john.h	Fri Oct 07 15:25:32 2022 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,24 +0,0 @@
-/*
- * character-john.h -- john character
- *
- * Copyright (c) 2020-2022 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#ifndef EXAMPLES_BATTLE_CHARACTER_JOHN_H
-#define EXAMPLES_BATTLE_CHARACTER_JOHN_H
-
-const struct character character_john;
-
-#endif /* !EXAMPLES_BATTLE_CHARACTER_JOHN_H */
--- a/examples/example-battle/registry.c	Fri Oct 07 15:25:32 2022 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,140 +0,0 @@
-/*
- * registry.h -- registry of resources
- *
- * Copyright (c) 2020-2022 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <stddef.h>
-
-#include <core/image.h>
-#include <core/panic.h>
-#include <core/util.h>
-#include <core/sys.h>
-
-#include <assets/images/battle-background.h>
-#include <assets/images/black-cat.h>
-#include <assets/images/haunted-wood.h>
-
-#include <assets/sounds/fire.h>
-
-#include <assets/sprites/explosion.h>
-#include <assets/sprites/john-sword.h>
-#include <assets/sprites/john-walk.h>
-#include <assets/sprites/ui-cursor.h>
-
-#include "registry.h"
-
-struct texture registry_images[REGISTRY_IMAGE_NUM];
-struct texture registry_textures[REGISTRY_TEXTURE_NUM];
-struct sprite registry_sprites[REGISTRY_TEXTURE_NUM];
-struct sound registry_sounds[REGISTRY_SOUND_NUM];
-
-#define REGISTRY_IMAGE(i, data) \
-	{ (i), (data), sizeof (data) }
-
-static const struct {
-	enum registry_image index;
-	const unsigned char *data;
-	size_t datasz;
-} images[] = {
-	REGISTRY_IMAGE(REGISTRY_IMAGE_BATTLE_BACKGROUND, assets_images_battle_background)
-};
-
-#define REGISTRY_TEXTURE(s, data, cw, ch) \
-	{ (s), (data), sizeof (data), (cw), (ch) }
-
-static const struct {
-	enum registry_texture index;
-	const unsigned char *data;
-	size_t datasz;
-	unsigned int cellw;
-	unsigned int cellh;
-} textures[] = {
-	REGISTRY_TEXTURE(REGISTRY_TEXTURE_CURSOR, assets_sprites_ui_cursor, 24, 24),
-	REGISTRY_TEXTURE(REGISTRY_TEXTURE_EXPLOSION, assets_sprites_explosion, 256, 256),
-	REGISTRY_TEXTURE(REGISTRY_TEXTURE_JOHN_SWORD, assets_sprites_john_sword, 256, 256),
-	REGISTRY_TEXTURE(REGISTRY_TEXTURE_JOHN_WALK, assets_sprites_john_walk, 256, 256),
-	REGISTRY_TEXTURE(REGISTRY_TEXTURE_HAUNTED_WOOD, assets_images_haunted_wood, 0, 0),
-	REGISTRY_TEXTURE(REGISTRY_TEXTURE_BLACK_CAT, assets_images_black_cat, 0, 0)
-};
-
-#define REGISTRY_SOUND(s, data) \
-	{ (s), (data), sizeof (data) }
-
-static const struct {
-	enum registry_sound index;
-	const unsigned char *data;
-	size_t datasz;
-} sounds[] = {
-	REGISTRY_SOUND(REGISTRY_SOUND_FIRE, assets_sounds_fire)
-};
-
-static void
-load_images(void)
-{
-	for (size_t i = 0; i < UTIL_SIZE(images); ++i) {
-		struct texture *texture = &registry_images[images[i].index];
-
-		if (image_openmem(texture, images[i].data, images[i].datasz) < 0)
-			panic();
-	}
-}
-
-static void
-load_textures_and_sprites(void)
-{
-	for (size_t i = 0; i < UTIL_SIZE(textures); ++i) {
-		struct texture *texture = &registry_textures[textures[i].index];
-		struct sprite *sprite = &registry_sprites[textures[i].index];
-
-		if (image_openmem(texture, textures[i].data, textures[i].datasz) < 0)
-			panic();
-
-		if (textures[i].cellw == 0 || textures[i].cellh == 0)
-			sprite_init(sprite, texture, texture->w, texture->h);
-		else
-			sprite_init(sprite, texture, textures[i].cellw, textures[i].cellh);
-	}
-}
-
-static void
-load_sounds(void)
-{
-	for (size_t i = 0; i < UTIL_SIZE(sounds); ++i) {
-		struct sound *sound = &registry_sounds[sounds[i].index];
-
-		if (sound_openmem(sound, sounds[i].data, sounds[i].datasz) < 0)
-			panic();
-	}
-}
-
-void
-registry_init(void)
-{
-	load_images();
-	load_textures_and_sprites();
-	load_sounds();
-}
-
-void
-registry_finish(void)
-{
-	for (size_t i = 0; i < UTIL_SIZE(registry_images); ++i)
-		texture_finish(&registry_images[i]);
-	for (size_t i = 0; i < UTIL_SIZE(registry_textures); ++i)
-		texture_finish(&registry_textures[i]);
-	for (size_t i = 0; i < UTIL_SIZE(registry_sounds); ++i)
-		sound_finish(&registry_sounds[i]);
-}
--- a/examples/example-battle/registry.h	Fri Oct 07 15:25:32 2022 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,66 +0,0 @@
-/*
- * registry.h -- registry of resources
- *
- * Copyright (c) 2020-2022 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#ifndef EXAMPLES_BATTLE_REGISTRY_H
-#define EXAMPLES_BATTLE_REGISTRY_H
-
-#include <core/sound.h>
-#include <core/sprite.h>
-#include <core/texture.h>
-
-enum registry_texture {
-	/* UI. */
-	REGISTRY_TEXTURE_CURSOR,
-
-	/* Animations. */
-	REGISTRY_TEXTURE_EXPLOSION,
-
-	/* Characters. */
-	REGISTRY_TEXTURE_JOHN_WALK,
-	REGISTRY_TEXTURE_JOHN_SWORD,
-
-	/* Enemies. */
-	REGISTRY_TEXTURE_HAUNTED_WOOD,
-	REGISTRY_TEXTURE_BLACK_CAT,
-
-	/* Unused.*/
-	REGISTRY_TEXTURE_NUM
-};
-
-enum registry_image {
-	REGISTRY_IMAGE_BATTLE_BACKGROUND,
-	REGISTRY_IMAGE_NUM
-};
-
-enum registry_sound {
-	REGISTRY_SOUND_FIRE,
-	REGISTRY_SOUND_NUM
-};
-
-extern struct texture registry_images[REGISTRY_IMAGE_NUM];
-extern struct texture registry_textures[REGISTRY_TEXTURE_NUM];
-extern struct sprite registry_sprites[REGISTRY_TEXTURE_NUM];
-extern struct sound registry_sounds[REGISTRY_SOUND_NUM];
-
-void
-registry_init(void);
-
-void
-registry_finish(void);
-
-#endif /* !EXAMPLES_BATTLE_REGISTRY_H */
--- a/examples/example-battle/spell-fire.c	Fri Oct 07 15:25:32 2022 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,132 +0,0 @@
-/*
- * spell-fire.c -- example of spell: fire
- *
- * Copyright (c) 2020-2022 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <stdlib.h>
-
-#include <core/action.h>
-#include <core/animation.h>
-#include <core/drawable.h>
-#include <core/alloc.h>
-
-#include <ui/align.h>
-
-#include <rpg/battle-state-check.h>
-#include <rpg/battle-state-rendering.h>
-#include <rpg/battle.h>
-#include <rpg/character.h>
-#include <rpg/spell.h>
-
-#include "registry.h"
-#include "spell-fire.h"
-
-struct self {
-	struct battle *battle;
-	struct character *target;
-	struct animation animation;
-	struct drawable drawable;
-	unsigned int selection;
-};
-
-static int
-update(struct drawable *dw, unsigned int ticks)
-{
-	struct self *self = dw->data;
-
-	return animation_update(&self->animation, ticks);
-}
-
-static void
-draw(struct drawable *dw)
-{
-	const struct self *self = dw->data;
-	const struct battle_entity *et = self->battle->enemies[self->selection];
-	const struct sprite *sprite = et->ch->sprites[CHARACTER_SPRITE_NORMAL];
-	int x, y;
-
-	align(ALIGN_CENTER,
-	    &x, &y, self->animation.sprite->cellw, self->animation.sprite->cellh,
-	    et->x, et->y, sprite->cellw, sprite->cellh);
-
-	animation_draw(&self->animation, x, y);
-}
-
-static void
-end(struct drawable *dw)
-{
-	struct self *self = dw->data;
-	struct character *ch = self->battle->enemies[self->selection]->ch;
-
-	/* TODO: compute damage. */
-	const unsigned int damage = 100;
-
-	if ((unsigned int)ch->hp < damage)
-		ch->hp = 0;
-	else
-		ch->hp -= damage;
-
-	battle_indicator_hp(self->battle, self->battle->enemies[self->selection]->ch, 100);
-	battle_state_check(self->battle);
-}
-
-static void
-finish(struct drawable *dw)
-{
-	free(dw->data);
-}
-
-static void
-fire_select(const struct battle *bt, struct selection *slt)
-{
-	slt->index_side = 0;
-
-	selection_first(slt, bt);
-}
-
-static void
-fire_action(struct battle *bt, struct character *owner, const struct selection *slt)
-{
-	struct self *self;
-
-	(void)owner;
-
-	self = alloc_new0(sizeof (*self));
-	self->selection = slt->index_character;
-	self->battle = bt;
-	self->drawable.data = self;
-	self->drawable.update = update;
-	self->drawable.draw = draw;
-	self->drawable.finish = finish;
-	self->drawable.end = end;
-
-	animation_init(&self->animation, &registry_sprites[REGISTRY_TEXTURE_EXPLOSION], 12);
-	animation_start(&self->animation);
-
-	sound_play(&registry_sounds[REGISTRY_SOUND_FIRE]);
-	battle_state_rendering(bt, &self->drawable);
-}
-
-const struct spell spell_fire = {
-	.name = "Fire",
-	.description = "A delicate fire.",
-	.mp = 5,
-	.type = SPELL_TYPE_FIRE,
-	.select = fire_select,
-	.select_kind = SELECTION_KIND_ONE,
-	.select_side = SELECTION_SIDE_ENEMY,
-	.action = fire_action
-};
--- a/examples/example-battle/spell-fire.h	Fri Oct 07 15:25:32 2022 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,24 +0,0 @@
-/*
- * spell-fire.c -- example of spell: fire
- *
- * Copyright (c) 2020-2022 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#ifndef EXAMPLES_BATTLE_SPELL_FIRE_H
-#define EXAMPLES_BATTLE_SPELL_FIRE_H
-
-extern const struct spell spell_fire;
-
-#endif /* !EXAMPLES_BATTLE_SPELL_FIRE_H */
--- a/examples/example-cursor/main.c	Fri Oct 07 15:25:32 2022 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,148 +0,0 @@
-/*
- * example-cursor.c -- example on how to change cursor
- *
- * Copyright (c) 2020-2022 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <stdio.h>
-
-#include <core/core.h>
-#include <core/event.h>
-#include <core/game.h>
-#include <core/key.h>
-#include <core/painter.h>
-#include <core/panic.h>
-#include <core/state.h>
-#include <core/sys.h>
-#include <core/util.h>
-#include <core/window.h>
-
-#include <ui/label.h>
-#include <ui/ui.h>
-
-#define W 1280
-#define H 720
-
-static struct state *states[1];
-static char help_text[128];
-static enum window_cursor cursor = WINDOW_CURSOR_ARROW;
-
-static struct label help = {
-	.x = 10,
-	.y = 10,
-	.text = help_text,
-	.flags = LABEL_FLAGS_SHADOW
-};
-
-static void
-init(void)
-{
-	if (core_init("fr.malikania", "example-cursor") < 0 || ui_init() < 0)
-		panic();
-	if (window_open("Example - Cursor", W, H) < 0)
-		panic();
-}
-
-static void
-change(enum window_cursor cursor)
-{
-	static const char *names[] = {
-		[WINDOW_CURSOR_ARROW]           = "WINDOW_CURSOR_ARROW",
-		[WINDOW_CURSOR_EDIT]            = "WINDOW_CURSOR_EDIT",
-		[WINDOW_CURSOR_WAIT]            = "WINDOW_CURSOR_WAIT",
-		[WINDOW_CURSOR_CROSSHAIR]       = "WINDOW_CURSOR_CROSSHAIR",
-		[WINDOW_CURSOR_SIZE]            = "WINDOW_CURSOR_SIZE",
-		[WINDOW_CURSOR_NO]              = "WINDOW_CURSOR_NO",
-		[WINDOW_CURSOR_HAND]            = "WINDOW_CURSOR_HAND"
-	};
-
-	snprintf(help_text, sizeof (help_text), "Keys: <Left>/<Right> to change cursor. Current: %s", names[cursor]);
-	window_set_cursor(cursor);
-}
-
-static void
-handle(struct state *st, const union event *ev)
-{
-	(void)st;
-
-	switch (ev->type) {
-	case EVENT_KEYDOWN:
-		switch (ev->key.key) {
-		case KEY_LEFT:
-			if (cursor > 0)
-				change(--cursor);
-			break;
-		case KEY_RIGHT:
-			if (cursor + 1 < WINDOW_CURSOR_LAST)
-				change(++cursor);
-			break;
-		default:
-			break;
-		}
-
-
-		break;
-	case EVENT_QUIT:
-		game_quit();
-		break;
-	default:
-		break;
-	}
-}
-
-static void
-draw(struct state *st)
-{
-	(void)st;
-
-	painter_set_color(0xebede9ff);
-	painter_clear();
-	label_draw(&help);
-	painter_present();
-}
-
-static void
-run(void)
-{
-	struct state state = {
-		.handle = handle,
-		.draw = draw
-	};
-
-	change(cursor);
-
-	game_init(states, UTIL_SIZE(states));
-	game_push(&state);
-	game_loop();
-}
-
-static void
-quit(void)
-{
-	window_finish();
-	ui_finish();
-	core_finish();
-}
-
-int
-main(int argc, char **argv)
-{
-	(void)argc;
-	(void)argv;
-
-	init();
-	run();
-	quit();
-}
--- a/examples/example-debug/main.c	Fri Oct 07 15:25:32 2022 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,113 +0,0 @@
-/*
- * example-debug.c -- example on how to use debug interface
- *
- * Copyright (c) 2020-2022 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <core/core.h>
-#include <core/event.h>
-#include <core/game.h>
-#include <core/window.h>
-#include <core/painter.h>
-#include <core/panic.h>
-#include <core/state.h>
-#include <core/util.h>
-
-#include <ui/debug.h>
-#include <ui/theme.h>
-#include <ui/ui.h>
-
-#define W 1280
-#define H 720
-
-static struct state *states[1];
-static int mouse_x;
-static int mouse_y;
-
-static void
-init(void)
-{
-	if (core_init("fr.malikania", "example-debug") < 0 || ui_init() < 0)
-		panic();
-	if (window_open("Example - Debug", W, H) < 0)
-		panic();
-
-	debug_options.enable = 1;
-}
-
-static void
-handle(struct state *st, const union event *ev)
-{
-	(void)st;
-
-	switch (ev->type) {
-	case EVENT_MOUSE:
-		mouse_x = ev->mouse.x;
-		mouse_y = ev->mouse.y;
-		break;
-	case EVENT_QUIT:
-		game_quit();
-		break;
-	default:
-		break;
-	}
-}
-
-static void
-draw(struct state *st)
-{
-	(void)st;
-
-	struct debug_report report = {0};
-
-	painter_set_color(0x4f8fbaff);
-	painter_clear();
-	debugf(&report, "Game running.");
-	debugf(&report, "mouse: %d, %d", mouse_x, mouse_y);
-	painter_present();
-}
-
-static void
-run(void)
-{
-	struct state state = {
-		.handle = handle,
-		.draw = draw
-	};
-
-	game_init(states, UTIL_SIZE(states));
-	game_push(&state);
-	game_loop();
-}
-
-static void
-quit(void)
-{
-	window_finish();
-	ui_finish();
-	core_finish();
-}
-
-int
-main(int argc, char **argv)
-{
-	(void)argc;
-	(void)argv;
-
-	init();
-	run();
-	quit();
-}
-
Binary file examples/example-drawable/assets/sprites/explosion.png has changed
--- a/examples/example-drawable/main.c	Fri Oct 07 15:25:32 2022 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,208 +0,0 @@
-/*
- * example-drawable.c -- example on how to use automatic drawables
- *
- * Copyright (c) 2020-2022 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-
-#include <core/alloc.h>
-#include <core/animation.h>
-#include <core/core.h>
-#include <core/event.h>
-#include <core/game.h>
-#include <core/drawable.h>
-#include <core/drawable-stack.h>
-#include <core/key.h>
-#include <core/painter.h>
-#include <core/panic.h>
-#include <core/sys.h>
-#include <core/image.h>
-#include <core/sprite.h>
-#include <core/state.h>
-#include <core/texture.h>
-#include <core/util.h>
-#include <core/window.h>
-
-#include <ui/label.h>
-#include <ui/theme.h>
-#include <ui/ui.h>
-
-#include <assets/sprites/explosion.h>
-
-#define W       1280
-#define H       720
-
-static struct label help = {
-	.x = 10,
-	.y = 10,
-	.text = "Keys: <Esc> to reset. Click anywhere to spawn a drawable.",
-	.flags = LABEL_FLAGS_SHADOW
-};
-
-static struct state *states[1];
-static struct drawable *drawables[64];
-static struct drawable_stack stack;
-
-/*
- * List of drawables for this example.
- * -----------------------------------------------------------------------------
- */
-
-/* 0: Explosion animation. */
-static struct texture explosion_tex;
-static struct sprite explosion_sprite;
-
-struct explosion {
-	struct animation anim;
-	struct drawable dw;
-};
-
-static void
-init(void)
-{
-	if (core_init("fr.malikania", "example-drawable") < 0 || ui_init() < 0)
-		panic();
-	if (window_open("Example - Drawable", W, H) < 0)
-		panic();
-
-	/* 0: Explosion animation. */
-	if (image_openmem(&explosion_tex, assets_sprites_explosion, sizeof (assets_sprites_explosion)) < 0)
-		panic();
-
-	sprite_init(&explosion_sprite, &explosion_tex, 256, 256);
-}
-
-static int
-explosion_update(struct drawable *dw, unsigned int ticks)
-{
-	struct explosion *ex = dw->data;
-
-	return animation_update(&ex->anim, ticks);
-}
-
-static void
-explosion_draw(struct drawable *dw)
-{
-	struct explosion *ex = dw->data;
-
-	animation_draw(&ex->anim, ex->dw.x, ex->dw.y);
-}
-
-static void
-explosion_finish(struct drawable *dw)
-{
-	free(dw->data);
-}
-
-static void
-spawn(int x, int y)
-{
-	struct explosion *ex = alloc_new0(sizeof (*ex));
-
-	animation_init(&ex->anim, &explosion_sprite, 15);
-
-	ex->dw.data = ex;
-	ex->dw.x = x - (int)(explosion_sprite.cellw / 2);
-	ex->dw.y = y - (int)(explosion_sprite.cellh / 2);
-	ex->dw.update = explosion_update;
-	ex->dw.draw = explosion_draw;
-	ex->dw.finish = explosion_finish;
-
-	drawable_stack_add(&stack, &ex->dw);
-}
-
-static void
-handle(struct state *st, const union event *ev)
-{
-	(void)st;
-
-	switch (ev->type) {
-	case EVENT_KEYDOWN:
-		switch (ev->key.key) {
-		case KEY_ESCAPE:
-			drawable_stack_finish(&stack);
-			break;
-		default:
-			break;
-		}
-		break;
-	case EVENT_CLICKDOWN:
-		spawn(ev->click.x, ev->click.y);
-		break;
-	case EVENT_QUIT:
-		game_quit();
-		break;
-	default:
-		break;
-	}
-}
-
-static void
-update(struct state *st, unsigned int ticks)
-{
-	(void)st;
-
-	drawable_stack_update(&stack, ticks);
-
-}
-
-static void
-draw(struct state *st)
-{
-	(void)st;
-
-	painter_set_color(0xebede9ff);
-	painter_clear();
-	label_draw(&help);
-	drawable_stack_draw(&stack);
-	painter_present();
-}
-
-static void
-run(void)
-{
-	struct state state = {
-		.handle = handle,
-		.update = update,
-		.draw = draw
-	};
-
-	drawable_stack_init(&stack, drawables, UTIL_SIZE(drawables));
-
-	game_init(states, UTIL_SIZE(states));
-	game_push(&state);
-	game_loop();
-}
-
-static void
-quit(void)
-{
-	window_finish();
-	ui_finish();
-	core_finish();
-}
-
-int
-main(int argc, char **argv)
-{
-	(void)argc;
-	(void)argv;
-
-	init();
-	run();
-	quit();
-}
--- a/examples/example-font/main.c	Fri Oct 07 15:25:32 2022 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,148 +0,0 @@
-/*
- * example-font.c -- show how to use fonts
- *
- * Copyright (c) 2020-2022 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <core/core.h>
-#include <core/event.h>
-#include <core/font.h>
-#include <core/game.h>
-#include <core/painter.h>
-#include <core/panic.h>
-#include <core/state.h>
-#include <core/sys.h>
-#include <core/texture.h>
-#include <core/util.h>
-#include <core/window.h>
-
-#include <ui/theme.h>
-#include <ui/ui.h>
-
-#define W       (1280)
-#define H       (720)
-
-/* Friendly taken from: https://lospec.com/palette-list/apollo */
-static const unsigned long colors[] = {
-	0x3c5e8bff,     /* Blue. */
-	0x468232ff,     /* Green. */
-	0xad7757ff,     /* Light brown. */
-	0x884b2bff,     /* Brown. */
-	0x752438ff,     /* Red. */
-	0x7a367bff,     /* Magenta. */
-	0x151d28ff,     /* Dark */
-	0xc7cfccff,     /* Christian Grey. */
-};
-
-static struct state *states[1];
-static int ci = 0;
-static enum font_style style = FONT_STYLE_ANTIALIASED;
-
-static void
-init(void)
-{
-	if (core_init("fr.malikania", "example-font") < 0 || ui_init() < 0)
-		panic();
-	if (window_open("Example - Font", W, H) < 0)
-		panic();
-}
-
-static void
-handle(struct state *st, const union event *ev)
-{
-	(void)st;
-
-	switch (ev->type) {
-	case EVENT_KEYDOWN:
-		switch (ev->key.key) {
-		case KEY_LEFT:
-			if (ci > 0)
-				ci--;
-			break;
-		case KEY_RIGHT:
-			if ((size_t)ci < UTIL_SIZE(colors))
-				ci++;
-			break;
-		case KEY_SPACE:
-			if (style == FONT_STYLE_ANTIALIASED)
-				style = FONT_STYLE_NONE;
-			else
-				style = FONT_STYLE_ANTIALIASED;
-
-			theme_default()->fonts[THEME_FONT_INTERFACE]->style = style;
-		default:
-			break;
-		}
-		break;
-	case EVENT_QUIT:
-		game_quit();
-		break;
-	default:
-		break;
-	}
-}
-
-static void
-draw(struct state *st)
-{
-	(void)st;
-
-	struct font *font = theme_default()->fonts[THEME_FONT_INTERFACE];
-	struct texture tex;
-
-	painter_set_color(0xffffffff);
-	painter_clear();
-
-	if (font_render(font, &tex, "Example of text. Use <Left>/<Right> to change color and <Space> to toggle antialiasing.", colors[ci]) < 0)
-		panic();
-
-	texture_draw(&tex, 10, 10);
-	painter_present();
-	texture_finish(&tex);
-}
-
-static void
-run(void)
-{
-	struct state state = {
-		.handle = handle,
-		.draw = draw
-	};
-
-	game_init(states, UTIL_SIZE(states));
-	game_push(&state);
-	game_loop();
-}
-
-static void
-quit(void)
-{
-	window_finish();
-	ui_finish();
-	core_finish();
-}
-
-int
-main(int argc, char **argv)
-{
-	(void)argc;
-	(void)argv;
-
-	init();
-	run();
-	quit();
-
-	return 0;
-}
--- a/examples/example-gridmenu/main.c	Fri Oct 07 15:25:32 2022 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,132 +0,0 @@
-/*
- * example-gridmenu.c -- show how to use grid menu
- *
- * Copyright (c) 2020-2022 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <core/core.h>
-#include <core/event.h>
-#include <core/game.h>
-#include <core/painter.h>
-#include <core/panic.h>
-#include <core/state.h>
-#include <core/sys.h>
-#include <core/trace.h>
-#include <core/util.h>
-#include <core/window.h>
-
-#include <ui/align.h>
-#include <ui/gridmenu.h>
-#include <ui/label.h>
-#include <ui/theme.h>
-#include <ui/ui.h>
-
-#define W       (1280)
-#define H       (720)
-
-static struct state *states[1];
-
-static void
-init(void)
-{
-	if (core_init("fr.malikania", "example-gridmenu") < 0 || ui_init() < 0)
-		panic();
-	if (window_open("Example - Grid menu", W, H) < 0)
-		panic();
-}
-
-static void
-quit(void)
-{
-	window_finish();
-	ui_finish();
-	core_finish();
-}
-
-static void
-handle(struct state *st, const union event *ev)
-{
-	struct gridmenu *menu = st->data;
-
-	switch (ev->type) {
-	case EVENT_QUIT:
-		game_quit();
-		break;
-	default:
-		if (gridmenu_handle(st->data, ev))
-			tracef("selected index: %zu (%s)", menu->selected, menu->items[menu->selected]);
-		break;
-	}
-}
-
-static void
-draw(struct state *st)
-{
-	painter_set_color(0x4f8fbaff);
-	painter_clear();
-	gridmenu_draw(st->data);
-	painter_present();
-}
-
-static void
-run(void)
-{
-	const char * const items[] = {
-	    "Feu mineur",
-	    "Feu majeur",
-	    "Feu septième",
-	    "Glace mineure",
-	    "Glace majeure",
-	    "Glace septième",
-	    "Foudre mineure",
-	    "Foudre majeure",
-	    "Foudre septième",
-	    "Choc mineur",
-	    "Choc majeur",
-	    "Choc septième",
-	    "Portée",
-	    "Escapade",
-	    "Destruction",
-	    "Résurrection",
-	    "Double tour"
-	};
-
-	struct gridmenu menu = {0};
-	struct state state = {
-		.data = &menu,
-		.handle = handle,
-		.draw = draw,
-	};
-
-	gridmenu_init(&menu, 3, 2, items, UTIL_SIZE(items));
-	gridmenu_resize(&menu, 0, 0, 300, 100);
-
-	align(ALIGN_CENTER, &menu.x, &menu.y, menu.w, menu.h, 0, 0, W, H);
-
-	game_init(states, UTIL_SIZE(states));
-	game_push(&state);
-	game_loop();
-}
-
-int
-main(int argc, char **argv)
-{
-	(void)argc;
-	(void)argv;
-
-	init();
-	run();
-	quit();
-}
--- a/examples/example-label/main.c	Fri Oct 07 15:25:32 2022 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,186 +0,0 @@
-/*
- * example-label.c -- show how to use label and alignments
- *
- * Copyright (c) 2020-2022 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <stddef.h>
-
-#include <core/core.h>
-#include <core/event.h>
-#include <core/game.h>
-#include <core/painter.h>
-#include <core/panic.h>
-#include <core/state.h>
-#include <core/sys.h>
-#include <core/util.h>
-#include <core/window.h>
-
-#include <ui/align.h>
-#include <ui/label.h>
-#include <ui/theme.h>
-#include <ui/ui.h>
-
-#define W       (1280)
-#define H       (720)
-
-struct {
-	enum align align;
-	struct label label;
-} table[] = {
-	{
-		.align = ALIGN_TOP_LEFT,
-		.label = {
-			.text = "Top left"
-		}
-	},
-	{
-		.align = ALIGN_TOP,
-		.label = {
-			.text = "Top",
-		}
-	},
-	{
-		.align = ALIGN_TOP_RIGHT,
-		.label = {
-			.text = "Top right",
-		}
-	},
-	{
-		.align = ALIGN_RIGHT,
-		.label = {
-			.text = "Right",
-		}
-	},
-	{
-		.align = ALIGN_BOTTOM_RIGHT,
-		.label = {
-			.text = "Bottom right",
-		}
-	},
-	{
-		.align = ALIGN_BOTTOM,
-		.label = {
-			.text = "Bottom",
-		}
-	},
-	{
-		.align = ALIGN_BOTTOM_LEFT,
-		.label = {
-			.text = "Bottom left",
-		}
-	},
-	{
-		.align = ALIGN_LEFT,
-		.label = {
-			.text = "Left",
-		}
-	},
-	{
-		.align = ALIGN_CENTER,
-		.label = {
-			.text = "The world is Malikania.",
-			.flags = LABEL_FLAGS_SHADOW
-		}
-	}
-};
-
-static struct label mlabel = {
-	.text = "This one follows your mouse and is not aligned."
-};
-
-static struct state *states[1];
-
-static void
-init(void)
-{
-	if (core_init("fr.malikania", "example-label") < 0 || ui_init() < 0)
-		panic();
-	if (window_open("Example - Label", W, H) < 0)
-		panic();
-
-	for (size_t i = 0; i < UTIL_SIZE(table); ++i) {
-		struct label *l = &table[i].label;
-		unsigned int w, h;
-
-		label_query(l, &w, &h);
-		align(table[i].align, &l->x, &l->y, w, h, 0, 0, W, H);
-	}
-}
-
-static void
-quit(void)
-{
-	window_finish();
-	ui_finish();
-	core_finish();
-}
-
-static void
-handle(struct state *st, const union event *ev)
-{
-	(void)st;
-
-	switch (ev->type) {
-	case EVENT_MOUSE:
-		mlabel.x = ev->mouse.x;
-		mlabel.y = ev->mouse.y;
-		break;
-	case EVENT_QUIT:
-		game_quit();
-		break;
-	default:
-		break;
-	}
-}
-
-static void
-draw(struct state *st)
-{
-	(void)st;
-
-	painter_set_color(0x4f8fbaff);
-	painter_clear();
-
-	for (size_t i = 0; i < UTIL_SIZE(table); ++i)
-		label_draw(&table[i].label);
-
-	label_draw(&mlabel);
-	painter_present();
-}
-
-static void
-run(void)
-{
-	struct state state = {
-		.handle = handle,
-		.draw = draw
-	};
-
-	game_init(states, UTIL_SIZE(states));
-	game_push(&state);
-	game_loop();
-}
-
-int
-main(int argc, char **argv)
-{
-	(void)argc;
-	(void)argv;
-
-	init();
-	run();
-	quit();
-}
--- a/examples/example-message/main.c	Fri Oct 07 15:25:32 2022 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,350 +0,0 @@
-/*
- * example-message.c -- show how to use messages
- *
- * Copyright (c) 2020-2022 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <stddef.h>
-
-#include <core/core.h>
-#include <core/event.h>
-#include <core/game.h>
-#include <core/painter.h>
-#include <core/panic.h>
-#include <core/state.h>
-#include <core/sys.h>
-#include <core/util.h>
-#include <core/window.h>
-
-#include <ui/frame.h>
-#include <ui/theme.h>
-#include <ui/ui.h>
-
-#include <rpg/message.h>
-#include <rpg/rpg.h>
-
-#define W       (1280)
-#define H       (720)
-
-#define MW      (W * 0.75)
-#define MH      (H * 0.120)
-#define MX      ((W / 2) - (MW / 2))
-#define MY      (100)
-
-static struct state *states[1];
-
-static void
-init(void)
-{
-	if (core_init("fr.malikania", "example-message") < 0 || ui_init() < 0 || rpg_init() < 0)
-		panic();
-	if (window_open("Example - Message", W, H) < 0)
-		panic();
-}
-
-static void
-quit(void)
-{
-	window_finish();
-	rpg_finish();
-	ui_finish();
-	core_finish();
-}
-
-static void
-handle(struct state *st, const union event *ev)
-{
-	switch (ev->type) {
-	case EVENT_QUIT:
-		game_quit();
-		break;
-	default:
-		message_handle(st->data, ev);
-		break;
-	}
-}
-
-static void
-update(struct state *st, unsigned int ticks)
-{
-	if (message_update(st->data, ticks))
-		game_quit();
-}
-
-static void
-draw(struct state *st)
-{
-	painter_set_color(0xffffffff);
-	painter_clear();
-	message_draw(st->data);
-	painter_present();
-}
-
-static void
-run(struct message *msg)
-{
-	struct state state = {
-		.data = msg,
-		.handle = handle,
-		.update = update,
-		.draw = draw
-	};
-
-	message_start(msg);
-
-	game_init(states, UTIL_SIZE(states));
-	game_push(&state);
-	game_loop();
-}
-
-static void
-my_draw_frame(const struct theme *th, const struct frame *f)
-{
-	(void)th;
-
-	painter_set_color(0xff0000ff);
-	painter_draw_rectangle(f->x, f->y, f->w, f->h);
-}
-
-static void
-basic(void)
-{
-	const char * const text[] = {
-		"This is a basic message.",
-		"Vertical spacing is automatically computed.",
-		"You need to press <Enter> to close it.",
-	};
-	struct message msg = {
-		.x = MX,
-		.y = MY,
-		.w = MW,
-		.spacing = 12,
-		.lines = text,
-		.linesz = 3
-	};
-
-	message_query(&msg, NULL, &msg.h);
-	run(&msg);
-}
-
-static void
-automatic(void)
-{
-	const char * const text[] = {
-		"This is a an automatic message.",
-		"It will disappear in a few seconds.",
-		"You can still press <Enter> to close it quicker."
-	};
-	struct message msg = {
-		.x = MX,
-		.y = MY,
-		.w = MW,
-		.timeout = MESSAGE_TIMEOUT_DEFAULT,
-		.lines = text,
-		.linesz = 3,
-		.flags = MESSAGE_FLAGS_AUTOMATIC
-	};
-
-	message_query(&msg, NULL, &msg.h);
-	run(&msg);
-}
-
-static void
-fadein(void)
-{
-	const char * const text[] = {
-		"This message will fade in."
-	};
-	struct message msg = {
-		.x = MX,
-		.y = MY,
-		.w = MW,
-		.delay = MESSAGE_DELAY_DEFAULT,
-		.lines = text,
-		.linesz = 1,
-		.flags = MESSAGE_FLAGS_FADEIN
-	};
-
-	message_query(&msg, NULL, &msg.h);
-	run(&msg);
-}
-
-static void
-fadeout(void)
-{
-	const char * const text[] = {
-		"This message will fade out."
-	};
-	struct message msg = {
-		.x = MX,
-		.y = MY,
-		.w = MW,
-		.delay = MESSAGE_DELAY_DEFAULT,
-		.lines = text,
-		.linesz = 1,
-		.flags = MESSAGE_FLAGS_FADEOUT
-	};
-
-	message_query(&msg, NULL, &msg.h);
-	run(&msg);
-}
-
-static void
-fade(void)
-{
-	const char * const text[] = {
-		"This message will fade in and out."
-	};
-	struct message msg = {
-		.x = MX,
-		.y = MY,
-		.w = MW,
-		.delay = MESSAGE_DELAY_DEFAULT,
-		.lines = text,
-		.linesz = 1,
-		.flags = MESSAGE_FLAGS_FADEIN | MESSAGE_FLAGS_FADEOUT
-	};
-
-	message_query(&msg, NULL, &msg.h);
-	run(&msg);
-}
-
-static void
-question(void)
-{
-	const char * const text[] = {
-		"Okay, I've understood.",
-		"Nevermind, I'll do it again."
-	};
-	struct message msg = {
-		.x = MX,
-		.y = MY,
-		.w = MW,
-		.lines = text,
-		.linesz = 2,
-		.flags = MESSAGE_FLAGS_QUESTION
-	};
-
-	message_query(&msg, NULL, &msg.h);
-	run(&msg);
-}
-
-static void
-smallbottom(void)
-{
-	const unsigned int w = window.w / 4;
-	const unsigned int h = MH;
-	const int x = (window.w / 2) - (w / 2);
-	const int y = (window.h - h - 10);
-	const char * const text[] = {
-		"This one is small here."
-	};
-
-	struct message msg = {
-		.x = x,
-		.y = y,
-		.w = w,
-		.h = h,
-		.delay = MESSAGE_DELAY_DEFAULT,
-		.flags = MESSAGE_FLAGS_FADEIN | MESSAGE_FLAGS_FADEOUT,
-		.lines = text,
-		.linesz = 1
-	};
-
-	run(&msg);
-}
-
-static void
-toosmallh(void)
-{
-	const char * const text[] = {
-		"This one is too small in height and will emit a warning.",
-		"Because this line will be incomplete."
-	};
-	struct message msg = {
-		.x = MX,
-		.y = MY,
-		.w = MW,
-		.h = 40,
-		.lines = text,
-		.linesz = 2
-	};
-
-	run(&msg);
-}
-
-static void
-toosmallw(void)
-{
-	const char * const text[] = {
-		"This one is too small in width."
-	};
-	struct message msg = {
-		.x = MX,
-		.y = MY,
-		.w = 160,
-		.h = MH,
-		.lines = text,
-		.linesz = 1
-	};
-
-	run(&msg);
-}
-
-static void
-custom(void)
-{
-	const char * const text[] = {
-		"This one will destroy your eyes.",
-		"Because it use a terrible custom theme."
-	};
-	struct theme theme;
-	struct message msg = {
-		.x = MX,
-		.y = MY,
-		.w = MW,
-		.h = MH,
-		.lines = text,
-		.linesz = 2,
-		.theme = &theme
-	};
-
-	/* Borrow default theme and change its frame drawing. */
-	theme_shallow(&theme, NULL);
-	theme.draw_frame = my_draw_frame;
-	theme.colors[THEME_COLOR_NORMAL] = 0x0000ffff;
-
-	run(&msg);
-}
-
-int
-main(int argc, char **argv)
-{
-	(void)argc;
-	(void)argv;
-
-	init();
-	basic();
-	fadein();
-	fadeout();
-	fade();
-	automatic();
-	question();
-	smallbottom();
-	toosmallh();
-	toosmallw();
-	custom();
-	quit();
-}
Binary file examples/example-notify/assets/images/sword.png has changed
--- a/examples/example-notify/main.c	Fri Oct 07 15:25:32 2022 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,133 +0,0 @@
-/*
- * example-notify -- show how notifications work
- *
- * Copyright (c) 2020-2022 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <assert.h>
-
-#include <core/core.h>
-#include <core/event.h>
-#include <core/game.h>
-#include <core/image.h>
-#include <core/painter.h>
-#include <core/panic.h>
-#include <core/state.h>
-#include <core/texture.h>
-#include <core/util.h>
-#include <core/window.h>
-
-#include <ui/notify.h>
-#include <ui/label.h>
-#include <ui/ui.h>
-
-/* Sword by Icongeek26 (https://www.flaticon.com). */
-#include <assets/images/sword.h>
-
-#define W       1280
-#define H       720
-
-static struct label help = {
-	.text = "Keys: <Space> to generate a notification.",
-	.x = 10,
-	.y = 10,
-	.flags = LABEL_FLAGS_SHADOW
-};
-static struct texture icon;
-static struct state *states[1];
-
-static void
-init(void)
-{
-	if (core_init("fr.malikania", "example-notify") < 0 || ui_init() < 0)
-		panic();
-	if (window_open("Example - Notify", W, H) < 0)
-		panic();
-	if (image_openmem(&icon, assets_images_sword, sizeof (assets_images_sword)) < 0)
-		panic();
-}
-
-static void
-handle(struct state *st, const union event *ev)
-{
-	(void)st;
-
-	switch (ev->type) {
-	case EVENT_QUIT:
-		game_quit();
-		break;
-	case EVENT_KEYDOWN:
-		if (ev->key.key == KEY_SPACE)
-			notify(&icon, "Quest", "Quest finished.");
-		break;
-	default:
-		break;
-	}
-}
-
-static void
-update(struct state *st, unsigned int ticks)
-{
-	(void)st;
-
-	notify_update(ticks);
-}
-
-static void
-draw(struct state *st)
-{
-	(void)st;
-
-	painter_set_color(0xffffffff);
-	painter_clear();
-	label_draw(&help);
-	notify_draw();
-	painter_present();
-}
-
-static void
-run(void)
-{
-	struct state state = {
-		.handle = handle,
-		.update = update,
-		.draw = draw
-	};
-
-	game_init(states, UTIL_SIZE(states));
-	game_push(&state);
-	game_loop();
-}
-
-static void
-quit(void)
-{
-	window_finish();
-	ui_finish();
-	core_finish();
-}
-
-int
-main(int argc, char **argv)
-{
-	(void)argc;
-	(void)argv;
-
-	init();
-	run();
-	quit();
-
-	return 0;
-}
Binary file examples/example-sprite/assets/sprites/people.png has changed
--- a/examples/example-sprite/main.c	Fri Oct 07 15:25:32 2022 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,164 +0,0 @@
-/*
- * example-sprite.c -- example on how to use sprites
- *
- * Copyright (c) 2020-2022 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <stdio.h>
-
-#include <core/core.h>
-#include <core/event.h>
-#include <core/game.h>
-#include <core/image.h>
-#include <core/key.h>
-#include <core/painter.h>
-#include <core/panic.h>
-#include <core/image.h>
-#include <core/sprite.h>
-#include <core/state.h>
-#include <core/sys.h>
-#include <core/texture.h>
-#include <core/util.h>
-#include <core/window.h>
-
-#include <ui/align.h>
-#include <ui/label.h>
-#include <ui/ui.h>
-
-#include <assets/sprites/people.h>
-
-#define W       1280
-#define H       720
-#define HEADER "Keys: <Left>/<Right> and <Up/Down> to select a column/row. Current: %u, %u (total %u/%u)"
-
-static char msg[512];
-static struct texture texture;
-static struct sprite sprite;
-static unsigned int row, column;
-static struct state *states[1];
-
-static struct label help = {
-	.x = 10,
-	.y = 10,
-	.text = msg,
-	.flags = LABEL_FLAGS_SHADOW
-};
-
-static void
-changed(void)
-{
-	snprintf(msg, sizeof (msg), HEADER, column, row, sprite.ncols, sprite.nrows);
-}
-
-static void
-init(void)
-{
-	if (core_init("fr.malikania", "example-sprite") < 0 || ui_init() < 0)
-		panic();
-	if (window_open("Example - Sprite", W, H) < 0)
-		panic();
-	if (image_openmem(&texture, assets_sprites_people, sizeof (assets_sprites_people)) < 0)
-		panic();
-
-	sprite_init(&sprite, &texture, 48, 48);
-}
-
-static void
-handle(struct state *st, const union event *ev)
-{
-	(void)st;
-
-	switch (ev->type) {
-	case EVENT_KEYDOWN:
-		switch (ev->key.key) {
-		case KEY_LEFT:
-			if (column > 0)
-				column--;
-			break;
-		case KEY_RIGHT:
-			if (column + 1 < sprite.ncols)
-				column++;
-			break;
-		case KEY_UP:
-			if (row > 0)
-				row--;
-			break;
-		case KEY_DOWN:
-			if (row + 1 < sprite.nrows)
-				row++;
-			break;
-		default:
-			break;
-		}
-
-		changed();
-		break;
-	case EVENT_QUIT:
-		game_quit();
-		break;
-	default:
-		break;
-	}
-}
-
-static void
-draw(struct state *st)
-{
-	(void)st;
-
-	int x, y;
-
-	painter_set_color(0xebede9ff);
-	painter_clear();
-	align(ALIGN_CENTER, &x, &y, sprite.cellw, sprite.cellh, 0, 0, W, H);
-	sprite_draw(&sprite, row, column, x, y);
-	label_draw(&help);
-	painter_present();
-}
-
-static void
-run(void)
-{
-	struct state state = {
-		.handle = handle,
-		.draw = draw
-	};
-
-	changed();
-
-	game_init(states, UTIL_SIZE(states));
-	game_push(&state);
-	game_loop();
-}
-
-static void
-quit(void)
-{
-	texture_finish(&texture);
-	window_finish();
-	ui_finish();
-	core_finish();
-}
-
-int
-main(int argc, char **argv)
-{
-	(void)argc;
-	(void)argv;
-
-	init();
-	run();
-	quit();
-}
--- a/examples/example-trace/main.c	Fri Oct 07 15:25:32 2022 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,129 +0,0 @@
-/*
- * example-trace.c -- example on how to use custom trace handlers
- *
- * Copyright (c) 2020-2022 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <core/core.h>
-#include <core/event.h>
-#include <core/game.h>
-#include <core/sys.h>
-#include <core/window.h>
-#include <core/painter.h>
-#include <core/panic.h>
-#include <core/state.h>
-#include <core/trace.h>
-#include <core/util.h>
-
-#include <ui/theme.h>
-#include <ui/ui.h>
-
-#include "trace_hud.h"
-
-#define W 1280
-#define H 720
-
-static struct state *states[1];
-
-static void
-init(void)
-{
-	if (core_init("fr.malikania", "example-trace") < 0 || ui_init() < 0)
-		panic();
-	if (window_open("Example - Trace", W, H) < 0)
-		panic();
-
-	trace_handler = trace_hud_handler;
-}
-
-static void
-handle(struct state *st, const union event *ev)
-{
-	(void)st;
-
-	switch (ev->type) {
-	case EVENT_KEYDOWN:
-		switch (ev->key.key) {
-		case KEY_ESCAPE:
-			trace_hud_clear();
-			break;
-		default:
-			tracef("keydown pressed: %d", ev->key.key);
-			break;
-		}
-		break;
-	case EVENT_CLICKDOWN:
-		tracef("click at %d,%d", ev->click.x, ev->click.y);
-		break;
-	case EVENT_QUIT:
-		game_quit();
-		break;
-	default:
-		break;
-	}
-}
-
-static void
-update(struct state *st, unsigned int ticks)
-{
-	(void)st;
-
-	trace_hud_update(ticks);
-}
-
-static void
-draw(struct state *st)
-{
-	(void)st;
-
-	painter_set_color(0x4f8fbaff);
-	painter_clear();
-	trace_hud_draw();
-	painter_present();
-}
-
-static void
-run(void)
-{
-	struct state state = {
-		.handle = handle,
-		.update = update,
-		.draw = draw
-	};
-
-	game_init(states, UTIL_SIZE(states));
-	game_push(&state);
-	game_loop();
-}
-
-static void
-quit(void)
-{
-	window_finish();
-	ui_finish();
-	core_finish();
-}
-
-int
-main(int argc, char **argv)
-{
-	(void)argc;
-	(void)argv;
-
-	init();
-	run();
-	quit();
-}
-
--- a/examples/example-trace/trace_hud.c	Fri Oct 07 15:25:32 2022 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,145 +0,0 @@
-/*
- * trace_hud.c -- on screen hud
- *
- * Copyright (c) 2020-2022 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <assert.h>
-#include <stdio.h>
-#include <string.h>
-
-#include <core/action.h>
-#include <core/font.h>
-#include <core/trace.h>
-#include <core/window.h>
-
-#include <ui/label.h>
-#include <ui/theme.h>
-
-#include "trace_hud.h"
-
-#define LINES_MAX       (4)
-#define THEME(t)        ((t) ? (t) : theme_default())
-
-static struct {
-	char lines[LINES_MAX + 1][TRACE_LINE_MAX];
-	unsigned int elapsed;
-} data;
-
-struct trace_hud trace_hud = {
-	.timeout = TRACE_HUD_TIMEOUT_DEFAULT
-};
-
-void
-trace_hud_handler(const char *str)
-{
-	assert(str);
-
-	/* 1.Try to find an empty line. */
-	for (size_t i = 0; i < LINES_MAX; ++i) {
-		if (data.lines[i][0] == '\0') {
-			snprintf(data.lines[i], sizeof (data.lines[i]), "%s", str);
-			return;
-		}
-	}
-
-	/* 2. All lines are full, put in last one and move other. */
-	memmove(&data.lines[0], &data.lines[1], sizeof (data.lines[0]) * LINES_MAX - 1);
-	snprintf(data.lines[LINES_MAX - 1], sizeof (data.lines[0]), "%s", str);
-
-	/* 3. Reset elapsed time now. */
-	data.elapsed = 0;
-}
-
-void
-trace_hud_update(unsigned int ticks)
-{
-	data.elapsed += ticks;
-
-	/*
-	 * We have an empty line in the data.lines at LINES_MAX, so we simply so
-	 * to move the whole array.
-	 *
-	 * [0] = "abc"
-	 * [1] = "def"
-	 * [2] = "xyz"
-	 * [3] = "zef"
-	 * [n] = "ldkf"
-	 * [LINES_MAX + 1] = "\0"
-	 */
-	if (data.elapsed >= trace_hud.timeout) {
-		data.elapsed = 0;
-		memmove(&data.lines[0], &data.lines[1], sizeof (data.lines[0]) * LINES_MAX);
-	}
-}
-
-void
-trace_hud_draw(void)
-{
-	struct theme *th;
-	int x, y;
-
-	th = THEME(trace_hud.theme);
-	x = th->padding;
-	y = th->padding;
-
-	for (int i = 0; i < LINES_MAX && data.lines[i][0]; ++i) {
-		label_draw(&(struct label) {
-			.x = x,
-			.y = y,
-			.text = data.lines[i],
-			.theme = th,
-			.flags = LABEL_FLAGS_SHADOW
-		});
-
-		y += font_height(th->fonts[THEME_FONT_INTERFACE]);
-		y += th->padding;
-	}
-}
-
-void
-trace_hud_clear(void)
-{
-	memset(&data, 0, sizeof (data));
-}
-
-static int
-update(struct action *a, unsigned int ticks)
-{
-	(void)a;
-
-	trace_hud_update(ticks);
-
-	return 0;
-}
-
-static void
-draw(struct action *a)
-{
-	(void)a;
-
-	trace_hud_draw();
-}
-
-struct action *
-trace_hud_action(void)
-{
-	static struct action a = {
-		.update = update,
-		.draw = draw
-	};
-
-	return &a;
-}
--- a/examples/example-trace/trace_hud.h	Fri Oct 07 15:25:32 2022 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,49 +0,0 @@
-/*
- * trace_hud.h -- on screen hud
- *
- * Copyright (c) 2020-2022 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#ifndef EXAMPLES_TRACE_TRACE_HUD_H
-#define EXAMPLES_TRACE_TRACE_HUD_H
-
-#define TRACE_HUD_TIMEOUT_DEFAULT (3000)
-
-struct action;
-struct theme;
-
-struct trace_hud {
-	struct theme *theme;
-	unsigned int timeout;
-};
-
-extern struct trace_hud trace_hud;
-
-void
-trace_hud_handler(const char *);
-
-void
-trace_hud_update(unsigned int);
-
-void
-trace_hud_draw(void);
-
-void
-trace_hud_clear(void);
-
-struct action *
-trace_hud_action(void);
-
-#endif /* !EXAMPLES_TRACE_TRACE_HUD_H */
--- a/examples/example-ui/main.c	Fri Oct 07 15:25:32 2022 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,288 +0,0 @@
-/*
- * example-action.c -- example on how to use automatic actions
- *
- * Copyright (c) 2020-2022 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <core/core.h>
-#include <core/event.h>
-#include <core/game.h>
-#include <core/maths.h>
-#include <core/panic.h>
-#include <core/painter.h>
-#include <core/state.h>
-#include <core/sys.h>
-#include <core/util.h>
-#include <core/window.h>
-
-#include <ui/align.h>
-#include <ui/button.h>
-#include <ui/checkbox.h>
-#include <ui/frame.h>
-#include <ui/label.h>
-#include <ui/theme.h>
-#include <ui/ui.h>
-
-#define W               (1280)
-#define H               (720)
-
-#define FRAME_ORIGIN_X  (10)
-#define FRAME_ORIGIN_Y  (10)
-#define FRAME_WIDTH     (400)
-#define FRAME_HEIGHT    (200)
-
-#define HEADER_HEIGHT   (32)
-
-#define ELEMENT_HEIGHT  (20)
-
-/*
- * We design a basic UI like this.
- *
- *                    FRAME_WIDTH
- * +---------------------------------------------+--
- * | Title                                       | | HEADER_HEIGHT
- * +---------------------------------------------+--
- * | [x] Auto save                               |
- * |                                             |
- * |                                             |
- * |                                    [ Quit ] |
- * +---------------------------------------------+
- */
-static struct {
-	struct {
-		int active;
-		int x;
-		int y;
-	} motion;
-
-	struct {
-		struct frame frame;
-	} panel;
-
-	struct {
-		struct label label;
-	} header;
-
-	struct {
-		struct checkbox cb;
-		struct label label;
-	} autosave;
-
-	struct {
-		struct button button;
-	} quit;
-} ui = {
-	.panel = {
-		.frame = {
-			.x = FRAME_ORIGIN_X,
-			.y = FRAME_ORIGIN_Y,
-			.w = FRAME_WIDTH,
-			.h = FRAME_HEIGHT
-		}
-	},
-	.header = {
-		.label = {
-			.text = "Preferences",
-			.x = FRAME_ORIGIN_X,
-			.y = FRAME_ORIGIN_Y,
-			.flags = LABEL_FLAGS_SHADOW,
-		}
-	},
-	.autosave = {
-		.cb = {
-			.w = ELEMENT_HEIGHT,
-			.h = ELEMENT_HEIGHT
-		},
-		.label = {
-			.text = "Auto save game",
-			.flags = LABEL_FLAGS_SHADOW,
-		}
-	},
-	.quit = {
-		.button = {
-			.text = "Quit",
-			.h = ELEMENT_HEIGHT
-		}
-	}
-};
-
-static struct state *states[1];
-
-static void
-init(void)
-{
-	if (core_init("fr.malikania", "example-ui") < 0 || ui_init() < 0)
-		panic();
-	if (window_open("Example - UI", W, H) < 0)
-		panic();
-}
-
-static void
-resize_header(void)
-{
-	struct frame *f = &ui.panel.frame;
-	struct label *l = &ui.header.label;
-	unsigned int w, h;
-
-	/* Header. */
-	label_query(l, &w, &h);
-	align(ALIGN_LEFT, &l->x, &l->y, w, h, f->x, f->y, f->w, HEADER_HEIGHT);
-
-	l->x += theme_default()->padding;
-}
-
-static void
-resize_autosave(void)
-{
-	unsigned int padding = theme_default()->padding;
-	struct frame *f = &ui.panel.frame;
-	struct checkbox *c = &ui.autosave.cb;
-	struct label *l = &ui.autosave.label;
-
-	c->x = f->x + padding;
-	c->y = f->y + HEADER_HEIGHT + padding;
-
-	l->x = c->x + c->w + padding;
-	l->y = c->y;
-}
-
-static void
-resize_button(void)
-{
-	unsigned int padding = theme_default()->padding;
-	struct frame *f = &ui.panel.frame;
-	struct button *b = &ui.quit.button;
-
-	/* Button. */
-	b->w = f->w / 4;
-
-	align(ALIGN_BOTTOM_RIGHT, &b->x, &b->y, b->w, b->h,
-	    f->x, f->y, f->w, f->h);
-
-	b->x -= padding;
-	b->y -= padding;
-}
-
-static void
-resize(void)
-{
-	resize_header();
-	resize_autosave();
-	resize_button();
-}
-
-static int
-headerclick(int x, int y)
-{
-	return maths_is_boxed(
-	    ui.panel.frame.x,
-	    ui.panel.frame.y,
-	    ui.panel.frame.w,
-	    HEADER_HEIGHT,
-	    x,
-	    y
-	);
-}
-
-static void
-handle(struct state *st, const union event *ev)
-{
-	(void)st;
-
-	switch (ev->type) {
-	case EVENT_QUIT:
-		game_quit();
-		break;
-	case EVENT_MOUSE:
-		if (ui.motion.active) {
-			ui.panel.frame.x += ev->mouse.x - ui.motion.x;
-			ui.panel.frame.y += ev->mouse.y - ui.motion.y;
-			ui.motion.x = ev->mouse.x;
-			ui.motion.y = ev->mouse.y;
-			resize();
-		}
-		break;
-	case EVENT_CLICKDOWN:
-		if (headerclick(ev->click.x, ev->click.y)) {
-			ui.motion.active = 1;
-			ui.motion.x = ev->click.x;
-			ui.motion.y = ev->click.y;
-			window_set_cursor(WINDOW_CURSOR_SIZE);
-		}
-		break;
-	case EVENT_CLICKUP:
-		ui.motion.active = 0;
-		window_set_cursor(WINDOW_CURSOR_ARROW);
-		break;
-	default:
-		break;
-	}
-
-	checkbox_handle(&ui.autosave.cb, ev);
-
-	if (button_handle(&ui.quit.button, ev))
-		game_quit();
-}
-
-static void
-draw(struct state *st)
-{
-	(void)st;
-
-	painter_set_color(0xffffffff);
-	painter_clear();
-	frame_draw(&ui.panel.frame);
-	label_draw(&ui.header.label);
-	checkbox_draw(&ui.autosave.cb);
-	label_draw(&ui.autosave.label);
-	button_draw(&ui.quit.button);
-	painter_present();
-}
-
-static void
-run(void)
-{
-	struct state state = {
-		.handle = handle,
-		.draw = draw
-	};
-
-	resize();
-
-	game_init(states, UTIL_SIZE(states));
-	game_push(&state);
-	game_loop();
-}
-
-static void
-quit(void)
-{
-	window_finish();
-	ui_finish();
-	core_finish();
-}
-
-int
-main(int argc, char **argv)
-{
-	(void)argc;
-	(void)argv;
-
-	init();
-	run();
-	quit();
-
-	return 0;
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/examples/example/character-john.c	Sun Oct 09 13:51:03 2022 +0200
@@ -0,0 +1,50 @@
+/*
+ * character-john.c -- john character
+ *
+ * Copyright (c) 2020-2022 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <rpg/character.h>
+
+#include "character-john.h"
+#include "spell-fire.h"
+#include "registry.h"
+
+static void
+adventurer_reset(struct character *ch)
+{
+	/* TODO: this function should compute the attack thanks to the level. */
+	ch->hpmax = 120;
+	ch->mpmax = 50;
+	ch->atk = 50;
+	ch->def = 50;
+	ch->agt = 50;
+	ch->luck = 50;
+}
+
+const struct character character_john = {
+	.name = "John ",
+	.level = 1,
+	.hp = 120,
+	.mp = 50,
+	.reset = adventurer_reset,
+	.sprites = {
+		[CHARACTER_SPRITE_NORMAL] = &registry_sprites[REGISTRY_TEXTURE_JOHN_WALK],
+		[CHARACTER_SPRITE_SWORD] = &registry_sprites[REGISTRY_TEXTURE_JOHN_SWORD]
+	},
+	.spells = {
+		&spell_fire
+	}
+};
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/examples/example/character-john.h	Sun Oct 09 13:51:03 2022 +0200
@@ -0,0 +1,24 @@
+/*
+ * character-john.h -- john character
+ *
+ * Copyright (c) 2020-2022 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef EXAMPLES_BATTLE_CHARACTER_JOHN_H
+#define EXAMPLES_BATTLE_CHARACTER_JOHN_H
+
+const struct character character_john;
+
+#endif /* !EXAMPLES_BATTLE_CHARACTER_JOHN_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/examples/example/registry.c	Sun Oct 09 13:51:03 2022 +0200
@@ -0,0 +1,140 @@
+/*
+ * registry.h -- registry of resources
+ *
+ * Copyright (c) 2020-2022 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <stddef.h>
+
+#include <core/image.h>
+#include <core/panic.h>
+#include <core/util.h>
+#include <core/sys.h>
+
+#include <assets/images/battle-background.h>
+#include <assets/images/black-cat.h>
+#include <assets/images/haunted-wood.h>
+
+#include <assets/sounds/fire.h>
+
+#include <assets/sprites/explosion.h>
+#include <assets/sprites/john-sword.h>
+#include <assets/sprites/john-walk.h>
+#include <assets/sprites/ui-cursor.h>
+
+#include "registry.h"
+
+struct texture registry_images[REGISTRY_IMAGE_NUM];
+struct texture registry_textures[REGISTRY_TEXTURE_NUM];
+struct sprite registry_sprites[REGISTRY_TEXTURE_NUM];
+struct sound registry_sounds[REGISTRY_SOUND_NUM];
+
+#define REGISTRY_IMAGE(i, data) \
+	{ (i), (data), sizeof (data) }
+
+static const struct {
+	enum registry_image index;
+	const unsigned char *data;
+	size_t datasz;
+} images[] = {
+	REGISTRY_IMAGE(REGISTRY_IMAGE_BATTLE_BACKGROUND, assets_battle_background)
+};
+
+#define REGISTRY_TEXTURE(s, data, cw, ch) \
+	{ (s), (data), sizeof (data), (cw), (ch) }
+
+static const struct {
+	enum registry_texture index;
+	const unsigned char *data;
+	size_t datasz;
+	unsigned int cellw;
+	unsigned int cellh;
+} textures[] = {
+	REGISTRY_TEXTURE(REGISTRY_TEXTURE_CURSOR, assets_ui_cursor, 24, 24),
+	REGISTRY_TEXTURE(REGISTRY_TEXTURE_EXPLOSION, assets_explosion, 256, 256),
+	REGISTRY_TEXTURE(REGISTRY_TEXTURE_JOHN_SWORD, assets_john_sword, 256, 256),
+	REGISTRY_TEXTURE(REGISTRY_TEXTURE_JOHN_WALK, assets_john_walk, 256, 256),
+	REGISTRY_TEXTURE(REGISTRY_TEXTURE_HAUNTED_WOOD, assets_haunted_wood, 0, 0),
+	REGISTRY_TEXTURE(REGISTRY_TEXTURE_BLACK_CAT, assets_black_cat, 0, 0)
+};
+
+#define REGISTRY_SOUND(s, data) \
+	{ (s), (data), sizeof (data) }
+
+static const struct {
+	enum registry_sound index;
+	const unsigned char *data;
+	size_t datasz;
+} sounds[] = {
+	REGISTRY_SOUND(REGISTRY_SOUND_FIRE, assets_fire)
+};
+
+static void
+load_images(void)
+{
+	for (size_t i = 0; i < UTIL_SIZE(images); ++i) {
+		struct texture *texture = &registry_images[images[i].index];
+
+		if (image_openmem(texture, images[i].data, images[i].datasz) < 0)
+			panic();
+	}
+}
+
+static void
+load_textures_and_sprites(void)
+{
+	for (size_t i = 0; i < UTIL_SIZE(textures); ++i) {
+		struct texture *texture = &registry_textures[textures[i].index];
+		struct sprite *sprite = &registry_sprites[textures[i].index];
+
+		if (image_openmem(texture, textures[i].data, textures[i].datasz) < 0)
+			panic();
+
+		if (textures[i].cellw == 0 || textures[i].cellh == 0)
+			sprite_init(sprite, texture, texture->w, texture->h);
+		else
+			sprite_init(sprite, texture, textures[i].cellw, textures[i].cellh);
+	}
+}
+
+static void
+load_sounds(void)
+{
+	for (size_t i = 0; i < UTIL_SIZE(sounds); ++i) {
+		struct sound *sound = &registry_sounds[sounds[i].index];
+
+		if (sound_openmem(sound, sounds[i].data, sounds[i].datasz) < 0)
+			panic();
+	}
+}
+
+void
+registry_init(void)
+{
+	load_images();
+	load_textures_and_sprites();
+	load_sounds();
+}
+
+void
+registry_finish(void)
+{
+	for (size_t i = 0; i < UTIL_SIZE(registry_images); ++i)
+		texture_finish(&registry_images[i]);
+	for (size_t i = 0; i < UTIL_SIZE(registry_textures); ++i)
+		texture_finish(&registry_textures[i]);
+	for (size_t i = 0; i < UTIL_SIZE(registry_sounds); ++i)
+		sound_finish(&registry_sounds[i]);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/examples/example/registry.h	Sun Oct 09 13:51:03 2022 +0200
@@ -0,0 +1,66 @@
+/*
+ * registry.h -- registry of resources
+ *
+ * Copyright (c) 2020-2022 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef EXAMPLES_BATTLE_REGISTRY_H
+#define EXAMPLES_BATTLE_REGISTRY_H
+
+#include <core/sound.h>
+#include <core/sprite.h>
+#include <core/texture.h>
+
+enum registry_texture {
+	/* UI. */
+	REGISTRY_TEXTURE_CURSOR,
+
+	/* Animations. */
+	REGISTRY_TEXTURE_EXPLOSION,
+
+	/* Characters. */
+	REGISTRY_TEXTURE_JOHN_WALK,
+	REGISTRY_TEXTURE_JOHN_SWORD,
+
+	/* Enemies. */
+	REGISTRY_TEXTURE_HAUNTED_WOOD,
+	REGISTRY_TEXTURE_BLACK_CAT,
+
+	/* Unused.*/
+	REGISTRY_TEXTURE_NUM
+};
+
+enum registry_image {
+	REGISTRY_IMAGE_BATTLE_BACKGROUND,
+	REGISTRY_IMAGE_NUM
+};
+
+enum registry_sound {
+	REGISTRY_SOUND_FIRE,
+	REGISTRY_SOUND_NUM
+};
+
+extern struct texture registry_images[REGISTRY_IMAGE_NUM];
+extern struct texture registry_textures[REGISTRY_TEXTURE_NUM];
+extern struct sprite registry_sprites[REGISTRY_TEXTURE_NUM];
+extern struct sound registry_sounds[REGISTRY_SOUND_NUM];
+
+void
+registry_init(void);
+
+void
+registry_finish(void);
+
+#endif /* !EXAMPLES_BATTLE_REGISTRY_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/examples/example/spell-fire.c	Sun Oct 09 13:51:03 2022 +0200
@@ -0,0 +1,132 @@
+/*
+ * spell-fire.c -- example of spell: fire
+ *
+ * Copyright (c) 2020-2022 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <stdlib.h>
+
+#include <core/action.h>
+#include <core/animation.h>
+#include <core/drawable.h>
+#include <core/alloc.h>
+
+#include <ui/align.h>
+
+#include <rpg/battle-state-check.h>
+#include <rpg/battle-state-rendering.h>
+#include <rpg/battle.h>
+#include <rpg/character.h>
+#include <rpg/spell.h>
+
+#include "registry.h"
+#include "spell-fire.h"
+
+struct self {
+	struct battle *battle;
+	struct character *target;
+	struct animation animation;
+	struct drawable drawable;
+	unsigned int selection;
+};
+
+static int
+update(struct drawable *dw, unsigned int ticks)
+{
+	struct self *self = dw->data;
+
+	return animation_update(&self->animation, ticks);
+}
+
+static void
+draw(struct drawable *dw)
+{
+	const struct self *self = dw->data;
+	const struct battle_entity *et = self->battle->enemies[self->selection];
+	const struct sprite *sprite = et->ch->sprites[CHARACTER_SPRITE_NORMAL];
+	int x, y;
+
+	align(ALIGN_CENTER,
+	    &x, &y, self->animation.sprite->cellw, self->animation.sprite->cellh,
+	    et->x, et->y, sprite->cellw, sprite->cellh);
+
+	animation_draw(&self->animation, x, y);
+}
+
+static void
+end(struct drawable *dw)
+{
+	struct self *self = dw->data;
+	struct character *ch = self->battle->enemies[self->selection]->ch;
+
+	/* TODO: compute damage. */
+	const unsigned int damage = 100;
+
+	if ((unsigned int)ch->hp < damage)
+		ch->hp = 0;
+	else
+		ch->hp -= damage;
+
+	battle_indicator_hp(self->battle, self->battle->enemies[self->selection]->ch, 100);
+	battle_state_check(self->battle);
+}
+
+static void
+finish(struct drawable *dw)
+{
+	free(dw->data);
+}
+
+static void
+fire_select(const struct battle *bt, struct selection *slt)
+{
+	slt->index_side = 0;
+
+	selection_first(slt, bt);
+}
+
+static void
+fire_action(struct battle *bt, struct character *owner, const struct selection *slt)
+{
+	struct self *self;
+
+	(void)owner;
+
+	self = alloc_new0(sizeof (*self));
+	self->selection = slt->index_character;
+	self->battle = bt;
+	self->drawable.data = self;
+	self->drawable.update = update;
+	self->drawable.draw = draw;
+	self->drawable.finish = finish;
+	self->drawable.end = end;
+
+	animation_init(&self->animation, &registry_sprites[REGISTRY_TEXTURE_EXPLOSION], 12);
+	animation_start(&self->animation);
+
+	sound_play(&registry_sounds[REGISTRY_SOUND_FIRE]);
+	battle_state_rendering(bt, &self->drawable);
+}
+
+const struct spell spell_fire = {
+	.name = "Fire",
+	.description = "A delicate fire.",
+	.mp = 5,
+	.type = SPELL_TYPE_FIRE,
+	.select = fire_select,
+	.select_kind = SELECTION_KIND_ONE,
+	.select_side = SELECTION_SIDE_ENEMY,
+	.action = fire_action
+};
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/examples/example/spell-fire.h	Sun Oct 09 13:51:03 2022 +0200
@@ -0,0 +1,24 @@
+/*
+ * spell-fire.c -- example of spell: fire
+ *
+ * Copyright (c) 2020-2022 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef EXAMPLES_BATTLE_SPELL_FIRE_H
+#define EXAMPLES_BATTLE_SPELL_FIRE_H
+
+extern const struct spell spell_fire;
+
+#endif /* !EXAMPLES_BATTLE_SPELL_FIRE_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/examples/example/trace_hud.c	Sun Oct 09 13:51:03 2022 +0200
@@ -0,0 +1,145 @@
+/*
+ * trace_hud.c -- on screen hud
+ *
+ * Copyright (c) 2020-2022 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <assert.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <core/action.h>
+#include <core/font.h>
+#include <core/trace.h>
+#include <core/window.h>
+
+#include <ui/label.h>
+#include <ui/theme.h>
+
+#include "trace_hud.h"
+
+#define LINES_MAX       (4)
+#define THEME(t)        ((t) ? (t) : theme_default())
+
+static struct {
+	char lines[LINES_MAX + 1][TRACE_LINE_MAX];
+	unsigned int elapsed;
+} data;
+
+struct trace_hud trace_hud = {
+	.timeout = TRACE_HUD_TIMEOUT_DEFAULT
+};
+
+void
+trace_hud_handler(const char *str)
+{
+	assert(str);
+
+	/* 1.Try to find an empty line. */
+	for (size_t i = 0; i < LINES_MAX; ++i) {
+		if (data.lines[i][0] == '\0') {
+			snprintf(data.lines[i], sizeof (data.lines[i]), "%s", str);
+			return;
+		}
+	}
+
+	/* 2. All lines are full, put in last one and move other. */
+	memmove(&data.lines[0], &data.lines[1], sizeof (data.lines[0]) * LINES_MAX - 1);
+	snprintf(data.lines[LINES_MAX - 1], sizeof (data.lines[0]), "%s", str);
+
+	/* 3. Reset elapsed time now. */
+	data.elapsed = 0;
+}
+
+void
+trace_hud_update(unsigned int ticks)
+{
+	data.elapsed += ticks;
+
+	/*
+	 * We have an empty line in the data.lines at LINES_MAX, so we simply so
+	 * to move the whole array.
+	 *
+	 * [0] = "abc"
+	 * [1] = "def"
+	 * [2] = "xyz"
+	 * [3] = "zef"
+	 * [n] = "ldkf"
+	 * [LINES_MAX + 1] = "\0"
+	 */
+	if (data.elapsed >= trace_hud.timeout) {
+		data.elapsed = 0;
+		memmove(&data.lines[0], &data.lines[1], sizeof (data.lines[0]) * LINES_MAX);
+	}
+}
+
+void
+trace_hud_draw(void)
+{
+	struct theme *th;
+	int x, y;
+
+	th = THEME(trace_hud.theme);
+	x = th->padding;
+	y = th->padding;
+
+	for (int i = 0; i < LINES_MAX && data.lines[i][0]; ++i) {
+		label_draw(&(struct label) {
+			.x = x,
+			.y = y,
+			.text = data.lines[i],
+			.theme = th,
+			.flags = LABEL_FLAGS_SHADOW
+		});
+
+		y += font_height(th->fonts[THEME_FONT_INTERFACE]);
+		y += th->padding;
+	}
+}
+
+void
+trace_hud_clear(void)
+{
+	memset(&data, 0, sizeof (data));
+}
+
+static int
+update(struct action *a, unsigned int ticks)
+{
+	(void)a;
+
+	trace_hud_update(ticks);
+
+	return 0;
+}
+
+static void
+draw(struct action *a)
+{
+	(void)a;
+
+	trace_hud_draw();
+}
+
+struct action *
+trace_hud_action(void)
+{
+	static struct action a = {
+		.update = update,
+		.draw = draw
+	};
+
+	return &a;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/examples/example/trace_hud.h	Sun Oct 09 13:51:03 2022 +0200
@@ -0,0 +1,49 @@
+/*
+ * trace_hud.h -- on screen hud
+ *
+ * Copyright (c) 2020-2022 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef EXAMPLES_TRACE_TRACE_HUD_H
+#define EXAMPLES_TRACE_TRACE_HUD_H
+
+#define TRACE_HUD_TIMEOUT_DEFAULT (3000)
+
+struct action;
+struct theme;
+
+struct trace_hud {
+	struct theme *theme;
+	unsigned int timeout;
+};
+
+extern struct trace_hud trace_hud;
+
+void
+trace_hud_handler(const char *);
+
+void
+trace_hud_update(unsigned int);
+
+void
+trace_hud_draw(void);
+
+void
+trace_hud_clear(void);
+
+struct action *
+trace_hud_action(void);
+
+#endif /* !EXAMPLES_TRACE_TRACE_HUD_H */
--- a/src/libmlk-core-js/CMakeLists.txt	Fri Oct 07 15:25:32 2022 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,73 +0,0 @@
-#
-# CMakeLists.txt -- CMake build system for Molko's Engine
-#
-# Copyright (c) 2020-2022 David Demelier <markand@malikania.fr>
-#
-# Permission to use, copy, modify, and/or distribute this software for any
-# purpose with or without fee is hereby granted, provided that the above
-# copyright notice and this permission notice appear in all copies.
-#
-# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
-# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
-# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
-# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
-# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
-# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
-# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-#
-
-project(libmlk-core-js)
-
-set(
-	SOURCES
-	${libmlk-core-js_SOURCE_DIR}/core/js-action-stack.c
-	${libmlk-core-js_SOURCE_DIR}/core/js-action-stack.h
-	${libmlk-core-js_SOURCE_DIR}/core/js-action.c
-	${libmlk-core-js_SOURCE_DIR}/core/js-action.h
-	${libmlk-core-js_SOURCE_DIR}/core/js-animation.c
-	${libmlk-core-js_SOURCE_DIR}/core/js-animation.h
-	${libmlk-core-js_SOURCE_DIR}/core/js-clock.c
-	${libmlk-core-js_SOURCE_DIR}/core/js-clock.h
-	${libmlk-core-js_SOURCE_DIR}/core/js-color.c
-	${libmlk-core-js_SOURCE_DIR}/core/js-color.h
-	${libmlk-core-js_SOURCE_DIR}/core/js-core.c
-	${libmlk-core-js_SOURCE_DIR}/core/js-core.h
-	${libmlk-core-js_SOURCE_DIR}/core/js-drawable-stack.c
-	${libmlk-core-js_SOURCE_DIR}/core/js-drawable-stack.h
-	${libmlk-core-js_SOURCE_DIR}/core/js-drawable.c
-	${libmlk-core-js_SOURCE_DIR}/core/js-drawable.h
-	${libmlk-core-js_SOURCE_DIR}/core/js-event.c
-	${libmlk-core-js_SOURCE_DIR}/core/js-event.h
-	${libmlk-core-js_SOURCE_DIR}/core/js-font.c
-	${libmlk-core-js_SOURCE_DIR}/core/js-font.h
-	${libmlk-core-js_SOURCE_DIR}/core/js-game.c
-	${libmlk-core-js_SOURCE_DIR}/core/js-game.h
-	${libmlk-core-js_SOURCE_DIR}/core/js-music.c
-	${libmlk-core-js_SOURCE_DIR}/core/js-music.h
-	${libmlk-core-js_SOURCE_DIR}/core/js-painter.c
-	${libmlk-core-js_SOURCE_DIR}/core/js-painter.h
-	${libmlk-core-js_SOURCE_DIR}/core/js-panic.c
-	${libmlk-core-js_SOURCE_DIR}/core/js-panic.h
-	${libmlk-core-js_SOURCE_DIR}/core/js-sound.c
-	${libmlk-core-js_SOURCE_DIR}/core/js-sound.h
-	${libmlk-core-js_SOURCE_DIR}/core/js-sprite.c
-	${libmlk-core-js_SOURCE_DIR}/core/js-sprite.h
-	${libmlk-core-js_SOURCE_DIR}/core/js-state.c
-	${libmlk-core-js_SOURCE_DIR}/core/js-state.h
-	${libmlk-core-js_SOURCE_DIR}/core/js-texture.c
-	${libmlk-core-js_SOURCE_DIR}/core/js-texture.h
-	${libmlk-core-js_SOURCE_DIR}/core/js-trace.c
-	${libmlk-core-js_SOURCE_DIR}/core/js-trace.h
-	${libmlk-core-js_SOURCE_DIR}/core/js-window.c
-	${libmlk-core-js_SOURCE_DIR}/core/js-window.h
-)
-
-mlk_library(
-	NAME libmlk-core-js
-	SOURCES ${SOURCES}
-	LIBRARIES libmlk-core libmlk-duktape
-	INCLUDES
-		PUBLIC $<BUILD_INTERFACE:${libmlk-core-js_SOURCE_DIR}>
-)
-
-source_group(TREE ${libmlk-core-js_SOURCE_DIR} FILES ${SOURCES})
--- a/src/libmlk-core-js/core/js-action-stack.c	Fri Oct 07 15:25:32 2022 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,143 +0,0 @@
-/*
- * js-drawable-stack.h -- core drawable_stack binding
- *
- * Copyright (c) 2020-2022 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <assert.h>
-
-#include <core/action.h>
-#include <core/action-stack.h>
-#include <core/alloc.h>
-
-#include "js-event.h"
-#include "js-action-stack.h"
-#include "js-action.h"
-
-#define SIGNATURE DUK_HIDDEN_SYMBOL("Mlk.ActionStack")
-
-static inline struct action_stack *
-self(duk_context *ctx)
-{
-	struct action_stack *sf = NULL;
-
-	duk_push_this(ctx);
-	duk_get_prop_string(ctx, -1, SIGNATURE);
-	sf = duk_to_pointer(ctx, -1);
-	duk_pop_2(ctx);
-
-	if (!sf)
-		return (void)duk_error(ctx, DUK_ERR_TYPE_ERROR, "not a ActionStack object"), NULL;
-
-	return sf;
-}
-
-static duk_ret_t
-ActionStack_constructor(duk_context *ctx)
-{
-	duk_push_this(ctx);
-	duk_push_pointer(ctx, alloc_new0(sizeof (struct action_stack)));
-	duk_put_prop_string(ctx, -2, SIGNATURE);
-	duk_pop(ctx);
-
-	return 0;
-}
-
-static duk_ret_t
-ActionStack_destructor(duk_context *ctx)
-{
-	struct action_stack *st;
-
-	duk_get_prop_string(ctx, 0, SIGNATURE);
-
-	if ((st = duk_to_pointer(ctx, -1))) {
-		action_stack_finish(st);
-		free(st);
-	}
-
-	duk_pop(ctx);
-	duk_del_prop_string(ctx, 0, SIGNATURE);
-
-	return 0;
-}
-
-static duk_ret_t
-ActionStack_add(duk_context *ctx)
-{
-	struct action_stack *st = self(ctx);
-	struct js_action *act = js_action_require(ctx, 0);
-
-	if (action_stack_add(st, &act->act) < 0)
-		action_finish(&act->act);
-	else {
-		duk_push_this(ctx);
-		act->parent = duk_get_heapptr(ctx, -1);
-		duk_push_sprintf(ctx, "%p", act);
-		duk_dup(ctx, 0);
-		duk_put_prop(ctx, -3);
-		duk_pop(ctx);
-	}
-
-	return 0;
-}
-
-static duk_ret_t
-ActionStack_handle(duk_context *ctx)
-{
-	action_stack_handle(self(ctx), js_event_require(ctx, 0));
-
-	return 0;
-}
-
-static duk_ret_t
-ActionStack_update(duk_context *ctx)
-{
-	struct action_stack *st = self(ctx);
-	const unsigned int ticks = duk_require_uint(ctx, 0);
-
-	duk_push_uint(ctx, action_stack_update(st, ticks));
-
-	return 1;
-}
-
-static duk_ret_t
-ActionStack_draw(duk_context *ctx)
-{
-	action_stack_draw(self(ctx));
-
-	return 0;
-}
-
-static duk_function_list_entry methods[] = {
-	{ "add",        ActionStack_add,        1       },
-	{ "handle",     ActionStack_handle,     1       },
-	{ "update",     ActionStack_update,     1       },
-	{ "draw",       ActionStack_draw,       0       },
-	{ NULL,         NULL,                   0       }
-};
-
-void
-js_action_stack_bind(duk_context *ctx)
-{
-	assert(ctx);
-
-	duk_push_c_function(ctx, ActionStack_constructor, 0);
-	duk_push_object(ctx);
-	duk_put_function_list(ctx, -1, methods);
-	duk_push_c_function(ctx, ActionStack_destructor, 1);
-	duk_set_finalizer(ctx, -2);
-	duk_put_prop_string(ctx, -2, "prototype");
-	duk_put_global_string(ctx, "ActionStack");
-}
--- a/src/libmlk-core-js/core/js-action-stack.h	Fri Oct 07 15:25:32 2022 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,27 +0,0 @@
-/*
- * js-action-stack.h -- core action_stack binding
- *
- * Copyright (c) 2020-2022 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#ifndef MLK_CORE_JS_ACTION_STACK_H
-#define MLK_CORE_JS_ACTION_STACK_H
-
-#include <duktape.h>
-
-void
-js_action_stack_bind(duk_context *);
-
-#endif /* !MLK_CORE_JS_ACTION_STACK_H */
--- a/src/libmlk-core-js/core/js-action.c	Fri Oct 07 15:25:32 2022 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,193 +0,0 @@
-/*
- * js-action.c -- core action binding
- *
- * Copyright (c) 2020-2022 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <assert.h>
-#include <stdlib.h>
-
-#include <core/alloc.h>
-#include <core/action.h>
-
-#include "js-action.h"
-#include "js-event.h"
-
-#define SIGNATURE DUK_HIDDEN_SYMBOL("Mlk.Action")
-
-static inline duk_context *
-callable(struct js_action *data, const char *prop)
-{
-	duk_context *ctx;
-
-	if (!data->ptr)
-		return NULL;
-
-	duk_push_heapptr(data->ctx, data->ptr);
-	duk_get_prop_string(data->ctx, -1, prop);
-	duk_pull(data->ctx, -2);
-
-	if (duk_is_callable(data->ctx, -2))
-		ctx = data->ctx;
-	else {
-		ctx = NULL;
-		duk_pop_n(data->ctx, 2);
-	}
-
-	return ctx;
-}
-
-static void
-handle(struct action *data, const union event *ev)
-{
-	duk_context *ctx;
-
-	if ((ctx = callable(data->data, "handle"))) {
-		js_event_push(ctx, ev);
-		duk_call_method(ctx, 1);
-		duk_pop(ctx);
-	}
-}
-
-static int
-update(struct action *data, unsigned int ticks)
-{
-	duk_context *ctx;
-	int ret = 0;
-
-	if ((ctx = callable(data->data, "update"))) {
-		duk_push_uint(ctx, ticks);
-		duk_call_method(ctx, 1);
-		ret = duk_to_int(ctx, -1);
-		duk_pop(ctx);
-	}
-
-	return ret;
-}
-
-static void
-draw(struct action *data)
-{
-	duk_context *ctx;
-
-	if ((ctx = callable(data->data, "draw"))) {
-		duk_call_method(ctx, 0);
-		duk_pop(ctx);
-	}
-}
-
-static void
-end(struct action *data)
-{
-	duk_context *ctx;
-
-	if ((ctx = callable(data->data, "end"))) {
-		duk_call_method(ctx, 0);
-		duk_pop(ctx);
-	}
-}
-
-static void
-finish(struct action *act)
-{
-	struct js_action *data = act->data;
-
-	/* I must not be called anymore. */
-	data->ptr = NULL;
-
-	/* Remove myself from parent stack if any. */
-	if (data->parent) {
-		duk_push_heapptr(data->ctx, data->parent);
-		duk_push_sprintf(data->ctx, "%p", data);
-		duk_del_prop(data->ctx, -2);
-		duk_pop(data->ctx);
-		data->parent = NULL;
-	}
-
-	if (--data->refc == 0)
-		free(data);
-}
-
-static duk_ret_t
-Action_constructor(duk_context *ctx)
-{
-	struct js_action *data;
-
-	data = alloc_new0(sizeof (*data));
-	data->ctx = ctx;
-	data->refc = 1;
-	data->act.data = data;
-	data->act.handle = handle;
-	data->act.update = update;
-	data->act.finish = finish;
-	data->act.draw = draw;
-	data->act.end = end;
-
-	duk_push_this(ctx);
-	data->ptr = duk_get_heapptr(ctx, -1);
-	duk_push_pointer(ctx, data);
-	duk_put_prop_string(ctx, -2, SIGNATURE);
-	duk_pop(ctx);
-
-	return 0;
-}
-
-static duk_ret_t
-Action_destructor(duk_context *ctx)
-{
-	struct js_action *data;
-
-	duk_get_prop_string(ctx, 0, SIGNATURE);
-
-	if ((data = duk_to_pointer(ctx, -1)))
-		action_finish(&data->act);
-
-	duk_del_prop_string(ctx, 0, SIGNATURE);
-	duk_pop(ctx);
-
-	return 0;
-}
-
-void
-js_action_bind(duk_context *ctx)
-{
-	assert(ctx);
-
-	duk_push_c_function(ctx, Action_constructor, 0);
-	duk_push_object(ctx);
-	duk_push_c_function(ctx, Action_destructor, 1);
-	duk_set_finalizer(ctx, -2);
-	duk_put_prop_string(ctx, -2, "prototype");
-	duk_put_global_string(ctx, "Action");
-}
-
-struct js_action *
-js_action_require(duk_context *ctx, duk_idx_t idx)
-{
-	struct js_action *act = NULL;
-
-	if (duk_is_object(ctx, idx)) {
-		duk_get_prop_string(ctx, idx, SIGNATURE);
-		act = duk_to_pointer(ctx, -1);
-		duk_pop(ctx);
-	}
-
-	if (!act)
-		return (void)duk_error(ctx, DUK_ERR_TYPE_ERROR, "not a Action object"), NULL;
-
-	act->refc++;
-
-	return act;
-}
--- a/src/libmlk-core-js/core/js-action.h	Fri Oct 07 15:25:32 2022 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,40 +0,0 @@
-/*
- * js-action.h -- core action binding
- *
- * Copyright (c) 2020-2022 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#ifndef MLK_CORE_JS_ACTION_H
-#define MLK_CORE_JS_ACTION_H
-
-#include <duktape.h>
-
-#include <core/action.h>
-
-struct js_action {
-	duk_context *ctx;
-	void *ptr;
-	void *parent;
-	unsigned int refc;
-	struct action act;
-};
-
-void
-js_action_bind(duk_context *);
-
-struct js_action *
-js_action_require(duk_context *, duk_idx_t);
-
-#endif /* !MLK_CORE_JS_ACTION_H */
--- a/src/libmlk-core-js/core/js-animation.c	Fri Oct 07 15:25:32 2022 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,140 +0,0 @@
-/*
- * js-animation.c -- core animation binding
- *
- * Copyright (c) 2020-2022 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <assert.h>
-
-#include <core/alloc.h>
-#include <core/animation.h>
-
-#include "js-animation.h"
-#include "js-sprite.h"
-
-#define SIGNATURE DUK_HIDDEN_SYMBOL("Mlk.Animation")
-#define SPRITEREF DUK_HIDDEN_SYMBOL("Mlk.Animation.sprite")
-
-static inline struct animation *
-self(duk_context *ctx)
-{
-	struct animation *anim;
-
-	duk_push_this(ctx);
-	duk_get_prop_string(ctx, -1, SIGNATURE);
-	anim = duk_to_pointer(ctx, -1);
-	duk_pop_2(ctx);
-
-	if (!anim)
-		return (void)duk_error(ctx, DUK_ERR_TYPE_ERROR, "not a Animation object"), NULL;
-
-	return anim;
-}
-
-static duk_ret_t
-Animation_getComplete(duk_context *ctx)
-{
-	duk_push_boolean(ctx, animation_completed(self(ctx)));
-
-	return 1;
-}
-
-static duk_ret_t
-Animation_start(duk_context *ctx)
-{
-	animation_start(self(ctx));
-
-	return 0;
-}
-
-static duk_ret_t
-Animation_update(duk_context *ctx)
-{
-	struct animation *anim = self(ctx);
-	const unsigned int dt = duk_require_uint(ctx, 0);
-
-	duk_push_boolean(ctx, animation_update(anim, dt));
-
-	return 1;
-}
-
-static duk_ret_t
-Animation_draw(duk_context *ctx)
-{
-	const struct animation *anim = self(ctx);
-	const int x = duk_require_int(ctx, 0);
-	const int y = duk_require_int(ctx, 1);
-
-	if (!animation_completed(anim))
-		animation_draw(anim, x, y);
-
-	return 0;
-}
-
-static duk_ret_t
-Animation_constructor(duk_context *ctx)
-{
-	const struct sprite *sprite = js_sprite_require(ctx, 0);
-	const unsigned int delay = duk_require_uint(ctx, 1);
-	struct animation *anim;
-
-	anim = alloc_new0(sizeof (*anim));
-	animation_init(anim, sprite, delay);
-
-	duk_push_this(ctx);
-	duk_push_pointer(ctx, anim);
-	duk_put_prop_string(ctx, -2, SIGNATURE);
-	duk_push_string(ctx, "complete");
-	duk_push_c_function(ctx, Animation_getComplete, 0);
-	duk_def_prop(ctx, -3, DUK_DEFPROP_HAVE_GETTER);
-	duk_pop(ctx);
-
-	return 0;
-}
-
-static duk_ret_t
-Animation_destructor(duk_context *ctx)
-{
-	duk_get_prop_string(ctx, 0, SIGNATURE);
-	free(duk_to_pointer(ctx, -1));
-	duk_pop(ctx);
-	duk_del_prop_string(ctx, 0, SIGNATURE);
-
-	/* Remove sprite reference. */
-	duk_del_prop_string(ctx, 0, SPRITEREF);
-
-	return 0;
-}
-
-static const duk_function_list_entry methods[] = {
-	{ "start",              Animation_start,        0               },
-	{ "update",             Animation_update,       1               },
-	{ "draw",               Animation_draw,         2               },
-	{ NULL,                 NULL,                   0               }
-};
-
-void
-js_animation_bind(duk_context *ctx)
-{
-	assert(ctx);
-
-	duk_push_c_function(ctx, Animation_constructor, 2);
-	duk_push_object(ctx);
-	duk_put_function_list(ctx, -1, methods);
-	duk_push_c_function(ctx, Animation_destructor, 1);
-	duk_set_finalizer(ctx, -2);
-	duk_put_prop_string(ctx, -2, "prototype");
-	duk_put_global_string(ctx, "Animation");
-}
--- a/src/libmlk-core-js/core/js-animation.h	Fri Oct 07 15:25:32 2022 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,27 +0,0 @@
-/*
- * js-animation.h -- core animation binding
- *
- * Copyright (c) 2020-2022 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#ifndef MLK_CORE_JS_ANIMATION_H
-#define MLK_CORE_JS_ANIMATION_H
-
-#include <duktape.h>
-
-void
-js_animation_bind(duk_context *);
-
-#endif /* !MLK_CORE_JS_ANIMATION_H */
--- a/src/libmlk-core-js/core/js-clock.c	Fri Oct 07 15:25:32 2022 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,107 +0,0 @@
-/*
- * js-clock.c -- core clock binding
- *
- * Copyright (c) 2020-2022 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <assert.h>
-
-#include <core/alloc.h>
-#include <core/clock.h>
-
-#include "js-clock.h"
-
-#define SIGNATURE DUK_HIDDEN_SYMBOL("Mlk.Clock")
-
-static inline struct clock *
-self(duk_context *ctx)
-{
-	struct clock *clk;
-
-	duk_push_this(ctx);
-	duk_get_prop_string(ctx, -1, SIGNATURE);
-	clk = duk_to_pointer(ctx, -1);
-	duk_pop_2(ctx);
-
-	if (!clk)
-		return (void)duk_error(ctx, DUK_ERR_TYPE_ERROR, "not a Clock object"), NULL;
-
-	return clk;
-}
-
-static duk_ret_t
-Clock_elapsed(duk_context *ctx)
-{
-	duk_push_number(ctx, clock_elapsed(self(ctx)));
-
-	return 1;
-}
-
-static duk_ret_t
-Clock_constructor(duk_context *ctx)
-{
-	struct clock *clk;
-
-	clk = alloc_new(sizeof (*clk));
-	clock_start(clk);
-
-	duk_push_this(ctx);
-	duk_push_pointer(ctx, clk);
-	duk_put_prop_string(ctx, -2, SIGNATURE);
-	duk_push_string(ctx, "elapsed");
-	duk_push_c_function(ctx, Clock_elapsed, 0);
-	duk_def_prop(ctx, -3, DUK_DEFPROP_HAVE_GETTER);
-	duk_pop(ctx);
-
-	return 0;
-}
-
-static duk_ret_t
-Clock_start(duk_context *ctx)
-{
-	clock_start(self(ctx));
-
-	return 0;
-}
-
-static duk_ret_t
-Clock_destructor(duk_context *ctx)
-{
-	duk_get_prop_string(ctx, 0, SIGNATURE);
-	free(duk_to_pointer(ctx, -1));
-	duk_pop(ctx);
-	duk_del_prop_string(ctx, 0, SIGNATURE);
-
-	return 0;
-}
-
-static const duk_function_list_entry methods[] = {
-	{ "start",      Clock_start,    0 },
-	{ NULL,         NULL,           0 }
-};
-
-void
-js_clock_bind(duk_context *ctx)
-{
-	assert(ctx);
-
-	duk_push_c_function(ctx, Clock_constructor, 0);
-	duk_push_object(ctx);
-	duk_put_function_list(ctx, -1, methods);
-	duk_push_c_function(ctx, Clock_destructor, 1);
-	duk_set_finalizer(ctx, -2);
-	duk_put_prop_string(ctx, -2, "prototype");
-	duk_put_global_string(ctx, "Clock");
-}
--- a/src/libmlk-core-js/core/js-clock.h	Fri Oct 07 15:25:32 2022 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,27 +0,0 @@
-/*
- * js-clock.h -- core clock binding
- *
- * Copyright (c) 2020-2022 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#ifndef MLK_CORE_JS_CLOCK_H
-#define MLK_CORE_JS_CLOCK_H
-
-#include <duktape.h>
-
-void
-js_clock_bind(duk_context *);
-
-#endif /* !MLK_CORE_JS_CLOCK_H */
--- a/src/libmlk-core-js/core/js-color.c	Fri Oct 07 15:25:32 2022 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,85 +0,0 @@
-/*
- * js-color.c -- core color binding
- *
- * Copyright (c) 2020-2022 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <assert.h>
-
-#include <core/color.h>
-
-#include "js-color.h"
-
-static inline unsigned int
-component(duk_context *ctx, duk_idx_t ind, const char *prop, unsigned int def)
-{
-	unsigned int value = 0;
-
-	if (duk_is_object(ctx, 0)) {
-		duk_get_prop_string(ctx, 0, prop);
-		value = duk_get_uint_default(ctx, -1, def);
-		duk_pop(ctx);
-	} else
-		value = duk_get_uint_default(ctx, ind, def);
-
-	return value;
-}
-
-static duk_ret_t
-Color_fromRGB(duk_context *ctx)
-{
-	const unsigned int r = component(ctx, 0, "r", 0);
-	const unsigned int g = component(ctx, 1, "g", 0);
-	const unsigned int b = component(ctx, 2, "b", 0);
-	const unsigned int a = component(ctx, 3, "a", 255);
-
-	duk_push_number(ctx, COLOR_HEX(r, g, b, a));
-
-	return 1;
-}
-
-static duk_ret_t
-Color_toRGB(duk_context *ctx)
-{
-	const unsigned long hex = duk_require_uint(ctx, 0);
-
-	duk_push_object(ctx);
-	duk_push_uint(ctx, COLOR_R(hex));
-	duk_put_prop_string(ctx, -2, "r");
-	duk_push_uint(ctx, COLOR_G(hex));
-	duk_put_prop_string(ctx, -2, "g");
-	duk_push_uint(ctx, COLOR_B(hex));
-	duk_put_prop_string(ctx, -2, "b");
-	duk_push_uint(ctx, COLOR_A(hex));
-	duk_put_prop_string(ctx, -2, "a");
-
-	return 1;
-}
-
-static const duk_function_list_entry functions[] = {
-	{ "fromRGB",    Color_fromRGB,  DUK_VARARGS     },
-	{ "toRGB",      Color_toRGB,    1               },
-	{ NULL,         NULL,           0               }
-};
-
-void
-js_color_bind(duk_context *ctx)
-{
-	assert(ctx);
-
-	duk_push_object(ctx);
-	duk_put_function_list(ctx, -1, functions);
-	duk_put_global_string(ctx, "Color");
-}
--- a/src/libmlk-core-js/core/js-color.h	Fri Oct 07 15:25:32 2022 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,27 +0,0 @@
-/*
- * js-color.h -- core color binding
- *
- * Copyright (c) 2020-2022 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#ifndef MLK_CORE_JS_COLOR_H
-#define MLK_CORE_JS_COLOR_H
-
-#include <duktape.h>
-
-void
-js_color_bind(duk_context *);
-
-#endif /* !MLK_CORE_JS_COLOR_H */
--- a/src/libmlk-core-js/core/js-core.c	Fri Oct 07 15:25:32 2022 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,54 +0,0 @@
-/*
- * js-core.c -- core binding
- *
- * Copyright (c) 2020-2022 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <assert.h>
-
-#include "js-core.h"
-
-#define VFS DUK_HIDDEN_SYMBOL("Mlk.Vfs")
-
-void
-js_core_bind(duk_context *ctx, struct vfs *vfs)
-{
-	assert(ctx);
-	assert(vfs);
-
-	/* We put the VFS object. */
-	duk_push_global_stash(ctx);
-	duk_push_pointer(ctx, vfs);
-	duk_put_prop_string(ctx, -2, VFS);
-	duk_pop(ctx);
-
-	duk_push_object(ctx);
-	duk_put_global_string(ctx, "Mlk");
-}
-
-struct vfs *
-js_core_global_vfs(duk_context *ctx)
-{
-	assert(ctx);
-
-	struct vfs *vfs;
-
-	duk_push_global_stash(ctx);
-	duk_get_prop_string(ctx, -1, VFS);
-	vfs = duk_to_pointer(ctx, -1);
-	duk_pop(ctx);
-
-	return vfs;
-}
--- a/src/libmlk-core-js/core/js-core.h	Fri Oct 07 15:25:32 2022 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,32 +0,0 @@
-/*
- * js-core.h -- core binding
- *
- * Copyright (c) 2020-2022 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#ifndef MLK_CORE_JS_H
-#define MLK_CORE_JS_H
-
-#include <duktape.h>
-
-struct vfs;
-
-void
-js_core_bind(duk_context *, struct vfs *);
-
-struct vfs *
-js_core_global_vfs(duk_context *);
-
-#endif /* !MLK_CORE_JS_H */
--- a/src/libmlk-core-js/core/js-drawable-stack.c	Fri Oct 07 15:25:32 2022 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,133 +0,0 @@
-/*
- * js-drawable-stack.h -- core drawable_stack binding
- *
- * Copyright (c) 2020-2022 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <assert.h>
-
-#include <core/alloc.h>
-#include <core/drawable.h>
-#include <core/drawable-stack.h>
-
-#include "js-drawable-stack.h"
-#include "js-drawable.h"
-
-#define SIGNATURE DUK_HIDDEN_SYMBOL("Mlk.DrawableStack")
-
-static inline struct drawable_stack *
-self(duk_context *ctx)
-{
-	struct drawable_stack *sf = NULL;
-
-	duk_push_this(ctx);
-	duk_get_prop_string(ctx, -1, SIGNATURE);
-	sf = duk_to_pointer(ctx, -1);
-	duk_pop_2(ctx);
-
-	if (!sf)
-		return (void)duk_error(ctx, DUK_ERR_TYPE_ERROR, "not a DrawableStack object"), NULL;
-
-	return sf;
-}
-
-static duk_ret_t
-DrawableStack_constructor(duk_context *ctx)
-{
-	duk_push_this(ctx);
-	duk_push_pointer(ctx, alloc_new0(sizeof (struct drawable_stack)));
-	duk_put_prop_string(ctx, -2, SIGNATURE);
-	duk_pop(ctx);
-
-	return 0;
-}
-
-static duk_ret_t
-DrawableStack_destructor(duk_context *ctx)
-{
-	struct drawable_stack *st;
-
-	duk_get_prop_string(ctx, 0, SIGNATURE);
-
-	if ((st = duk_to_pointer(ctx, -1))) {
-		drawable_stack_finish(st);
-		free(st);
-	}
-
-	duk_pop(ctx);
-	duk_del_prop_string(ctx, 0, SIGNATURE);
-
-	return 0;
-}
-
-static duk_ret_t
-DrawableStack_add(duk_context *ctx)
-{
-	struct drawable_stack *st = self(ctx);
-	struct js_drawable *dw = js_drawable_require(ctx, 0);
-
-	if (drawable_stack_add(st, &dw->dw) < 0)
-		drawable_finish(&dw->dw);
-	else {
-		duk_push_this(ctx);
-		dw->parent = duk_get_heapptr(ctx, -1);
-		duk_push_sprintf(ctx, "%p", dw);
-		duk_dup(ctx, 0);
-		duk_put_prop(ctx, -3);
-		duk_pop(ctx);
-	}
-
-	return 0;
-}
-
-static duk_ret_t
-DrawableStack_update(duk_context *ctx)
-{
-	struct drawable_stack *st = self(ctx);
-	const unsigned int ticks = duk_require_uint(ctx, 0);
-
-	duk_push_uint(ctx, drawable_stack_update(st, ticks));
-
-	return 1;
-}
-
-static duk_ret_t
-DrawableStack_draw(duk_context *ctx)
-{
-	drawable_stack_draw(self(ctx));
-
-	return 0;
-}
-
-static duk_function_list_entry methods[] = {
-	{ "add",        DrawableStack_add,      1       },
-	{ "update",     DrawableStack_update,   1       },
-	{ "draw",       DrawableStack_draw,     0       },
-	{ NULL,         NULL,                   0       }
-};
-
-void
-js_drawable_stack_bind(duk_context *ctx)
-{
-	assert(ctx);
-
-	duk_push_c_function(ctx, DrawableStack_constructor, 0);
-	duk_push_object(ctx);
-	duk_put_function_list(ctx, -1, methods);
-	duk_push_c_function(ctx, DrawableStack_destructor, 1);
-	duk_set_finalizer(ctx, -2);
-	duk_put_prop_string(ctx, -2, "prototype");
-	duk_put_global_string(ctx, "DrawableStack");
-}
--- a/src/libmlk-core-js/core/js-drawable-stack.h	Fri Oct 07 15:25:32 2022 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,27 +0,0 @@
-/*
- * js-drawable-stack.h -- core drawable_stack binding
- *
- * Copyright (c) 2020-2022 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#ifndef MLK_CORE_JS_DRAWABLE_STACK_H
-#define MLK_CORE_JS_DRAWABLE_STACK_H
-
-#include <duktape.h>
-
-void
-js_drawable_stack_bind(duk_context *);
-
-#endif /* !MLK_CORE_JS_DRAWABLE_STACK_H */
--- a/src/libmlk-core-js/core/js-drawable.c	Fri Oct 07 15:25:32 2022 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,238 +0,0 @@
-/*
- * js-drawable.c -- core drawable binding
- *
- * Copyright (c) 2020-2022 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <assert.h>
-
-#include <core/alloc.h>
-#include <core/drawable.h>
-
-#include "js-drawable.h"
-
-#define SIGNATURE DUK_HIDDEN_SYMBOL("Mlk.Drawable")
-
-static inline struct js_drawable *
-self(duk_context *ctx)
-{
-	struct js_drawable *data = NULL;
-
-	duk_push_this(ctx);
-	duk_get_prop_string(ctx, -1, SIGNATURE);
-	data = duk_to_pointer(ctx, -1);
-	duk_pop_2(ctx);
-
-	if (!data)
-		return (void)duk_error(ctx, DUK_ERR_TYPE_ERROR, "not a Drawable object"), NULL;
-
-	return data;
-}
-
-static inline duk_context *
-callable(struct js_drawable *data, const char *prop)
-{
-	duk_context *ctx;
-
-	if (!data->ptr)
-		return NULL;
-
-	duk_push_heapptr(data->ctx, data->ptr);
-	duk_get_prop_string(data->ctx, -1, prop);
-	duk_pull(data->ctx, -2);
-
-	if (duk_is_callable(data->ctx, -2))
-		ctx = data->ctx;
-	else {
-		ctx = NULL;
-		duk_pop_n(data->ctx, 2);
-	}
-
-	return ctx;
-}
-
-static int
-update(struct drawable *dw, unsigned int ticks)
-{
-	duk_context *ctx;
-	int ret = 0;
-
-	if ((ctx = callable(dw->data, "update"))) {
-		duk_push_uint(ctx, ticks);
-		duk_call_method(ctx, 1);
-		ret = duk_to_int(ctx, -1);
-		duk_pop(ctx);
-	}
-
-	return ret;
-}
-
-static void
-draw(struct drawable *dw)
-{
-	duk_context *ctx;
-
-	if ((ctx = callable(dw->data, "draw"))) {
-		duk_call_method(ctx, 0);
-		duk_pop(ctx);
-	}
-}
-
-static void
-end(struct drawable *dw)
-{
-	duk_context *ctx;
-
-	if ((ctx = callable(dw->data, "end"))) {
-		duk_call_method(ctx, 0);
-		duk_pop(ctx);
-	}
-}
-
-static void
-finish(struct drawable *dw)
-{
-	struct js_drawable *data = dw->data;
-
-	/* I must not be called anymore. */
-	data->ptr = NULL;
-
-	/* Remove myself from parent stack if any. */
-	if (data->parent) {
-		duk_push_heapptr(data->ctx, data->parent);
-		duk_push_sprintf(data->ctx, "%p", data);
-		duk_del_prop(data->ctx, -2);
-		duk_pop(data->ctx);
-		data->parent = NULL;
-	}
-
-	if (--data->refc == 0)
-		free(data);
-}
-
-static duk_ret_t
-Drawable_getX(duk_context *ctx)
-{
-	duk_push_uint(ctx, self(ctx)->dw.x);
-
-	return 1;
-}
-
-static duk_ret_t
-Drawable_setX(duk_context *ctx)
-{
-	self(ctx)->dw.x = duk_require_uint(ctx, 0);
-
-	return 0;
-}
-
-static duk_ret_t
-Drawable_getY(duk_context *ctx)
-{
-	duk_push_uint(ctx, self(ctx)->dw.y);
-
-	return 1;
-}
-
-static duk_ret_t
-Drawable_setY(duk_context *ctx)
-{
-	self(ctx)->dw.y = duk_require_uint(ctx, 0);
-
-	return 0;
-}
-
-static duk_ret_t
-Drawable_constructor(duk_context *ctx)
-{
-	struct js_drawable *data;
-	const int x = duk_require_int(ctx, 0);
-	const int y = duk_require_int(ctx, 1);
-
-	data = alloc_new0(sizeof (*data));
-	data->refc = 1;
-	data->ctx = ctx;
-	data->dw.x = x;
-	data->dw.y = y;
-	data->dw.data = data;
-	data->dw.update = update;
-	data->dw.finish = finish;
-	data->dw.draw = draw;
-	data->dw.end = end;
-
-	duk_push_this(ctx);
-	data->ptr = duk_get_heapptr(ctx, -1);
-	duk_push_pointer(ctx, data);
-	duk_put_prop_string(ctx, -2, SIGNATURE);
-	duk_push_string(ctx, "x");
-	duk_push_c_function(ctx, Drawable_getX, 0);
-	duk_push_c_function(ctx, Drawable_setX, 1);
-	duk_def_prop(ctx, -4, DUK_DEFPROP_HAVE_GETTER | DUK_DEFPROP_HAVE_SETTER);
-	duk_push_string(ctx, "y");
-	duk_push_c_function(ctx, Drawable_getY, 0);
-	duk_push_c_function(ctx, Drawable_setY, 1);
-	duk_def_prop(ctx, -4, DUK_DEFPROP_HAVE_GETTER | DUK_DEFPROP_HAVE_SETTER);
-	duk_pop(ctx);
-
-	return 0;
-}
-
-static duk_ret_t
-Drawable_destructor(duk_context *ctx)
-{
-	struct js_drawable *data;
-
-	duk_get_prop_string(ctx, 0, SIGNATURE);
-
-	if ((data = duk_to_pointer(ctx, -1)))
-		drawable_finish(&data->dw);
-
-	duk_del_prop_string(ctx, 0, SIGNATURE);
-	duk_pop(ctx);
-
-	return 0;
-}
-
-void
-js_drawable_bind(duk_context *ctx)
-{
-	assert(ctx);
-
-	duk_push_c_function(ctx, Drawable_constructor, 2);
-	duk_push_object(ctx);
-	duk_push_c_function(ctx, Drawable_destructor, 1);
-	duk_set_finalizer(ctx, -2);
-	duk_put_prop_string(ctx, -2, "prototype");
-	duk_put_global_string(ctx, "Drawable");
-}
-
-struct js_drawable *
-js_drawable_require(duk_context *ctx, duk_idx_t idx)
-{
-	struct js_drawable *data = NULL;
-
-	if (duk_is_object(ctx, idx)) {
-		duk_get_prop_string(ctx, idx, SIGNATURE);
-		data = duk_to_pointer(ctx, -1);
-		duk_pop(ctx);
-	}
-
-	if (!data)
-		return (void)duk_error(ctx, DUK_ERR_TYPE_ERROR, "not a Drawable object"), NULL;
-
-	data->refc++;
-
-	return data;
-}
--- a/src/libmlk-core-js/core/js-drawable.h	Fri Oct 07 15:25:32 2022 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,40 +0,0 @@
-/*
- * js-drawable.h -- core drawable binding
- *
- * Copyright (c) 2020-2022 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#ifndef MLK_CORE_JS_DRAWABLE_H
-#define MLK_CORE_JS_DRAWABLE_H
-
-#include <duktape.h>
-
-#include <core/drawable.h>
-
-struct js_drawable {
-	duk_context *ctx;
-	void *ptr;
-	void *parent;
-	unsigned int refc;
-	struct drawable dw;
-};
-
-void
-js_drawable_bind(duk_context *);
-
-struct js_drawable *
-js_drawable_require(duk_context *, duk_idx_t);
-
-#endif /* !MLK_CORE_JS_DRAWABLE_H */
--- a/src/libmlk-core-js/core/js-event.c	Fri Oct 07 15:25:32 2022 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,335 +0,0 @@
-/*
- * js-event.c -- core event binding
- *
- * Copyright (c) 2020-2022 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <assert.h>
-
-#include <core/alloc.h>
-#include <core/event.h>
-
-#include "js-event.h"
-
-#define SIGNATURE DUK_HIDDEN_SYMBOL("Mlk.Event")
-
-static void
-push_click(duk_context *ctx, const union event *ev)
-{
-	duk_push_int(ctx, ev->click.button);
-	duk_put_prop_string(ctx, -2, "button");
-	duk_push_int(ctx, ev->click.x);
-	duk_put_prop_string(ctx, -2, "x");
-	duk_push_int(ctx, ev->click.y);
-	duk_put_prop_string(ctx, -2, "y");
-	duk_push_int(ctx, ev->click.clicks);
-	duk_put_prop_string(ctx, -2, "clicks");
-}
-
-static void
-push_key(duk_context *ctx, const union event *ev)
-{
-	duk_push_int(ctx, ev->key.key);
-	duk_put_prop_string(ctx, -2, "key");
-}
-
-static void
-push_mouse(duk_context *ctx, const union event *ev)
-{
-	duk_push_int(ctx, ev->mouse.buttons);
-	duk_put_prop_string(ctx, -2, "buttons");
-	duk_push_int(ctx, ev->mouse.x);
-	duk_put_prop_string(ctx, -2, "x");
-	duk_push_int(ctx, ev->mouse.y);
-	duk_put_prop_string(ctx, -2, "y");
-}
-
-static void
-push_quit(duk_context *ctx, const union event *ev)
-{
-	(void)ctx;
-	(void)ev;
-}
-
-static void (*push[])(duk_context *, const union event *) = {
-	[EVENT_CLICKDOWN] = push_click,
-	[EVENT_CLICKUP] = push_click,
-	[EVENT_KEYDOWN] = push_key,
-	[EVENT_KEYUP] = push_key,
-	[EVENT_MOUSE] = push_mouse,
-	[EVENT_QUIT] = push_quit,
-};
-
-static duk_ret_t
-Event_poll(duk_context *ctx)
-{
-	(void)ctx;
-
-	union event ev;
-
-	if (!event_poll(&ev))
-		duk_push_null(ctx);
-	else
-		js_event_push(ctx, &ev);
-
-	return 1;
-}
-
-static const duk_function_list_entry functions[] = {
-	{ "poll",       Event_poll,     0               },
-	{ NULL,         NULL,           0               }
-};
-
-static const duk_number_list_entry types[] = {
-	{ "CLICKDOWN",          EVENT_CLICKDOWN         },
-	{ "CLICKUP",            EVENT_CLICKUP           },
-	{ "KEYDOWN",            EVENT_KEYDOWN           },
-	{ "KEYUP",              EVENT_KEYUP             },
-	{ "MOUSE",              EVENT_MOUSE             },
-	{ "QUIT",               EVENT_QUIT              },
-	{ NULL,                 0                       }
-};
-
-static const duk_number_list_entry keys[] = {
-	{ "UNKNOWN",            KEY_UNKNOWN             },
-	{ "ENTER",              KEY_ENTER               },
-	{ "ESCAPE",             KEY_ESCAPE              },
-	{ "BACKSPACE",          KEY_BACKSPACE           },
-	{ "TAB",                KEY_TAB                 },
-	{ "SPACE",              KEY_SPACE               },
-	{ "EXCLAIM",            KEY_EXCLAIM             },
-	{ "DOUBLE_QUOTE",       KEY_DOUBLE_QUOTE        },
-	{ "HASH",               KEY_HASH                },
-	{ "PERCENT",            KEY_PERCENT             },
-	{ "DOLLAR",             KEY_DOLLAR              },
-	{ "AMPERSAND",          KEY_AMPERSAND           },
-	{ "QUOTE",              KEY_QUOTE               },
-	{ "LEFT_PAREN",         KEY_LEFT_PAREN          },
-	{ "RIGHT_PAREN",        KEY_RIGHT_PAREN         },
-	{ "ASTERISK",           KEY_ASTERISK            },
-	{ "PLUS",               KEY_PLUS                },
-	{ "COMMA",              KEY_COMMA               },
-	{ "MINUS",              KEY_MINUS               },
-	{ "PERIOD",             KEY_PERIOD              },
-	{ "SLASH",              KEY_SLASH               },
-	{ "0",                  KEY_0                   },
-	{ "1",                  KEY_1                   },
-	{ "2",                  KEY_2                   },
-	{ "3",                  KEY_3                   },
-	{ "4",                  KEY_4                   },
-	{ "5",                  KEY_5                   },
-	{ "6",                  KEY_6                   },
-	{ "7",                  KEY_7                   },
-	{ "8",                  KEY_8                   },
-	{ "9",                  KEY_9                   },
-	{ "COLON",              KEY_COLON               },
-	{ "SEMICOLON",          KEY_SEMICOLON           },
-	{ "LESS",               KEY_LESS                },
-	{ "EQUALS",             KEY_EQUALS              },
-	{ "GREATER",            KEY_GREATER             },
-	{ "QUESTION",           KEY_QUESTION            },
-	{ "AT",                 KEY_AT                  },
-	{ "LEFT_BRACKET",       KEY_LEFT_BRACKET        },
-	{ "BACKSLASH",          KEY_BACKSLASH           },
-	{ "RIGHT_BRACKET",      KEY_RIGHT_BRACKET       },
-	{ "CARET",              KEY_CARET               },
-	{ "UNDERSCORE",         KEY_UNDERSCORE          },
-	{ "BACKQUOTE",          KEY_BACKQUOTE           },
-	{ "a",                  KEY_a                   },
-	{ "b",                  KEY_b                   },
-	{ "c",                  KEY_c                   },
-	{ "d",                  KEY_d                   },
-	{ "e",                  KEY_e                   },
-	{ "f",                  KEY_f                   },
-	{ "g",                  KEY_g                   },
-	{ "h",                  KEY_h                   },
-	{ "i",                  KEY_i                   },
-	{ "j",                  KEY_j                   },
-	{ "k",                  KEY_k                   },
-	{ "l",                  KEY_l                   },
-	{ "m",                  KEY_m                   },
-	{ "n",                  KEY_n                   },
-	{ "o",                  KEY_o                   },
-	{ "p",                  KEY_p                   },
-	{ "q",                  KEY_q                   },
-	{ "r",                  KEY_r                   },
-	{ "s",                  KEY_s                   },
-	{ "t",                  KEY_t                   },
-	{ "u",                  KEY_u                   },
-	{ "v",                  KEY_v                   },
-	{ "w",                  KEY_w                   },
-	{ "x",                  KEY_x                   },
-	{ "y",                  KEY_y                   },
-	{ "z",                  KEY_z                   },
-	{ "CAPSLOCK",           KEY_CAPSLOCK            },
-	{ "F1",                 KEY_F1                  },
-	{ "F2",                 KEY_F2                  },
-	{ "F3",                 KEY_F3                  },
-	{ "F4",                 KEY_F4                  },
-	{ "F5",                 KEY_F5                  },
-	{ "F6",                 KEY_F6                  },
-	{ "F7",                 KEY_F7                  },
-	{ "F8",                 KEY_F8                  },
-	{ "F9",                 KEY_F9                  },
-	{ "F10",                KEY_F10                 },
-	{ "F11",                KEY_F11                 },
-	{ "F12",                KEY_F12                 },
-	{ "F13",                KEY_F13                 },
-	{ "F14",                KEY_F14                 },
-	{ "F15",                KEY_F15                 },
-	{ "F16",                KEY_F16                 },
-	{ "F17",                KEY_F17                 },
-	{ "F18",                KEY_F18                 },
-	{ "F19",                KEY_F19                 },
-	{ "F20",                KEY_F20                 },
-	{ "F21",                KEY_F21                 },
-	{ "F22",                KEY_F22                 },
-	{ "F23",                KEY_F23                 },
-	{ "F24",                KEY_F24                 },
-	{ "PRINTSCREEN",        KEY_PRINTSCREEN         },
-	{ "SCROLL_LOCK",        KEY_SCROLL_LOCK         },
-	{ "PAUSE",              KEY_PAUSE               },
-	{ "INSERT",             KEY_INSERT              },
-	{ "HOME",               KEY_HOME                },
-	{ "PAGEUP",             KEY_PAGEUP              },
-	{ "DELETE",             KEY_DELETE              },
-	{ "END",                KEY_END                 },
-	{ "PAGEDOWN",           KEY_PAGEDOWN            },
-	{ "RIGHT",              KEY_RIGHT               },
-	{ "LEFT",               KEY_LEFT                },
-	{ "DOWN",               KEY_DOWN                },
-	{ "UP",                 KEY_UP                  },
-	{ "NUMLOCKCLEAR",       KEY_NUMLOCKCLEAR        },
-	{ "KP_DIVIDE",          KEY_KP_DIVIDE           },
-	{ "KP_MULTIPLY",        KEY_KP_MULTIPLY         },
-	{ "KP_MINUS",           KEY_KP_MINUS            },
-	{ "KP_PLUS",            KEY_KP_PLUS             },
-	{ "KP_ENTER",           KEY_KP_ENTER            },
-	{ "KP_00",              KEY_KP_00               },
-	{ "KP_000",             KEY_KP_000              },
-	{ "KP_1",               KEY_KP_1                },
-	{ "KP_2",               KEY_KP_2                },
-	{ "KP_3",               KEY_KP_3                },
-	{ "KP_4",               KEY_KP_4                },
-	{ "KP_5",               KEY_KP_5                },
-	{ "KP_6",               KEY_KP_6                },
-	{ "KP_7",               KEY_KP_7                },
-	{ "KP_8",               KEY_KP_8                },
-	{ "KP_9",               KEY_KP_9                },
-	{ "KP_0",               KEY_KP_0                },
-	{ "KP_PERIOD",          KEY_KP_PERIOD           },
-	{ "KP_COMMA",           KEY_KP_COMMA            },
-	{ "MENU",               KEY_MENU                },
-	{ "MUTE",               KEY_MUTE                },
-	{ "VOLUME_UP",          KEY_VOLUME_UP           },
-	{ "VOLUME_DOWN",        KEY_VOLUME_DOWN         },
-	{ "LCTRL",              KEY_LCTRL               },
-	{ "LSHIFT",             KEY_LSHIFT              },
-	{ "LALT",               KEY_LALT                },
-	{ "LSUPER",             KEY_LSUPER              },
-	{ "RCTRL",              KEY_RCTRL               },
-	{ "RSHIFT",             KEY_RSHIFT              },
-	{ "RALT",               KEY_RALT                },
-	{ "RSUPER",             KEY_RSUPER              },
-	{ NULL,                 0                       }
-};
-
-static const duk_number_list_entry keymods[] = {
-	{ "LSHIFT",             KEYMOD_LSHIFT           },
-	{ "LCTRL",              KEYMOD_LCTRL            },
-	{ "LALT",               KEYMOD_LALT             },
-	{ "LSUPER",             KEYMOD_LSUPER           },
-	{ "RSHIFT",             KEYMOD_RSHIFT           },
-	{ "RTCRL",              KEYMOD_RCTRL            },
-	{ "RALT",               KEYMOD_RALT             },
-	{ "RSUPER",             KEYMOD_RSUPER           },
-	{ NULL,                 0                       }
-};
-
-static const duk_number_list_entry buttons[] = {
-	{ "LEFT",               MOUSE_BUTTON_LEFT       },
-	{ "MIDDLE",             MOUSE_BUTTON_MIDDLE     },
-	{ "RIGHT",              MOUSE_BUTTON_RIGHT      },
-	{ NULL,                 0                       }
-};
-
-static duk_ret_t
-Event_destructor(duk_context *ctx)
-{
-	duk_get_prop_string(ctx, 0, SIGNATURE);
-	free(duk_to_pointer(ctx, -1));
-	duk_del_prop_string(ctx, 0, SIGNATURE);
-	duk_pop(ctx);
-
-	return 0;
-}
-
-void
-js_event_bind(duk_context *ctx)
-{
-	assert(ctx);
-
-	duk_push_global_object(ctx);
-	duk_push_object(ctx);
-	duk_put_function_list(ctx, -1, functions);
-	duk_push_object(ctx);
-	duk_put_number_list(ctx, -1, types);
-	duk_put_prop_string(ctx, -2, "Type");
-	duk_push_object(ctx);
-	duk_put_number_list(ctx, -1, keys);
-	duk_put_prop_string(ctx, -2, "Key");
-	duk_push_object(ctx);
-	duk_put_number_list(ctx, -1, keymods);
-	duk_put_prop_string(ctx, -2, "Keymod");
-	duk_push_object(ctx);
-	duk_put_number_list(ctx, -1, buttons);
-	duk_put_prop_string(ctx, -2, "Button");
-	duk_put_prop_string(ctx, -2, "Event");
-	duk_pop(ctx);
-}
-
-void
-js_event_push(duk_context *ctx, const union event *ev)
-{
-	assert(ctx);
-
-	duk_push_object(ctx);
-	duk_push_c_function(ctx, Event_destructor, 1);
-	duk_set_finalizer(ctx, -2);
-	duk_push_pointer(ctx, alloc_dup(ev, sizeof (*ev)));
-	duk_put_prop_string(ctx, -2, SIGNATURE);
-	duk_push_int(ctx, ev->type);
-	duk_put_prop_string(ctx, -2, "type");
-	push[ev->type](ctx, ev);
-}
-
-union event *
-js_event_require(duk_context *ctx, duk_idx_t idx)
-{
-	assert(ctx);
-
-	union event *ev = NULL;
-
-	if (duk_is_object(ctx, idx)) {
-		duk_get_prop_string(ctx, idx, SIGNATURE);
-		ev = duk_to_pointer(ctx, -1);
-		duk_pop(ctx);
-	}
-
-	if (!ev)
-		return (void)duk_error(ctx, DUK_ERR_TYPE_ERROR, "not an Event object"), NULL;
-
-	return ev;
-}
--- a/src/libmlk-core-js/core/js-event.h	Fri Oct 07 15:25:32 2022 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,35 +0,0 @@
-/*
- * js-event.h -- core event binding
- *
- * Copyright (c) 2020-2022 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#ifndef MLK_CORE_JS_EVENT_H
-#define MLK_CORE_JS_EVENT_H
-
-#include <duktape.h>
-
-union event;
-
-void
-js_event_bind(duk_context *);
-
-void
-js_event_push(duk_context *, const union event *);
-
-union event *
-js_event_require(duk_context *, duk_idx_t);
-
-#endif /* !MLK_CORE_JS_EVENT_H */
--- a/src/libmlk-core-js/core/js-font.c	Fri Oct 07 15:25:32 2022 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,195 +0,0 @@
-/*
- * js-font.c -- core texture binding
- *
- * Copyright (c) 2020-2022 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <assert.h>
-
-#include <core/alloc.h>
-#include <core/error.h>
-#include <core/font.h>
-#include <core/texture.h>
-#include <core/vfs.h>
-
-#include "js-core.h"
-#include "js-font.h"
-#include "js-texture.h"
-
-#define SIGNATURE DUK_HIDDEN_SYMBOL("Mlk.Font")
-
-static inline struct font *
-self(duk_context *ctx)
-{
-	struct font *font;
-
-	duk_push_this(ctx);
-	duk_get_prop_string(ctx, -1, SIGNATURE);
-	font = duk_to_pointer(ctx, -1);
-	duk_pop_2(ctx);
-
-	if (!font)
-		return (void)duk_error(ctx, DUK_ERR_TYPE_ERROR, "not a Font object"), NULL;
-
-	return font;
-}
-
-static duk_ret_t
-Font_getStyle(duk_context *ctx)
-{
-	duk_push_int(ctx, self(ctx)->style);
-
-	return 1;
-}
-
-static duk_ret_t
-Font_setStyle(duk_context *ctx)
-{
-	const int style = duk_require_int(ctx, 0);
-
-	if (style < 0 || style >= FONT_STYLE_LAST)
-		return duk_error(ctx, DUK_ERR_ERROR, "invalid style");
-
-	self(ctx)->style = style;
-
-	return 0;
-}
-
-static duk_ret_t
-Font_getHeight(duk_context *ctx)
-{
-	duk_push_uint(ctx, font_height(self(ctx)));
-
-	return 1;
-}
-
-static duk_ret_t
-Font_new(duk_context *ctx)
-{
-	const char *entry = duk_require_string(ctx, 0);
-	const unsigned int size = duk_require_uint(ctx, 1);
-	struct vfs_file file;
-	struct font *font;
-
-	if (vfs_open(js_core_global_vfs(ctx), &file, entry, "r") < 0)
-		return duk_error(ctx, DUK_ERR_ERROR, "%s", error());
-
-	font = alloc_new0(sizeof (*font));
-
-	if (font_openvfs(font, &file, size) < 0) {
-		free(font);
-		vfs_file_finish(&file);
-
-		return duk_error(ctx, DUK_ERR_ERROR, "%s", error());
-	}
-
-	vfs_file_finish(&file);
-
-	duk_push_this(ctx);
-	duk_push_pointer(ctx, font);
-	duk_put_prop_string(ctx, -2, SIGNATURE);
-	duk_push_string(ctx, "style");
-	duk_push_c_function(ctx, Font_getStyle, 0);
-	duk_push_c_function(ctx, Font_setStyle, 1);
-	duk_def_prop(ctx, -4, DUK_DEFPROP_HAVE_GETTER | DUK_DEFPROP_HAVE_SETTER);
-	duk_push_string(ctx, "height");
-	duk_push_c_function(ctx, Font_getHeight, 0);
-	duk_def_prop(ctx, -3, DUK_DEFPROP_HAVE_GETTER);
-	duk_pop(ctx);
-
-	return 0;
-}
-
-static duk_ret_t
-Font_render(duk_context *ctx)
-{
-	const char *text = duk_require_string(ctx, 0);
-	unsigned long color = duk_require_uint(ctx, 1);
-	struct texture *tex;
-
-	tex = alloc_new0(sizeof (*tex));
-
-	if (font_render(self(ctx), tex, text, color) < 0) {
-		free(tex);
-		return duk_error(ctx, DUK_ERR_ERROR, "%s", error());
-	}
-
-	js_texture_push(ctx, tex);
-
-	return 1;
-}
-
-static duk_ret_t
-Font_query(duk_context *ctx)
-{
-	const char *text = duk_require_string(ctx, 0);
-	unsigned int w, h;
-
-	if (font_query(self(ctx), text, &w, &h) < 0)
-		return duk_error(ctx, DUK_ERR_ERROR, "%s", error());
-
-	duk_push_object(ctx);
-	duk_push_uint(ctx, w);
-	duk_put_prop_string(ctx, -2, "w");
-	duk_push_uint(ctx, h);
-	duk_put_prop_string(ctx, -2, "h");
-
-	return 1;
-}
-
-static duk_ret_t
-Font_destructor(duk_context *ctx)
-{
-	struct font *font;
-
-	duk_get_prop_string(ctx, 0, SIGNATURE);
-
-	if ((font = duk_to_pointer(ctx, -1)))
-		font_finish(font);
-
-	duk_pop(ctx);
-	duk_del_prop_string(ctx, 0, SIGNATURE);
-
-	return 0;
-}
-
-static const struct duk_function_list_entry methods[] = {
-	{ "render",     Font_render,    2 },
-	{ "query",      Font_query,     1 },
-	{ NULL,         NULL,           0 }
-};
-
-static const struct duk_number_list_entry styles[] = {
-	{ "ANTIALIASED",        FONT_STYLE_ANTIALIASED, },
-	{ "NONE",               FONT_STYLE_NONE         },
-	{ NULL,                 0                       }
-};
-
-void
-js_font_bind(duk_context *ctx)
-{
-	assert(ctx);
-
-	duk_push_c_function(ctx, Font_new, 2);
-	duk_push_object(ctx);
-	duk_put_number_list(ctx, -1, styles);
-	duk_put_prop_string(ctx, -2, "Style");
-	duk_push_object(ctx);
-	duk_put_function_list(ctx, -1, methods);
-	duk_push_c_function(ctx, Font_destructor, 1);
-	duk_set_finalizer(ctx, -2);
-	duk_put_prop_string(ctx, -2, "prototype");
-	duk_put_global_string(ctx, "Font");
-}
--- a/src/libmlk-core-js/core/js-font.h	Fri Oct 07 15:25:32 2022 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,27 +0,0 @@
-/*
- * js-font.h -- core texture binding
- *
- * Copyright (c) 2020-2022 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#ifndef MLK_CORE_JS_FONT_H
-#define MLK_CORE_JS_FONT_H
-
-#include <duktape.h>
-
-void
-js_font_bind(duk_context *);
-
-#endif /* !MLK_CORE_JS_FONT_H */
--- a/src/libmlk-core-js/core/js-game.c	Fri Oct 07 15:25:32 2022 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,106 +0,0 @@
-/*
- * js-game.c -- core gamej binding
- *
- * Copyright (c) 2020-2022 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <assert.h>
-
-#include <core/game.h>
-#include <core/state.h>
-#include <core/util.h>
-
-#include "js-game.h"
-#include "js-state.h"
-
-/*
- * TODO: determine if it's worth it to add handle, update and draw functions.
- */
-static struct state *states[16];
-
-static duk_ret_t
-Game_push(duk_context *ctx)
-{
-	struct js_state *state = js_state_require(ctx, 0);
-
-	if (game.state == &game.states[game.statesz - 1]) {
-		state_finish(&state->st);
-		return duk_error(ctx, DUK_ERR_RANGE_ERROR, "too many states");
-	}
-
-	duk_push_this(ctx);
-	state->parent = duk_get_heapptr(ctx, -1);
-	duk_push_sprintf(ctx, "%p", state);
-	duk_dup(ctx, 0);
-	duk_put_prop(ctx, -3);
-	duk_pop(ctx);
-
-	game_push(&state->st);
-
-	return 0;
-}
-
-static duk_ret_t
-Game_pop(duk_context *ctx)
-{
-	(void)ctx;
-
-	if (game.state == &game.states[0])
-		return 0;
-
-	game_pop();
-
-	return 0;
-}
-
-static duk_ret_t
-Game_loop(duk_context *ctx)
-{
-	(void)ctx;
-
-	game_loop();
-
-	return 0;
-}
-
-static duk_ret_t
-Game_quit(duk_context *ctx)
-{
-	(void)ctx;
-
-	game_quit();
-
-	return 0;
-}
-
-static const duk_function_list_entry functions[] = {
-	{ "push",       Game_push,      1 },
-	{ "pop",        Game_pop,       0 },
-	{ "loop",       Game_loop,      0 },
-	{ "quit",       Game_quit,      0 },
-	{ NULL,         NULL,           0 }
-};
-
-void
-js_game_bind(duk_context *ctx)
-{
-	assert(ctx);
-
-	duk_push_object(ctx);
-	duk_put_function_list(ctx, -1, functions);
-	duk_put_global_string(ctx, "Game");
-
-	game_init(states, UTIL_SIZE(states));
-}
--- a/src/libmlk-core-js/core/js-game.h	Fri Oct 07 15:25:32 2022 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,27 +0,0 @@
-/*
- * js-game.h -- core game binding
- *
- * Copyright (c) 2020-2022 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#ifndef MLK_CORE_JS_GAME_H
-#define MLK_CORE_JS_GAME_H
-
-#include <duktape.h>
-
-void
-js_game_bind(duk_context *);
-
-#endif /* !MLK_CORE_JS_GAME_H */
--- a/src/libmlk-core-js/core/js-music.c	Fri Oct 07 15:25:32 2022 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,156 +0,0 @@
-/*
- * js-music.c -- core music binding
- *
- * Copyright (c) 2020-2022 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <assert.h>
-
-#include <core/alloc.h>
-#include <core/error.h>
-#include <core/music.h>
-#include <core/vfs.h>
-
-#include "js-core.h"
-#include "js-music.h"
-
-#define SIGNATURE DUK_HIDDEN_SYMBOL("Mlk.Music")
-
-static inline struct music *
-self(duk_context *ctx)
-{
-	struct music *mus;
-
-	duk_push_this(ctx);
-	duk_get_prop_string(ctx, -1, SIGNATURE);
-	mus = duk_to_pointer(ctx, -1);
-	duk_pop_2(ctx);
-
-	if (!mus)
-		return (void)duk_error(ctx, DUK_ERR_TYPE_ERROR, "not a Music object"), NULL;
-
-	return mus;
-}
-
-static duk_ret_t
-Music_new(duk_context *ctx)
-{
-	const char *entry = duk_require_string(ctx, 0);
-	struct vfs_file file;
-	struct music *mus;
-
-	if (vfs_open(js_core_global_vfs(ctx), &file, entry, "r") < 0)
-		return duk_error(ctx, DUK_ERR_ERROR, "%s", error());
-
-	mus = alloc_new0(sizeof (*mus));
-
-	if (music_openvfs(mus, &file) < 0) {
-		free(mus);
-		vfs_file_finish(&file);
-
-		return duk_error(ctx, DUK_ERR_ERROR, "%s", error());
-	}
-
-	vfs_file_finish(&file);
-
-	duk_push_this(ctx);
-	duk_push_pointer(ctx, mus);
-	duk_put_prop_string(ctx, -2, SIGNATURE);
-	duk_pop(ctx);
-
-	return 0;
-}
-
-static duk_ret_t
-Music_play(duk_context *ctx)
-{
-	const int flags = duk_get_int_default(ctx, 0, MUSIC_NONE);
-
-	if (music_play(self(ctx), flags) < 0)
-		return duk_error(ctx, DUK_ERR_ERROR, "%s", error());
-
-	return 0;
-}
-
-static duk_ret_t
-Music_pause(duk_context *ctx)
-{
-	music_pause(self(ctx));
-
-	return 0;
-}
-
-static duk_ret_t
-Music_resume(duk_context *ctx)
-{
-	music_resume(self(ctx));
-
-	return 0;
-}
-
-static duk_ret_t
-Music_stop(duk_context *ctx)
-{
-	music_stop(self(ctx));
-
-	return 0;
-}
-
-static duk_ret_t
-Music_destructor(duk_context *ctx)
-{
-	struct music *mus;
-
-	duk_get_prop_string(ctx, 0, SIGNATURE);
-
-	if ((mus = duk_to_pointer(ctx, -1)))
-		music_finish(mus);
-
-	duk_pop(ctx);
-	duk_del_prop_string(ctx, 0, SIGNATURE);
-
-	return 0;
-}
-
-static const duk_function_list_entry methods[] = {
-	{ "play",               Music_play,             DUK_VARARGS     },
-	{ "pause",              Music_pause,            0               },
-	{ "resume",             Music_resume,           0               },
-	{ "stop",               Music_stop,             DUK_VARARGS     },
-	{ NULL,                 NULL,                   0               }
-};
-
-static const duk_number_list_entry flags[] = {
-	{ "NONE",              MUSIC_NONE                               },
-	{ "LOOP",              MUSIC_LOOP                               },
-	{ NULL,                0                                        }
-};
-
-void
-js_music_bind(duk_context *ctx)
-{
-	assert(ctx);
-
-	duk_push_c_function(ctx, Music_new, 1);
-	duk_push_object(ctx);
-	duk_put_number_list(ctx, -1, flags);
-	duk_put_prop_string(ctx, -2, "Flags");
-	duk_push_object(ctx);
-	duk_put_function_list(ctx, -1, methods);
-	duk_push_c_function(ctx, Music_destructor, 1);
-	duk_set_finalizer(ctx, -2);
-	duk_put_prop_string(ctx, -2, "prototype");
-	duk_put_global_string(ctx, "Music");
-}
--- a/src/libmlk-core-js/core/js-music.h	Fri Oct 07 15:25:32 2022 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,27 +0,0 @@
-/*
- * js-music.h -- core music binding
- *
- * Copyright (c) 2020-2022 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#ifndef MLK_CORE_JS_MUSIC_H
-#define MLK_CORE_JS_MUSIC_H
-
-#include <duktape.h>
-
-void
-js_music_bind(duk_context *);
-
-#endif /* !MLK_CORE_JS_MUSIC_H */
--- a/src/libmlk-core-js/core/js-painter.c	Fri Oct 07 15:25:32 2022 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,165 +0,0 @@
-/*
- * js-painter.c -- core painter binding
- *
- * Copyright (c) 2020-2022 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <assert.h>
-
-#include <core/painter.h>
-
-#include "js-painter.h"
-#include "js-texture.h"
-
-static duk_ret_t
-Painter_getTarget(duk_context *ctx)
-{
-	struct texture *tex;
-
-	if (!(tex = painter_get_target()))
-		duk_push_null(ctx);
-	else
-		js_texture_push(ctx, tex);
-
-	return 1;
-}
-
-static duk_ret_t
-Painter_setTarget(duk_context *ctx)
-{
-	if (duk_is_null(ctx, 0))
-		painter_set_target(NULL);
-	else
-		painter_set_target(js_texture_require(ctx, 0));
-
-	return 0;
-}
-
-static duk_ret_t
-Painter_getColor(duk_context *ctx)
-{
-	duk_push_number(ctx, painter_get_color());
-
-	return 1;
-}
-
-static duk_ret_t
-Painter_setColor(duk_context *ctx)
-{
-	const unsigned long color = duk_require_number(ctx, 0);
-
-	painter_set_color(color);
-
-	return 0;
-}
-
-static duk_ret_t
-Painter_drawLine(duk_context *ctx)
-{
-	const int x1 = duk_require_int(ctx, 0);
-	const int y1 = duk_require_int(ctx, 1);
-	const int x2 = duk_require_int(ctx, 2);
-	const int y2 = duk_require_int(ctx, 3);
-
-	painter_draw_line(x1, y1, x2, y2);
-
-	return 0;
-}
-
-static duk_ret_t
-Painter_drawPoint(duk_context *ctx)
-{
-	const int x = duk_require_int(ctx, 0);
-	const int y = duk_require_int(ctx, 1);
-
-	painter_draw_point(x, y);
-
-	return 0;
-}
-
-static duk_ret_t
-Painter_drawRectangle(duk_context *ctx)
-{
-	const int x = duk_require_int(ctx, 0);
-	const int y = duk_require_int(ctx, 1);
-	const unsigned int w = duk_require_uint(ctx, 2);
-	const unsigned int h = duk_require_uint(ctx, 3);
-
-	painter_draw_rectangle(x, y, w, h);
-
-	return 0;
-}
-
-static duk_ret_t
-Painter_drawCircle(duk_context *ctx)
-{
-	const int x = duk_require_int(ctx, 0);
-	const int y = duk_require_int(ctx, 1);
-	const int r = duk_require_int(ctx, 2);
-
-	painter_draw_circle(x, y, r);
-
-	return 0;
-}
-
-static duk_ret_t
-Painter_clear(duk_context *ctx)
-{
-	(void)ctx;
-
-	painter_clear();
-
-	return 0;
-}
-
-static duk_ret_t
-Painter_present(duk_context *ctx)
-{
-	(void)ctx;
-
-	painter_present();
-
-	return 0;
-}
-
-static const duk_function_list_entry functions[] = {
-	{ "drawLine",           Painter_drawLine,       4       },
-	{ "drawPoint",          Painter_drawPoint,      2       },
-	{ "drawRectangle",      Painter_drawRectangle,  4       },
-	{ "drawCircle",         Painter_drawCircle,     3       },
-	{ "clear",              Painter_clear,          0       },
-	{ "present",            Painter_present,        0       },
-	{ NULL,                 NULL,                   0       }
-};
-
-void
-js_painter_bind(duk_context *ctx)
-{
-	assert(ctx);
-
-	duk_push_global_object(ctx);
-	duk_push_object(ctx);
-	duk_put_function_list(ctx, -1, functions);
-	duk_push_string(ctx, "color");
-	duk_push_c_function(ctx, Painter_getColor, 0);
-	duk_push_c_function(ctx, Painter_setColor, 1);
-	duk_def_prop(ctx, -4, DUK_DEFPROP_HAVE_SETTER | DUK_DEFPROP_HAVE_GETTER);
-	duk_push_string(ctx, "target");
-	duk_push_c_function(ctx, Painter_getTarget, 0);
-	duk_push_c_function(ctx, Painter_setTarget, 1);
-	duk_def_prop(ctx, -4, DUK_DEFPROP_HAVE_SETTER | DUK_DEFPROP_HAVE_GETTER);
-	duk_put_prop_string(ctx, -2, "Painter");
-	duk_pop(ctx);
-}
--- a/src/libmlk-core-js/core/js-painter.h	Fri Oct 07 15:25:32 2022 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,27 +0,0 @@
-/*
- * js-painter.h -- core painter binding
- *
- * Copyright (c) 2020-2022 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#ifndef MLK_CORE_JS_PAINTER_H
-#define MLK_CORE_JS_PAINTER_H
-
-#include <duktape.h>
-
-void
-js_painter_bind(duk_context *);
-
-#endif /* !MLK_CORE_JS_PAINTER_H */
--- a/src/libmlk-core-js/core/js-panic.c	Fri Oct 07 15:25:32 2022 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,88 +0,0 @@
-/*
- * js-panic.c -- core panic binding
- *
- * Copyright (c) 2020-2022 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <assert.h>
-#include <stdio.h>
-
-#include <core/panic.h>
-
-#include "js-panic.h"
-
-#define PANIC_CB DUK_HIDDEN_SYMBOL("Mlk.panicHandler")
-
-static void
-handler(void)
-{
-	duk_context *ctx = panic_data;
-
-	duk_push_global_stash(ctx);
-	duk_get_prop_string(ctx, -1, PANIC_CB);
-	duk_remove(ctx, -2);
-
-	if (duk_is_callable(ctx, -1)) {
-		duk_call(ctx, 0);
-		duk_pop(ctx);
-	} else {
-		fprintf(stderr, "abort: javascript panic handler not callable\n");
-		exit(1);
-	}
-}
-
-static duk_ret_t
-Mlk_panic(duk_context *ctx)
-{
-	if (duk_get_top(ctx) >= 1)
-		panicf("%s", duk_to_string(ctx, 0));
-	else
-		panic();
-
-	return 0;
-}
-
-static duk_ret_t
-Mlk_setPanic(duk_context *ctx)
-{
-	if (!duk_is_callable(ctx, 0))
-		return duk_error(ctx, DUK_ERR_TYPE_ERROR, "handler not callable");
-
-	duk_push_global_stash(ctx);
-	duk_dup(ctx, 0);
-	duk_put_prop_string(ctx, -2, PANIC_CB);
-	duk_pop(ctx);
-
-	panic_handler = handler;
-	panic_data = ctx;
-
-	return 0;
-}
-
-static const duk_function_list_entry functions[] = {
-	{ "panic",              Mlk_panic,              DUK_VARARGS     },
-	{ "setPanic",           Mlk_setPanic,           1               },
-	{ NULL,                 NULL,                   0               }
-};
-
-void
-js_panic_bind(duk_context *ctx)
-{
-	assert(ctx);
-
-	duk_get_global_string(ctx, "Mlk");
-	duk_put_function_list(ctx, -1, functions);
-	duk_pop(ctx);
-}
--- a/src/libmlk-core-js/core/js-panic.h	Fri Oct 07 15:25:32 2022 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,27 +0,0 @@
-/*
- * js-panic.h -- core panic binding
- *
- * Copyright (c) 2020-2022 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#ifndef MLK_CORE_JS_PANIC_H
-#define MLK_CORE_JS_PANIC_H
-
-#include <duktape.h>
-
-void
-js_panic_bind(duk_context *);
-
-#endif /* !MLK_CORE_JS_PANIC_H */
--- a/src/libmlk-core-js/core/js-sound.c	Fri Oct 07 15:25:32 2022 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,147 +0,0 @@
-/*
- * js-sound.c -- core sound binding
- *
- * Copyright (c) 2020-2022 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <assert.h>
-
-#include <core/alloc.h>
-#include <core/error.h>
-#include <core/sound.h>
-#include <core/vfs.h>
-
-#include "js-core.h"
-#include "js-sound.h"
-
-#define SIGNATURE DUK_HIDDEN_SYMBOL("Mlk.Sound")
-
-static struct sound *
-self(duk_context *ctx)
-{
-	struct sound *snd;
-
-	duk_push_this(ctx);
-	duk_get_prop_string(ctx, -1, SIGNATURE);
-	snd = duk_to_pointer(ctx, -1);
-	duk_pop_2(ctx);
-
-	if (!snd)
-		return (void)duk_error(ctx, DUK_ERR_TYPE_ERROR, "not a Sound object"), NULL;
-
-	return snd;
-}
-
-static duk_ret_t
-Sound_constructor(duk_context *ctx)
-{
-	const char *entry = duk_require_string(ctx, 0);
-	struct vfs_file file;
-	struct sound *snd;
-
-	if (vfs_open(js_core_global_vfs(ctx), &file, entry, "r") < 0)
-		return duk_error(ctx, DUK_ERR_ERROR, "%s", error());
-
-	snd = alloc_new0(sizeof (*snd));
-
-	if (sound_openvfs(snd, &file) < 0) {
-		free(snd);
-		vfs_file_finish(&file);
-
-		return duk_error(ctx, DUK_ERR_ERROR, "%s", error());
-	}
-
-	vfs_file_finish(&file);
-
-	duk_push_this(ctx);
-	duk_push_pointer(ctx, snd);
-	duk_put_prop_string(ctx, -2, SIGNATURE);
-	duk_pop(ctx);
-
-	return 0;
-
-
-}
-
-static duk_ret_t
-Sound_destructor(duk_context *ctx)
-{
-	struct sound *snd;
-
-	duk_get_prop_string(ctx, 0, SIGNATURE);
-
-	if ((snd = duk_to_pointer(ctx, -1)))
-		sound_finish(snd);
-
-	duk_pop(ctx);
-	duk_del_prop_string(ctx, 0, SIGNATURE);
-
-	return 0;
-}
-
-static duk_ret_t
-Sound_proto_play(duk_context *ctx)
-{
-	if (sound_play(self(ctx)) < 0)
-		return duk_error(ctx, DUK_ERR_ERROR, "%s", error());
-
-	return 0;
-}
-
-static duk_ret_t
-Sound_proto_pause(duk_context *ctx)
-{
-	sound_pause(self(ctx));
-
-	return 0;
-}
-
-static duk_ret_t
-Sound_proto_resume(duk_context *ctx)
-{
-	sound_resume(self(ctx));
-
-	return 0;
-}
-
-static duk_ret_t
-Sound_proto_stop(duk_context *ctx)
-{
-	sound_stop(self(ctx));
-
-	return 0;
-}
-
-static const duk_function_list_entry methods[] = {
-	{ "play",               Sound_proto_play,       DUK_VARARGS     },
-	{ "pause",              Sound_proto_pause,      0               },
-	{ "resume",             Sound_proto_resume,     0               },
-	{ "stop",               Sound_proto_stop,       DUK_VARARGS     },
-	{ NULL,                 NULL,                   0               }
-};
-
-void
-js_sound_bind(duk_context *ctx)
-{
-	assert(ctx);
-
-	duk_push_c_function(ctx, Sound_constructor, 1);
-	duk_push_object(ctx);
-	duk_put_function_list(ctx, -1, methods);
-	duk_push_c_function(ctx, Sound_destructor, 1);
-	duk_set_finalizer(ctx, -2);
-	duk_put_prop_string(ctx, -2, "prototype");
-	duk_put_global_string(ctx, "Sound");
-}
--- a/src/libmlk-core-js/core/js-sound.h	Fri Oct 07 15:25:32 2022 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,27 +0,0 @@
-/*
- * js-sound.h -- core sound binding
- *
- * Copyright (c) 2020-2022 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#ifndef MLK_CORE_JS_SOUND_H
-#define MLK_CORE_JS_SOUND_H
-
-#include <duktape.h>
-
-void
-js_sound_bind(duk_context *);
-
-#endif /* !MLK_CORE_JS_SOUND_H */
--- a/src/libmlk-core-js/core/js-sprite.c	Fri Oct 07 15:25:32 2022 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,160 +0,0 @@
-/*
- * js-sprite.c -- core sprite binding
- *
- * Copyright (c) 2020-2022 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <assert.h>
-
-#include <core/alloc.h>
-#include <core/sprite.h>
-
-#include "js-sprite.h"
-#include "js-texture.h"
-
-#define SIGNATURE DUK_HIDDEN_SYMBOL("Mlk.Sprite")
-#define PROTOTYPE DUK_HIDDEN_SYMBOL("Mlk.Sprite.Prototype")
-#define TEXREF DUK_HIDDEN_SYMBOL("Mlk.Sprite.texture")
-
-static inline struct sprite *
-self(duk_context *ctx)
-{
-	struct sprite *sprite;
-
-	duk_push_this(ctx);
-	duk_get_prop_string(ctx, -1, SIGNATURE);
-	sprite = duk_to_pointer(ctx, -1);
-	duk_pop_2(ctx);
-
-	if (!sprite)
-		return (void)duk_error(ctx, DUK_ERR_TYPE_ERROR, "not a Sprite object"), NULL;
-
-	return sprite;
-}
-
-static duk_ret_t
-Sprite_constructor(duk_context *ctx)
-{
-	struct sprite *sprite;
-	struct texture *tex = js_texture_require(ctx, 0);
-	const unsigned int cellw = duk_require_uint(ctx, 1);
-	const unsigned int cellh = duk_require_uint(ctx, 2);
-
-	sprite = alloc_new0(sizeof (*sprite));
-	sprite_init(sprite, tex, cellw, cellh);
-
-	duk_push_this(ctx);
-	duk_push_pointer(ctx, sprite);
-	duk_put_prop_string(ctx, -2, SIGNATURE);
-
-	/* Store a copy of texture to avoid destruction. */
-	duk_dup(ctx, 0);
-	duk_put_prop_string(ctx, -2, TEXREF);
-
-	duk_pop(ctx);
-
-	return 0;
-}
-
-static duk_ret_t
-Sprite_destructor(duk_context *ctx)
-{
-	duk_get_prop_string(ctx, 0, SIGNATURE);
-	free(duk_to_pointer(ctx, -1));
-	duk_pop(ctx);
-	duk_del_prop_string(ctx, 0, SIGNATURE);
-
-	/* Remove texture reference. */
-	duk_del_prop_string(ctx, 0, TEXREF);
-
-	return 0;
-}
-
-static duk_ret_t
-Sprite_draw(duk_context *ctx)
-{
-	const struct sprite *sprite = self(ctx);
-	const unsigned int r = duk_require_uint(ctx, 0);
-	const unsigned int c = duk_require_uint(ctx, 1);
-	const int x = duk_require_int(ctx, 2);
-	const int y = duk_require_int(ctx, 3);
-
-	if (r >= sprite->nrows)
-		return duk_error(ctx, DUK_ERR_RANGE_ERROR, "row overflow: %u >= %u", r, sprite->nrows);
-	if (c >= sprite->ncols)
-		return duk_error(ctx, DUK_ERR_RANGE_ERROR, "column overflow: %u >= %u", c, sprite->ncols);
-
-	sprite_draw(sprite, r, c, x, y);
-
-	return 0;
-}
-
-static duk_ret_t
-Sprite_scale(duk_context *ctx)
-{
-	const struct sprite *sprite = self(ctx);
-	const unsigned int r = duk_require_uint(ctx, 0);
-	const unsigned int c = duk_require_uint(ctx, 1);
-	const int x = duk_require_int(ctx, 2);
-	const int y = duk_require_int(ctx, 3);
-	const unsigned int w = duk_require_uint(ctx, 4);
-	const unsigned int h = duk_require_uint(ctx, 5);
-
-	if (r >= sprite->nrows)
-		return duk_error(ctx, DUK_ERR_RANGE_ERROR, "row overflow: %u >= %u", r, sprite->nrows);
-	if (c >= sprite->ncols)
-		return duk_error(ctx, DUK_ERR_RANGE_ERROR, "column overflow: %u >= %u", c, sprite->ncols);
-
-	sprite_scale(sprite, r, c, x, y, w, h);
-
-	return 0;
-}
-
-static const duk_function_list_entry methods[] = {
-	{ "draw",               Sprite_draw,            4               },
-	{ "scale",              Sprite_scale,           6               },
-	{ NULL,                 NULL,                   0               }
-};
-
-void
-js_sprite_bind(duk_context *ctx)
-{
-	assert(ctx);
-
-	duk_push_c_function(ctx, Sprite_constructor, 3);
-	duk_push_object(ctx);
-	duk_put_function_list(ctx, -1, methods);
-	duk_push_c_function(ctx, Sprite_destructor, 1);
-	duk_set_finalizer(ctx, -2);
-	duk_dup(ctx, -1);
-	duk_put_global_string(ctx, PROTOTYPE);
-	duk_put_prop_string(ctx, -2, "prototype");
-	duk_put_global_string(ctx, "Sprite");
-}
-
-struct sprite *
-js_sprite_require(duk_context *ctx, duk_idx_t idx)
-{
-	struct sprite *sprite;
-
-	duk_get_prop_string(ctx, idx, SIGNATURE);
-	sprite = duk_to_pointer(ctx, -1);
-	duk_pop(ctx);
-
-	if (!sprite)
-		return (void)duk_error(ctx, DUK_ERR_TYPE_ERROR, "not a Sprite object"), NULL;
-
-	return sprite;
-}
--- a/src/libmlk-core-js/core/js-sprite.h	Fri Oct 07 15:25:32 2022 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,32 +0,0 @@
-/*
- * js-sprite.h -- core sprite binding
- *
- * Copyright (c) 2020-2022 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#ifndef MLK_CORE_JS_SPRITE_H
-#define MLK_CORE_JS_SPRITE_H
-
-#include <duktape.h>
-
-struct sprite;
-
-void
-js_sprite_bind(duk_context *);
-
-struct sprite *
-js_sprite_require(duk_context *, duk_idx_t);
-
-#endif /* !MLK_CORE_JS_SPRITE_H */
--- a/src/libmlk-core-js/core/js-state.c	Fri Oct 07 15:25:32 2022 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,224 +0,0 @@
-/*
- * js-state.c -- core state binding
- *
- * Copyright (c) 2020-2022 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <assert.h>
-#include <stdlib.h>
-
-#include <core/alloc.h>
-#include <core/state.h>
-
-#include "js-event.h"
-#include "js-state.h"
-
-#define SIGNATURE DUK_HIDDEN_SYMBOL("Mlk.State")
-
-static inline duk_context *
-callable(struct js_state *data, const char *prop)
-{
-	duk_context *ctx;
-
-	if (!data->ptr)
-		return NULL;
-
-	duk_push_heapptr(data->ctx, data->ptr);
-	duk_get_prop_string(data->ctx, -1, prop);
-	duk_pull(data->ctx, -2);
-
-	if (duk_is_callable(data->ctx, -2))
-		ctx = data->ctx;
-	else {
-		ctx = NULL;
-		duk_pop_n(data->ctx, 2);
-	}
-
-	return ctx;
-}
-
-static void
-start(struct state *state)
-{
-	duk_context *ctx;
-
-	if ((ctx = callable(state->data, "start"))) {
-		duk_call_method(ctx, 0);
-		duk_pop(ctx);
-	}
-}
-
-static void
-handle(struct state *state, const union event *ev)
-{
-	duk_context *ctx;
-
-	if ((ctx = callable(state->data, "handle"))) {
-		js_event_push(ctx, ev);
-		duk_call_method(ctx, 1);
-		duk_pop(ctx);
-	}
-}
-
-static void
-update(struct state *state, unsigned int ticks)
-{
-	duk_context *ctx;
-
-	if ((ctx = callable(state->data, "update"))) {
-		duk_push_uint(ctx, ticks);
-		duk_call_method(ctx, 1);
-		duk_pop(ctx);
-	}
-}
-
-static void
-draw(struct state *state)
-{
-	duk_context *ctx;
-
-	if ((ctx = callable(state->data, "draw"))) {
-		duk_call_method(ctx, 0);
-		duk_pop(ctx);
-	}
-}
-
-static void
-suspend(struct state *state)
-{
-	duk_context *ctx;
-
-	if ((ctx = callable(state->data, "suspend"))) {
-		duk_call_method(ctx, 0);
-		duk_pop(ctx);
-	}
-}
-
-static void
-resume(struct state *state)
-{
-	duk_context *ctx;
-
-	if ((ctx = callable(state->data, "resume"))) {
-		duk_call_method(ctx, 0);
-		duk_pop(ctx);
-	}
-}
-
-static void
-end(struct state *state)
-{
-	duk_context *ctx;
-
-	if ((ctx = callable(state->data, "end"))) {
-		duk_call_method(ctx, 0);
-		duk_pop(ctx);
-	}
-}
-
-static void
-finish(struct state *state)
-{
-	struct js_state *data = state->data;
-
-	/* I must not be called anymore. */
-	data->ptr = NULL;
-
-	/* Remove myself from parent stack if any. */
-	if (data->parent) {
-		duk_push_heapptr(data->ctx, data->parent);
-		duk_push_sprintf(data->ctx, "%p", data);
-		duk_del_prop(data->ctx, -2);
-		duk_pop(data->ctx);
-		data->parent = NULL;
-	}
-
-	if (--data->refc == 0)
-		free(data);
-}
-
-static duk_ret_t
-State_constructor(duk_context *ctx)
-{
-	struct js_state *data;
-
-	data = alloc_new0(sizeof (*data));
-	data->ctx = ctx;
-	data->refc = 1;
-	data->st.data = data;
-	data->st.start = start;
-	data->st.handle = handle;
-	data->st.update = update;
-	data->st.draw = draw;
-	data->st.suspend = suspend;
-	data->st.resume = resume;
-	data->st.end = end;
-	data->st.finish = finish;
-
-	duk_push_this(ctx);
-	data->ptr = duk_get_heapptr(ctx, -1);
-	duk_push_pointer(ctx, data);
-	duk_put_prop_string(ctx, -2, SIGNATURE);
-	duk_pop(ctx);
-
-	return 0;
-}
-
-static duk_ret_t
-State_destructor(duk_context *ctx)
-{
-	struct js_state *data;
-
-	duk_get_prop_string(ctx, 0, SIGNATURE);
-
-	if ((data = duk_to_pointer(ctx, -1)))
-		state_finish(&data->st);
-
-	duk_pop(ctx);
-
-	return 0;
-}
-
-void
-js_state_bind(duk_context *ctx)
-{
-	assert(ctx);
-
-	duk_push_c_function(ctx, State_constructor, 1);
-	duk_push_object(ctx);
-	duk_push_c_function(ctx, State_destructor, 1);
-	duk_set_finalizer(ctx, -2);
-	duk_put_prop_string(ctx, -2, "prototype");
-	duk_put_global_string(ctx, "State");
-}
-
-struct js_state *
-js_state_require(duk_context *ctx, duk_idx_t idx)
-{
-	struct js_state *data = NULL;
-
-	if (duk_is_object(ctx, idx)) {
-		duk_get_prop_string(ctx, idx, SIGNATURE);
-		data = duk_to_pointer(ctx, -1);
-		duk_pop(ctx);
-	}
-
-	if (!data)
-		return (void)duk_error(ctx, DUK_ERR_TYPE_ERROR, "not a State object"), NULL;
-
-	data->refc++;
-
-	return data;
-}
--- a/src/libmlk-core-js/core/js-state.h	Fri Oct 07 15:25:32 2022 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,40 +0,0 @@
-/*
- * js-state.h -- core state binding
- *
- * Copyright (c) 2020-2022 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#ifndef MLK_CORE_JS_STATE_H
-#define MLK_CORE_JS_STATE_H
-
-#include <duktape.h>
-
-#include <core/state.h>
-
-struct js_state {
-	duk_context *ctx;
-	void *ptr;
-	void *parent;
-	unsigned int refc;
-	struct state st;
-};
-
-void
-js_state_bind(duk_context *);
-
-struct js_state *
-js_state_require(duk_context *, duk_idx_t);
-
-#endif /* !MLK_CORE_JS_STATE_H */
--- a/src/libmlk-core-js/core/js-texture.c	Fri Oct 07 15:25:32 2022 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,249 +0,0 @@
-/*
- * js-texture.c -- core texture binding
- *
- * Copyright (c) 2020-2022 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <assert.h>
-
-#include <core/alloc.h>
-#include <core/error.h>
-#include <core/image.h>
-#include <core/texture.h>
-#include <core/vfs.h>
-
-#include "js-core.h"
-#include "js-texture.h"
-
-#define SIGNATURE DUK_HIDDEN_SYMBOL("Mlk.Texture")
-#define PROTOTYPE DUK_HIDDEN_SYMBOL("Mlk.Texture.Prototype")
-
-static inline struct texture *
-self(duk_context *ctx)
-{
-	struct texture *tex;
-
-	duk_push_this(ctx);
-	duk_get_prop_string(ctx, -1, SIGNATURE);
-	tex = duk_to_pointer(ctx, -1);
-	duk_pop_2(ctx);
-
-	if (!tex)
-		return (void)duk_error(ctx, DUK_ERR_TYPE_ERROR, "not a Texture object"), NULL;
-
-	return tex;
-}
-
-static duk_ret_t
-Texture_constructor(duk_context *ctx)
-{
-	const unsigned int w = duk_require_uint(ctx, 0);
-	const unsigned int h = duk_require_uint(ctx, 1);
-	struct texture *tex;
-
-	tex = alloc_new(sizeof (*tex));
-
-	if (texture_new(tex, w, h) < 0)
-		return duk_error(ctx, DUK_ERR_ERROR, "%s", error());
-
-	duk_push_this(ctx);
-	duk_push_pointer(ctx, tex);
-	duk_put_prop_string(ctx, -2, SIGNATURE);
-	duk_pop(ctx);
-
-	return 0;
-}
-
-static duk_ret_t
-Texture_setBlendMode(duk_context *ctx)
-{
-	const int blend = duk_require_uint(ctx, 0);
-	struct texture *tex = self(ctx);
-
-	if (blend < 0 || blend >= TEXTURE_BLEND_LAST)
-		return duk_error(ctx, DUK_ERR_ERROR, "invalid blend");
-	if (texture_set_blend_mode(tex, blend) < 0)
-		return duk_error(ctx, DUK_ERR_ERROR, "%s", error());
-
-	return 0;
-}
-
-static duk_ret_t
-Texture_setAlphaMod(duk_context *ctx)
-{
-	const unsigned int alpha = duk_require_uint(ctx, 0);
-	struct texture *tex = self(ctx);
-
-	if (texture_set_alpha_mod(tex, alpha) < 0)
-		return duk_error(ctx, DUK_ERR_ERROR, "%s", error());
-
-	return 0;
-}
-
-static duk_ret_t
-Texture_setColorMod(duk_context *ctx)
-{
-	const unsigned long color = duk_require_uint(ctx, 0);
-	struct texture *tex = self(ctx);
-
-	if (texture_set_color_mod(tex, color) < 0)
-		return duk_error(ctx, DUK_ERR_ERROR, "%s", error());
-
-	return 0;
-}
-
-static duk_ret_t
-Texture_draw(duk_context *ctx)
-{
-	const int x = duk_require_int(ctx, 0);
-	const int y = duk_require_int(ctx, 1);
-	struct texture *tex = self(ctx);
-
-	if (texture_draw(tex, x, y) < 0)
-		return duk_error(ctx, DUK_ERR_ERROR, "%s", error());
-
-	return 0;
-}
-
-static duk_ret_t
-Texture_scale(duk_context *ctx)
-{
-	const int srcx      = duk_require_int(ctx, 0);
-	const int srcy      = duk_require_int(ctx, 1);
-	const unsigned srcw = duk_require_uint(ctx, 2);
-	const unsigned srch = duk_require_uint(ctx, 3);
-	const int dstx      = duk_require_int(ctx, 4);
-	const int dsty      = duk_require_int(ctx, 5);
-	const unsigned dstw = duk_require_uint(ctx, 6);
-	const unsigned dsth = duk_require_uint(ctx, 7);
-	const double angle  = duk_get_number_default(ctx, 8, .0);
-	struct texture *tex = self(ctx);
-
-	if (texture_scale(tex, srcx, srcy, srcw, srch, dstx, dsty, dstw, dsth, angle) < 0)
-		return duk_error(ctx, DUK_ERR_ERROR, "%s", error());
-
-	return 0;
-}
-
-static duk_ret_t
-Texture_destructor(duk_context *ctx)
-{
-	struct texture *tex;
-
-	duk_get_prop_string(ctx, 0, SIGNATURE);
-
-	if ((tex = duk_to_pointer(ctx, -1)))
-		texture_finish(tex);
-
-	duk_pop(ctx);
-	duk_del_prop_string(ctx, 0, SIGNATURE);
-
-	return 0;
-}
-
-static duk_ret_t
-Texture_fromImage(duk_context *ctx)
-{
-	const char *entry = duk_require_string(ctx, 0);
-	struct texture *tex;
-	struct vfs_file file;
-
-	if (vfs_open(js_core_global_vfs(ctx), &file, entry, "r") < 0)
-		return duk_error(ctx, DUK_ERR_ERROR, "%s", error());
-
-	tex = alloc_new0(sizeof (*tex));
-
-	if (image_openvfs(tex, &file) < 0) {
-		free(tex);
-		vfs_file_finish(&file);
-
-		return duk_error(ctx, DUK_ERR_ERROR, "%s", error());
-	}
-
-	vfs_file_finish(&file);
-	js_texture_push(ctx, tex);
-
-	return 1;
-}
-
-static const struct duk_function_list_entry methods[] = {
-	{ "setBlendMode",       Texture_setBlendMode,   1               },
-	{ "setAlphaMod",        Texture_setAlphaMod,    1               },
-	{ "setColorMod",        Texture_setColorMod,    1               },
-	{ "draw",               Texture_draw,           2               },
-	{ "scale",              Texture_scale,          DUK_VARARGS     },
-	{ NULL,                 NULL,                   0               }
-};
-
-static const struct duk_function_list_entry functions[] = {
-	{ "fromImage",          Texture_fromImage,      1               },
-	{ NULL,                 NULL,                   0               }
-};
-
-static const duk_number_list_entry blend[] = {
-	{ "NONE",       TEXTURE_BLEND_NONE      },
-	{ "BLEND",      TEXTURE_BLEND_BLEND     },
-	{ "ADD",        TEXTURE_BLEND_ADD       },
-	{ "MODULATE",   TEXTURE_BLEND_MODULATE  },
-	{ NULL,         0                       }
-};
-
-void
-js_texture_bind(duk_context *ctx)
-{
-	assert(ctx);
-
-	duk_push_c_function(ctx, Texture_constructor, 2);
-	duk_put_function_list(ctx, -1, functions);
-	duk_push_object(ctx);
-	duk_put_function_list(ctx, -1, methods);
-	duk_push_c_function(ctx, Texture_destructor, 1);
-	duk_set_finalizer(ctx, -2);
-	duk_push_object(ctx);
-	duk_put_number_list(ctx, -1, blend);
-	duk_put_prop_string(ctx, -2, "Blend");
-	duk_dup(ctx, -1);
-	duk_put_global_string(ctx, PROTOTYPE);
-	duk_put_prop_string(ctx, -2, "prototype");
-	duk_put_global_string(ctx, "Texture");
-}
-
-struct texture *
-js_texture_require(duk_context *ctx, duk_idx_t idx)
-{
-	struct texture *tex;
-
-	duk_get_prop_string(ctx, idx, SIGNATURE);
-	tex = duk_to_pointer(ctx, -1);
-	duk_pop(ctx);
-
-	if (!tex)
-		return (void)duk_error(ctx, DUK_ERR_TYPE_ERROR, "not a Texture object"), NULL;
-
-	return tex;
-}
-
-void
-js_texture_push(duk_context *ctx, struct texture *tex)
-{
-	assert(ctx);
-	assert(tex);
-
-	duk_push_object(ctx);
-	duk_push_pointer(ctx, tex);
-	duk_put_prop_string(ctx, -2, SIGNATURE);
-	duk_get_global_string(ctx, PROTOTYPE);
-	duk_set_prototype(ctx, -2);
-}
--- a/src/libmlk-core-js/core/js-texture.h	Fri Oct 07 15:25:32 2022 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,35 +0,0 @@
-/*
- * js-texture.h -- core texture binding
- *
- * Copyright (c) 2020-2022 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#ifndef MLK_CORE_JS_TEXTURE_H
-#define MLK_CORE_JS_TEXTURE_H
-
-#include <duktape.h>
-
-struct texture;
-
-void
-js_texture_bind(duk_context *);
-
-struct texture *
-js_texture_require(duk_context *, duk_idx_t);
-
-void
-js_texture_push(duk_context *, struct texture *);
-
-#endif /* !MLK_CORE_JS_TEXTURE_H */
--- a/src/libmlk-core-js/core/js-trace.c	Fri Oct 07 15:25:32 2022 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,85 +0,0 @@
-/*
- * js-trace.c -- core trace binding
- *
- * Copyright (c) 2020-2022 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <assert.h>
-
-#include <core/trace.h>
-
-#include "js-trace.h"
-
-#define TRACE_CB DUK_HIDDEN_SYMBOL("Mlk.traceHandler")
-
-static void
-handler(const char *msg)
-{
-	duk_context *ctx = trace_data;
-
-	duk_push_global_stash(ctx);
-	duk_get_prop_string(ctx, -1, TRACE_CB);
-	duk_remove(ctx, -2);
-
-	if (duk_is_callable(ctx, -1)) {
-		duk_push_string(ctx, msg);
-		duk_call(ctx, 1);
-		duk_pop(ctx);
-	} else {
-		fprintf(stderr, "abort: javascript panic handler not callable\n");
-		exit(1);
-	}
-}
-
-static duk_ret_t
-Mlk_trace(duk_context *ctx)
-{
-	tracef("%s", duk_require_string(ctx, 0));
-
-	return 0;
-}
-
-static duk_ret_t
-Mlk_setTrace(duk_context *ctx)
-{
-	if (!duk_is_callable(ctx, 0))
-		return duk_error(ctx, DUK_ERR_TYPE_ERROR, "handler not callable");
-
-	duk_push_global_stash(ctx);
-	duk_dup(ctx, 0);
-	duk_put_prop_string(ctx, -2, TRACE_CB);
-	duk_pop(ctx);
-
-	trace_handler = handler;
-	trace_data = ctx;
-
-	return 0;
-}
-
-static const duk_function_list_entry functions[] = {
-	{ "trace",              Mlk_trace,              1       },
-	{ "setTrace",           Mlk_setTrace,           1       },
-	{ NULL,                 NULL,                   0       }
-};
-
-void
-js_trace_bind(duk_context *ctx)
-{
-	assert(ctx);
-
-	duk_get_global_string(ctx, "Mlk");
-	duk_put_function_list(ctx, -1, functions);
-	duk_pop(ctx);
-}
--- a/src/libmlk-core-js/core/js-trace.h	Fri Oct 07 15:25:32 2022 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,27 +0,0 @@
-/*
- * js-trace.h -- core trace binding
- *
- * Copyright (c) 2020-2022 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#ifndef MLK_CORE_JS_TRACE_H
-#define MLK_CORE_JS_TRACE_H
-
-#include <duktape.h>
-
-void
-js_trace_bind(duk_context *);
-
-#endif /* !MLK_CORE_JS_TRACE_H */
--- a/src/libmlk-core-js/core/js-window.c	Fri Oct 07 15:25:32 2022 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,90 +0,0 @@
-/*
- * js-window.c -- core window binding
- *
- * Copyright (c) 2020-2022 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <assert.h>
-
-#include <core/error.h>
-#include <core/window.h>
-
-#include "js-window.h"
-
-#define SIGNATURE DUK_HIDDEN_SYMBOL("Mlk.Window")
-
-static duk_ret_t
-Window_constructor(duk_context *ctx)
-{
-	const char *title = duk_require_string(ctx, 0);
-	const unsigned int w = duk_require_uint(ctx, 1);
-	const unsigned int h = duk_require_uint(ctx, 2);
-
-	if (window_open(title, w, h) < 0)
-		return duk_error(ctx, DUK_ERR_ERROR, "%s", error());
-
-	duk_push_this(ctx);
-	duk_push_boolean(ctx, 1);
-	duk_put_prop_string(ctx, -2, SIGNATURE);
-	duk_pop(ctx);
-
-	return 0;
-}
-
-static duk_ret_t
-Window_setCursor(duk_context *ctx)
-{
-	const unsigned int cursor = duk_require_uint(ctx, 0);
-
-	if (cursor >= WINDOW_CURSOR_LAST)
-		return duk_error(ctx, DUK_ERR_RANGE_ERROR, "invalid cursor");
-
-	window_set_cursor(cursor);
-
-	return 0;
-}
-
-static duk_ret_t
-Window_destructor(duk_context *ctx)
-{
-	duk_get_prop_string(ctx, 0, SIGNATURE);
-
-	if (duk_to_boolean(ctx, -1))
-		window_finish();
-
-	duk_pop(ctx);
-	duk_del_prop_string(ctx, 0, SIGNATURE);
-
-	return 0;
-}
-
-static const duk_function_list_entry methods[] = {
-	{ "setCursor",  Window_setCursor,       1 },
-	{ NULL,         NULL,                   0 }
-};
-
-void
-js_window_bind(duk_context *ctx)
-{
-	assert(ctx);
-
-	duk_push_c_function(ctx, Window_constructor, 3);
-	duk_push_object(ctx);
-	duk_put_function_list(ctx, -1, methods);
-	duk_put_prop_string(ctx, -2, "prototype");
-	duk_push_c_function(ctx, Window_destructor, 1);
-	duk_set_finalizer(ctx, -2);
-	duk_put_global_string(ctx, "Window");
-}
--- a/src/libmlk-core-js/core/js-window.h	Fri Oct 07 15:25:32 2022 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,27 +0,0 @@
-/*
- * js-window.h -- core window binding
- *
- * Copyright (c) 2020-2022 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#ifndef MLK_CORE_JS_WINDOW_H
-#define MLK_CORE_JS_WINDOW_H
-
-#include <duktape.h>
-
-void
-js_window_bind(duk_context *);
-
-#endif /* !MLK_CORE_JS_WINDOW_H */
--- a/src/mlk-run/CMakeLists.txt	Fri Oct 07 15:25:32 2022 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,31 +0,0 @@
-#
-# CMakeLists.txt -- CMake build system for Molko's Engine
-#
-# Copyright (c) 2020-2022 David Demelier <markand@malikania.fr>
-#
-# Permission to use, copy, modify, and/or distribute this software for any
-# purpose with or without fee is hereby granted, provided that the above
-# copyright notice and this permission notice appear in all copies.
-#
-# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
-# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
-# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
-# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
-# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
-# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
-# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-#
-
-project(mlk-run)
-
-set(
-	SOURCES
-	${mlk-run_SOURCE_DIR}/main.c
-)
-
-mlk_executable(
-	NAME mlk-run
-	INSTALL
-	SOURCES ${SOURCES}
-	LIBRARIES libmlk-core-js
-)
--- a/src/mlk-run/main.c	Fri Oct 07 15:25:32 2022 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,215 +0,0 @@
-/*
- * main.c -- main mlk-run file
- *
- * Copyright (c) 2020-2022 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <limits.h>
-
-#include <duktape.h>
-#include <duk_module_duktape.h>
-
-#include <port/port.h>
-
-#include <core/error.h>
-#include <core/panic.h>
-#include <core/vfs-directory.h>
-#include <core/vfs-zip.h>
-#include <core/vfs.h>
-
-#include <core/js-action.h>
-#include <core/js-action-stack.h>
-#include <core/js-animation.h>
-#include <core/js-clock.h>
-#include <core/js-color.h>
-#include <core/js-core.h>
-#include <core/js-drawable.h>
-#include <core/js-drawable-stack.h>
-#include <core/js-event.h>
-#include <core/js-font.h>
-#include <core/js-game.h>
-#include <core/js-music.h>
-#include <core/js-painter.h>
-#include <core/js-panic.h>
-#include <core/js-sound.h>
-#include <core/js-sprite.h>
-#include <core/js-state.h>
-#include <core/js-texture.h>
-#include <core/js-trace.h>
-#include <core/js-window.h>
-
-/* VFS loader to support zip and directories when loading game. */
-static struct vfs vfs;
-
-/* Javascript context. */
-static duk_context *ctx;
-
-static duk_ret_t
-print(duk_context *ctx)
-{
-	puts(duk_require_string(ctx, 0));
-
-	return 0;
-}
-
-static duk_ret_t
-modsearch(duk_context *ctx)
-{
-	char path[PATH_MAX], *data;
-	struct vfs_file file;
-	size_t datasz;
-
-	snprintf(path, sizeof (path), "%s.js", duk_require_string(ctx, 0));
-
-	if (vfs_open(&vfs, &file, path, "r") < 0)
-		return duk_error(ctx, DUK_ERR_ERROR, "%s", error());
-	if (!(data = vfs_file_aread(&file, &datasz))) {
-		vfs_file_finish(&file);
-		return duk_error(ctx, DUK_ERR_ERROR, "%s", error());
-	}
-
-	vfs_file_finish(&file);
-	duk_push_lstring(ctx, data, datasz);
-	free(data);
-
-	return 1;
-}
-
-static void
-core_bind(duk_context *ctx)
-{
-	/* Brings Mlk global object. */
-	js_core_bind(ctx, &vfs);
-
-	js_action_bind(ctx);
-	js_action_stack_bind(ctx);
-	js_animation_bind(ctx);
-	js_clock_bind(ctx);
-	js_color_bind(ctx);
-	js_drawable_bind(ctx);
-	js_drawable_stack_bind(ctx);
-	js_event_bind(ctx);
-	js_font_bind(ctx);
-	js_game_bind(ctx);
-	js_music_bind(ctx);
-	js_painter_bind(ctx);
-	js_panic_bind(ctx);
-	js_sound_bind(ctx);
-	js_sprite_bind(ctx);
-	js_state_bind(ctx);
-	js_texture_bind(ctx);
-	js_trace_bind(ctx);
-	js_window_bind(ctx);
-}
-
-static void
-init(void)
-{
-	/* TODO: this is temporary. */
-	if (core_init("fr.malikania", "mlk-run") < 0)
-		panic();
-
-	/* Fireup Javascript. */
-	ctx = duk_create_heap_default();
-	core_bind(ctx);
-
-	/* Setup some convenient global functions. */
-	duk_push_global_object(ctx);
-	duk_push_c_function(ctx, print, 1);
-	duk_put_prop_string(ctx, -2, "print");
-	duk_pop(ctx);
-
-	/* Setup module loader. */
-	duk_module_duktape_init(ctx);
-	duk_get_global_string(ctx, "Duktape");
-	duk_push_c_function(ctx, modsearch, 4);
-	duk_put_prop_string(ctx, -2, "modSearch");
-	duk_pop(ctx);
-}
-
-static void
-startup(void)
-{
-	struct vfs_file main;
-	char *code;
-
-	if (vfs_open(&vfs, &main, "main.js", "r") < 0)
-		panic();
-	if (!(code = vfs_file_aread(&main, NULL)))
-		panic();
-
-	vfs_file_finish(&main);
-
-	if (duk_peval_string(ctx, code))
-		panicf("%s", duk_safe_to_string(ctx, -1));
-
-	free(code);
-}
-
-static void
-finish(void)
-{
-	vfs_finish(&vfs);
-	duk_destroy_heap(ctx);
-}
-
-static void
-loadzip(const char *path)
-{
-	if (vfs_zip(&vfs, path, "r") < 0)
-		panic();
-}
-
-static void
-loaddirectory(const char *path)
-{
-	vfs_directory(&vfs, path);
-}
-
-static void
-load(const char *path)
-{
-	/* TODO: improve this. */
-	if (strstr(path, ".mlk"))
-		loadzip(path);
-	else
-		loaddirectory(path);
-
-	startup();
-}
-
-static void
-run(int argc, char **argv)
-{
-	if (argc < 0) {
-		fprintf(stderr, "usage: mlk-run game\n");
-		exit(1);
-	}
-
-	load(argv[0]);
-}
-
-int
-main(int argc, char **argv)
-{
-	--argc;
-	++argv;
-
-	init();
-	run(argc, argv);
-	finish();
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/tools/bcc/arg.h	Sun Oct 09 13:51:03 2022 +0200
@@ -0,0 +1,49 @@
+/*
+ * Copy me if you can.
+ * by 20h
+ */
+
+#ifndef ARG_H__
+#define ARG_H__
+
+extern char *argv0;
+
+/* use main(int argc, char *argv[]) */
+#define ARGBEGIN	for (argv0 = *argv, argv++, argc--;\
+					argv[0] && argv[0][0] == '-'\
+					&& argv[0][1];\
+					argc--, argv++) {\
+				char argc_;\
+				char **argv_;\
+				int brk_;\
+				if (argv[0][1] == '-' && argv[0][2] == '\0') {\
+					argv++;\
+					argc--;\
+					break;\
+				}\
+				for (brk_ = 0, argv[0]++, argv_ = argv;\
+						argv[0][0] && !brk_;\
+						argv[0]++) {\
+					if (argv_ != argv)\
+						break;\
+					argc_ = argv[0][0];\
+					switch (argc_)
+
+#define ARGEND			}\
+			}
+
+#define ARGC()		argc_
+
+#define EARGF(x)	((argv[0][1] == '\0' && argv[1] == NULL)?\
+				((x), abort(), (char *)0) :\
+				(brk_ = 1, (argv[0][1] != '\0')?\
+					(&argv[0][1]) :\
+					(argc--, argv++, argv[0])))
+
+#define ARGF()		((argv[0][1] == '\0' && argv[1] == NULL)?\
+				(char *)0 :\
+				(brk_ = 1, (argv[0][1] != '\0')?\
+					(&argv[0][1]) :\
+					(argc--, argv++, argv[0])))
+
+#endif
--- a/src/tools/bcc/main.c	Fri Oct 07 15:25:32 2022 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,176 +0,0 @@
-/*
- * main.c -- binary to C/C++ arrays converter
- *
- * Copyright (c) 2020-2022 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <errno.h>
-#include <stdarg.h>
-#include <stdbool.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include <port/port.h>
-
-static const char *charset = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_";
-static char findentchar = '\t';
-static int findent = 1;
-static bool fconst;
-static bool fnull;
-static bool fstatic;
-static bool funsigned;
-
-static void
-usage(void)
-{
-	fprintf(stderr, "usage: bcc [-0csu] [-I tab-indent] [-i space-indent] input variable\n");
-	exit(1);
-}
-
-static void
-die(const char *fmt, ...)
-{
-	va_list ap;
-
-	va_start(ap, fmt);
-	fputs("abort: ", stderr);
-	vfprintf(stderr, fmt, ap);
-	va_end(ap);
-	exit(1);
-}
-
-static char *
-mangle(char *variable)
-{
-	char *p;
-	size_t pos;
-
-	/* Remove extension. */
-	if ((p = strrchr(variable, '.')))
-		*p = '\0';
-
-	/* Remove disallowed characters. */
-	while ((pos = strspn(variable, charset)) != strlen(variable))
-		variable[pos] = '_';
-
-	return variable;
-}
-
-static void
-indent(void)
-{
-	for (int i = 0; i < findent; ++i)
-		putchar(findentchar);
-}
-
-static void
-put(int ch)
-{
-	if (funsigned)
-		printf("0x%02hhx", (unsigned char)ch);
-	else
-		printf("%hhd", (signed char)ch);
-}
-
-static void
-process(const char *input, const char *variable)
-{
-	FILE *fp;
-	int ch, col = 0;
-
-	if (strcmp(input, "-") == 0)
-		fp = stdin;
-	else if (!(fp = fopen(input, "rb")))
-		die("%s: %s\n", input, strerror(errno));
-
-	if (fstatic)
-		printf("static ");
-	if (fconst)
-		printf("const ");
-
-	printf(funsigned ? "unsigned " : "signed ");
-	printf("char %s[] = {\n", variable);
-
-	for (ch = fgetc(fp); ch != EOF; ) {
-		if (col == 0)
-			indent();
-
-		put(ch);
-
-		if ((ch = fgetc(fp)) != EOF || fnull)
-			printf(",%s", col < 3 ? " " : "");
-
-		if (++col == 4) {
-			col = 0;
-			putchar('\n');
-		}
-
-		/* Add final '\0' if required. */
-		if (ch == EOF && fnull) {
-			if (col++ == 0)
-				indent();
-
-			put(0);
-		}
-	}
-
-	if (col != 0)
-		printf("\n");
-
-	puts("};");
-	fclose(fp);
-}
-
-int
-main(int argc, char **argv)
-{
-	int ch;
-
-	while ((ch = port_getopt(argc, argv, "0cI:i:su")) != -1) {
-		switch (ch) {
-		case '0':
-			fnull = true;
-			break;
-		case 'c':
-			fconst = true;
-			break;
-		case 'I':
-			findentchar = '\t';
-			findent = atoi(port_optarg);
-			break;
-		case 'i':
-			findentchar = ' ';
-			findent = atoi(port_optarg);
-			break;
-		case 's':
-			fstatic = true;
-			break;
-		case 'u':
-			funsigned = true;
-			break;
-		default:
-			break;
-		}
-	}
-
-	argc -= port_optind;
-	argv += port_optind;
-
-	if (argc < 2)
-		usage();
-
-	process(argv[0], mangle(argv[1]));
-}
--- a/src/tools/map/main.c	Fri Oct 07 15:25:32 2022 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,257 +0,0 @@
-/*
- * main.c -- convert tiled tiled JSON files into custom files
- *
- * Copyright (c) 2020-2022 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <assert.h>
-#include <limits.h>
-#include <stdarg.h>
-#include <stdbool.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include <jansson.h>
-
-#include <port/port.h>
-
-static void
-die(const char *fmt, ...)
-{
-	assert(fmt);
-
-	va_list ap;
-
-	va_start(ap, fmt);
-	vfprintf(stderr, fmt, ap);
-	va_end(ap);
-	exit(1);
-}
-
-static bool
-is_layer(const char *name)
-{
-	return strcmp(name, "background") == 0 ||
-	       strcmp(name, "foreground") == 0 ||
-	       strcmp(name, "above") == 0 ||
-	       strcmp(name, "actions") == 0;
-}
-
-static const json_t *
-find_property(const json_t *props, const char *which)
-{
-	const json_t *obj;
-	size_t index;
-
-	json_array_foreach(props, index, obj) {
-		if (!json_is_object(obj))
-			continue;
-
-		const json_t *key = json_object_get(obj, "name");
-		const json_t *value = json_object_get(obj, "value");
-
-		if (json_is_string(key) && value && strcmp(json_string_value(key), which) == 0)
-			return value;
-	}
-
-	return NULL;
-}
-
-static void
-write_title(const json_t *props)
-{
-	const json_t *prop_title = find_property(props, "title");
-
-	if (prop_title && json_is_string(prop_title))
-		printf("title|%s\n", json_string_value(prop_title));
-}
-
-static void
-write_origin(const json_t *props)
-{
-	const json_t *prop_origin_x = find_property(props, "origin-x");
-	const json_t *prop_origin_y = find_property(props, "origin-y");
-
-	if (!prop_origin_x || !json_is_integer(prop_origin_x) ||
-	    !prop_origin_y || !json_is_integer(prop_origin_y))
-		return;
-
-	printf("origin|%d|%d\n",
-	    (int)json_integer_value(prop_origin_x),
-	    (int)json_integer_value(prop_origin_y));
-}
-
-static void
-write_properties(const json_t *props)
-{
-	write_title(props);
-	write_origin(props);
-}
-
-static void
-write_dimensions(const json_t *document)
-{
-	json_t *width = json_object_get(document, "width");
-	json_t *height = json_object_get(document, "height");
-
-	if (!width || !json_is_integer(width))
-		die("missing 'width' property\n");
-	if (!height || !json_is_integer(height))
-		die("missing 'height' property\n");
-
-	printf("columns|%d\n", (int)json_integer_value(width));
-	printf("rows|%d\n", (int)json_integer_value(height));
-}
-
-static void
-write_object(const json_t *object)
-{
-	assert(json_is_object(object));
-
-	json_t *x = json_object_get(object, "x");
-	json_t *y = json_object_get(object, "y");
-	json_t *width = json_object_get(object, "width");
-	json_t *height = json_object_get(object, "height");
-	json_t *props = json_object_get(object, "properties");
-	const json_t *exec, *block;
-
-	if (!x || !json_is_number(x))
-		die("invalid 'x' property in object\n");
-	if (!y || !json_is_number(y))
-		die("invalid 'y' property in object\n");
-	if (!width || !json_is_number(width))
-		die("invalid 'width' property in object\n");
-	if (!height || !json_is_number(height))
-		die("invalid 'height' property in object\n");
-
-	/* This is optional and set to 0 if not present. */
-	block = find_property(props, "block");
-
-	/* In tiled, those properties are float but we only use ints in MA */
-	printf("%d|%d|%d|%d|%d",
-	    (int)json_integer_value(x),
-	    (int)json_integer_value(y),
-	    (int)json_integer_value(width),
-	    (int)json_integer_value(height),
-	    (int)json_is_true(block)
-	);
-
-	if ((exec = find_property(props, "exec")) && json_is_string(exec))
-		printf("|%s", json_string_value(exec));
-
-	printf("\n");
-}
-
-static void
-write_layer(const json_t *layer)
-{
-	json_t *objects = json_object_get(layer, "objects");
-	json_t *data = json_object_get(layer, "data");
-	json_t *name = json_object_get(layer, "name");
-	json_t *tile, *object;
-	size_t index;
-
-	if (!name || !json_is_string(name))
-		die("invalid 'name' property in layer");
-	if (!is_layer(json_string_value(name)))
-		die("invalid 'name' layer: %s\n", json_string_value(name));
-
-	printf("layer|%s\n", json_string_value(name));
-
-	/* Only foreground/background have 'data' property */
-	if (json_is_array(data)) {
-		json_array_foreach(data, index, tile) {
-			if (!json_is_integer(tile))
-				die("invalid 'data' property in layer\n");
-
-			printf("%d\n", (int)json_integer_value(tile));
-		}
-	}
-
-	/* Only objects has 'objects' property */
-	if (json_is_array(objects)) {
-		json_array_foreach(objects, index, object) {
-			if (!json_is_object(object))
-				die("invalid 'objects' property in layer\n");
-
-			write_object(object);
-		}
-	}
-}
-
-static void
-write_layers(const json_t *layers)
-{
-	size_t index;
-	json_t *layer;
-
-	if (!layers)
-		return;
-
-	json_array_foreach(layers, index, layer) {
-		if (!json_is_object(layer))
-			die("layer is not an object\n");
-
-		write_layer(layer);
-	}
-}
-
-static void
-write_tileset(const json_t *tilesets)
-{
-	char path[PATH_MAX];
-	char filename[FILENAME_MAX] = {0}, *ext;
-	const json_t *tileset, *source;
-
-	if (json_array_size(tilesets) != 1)
-		die("map must contain exactly one tileset");
-
-	tileset = json_array_get(tilesets, 0);
-	source = json_object_get(tileset, "source");
-
-	if (!json_is_string(source))
-		die("invalid 'source' property in tileset\n");
-
-	/* We need to replace the .json extension to .tileset. */
-	snprintf(path, sizeof (path), "%s", json_string_value(source));
-	snprintf(filename, sizeof (filename), "%s", port_basename(path));
-
-	if (!(ext = strstr(filename, ".json")))
-		die("could not determine tileset extension");
-
-	*ext = '\0';
-
-	printf("tileset|%s.tileset\n", filename);
-}
-
-int
-main(void)
-{
-	json_t *document;
-	json_error_t error;
-
-	document = json_loadf(stdin, 0, &error);
-
-	if (!document)
-		die("%d:%d: %s\n", error.line, error.column, error.text);
-
-	write_properties(json_object_get(document, "properties"));
-	write_dimensions(document);
-	write_layers(json_object_get(document, "layers"));
-	write_tileset(json_object_get(document, "tilesets"));
-
-	json_decref(document);
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/tools/map/mlk-map.c	Sun Oct 09 13:51:03 2022 +0200
@@ -0,0 +1,257 @@
+/*
+ * main.c -- convert tiled tiled JSON files into custom files
+ *
+ * Copyright (c) 2020-2022 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <assert.h>
+#include <limits.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <jansson.h>
+
+#include <port/port.h>
+
+static void
+die(const char *fmt, ...)
+{
+	assert(fmt);
+
+	va_list ap;
+
+	va_start(ap, fmt);
+	vfprintf(stderr, fmt, ap);
+	va_end(ap);
+	exit(1);
+}
+
+static bool
+is_layer(const char *name)
+{
+	return strcmp(name, "background") == 0 ||
+	       strcmp(name, "foreground") == 0 ||
+	       strcmp(name, "above") == 0 ||
+	       strcmp(name, "actions") == 0;
+}
+
+static const json_t *
+find_property(const json_t *props, const char *which)
+{
+	const json_t *obj;
+	size_t index;
+
+	json_array_foreach(props, index, obj) {
+		if (!json_is_object(obj))
+			continue;
+
+		const json_t *key = json_object_get(obj, "name");
+		const json_t *value = json_object_get(obj, "value");
+
+		if (json_is_string(key) && value && strcmp(json_string_value(key), which) == 0)
+			return value;
+	}
+
+	return NULL;
+}
+
+static void
+write_title(const json_t *props)
+{
+	const json_t *prop_title = find_property(props, "title");
+
+	if (prop_title && json_is_string(prop_title))
+		printf("title|%s\n", json_string_value(prop_title));
+}
+
+static void
+write_origin(const json_t *props)
+{
+	const json_t *prop_origin_x = find_property(props, "origin-x");
+	const json_t *prop_origin_y = find_property(props, "origin-y");
+
+	if (!prop_origin_x || !json_is_integer(prop_origin_x) ||
+	    !prop_origin_y || !json_is_integer(prop_origin_y))
+		return;
+
+	printf("origin|%d|%d\n",
+	    (int)json_integer_value(prop_origin_x),
+	    (int)json_integer_value(prop_origin_y));
+}
+
+static void
+write_properties(const json_t *props)
+{
+	write_title(props);
+	write_origin(props);
+}
+
+static void
+write_dimensions(const json_t *document)
+{
+	json_t *width = json_object_get(document, "width");
+	json_t *height = json_object_get(document, "height");
+
+	if (!width || !json_is_integer(width))
+		die("missing 'width' property\n");
+	if (!height || !json_is_integer(height))
+		die("missing 'height' property\n");
+
+	printf("columns|%d\n", (int)json_integer_value(width));
+	printf("rows|%d\n", (int)json_integer_value(height));
+}
+
+static void
+write_object(const json_t *object)
+{
+	assert(json_is_object(object));
+
+	json_t *x = json_object_get(object, "x");
+	json_t *y = json_object_get(object, "y");
+	json_t *width = json_object_get(object, "width");
+	json_t *height = json_object_get(object, "height");
+	json_t *props = json_object_get(object, "properties");
+	const json_t *exec, *block;
+
+	if (!x || !json_is_number(x))
+		die("invalid 'x' property in object\n");
+	if (!y || !json_is_number(y))
+		die("invalid 'y' property in object\n");
+	if (!width || !json_is_number(width))
+		die("invalid 'width' property in object\n");
+	if (!height || !json_is_number(height))
+		die("invalid 'height' property in object\n");
+
+	/* This is optional and set to 0 if not present. */
+	block = find_property(props, "block");
+
+	/* In tiled, those properties are float but we only use ints in MA */
+	printf("%d|%d|%d|%d|%d",
+	    (int)json_integer_value(x),
+	    (int)json_integer_value(y),
+	    (int)json_integer_value(width),
+	    (int)json_integer_value(height),
+	    (int)json_is_true(block)
+	);
+
+	if ((exec = find_property(props, "exec")) && json_is_string(exec))
+		printf("|%s", json_string_value(exec));
+
+	printf("\n");
+}
+
+static void
+write_layer(const json_t *layer)
+{
+	json_t *objects = json_object_get(layer, "objects");
+	json_t *data = json_object_get(layer, "data");
+	json_t *name = json_object_get(layer, "name");
+	json_t *tile, *object;
+	size_t index;
+
+	if (!name || !json_is_string(name))
+		die("invalid 'name' property in layer");
+	if (!is_layer(json_string_value(name)))
+		die("invalid 'name' layer: %s\n", json_string_value(name));
+
+	printf("layer|%s\n", json_string_value(name));
+
+	/* Only foreground/background have 'data' property */
+	if (json_is_array(data)) {
+		json_array_foreach(data, index, tile) {
+			if (!json_is_integer(tile))
+				die("invalid 'data' property in layer\n");
+
+			printf("%d\n", (int)json_integer_value(tile));
+		}
+	}
+
+	/* Only objects has 'objects' property */
+	if (json_is_array(objects)) {
+		json_array_foreach(objects, index, object) {
+			if (!json_is_object(object))
+				die("invalid 'objects' property in layer\n");
+
+			write_object(object);
+		}
+	}
+}
+
+static void
+write_layers(const json_t *layers)
+{
+	size_t index;
+	json_t *layer;
+
+	if (!layers)
+		return;
+
+	json_array_foreach(layers, index, layer) {
+		if (!json_is_object(layer))
+			die("layer is not an object\n");
+
+		write_layer(layer);
+	}
+}
+
+static void
+write_tileset(const json_t *tilesets)
+{
+	char path[PATH_MAX];
+	char filename[FILENAME_MAX] = {0}, *ext;
+	const json_t *tileset, *source;
+
+	if (json_array_size(tilesets) != 1)
+		die("map must contain exactly one tileset");
+
+	tileset = json_array_get(tilesets, 0);
+	source = json_object_get(tileset, "source");
+
+	if (!json_is_string(source))
+		die("invalid 'source' property in tileset\n");
+
+	/* We need to replace the .json extension to .tileset. */
+	snprintf(path, sizeof (path), "%s", json_string_value(source));
+	snprintf(filename, sizeof (filename), "%s", port_basename(path));
+
+	if (!(ext = strstr(filename, ".json")))
+		die("could not determine tileset extension");
+
+	*ext = '\0';
+
+	printf("tileset|%s.tileset\n", filename);
+}
+
+int
+main(void)
+{
+	json_t *document;
+	json_error_t error;
+
+	document = json_loadf(stdin, 0, &error);
+
+	if (!document)
+		die("%d:%d: %s\n", error.line, error.column, error.text);
+
+	write_properties(json_object_get(document, "properties"));
+	write_dimensions(document);
+	write_layers(json_object_get(document, "layers"));
+	write_tileset(json_object_get(document, "tilesets"));
+
+	json_decref(document);
+}
--- a/src/tools/tileset/main.c	Fri Oct 07 15:25:32 2022 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,204 +0,0 @@
-/*
- * main.c -- convert tiled tilesets JSON files into custom files
- *
- * Copyright (c) 2020-2022 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <assert.h>
-#include <stdarg.h>
-#include <stdio.h>
-#include <string.h>
-
-#include <jansson.h>
-
-static void
-die(const char *fmt, ...)
-{
-	assert(fmt);
-
-	va_list ap;
-
-	va_start(ap, fmt);
-	vfprintf(stderr, fmt, ap);
-	va_end(ap);
-	exit(1);
-}
-
-static void
-write_dimensions(const json_t *document)
-{
-	const json_t *tilewidth = json_object_get(document, "tilewidth");
-	const json_t *tileheight = json_object_get(document, "tileheight");
-
-	if (!json_is_integer(tilewidth))
-		die("invalid 'tilewidth' property\n");
-	if (!json_is_integer(tileheight))
-		die("invalid 'tileheight' property\n");
-
-	printf("tilewidth|%u\n", (unsigned int)json_integer_value(tilewidth));
-	printf("tileheight|%u\n", (unsigned int)json_integer_value(tileheight));
-}
-
-static void
-write_image(const json_t *document)
-{
-	const json_t *image = json_object_get(document, "image");
-
-	if (!json_is_string(image))
-		die("invalid 'image' property\n");
-
-	printf("image|%s\n", json_string_value(image));
-}
-
-static const json_t *
-find_property_value(const json_t *array, const char *prop)
-{
-	const json_t *obj;
-	size_t i;
-
-	json_array_foreach(array, i, obj) {
-		const json_t *name = json_object_get(obj, "name");
-
-		if (!name || !json_is_string(name))
-			die("invalid property object\n");
-
-		if (strcmp(json_string_value(name), prop) == 0)
-			return json_object_get(obj, "value");
-	}
-
-	return NULL;
-}
-
-static void
-write_animation(const json_t *tile)
-{
-	const json_t *id = json_object_get(tile, "id");
-	const json_t *properties = json_object_get(tile, "properties");
-	const json_t *file = find_property_value(properties, "animation-file");
-	const json_t *delay = find_property_value(properties, "animation-delay");
-
-	/* Animations are completely optional. */
-	if (!json_is_array(properties))
-		return;
-
-	if (!json_is_integer(id))
-		die("invalid 'id' property in tile\n");
-
-	if (json_is_string(file)) {
-		printf("%d|%s|", (int)json_integer_value(id), json_string_value(file));
-
-		if (json_is_integer(delay))
-			printf("%d\n", (int)json_integer_value(delay));
-		else
-			printf("10\n");
-	}
-}
-
-static void
-write_tiledef(const json_t *tile)
-{
-	const json_t *id = json_object_get(tile, "id");
-	const json_t *objectgroup = json_object_get(tile, "objectgroup");
-	const json_t *objects = json_object_get(objectgroup, "objects");
-	const json_t *first = json_array_get(objects, 0);
-	const json_t *x, *y, *w, *h;
-
-	/* Collisions are optional. */
-	if (!json_is_object(objectgroup))
-		return;
-
-	if (!json_is_integer(id))
-		die("invalid 'id' property in tile\n");
-	if (!json_is_array(objects))
-		die("invalid 'objects' property in tile\n");
-
-	x = json_object_get(first, "x");
-	y = json_object_get(first, "y");
-	w = json_object_get(first, "width");
-	h = json_object_get(first, "height");
-
-	if (!json_is_integer(x) || !json_is_integer(y) ||
-	    !json_is_integer(w) || !json_is_integer(h))
-		die("invalid collide object in tile description\n");
-
-	printf("%d|%d|%d|%d|%d\n",
-	    (int)json_integer_value(id),
-	    (int)json_integer_value(x),
-	    (int)json_integer_value(y),
-	    (int)json_integer_value(w),
-	    (int)json_integer_value(h));
-}
-
-static void
-write_tiledefs(const json_t *tiles)
-{
-	size_t index;
-	json_t *object;
-
-	if (!json_is_array(tiles))
-		return;
-
-	puts("tiledefs");
-
-	json_array_foreach(tiles, index, object) {
-		if (!json_is_object(object))
-			die("tile is not an object\n");
-
-		write_tiledef(object);
-	}
-}
-
-static void
-write_animations(const json_t *tiles)
-{
-	size_t index;
-	json_t *object;
-
-	if (!json_is_array(tiles))
-		return;
-
-	puts("animations");
-
-	json_array_foreach(tiles, index, object) {
-		if (!json_is_object(object))
-			die("tile is not an object\n");
-
-		write_animation(object);
-	}
-}
-
-int
-main(int argc, char *argv[])
-{
-	(void)argc;
-	(void)argv;
-
-	json_t *document;
-	json_error_t error;
-
-	document = json_loadf(stdin, 0, &error);
-
-	if (!document)
-		die("%d:%d: %s\n", error.line, error.column, error.text);
-	if (!json_is_object(document))
-		die("root value isn't an object\n");
-
-	write_dimensions(document);
-	write_image(document);
-	write_tiledefs(json_object_get(document, "tiles"));
-	write_animations(json_object_get(document, "tiles"));
-
-	json_decref(document);
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/tools/tileset/mlk-tileset.c	Sun Oct 09 13:51:03 2022 +0200
@@ -0,0 +1,204 @@
+/*
+ * main.c -- convert tiled tilesets JSON files into custom files
+ *
+ * Copyright (c) 2020-2022 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <assert.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <jansson.h>
+
+static void
+die(const char *fmt, ...)
+{
+	assert(fmt);
+
+	va_list ap;
+
+	va_start(ap, fmt);
+	vfprintf(stderr, fmt, ap);
+	va_end(ap);
+	exit(1);
+}
+
+static void
+write_dimensions(const json_t *document)
+{
+	const json_t *tilewidth = json_object_get(document, "tilewidth");
+	const json_t *tileheight = json_object_get(document, "tileheight");
+
+	if (!json_is_integer(tilewidth))
+		die("invalid 'tilewidth' property\n");
+	if (!json_is_integer(tileheight))
+		die("invalid 'tileheight' property\n");
+
+	printf("tilewidth|%u\n", (unsigned int)json_integer_value(tilewidth));
+	printf("tileheight|%u\n", (unsigned int)json_integer_value(tileheight));
+}
+
+static void
+write_image(const json_t *document)
+{
+	const json_t *image = json_object_get(document, "image");
+
+	if (!json_is_string(image))
+		die("invalid 'image' property\n");
+
+	printf("image|%s\n", json_string_value(image));
+}
+
+static const json_t *
+find_property_value(const json_t *array, const char *prop)
+{
+	const json_t *obj;
+	size_t i;
+
+	json_array_foreach(array, i, obj) {
+		const json_t *name = json_object_get(obj, "name");
+
+		if (!name || !json_is_string(name))
+			die("invalid property object\n");
+
+		if (strcmp(json_string_value(name), prop) == 0)
+			return json_object_get(obj, "value");
+	}
+
+	return NULL;
+}
+
+static void
+write_animation(const json_t *tile)
+{
+	const json_t *id = json_object_get(tile, "id");
+	const json_t *properties = json_object_get(tile, "properties");
+	const json_t *file = find_property_value(properties, "animation-file");
+	const json_t *delay = find_property_value(properties, "animation-delay");
+
+	/* Animations are completely optional. */
+	if (!json_is_array(properties))
+		return;
+
+	if (!json_is_integer(id))
+		die("invalid 'id' property in tile\n");
+
+	if (json_is_string(file)) {
+		printf("%d|%s|", (int)json_integer_value(id), json_string_value(file));
+
+		if (json_is_integer(delay))
+			printf("%d\n", (int)json_integer_value(delay));
+		else
+			printf("10\n");
+	}
+}
+
+static void
+write_tiledef(const json_t *tile)
+{
+	const json_t *id = json_object_get(tile, "id");
+	const json_t *objectgroup = json_object_get(tile, "objectgroup");
+	const json_t *objects = json_object_get(objectgroup, "objects");
+	const json_t *first = json_array_get(objects, 0);
+	const json_t *x, *y, *w, *h;
+
+	/* Collisions are optional. */
+	if (!json_is_object(objectgroup))
+		return;
+
+	if (!json_is_integer(id))
+		die("invalid 'id' property in tile\n");
+	if (!json_is_array(objects))
+		die("invalid 'objects' property in tile\n");
+
+	x = json_object_get(first, "x");
+	y = json_object_get(first, "y");
+	w = json_object_get(first, "width");
+	h = json_object_get(first, "height");
+
+	if (!json_is_integer(x) || !json_is_integer(y) ||
+	    !json_is_integer(w) || !json_is_integer(h))
+		die("invalid collide object in tile description\n");
+
+	printf("%d|%d|%d|%d|%d\n",
+	    (int)json_integer_value(id),
+	    (int)json_integer_value(x),
+	    (int)json_integer_value(y),
+	    (int)json_integer_value(w),
+	    (int)json_integer_value(h));
+}
+
+static void
+write_tiledefs(const json_t *tiles)
+{
+	size_t index;
+	json_t *object;
+
+	if (!json_is_array(tiles))
+		return;
+
+	puts("tiledefs");
+
+	json_array_foreach(tiles, index, object) {
+		if (!json_is_object(object))
+			die("tile is not an object\n");
+
+		write_tiledef(object);
+	}
+}
+
+static void
+write_animations(const json_t *tiles)
+{
+	size_t index;
+	json_t *object;
+
+	if (!json_is_array(tiles))
+		return;
+
+	puts("animations");
+
+	json_array_foreach(tiles, index, object) {
+		if (!json_is_object(object))
+			die("tile is not an object\n");
+
+		write_animation(object);
+	}
+}
+
+int
+main(int argc, char *argv[])
+{
+	(void)argc;
+	(void)argv;
+
+	json_t *document;
+	json_error_t error;
+
+	document = json_loadf(stdin, 0, &error);
+
+	if (!document)
+		die("%d:%d: %s\n", error.line, error.column, error.text);
+	if (!json_is_object(document))
+		die("root value isn't an object\n");
+
+	write_dimensions(document);
+	write_image(document);
+	write_tiledefs(json_object_get(document, "tiles"));
+	write_animations(json_object_get(document, "tiles"));
+
+	json_decref(document);
+}