changeset 346:323d13f49233

js: add state and game bindings
author David Demelier <markand@malikania.fr>
date Mon, 18 Oct 2021 21:52:46 +0200
parents 56830e59f776
children 0969931b98e4
files src/libmlk-core-js/CMakeLists.txt src/libmlk-core-js/core/js-event.c src/libmlk-core-js/core/js-event.h src/libmlk-core-js/core/js-game.c src/libmlk-core-js/core/js-game.h src/libmlk-core-js/core/js-state.c src/libmlk-core-js/core/js-state.h src/mlk-run/main.c
diffstat 8 files changed, 460 insertions(+), 7 deletions(-) [+]
line wrap: on
line diff
--- a/src/libmlk-core-js/CMakeLists.txt	Mon Oct 18 13:08:28 2021 +0200
+++ b/src/libmlk-core-js/CMakeLists.txt	Mon Oct 18 21:52:46 2021 +0200
@@ -30,6 +30,8 @@
 	${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
@@ -38,6 +40,8 @@
 	${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-window.c
--- a/src/libmlk-core-js/core/js-event.c	Mon Oct 18 13:08:28 2021 +0200
+++ b/src/libmlk-core-js/core/js-event.c	Mon Oct 18 21:52:46 2021 +0200
@@ -78,12 +78,8 @@
 
 	if (!event_poll(&ev))
 		duk_push_null(ctx);
-	else {
-		duk_push_object(ctx);
-		duk_push_int(ctx, ev.type);
-		duk_put_prop_string(ctx, -2, "type");
-		push[ev.type](ctx, &ev);
-	}
+	else
+		js_event_push(ctx, &ev);
 
 	return 1;
 }
@@ -289,3 +285,12 @@
 	duk_put_prop_string(ctx, -2, "Event");
 	duk_pop(ctx);
 }
+
+void
+js_event_push(duk_context *ctx, const union event *ev)
+{
+	duk_push_object(ctx);
+	duk_push_int(ctx, ev->type);
+	duk_put_prop_string(ctx, -2, "type");
+	push[ev->type](ctx, ev);
+}
--- a/src/libmlk-core-js/core/js-event.h	Mon Oct 18 13:08:28 2021 +0200
+++ b/src/libmlk-core-js/core/js-event.h	Mon Oct 18 21:52:46 2021 +0200
@@ -21,7 +21,12 @@
 
 #include <duktape.h>
 
+union event;
+
 void
 js_event_bind(duk_context *);
 
+void
+js_event_push(duk_context *, const union event *);
+
 #endif /* !MLK_CORE_JS_EVENT_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/libmlk-core-js/core/js-game.c	Mon Oct 18 21:52:46 2021 +0200
