aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSimon Tatham <anakin@pobox.com>2023-04-21 15:50:05 +0100
committerSimon Tatham <anakin@pobox.com>2023-04-23 13:25:06 +0100
commit0058331aeb027f7441a04d99cc7c1e445bd896d9 (patch)
treedbc122795b93195c7bb1b7c7b51e50927b547415
parent0d1a1f08bac25a4641c38a8e42dfa6e2bb9981d7 (diff)
downloadpuzzles-0058331aeb027f7441a04d99cc7c1e445bd896d9.zip
puzzles-0058331aeb027f7441a04d99cc7c1e445bd896d9.tar.gz
puzzles-0058331aeb027f7441a04d99cc7c1e445bd896d9.tar.bz2
puzzles-0058331aeb027f7441a04d99cc7c1e445bd896d9.tar.xz
New backend functions: get_prefs and set_prefs.
These are similar to the existing pair configure() and custom_params() in that get_prefs() returns an array of config_item describing a set of dialog-box controls to present to the user, and set_prefs() receives the same array with answers filled in and implements the answers. But where configure() and custom_params() operate on a game_params structure, the new pair operate on a game_ui, and are intended to permit GUI configuration of all the settings I just moved into that structure. However, nothing actually _calls_ these routines yet. All I've done in this commit is to add them to 'struct game' and implement them for the functions that need them. Also, config_item has new fields, permitting each config option to define a machine-readable identifying keyword as well as the user-facing description. For options of type C_CHOICES, each choice also has a keyword. These keyword fields are only defined at all by the new get_prefs() function - they're left uninitialised in existing uses of the dialog system. The idea is to use them when writing out the user's preferences into a configuration file on disk, although I haven't actually done any of that work in this commit.
-rw-r--r--blackbox.c1
-rw-r--r--bridges.c1
-rw-r--r--cube.c1
-rw-r--r--devel.but114
-rw-r--r--dominosa.c1
-rw-r--r--fifteen.c25
-rw-r--r--filling.c1
-rw-r--r--flip.c1
-rw-r--r--flood.c1
-rw-r--r--galaxies.c1
-rw-r--r--guess.c1
-rw-r--r--inertia.c1
-rw-r--r--keen.c1
-rw-r--r--lightup.c23
-rw-r--r--loopy.c32
-rw-r--r--magnets.c1
-rw-r--r--map.c25
-rw-r--r--mines.c1
-rw-r--r--mosaic.c1
-rw-r--r--net.c1
-rw-r--r--netslide.c1
-rw-r--r--nullgame.c1
-rw-r--r--palisade.c1
-rw-r--r--pattern.c1
-rw-r--r--pearl.c25
-rw-r--r--pegs.c1
-rw-r--r--puzzles.h16
-rw-r--r--range.c26
-rw-r--r--rect.c1
-rw-r--r--samegame.c1
-rw-r--r--signpost.c25
-rw-r--r--singles.c1
-rw-r--r--sixteen.c1
-rw-r--r--slant.c25
-rw-r--r--solo.c1
-rw-r--r--tents.c1
-rw-r--r--towers.c25
-rw-r--r--tracks.c1
-rw-r--r--twiddle.c1
-rw-r--r--undead.c1
-rw-r--r--unequal.c1
-rw-r--r--unfinished/group.c1
-rw-r--r--unfinished/separate.c1
-rw-r--r--unfinished/slide.c1
-rw-r--r--unfinished/sokoban.c1
-rw-r--r--unruly.c1
-rw-r--r--untangle.c1
47 files changed, 378 insertions, 19 deletions
diff --git a/blackbox.c b/blackbox.c
index 114a2ac..1b7241d 100644
--- a/blackbox.c
+++ b/blackbox.c
@@ -1556,6 +1556,7 @@ const struct game thegame = {
free_game,
true, solve_game,
false, NULL, NULL, /* can_format_as_text_now, text_format */
+ NULL, NULL, /* get_prefs, set_prefs */
new_ui,
free_ui,
encode_ui,
diff --git a/bridges.c b/bridges.c
index ce79640..acb64dc 100644
--- a/bridges.c
+++ b/bridges.c
@@ -3289,6 +3289,7 @@ const struct game thegame = {
free_game,
true, solve_game,
true, game_can_format_as_text_now, game_text_format,
+ NULL, NULL, /* get_prefs, set_prefs */
new_ui,
free_ui,
NULL, /* encode_ui */
diff --git a/cube.c b/cube.c
index 41057fc..c4073b7 100644
--- a/cube.c
+++ b/cube.c
@@ -1760,6 +1760,7 @@ const struct game thegame = {
free_game,
false, NULL, /* solve */
false, NULL, NULL, /* can_format_as_text_now, text_format */
+ NULL, NULL, /* get_prefs, set_prefs */
new_ui,
free_ui,
NULL, /* encode_ui */
diff --git a/devel.but b/devel.but
index 0fb1a14..68a0df2 100644
--- a/devel.but
+++ b/devel.but
@@ -332,23 +332,49 @@ game ID etc). It persists until the user finishes playing that game
and begins another one (or closes the window); in particular,
\q{Restart Game} does \e{not} destroy the \c{game_ui}.
-\c{game_ui} is useful for implementing user-interface state which is
-not part of \c{game_state}. Common examples are keyboard control
-(you wouldn't want to have to separately Undo through every cursor
-motion) and mouse dragging. See \k{writing-keyboard-cursor} and
-\k{writing-howto-dragging}, respectively, for more details.
-
-Another use for \c{game_ui} is to store highly persistent data such
-as the Mines death counter. This is conceptually rather different:
-where the Net cursor position was \e{not important enough} to
-preserve for the player to restore by Undo, the Mines death counter
-is \e{too important} to permit the player to revert by Undo!
-
-A final use for \c{game_ui} is to pass information to the redraw
-function about recent changes to the game state. This is used in
-Mines, for example, to indicate whether a requested \q{flash} should
-be a white flash for victory or a red flash for defeat; see
-\k{writing-flash-types}.
+There are various things that you might store in \c{game_ui}, which
+are conceptually different from each other, but I haven't yet found a
+need to split them out into smaller sub-structures for different
+purposes:
+
+\dt Transient UI state:
+
+\dd Storing a piece of UI state in \c{game_state} means that you can
+only update it by appending a move to the undo chain. Some UI state
+shouldn't really be treated this way. For example, if your puzzle has
+a keyboard-controlled cursor, you probably don't want every cursor
+movement to be an undoable action, because the history of where the
+cursor went just isn't interesting. More likely the cursor should just
+move freely, and the only undoable actions are the ones where you
+modify the element under the cursor. So you'd store the cursor
+position in \c{game_ui} rather than \c{game_state}. See
+\k{writing-keyboard-cursor} for more details.
+
+\lcont{ Another example of this is the state of an ongoing mouse drag.
+If there's an undoable action involved, it will probably occur when
+the drag is released. In between, you still need to store state that
+the redraw function will use to update the display \dash and that can
+live in \c{game_ui}. See \k{writing-howto-dragging} for more details
+of this. }
+
+\dt Persistent UI state:
+
+\dd An example of this is the counter of deaths in Mines or Inertia.
+This shouldn't be reverted by pressing Undo, for the opposite reason
+to the cursor position: the cursor position is too boring to store the
+history of, but the deaths counter is too \e{important}!
+
+\dt Information about recent changes to the game state:
+
+\dd This is used in Mines, for example, to indicate whether a
+requested \q{flash} should be a white flash for victory or a red flash
+for defeat; see \k{writing-flash-types}.
+
+\dt User preferences:
+
+\dd Any user preference about display or UI handled by
+\cw{get_prefs()} and \cw{set_prefs()} will need to live in
+\c{game_ui}, because that's the structure that those functions access.
\H{backend-simple} Simple data in the back end
@@ -579,7 +605,8 @@ its initial value; the front end will modify the value fields and
return the updated array to \cw{custom_params()} (see
\k{backend-custom-params}).
-The \cw{config_item} structure contains the following elements:
+The \cw{config_item} structure contains the following elements used by
+this function:
\c const char *name;
\c int type;
@@ -688,6 +715,57 @@ the dialog box will stay open.)
If the game's \c{can_configure} flag is set to \cw{false}, this
function is never called and can be \cw{NULL}.
+\S{backend-get-prefs} \cw{get_prefs()}
+
+\c config_item *(*get_prefs)(game_ui *ui);
+
+This function works very like \cw{configure()}, but instead of
+receiving a \c{game_params} and returning GUI elements describing the
+data in it, this function receives a \c{game_ui} and returns GUI
+elements describing any user preferences stored in that.
+
+This function should only deal with fields of \c{game_ui} that are
+user-settable preferences. In-game state like cursor position and
+mouse drags, or per-game state like death counters, are nothing to do
+with this function.
+
+If there are no user preferences, you can set both this function
+pointer and \c{set_prefs} to \cw{NULL}.
+
+In every \c{config_item} returned from this function, you must set an
+additional field beyond the ones described in \k{backend-configure}:
+
+\c const char *kw;
+
+This should be an identifying keyword for the user preference in
+question, suitable for use in configuration files. That means it
+should remain stable, even if the user-facing wording in the \c{name}
+field is reworded for clarity. If it doesn't stay stable, old
+configuration files will not be read correctly.
+
+For \c{config_item}s of type \cw{C_CHOICES}, you must also set an
+extra field in \c{u.choices}:
+
+\c const char *choicekws;
+
+This has the same structure as the \c{choicenames} field (a list of
+values delimited by the first character in the whole string), and it
+provides an identifying keyword for each individual choice in the
+list, in the same order as the entries of \c{choicenames}.
+
+\S{backend-set-prefs} \cw{set_prefs()}
+
+\c void (*set_prefs)(game_ui *ui, const config_item *cfg);
+
+This function is the counterpart to \cw{set_prefs()}, as
+\cw{custom_params()} is to \cw{configure()}. It receives an array of
+\c{config_item}s which was originally created by \cw{get_prefs()},
+with the controls' values updated from user input, and it should
+transcribe the new settings into the provided \c{game_ui}.
+
+If there are no user preferences, you can set both this function
+pointer and \c{get_prefs} to \cw{NULL}.
+
\S{backend-validate-params} \cw{validate_params()}
\c const char *(*validate_params)(const game_params *params,
diff --git a/dominosa.c b/dominosa.c
index 8589018..e1bd96a 100644
--- a/dominosa.c
+++ b/dominosa.c
@@ -3442,6 +3442,7 @@ const struct game thegame = {
free_game,
true, solve_game,
true, game_can_format_as_text_now, game_text_format,
+ NULL, NULL, /* get_prefs, set_prefs */
new_ui,
free_ui,
NULL, /* encode_ui */
diff --git a/fifteen.c b/fifteen.c
index d02d0c0..a454a13 100644
--- a/fifteen.c
+++ b/fifteen.c
@@ -484,6 +484,30 @@ static game_ui *new_ui(const game_state *state)
return ui;
}
+static config_item *get_prefs(game_ui *ui)
+{
+ config_item *ret;
+
+ ret = snewn(2, config_item);
+
+ ret[0].name = "Sense of arrow keys";
+ ret[0].kw = "arrow-semantics";
+ ret[0].type = C_CHOICES;
+ ret[0].u.choices.choicenames = ":Move the tile:Move the gap";
+ ret[0].u.choices.choicekws = ":tile:gap";
+ ret[0].u.choices.selected = ui->invert_cursor;
+
+ ret[1].name = NULL;
+ ret[1].type = C_END;
+
+ return ret;
+}
+
+static void set_prefs(game_ui *ui, const config_item *cfg)
+{
+ ui->invert_cursor = cfg[0].u.choices.selected;
+}
+
static void free_ui(game_ui *ui)
{
sfree(ui);
@@ -1122,6 +1146,7 @@ const struct game thegame = {
free_game,
true, solve_game,
true, game_can_format_as_text_now, game_text_format,
+ get_prefs, set_prefs,
new_ui,
free_ui,
NULL, /* encode_ui */
diff --git a/filling.c b/filling.c
index 970301c..09156b0 100644
--- a/filling.c
+++ b/filling.c
@@ -2174,6 +2174,7 @@ const struct game thegame = {
free_game,
true, solve_game,
true, game_can_format_as_text_now, game_text_format,
+ NULL, NULL, /* get_prefs, set_prefs */
new_ui,
free_ui,
NULL, /* encode_ui */
diff --git a/flip.c b/flip.c
index 10a71b0..1a24e81 100644
--- a/flip.c
+++ b/flip.c
@@ -1337,6 +1337,7 @@ const struct game thegame = {
free_game,
true, solve_game,
true, game_can_format_as_text_now, game_text_format,
+ NULL, NULL, /* get_prefs, set_prefs */
new_ui,
free_ui,
NULL, /* encode_ui */
diff --git a/flood.c b/flood.c
index 6274252..87d0bf2 100644
--- a/flood.c
+++ b/flood.c
@@ -1365,6 +1365,7 @@ const struct game thegame = {
free_game,
true, solve_game,
true, game_can_format_as_text_now, game_text_format,
+ NULL, NULL, /* get_prefs, set_prefs */
new_ui,
free_ui,
NULL, /* encode_ui */
diff --git a/galaxies.c b/galaxies.c
index dfa1274..ffc64e3 100644
--- a/galaxies.c
+++ b/galaxies.c
@@ -4129,6 +4129,7 @@ const struct game thegame = {
true, solve_game,
#endif
true, game_can_format_as_text_now, game_text_format,
+ NULL, NULL, /* get_prefs, set_prefs */
new_ui,
free_ui,
NULL, /* encode_ui */
diff --git a/guess.c b/guess.c
index 27aeabd..a59e54e 100644
--- a/guess.c
+++ b/guess.c
@@ -1518,6 +1518,7 @@ const struct game thegame = {
free_game,
true, solve_game,
false, NULL, NULL, /* can_format_as_text_now, text_format */
+ NULL, NULL, /* get_prefs, set_prefs */
new_ui,
free_ui,
encode_ui,
diff --git a/inertia.c b/inertia.c
index 4ac3eae..7a97562 100644
--- a/inertia.c
+++ b/inertia.c
@@ -2231,6 +2231,7 @@ const struct game thegame = {
free_game,
true, solve_game,
true, game_can_format_as_text_now, game_text_format,
+ NULL, NULL, /* get_prefs, set_prefs */
new_ui,
free_ui,
encode_ui,
diff --git a/keen.c b/keen.c
index 0b28a90..333164f 100644
--- a/keen.c
+++ b/keen.c
@@ -2479,6 +2479,7 @@ const struct game thegame = {
free_game,
true, solve_game,
false, NULL, NULL, /* can_format_as_text_now, text_format */
+ NULL, NULL, /* get_prefs, set_prefs */
new_ui,
free_ui,
NULL, /* encode_ui */
diff --git a/lightup.c b/lightup.c
index 779437b..39ada9f 100644
--- a/lightup.c
+++ b/lightup.c
@@ -1867,6 +1867,28 @@ static game_ui *new_ui(const game_state *state)
return ui;
}
+static config_item *get_prefs(game_ui *ui)
+{
+ config_item *ret;
+
+ ret = snewn(2, config_item);
+
+ ret[0].name = "Draw non-light marks even when lit";
+ ret[0].kw = "show-lit-blobs";
+ ret[0].type = C_BOOLEAN;
+ ret[0].u.boolean.bval = ui->draw_blobs_when_lit;
+
+ ret[1].name = NULL;
+ ret[1].type = C_END;
+
+ return ret;
+}
+
+static void set_prefs(game_ui *ui, const config_item *cfg)
+{
+ ui->draw_blobs_when_lit = cfg[0].u.boolean.bval;
+}
+
static void free_ui(game_ui *ui)
{
sfree(ui);
@@ -2353,6 +2375,7 @@ const struct game thegame = {
free_game,
true, solve_game,
true, game_can_format_as_text_now, game_text_format,
+ get_prefs, set_prefs,
new_ui,
free_ui,
NULL, /* encode_ui */
diff --git a/loopy.c b/loopy.c
index 0f9dc52..f227e17 100644
--- a/loopy.c
+++ b/loopy.c
@@ -931,6 +931,37 @@ static void free_ui(game_ui *ui)
sfree(ui);
}
+static config_item *get_prefs(game_ui *ui)
+{
+ config_item *ret;
+
+ ret = snewn(3, config_item);
+
+ ret[0].name = "Draw excluded grid lines faintly";
+ ret[0].kw = "draw-faint-lines";
+ ret[0].type = C_BOOLEAN;
+ ret[0].u.boolean.bval = ui->draw_faint_lines;
+
+ ret[1].name = "Auto-follow unique paths of edges";
+ ret[1].kw = "auto-follow";
+ ret[1].type = C_CHOICES;
+ ret[1].u.choices.choicenames =
+ ":No:Based on grid only:Based on grid and game state";
+ ret[1].u.choices.choicekws = ":off:fixed:adaptive";
+ ret[1].u.choices.selected = ui->autofollow;
+
+ ret[2].name = NULL;
+ ret[2].type = C_END;
+
+ return ret;
+}
+
+static void set_prefs(game_ui *ui, const config_item *cfg)
+{
+ ui->draw_faint_lines = cfg[0].u.boolean.bval;
+ ui->autofollow = cfg[1].u.choices.selected;
+}
+
static void game_changed_state(game_ui *ui, const game_state *oldstate,
const game_state *newstate)
{
@@ -3710,6 +3741,7 @@ const struct game thegame = {
free_game,
true, solve_game,
true, game_can_format_as_text_now, game_text_format,
+ get_prefs, set_prefs,
new_ui,
free_ui,
NULL, /* encode_ui */
diff --git a/magnets.c b/magnets.c
index 6f216df..e58d8bf 100644
--- a/magnets.c
+++ b/magnets.c
@@ -2459,6 +2459,7 @@ const struct game thegame = {
free_game,
true, solve_game,
true, game_can_format_as_text_now, game_text_format,
+ NULL, NULL, /* get_prefs, set_prefs */
new_ui,
free_ui,
NULL, /* encode_ui */
diff --git a/map.c b/map.c
index 31dc96c..966ab75 100644
--- a/map.c
+++ b/map.c
@@ -2333,6 +2333,30 @@ static game_ui *new_ui(const game_state *state)
return ui;
}
+static config_item *get_prefs(game_ui *ui)
+{
+ config_item *ret;
+
+ ret = snewn(2, config_item);
+
+ ret[0].name = "Victory flash effect";
+ ret[0].kw = "flash-type";
+ ret[0].type = C_CHOICES;
+ ret[0].u.choices.choicenames = ":Cyclic:Each to white:All to white";
+ ret[0].u.choices.choicekws = ":cyclic:each-white:all-white";
+ ret[0].u.choices.selected = ui->flash_type;
+
+ ret[1].name = NULL;
+ ret[1].type = C_END;
+
+ return ret;
+}
+
+static void set_prefs(game_ui *ui, const config_item *cfg)
+{
+ ui->flash_type = cfg[0].u.choices.selected;
+}
+
static void free_ui(game_ui *ui)
{
sfree(ui);
@@ -3291,6 +3315,7 @@ const struct game thegame = {
free_game,
true, solve_game,
false, NULL, NULL, /* can_format_as_text_now, text_format */
+ get_prefs, set_prefs,
new_ui,
free_ui,
NULL, /* encode_ui */
diff --git a/mines.c b/mines.c
index 5781c4c..9b77683 100644
--- a/mines.c
+++ b/mines.c
@@ -3193,6 +3193,7 @@ const struct game thegame = {
free_game,
true, solve_game,
true, game_can_format_as_text_now, game_text_format,
+ NULL, NULL, /* get_prefs, set_prefs */
new_ui,
free_ui,
encode_ui,
diff --git a/mosaic.c b/mosaic.c
index 51d379e..1fb58b6 100644
--- a/mosaic.c
+++ b/mosaic.c
@@ -1609,6 +1609,7 @@ const struct game thegame = {
free_game,
true, solve_game,
true, game_can_format_as_text_now, game_text_format,
+ NULL, NULL, /* get_prefs, set_prefs */
new_ui,
free_ui,
NULL, /* encode_ui */
diff --git a/net.c b/net.c
index 2c8909d..9650ef3 100644
--- a/net.c
+++ b/net.c
@@ -3270,6 +3270,7 @@ const struct game thegame = {
free_game,
true, solve_game,
false, NULL, NULL, /* can_format_as_text_now, text_format */
+ NULL, NULL, /* get_prefs, set_prefs */
new_ui,
free_ui,
encode_ui,
diff --git a/netslide.c b/netslide.c
index 6472e23..d58a615 100644
--- a/netslide.c
+++ b/netslide.c
@@ -1858,6 +1858,7 @@ const struct game thegame = {
free_game,
true, solve_game,
false, NULL, NULL, /* can_format_as_text_now, text_format */
+ NULL, NULL, /* get_prefs, set_prefs */
new_ui,
free_ui,
NULL, /* encode_ui */
diff --git a/nullgame.c b/nullgame.c
index a779bc6..c1c2ed1 100644
--- a/nullgame.c
+++ b/nullgame.c
@@ -237,6 +237,7 @@ const struct game thegame = {
free_game,
false, NULL, /* solve */
false, NULL, NULL, /* can_format_as_text_now, text_format */
+ NULL, NULL, /* get_prefs, set_prefs */
new_ui,
free_ui,
NULL, /* encode_ui */
diff --git a/palisade.c b/palisade.c
index 0af19e5..2d9c990 100644
--- a/palisade.c
+++ b/palisade.c
@@ -1380,6 +1380,7 @@ const struct game thegame = {
free_game,
true, solve_game,
true, game_can_format_as_text_now, game_text_format,
+ NULL, NULL, /* get_prefs, set_prefs */
new_ui,
free_ui,
NULL, /* encode_ui */
diff --git a/pattern.c b/pattern.c
index fc01d1f..9ca42f6 100644
--- a/pattern.c
+++ b/pattern.c
@@ -2078,6 +2078,7 @@ const struct game thegame = {
free_game,
true, solve_game,
true, game_can_format_as_text_now, game_text_format,
+ NULL, NULL, /* get_prefs, set_prefs */
new_ui,
free_ui,
NULL, /* encode_ui */
diff --git a/pearl.c b/pearl.c
index 7caaae2..7265c70 100644
--- a/pearl.c
+++ b/pearl.c
@@ -1916,6 +1916,30 @@ static void free_ui(game_ui *ui)
sfree(ui);
}
+static config_item *get_prefs(game_ui *ui)
+{
+ config_item *ret;
+
+ ret = snewn(2, config_item);
+
+ ret[0].name = "Puzzle appearance";
+ ret[0].kw = "appearance";
+ ret[0].type = C_CHOICES;
+ ret[0].u.choices.choicenames = ":Traditional:Loopy-style";
+ ret[0].u.choices.choicekws = ":traditional:loopy";
+ ret[0].u.choices.selected = ui->gui_style;
+
+ ret[1].name = NULL;
+ ret[1].type = C_END;
+
+ return ret;
+}
+
+static void set_prefs(game_ui *ui, const config_item *cfg)
+{
+ ui->gui_style = cfg[0].u.choices.selected;
+}
+
static void game_changed_state(game_ui *ui, const game_state *oldstate,
const game_state *newstate)
{
@@ -2744,6 +2768,7 @@ const struct game thegame = {
free_game,
true, solve_game,
true, game_can_format_as_text_now, game_text_format,
+ get_prefs, set_prefs,
new_ui,
free_ui,
NULL, /* encode_ui */
diff --git a/pegs.c b/pegs.c
index 054d198..35c9a1e 100644
--- a/pegs.c
+++ b/pegs.c
@@ -1361,6 +1361,7 @@ const struct game thegame = {
free_game,
false, NULL, /* solve */
true, game_can_format_as_text_now, game_text_format,
+ NULL, NULL, /* get_prefs, set_prefs */
new_ui,
free_ui,
NULL, /* encode_ui */
diff --git a/puzzles.h b/puzzles.h
index b7c34f7..7e192c0 100644
--- a/puzzles.h
+++ b/puzzles.h
@@ -127,8 +127,13 @@ typedef struct psdata psdata;
*/
enum { C_STRING, C_CHOICES, C_BOOLEAN, C_END };
struct config_item {
- /* Not dynamically allocated */
+ /* Not dynamically allocated: the GUI display name for the option */
const char *name;
+ /* Not dynamically allocated: the keyword identifier for the
+ * option. Only examined in the case where this structure is being
+ * used for options that appear in config files, i.e. the
+ * get_prefs method fills this in but configure does not. */
+ const char *kw;
/* Value from the above C_* enum */
int type;
union {
@@ -146,6 +151,13 @@ struct config_item {
*/
const char *choicenames;
/*
+ * choicekws is non-NULL, not dynamically allocated, and
+ * contains a parallel list of keyword strings used to
+ * represent the enumeration in config files. As with 'kw'
+ * above, this is only expected to be set by get_prefs.
+ */
+ const char *choicekws;
+ /*
* Indicates the chosen index from the options in
* choicenames. In the above example, 0==Foo, 1==Bar and
* 2==Baz.
@@ -676,6 +688,8 @@ struct game {
bool can_format_as_text_ever;
bool (*can_format_as_text_now)(const game_params *params);
char *(*text_format)(const game_state *state);
+ config_item *(*get_prefs)(game_ui *ui);
+ void (*set_prefs)(game_ui *ui, const config_item *cfg);
game_ui *(*new_ui)(const game_state *state);
void (*free_ui)(game_ui *ui);
char *(*encode_ui)(const game_ui *ui);
diff --git a/range.c b/range.c
index cd5e89e..4cb771a 100644
--- a/range.c
+++ b/range.c
@@ -1271,6 +1271,31 @@ static game_ui *new_ui(const game_state *state)
return ui;
}
+static config_item *get_prefs(game_ui *ui)
+{
+ config_item *ret;
+
+ ret = snewn(2, config_item);
+
+ ret[0].name = "Mouse button order";
+ ret[0].kw = "left-mouse-button";
+ ret[0].type = C_CHOICES;
+ ret[0].u.choices.choicenames =
+ ":Left to fill, right to dot:Left to dot, right to fill";
+ ret[0].u.choices.choicekws = ":fill:dot";
+ ret[0].u.choices.selected = ui->swap_buttons;
+
+ ret[1].name = NULL;
+ ret[1].type = C_END;
+
+ return ret;
+}
+
+static void set_prefs(game_ui *ui, const config_item *cfg)
+{
+ ui->swap_buttons = cfg[0].u.choices.selected;
+}
+
static void free_ui(game_ui *ui)
{
sfree(ui);
@@ -1834,6 +1859,7 @@ struct game const thegame = {
free_game,
true, solve_game,
true, game_can_format_as_text_now, game_text_format,
+ get_prefs, set_prefs,
new_ui,
free_ui,
NULL, /* encode_ui */
diff --git a/rect.c b/rect.c
index a4ff8d2..2c41db8 100644
--- a/rect.c
+++ b/rect.c
@@ -2995,6 +2995,7 @@ const struct game thegame = {
free_game,
true, solve_game,
true, game_can_format_as_text_now, game_text_format,
+ NULL, NULL, /* get_prefs, set_prefs */
new_ui,
free_ui,
NULL, /* encode_ui */
diff --git a/samegame.c b/samegame.c
index 5c57a55..f228b19 100644
--- a/samegame.c
+++ b/samegame.c
@@ -1666,6 +1666,7 @@ const struct game thegame = {
free_game,
false, NULL, /* solve */
true, game_can_format_as_text_now, game_text_format,
+ NULL, NULL, /* get_prefs, set_prefs */
new_ui,
free_ui,
NULL, /* encode_ui */
diff --git a/signpost.c b/signpost.c
index 787239c..9c1bae3 100644
--- a/signpost.c
+++ b/signpost.c
@@ -1437,6 +1437,30 @@ static void free_ui(game_ui *ui)
sfree(ui);
}
+static config_item *get_prefs(game_ui *ui)
+{
+ config_item *ret;
+
+ ret = snewn(2, config_item);
+
+ ret[0].name = "Victory rotation effect";
+ ret[0].kw = "flash-type";
+ ret[0].type = C_CHOICES;
+ ret[0].u.choices.choicenames = ":Unidirectional:Meshing gears";
+ ret[0].u.choices.choicekws = ":unidirectional:gears";
+ ret[0].u.choices.selected = ui->gear_mode;
+
+ ret[1].name = NULL;
+ ret[1].type = C_END;
+
+ return ret;
+}
+
+static void set_prefs(game_ui *ui, const config_item *cfg)
+{
+ ui->gear_mode = cfg[0].u.choices.selected;
+}
+
static void game_changed_state(game_ui *ui, const game_state *oldstate,
const game_state *newstate)
{
@@ -2292,6 +2316,7 @@ const struct game thegame = {
free_game,
true, solve_game,
true, game_can_format_as_text_now, game_text_format,
+ get_prefs, set_prefs,
new_ui,
free_ui,
NULL, /* encode_ui */
diff --git a/singles.c b/singles.c
index 6eb5a2c..7b7b48f 100644
--- a/singles.c
+++ b/singles.c
@@ -1852,6 +1852,7 @@ const struct game thegame = {
free_game,
true, solve_game,
true, game_can_format_as_text_now, game_text_format,
+ NULL, NULL, /* get_prefs, set_prefs */
new_ui,
free_ui,
NULL, /* encode_ui */
diff --git a/sixteen.c b/sixteen.c
index 3f5ed5b..57be015 100644
--- a/sixteen.c
+++ b/sixteen.c
@@ -1191,6 +1191,7 @@ const struct game thegame = {
free_game,
true, solve_game,
true, game_can_format_as_text_now, game_text_format,
+ NULL, NULL, /* get_prefs, set_prefs */
new_ui,
free_ui,
NULL, /* encode_ui */
diff --git a/slant.c b/slant.c
index ee39d74..77fec1e 100644
--- a/slant.c
+++ b/slant.c
@@ -1617,6 +1617,30 @@ static game_ui *new_ui(const game_state *state)
return ui;
}
+static config_item *get_prefs(game_ui *ui)
+{
+ config_item *ret;
+
+ ret = snewn(2, config_item);
+
+ ret[0].name = "Mouse button order";
+ ret[0].kw = "left-button";
+ ret[0].type = C_CHOICES;
+ ret[0].u.choices.choicenames = ":Left \\, right /:Left /, right \\";
+ ret[0].u.choices.choicekws = ":\\:/";
+ ret[0].u.choices.selected = ui->swap_buttons;
+
+ ret[1].name = NULL;
+ ret[1].type = C_END;
+
+ return ret;
+}
+
+static void set_prefs(game_ui *ui, const config_item *cfg)
+{
+ ui->swap_buttons = cfg[0].u.choices.selected;
+}
+
static void free_ui(game_ui *ui)
{
sfree(ui);
@@ -2204,6 +2228,7 @@ const struct game thegame = {
free_game,
true, solve_game,
true, game_can_format_as_text_now, game_text_format,
+ get_prefs, set_prefs,
new_ui,
free_ui,
NULL, /* encode_ui */
diff --git a/solo.c b/solo.c
index 4005f2d..27ac7c9 100644
--- a/solo.c
+++ b/solo.c
@@ -5630,6 +5630,7 @@ const struct game thegame = {
free_game,
true, solve_game,
true, game_can_format_as_text_now, game_text_format,
+ NULL, NULL, /* get_prefs, set_prefs */
new_ui,
free_ui,
NULL, /* encode_ui */
diff --git a/tents.c b/tents.c
index c6a3514..e772e57 100644
--- a/tents.c
+++ b/tents.c
@@ -2649,6 +2649,7 @@ const struct game thegame = {
free_game,
true, solve_game,
true, game_can_format_as_text_now, game_text_format,
+ NULL, NULL, /* get_prefs, set_prefs */
new_ui,
free_ui,
NULL, /* encode_ui */
diff --git a/towers.c b/towers.c
index 0c2bf3a..84a8e71 100644
--- a/towers.c
+++ b/towers.c
@@ -1209,6 +1209,30 @@ static void free_ui(game_ui *ui)
sfree(ui);
}
+static config_item *get_prefs(game_ui *ui)
+{
+ config_item *ret;
+
+ ret = snewn(2, config_item);
+
+ ret[0].name = "Puzzle appearance";
+ ret[0].kw = "appearance";
+ ret[0].type = C_CHOICES;
+ ret[0].u.choices.choicenames = ":2D:3D";
+ ret[0].u.choices.choicekws = ":2d:3d";
+ ret[0].u.choices.selected = ui->three_d;
+
+ ret[1].name = NULL;
+ ret[1].type = C_END;
+
+ return ret;
+}
+
+static void set_prefs(game_ui *ui, const config_item *cfg)
+{
+ ui->three_d = cfg[0].u.choices.selected;
+}
+
static void game_changed_state(game_ui *ui, const game_state *oldstate,
const game_state *newstate)
{
@@ -2084,6 +2108,7 @@ const struct game thegame = {
free_game,
true, solve_game,
true, game_can_format_as_text_now, game_text_format,
+ get_prefs, set_prefs,
new_ui,
free_ui,
NULL, /* encode_ui */
diff --git a/tracks.c b/tracks.c
index 294ae04..4578296 100644
--- a/tracks.c
+++ b/tracks.c
@@ -3047,6 +3047,7 @@ const struct game thegame = {
free_game,
true, solve_game,
true, game_can_format_as_text_now, game_text_format,
+ NULL, NULL, /* get_prefs, set_prefs */
new_ui,
free_ui,
NULL, /* encode_ui */
diff --git a/twiddle.c b/twiddle.c
index 3095c99..f7216df 100644
--- a/twiddle.c
+++ b/twiddle.c
@@ -1305,6 +1305,7 @@ const struct game thegame = {
free_game,
true, solve_game,
true, game_can_format_as_text_now, game_text_format,
+ NULL, NULL, /* get_prefs, set_prefs */
new_ui,
free_ui,
NULL, /* encode_ui */
diff --git a/undead.c b/undead.c
index 2e18e11..74dbefc 100644
--- a/undead.c
+++ b/undead.c
@@ -2784,6 +2784,7 @@ const struct game thegame = {
free_game,
true, solve_game,
true, game_can_format_as_text_now, game_text_format,
+ NULL, NULL, /* get_prefs, set_prefs */
new_ui,
free_ui,
NULL, /* encode_ui */
diff --git a/unequal.c b/unequal.c
index dff5885..e67f028 100644
--- a/unequal.c
+++ b/unequal.c
@@ -2154,6 +2154,7 @@ const struct game thegame = {
free_game,
true, solve_game,
true, game_can_format_as_text_now, game_text_format,
+ NULL, NULL, /* get_prefs, set_prefs */
new_ui,
free_ui,
NULL, /* encode_ui */
diff --git a/unfinished/group.c b/unfinished/group.c
index dfa466d..7faee89 100644
--- a/unfinished/group.c
+++ b/unfinished/group.c
@@ -2323,6 +2323,7 @@ const struct game thegame = {
free_game,
true, solve_game,
true, game_can_format_as_text_now, game_text_format,
+ NULL, NULL, /* get_prefs, set_prefs */
new_ui,
free_ui,
NULL, /* encode_ui */
diff --git a/unfinished/separate.c b/unfinished/separate.c
index 635d7c6..6ca0725 100644
--- a/unfinished/separate.c
+++ b/unfinished/separate.c
@@ -835,6 +835,7 @@ const struct game thegame = {
free_game,
false, solve_game,
false, game_can_format_as_text_now, game_text_format,
+ NULL, NULL, /* get_prefs, set_prefs */
new_ui,
free_ui,
NULL, /* encode_ui */
diff --git a/unfinished/slide.c b/unfinished/slide.c
index 6368eba..c797d7e 100644
--- a/unfinished/slide.c
+++ b/unfinished/slide.c
@@ -2328,6 +2328,7 @@ const struct game thegame = {
free_game,
true, solve_game,
true, game_can_format_as_text_now, game_text_format,
+ NULL, NULL, /* get_prefs, set_prefs */
new_ui,
free_ui,
NULL, /* encode_ui */
diff --git a/unfinished/sokoban.c b/unfinished/sokoban.c
index 3b847af..1f3c688 100644
--- a/unfinished/sokoban.c
+++ b/unfinished/sokoban.c
@@ -1450,6 +1450,7 @@ const struct game thegame = {
free_game,
false, solve_game,
false, game_can_format_as_text_now, game_text_format,
+ NULL, NULL, /* get_prefs, set_prefs */
new_ui,
free_ui,
NULL, /* encode_ui */
diff --git a/unruly.c b/unruly.c
index 3ee1767..2f2e51e 100644
--- a/unruly.c
+++ b/unruly.c
@@ -2030,6 +2030,7 @@ const struct game thegame = {
free_game,
true, solve_game,
true, game_can_format_as_text_now, game_text_format,
+ NULL, NULL, /* get_prefs, set_prefs */
new_ui,
free_ui,
NULL, /* encode_ui */
diff --git a/untangle.c b/untangle.c
index 8ae1a7f..024a167 100644
--- a/untangle.c
+++ b/untangle.c
@@ -1454,6 +1454,7 @@ const struct game thegame = {
free_game,
true, solve_game,
false, NULL, NULL, /* can_format_as_text_now, text_format */
+ NULL, NULL, /* get_prefs, set_prefs */
new_ui,
free_ui,
NULL, /* encode_ui */