diff options
author | quou <quou@disroot.org> | 2024-10-12 00:23:57 +1100 |
---|---|---|
committer | quou <quou@disroot.org> | 2024-10-12 00:24:07 +1100 |
commit | 7a7628a3feaec81d9a445acbec604baecac3a167 (patch) | |
tree | 962d56698071cdf5766083b77fa05607b9e37668 | |
parent | c5445eb3032f2081c8cf798e03b088b64b016a1f (diff) |
sdl + emscripten backend
-rw-r--r-- | Makefile | 24 | ||||
-rwxr-xr-x | distrib.sh | 6 | ||||
-rw-r--r-- | ems/post_run.js | 3 | ||||
-rw-r--r-- | ems/template.html | 48 | ||||
-rw-r--r-- | hftrss.c | 49 | ||||
-rw-r--r-- | plat.c | 342 |
6 files changed, 447 insertions, 25 deletions
@@ -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 \ @@ -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> @@ -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; } + @@ -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 |