diff options
author | untir_l <87096069+untir-l@users.noreply.github.com> | 2022-06-11 15:43:23 +0000 |
---|---|---|
committer | untir_l <87096069+untir-l@users.noreply.github.com> | 2022-06-11 15:49:12 +0000 |
commit | 2aa35e2c108f954949ec001f1e33846379e7cc98 (patch) | |
tree | 1653252d1419d01b7347a70f47416ae45d4fb2c4 /web | |
parent | e0babc53b16ef49ff40db21bbc4326e6e23185fa (diff) | |
download | hitomezashi-2aa35e2c108f954949ec001f1e33846379e7cc98.tar hitomezashi-2aa35e2c108f954949ec001f1e33846379e7cc98.tar.gz hitomezashi-2aa35e2c108f954949ec001f1e33846379e7cc98.zip |
Web: canvas now updates on the fly based on input
Also some other minor changes to thw web README.md and the Makefile, and
making some stuff const where they weren't throughout the lib/ tree as well.
Diffstat (limited to 'web')
-rw-r--r-- | web/hitomezashi_web.c | 169 | ||||
-rw-r--r-- | web/hitomezashi_web.h | 33 | ||||
-rw-r--r-- | web/hitomezashi_web_shell.html | 164 |
3 files changed, 166 insertions, 200 deletions
diff --git a/web/hitomezashi_web.c b/web/hitomezashi_web.c index 0a36a2c..413f727 100644 --- a/web/hitomezashi_web.c +++ b/web/hitomezashi_web.c @@ -1,120 +1,111 @@ #include "hitomezashi_web.h" - -#include <stdbool.h> -#include <stdio.h> -#include <string.h> - #include "SDL2/SDL.h" #include "emscripten.h" #include "hitomezashi.h" #include "hitomezashi_utils.h" +#include <stdbool.h> +#include <stdlib.h> +#include <string.h> -int EMSCRIPTEN_KEEPALIVE main(void) { - char *param_str = hitomezashi_web_get_args(); +SDL_Window *window; - if (param_str[0] == 'i') { - return Hitomezashi_Web_Result_Err_Get_Params; - } +int EMSCRIPTEN_KEEPALIVE main(void) { + if (SDL_Init(SDL_INIT_VIDEO) != 0) + return EXIT_FAILURE; - char *x_pattern_str = strtok(param_str, "\n"); - int x_pattern_len = strlen(x_pattern_str); - // We needn't bother checking whether - // hitomezashi_ascii_binary_str_to_ints() was successful as the strings - // passed to it are validated by hitomezashi_web_get_args() in JS. - char *x_pattern = - hitomezashi_ascii_binary_str_to_ints(x_pattern_str, x_pattern_len); + // Stop SDL from capturing all input and stopping input elements from working + SDL_SetHint(SDL_HINT_EMSCRIPTEN_KEYBOARD_ELEMENT, "#canvas"); - char *y_pattern_str = strtok(NULL, "\n"); - int y_pattern_len = strlen(y_pattern_str); - char *y_pattern = - hitomezashi_ascii_binary_str_to_ints(y_pattern_str, y_pattern_len); + if (!(window = SDL_CreateWindow("", 0, 0, 100, 100, SDL_WINDOW_RESIZABLE))) + return EXIT_FAILURE; - int gap = strtol(strtok(NULL, "\n"), NULL, 0); - int line_thickness = strtol(strtok(NULL, "\n"), NULL, 0); + emscripten_set_main_loop(main_loop, -1, true); - Uint32 fg_colour = strtol(strtok(NULL, "\n"), NULL, 0); - Uint32 bg_colour = strtol(strtok(NULL, "\n"), NULL, 0); + return EXIT_SUCCESS; +} - if (SDL_Init(SDL_INIT_VIDEO) != 0) { - return Hitomezashi_Web_Result_Err_Sdl_Init; - } +void EMSCRIPTEN_KEEPALIVE main_loop(void) { + const char *x_pattern_raw = hitomezashi_web_get_x_pattern(); + const char *y_pattern_raw = hitomezashi_web_get_y_pattern(); + const int x_pattern_len = strlen(x_pattern_raw); + const int y_pattern_len = strlen(y_pattern_raw); + const char *x_pattern = + hitomezashi_ascii_binary_str_to_ints(x_pattern_raw, x_pattern_len); + const char *y_pattern = + hitomezashi_ascii_binary_str_to_ints(y_pattern_raw, y_pattern_len); - SDL_Window *window; - SDL_Renderer *renderer; - SDL_Texture *texture; + const int gap = hitomezashi_web_get_gap(); + const int line_thickness = hitomezashi_web_get_line_thickness(); - struct Hitomezashi_State state; - if (hitomezashi_state_init(&state, x_pattern_len, y_pattern_len, x_pattern, - y_pattern, gap, line_thickness, fg_colour, - bg_colour) != 0) { - return Hitomezashi_Web_Result_Err_State_Init; - } + const Uint32 fg_colour = hitomezashi_web_get_fg_colour(); + const Uint32 bg_colour = hitomezashi_web_get_bg_colour(); - // Stop SDL from capturing all input and stopping input elements from working - SDL_SetHint(SDL_HINT_EMSCRIPTEN_KEYBOARD_ELEMENT, "#canvas"); - SDL_CreateWindowAndRenderer(state.output_width, state.output_height, 0, - &window, &renderer); + // If any of these are 0, input was invalid + if (!(x_pattern_len && y_pattern_len && gap && line_thickness)) + return; + struct Hitomezashi_State state; + hitomezashi_state_init(&state, x_pattern_len, y_pattern_len, x_pattern, + y_pattern, gap, line_thickness, fg_colour, bg_colour); + SDL_SetWindowSize(window, state.output_width, state.output_height); hitomezashi_draw(&state); - texture = SDL_CreateTextureFromSurface(renderer, state.surface); - SDL_RenderClear(renderer); - SDL_RenderCopy(renderer, texture, NULL, NULL); - SDL_RenderPresent(renderer); - - // Since this is a short-lived application which is only run on page load, - // we do not need to delay the exit of main by freeing memory - it will be - // reclaimed by the browser automatically. + SDL_BlitSurface(state.surface, NULL, SDL_GetWindowSurface(window), NULL); + SDL_UpdateWindowSurface(window); - return Hitomezashi_Web_Result_Success; + free((char *)x_pattern_raw); + free((char *)y_pattern_raw); + free((char *)x_pattern); + free((char *)y_pattern); + SDL_FreeSurface(state.surface); } -EM_JS(char *, hitomezashi_web_get_args, (void), { - function validate_pattern(pattern) { - let valid = true; - for (let c of pattern) { - if (c != "0" && c != "1") { - valid = false; - break; - } +EM_JS(char *, hitomezashi_web_get_x_pattern, (void), { + let result_js = document.querySelector("input[name=\"x_pattern\"]").value; + for (let c of result_js) { + if (c != "0" && c != "1") { + result_js = ""; + break; } - return valid; } + const result_length_bytes = lengthBytesUTF8(result_js) + 1; + let result_wasm_heap = _malloc(result_length_bytes); + stringToUTF8(result_js, result_wasm_heap, result_length_bytes); + return result_wasm_heap; +}) - let result_js = ""; - - let search_params = new URLSearchParams(window.location.search); - - const x_pattern = search_params.get("x_pattern"); - const y_pattern = search_params.get("y_pattern"); - const gap = search_params.get("gap"); - const line_thickness = search_params.get("line_thickness"); - let fg_colour = search_params.get("fg_colour"); - let bg_colour = search_params.get("bg_colour"); - if (!fg_colour) { - fg_colour = "#000000"; - } - if (!bg_colour) { - bg_colour = "#FFFFFF"; - } - fg_colour = "0x" + fg_colour.slice(1); - bg_colour = "0x" + bg_colour.slice(1); - - if (!(x_pattern && y_pattern && gap && line_thickness)) { - result_js = "i"; - } else { - const patterns_are_valid = - validate_pattern(x_pattern) && validate_pattern(y_pattern); - if (!patterns_are_valid) { - result_js = "i"; - } else { - result_js = x_pattern + "\n" + y_pattern + "\n" + gap + "\n" + - line_thickness + "\n" + fg_colour + "\n" + bg_colour + "\n"; +EM_JS(char *, hitomezashi_web_get_y_pattern, (void), { + let result_js = document.querySelector("input[name=\"y_pattern\"]").value; + for (let c of result_js) { + if (c != "0" && c != "1") { + result_js = ""; + break; } } - const result_length_bytes = lengthBytesUTF8(result_js) + 1; let result_wasm_heap = _malloc(result_length_bytes); stringToUTF8(result_js, result_wasm_heap, result_length_bytes); return result_wasm_heap; }) + +EM_JS(unsigned int, hitomezashi_web_get_gap, (void), { + const result = +document.querySelector("input[name=\"gap\"").value; + return (result > 0 ? result : 0); +}) + +EM_JS(unsigned int, hitomezashi_web_get_line_thickness, (void), { + const result = +document.querySelector("input[name=\"line_thickness\"").value; + return (result > 0 ? result : 0); +}) + +EM_JS(Uint32, hitomezashi_web_get_fg_colour, (void), { + const result = parseInt( + document.querySelector("input[name=\"fg_colour\"]").value.slice(1), 16); + return (result ? result : 0); +}) + +EM_JS(Uint32, hitomezashi_web_get_bg_colour, (void), { + const result = parseInt( + document.querySelector("input[name=\"bg_colour\"]").value.slice(1), 16); + return (result ? result : 0); +}) diff --git a/web/hitomezashi_web.h b/web/hitomezashi_web.h index 361a8c2..c7db1c9 100644 --- a/web/hitomezashi_web.h +++ b/web/hitomezashi_web.h @@ -5,32 +5,13 @@ #include "SDL2/SDL.h" #include "emscripten.h" -enum Hitomezashi_Web_Result { - Hitomezashi_Web_Result_Success = 0, - Hitomezashi_Web_Result_Err_Get_Params = 1, - Hitomezashi_Web_Result_Err_Sdl_Init = 2, - Hitomezashi_Web_Result_Err_State_Init = 3, -}; +void main_loop(void); -int hitomezashi_web(int x_pattern_len, int y_pattern_len, char *x_pattern, - char *y_pattern, int gap, int line_thickness, - Uint32 fg_colour, Uint32 bg_colour); - -/** Parse the URL query string and return the parameters thereof. - * - * This is implemented in JS with EM_JS, in order to access the query string. - * The friction of interoperation is the reason for turning the query string - * into a different string that the C code must parse to the actual values, - * rather than sending the data back individually with the correct types. - * That is probably possible, but it seems unpleasant. - * - * The query string's x_pattern, y_pattern, gap, and line_thickness are - * parsed. If not all of them are present, "i" is returned. If x_pattern or - * y_pattern in the query string have characters other than ASCII digits 0 - * and 1, "i" is returned. Otherwise, the values are returned as a - * newline-delimited string in the order x_pattern, y_pattern, gap, - * line_thickness. - */ -char *hitomezashi_web_get_args(void); +char *hitomezashi_web_get_x_pattern(void); +char *hitomezashi_web_get_y_pattern(void); +unsigned int hitomezashi_web_get_gap(void); +unsigned int hitomezashi_web_get_line_thickness(void); +Uint32 hitomezashi_web_get_fg_colour(void); +Uint32 hitomezashi_web_get_bg_colour(void); #endif // HITOMEZASHI_WEB_H diff --git a/web/hitomezashi_web_shell.html b/web/hitomezashi_web_shell.html index a774f1e..e0e23d4 100644 --- a/web/hitomezashi_web_shell.html +++ b/web/hitomezashi_web_shell.html @@ -1,90 +1,84 @@ <!doctype html> <html lang="en-us"> - <head> - <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> - <!--This is code modified from https://github.com/emscripten-core/emscripten/blob/main/src/shell_minimal.html.--> - <meta name="viewport" content="width=device-width, initial-scale=1.0"> - <title>Hitomezashi</title> - <style> - canvas { - padding-right: 0; - margin-left: auto; - margin-right: auto; - display: block; - border: 0px none; - background-color: black; - } - form { - display: table; - margin: auto; - } - form > p { - display: table-row; - } - form > p > label { - display: table-cell; - } - form > p > label > input { - display: table-cell; - } - button#downloadCanvas { - display: block; - margin: 0 auto; - } - p#info { - text-align: center; - } - p#info > a { - margin-left: 1ex; - margin-right: 1ex; - } - </style> - </head> - <body> - <p id="info">See <a href="https://github.com/untir-l/hitomezashi/blob/main/README.md">the README</a> for information.</p> - <form name="hitomezashi parameters" autocomplete="off" method="get"> - <p><label>x pattern: <input name="x_pattern"></label></p> - <p><label>y pattern: <input name="y_pattern"></label></p> - <p><label>gap: <input name="gap" type="number"></label></p> - <p><label>line thickness: <input name="line_thickness" type="number"></label></p> - <p><label>foreground colour: <input name="fg_colour" type="color"></label></p> - <p><label>background colour: <input name="bg_colour" type="color"></label></p> - <p><input type="submit" value="submit"></p> - </form> - <br> - <canvas class="emscripten" id="canvas" oncontextmenu="event.preventDefault()" tabindex=-1></canvas> - <br> - <button id="downloadCanvas">download as image</button> - <script type='text/javascript'> - // Make sure the WebGL context preserves the drawing buffer so the download button works - // Based on https://stackoverflow.com/a/43979935 - const canvas = document.querySelector("#canvas"); - canvas.getContext("webgl", {preserveDrawingBuffer: true}); - var Module = { - canvas: (function() { - canvas.addEventListener("webglcontextlost", function(e) { alert('WebGL context lost. You will need to reload the page.'); e.preventDefault(); }, false); - return canvas; - })(), - }; +<head> + <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> + <!--This is code modified from https://github.com/emscripten-core/emscripten/blob/main/src/shell_minimal.html.--> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> + <title>Hitomezashi</title> + <style> + canvas { + padding-right: 0; + margin-left: auto; + margin-right: auto; + display: block; + border: 0px none; + background-color: black; + } - // Set the input fields based on the query string so they appear sticky - const params = new URLSearchParams(window.location.search); - document.querySelector("input[name=\"x_pattern\"]").value = params.get("x_pattern"); - document.querySelector("input[name=\"y_pattern\"]").value = params.get("y_pattern"); - document.querySelector("input[name=\"gap\"]").value = params.get("gap"); - document.querySelector("input[name=\"line_thickness\"]").value = params.get("line_thickness"); - document.querySelector("input[name=\"fg_colour\"]").value = params.get("fg_colour"); - document.querySelector("input[name=\"bg_colour\"]").value = params.get("bg_colour"); + form { + display: table; + margin: auto; + } - function downloadCanvas() { - let downloadLink = document.createElement("a"); - downloadLink.setAttribute("download", "hitomezashi"); - downloadLink.setAttribute("href", canvas.toDataURL()); - downloadLink.click(); - } - document.querySelector("#downloadCanvas").addEventListener("click", downloadCanvas); - </script> - {{{ SCRIPT }}} - </body> -</html> + form>p { + display: table-row; + } + + form>p>label { + display: table-cell; + } + + form>p>label>input { + display: table-cell; + } + + button#downloadCanvas { + display: block; + margin: 0 auto; + } + + p#info { + text-align: center; + } + + p#info>a { + margin-left: 1ex; + margin-right: 1ex; + } + </style> +</head> + +<body> + <form name="hitomezashi parameters" autocomplete="off"> + <p><label>x pattern: <input name="x_pattern"></label></p> + <p><label>y pattern: <input name="y_pattern"></label></p> + <p><label>gap: <input name="gap" type="number"></label></p> + <p><label>line thickness: <input name="line_thickness" type="number"></label></p> + <p><label>foreground colour: <input name="fg_colour" type="color"></label></p> + <p><label>background colour: <input name="bg_colour" type="color"></label></p> + </form> + + <canvas class="emscripten" id="canvas" oncontextmenu="event.preventDefault()" tabindex=-1></canvas> + <script type='text/javascript'> + var Module = { + canvas: (function () { + var canvas = document.getElementById('canvas'); + + canvas.addEventListener("webglcontextlost", function (e) { alert('Error: WebGL context lost. Please reload the page.'); e.preventDefault(); }, false); + + return canvas; + })(), + }; + function downloadCanvas() { + let downloadLink = document.createElement("a"); + downloadLink.setAttribute("download", "hitomezashi"); + downloadLink.setAttribute("href", canvas.toDataURL()); + downloadLink.click(); + } + document.querySelector("#downloadCanvas").addEventListener("click", downloadCanvas); + </script> + {{{ SCRIPT }}} +</body> + +</html>
\ No newline at end of file |