aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorquou <quou@disroot.org>2024-10-12 00:23:57 +1100
committerquou <quou@disroot.org>2024-10-12 00:24:07 +1100
commit7a7628a3feaec81d9a445acbec604baecac3a167 (patch)
tree962d56698071cdf5766083b77fa05607b9e37668
parentc5445eb3032f2081c8cf798e03b088b64b016a1f (diff)
sdl + emscripten backend
-rw-r--r--Makefile24
-rwxr-xr-xdistrib.sh6
-rw-r--r--ems/post_run.js3
-rw-r--r--ems/template.html48
-rw-r--r--hftrss.c49
-rw-r--r--plat.c342
6 files changed, 447 insertions, 25 deletions
diff --git a/Makefile b/Makefile
index b77608e..4042ab4 100644
--- a/Makefile
+++ b/Makefile
@@ -42,6 +42,30 @@ ifeq ($(config), release_gnu64)
libs = -lX11 -lXi -lpulse -lpulse-simple -lpthread -lm
endif
+ifeq ($(config), debug_sdl2)
+ compiler = gcc
+ tool_compiler = gcc
+ linker = gcc
+ conf_cflags = -DDEBUG -Dplat_sdl2 -Dplat_x86 \
+ -Dallocation_default_alignment=8 \
+ -g
+ libs = -lSDL2 -lm
+endif
+
+ifeq ($(config), release_ems)
+ compiler = emcc
+ tool_compiler = gcc
+ target = ems/hftrss.html
+ linker = emcc
+ conf_cflags = -DNDEBUG -Dplat_ems -Dplat_sdl2 \
+ -Dplat_ems \
+ -Dallocation_default_alignment=8 \
+ -O3
+ conf_lflags = -m64 -s --shell-file ems/template.html \
+ --post-js ems/post_run.js -sUSE_SDL=2
+ libs = -lm
+endif
+
sources = \
animation.c \
asset.c \
diff --git a/distrib.sh b/distrib.sh
index 60fb36d..e4ff5f2 100755
--- a/distrib.sh
+++ b/distrib.sh
@@ -2,4 +2,8 @@
mkdir -p distrib
make clean
make config=release_gnu64
-cp 1bitjam distrib/1bitjam
+cp hftrss distrib/hftrss
+
+make clean
+make config=release_ems
+zip distrib/ems.zip ems/hftrss.js ems/hftrss.html ems/hftrss.wasm
diff --git a/ems/post_run.js b/ems/post_run.js
new file mode 100644
index 0000000..b52a931
--- /dev/null
+++ b/ems/post_run.js
@@ -0,0 +1,3 @@
+addOnPostRun(function() {
+ document.getElementById('canvas').focus();
+});
diff --git a/ems/template.html b/ems/template.html
new file mode 100644
index 0000000..10694e2
--- /dev/null
+++ b/ems/template.html
@@ -0,0 +1,48 @@
+<!doctype html>
+<html lang="en-us">
+ <head>
+ <meta charset="utf-8">
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+ <title>hftrss</title>
+
+ <style>
+ body {
+ padding: none;
+ margin-top: 0px;
+ margin-left: 0px;
+ background-color: #000000;
+ image-rendering: crisp-edges;
+ }
+ </style>
+ </head>
+ <body>
+ <canvas class="emscripten" id="canvas" oncontextmenu="event.preventDefault()" tabindex="0" onclick="focus_canvas()"></canvas>
+
+ <script>
+ function focus_canvas() {
+ document.getElementById('canvas').focus();
+ }
+ Module = {
+ print:
+ function(what)
+ {
+ console.log(what);
+ },
+ printErr:
+ function(what)
+ {
+ console.error(what);
+ },
+ canvas:
+ (
+ function()
+ {
+ return document.getElementById('canvas');
+ }
+ )()
+ };
+ </script>
+
+ {{{ SCRIPT }}}
+ </body>
+</html>
diff --git a/hftrss.c b/hftrss.c
index 1b903c1..b52fcd6 100644
--- a/hftrss.c
+++ b/hftrss.c
@@ -6,14 +6,13 @@
#include "plat.h"
#include "render.h"
-int entrypoint(int argc, const char** argv, Arena* m) {
- Heap h;
- App* a;
- FPS f;
- Renderer r;
- Game* game;
- (void)argc;
- (void)argv;
+static Heap h;
+static App* a;
+static FPS f;
+static Renderer r;
+static Game* game;
+
+int prog_init(Arena* m) {
init_maths();
init_heap(
&h,
@@ -25,21 +24,27 @@ int entrypoint(int argc, const char** argv, Arena* m) {
init_fps(&f, default_mpf);
game = arena_alloc(m, sizeof *game);
init_game(game, game_state_menu);
- while (a->o) {
- fps_begin(&f);
- while (f.now >= f.next && a->o) {
- app_begin(a);
- update_game(game, a);
- ren_begin(&r, a->fb, viewport_w, viewport_h);
- ren_clear(&r);
- ren_game(game, &r);
- ren_end(&r);
- app_end(a);
- fps_update(&f);
- }
- fps_end(&f);
+ return 0;
+}
+
+int prog_update(void) {
+ fps_begin(&f);
+ while (f.now >= f.next && a->o) {
+ app_begin(a);
+ update_game(game, a);
+ ren_begin(&r, a->fb, viewport_w, viewport_h);
+ ren_clear(&r);
+ ren_game(game, &r);
+ ren_end(&r);
+ app_end(a);
+ fps_update(&f);
}
+ fps_end(&f);
+ return a->o;
+}
+
+void prog_deinit(void) {
deinit_audio();
deinit_app(a);
- return error_none;
}
+
diff --git a/plat.c b/plat.c
index 6669c12..3f2a007 100644
--- a/plat.c
+++ b/plat.c
@@ -179,11 +179,16 @@ void fps_update(FPS* f) {
f->fps = 1000000000 / (f->ct - f->pt);
}
-extern int entrypoint(int, const char**, Arena*);
+extern int prog_init(Arena* m);
+extern int prog_update(void);
+extern int prog_deinit(void);
int main(int argc, const char** argv) {
Arena a;
void* mem = malloc(memory_size);
+ int r;
+ (void)argc;
+ (void)argv;
if (!mem) {
print_err("Out of memory.\n");
return error_out_of_memory;
@@ -194,7 +199,10 @@ int main(int argc, const char** argv) {
mem,
memory_size
);
- return entrypoint(argc, argv, &a);
+ if ((r = prog_init(&a))) return r;
+ for (r = 1; r; r = prog_update());
+ prog_deinit();
+ return error_none;
}
#endif
@@ -580,3 +588,333 @@ void play_sound(int len) {
}
#endif
+
+#ifdef plat_sdl2
+
+#if defined(plat_ems)
+#include <emscripten.h>
+#endif
+
+#include <SDL2/SDL.h>
+#include <stdio.h>
+#include <string.h>
+
+void print(const char* fmt, ...) {
+ va_list a;
+ va_start(a, fmt);
+ SDL_LogMessageV(0, SDL_LOG_PRIORITY_INFO, fmt, a);
+ va_end(a);
+}
+
+void print_err(const char* fmt, ...) {
+ va_list a;
+ va_start(a, fmt);
+ SDL_LogMessageV(0, SDL_LOG_PRIORITY_ERROR, fmt, a);
+ va_end(a);
+}
+
+void print_war(const char* fmt, ...) {
+ va_list a;
+ va_start(a, fmt);
+ SDL_LogMessageV(0, SDL_LOG_PRIORITY_WARN, fmt, a);
+ va_end(a);
+}
+
+int imp_assert(
+ int val,
+ const char* expr,
+ const char* file,
+ int line
+) {
+ if (!val) {
+ print_err(
+ "%d:%s: Assertion failed: %s.\n",
+ line,
+ file,
+ expr
+ );
+ pbreak(error_assertion_failed);
+ return 0;
+ }
+ return 1;
+}
+
+void pbreak(Error code) {
+ (void)code;
+ SDL_TriggerBreakpoint();
+}
+
+void init_fps(FPS* f, int mpf) {
+ f->now = SDL_GetTicks();
+ f->next = f->now;
+ f->mpf = mpf;
+ f->pt = 0;
+ f->ct = -1;
+}
+
+void fps_begin(FPS* f) {
+ f->now = SDL_GetTicks();
+}
+
+void fps_end(FPS* f) {
+ unsigned long ts = f->next - f->now;
+ if (ts > 0) {
+ SDL_Delay(ts);
+ }
+}
+
+void fps_update(FPS* f) {
+ f->next += f->mpf;
+ f->pt = f->ct;
+ f->ct = SDL_GetTicks();
+ f->fps = 1000 / (f->ct - f->pt);
+}
+
+extern int prog_init(Arena* m);
+extern int prog_update(void);
+extern void prog_deinit(void);
+
+static void main_loop(void) {
+#ifdef plat_ems
+ prog_update();
+#else
+ int r;
+ for (r = 1; r; r = prog_update());
+ SDL_Quit();
+#endif
+}
+
+int main(int argc, const char** argv) {
+ Arena a;
+ void* mem = malloc(memory_size);
+ int r;
+ (void)argc;
+ (void)argv;
+ if (!mem) {
+ print_err("Out of memory.\n");
+ return error_out_of_memory;
+ }
+ init_arena(
+ &a,
+ mem,
+ memory_size
+ );
+ if ((r = prog_init(&a))) return r;
+#ifdef plat_ems
+ emscripten_set_main_loop(main_loop, 0, 1);
+#else
+ main_loop();
+#endif
+ prog_deinit();
+ return 0;
+}
+
+Btn sdl_to_btn(const SDL_Event* e) {
+ switch (e->key.keysym.sym) {
+ case SDLK_z:
+ case SDLK_RETURN:
+ return btn_jump;
+ case SDLK_x:
+ return btn_shoot;
+ case SDLK_c:
+ return btn_special;
+ case SDLK_LEFT:
+ case SDLK_h:
+ return btn_left;
+ case SDLK_RIGHT:
+ case SDLK_l:
+ return btn_right;
+ case SDLK_UP:
+ case SDLK_k:
+ return btn_up;
+ case SDLK_DOWN:
+ case SDLK_j:
+ return btn_down;
+ default: return btn_unknown;
+ }
+}
+
+typedef struct {
+ SDL_Window* wi;
+ SDL_Renderer* re;
+ SDL_Texture* bb;
+ unsigned* bbp;
+ int w, h;
+} App_Internal;
+
+void init_window(App* a, App_Internal* i, const char* n) {
+ int flag = 0;
+#ifndef plat_ems
+ flag = SDL_WINDOW_RESIZABLE;
+#endif
+ SDL_CreateWindowAndRenderer(
+ viewport_w * 2,
+ viewport_h * 2,
+ flag,
+ &i->wi,
+ &i->re
+ );
+ i->w = viewport_w * 2;
+ i->h = viewport_h * 2;
+ SDL_SetWindowTitle(i->wi, n);
+ i->bb = SDL_CreateTexture(
+ i->re,
+ SDL_PIXELFORMAT_ABGR8888,
+ SDL_TEXTUREACCESS_STREAMING,
+ viewport_w,
+ viewport_h
+ );
+ a->fb = heap_alloc(
+ a->heap,
+ (viewport_w * viewport_h + 32) / 32 * sizeof *a->fb
+ );
+}
+
+App* new_app(struct Heap* mem, const char* n) {
+ App* a;
+ App_Internal* i;
+ a = heap_alloc(mem, sizeof *a + sizeof *i);
+ i = (App_Internal*)(&a[1]);
+ a->heap = mem;
+ a->s = 2;
+ a->o = 0;
+ a->err = error_none;
+ init_window(a, i, n);
+ a->o = 1;
+ return a;
+}
+
+void deinit_app(App* a) {
+
+}
+
+void app_begin(App* a) {
+ App_Internal* i = (App_Internal*)(&a[1]);
+ SDL_Event e;
+ int j;
+ Btn btn;
+ for (j = 0; j < btn_count; j++)
+ a->btn_states[j] &= ~(
+ btn_state_just_pressed | btn_state_just_released
+ );
+ while (SDL_PollEvent(&e)) {
+ switch (e.type) {
+ case SDL_QUIT:
+ a->o = 0;
+ break;
+ case SDL_KEYDOWN:
+ btn = sdl_to_btn(&e);
+ a->btn_states[btn] |=
+ btn_state_pressed |
+ btn_state_just_pressed;
+ break;
+ case SDL_KEYUP:
+ btn = sdl_to_btn(&e);
+ a->btn_states[btn] &= ~btn_state_pressed;
+ a->btn_states[btn] |= btn_state_just_released;
+ break;
+ case SDL_WINDOWEVENT:
+ switch (e.window.event) {
+ case SDL_WINDOWEVENT_RESIZED:
+ i->w = e.window.data1;
+ i->h = e.window.data2;
+ break;
+ }
+ break;
+ default: break;
+ }
+ }
+}
+
+void rencpy(App* a, unsigned* t, int p) {
+ const unsigned white = 0xffffffff;
+ const unsigned black = 0x00000000;
+ int x, y, i = 0;
+ p /= 4;
+ for (y = 0; y < viewport_h; y++) {
+ for (x = 0; x < viewport_w; x++, i++) {
+ int bit = 1 << (i & 0x1f);
+ bit &= a->fb[i >> 5];
+ t[x + y * p] = bit? white: black;
+ }
+ }
+}
+
+void fit_dr(App_Internal* i, SDL_Rect* r) {
+ int w = viewport_w, h = viewport_h, s;
+ for (s = 1; w * s <= i->w && h * s <= i->h; s++);
+ if (s > 1) s--;
+ r->w = viewport_w * s;
+ r->h = viewport_h * s;
+ r->x = i->w / 2 - r->w / 2;
+ r->y = i->h / 2 - r->h / 2;
+}
+
+void app_end(App* a) {
+ App_Internal* i = (App_Internal*)(&a[1]);
+ unsigned* target;
+ SDL_Rect sr = { 0, 0, viewport_w, viewport_h };
+ SDL_Rect dr = sr;
+ int pitch;
+ fit_dr((App_Internal*)&a[1], &dr);
+ SDL_LockTexture(
+ i->bb,
+ 0,
+ (void**)&target,
+ &pitch
+ );
+ rencpy(a, target, pitch);
+ SDL_UnlockTexture(i->bb);
+ SDL_RenderClear(i->re);
+ SDL_RenderCopy(
+ i->re,
+ i->bb,
+ &sr,
+ &dr
+ );
+ SDL_RenderPresent(i->re);
+}
+
+static SDL_AudioSpec aud_spec;
+int audio_len, audio_time;
+
+void fill_audio(void *udata, Uint8 *stream, int len) {
+ int i;
+ int16_t* pcm = (int16_t*)stream;
+ len /= 2;
+ for (
+ i = 0;
+ i < len && audio_len;
+ i++, audio_len--, audio_time++
+ ) pcm[i] = (int16_t)(sin((double)audio_time / 0.05) * 16000.0);
+ for (; i < len; i++)
+ pcm[i] = 0;
+}
+
+void init_audio(void) {
+ audio_len = 0;
+ audio_time = 0;
+ aud_spec.freq = audio_sample_rate;
+ aud_spec.format = AUDIO_S16;
+ aud_spec.channels = 1;
+#ifndef plat_ems
+ aud_spec.samples = audio_buffer_size;
+#else
+ /* websites are retarded */
+ aud_spec.samples = 1024;
+#endif
+ aud_spec.callback = fill_audio;
+ aud_spec.userdata = 0;
+ SDL_OpenAudio(&aud_spec, 0);
+ SDL_PauseAudio(0);
+}
+
+void deinit_audio(void) {
+ SDL_CloseAudio();
+}
+
+void play_sound(int len) {
+ audio_len += len;
+}
+
+#endif