diff options
| -rw-r--r-- | cmake/platforms/emscripten.cmake | 2 | ||||
| -rw-r--r-- | emcc.c | 84 | ||||
| -rw-r--r-- | emcclib.js | 23 | ||||
| -rw-r--r-- | emccpre.js | 12 | ||||
| -rwxr-xr-x | html/jspage.pl | 1 | ||||
| -rw-r--r-- | puzzles.but | 8 |
6 files changed, 124 insertions, 6 deletions
diff --git a/cmake/platforms/emscripten.cmake b/cmake/platforms/emscripten.cmake index c84a3c7..d74217f 100644 --- a/cmake/platforms/emscripten.cmake +++ b/cmake/platforms/emscripten.cmake @@ -47,6 +47,8 @@ set(emcc_export_list _restore_puzzle_size # Callback when device pixel ratio changes _rescale_puzzle + # Callback for loading user preferences + _prefs_load_callback # Main program, run at initialisation time _main) @@ -101,6 +101,9 @@ extern void js_focus_canvas(void); extern bool js_savefile_read(void *buf, int len); +extern void js_save_prefs(const char *); +extern void js_load_prefs(midend *); + /* * These functions are called from JavaScript, so their prototypes * need to be kept in sync with emccpre.js. @@ -122,6 +125,11 @@ void restore_puzzle_size(int w, int h); void rescale_puzzle(void); /* + * Internal forward references. + */ +static void save_prefs(midend *me); + +/* * Call JS to get the date, and use that to initialise our random * number generator to invent the first game seed. */ @@ -743,10 +751,20 @@ static void cfg_end(bool use_results) * open for the user to adjust them and try again. */ js_error_box(err); + } else if (cfg_which == CFG_PREFS) { + /* + * Acceptable settings for user preferences: enact them + * without blowing away the current game. + */ + resize(); + midend_redraw(me); + free_cfg(cfg); + js_dialog_cleanup(); + save_prefs(me); } else { /* - * New settings are fine; start a new game and close the - * dialog. + * Acceptable settings for the remaining configuration + * types: start a new game and close the dialog. */ select_appropriate_preset(); midend_new_game(me); @@ -849,6 +867,9 @@ void command(int n) post_move(); js_focus_canvas(); break; + case 10: /* user preferences */ + cfg_start(CFG_PREFS); + break; } } @@ -926,6 +947,64 @@ void load_game(void) } /* ---------------------------------------------------------------------- + * Functions to load and save preferences, calling out to JS to access + * the appropriate localStorage slot. + */ + +static void save_prefs(midend *me) +{ + struct savefile_write_ctx ctx; + size_t size; + + /* First pass, to count up the size */ + ctx.buffer = NULL; + ctx.pos = 0; + midend_save_prefs(me, savefile_write, &ctx); + size = ctx.pos; + + /* Second pass, to actually write out the data. As with + * get_save_file, we append a terminating \0. */ + ctx.buffer = snewn(size+1, char); + ctx.pos = 0; + midend_save_prefs(me, savefile_write, &ctx); + assert(ctx.pos == size); + ctx.buffer[ctx.pos] = '\0'; + + js_save_prefs(ctx.buffer); + + sfree(ctx.buffer); +} + +struct prefs_read_ctx { + const char *buffer; + size_t pos, len; +}; + +static bool prefs_read(void *vctx, void *buf, int len) +{ + struct prefs_read_ctx *ctx = (struct prefs_read_ctx *)vctx; + + if (len < 0) + return false; + if (ctx->len - ctx->pos < len) + return false; + memcpy(buf, ctx->buffer + ctx->pos, len); + ctx->pos += len; + return true; +} + +void prefs_load_callback(midend *me, const char *prefs) +{ + struct prefs_read_ctx ctx; + + ctx.buffer = prefs; + ctx.len = strlen(prefs); + ctx.pos = 0; + + midend_load_prefs(me, prefs_read, &ctx); +} + +/* ---------------------------------------------------------------------- * Setup function called at page load time. It's called main() because * that's the most convenient thing in Emscripten, but it's not main() * in the usual sense of bounding the program's entire execution. @@ -948,6 +1027,7 @@ int main(int argc, char **argv) * Instantiate a midend. */ me = midend_new(NULL, &thegame, &js_drawing, NULL); + js_load_prefs(me); /* * Chuck in the HTML fragment ID if we have one (trimming the @@ -792,5 +792,28 @@ mergeInto(LibraryManager.library, { */ js_savefile_read: function(buf, len) { return savefile_read_callback(buf, len); + }, + + /* + * void js_save_prefs(const char *); + * + * Write a buffer of serialised preferences data into localStorage. + */ + js_save_prefs: function(buf) { + var prefsdata = UTF8ToString(buf); + localStorage.setItem(location.pathname + "#preferences", prefsdata); + }, + + /* + * void js_load_prefs(midend *); + * + * Retrieve preferences data from localStorage. If there is any, + * pass it back in as a string, via prefs_load_callback. + */ + js_load_prefs: function(me) { + var prefsdata = localStorage.getItem(location.pathname+"#preferences"); + if (prefsdata !== undefined && prefsdata !== null) { + prefs_load_callback(me, prefsdata); + } } }); @@ -141,6 +141,11 @@ var dlg_return_sval, dlg_return_ival; // process of loading at a time. var savefile_read_callback; +// void prefs_load_callback(midend *me, const char *prefs); +// +// Callback for passing in preferences data retrieved from localStorage. +var prefs_load_callback; + // The <ul> object implementing the game-type drop-down, and a list of // the sub-lists inside it. Used by js_add_preset(). var gametypelist = document.getElementById("gametype"); @@ -161,6 +166,7 @@ var permalink_desc = document.getElementById("permalink-desc"); // The various buttons. Undo and redo are used by js_enable_undo_redo(). var specific_button = document.getElementById("specific"); var random_button = document.getElementById("random"); +var prefs_button = document.getElementById("prefs"); var new_button = document.getElementById("new"); var restart_button = document.getElementById("restart"); var undo_button = document.getElementById("undo"); @@ -425,6 +431,10 @@ function initPuzzle() { if (dlg_dimmer === null) command(9); }; + if (prefs_button) prefs_button.onclick = function(event) { + if (dlg_dimmer === null) + command(10); + }; // 'number' is used for C pointers var get_save_file = Module.cwrap('get_save_file', 'number', []); @@ -682,6 +692,8 @@ function initPuzzle() { dlg_return_ival = Module.cwrap('dlg_return_ival', 'void', ['number','number']); timer_callback = Module.cwrap('timer_callback', 'void', ['number']); + prefs_load_callback = Module.cwrap('prefs_load_callback', 'void', + ['number','string']); if (resizable_div !== null) { var resize_handle = document.getElementById("resizehandle"); diff --git a/html/jspage.pl b/html/jspage.pl index de70b30..5c1689e 100755 --- a/html/jspage.pl +++ b/html/jspage.pl @@ -340,6 +340,7 @@ ${unfinishedpara} <li><button type="button" id="random">Enter random seed...</button></li> <li><button type="button" id="save">Download save file...</button></li> <li><button type="button" id="load">Upload save file...</button></li> + <li><button type="button" id="prefs">Preferences...</button></li> </ul></div></li> <li><div tabindex="0">Type<ul role="menu" id="gametype"></ul></div></li> <li role="separator"></li> diff --git a/puzzles.but b/puzzles.but index be55f8e..1e0ebb7 100644 --- a/puzzles.but +++ b/puzzles.but @@ -179,10 +179,10 @@ solving it yourself after seeing the answer, you can just press Undo. \dt \i\e{Preferences} -\dd Where supported (currently on Windows, Unix and MacOS), brings up -a dialog allowing you to configure personal preferences about a -particular game. Some of these preferences will be specific to a -particular game; others will be common to all games. +\dd Where supported, brings up a dialog allowing you to configure +personal preferences about a particular game. Some of these +preferences will be specific to a particular game; others will be +common to all games. \lcont{ |