@@ -0,0 +1,107 @@
+/*
+ * js-game.c -- core gamej binding
+ *
+ * Copyright (c) 2020-2021 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 "js-game.h"
+#include "js-state.h"
+
+#define TABLE DUK_HIDDEN_SYMBOL("Mlk.Game.table")
+
+/*
+ * TODO: determine if it's worth it to add handle, update and draw functions.
+ */
+
+static duk_ret_t
+Game_push(duk_context *ctx)
+{
+	struct state *state = js_state_require(ctx, 0);
+
+	if (game.state == &game.states[GAME_STATE_MAX])
+		duk_error(ctx, DUK_ERR_RANGE_ERROR, "too many states");
+
+	game_push(state);
+
+	/* Store the state to avoid destruction. */
+	duk_push_global_stash(ctx);
+	duk_get_prop_string(ctx, -1, TABLE);
+	duk_dup(ctx, 0);
+	duk_put_prop_index(ctx, -2, duk_get_length(ctx, -2));
+	duk_pop_n(ctx, 2);
+
+	return 0;
+}
+
+static duk_ret_t
+Game_pop(duk_context *ctx)
+{
+	if (game.state == &game.states[0])
+		return 0;
+
+	/* Remove the stored reference. */
+	duk_push_global_stash(ctx);
+	duk_get_prop_string(ctx, -1, TABLE);
+	duk_del_prop_index(ctx, -1, duk_get_length(ctx, -1) - 1);
+	duk_pop_n(ctx, 2);
+
+	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");
+	duk_push_global_stash(ctx);
+	duk_push_array(ctx);
+	duk_put_prop_string(ctx, -2, TABLE);
+	duk_pop(ctx);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/libmlk-core-js/core/js-game.h	Mon Oct 18 21:52:46 2021 +0200
@@ -0,0 +1,27 @@
+/*
+ * js-game.h -- core game binding
+ *
+ * Copyright (c) 2020-2021 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 */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/libmlk-core-js/core/js-state.c	Mon Oct 18 21:52:46 2021 +0200
@@ -0,0 +1,271 @@
+/*
+ * js-state.c -- core state binding
+ *
+ * Copyright (c) 2020-2021 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")
+#define FUNCTIONS DUK_HIDDEN_SYMBOL("Mlk.State.functions")
+
+#define INVOKE(state, function, block)                          \
+do {                                                            \
+        struct statedata *data = state->data;                   \
+                                                                \
+        duk_push_heapptr(data->ctx, data->heapptr);             \
+        duk_get_prop_index(data->ctx, -1, function);            \
+        duk_remove(data->ctx, -2);                              \
+                                                                \
+        if (duk_is_callable(data->ctx, -1)) {                   \
+                block                                           \
+                duk_pop(data->ctx);                             \
+        } else {                                                \
+                duk_pop(data->ctx);                             \
+        }                                                       \
+} while (0)
+
+#define SET(ctx, function)                                      \
+do {                                                            \
+        struct statedata *data = self(ctx);                     \
+                                                                \
+        duk_push_heapptr(ctx, data->heapptr);                   \
+        duk_dup(ctx, 0);                                        \
+        duk_put_prop_index(ctx, -2, function);                  \
+        duk_pop(ctx);                                           \
+                                                                \
+        return 0;                                               \
+} while (0)
+
+enum statefunc {
+	FUNC_START,
+	FUNC_HANDLE,
+	FUNC_UPDATE,
+	FUNC_DRAW,
+	FUNC_SUSPEND,
+	FUNC_RESUME,
+	FUNC_END
+};
+
+struct statedata {
+	duk_context *ctx;
+	void *heapptr;
+	struct state state;
+};
+
+static void
+start(struct state *state)
+{
+	INVOKE(state, FUNC_START, {
+		duk_call(data->ctx, 0);
+	});
+}
+
+static void
+handle(struct state *state, const union event *ev)
+{
+	INVOKE(state, FUNC_HANDLE, {
+		js_event_push(data->ctx, ev);
+		duk_call(data->ctx, 1);
+	});
+}
+
+static void
+update(struct state *state, unsigned int ticks)
+{
+	INVOKE(state, FUNC_UPDATE, {
+		duk_push_uint(data->ctx, ticks);
+		duk_call(data->ctx, 1);
+	});
+}
+
+static void
+draw(struct state *state)
+{
+	INVOKE(state, FUNC_DRAW, {
+		duk_call(data->ctx, 0);
+	});
+}
+
+static void
+suspend(struct state *state)
+{
+	INVOKE(state, FUNC_SUSPEND, {
+		duk_call(data->ctx, 0);
+	});
+}
+
+static void
+resume(struct state *state)
+{
+	INVOKE(state, FUNC_RESUME, {
+		duk_call(data->ctx, 0);
+	});
+}
+
+static void
+end(struct state *state)
+{
+	INVOKE(state, FUNC_END, {
+		duk_call(data->ctx, 0);
+	});
+}
+
+static void
+finish(struct state *state)
+{
+	free(state->data);
+}
+
+static inline struct statedata *
+self(duk_context *ctx)
+{
+	struct statedata *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)
+		duk_error(ctx, DUK_ERR_TYPE_ERROR, "not a State object");
+
+	return data;
+}
+
+static duk_ret_t
+State_setStart(duk_context *ctx)
+{
+	SET(ctx, FUNC_START);
+}
+
+static duk_ret_t
+State_setHandle(duk_context *ctx)
+{
+	SET(ctx, FUNC_HANDLE);
+}
+
+static duk_ret_t
+State_setUpdate(duk_context *ctx)
+{
+	SET(ctx, FUNC_UPDATE);
+}
+
+static duk_ret_t
+State_setDraw(duk_context *ctx)
+{
+	SET(ctx, FUNC_DRAW);
+}
+
+static duk_ret_t
+State_setSuspend(duk_context *ctx)
+{
+	SET(ctx, FUNC_SUSPEND);
+}
+
+static duk_ret_t
+State_setResume(duk_context *ctx)
+{
+	SET(ctx, FUNC_RESUME);
+}
+
+static duk_ret_t
+State_setEnd(duk_context *ctx)
+{
+	SET(ctx, FUNC_END);
+}
+
+static const struct {
+	const char *property;
+	duk_ret_t (*setter)(duk_context *ctx);
+} properties[] = {
+	{ "start",      State_setStart          },
+	{ "handle",     State_setHandle         },
+	{ "update",     State_setUpdate         },
+	{ "draw",       State_setDraw           },
+	{ "suspend",    State_setSuspend        },
+	{ "resume",     State_setResume         },
+	{ "end",        State_setEnd            },
+	{ NULL,         NULL                    }
+};
+
+static duk_ret_t
+State_constructor(duk_context *ctx)
+{
+	struct statedata *data;
+
+	data = alloc_new0(sizeof (*data));
+	data->ctx = ctx;
+	data->state.data = data;
+	data->state.start = start;
+	data->state.handle = handle;
+	data->state.update = update;
+	data->state.draw = draw;
+	data->state.suspend = suspend;
+	data->state.resume = resume;
+	data->state.end = end;
+	data->state.finish = finish;
+
+	duk_push_this(ctx);
+	duk_push_pointer(ctx, data);
+	duk_put_prop_string(ctx, -2, SIGNATURE);
+	duk_push_array(ctx);
+	data->heapptr = duk_get_heapptr(ctx, -1);
+	duk_put_prop_string(ctx, -2, FUNCTIONS);
+
+	/* Push every properties. */
+	for (size_t i = 0; properties[i].property; ++i) {
+		duk_push_string(ctx, properties[i].property);
+		duk_push_c_function(ctx, properties[i].setter, 1);
+		duk_def_prop(ctx, -3, DUK_DEFPROP_HAVE_SETTER);
+	}
+
+	duk_pop(ctx);
+
+	return 0;
+}
+
+void
+js_state_bind(duk_context *ctx)
+{
+	assert(ctx);
+
+	duk_push_c_function(ctx, State_constructor, 1);
+	duk_put_global_string(ctx, "State");
+}
+
+struct state *
+js_state_require(duk_context *ctx, duk_idx_t idx)
+{
+	struct statedata *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)
+		duk_error(ctx, DUK_ERR_TYPE_ERROR, "not a State object");
+
+	return &data->state;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/libmlk-core-js/core/js-state.h	Mon Oct 18 21:52:46 2021 +0200
@@ -0,0 +1,30 @@
+/*
+ * js-state.h -- core state binding
+ *
+ * Copyright (c) 2020-2021 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>
+
+void
+js_state_bind(duk_context *);
+
+struct state *
+js_state_require(duk_context *, duk_idx_t);
+
+#endif /* !MLK_CORE_JS_STATE_H */
--- a/src/mlk-run/main.c	Mon Oct 18 13:08:28 2021 +0200
+++ b/src/mlk-run/main.c	Mon Oct 18 21:52:46 2021 +0200
@@ -31,10 +31,12 @@
 #include <core/js-core.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-sound.h>
 #include <core/js-sprite.h>
+#include <core/js-state.h>
 #include <core/js-texture.h>
 #include <core/js-window.h>
 
@@ -58,12 +60,14 @@
 	js_animation_bind(ctx);
 	js_clock_bind(ctx);
 	js_core_bind(ctx, &vfs);
+	js_event_bind(ctx);
 	js_font_bind(ctx);
-	js_event_bind(ctx);
+	js_game_bind(ctx);
 	js_music_bind(ctx);
 	js_painter_bind(ctx);
 	js_sound_bind(ctx);
 	js_sprite_bind(ctx);
+	js_state_bind(ctx);
 	js_texture_bind(ctx);
 	js_window_bind(ctx);
 }