From 21246a1cedf9fa70deea1f39866fc94e9eecedb6 Mon Sep 17 00:00:00 2001 From: Andre Weissflog Date: Thu, 21 Nov 2024 18:07:57 +0100 Subject: [PATCH 1/8] sokol_app.h html5: cleanup canvas lookup handling (see #1154) --- sokol_app.h | 35 ++++++++++++++++------------------- 1 file changed, 16 insertions(+), 19 deletions(-) diff --git a/sokol_app.h b/sokol_app.h index 54b0973b4..e1dcedbfa 100644 --- a/sokol_app.h +++ b/sokol_app.h @@ -1717,7 +1717,7 @@ typedef struct sapp_desc { bool win32_console_utf8; // if true, set the output console codepage to UTF-8 bool win32_console_create; // if true, attach stdout/stderr to a new console window bool win32_console_attach; // if true, attach stdout/stderr to parent process - const char* html5_canvas_name; // the name (id) of the HTML5 canvas element, default is "canvas" + const char* html5_canvas_selector; // css selector of the HTML5 canvas element, default is "#canvas" bool html5_canvas_resize; // if true, the HTML5 canvas size is set to sapp_desc.width/height, otherwise canvas size is tracked bool html5_preserve_drawing_buffer; // HTML5 only: whether to preserve default framebuffer content between frames bool html5_premultiplied_alpha; // HTML5 only: whether the rendered pixels use premultiplied alpha convention @@ -3115,7 +3115,7 @@ _SOKOL_PRIVATE sapp_desc _sapp_desc_defaults(const sapp_desc* desc) { res.gl_minor_version = 3; #endif } - res.html5_canvas_name = _sapp_def(res.html5_canvas_name, "canvas"); + res.html5_canvas_selector = _sapp_def(res.html5_canvas_selector, "#canvas"); res.clipboard_size = _sapp_def(res.clipboard_size, 8192); res.max_dropped_files = _sapp_def(res.max_dropped_files, 1); res.max_dropped_file_path_length = _sapp_def(res.max_dropped_file_path_length, 2048); @@ -3142,9 +3142,8 @@ _SOKOL_PRIVATE void _sapp_init_state(const sapp_desc* desc) { _sapp.framebuffer_height = _sapp.window_height; _sapp.sample_count = _sapp.desc.sample_count; _sapp.swap_interval = _sapp.desc.swap_interval; - _sapp.html5_canvas_selector[0] = '#'; - _sapp_strcpy(_sapp.desc.html5_canvas_name, &_sapp.html5_canvas_selector[1], sizeof(_sapp.html5_canvas_selector) - 1); - _sapp.desc.html5_canvas_name = &_sapp.html5_canvas_selector[1]; + _sapp_strcpy(_sapp.desc.html5_canvas_selector, _sapp.html5_canvas_selector, sizeof(_sapp.html5_canvas_selector)); + _sapp.desc.html5_canvas_selector = _sapp.html5_canvas_selector; _sapp.html5_ask_leave_site = _sapp.desc.html5_ask_leave_site; _sapp.clipboard.enabled = _sapp.desc.enable_clipboard; if (_sapp.clipboard.enabled) { @@ -4780,7 +4779,7 @@ _SOKOL_PRIVATE void _sapp_ios_show_keyboard(bool shown) { #if defined(_SAPP_EMSCRIPTEN) #if defined(EM_JS_DEPS) -EM_JS_DEPS(sokol_app, "$withStackSave,$stringToUTF8OnStack"); +EM_JS_DEPS(sokol_app, "$withStackSave,$stringToUTF8OnStack,$findCanvasEventTarget"); #endif #ifdef __cplusplus @@ -4930,10 +4929,9 @@ _SOKOL_PRIVATE void _sapp_emsc_set_clipboard_string(const char* str) { sapp_js_write_clipboard(str); } -EM_JS(void, sapp_js_add_dragndrop_listeners, (const char* canvas_name_cstr), { +EM_JS(void, sapp_js_add_dragndrop_listeners, (void), { Module.sokol_drop_files = []; - const canvas_name = UTF8ToString(canvas_name_cstr); - const canvas = document.getElementById(canvas_name); + const canvas = Module.sapp_emsc_target; Module.sokol_dragenter = (event) => { event.stopPropagation(); event.preventDefault(); @@ -5005,21 +5003,20 @@ EM_JS(void, sapp_js_fetch_dropped_file, (int index, _sapp_html5_fetch_callback c reader.readAsArrayBuffer(files[index]); }); -EM_JS(void, sapp_js_remove_dragndrop_listeners, (const char* canvas_name_cstr), { - const canvas_name = UTF8ToString(canvas_name_cstr); - const canvas = document.getElementById(canvas_name); +EM_JS(void, sapp_js_remove_dragndrop_listeners, (void), { + const canvas = Module.sapp_emsc_target; canvas.removeEventListener('dragenter', Module.sokol_dragenter); canvas.removeEventListener('dragleave', Module.sokol_dragleave); canvas.removeEventListener('dragover', Module.sokol_dragover); canvas.removeEventListener('drop', Module.sokol_drop); }); -EM_JS(void, sapp_js_init, (const char* c_str_target), { +EM_JS(void, sapp_js_init, (const char* c_str_target_selector), { // lookup and store canvas object by name - const target_str = UTF8ToString(c_str_target); - Module.sapp_emsc_target = document.getElementById(target_str); + const target_selector_str = UTF8ToString(c_str_target_selector); + Module.sapp_emsc_target = findCanvasEventTarget(target_selector_str); if (!Module.sapp_emsc_target) { - console.log("sokol_app.h: invalid target:" + target_str); + console.log("sokol_app.h: invalid target selector:" + target_str); } if (!Module.sapp_emsc_target.requestPointerLock) { console.log("sokol_app.h: target doesn't support requestPointerLock:" + target_str); @@ -5853,7 +5850,7 @@ _SOKOL_PRIVATE void _sapp_emsc_register_eventhandlers(void) { sapp_js_add_clipboard_listener(); } if (_sapp.drop.enabled) { - sapp_js_add_dragndrop_listeners(&_sapp.html5_canvas_selector[1]); + sapp_js_add_dragndrop_listeners(); } #if defined(SOKOL_GLES3) emscripten_set_webglcontextlost_callback(_sapp.html5_canvas_selector, 0, true, _sapp_emsc_webgl_context_cb); @@ -5887,7 +5884,7 @@ _SOKOL_PRIVATE void _sapp_emsc_unregister_eventhandlers(void) { sapp_js_remove_clipboard_listener(); } if (_sapp.drop.enabled) { - sapp_js_remove_dragndrop_listeners(&_sapp.html5_canvas_selector[1]); + sapp_js_remove_dragndrop_listeners(); } #if defined(SOKOL_GLES3) emscripten_set_webglcontextlost_callback(_sapp.html5_canvas_selector, 0, true, 0); @@ -5931,7 +5928,7 @@ _SOKOL_PRIVATE void _sapp_emsc_frame_main_loop(void) { _SOKOL_PRIVATE void _sapp_emsc_run(const sapp_desc* desc) { _sapp_init_state(desc); - sapp_js_init(&_sapp.html5_canvas_selector[1]); + sapp_js_init(_sapp.html5_canvas_selector); double w, h; if (_sapp.desc.html5_canvas_resize) { w = (double) _sapp_def(_sapp.desc.width, _SAPP_FALLBACK_DEFAULT_WINDOW_WIDTH); From 554e7772dc7b0502c69bea34f25ac4bc457266d3 Mon Sep 17 00:00:00 2001 From: Andre Weissflog Date: Thu, 21 Nov 2024 18:22:52 +0100 Subject: [PATCH 2/8] sokol_app.h html5: fix closure errors and false positives --- sokol_app.h | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/sokol_app.h b/sokol_app.h index e1dcedbfa..581d399d9 100644 --- a/sokol_app.h +++ b/sokol_app.h @@ -4931,7 +4931,6 @@ _SOKOL_PRIVATE void _sapp_emsc_set_clipboard_string(const char* str) { EM_JS(void, sapp_js_add_dragndrop_listeners, (void), { Module.sokol_drop_files = []; - const canvas = Module.sapp_emsc_target; Module.sokol_dragenter = (event) => { event.stopPropagation(); event.preventDefault(); @@ -4964,6 +4963,8 @@ EM_JS(void, sapp_js_add_dragndrop_listeners, (void), { // FIXME? see computation of targetX/targetY in emscripten via getClientBoundingRect __sapp_emsc_end_drop(event.clientX, event.clientY, mods); }; + \x2F\x2A\x2A @suppress {missingProperties} \x2A\x2F + const canvas = Module.sapp_emsc_target; canvas.addEventListener('dragenter', Module.sokol_dragenter, false); canvas.addEventListener('dragleave', Module.sokol_dragleave, false); canvas.addEventListener('dragover', Module.sokol_dragover, false); @@ -5004,6 +5005,7 @@ EM_JS(void, sapp_js_fetch_dropped_file, (int index, _sapp_html5_fetch_callback c }); EM_JS(void, sapp_js_remove_dragndrop_listeners, (void), { + \x2F\x2A\x2A @suppress {missingProperties} \x2A\x2F const canvas = Module.sapp_emsc_target; canvas.removeEventListener('dragenter', Module.sokol_dragenter); canvas.removeEventListener('dragleave', Module.sokol_dragleave); @@ -5012,14 +5014,13 @@ EM_JS(void, sapp_js_remove_dragndrop_listeners, (void), { }); EM_JS(void, sapp_js_init, (const char* c_str_target_selector), { - // lookup and store canvas object by name const target_selector_str = UTF8ToString(c_str_target_selector); Module.sapp_emsc_target = findCanvasEventTarget(target_selector_str); if (!Module.sapp_emsc_target) { - console.log("sokol_app.h: invalid target selector:" + target_str); + console.log("sokol_app.h: invalid target selector:" + target_selector_str); } if (!Module.sapp_emsc_target.requestPointerLock) { - console.log("sokol_app.h: target doesn't support requestPointerLock:" + target_str); + console.log("sokol_app.h: target doesn't support requestPointerLock:" + target_selector_str); } }); From 05190b6d4359eaa87b1e44f98e8460364f16fc24 Mon Sep 17 00:00:00 2001 From: Andre Weissflog Date: Fri, 22 Nov 2024 16:57:21 +0100 Subject: [PATCH 3/8] sokol_app.h html5: allow to initialize canvas from Module['canvas'] --- sokol_app.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/sokol_app.h b/sokol_app.h index 581d399d9..ca3e79ad8 100644 --- a/sokol_app.h +++ b/sokol_app.h @@ -5015,6 +5015,12 @@ EM_JS(void, sapp_js_remove_dragndrop_listeners, (void), { EM_JS(void, sapp_js_init, (const char* c_str_target_selector), { const target_selector_str = UTF8ToString(c_str_target_selector); + // check if canvas is provided via Module['canvas'], if yes make it visible + // in specialHTMLTargets[], this is an additional way to inject a canvas into + // sokol_app.h when it can't be found via document.querySelector() + if (Module['canvas']) { + specialHTMLTargets[target_selector_str] = Module['canvas']; + } Module.sapp_emsc_target = findCanvasEventTarget(target_selector_str); if (!Module.sapp_emsc_target) { console.log("sokol_app.h: invalid target selector:" + target_selector_str); From 33ff86de2949b6be8f6c8640dc130b251315970e Mon Sep 17 00:00:00 2001 From: Andre Weissflog Date: Sat, 23 Nov 2024 13:30:35 +0100 Subject: [PATCH 4/8] sokol_app.h html5: fix Module.canvas detection, better warnings, add doc section --- sokol_app.h | 99 ++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 91 insertions(+), 8 deletions(-) diff --git a/sokol_app.h b/sokol_app.h index ca3e79ad8..6b8d2b89c 100644 --- a/sokol_app.h +++ b/sokol_app.h @@ -998,6 +998,87 @@ To prevent individual events from bubbling, call sapp_consume_event() from within the sokol_app.h event callback when that specific event is reported. + + SETTING THE CANVAS OBJECT ON THE WEB PLATFORM + ============================================= + On the web, sokol_app.h and the Emscripten SDK functions need to find + the WebGL/WebGPU canvas intended for rendering and attaching event + handlers. This can happen in four ways: + + 1. do nothing and just set the id of the canvas object to 'canvas' (preferred) + 2. via a CSS Selector string (preferred) + 3. by setting the `Module.canvas` property to the canvas object + 4. by adding the canvas object to the global variable `specialHTMLTargets[]` + (this is a special variable used by the Emscripten runtime to lookup + event target objects for which document.querySelector() cannot be used) + + The easiest way is to just name your canvas object 'canvas': + + + + This works because the default css selector string used by sokol_app.h + is '#canvas'. + + If you name your canvas differently, you need to communicate that name to + sokol_app.h via `sapp_desc.html5_canvas_selector` as a regular css selector + string that's compatible with `document.querySelector()`. E.g. if your canvas + object looks like this: + + + + The `sapp_desc.html5_canvas_selector` string must be set to '#bla': + + .html5_canvas_selector = "#bla" + + If the canvas object cannot be looked up via `document.querySelector()` you + need to use one of the alternative methods, both involve the special + Emscripten runtime `Module` object which is usually setup in the index.html + like this before the WASM blob is loaded and instantiated: + + + + The first option is to set the `Module.canvas` property to your canvas object: + + + + When sokol_app.h initializes, it will check the global Module object whether + a `Module.canvas` property exists and is an object. This method will add + a new entry to the `specialHTMLTargets[]` object + + The other option is to add the canvas under a name chosen by you to the + special `specialHTMLTargets[]` map, which is used by the Emscripten runtime + to lookup 'event target objects' which are not visible to `document.querySelector()`. + Note that `specialHTMLTargets[]` must be updated after the Emscripten runtime + has started but before the WASM code is running. A good place for this is + the special `Module.preRun` array in index.html: + + + + In that case, pass the same string to sokol_app.h which is used as key + in the specialHTMLTargets[] map: + + .html5_canvas_selector = "my_canvas" + + If sokol_app.h can't find your canvas for some reason check for warning + messages on the browser console. + + OPTIONAL: DON'T HIJACK main() (#define SOKOL_NO_ENTRY) ====================================================== NOTE: SOKOL_NO_ENTRY and sapp_run() is currently not supported on Android. @@ -1147,7 +1228,6 @@ the standard logger in sokol_log.h instead, otherwise you won't see any warnings or errors. - TEMP NOTE DUMP ============== - sapp_desc needs a bool whether to initialize depth-stencil surface @@ -5015,18 +5095,21 @@ EM_JS(void, sapp_js_remove_dragndrop_listeners, (void), { EM_JS(void, sapp_js_init, (const char* c_str_target_selector), { const target_selector_str = UTF8ToString(c_str_target_selector); - // check if canvas is provided via Module['canvas'], if yes make it visible - // in specialHTMLTargets[], this is an additional way to inject a canvas into - // sokol_app.h when it can't be found via document.querySelector() - if (Module['canvas']) { - specialHTMLTargets[target_selector_str] = Module['canvas']; + // Special way to lookup the canvas by setting Module['canvas'] outside + // the WASM, this may be useful when the canvas can't be setup via document.querySelector() + if (Module['canvas'] !== undefined) { + if (typeof Module['canvas'] === 'object') { + specialHTMLTargets[target_selector_str] = Module['canvas']; + } else { + console.warn("sokol_app.h: Module['canvas'] is set but is not an object"); + } } Module.sapp_emsc_target = findCanvasEventTarget(target_selector_str); if (!Module.sapp_emsc_target) { - console.log("sokol_app.h: invalid target selector:" + target_selector_str); + console.warn("sokol_app.h: can't find html5_canvas_selector ", target_selector_str); } if (!Module.sapp_emsc_target.requestPointerLock) { - console.log("sokol_app.h: target doesn't support requestPointerLock:" + target_selector_str); + console.warn("sokol_app.h: target doesn't support requestPointerLock: ", target_selector_str); } }); From 2e324c89f8d7fb14ba8c062704bc549ba2b6e4d4 Mon Sep 17 00:00:00 2001 From: Andre Weissflog Date: Sat, 23 Nov 2024 13:44:20 +0100 Subject: [PATCH 5/8] sokol_app.h html5: remove misleading comment --- sokol_app.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/sokol_app.h b/sokol_app.h index 6b8d2b89c..51f8a421d 100644 --- a/sokol_app.h +++ b/sokol_app.h @@ -5095,8 +5095,6 @@ EM_JS(void, sapp_js_remove_dragndrop_listeners, (void), { EM_JS(void, sapp_js_init, (const char* c_str_target_selector), { const target_selector_str = UTF8ToString(c_str_target_selector); - // Special way to lookup the canvas by setting Module['canvas'] outside - // the WASM, this may be useful when the canvas can't be setup via document.querySelector() if (Module['canvas'] !== undefined) { if (typeof Module['canvas'] === 'object') { specialHTMLTargets[target_selector_str] = Module['canvas']; From 987b011390b43cf06ced8c4b8642e72de1462652 Mon Sep 17 00:00:00 2001 From: Andre Weissflog Date: Sat, 23 Nov 2024 14:10:28 +0100 Subject: [PATCH 6/8] update changelog (https://github.com/floooh/sokol/pull/1159) --- CHANGELOG.md | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d39522615..95d712fb2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,33 @@ ## Updates +### 23-Nov-2024 + +- sokol_app.h html5: Merged PR https://github.com/floooh/sokol/pull/1159 (related + issue https://github.com/floooh/sokol/issues/1154). + + This cleans up code that is concerned about finding the HTML canvas to + render to by: + + - removing any leftover hacks from the time when Emscripten moved + from `document.getElementById()` to `document.querySelector()` for + looking up the canvas object + - adding two options for canvas objects that can't be looked up via + `document.querySelector()` + + If you don't provide a custom canvas name to sokol_app.h this change + is non-breaking. Otherwise: + + - in sokol_main(): change `.html5_canvas_name` to `.html5_canvas_selector` + - change the canvas name string to a CSS selector string (e.g. + from `"my_canvas"` to `"#my_canvas"`) + + For more options to communicate the HTML canvas object to sokol_app.h, + please read the new doc section `SETTING THE CANVAS OBJECT ON THE WEB PLATFORM` in sokol_app.h. + + Many thanks to @konsumer for kicking off the feature and the following + discussion :) + + ### 19-Nov-2024 - Merged PR https://github.com/floooh/sokol/pull/1155, this allows to use From adc7174ec58eeb94f7d3a4ebde9a4db4a28b7897 Mon Sep 17 00:00:00 2001 From: Andre Weissflog Date: Sat, 23 Nov 2024 14:12:08 +0100 Subject: [PATCH 7/8] update changelog --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 95d712fb2..0d6666eb3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,6 +24,11 @@ For more options to communicate the HTML canvas object to sokol_app.h, please read the new doc section `SETTING THE CANVAS OBJECT ON THE WEB PLATFORM` in sokol_app.h. + Additionally, please also note the simplified `shell.html` in the + sokol-samples repository (some outdated cruft has been removed): + + https://github.com/floooh/sokol-samples/blob/master/webpage/shell.html + Many thanks to @konsumer for kicking off the feature and the following discussion :) From d56a553235252b7b5e1b2e9f7bce5e71caf8838e Mon Sep 17 00:00:00 2001 From: Andre Weissflog Date: Sat, 23 Nov 2024 14:12:53 +0100 Subject: [PATCH 8/8] tweak changelog --- CHANGELOG.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0d6666eb3..606b626af 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,8 +5,7 @@ - sokol_app.h html5: Merged PR https://github.com/floooh/sokol/pull/1159 (related issue https://github.com/floooh/sokol/issues/1154). - This cleans up code that is concerned about finding the HTML canvas to - render to by: + This cleans up code that is concerned about finding the WebGL/WebGPU HTML canvas by: - removing any leftover hacks from the time when Emscripten moved from `document.getElementById()` to `document.querySelector()` for