diff --git a/cmake/platforms/emscripten.cmake b/cmake/platforms/emscripten.cmake index 3cbcab4..70aa5c9 100644 --- a/cmake/platforms/emscripten.cmake +++ b/cmake/platforms/emscripten.cmake @@ -42,7 +42,7 @@ string(JOIN "," emcc_export_string ${emcc_export_list}) set(CMAKE_C_LINK_FLAGS "\ -s ALLOW_MEMORY_GROWTH=1 \ -s EXPORTED_FUNCTIONS='[${emcc_export_string}]' \ --s EXTRA_EXPORTED_RUNTIME_METHODS='[cwrap,callMain]' \ +-s EXTRA_EXPORTED_RUNTIME_METHODS='[cwrap]' \ -s STRICT_JS=1") if(WASM) set(CMAKE_C_LINK_FLAGS "${CMAKE_C_LINK_FLAGS} -s WASM=1") @@ -63,7 +63,6 @@ endfunction() function(set_platform_puzzle_target_properties NAME TARGET) em_link_pre_js(${TARGET} ${CMAKE_SOURCE_DIR}/emccpre.js) em_link_js_library(${TARGET} ${CMAKE_SOURCE_DIR}/emcclib.js) - em_link_post_js(${TARGET} ${CMAKE_SOURCE_DIR}/emccpost.js) endfunction() function(build_platform_extras) diff --git a/emcc.c b/emcc.c index def12ac..393b28c 100644 --- a/emcc.c +++ b/emcc.c @@ -43,6 +43,8 @@ /* * Extern references to Javascript functions provided in emcclib.js. */ +extern void js_init_puzzle(void); +extern void js_post_init(void); extern void js_debug(const char *); extern void js_error_box(const char *message); extern void js_remove_type_dropdown(void); @@ -936,6 +938,11 @@ int main(int argc, char **argv) float *colours; int i; + /* + * Initialise JavaScript event handlers. + */ + js_init_puzzle(); + /* * Instantiate a midend. */ @@ -1040,6 +1047,11 @@ int main(int argc, char **argv) if (param_err) js_error_box(param_err); + /* + * Reveal the puzzle! + */ + js_post_init(); + /* * Done. Return to JS, and await callbacks! */ diff --git a/emcclib.js b/emcclib.js index 98c20f6..676c429 100644 --- a/emcclib.js +++ b/emcclib.js @@ -16,6 +16,23 @@ */ mergeInto(LibraryManager.library, { + /* + * void js_init_puzzle(void); + * + * Called at the start of main() to set up event handlers. + */ + js_init_puzzle: function() { + initPuzzle(); + }, + /* + * void js_post_init(void); + * + * Called at the end of main() once the initial puzzle has been + * started. + */ + js_post_init: function() { + post_init(); + }, /* * void js_debug(const char *message); * diff --git a/emccpost.js b/emccpost.js deleted file mode 100644 index b12b0af..0000000 --- a/emccpost.js +++ /dev/null @@ -1 +0,0 @@ -initPuzzle(); diff --git a/emccpre.js b/emccpre.js index 240cb98..38e0a4a 100644 --- a/emccpre.js +++ b/emccpre.js @@ -31,11 +31,8 @@ var ctx; var update_xmin, update_xmax, update_ymin, update_ymax; // Module object for Emscripten. We fill in these parameters to ensure -// that Module.run() won't be called until we're ready (we want to do -// our own init stuff first), and that when main() returns nothing -// will get cleaned up so we remain able to call the puzzle's various -// callbacks. -// +// that when main() returns nothing will get cleaned up so we remain +// able to call the puzzle's various callbacks. // // Page loading order: // @@ -49,39 +46,45 @@ var update_xmin, update_xmax, update_ymin, update_ymax; // // 3. The HTML finishes loading. The browser is about to fire the // `DOMContentLoaded` event (ie `onload`) but before that, it -// actually runs the deferred JS. THis consists of +// actually runs the deferred JS. This consists of // // (i) emccpre.js (this file). This sets up various JS variables -// including the emscripten Module object. +// including the emscripten Module object, which includes the +// environment variables and argv seen by main(). // // (ii) emscripten's JS. This starts the WASM loading. // -// (iii) emccpost.js. This calls initPuzzle, which is defined here -// in this file. initPuzzle: -// -// (a) finds various DOM elements and bind them to variables, -// which depend on the HTML having loaded (it has). -// -// (b) makes various `cwrap` calls into the emscripten module to -// set up hooks; this depends on the emscripten JS having been -// loaded (it has). -// -// (c) Makes the call to emscripten's -// Module.onRuntimeInitialized, which sets the callback for when -// the WASM has finished loading and initialising. This has to -// come before the WASM finishes loading, or we'll miss the -// callback. We are executing synchronously here in the same JS -// file as started the WASM loading, so that is guaranteed. -// // When this JS execution is complete, the browser fires the `onload` // event. This is ignored. It continues loading the WASM. // -// 4. The WASM loading and initialisation completes. The -// onRuntimeInitialised callback calls into emscripten-generated -// WASM to call the C `main`, to actually start the puzzle. +// 4. The WASM loading and initialisation completes. Emscripten's +// runtime calls the C `main` to actually start the puzzle. It +// then calls initPuzzle, which: +// +// (a) finds various DOM elements and bind them to variables, +// which depends on the HTML having loaded (it has). +// +// (b) makes various `cwrap` calls into the emscripten module to +// set up hooks; this depends on the emscripten JS having been +// loaded (it has). var Module = { - 'noInitialRun': true, + 'preRun': function() { + // Merge environment variables from HTML script element. + // This means you can add something like this to the HTML: + // + var envscript = document.getElementById("environment"); + var k, v; + if (envscript !== null) + for ([k, v] of + Object.entries(JSON.parse(envscript.textContent))) + ENV[k] = v; + }, + // Pass argv[1] as the fragment identifier (so that permalinks of + // the form puzzle.html#game-id can launch the specified id). + 'arguments': [decodeURIComponent(location.hash)], 'noExitRuntime': true }; @@ -261,7 +264,7 @@ function dialog_cleanup() { onscreen_canvas.focus(); } -// Init function called from body.onload. +// Init function called early in main(). function initPuzzle() { // Construct the off-screen canvas used for double buffering. onscreen_canvas = document.getElementById("puzzlecanvas"); @@ -686,22 +689,7 @@ function initPuzzle() { }); } - /* - * Arrange to detect changes of device pixel ratio. Adapted from - * (CC0) to work on older browsers. - */ var rescale_puzzle = Module.cwrap('rescale_puzzle', 'void', []); - var mql = null; - var update_pixel_ratio = function() { - var dpr = window.devicePixelRatio; - if (mql !== null) - mql.removeListener(update_pixel_ratio); - mql = window.matchMedia(`(resolution: ${dpr}dppx)`); - mql.addListener(update_pixel_ratio); - rescale_puzzle(); - } - /* * If the puzzle is sized to fit the page, try to detect changes * of size of the containing element. Ideally this would use a @@ -720,36 +708,34 @@ function initPuzzle() { window.addEventListener("load", resize_handler); } - Module.preRun = function() { - // Merge environment variables from HTML script element. - // This means you can add something like this to the HTML: - // - var envscript = document.getElementById("environment"); - var k, v; - if (envscript !== null) - for ([k, v] of - Object.entries(JSON.parse(envscript.textContent))) - ENV[k] = v; - }; - - Module.onRuntimeInitialized = function() { - // Run the C setup function, passing argv[1] as the fragment - // identifier (so that permalinks of the form puzzle.html#game-id - // can launch the specified id). - Module.callMain([decodeURIComponent(location.hash)]); - - update_pixel_ratio(); - // And if we get here with everything having gone smoothly, i.e. - // we haven't crashed for one reason or another during setup, then - // it's probably safe to hide the 'sorry, no puzzle here' div and - // show the div containing the actual puzzle. - var apology = document.getElementById("apology"); - if (apology !== null) apology.style.display = "none"; - document.getElementById("puzzle").style.display = ""; - - // Default to giving keyboard focus to the puzzle. - onscreen_canvas.focus(); - }; +} + +function post_init() { + /* + * Arrange to detect changes of device pixel ratio. Adapted from + * (CC0) to work on older browsers. + */ + var rescale_puzzle = Module.cwrap('rescale_puzzle', 'void', []); + var mql = null; + var update_pixel_ratio = function() { + var dpr = window.devicePixelRatio; + if (mql !== null) + mql.removeListener(update_pixel_ratio); + mql = window.matchMedia(`(resolution: ${dpr}dppx)`); + mql.addListener(update_pixel_ratio); + rescale_puzzle(); + } + + update_pixel_ratio(); + // If we get here with everything having gone smoothly, i.e. + // we haven't crashed for one reason or another during setup, then + // it's probably safe to hide the 'sorry, no puzzle here' div and + // show the div containing the actual puzzle. + var apology = document.getElementById("apology"); + if (apology !== null) apology.style.display = "none"; + document.getElementById("puzzle").style.display = ""; + + // Default to giving keyboard focus to the puzzle. + onscreen_canvas.focus(); }