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) diff --git a/emcc.c b/emcc.c index 5338dd4..60ef8c8 100644 --- a/emcc.c +++ b/emcc.c @@ -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. @@ -121,6 +124,11 @@ void resize_puzzle(int w, int h); 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; } } @@ -925,6 +946,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() @@ -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 diff --git a/emcclib.js b/emcclib.js index 80d17b9..d161c49 100644 --- a/emcclib.js +++ b/emcclib.js @@ -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); + } } }); diff --git a/emccpre.js b/emccpre.js index 7c68445..a114ce1 100644 --- a/emccpre.js +++ b/emccpre.js @@ -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
  • Type
  • 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{