diff options
| author | Charlie Stanton <charlie@shtanton.xyz> | 2025-04-05 08:10:21 +0100 | 
|---|---|---|
| committer | Charlie Stanton <charlie@shtanton.xyz> | 2025-04-05 08:10:21 +0100 | 
| commit | 935f69ca25bca24fd36166e7192ebb69af2d35e9 (patch) | |
| tree | 182cd864dc0d486ca64868f037da306b60ec51e2 | |
| parent | f0bf72a54477fb18b1666293a652b52f53320a1d (diff) | |
| download | ldjam57-935f69ca25bca24fd36166e7192ebb69af2d35e9.tar | |
Get web assembly working
| -rw-r--r-- | .gitignore | 1 | ||||
| -rw-r--r-- | Makefile | 19 | ||||
| -rw-r--r-- | src/all.c | 83 | ||||
| -rw-r--r-- | src/all.h | 13 | ||||
| -rw-r--r-- | src/index.html.in | 105 | 
5 files changed, 185 insertions, 36 deletions
| diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..378eac2 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +build @@ -1,2 +1,17 @@ -main: src/all.c src/all.h -	gcc -Wall -Wextra -Wpedantic -DSDL $$(pkg-config --libs sdl3) -o main src/all.c +build/main: src/all.c src/all.h +	mkdir -p build +	gcc -Wall -Wextra -Wpedantic -DSDL $$(pkg-config --libs sdl3) -o build/main src/all.c + +build/main.wasm: src/all.c src/all.h +	mkdir -p build +	clang --target=wasm32 -nostdlib -DWASM -Wall -Wextra -Wpedantic \ +		-Wl,--no-entry -fno-builtin -o build/main.wasm src/all.c + +build/main.wasm.b64: build/main.wasm +	(printf '"'; base64 < build/main.wasm | tr -d '\n'; printf '"') > build/main.wasm.b64 + +build/index.html: src/index.html.in build/main.wasm.b64 +	clang -E -P -undef -nostdinc -x c -o build/index.html src/index.html.in + +clean: +	rm -r build @@ -1,4 +1,15 @@ -#include "all.h" +#include <stddef.h> +#include <stdint.h> + +typedef struct { +	int x, y, w, h; +	unsigned char r, g, b, a; +} DrawElement; + +typedef struct { +	int len; +	DrawElement els[256]; +} DrawList;  #define MEM_SIZE (1<<16) @@ -30,6 +41,7 @@ typedef struct {  	int x, y;  } State; +// Mirror these in src/index.html.in  enum {  	INPUT_NONE,  	INPUT_UP, @@ -50,18 +62,14 @@ static DrawList *render(State state, UI *ui, Arena *a) {  	DrawList *drawList = new(a, 1, DrawList);  	drawList->len = 1;  	drawList->els[0] = (DrawElement) { -		.rect = (SDL_Rect) { -			.x = state.x, -			.y = state.y, -			.w = 100, -			.h = 100, -		}, -		.color = (SDL_Color) { -			.r = 255, -			.g = 0, -			.b = 0, -			.a = 255, -		}, +		.x = state.x, +		.y = state.y, +		.w = 100, +		.h = 100, +		.r = 255, +		.g = 0, +		.b = 0, +		.a = 255,  	};  	return drawList; @@ -88,6 +96,8 @@ static void update(Game *game, Arena a) {  #if SDL +#include <SDL3/SDL.h> +  int main(int argc, char **argv) {  	(void) argc;  	(void) argv; @@ -154,16 +164,16 @@ int main(int argc, char **argv) {  		for (int i = 0; i < drawList->len; i++) {  			SDL_SetRenderDrawColor(  				r, -				drawList->els[i].color.r, -				drawList->els[i].color.g, -				drawList->els[i].color.b, -				drawList->els[i].color.a +				drawList->els[i].r, +				drawList->els[i].g, +				drawList->els[i].b, +				drawList->els[i].a  			);  			SDL_FRect rect = { -				.x = (float) drawList->els[i].rect.x, -				.y = (float) drawList->els[i].rect.y, -				.w = (float) drawList->els[i].rect.w, -				.h = (float) drawList->els[i].rect.h +				.x = (float) drawList->els[i].x, +				.y = (float) drawList->els[i].y, +				.w = (float) drawList->els[i].w, +				.h = (float) drawList->els[i].h  			};  			SDL_RenderFillRect(r, &rect);  		} @@ -177,4 +187,35 @@ int main(int argc, char **argv) {  }  #elif WASM + +static Game *game; +static Arena perm; + +__attribute((export_name("game_init"))) +void game_init(void) { +	static char heap[MEM_SIZE]; +	perm.start = heap; +	perm.end = heap + MEM_SIZE; + +	game = new(&perm, 1, Game); +	game->state = (State) { +		.x = 100, +		.y = 100, +	}; +} + +__attribute((export_name("game_render"))) +DrawList *game_render(int width, int height) { +	game->ui.width = width; +	game->ui.height = height; +	Arena scratch = perm; +	return render(game->state, &game->ui, &scratch); +} + +__attribute((export_name("game_update"))) +void game_update(int input) { +	game->input = input; +	update(game, perm); +} +  #endif @@ -1,13 +0,0 @@ -#include <SDL3/SDL.h> -#include <stddef.h> -#include <stdint.h> - -typedef struct { -	SDL_Rect rect; -	SDL_Color color; -} DrawElement; - -typedef struct { -	int len; -	DrawElement els[256]; -} DrawList; diff --git a/src/index.html.in b/src/index.html.in new file mode 100644 index 0000000..fcfb8ef --- /dev/null +++ b/src/index.html.in @@ -0,0 +1,105 @@ +<!doctype html> +<title>LDJam 57</title> +<meta charset="utf-8"/> +<meta name="viewport" content="width=device-width"> +<style> +html, body { +    background: #222222; +    font-family: sans-serif; +    height: 100%; +    margin: 0; +    overflow: hidden; +    text-align: center; +    width: 100%; +} +h1 { +    color: white; +    margin: 0; +} +button { +    font-size: 150%; +    height: 8%; +    margin: 0.3em; +    width: 25%; +} +</style> + +<canvas></canvas> + +<script> +// Mirror these in src/all.c +const INPUT_NONE  = 0; +const INPUT_UP = 1; +const INPUT_DOWN  = 2; +const INPUT_LEFT = 3; +const INPUT_RIGHT  = 4; + +const WASM = +#include "../build/main.wasm.b64" + +async function main() { +    let bytes   = Uint8Array.from(atob(WASM), function(c) { +        return c.charCodeAt(0); +    }); +    let module  = await WebAssembly.compile(bytes); +    let wasm    = await WebAssembly.instantiate(module); +    let exports = wasm.exports; +    let html    = document.querySelector("html"); +    let canvas  = document.querySelector("canvas"); +    let ctx     = canvas.getContext("2d"); +    let memory  = exports.memory; + +    function min(a, b) { +        return b<a ? b : a; +    } + +    function max_width() { +        return html.clientHeight * 0.7 | 0; +    } + +    function render() { +        let width  = canvas.width  = min(html.clientWidth, max_width()); +        let height = canvas.height = width; +        let ptr    = exports.game_render(width, height); +        let dl     = new Int32Array(memory.buffer, ptr); +        let len    = dl[0]; +        let ops    = dl.subarray(1); + +        for (let i = 0; i < len; i++) { +            let op    = ops.subarray(5*i, 5*i+5); +					const color = new Uint8Array(new Uint32Array(ops.subarray(4, 5)).buffer); +					let style = `#${color[0].toString(16).padStart(2, "0")}${color[1].toString(16).padStart(2, "0")}${color[2].toString(16).padStart(2, "0")}`; +						ctx.fillStyle = style; +						ctx.fillRect(op[0], op[1], op[2], op[3]); +        } +    } + +    function onresize() { html.style.maxWidth = `${max_width()}px`; } +    window.addEventListener("resize", onresize); +    onresize(); + +    document.addEventListener("keydown", function(e) { +        if (e.key == "w") { +            exports.game_update(INPUT_UP); +        } else if (e.key == "a") { +            exports.game_update(INPUT_LEFT); +        } else if (e.key == "s") { +            exports.game_update(INPUT_DOWN); +        } else if (e.key == "d") { +            exports.game_update(INPUT_RIGHT); +        } +    }) + +    function animate() { +        // TODO: stop requesting frames when state is static +        requestAnimationFrame(animate); +        exports.game_update(INPUT_NONE); +        render(); +    } +    requestAnimationFrame(animate); + +	exports.game_init(); +} + +main() +</script> | 
