Mercurial > molko
changeset 645:83781cc87fca
core: rework game stack state mechanism
The current model was fundamentally broken as the state could continue its
execution when calling mlk_game_pop from itself (e.g. in update).
The current model uses a sjlj mechanism with mlk_game_push/pop being disallowed
in special state function like end, finish, suspend.
line wrap: on
line diff
--- a/.clang Sat Dec 23 09:34:04 2023 +0100 +++ b/.clang Sun Feb 04 15:24:00 2024 +0100 @@ -1,6 +1,7 @@ -I/usr/include/SDL2 -Iextern/libdt -Iextern/libsqlite +-Iextern/libduktape -Ilibmlk-core -Ilibmlk-example -Ilibmlk-rpg
--- a/examples/CMakeLists.txt Sat Dec 23 09:34:04 2023 +0100 +++ b/examples/CMakeLists.txt Sun Feb 04 15:24:00 2024 +0100 @@ -34,6 +34,7 @@ example-message example-notify example-sprite + example-states example-tileset example-trace example-ui
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/example-states/CMakeLists.txt Sun Feb 04 15:24:00 2024 +0100 @@ -0,0 +1,47 @@ +# +# CMakeLists.txt -- CMake build system for Molko's Engine +# +# Copyright (c) 2020-2023 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(example-states) + +set( + SOURCES + ${example-states_SOURCE_DIR}/example-states.c + ${example-states_SOURCE_DIR}/state-menu.c + ${example-states_SOURCE_DIR}/state-menu.h + ${example-states_SOURCE_DIR}/state-play.c + ${example-states_SOURCE_DIR}/state-play.h + ${example-states_SOURCE_DIR}/state-splash.c + ${example-states_SOURCE_DIR}/state-splash.h +) + +set( + ASSETS + ${example-states_SOURCE_DIR}/assets/fonts/breamcatcher.otf + ${example-states_SOURCE_DIR}/assets/fonts/zenda.ttf + ${example-states_SOURCE_DIR}/assets/images/dvd.png +) + +mlk_executable( + NAME example-states + FOLDER examples + LIBRARIES libmlk-example + SOURCES ${ASSETS} ${SOURCES} + ASSETS ${ASSETS} +) + +source_group(TREE ${example-states_SOURCE_DIR} FILES ${ASSETS} ${SOURCES})
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/example-states/example-states.c Sun Feb 04 15:24:00 2024 +0100 @@ -0,0 +1,56 @@ +/* + * example-states.c -- example on how states stack works + * + * Copyright (c) 2020-2023 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 <mlk/core/game.h> +#include <mlk/core/panic.h> + +#include <mlk/example/example.h> + +#include "state-splash.h" + +static void +init(void) +{ + if (mlk_example_init("example-states") < 0) + mlk_panic(); +} + +static void +run(void) +{ + mlk_game_loop(state_splash_new()); +} + +static void +quit(void) +{ + mlk_example_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-states/state-menu.c Sun Feb 04 15:24:00 2024 +0100 @@ -0,0 +1,159 @@ +/* + * state-menu.c -- basic main menu + * + * Copyright (c) 2020-2023 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 <mlk/core/alloc.h> +#include <mlk/core/event.h> +#include <mlk/core/font.h> +#include <mlk/core/game.h> +#include <mlk/core/painter.h> +#include <mlk/core/panic.h> +#include <mlk/core/state.h> +#include <mlk/core/util.h> + +#include <mlk/ui/align.h> +#include <mlk/ui/button.h> +#include <mlk/ui/label.h> + +#include <assets/fonts/breamcatcher.h> + +#include "state-menu.h" +#include "state-play.h" + +#define BG 0xdfededff +#define BUTTON_H (mlk_window.h / 12) +#define BUTTON_W (mlk_window.w / 6) +#define LABEL_FG 0x4d6a94ff +#define LABEL_FONT assets_fonts_breamcatcher +#define LABEL_SIZE 32 +#define LABEL_TEXT "Sample Game" + +#define MENU(self) MLK_CONTAINER_OF(self, struct menu, state) + +struct menu { + struct mlk_font font_header; + struct mlk_label_style label_style_header; + struct mlk_label label_header; + struct mlk_button button_play; + struct mlk_button button_quit; + struct mlk_state state; +}; + +static void +state_menu__start(struct mlk_state *self) +{ + struct menu *menu = MENU(self); + unsigned int w, h; + + /* Top header. */ + if (mlk_font_openmem(&menu->font_header, LABEL_FONT, sizeof (LABEL_FONT), LABEL_SIZE) < 0) + mlk_panic(); + + menu->font_header.style = MLK_FONT_STYLE_ANTIALIASED; + menu->label_style_header.font = &menu->font_header; + menu->label_style_header.color = LABEL_FG; + menu->label_header.text = LABEL_TEXT; + menu->label_header.style = &menu->label_style_header; + mlk_label_query(&menu->label_header, &w, &h); + mlk_align(MLK_ALIGN_TOP, + &menu->label_header.x, + &menu->label_header.y, + w, + h, + 0, + 10, + mlk_window.w, + 0); + + /* Buttons. */ + menu->button_play.text = "Play"; + menu->button_play.w = BUTTON_W; + menu->button_play.h = BUTTON_H; + + menu->button_quit.text = "Quit"; + menu->button_quit.w = BUTTON_W; + menu->button_quit.h = BUTTON_H; + + mlk_align(MLK_ALIGN_CENTER, + &menu->button_play.x, + &menu->button_play.y, + menu->button_play.w, + menu->button_play.h, + 0, + 0, + mlk_window.w, + mlk_window.h + ); + + menu->button_quit.x = menu->button_play.x; + menu->button_quit.y = menu->button_play.y + BUTTON_H + 20; +} + +static void +state_menu__handle(struct mlk_state *self, const union mlk_event *event) +{ + struct menu *menu = MENU(self); + + switch (event->type) { + case MLK_EVENT_QUIT: + mlk_game_quit(); + break; + default: + if (mlk_button_handle(&menu->button_play, event)) + mlk_game_push(state_play_new()); + else if (mlk_button_handle(&menu->button_quit, event)) + mlk_game_quit(); + break; + } +} + +static void +state_menu__update(struct mlk_state *self, unsigned int ticks) +{ + struct menu *menu = MENU(self); + + mlk_button_update(&menu->button_play, ticks); + mlk_button_update(&menu->button_quit, ticks); +} + +static void +state_menu__draw(struct mlk_state *self) +{ + struct menu *menu = MENU(self); + + mlk_painter_set_color(BG); + mlk_painter_clear(); + mlk_label_draw(&menu->label_header); + mlk_button_draw(&menu->button_play); + mlk_button_draw(&menu->button_quit); + mlk_painter_present(); +} + +struct mlk_state * +state_menu_new(void) +{ + struct menu *menu; + + menu = mlk_alloc_new0(1, sizeof (*menu)); + menu->state.name = "menu"; + menu->state.start = state_menu__start; + menu->state.handle = state_menu__handle; + menu->state.update = state_menu__update; + menu->state.draw = state_menu__draw; + + return &menu->state; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/example-states/state-menu.h Sun Feb 04 15:24:00 2024 +0100 @@ -0,0 +1,27 @@ +/* + * state-menu.h -- basic main menu + * + * Copyright (c) 2020-2023 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 STATE_MENU_H +#define STATE_MENU_H + +struct mlk_state; + +struct mlk_state * +state_menu_new(void); + +#endif /* !STATE_MENU_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/example-states/state-play.c Sun Feb 04 15:24:00 2024 +0100 @@ -0,0 +1,179 @@ +/* + * state-play.c -- very funny game + * + * Copyright (c) 2020-2023 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 <mlk/core/alloc.h> +#include <mlk/core/event.h> +#include <mlk/core/game.h> +#include <mlk/core/image.h> +#include <mlk/core/painter.h> +#include <mlk/core/panic.h> +#include <mlk/core/state.h> +#include <mlk/core/texture.h> +#include <mlk/core/util.h> + +#include <mlk/ui/align.h> +#include <mlk/ui/button.h> + +#include <assets/images/dvd.h> + +#include "state-menu.h" +#include "state-play.h" + +#define BG 0x99c2dbff +#define BUTTON_H (mlk_window.h / 20) +#define BUTTON_W (mlk_window.w / 10) +#define LOGO assets_images_dvd +#define LOGO_SPEED 100 + +#define PLAY(self) MLK_CONTAINER_OF(self, struct play, state) + +struct play { + struct mlk_button button_leave; + struct mlk_texture logo; + int x; + int y; + int dx; + int dy; + struct mlk_state state; +}; + +static void +state_play__start(struct mlk_state *self) +{ + struct play *play = PLAY(self); + + if (mlk_image_openmem(&play->logo, LOGO, sizeof (LOGO)) < 0) + mlk_panic(); + + mlk_align(MLK_ALIGN_CENTER, + &play->x, + &play->y, + play->logo.w, + play->logo.h, + 0, + 0, + mlk_window.w, + mlk_window.h + ); + + play->button_leave.text = "menu"; + play->button_leave.w = BUTTON_W; + play->button_leave.h = BUTTON_H; + + mlk_align(MLK_ALIGN_BOTTOM, + &play->button_leave.x, + &play->button_leave.y, + play->button_leave.w, + play->button_leave.h, + 0, + 0, + mlk_window.w, + mlk_window.h - 20 + ); + + play->dx = play->dy = 1; +} + +static void +state_play__handle(struct mlk_state *self, const union mlk_event *event) +{ + struct play *play = PLAY(self); + + if (mlk_button_handle(&play->button_leave, event)) + mlk_game_pop(); + + switch (event->type) { + case MLK_EVENT_QUIT: + mlk_game_quit(); + break; + case MLK_EVENT_KEYUP: + switch (event->key.key) { + case MLK_KEY_ESCAPE: + mlk_game_pop(); + break; + default: + break; + } + break; + default: + break; + } +} + +static void +state_play__update(struct mlk_state *self, unsigned int ticks) +{ + struct play *play = PLAY(self); + + play->x += play->dx * (LOGO_SPEED * ticks / 1000); + play->y += play->dy * (LOGO_SPEED * ticks / 1000); + + if (play->x < 0) { + play->dx = 1; + play->x = 0; + } else if (play->x + play->logo.w >= mlk_window.w) { + play->dx = -1; + play->x = mlk_window.w - play->logo.w; + } + + if (play->y < 0) { + play->dy = 1; + play->y = 0; + } else if (play->y + play->logo.h >= mlk_window.h) { + play->dy = -1; + play->y = mlk_window.h - play->logo.h; + } + + mlk_button_update(&play->button_leave, ticks); +} + +static void +state_play__draw(struct mlk_state *self) +{ + struct play *play = PLAY(self); + + mlk_painter_set_color(BG); + mlk_painter_clear(); + mlk_texture_draw(&play->logo, play->x, play->y); + mlk_button_draw(&play->button_leave); + mlk_painter_present(); +} + +static void +state_play__finish(struct mlk_state *self) +{ + struct play *play = PLAY(self); + + mlk_texture_finish(&play->logo); +} + +struct mlk_state * +state_play_new(void) +{ + struct play *play; + + play = mlk_alloc_new0(1, sizeof (*play)); + play->state.name = "play"; + play->state.start = state_play__start; + play->state.handle = state_play__handle; + play->state.update = state_play__update; + play->state.draw = state_play__draw; + play->state.finish = state_play__finish; + + return &play->state; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/example-states/state-play.h Sun Feb 04 15:24:00 2024 +0100 @@ -0,0 +1,27 @@ +/* + * state-play.h -- very funny game + * + * Copyright (c) 2020-2023 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 STATE_PLAY_H +#define STATE_PLAY_H + +struct mlk_state; + +struct mlk_state * +state_play_new(void); + +#endif /* !STATE_PLAY_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/example-states/state-splash.c Sun Feb 04 15:24:00 2024 +0100 @@ -0,0 +1,123 @@ +/* + * state-splash.c -- minimal splash screen + * + * Copyright (c) 2020-2023 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 <mlk/core/alloc.h> +#include <mlk/core/font.h> +#include <mlk/core/game.h> +#include <mlk/core/painter.h> +#include <mlk/core/panic.h> +#include <mlk/core/state.h> +#include <mlk/core/texture.h> +#include <mlk/core/util.h> +#include <mlk/core/window.h> + +#include <mlk/ui/align.h> + +#include <assets/fonts/zenda.h> + +#include "state-menu.h" +#include "state-splash.h" + +#define TITLE "malikania" +#define SIZE 80 +#define BG 0xf4f4f4ff +#define FG 0x29366fff +#define DELAY 1000 + +#define SPLASH(self) MLK_CONTAINER_OF(self, struct splash, state) + +struct splash { + unsigned int elapsed; + struct mlk_texture texture; + int x; + int y; + struct mlk_state state; +}; + +static void +state_splash__start(struct mlk_state *self) +{ + struct splash *splash = SPLASH(self); + struct mlk_font font; + + if (mlk_font_openmem(&font, assets_fonts_zenda, sizeof (assets_fonts_zenda), SIZE) < 0) + mlk_panic(); + + font.style = MLK_FONT_STYLE_ANTIALIASED; + + if (mlk_font_render(&font, &splash->texture, TITLE, FG) < 0) + mlk_panic(); + + mlk_align(MLK_ALIGN_CENTER, + &splash->x, + &splash->y, + splash->texture.w, + splash->texture.h, + 0, + 0, + mlk_window.w, + mlk_window.h + ); + + mlk_font_finish(&font); +} + +static void +state_splash__update(struct mlk_state *self, unsigned int ticks) +{ + struct splash *splash = SPLASH(self); + + splash->elapsed += ticks; + + if (splash->elapsed >= DELAY) + mlk_game_push(state_menu_new()); +} + +static void +state_splash__draw(struct mlk_state *self) +{ + struct splash *splash = SPLASH(self); + + mlk_painter_set_color(BG); + mlk_painter_clear(); + mlk_texture_draw(&splash->texture, splash->x, splash->y); + mlk_painter_present(); +} + +static void +state_splash__finish(struct mlk_state *self) +{ + struct splash *splash = SPLASH(self); + + mlk_texture_finish(&splash->texture); +} + +struct mlk_state * +state_splash_new(void) +{ + struct splash *splash; + + splash = mlk_alloc_new0(1, sizeof (*splash)); + splash->state.name = "splash"; + splash->state.start = state_splash__start; + splash->state.update = state_splash__update; + splash->state.draw = state_splash__draw; + splash->state.finish = state_splash__finish; + + return &splash->state; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/example-states/state-splash.h Sun Feb 04 15:24:00 2024 +0100 @@ -0,0 +1,27 @@ +/* + * state-splash.h -- minimal splash screen + * + * Copyright (c) 2020-2023 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 STATE_SPLASH_H +#define STATE_SPLASH_H + +struct mlk_state; + +struct mlk_state * +state_splash_new(void); + +#endif /* !STATE_SPLASH_H */
--- a/libmlk-core/CMakeLists.txt Sat Dec 23 09:34:04 2023 +0100 +++ b/libmlk-core/CMakeLists.txt Sun Feb 04 15:24:00 2024 +0100 @@ -161,6 +161,8 @@ ${libmlk-core_SOURCE_DIR}/mlk/core/js/js-event.h ${libmlk-core_SOURCE_DIR}/mlk/core/js/js-font.c ${libmlk-core_SOURCE_DIR}/mlk/core/js/js-font.h + ${libmlk-core_SOURCE_DIR}/mlk/core/js/js-game.c + ${libmlk-core_SOURCE_DIR}/mlk/core/js/js-game.h ${libmlk-core_SOURCE_DIR}/mlk/core/js/js-music.c ${libmlk-core_SOURCE_DIR}/mlk/core/js/js-music.h ${libmlk-core_SOURCE_DIR}/mlk/core/js/js-painter.c @@ -169,6 +171,8 @@ ${libmlk-core_SOURCE_DIR}/mlk/core/js/js-sound.h ${libmlk-core_SOURCE_DIR}/mlk/core/js/js-sprite.c ${libmlk-core_SOURCE_DIR}/mlk/core/js/js-sprite.h + ${libmlk-core_SOURCE_DIR}/mlk/core/js/js-state.c + ${libmlk-core_SOURCE_DIR}/mlk/core/js/js-state.h ${libmlk-core_SOURCE_DIR}/mlk/core/js/js-texture.c ${libmlk-core_SOURCE_DIR}/mlk/core/js/js-texture.h ${libmlk-core_SOURCE_DIR}/mlk/core/js/js-util.c
--- a/libmlk-core/mlk/core/game.c Sat Dec 23 09:34:04 2023 +0100 +++ b/libmlk-core/mlk/core/game.c Sun Feb 04 15:24:00 2024 +0100 @@ -18,6 +18,7 @@ #include <assert.h> #include <string.h> +#include <setjmp.h> #include "clock.h" #include "core_p.h" @@ -28,7 +29,16 @@ #include "util.h" #include "window.h" +enum { + JMP_OK = 0, + JMP_PUSH, + JMP_POP +}; + static struct mlk_state *states[8]; +static jmp_buf game_jmp_buf; +static int game_jmp_allowed; +static int game_run = 1; struct mlk_game mlk_game = { .states = states, @@ -42,68 +52,33 @@ mlk_game.states[i] = NULL; } -int +_Noreturn int mlk_game_push(struct mlk_state *state) { assert(state); - - if (!mlk_game.state) { - mlk_game.state = &mlk_game.states[0]; - mlk_state_start(*mlk_game.state = state); - return 0; - } + assert(game_jmp_allowed); if (mlk_game.state == &mlk_game.states[mlk_game.statesz - 1]) - return mlk_errf(_("no space in game states stack")); + mlk_errf(_("no space in game states stack")); + + mlk_game.state[1] = state; + longjmp(game_jmp_buf, JMP_PUSH); +} - mlk_state_suspend(*mlk_game.state); - mlk_state_start(*(++mlk_game.state) = state); +_Noreturn void +mlk_game_pop(void) +{ + assert(game_jmp_allowed); - return 0; + longjmp(game_jmp_buf, JMP_POP); } void -mlk_game_pop(void) +mlk_game_loop(struct mlk_state *state) { - if (!mlk_game.state) - return; - - mlk_state_end(*mlk_game.state); - mlk_state_finish(*mlk_game.state); - - if (mlk_game.state == mlk_game.states) - mlk_game.state = NULL; - else - mlk_state_resume(*--mlk_game.state); -} - -void -mlk_game_handle(const union mlk_event *ev) -{ - assert(ev); + assert(state); - if (mlk_game.state && !(mlk_game.inhibit & MLK_GAME_INHIBIT_INPUT)) - mlk_state_handle(*mlk_game.state, ev); -} - -void -mlk_game_update(unsigned int ticks) -{ - if (mlk_game.state && !(mlk_game.inhibit & MLK_GAME_INHIBIT_UPDATE)) - mlk_state_update(*mlk_game.state, ticks); -} - -void -mlk_game_draw(void) -{ - if (mlk_game.state && !(mlk_game.inhibit & MLK_GAME_INHIBIT_DRAW)) - mlk_state_draw(*mlk_game.state); -} - -void -mlk_game_loop(void) -{ - struct mlk_clock clock = {0}; + struct mlk_clock clock = {}; unsigned int elapsed = 0; unsigned int frametime; @@ -113,36 +88,87 @@ /* Assuming 60.0 FPS. */ frametime = 1000.0 / 60.0; - while (mlk_game.state) { - mlk_clock_start(&clock); + game_jmp_allowed = 1; + + while (game_run) { + switch (setjmp(game_jmp_buf)) { + case JMP_OK: + /* Initial entrypoint. */ + if (!mlk_game.state) { + mlk_game.states[0] = state; + mlk_game.state = &mlk_game.states[0]; + mlk_state_start(state); + } + + mlk_clock_start(&clock); - for (union mlk_event ev; mlk_event_poll(&ev); ) - mlk_game_handle(&ev); + for (union mlk_event ev; mlk_event_poll(&ev); ) { + if (mlk_game.state && !(mlk_game.inhibit & MLK_GAME_INHIBIT_INPUT)) + mlk_state_handle(*mlk_game.state, ev); + } - mlk_game_update(elapsed); - mlk_game_draw(); + if (mlk_game.state && !(mlk_game.inhibit & MLK_GAME_INHIBIT_UPDATE)) + mlk_state_update(*mlk_game.state, ticks); + if (mlk_game.state && !(mlk_game.inhibit & MLK_GAME_INHIBIT_DRAW)) + mlk_state_draw(*mlk_game.state); + + /* + * If vsync is enabled, it should have wait, otherwise + * sleep a little to save CPU cycles. + */ + if ((elapsed = mlk_clock_elapsed(&clock)) < frametime) + mlk_util_sleep(frametime - elapsed); + + elapsed = mlk_clock_elapsed(&clock); - /* - * If vsync is enabled, it should have wait, otherwise sleep - * a little to save CPU cycles. - */ - if ((elapsed = mlk_clock_elapsed(&clock)) < frametime) - mlk_util_sleep(frametime - elapsed); + /* + * Cap to frametime if it's too slow because it would + * create unexpected results otherwise. + */ + if (elapsed > frametime) + elapsed = frametime; + break; + case JMP_PUSH: + /* + * We have pushed a new state, suspend should not modify + * the stack because we would have a leak in the next + * state being overriden without being finalized. + */ + game_jmp_allowed = 0; + mlk_state_suspend(*mlk_game.state); + game_jmp_allowed = 1; - elapsed = mlk_clock_elapsed(&clock); + /* Start next state. */ + mlk_state_start(*++mlk_game.state); + break; + case JMP_POP: + /* + * We need to finalize this state so the end/finish + * functions are not allowed to modify the stack. + */ + game_jmp_allowed = 0; + mlk_state_end(*mlk_game.state); + mlk_state_finish(*mlk_game.state); + game_jmp_allowed = 1; - /* - * Cap to frametime if it's too slow because it would create - * unexpected results otherwise. - */ - if (elapsed > frametime) - elapsed = frametime; + /* Resume previous state. */ + if (mlk_game.state == mlk_game.states) + mlk_game.state = NULL; + else + mlk_state_resume(*--mlk_game.state); + break; + default: + break; + } } } void mlk_game_quit(void) { + game_jmp_allowed = 0; + game_run = 0; + for (size_t i = 0; i < mlk_game.statesz; ++i) { if (mlk_game.states[i]) mlk_state_finish(mlk_game.states[i]);
--- a/libmlk-core/mlk/core/game.h Sat Dec 23 09:34:04 2023 +0100 +++ b/libmlk-core/mlk/core/game.h Sun Feb 04 15:24:00 2024 +0100 @@ -108,58 +108,34 @@ mlk_game_init(void); /** - * Try to append a new state into the game loop at the end unless the array is - * full. + * Append the state into the game stack and switch to it, suspending current + * state. * - * The state is inserted as-is and ownership is left to the caller. + * The function takes ownership of the state and will be finalized later. * * \pre state != NULL - * \param state - * \return 0 on success or -1 on error + * \param state the state to switch */ -int +_Noreturn void mlk_game_push(struct mlk_state *state); /** * Pop the current state if any and resume the previous one. */ -void +_Noreturn void mlk_game_pop(void); /** - * Call the current state's mlk_state::handle function unless it is inhibited - * or NULL. - * - * \pre event != NULL - * \param event the event - */ -void -mlk_game_handle(const union mlk_event *event); - -/** - * Call the current state's mlk_state::update function unless it is inhibited - * or NULL. - * - * \param ticks frame ticks - */ -void -mlk_game_update(unsigned int ticks); - -/** - * Call the current state's mlk_state::draw function unless it is inhibited - * or NULL. - */ -void -mlk_game_draw(void); - -/** * Enter a game loop until there is no more states. * * The current implementation will perform a loop capped to a 60 FPS rate and * update the states with the appropriate number of ticks. + * + * \pre state != NULL + * \param state the first state to run */ void -mlk_game_loop(void); +mlk_game_loop(struct mlk_state *state); /** * Request the game loop to stop by removing all states.
--- a/libmlk-core/mlk/core/js/js-event.c Sat Dec 23 09:34:04 2023 +0100 +++ b/libmlk-core/mlk/core/js/js-event.c Sun Feb 04 15:24:00 2024 +0100 @@ -26,8 +26,37 @@ #include "js.h" static duk_ret_t -push(duk_context *ctx, const union mlk_event *ev) +mlk_js_event_poll(duk_context *ctx) { + union mlk_event ev; + + if (!mlk_event_poll(&ev)) + return 0; + + return mlk_js_event_push(ctx, &ev); +} + +static const duk_number_list_entry types[] = { + { "CLICK_DOWN", MLK_EVENT_CLICKDOWN }, + { "CLICK_UP", MLK_EVENT_CLICKUP }, + { "KEY_DOWN", MLK_EVENT_KEYDOWN }, + { "KEY_UP", MLK_EVENT_KEYUP }, + { "MOUSE", MLK_EVENT_MOUSE }, + { "QUIT", MLK_EVENT_QUIT }, + { NULL, 0 } +}; + +static const duk_function_list_entry functions[] = { + { "poll", mlk_js_event_poll, 0 }, + { NULL, NULL, 0 } +}; + +duk_ret_t +mlk_js_event_push(duk_context *ctx, const union mlk_event *ev) +{ + assert(ctx); + assert(ev); + duk_push_object(ctx); duk_push_int(ctx, ev->type); duk_put_prop_string(ctx, -2, "type"); @@ -62,32 +91,6 @@ return 1; } -static duk_ret_t -mlk_js_event_poll(duk_context *ctx) -{ - union mlk_event ev; - - if (!mlk_event_poll(&ev)) - return 0; - - return push(ctx, &ev); -} - -static const duk_number_list_entry types[] = { - { "CLICK_DOWN", MLK_EVENT_CLICKDOWN }, - { "CLICK_UP", MLK_EVENT_CLICKUP }, - { "KEY_DOWN", MLK_EVENT_KEYDOWN }, - { "KEY_UP", MLK_EVENT_KEYUP }, - { "MOUSE", MLK_EVENT_MOUSE }, - { "QUIT", MLK_EVENT_QUIT }, - { NULL, 0 } -}; - -static const duk_function_list_entry functions[] = { - { "poll", mlk_js_event_poll, 0 }, - { NULL, NULL, 0 } -}; - void mlk_js_event_load(duk_context *ctx) {
--- a/libmlk-core/mlk/core/js/js-event.h Sat Dec 23 09:34:04 2023 +0100 +++ b/libmlk-core/mlk/core/js/js-event.h Sun Feb 04 15:24:00 2024 +0100 @@ -21,10 +21,15 @@ #include <duktape.h> +union mlk_event; + #if defined(__cplusplus) extern "C" { #endif +duk_ret_t +mlk_js_event_push(duk_context *ctx, const union mlk_event *ev); + void mlk_js_event_load(duk_context *ctx);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libmlk-core/mlk/core/js/js-game.c Sun Feb 04 15:24:00 2024 +0100 @@ -0,0 +1,143 @@ +/* + * js-game.c -- main game object (Javascript bindings) + * + * Copyright (c) 2020-2023 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 <mlk/core/err.h> +#include <mlk/core/game.h> +#include <mlk/core/state.h> + +#include "js-game.h" +#include "js-state.h" + +#define REFS DUK_HIDDEN_SYMBOL("mlk::game::states") + +static duk_ret_t +mlk_js_game_push(duk_context *ctx) +{ + struct mlk_state *st; + + st = mlk_js_state_require(ctx, 0); + + if (mlk_game_push(st) < 0) + duk_error(ctx, DUK_ERR_ERROR, "%s", mlk_err()); + + /* Keep a reference to this object, otherwise it would be collected. */ + duk_push_global_stash(ctx); + duk_get_prop_string(ctx, -1, REFS); + 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 +mlk_js_game_pop(duk_context *ctx) +{ + (void)ctx; + + duk_size_t len; + + mlk_game_pop(); + + /* Remove reference of previous state. */ + duk_push_global_stash(ctx); + duk_get_prop_string(ctx, -1, REFS); + + if ((len = duk_get_length(ctx, -1))) + duk_del_prop_index(ctx, -1, len - 1); + + duk_pop_n(ctx, 2); + + return 0; +} + +#if 0 +static duk_ret_t +mlk_js_game_handle(duk_context *ctx) +{ +} + +static duk_ret_t +mlk_js_game_update(duk_context *ctx) +{ + mlk_game_update(duk_require_uint(ctx, 0)); + + return 0; +} + +static duk_ret_t +mlk_js_game_draw(duk_context *ctx) +{ + (void)ctx; + + mlk_game_draw(); + + return 0; +} +#endif + +static duk_ret_t +mlk_js_game_loop(duk_context *ctx) +{ + (void)ctx; + + mlk_game_loop(NULL); + + return 0; +} + +static duk_ret_t +mlk_js_game_quit(duk_context *ctx) +{ + (void)ctx; + + mlk_game_quit(); + + return 0; +} + +static const duk_function_list_entry functions[] = { + { "push", mlk_js_game_push, 1 }, + { "pop", mlk_js_game_pop, 0 }, +#if 0 + { "update", mlk_js_game_update, 1 }, + { "draw", mlk_js_game_draw, 0 }, +#endif + { "loop", mlk_js_game_loop, 0 }, + { "quit", mlk_js_game_quit, 0 }, + { NULL, NULL, -1 }, +}; + +void +mlk_js_game_load(duk_context *ctx) +{ + assert(ctx); + + duk_push_global_object(ctx); + duk_get_prop_string(ctx, -1, "Mlk"); + duk_push_object(ctx); + duk_put_function_list(ctx, -1, functions); + duk_put_prop_string(ctx, -2, "Game"); + duk_pop(ctx); + duk_push_global_stash(ctx); + duk_push_array(ctx); + duk_put_prop_string(ctx, -2, REFS); + duk_pop(ctx); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libmlk-core/mlk/core/js/js-game.h Sun Feb 04 15:24:00 2024 +0100 @@ -0,0 +1,30 @@ +/* + * js-game.h -- main game object (Javascript bindings) + * + * Copyright (c) 2020-2023 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 <duktape.h> + +#if defined(__cplusplus) +extern "C" { +#endif + +void +mlk_js_game_load(duk_context *ctx); + +#if defined(__cplusplus) +} +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libmlk-core/mlk/core/js/js-state.c Sun Feb 04 15:24:00 2024 +0100 @@ -0,0 +1,255 @@ +/* + * js-state.c -- abstract game loop state (Javascript bindings) + * + * Copyright (c) 2020-2023 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 <mlk/core/alloc.h> +#include <mlk/core/panic.h> +#include <mlk/core/state.h> +#include <mlk/core/util.h> + +#include "js-event.h" +#include "js-state.h" + +#define SYMBOL DUK_HIDDEN_SYMBOL("mlk::state") + +/* + * Cache virtual functions inside the object structure for performance reasons + * to avoid permanent lookup. + */ +#define SET(obj, name) \ +do { \ + duk_push_this(obj->ctx); \ + duk_get_prototype(obj->ctx, -1); \ + duk_get_prop_string(obj->ctx, -1, #name); \ + \ + if (duk_is_callable(obj->ctx, -1)) { \ + obj->ref_##name = duk_get_heapptr(obj->ctx, -1); \ + obj->state.name = mlk_js_state__##name; \ + } \ + \ + duk_pop_n(obj->ctx, 3); \ +} while (0); + +struct object { + duk_context *ctx; + void *ref_this; + void *ref_start; + void *ref_handle; + void *ref_update; + void *ref_draw; + void *ref_suspend; + void *ref_resume; + void *ref_end; + struct mlk_state state; +}; + +#if 0 +static struct object * +mlk_js_state__this(duk_context *ctx) +{ + struct object *obj; + + duk_push_this(ctx); + duk_get_prop_string(ctx, -1, SYMBOL); + obj = duk_to_pointer(ctx, -1); + duk_pop_n(ctx, 2); + + if (!obj) + duk_error(ctx, DUK_ERR_TYPE_ERROR, "Not a State object"); + + return obj; +} +#endif + +static void +mlk_js_state__start(struct mlk_state *self) +{ + struct object *obj = MLK_CONTAINER_OF(self, struct object, state); + + duk_push_heapptr(obj->ctx, obj->ref_start); + duk_push_heapptr(obj->ctx, obj->ref_this); + + if (duk_pcall_method(obj->ctx, 0) != 0) + mlk_panicf("%s", duk_to_string(obj->ctx, -1)); + + duk_pop(obj->ctx); +} + +static void +mlk_js_state__handle(struct mlk_state *self, const union mlk_event *event) +{ + struct object *obj = MLK_CONTAINER_OF(self, struct object, state); + + duk_push_heapptr(obj->ctx, obj->ref_handle); + duk_push_heapptr(obj->ctx, obj->ref_this); + mlk_js_event_push(obj->ctx, event); + + if (duk_pcall_method(obj->ctx, 1) != 0) + mlk_panicf("%s", duk_to_string(obj->ctx, -1)); + + duk_pop(obj->ctx); +} + +static void +mlk_js_state__update(struct mlk_state *self, unsigned int ticks) +{ + struct object *obj = MLK_CONTAINER_OF(self, struct object, state); + + duk_push_heapptr(obj->ctx, obj->ref_update); + duk_push_heapptr(obj->ctx, obj->ref_this); + duk_push_uint(obj->ctx, ticks); + + if (duk_pcall_method(obj->ctx, 1) != 0) + mlk_panicf("%s", duk_to_string(obj->ctx, -1)); + + duk_pop(obj->ctx); +} + +static void +mlk_js_state__draw(struct mlk_state *self) +{ + struct object *obj = MLK_CONTAINER_OF(self, struct object, state); + + duk_push_heapptr(obj->ctx, obj->ref_draw); + duk_push_heapptr(obj->ctx, obj->ref_this); + + if (duk_pcall_method(obj->ctx, 0) != 0) + mlk_panicf("%s", duk_to_string(obj->ctx, -1)); + + duk_pop(obj->ctx); +} + +static void +mlk_js_state__suspend(struct mlk_state *self) +{ + struct object *obj = MLK_CONTAINER_OF(self, struct object, state); + + duk_push_heapptr(obj->ctx, obj->ref_suspend); + duk_push_heapptr(obj->ctx, obj->ref_this); + + if (duk_pcall_method(obj->ctx, 0) != 0) + mlk_panicf("%s", duk_to_string(obj->ctx, -1)); + + duk_pop(obj->ctx); +} + +static void +mlk_js_state__resume(struct mlk_state *self) +{ + struct object *obj = MLK_CONTAINER_OF(self, struct object, state); + + if (!obj->ref_resume) + return; + + duk_push_heapptr(obj->ctx, obj->ref_resume); + duk_push_heapptr(obj->ctx, obj->ref_this); + + if (duk_pcall_method(obj->ctx, 0) != 0) + mlk_panicf("%s", duk_to_string(obj->ctx, -1)); + + duk_pop(obj->ctx); +} + +static void +mlk_js_state__end(struct mlk_state *self) +{ + struct object *obj = MLK_CONTAINER_OF(self, struct object, state); + + duk_push_heapptr(obj->ctx, obj->ref_end); + duk_push_heapptr(obj->ctx, obj->ref_this); + + if (duk_pcall_method(obj->ctx, 0) != 0) + mlk_panicf("%s", duk_to_string(obj->ctx, -1)); + + duk_pop(obj->ctx); +} + +static duk_ret_t +mlk_js_state__finish(duk_context *ctx) +{ + struct object *obj; + + printf("FINALIZING STATE\n"); + duk_get_prop_string(ctx, 0, SYMBOL); + obj = duk_to_pointer(ctx, -1); + mlk_alloc_free(obj); + duk_pop(ctx); + duk_del_prop_string(ctx, 0, SYMBOL); + + return 0; +} + +static duk_ret_t +mlk_js_state__new(duk_context *ctx) +{ + struct object *obj; + + obj = mlk_alloc_new(1, sizeof (*obj)); + obj->ctx = ctx; + obj->state.name = duk_require_string(ctx, 0); + + /* Cache virtual functions for performance reasons. */ + SET(obj, start); + SET(obj, handle); + SET(obj, update); + SET(obj, draw); + SET(obj, suspend); + SET(obj, resume); + SET(obj, end); + + duk_push_this(ctx); + obj->ref_this = duk_get_heapptr(ctx, -1); + printf("%p\n", obj->ref_this); + duk_push_pointer(ctx, obj); + duk_put_prop_string(ctx, -2, SYMBOL); + duk_push_c_function(ctx, mlk_js_state__finish, 1); + duk_set_finalizer(ctx, -2); + duk_pop(ctx); + + return 0; +} + +struct mlk_state * +mlk_js_state_require(duk_context *ctx, duk_idx_t index) +{ + struct object *obj; + + duk_get_prop_string(ctx, index, SYMBOL); + obj = duk_to_pointer(ctx, -1); + duk_pop(ctx); + + if (!obj) + duk_error(ctx, DUK_ERR_TYPE_ERROR, "State expected on argument #%u", index); + + return &obj->state; +} + +void +mlk_js_state_load(duk_context *ctx) +{ + assert(ctx); + + duk_push_global_object(ctx); + duk_get_prop_string(ctx, -1, "Mlk"); + duk_push_c_function(ctx, mlk_js_state__new, 1); + duk_push_object(ctx); + duk_put_prop_string(ctx, -2, "prototype"); + duk_put_prop_string(ctx, -2, "State"); + duk_pop(ctx); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libmlk-core/mlk/core/js/js-state.h Sun Feb 04 15:24:00 2024 +0100 @@ -0,0 +1,35 @@ +/* + * js-state.h -- abstract game loop state (Javascript bindings) + * + * Copyright (c) 2020-2023 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 <duktape.h> + +struct mlk_state; + +#if defined(__cplusplus) +extern "C" { +#endif + +void +mlk_js_state_load(duk_context *ctx); + +struct mlk_state * +mlk_js_state_require(duk_context *ctx, duk_idx_t index); + +#if defined(__cplusplus) +} +#endif
--- a/libmlk-core/mlk/core/js/js.c Sat Dec 23 09:34:04 2023 +0100 +++ b/libmlk-core/mlk/core/js/js.c Sun Feb 04 15:24:00 2024 +0100 @@ -31,10 +31,12 @@ #include "js-clock.h" #include "js-event.h" #include "js-font.h" +#include "js-game.h" #include "js-music.h" #include "js-painter.h" #include "js-sound.h" #include "js-sprite.h" +#include "js-state.h" #include "js-texture.h" #include "js-util.h" #include "js-window.h" @@ -151,11 +153,11 @@ /* Create global "Mlk" property. */ duk_push_global_object(js->handle); duk_push_object(js->handle); - duk_put_prop_string(js->handle, -2, "Mlk"); duk_push_c_function(js->handle, mlk_js_print, 1); duk_put_prop_string(js->handle, -2, "print"); duk_push_c_function(js->handle, mlk_js_trace, 1); duk_put_prop_string(js->handle, -2, "trace"); + duk_put_prop_string(js->handle, -2, "Mlk"); duk_pop(js->handle); } @@ -178,10 +180,12 @@ mlk_js_clock_load(js->handle); mlk_js_event_load(js->handle); mlk_js_font_load(js->handle); + mlk_js_game_load(js->handle); mlk_js_music_load(js->handle); mlk_js_painter_load(js->handle); mlk_js_sound_load(js->handle); mlk_js_sprite_load(js->handle); + mlk_js_state_load(js->handle); mlk_js_texture_load(js->handle); mlk_js_util_load(js->handle); mlk_js_window_load(js->handle);
--- a/libmlk-core/mlk/core/state.h Sat Dec 23 09:34:04 2023 +0100 +++ b/libmlk-core/mlk/core/state.h Sun Feb 04 15:24:00 2024 +0100 @@ -44,6 +44,13 @@ void *data; /** + * (read-write, borrowed, optional) + * + * Arbitrary state name for diagnostic purposes. + */ + const char *name; + + /** * (read-write, optional) * * Invoked when the state starts, which is called only one time.