diff --git a/blackbox.c b/blackbox.c index d92af92..881bf4a 100644 --- a/blackbox.c +++ b/blackbox.c @@ -1519,6 +1519,7 @@ const struct game thegame = { free_ui, encode_ui, decode_ui, + NULL, /* game_request_keys */ game_changed_state, interpret_move, execute_move, diff --git a/bridges.c b/bridges.c index 0a326f2..098fea2 100644 --- a/bridges.c +++ b/bridges.c @@ -3238,6 +3238,7 @@ const struct game thegame = { free_ui, encode_ui, decode_ui, + NULL, /* game_request_keys */ game_changed_state, interpret_move, execute_move, diff --git a/cube.c b/cube.c index 2a79a81..7491565 100644 --- a/cube.c +++ b/cube.c @@ -1751,6 +1751,7 @@ const struct game thegame = { free_ui, encode_ui, decode_ui, + NULL, /* game_request_keys */ game_changed_state, interpret_move, execute_move, diff --git a/devel.but b/devel.but index 131678c..f30e8ee 100644 --- a/devel.but +++ b/devel.but @@ -1560,6 +1560,41 @@ the game was first completed (by setting a flag in freeze the timer thereafter so that the user can undo back through their solution process without altering their time. +\S{backend-request-keys} \cw{request_keys()} + +\c key_label *(*request_keys)(const game_params *params, int *nkeys); + +This function returns a dynamically allocated array of \cw{key_label} +items containing the buttons the back end deems absolutely +\e{necessary} for gameplay, not an exhaustive list of every button the +back end could accept. For example, Keen only returns the digits up to +the game size and the backspace character, \cw{\\b}, even though it +\e{could} accept \cw{M}, as only these buttons are actually needed to +play the game. Each \cw{key_label} item contains the following fields: + +\c struct key_label { +\c const char *label; /* label for frontend use */ +\c int button; /* button to pass to midend */ +\c } key_label; + +The \cw{label} field of this structure can (and often will) be set by +the backend to \cw{NULL}, in which case the midend will instead call +\c{button2label()} (\k{utils-button2label}) and fill in a generic +label. The \cw{button} field is the associated code that can be passed +to the midend when the frontend deems appropriate. + +The backend should set \cw{*nkeys} to the number of elements in the +returned array. + +The field for this function point in the \cw{game} structure might be +set to \cw{NULL} (and indeed it is for the majority of the games) to +indicate that no additional buttons (apart from the cursor keys) are +required to play the game. + +This function should not be called directly by frontends. Instead, +frontends should use \cw{midend_request_keys()} +(\k{midend-request-keys}). + \S{backend-flags} \c{flags} \c int flags; @@ -2998,6 +3033,18 @@ the effect of the keypress was to request termination of the program. A front end should shut down the puzzle in response to a zero return. +\H{midend-request-keys} \cw{midend_request_keys()} + +\c key_label *midend_request_keys(midend *me, int *nkeys); + +This function behaves similarly to the backend's \cw{request_keys()} +function (\k{backend-request-keys}). If the backend does not provide +\cw{request_keys()}, this function will return \cw{NULL} and set +\cw{*nkeys} to zero. Otherwise, this function will fill in the generic +labels (i.e. the \cw{key_label} items that have their \cw{label} +fields set to \cw{NULL}) by using \cw{button2label()} +(\k{utils-button2label}). + \H{midend-colours} \cw{midend_colours()} \c float *midend_colours(midend *me, int *ncolours); @@ -4215,6 +4262,24 @@ Thus, \cw{ret[background*3]} to \cw{ret[background*3+2]} will be set to RGB values defining a sensible background colour, and similary \c{highlight} and \c{lowlight} will be set to sensible colours. +\S{utils-button2label} \cw{button2label()} + +\c char *button2label(int button); + +This function generates a descriptive text label for \cw{button}, +which should be a button code that can be passed to the midend. For +example, calling this function with \cw{CURSOR_UP} will result in the +string \cw{"Up"}. This function should only be called when the +\cw{key_label} item returned by a backend's \cw{request_keys()} +(\k{backend-request-keys}) function has its \cw{label} field set to +\cw{NULL}; in this case, the corresponding \cw{button} field can be +passed to this function to obtain an appropriate label. If, however, +the field is not \cw{NULL}, this function should not be called with +the corresponding \cw{button} field. + +The returned string is dynamically allocated and should be +\cw{sfree}'d by the caller. + \C{writing} How to write a new puzzle This chapter gives a guide to how to actually write a new puzzle: diff --git a/dominosa.c b/dominosa.c index a072ac6..73b744d 100644 --- a/dominosa.c +++ b/dominosa.c @@ -1723,6 +1723,7 @@ const struct game thegame = { free_ui, encode_ui, decode_ui, + NULL, /* game_request_keys */ game_changed_state, interpret_move, execute_move, diff --git a/fifteen.c b/fifteen.c index f749e96..35bcb32 100644 --- a/fifteen.c +++ b/fifteen.c @@ -1103,6 +1103,7 @@ const struct game thegame = { free_ui, encode_ui, decode_ui, + NULL, /* game_request_keys */ game_changed_state, interpret_move, execute_move, diff --git a/filling.c b/filling.c index 231de4c..db6f95d 100644 --- a/filling.c +++ b/filling.c @@ -1287,6 +1287,24 @@ static const char *validate_desc(const game_params *params, const char *desc) return (area < sz) ? "Not enough data to fill grid" : NULL; } +static key_label *game_request_keys(const game_params *params, int *nkeys) +{ + key_label *keys = snewn(11, key_label); + *nkeys = 11; + + int i; + + for(i = 0; i < 10; ++i) + { + keys[i].button = '0' + i; + keys[i].label = NULL; + } + keys[10].button = '\b'; + keys[10].label = NULL; + + return keys; +} + static game_state *new_game(midend *me, const game_params *params, const char *desc) { @@ -2125,6 +2143,7 @@ const struct game thegame = { free_ui, encode_ui, decode_ui, + game_request_keys, game_changed_state, interpret_move, execute_move, diff --git a/flip.c b/flip.c index 9e5716a..481c4b5 100644 --- a/flip.c +++ b/flip.c @@ -1327,6 +1327,7 @@ const struct game thegame = { free_ui, encode_ui, decode_ui, + NULL, /* game_request_keys */ game_changed_state, interpret_move, execute_move, diff --git a/flood.c b/flood.c index a3a2506..99bbae3 100644 --- a/flood.c +++ b/flood.c @@ -1348,6 +1348,7 @@ const struct game thegame = { free_ui, encode_ui, decode_ui, + NULL, /* game_request_keys */ game_changed_state, interpret_move, execute_move, diff --git a/galaxies.c b/galaxies.c index 5d7dbdf..11efd34 100644 --- a/galaxies.c +++ b/galaxies.c @@ -3652,6 +3652,7 @@ const struct game thegame = { free_ui, encode_ui, decode_ui, + NULL, /* game_request_keys */ game_changed_state, interpret_move, execute_move, diff --git a/guess.c b/guess.c index a14f3bd..98019d2 100644 --- a/guess.c +++ b/guess.c @@ -1493,6 +1493,7 @@ const struct game thegame = { free_ui, encode_ui, decode_ui, + NULL, /* game_request_keys */ game_changed_state, interpret_move, execute_move, diff --git a/inertia.c b/inertia.c index ad58bd2..6963dee 100644 --- a/inertia.c +++ b/inertia.c @@ -2229,6 +2229,7 @@ const struct game thegame = { free_ui, encode_ui, decode_ui, + NULL, /* game_request_keys */ game_changed_state, interpret_move, execute_move, diff --git a/keen.c b/keen.c index ec7af12..1e531d9 100644 --- a/keen.c +++ b/keen.c @@ -1251,6 +1251,27 @@ static const char *validate_desc(const game_params *params, const char *desc) return NULL; } +static key_label *game_request_keys(const game_params *params, int *nkeys) +{ + int i; + int w = params->w; + + key_label *keys = snewn(w+1, key_label); + *nkeys = w + 1; + + for (i = 0; i < w; i++) { + if (i<9) keys[i].button = '1' + i; + else keys[i].button = 'a' + i - 9; + + keys[i].label = NULL; + } + keys[w].button = '\b'; + keys[w].label = NULL; + + + return keys; +} + static game_state *new_game(midend *me, const game_params *params, const char *desc) { @@ -2354,6 +2375,7 @@ const struct game thegame = { free_ui, encode_ui, decode_ui, + game_request_keys, game_changed_state, interpret_move, execute_move, diff --git a/lightup.c b/lightup.c index fd363e0..acee853 100644 --- a/lightup.c +++ b/lightup.c @@ -2303,6 +2303,7 @@ const struct game thegame = { free_ui, encode_ui, decode_ui, + NULL, /* game_request_keys */ game_changed_state, interpret_move, execute_move, diff --git a/loopy.c b/loopy.c index c14412d..f5f8917 100644 --- a/loopy.c +++ b/loopy.c @@ -3658,6 +3658,7 @@ const struct game thegame = { free_ui, encode_ui, decode_ui, + NULL, /* game_request_keys */ game_changed_state, interpret_move, execute_move, diff --git a/magnets.c b/magnets.c index ebb5376..c9f97b6 100644 --- a/magnets.c +++ b/magnets.c @@ -2409,6 +2409,7 @@ const struct game thegame = { free_ui, encode_ui, decode_ui, + NULL, /* game_request_keys */ game_changed_state, interpret_move, execute_move, diff --git a/map.c b/map.c index ffd2123..ccec127 100644 --- a/map.c +++ b/map.c @@ -3236,6 +3236,7 @@ const struct game thegame = { free_ui, encode_ui, decode_ui, + NULL, /* game_request_keys */ game_changed_state, interpret_move, execute_move, diff --git a/midend.c b/midend.c index 99112fa..1a08ef6 100644 --- a/midend.c +++ b/midend.c @@ -1104,6 +1104,27 @@ int midend_process_key(midend *me, int x, int y, int button) return ret; } +key_label *midend_request_keys(midend *me, int *n) +{ + key_label *keys = NULL; + int nkeys = 0, i; + + if(me->ourgame->request_keys) + { + keys = me->ourgame->request_keys(midend_get_params(me), &nkeys); + for(i = 0; i < nkeys; ++i) + { + if(!keys[i].label) + keys[i].label = button2label(keys[i].button); + } + } + + if(n) + *n = nkeys; + + return keys; +} + void midend_redraw(midend *me) { assert(me->drawing); diff --git a/mines.c b/mines.c index 6a854c6..e606735 100644 --- a/mines.c +++ b/mines.c @@ -3191,6 +3191,7 @@ const struct game thegame = { free_ui, encode_ui, decode_ui, + NULL, /* game_request_keys */ game_changed_state, interpret_move, execute_move, diff --git a/misc.c b/misc.c index 852f869..917958a 100644 --- a/misc.c +++ b/misc.c @@ -21,6 +21,15 @@ void free_cfg(config_item *cfg) sfree(cfg); } +void free_keys(key_label *keys, int nkeys) +{ + int i; + + for(i = 0; i < nkeys; i++) + sfree(keys->label); + sfree(keys); +} + /* * The Mines (among others) game descriptions contain the location of every * mine, and can therefore be used to cheat. @@ -393,4 +402,40 @@ void copy_left_justified(char *buf, size_t sz, const char *str) buf[sz - 1] = 0; } +/* Returns a dynamically allocated label for a generic button. + * Game-specific buttons should go into the `label' field of key_label + * instead. */ +char *button2label(int button) +{ + /* check if it's a keyboard button */ + if(('A' <= button && button <= 'Z') || + ('a' <= button && button <= 'z') || + ('0' <= button && button <= '9') ) + { + char str[2] = { button, '\0' }; + return dupstr(str); + } + + switch(button) + { + case CURSOR_UP: + return dupstr("Up"); + case CURSOR_DOWN: + return dupstr("Down"); + case CURSOR_LEFT: + return dupstr("Left"); + case CURSOR_RIGHT: + return dupstr("Right"); + case CURSOR_SELECT: + return dupstr("Select"); + case '\b': + return dupstr("Clear"); + default: + fatal("unknown generic key"); + } + + /* should never get here */ + return NULL; +} + /* vim: set shiftwidth=4 tabstop=8: */ diff --git a/net.c b/net.c index 20366a2..f26b5d7 100644 --- a/net.c +++ b/net.c @@ -3236,6 +3236,7 @@ const struct game thegame = { free_ui, encode_ui, decode_ui, + NULL, /* game_request_keys */ game_changed_state, interpret_move, execute_move, diff --git a/netslide.c b/netslide.c index aee5973..bb0ed1e 100644 --- a/netslide.c +++ b/netslide.c @@ -1866,6 +1866,7 @@ const struct game thegame = { free_ui, encode_ui, decode_ui, + NULL, /* game_request_keys */ game_changed_state, interpret_move, execute_move, diff --git a/nullgame.c b/nullgame.c index bc19c1e..13c464c 100644 --- a/nullgame.c +++ b/nullgame.c @@ -285,6 +285,7 @@ const struct game thegame = { free_ui, encode_ui, decode_ui, + NULL, /* game_request_keys */ game_changed_state, interpret_move, execute_move, diff --git a/palisade.c b/palisade.c index 5227a1d..34257eb 100644 --- a/palisade.c +++ b/palisade.c @@ -1375,6 +1375,7 @@ const struct game thegame = { free_ui, encode_ui, decode_ui, + NULL, /* game_request_keys */ game_changed_state, interpret_move, execute_move, diff --git a/pattern.c b/pattern.c index 45b1b0c..632a986 100644 --- a/pattern.c +++ b/pattern.c @@ -1998,6 +1998,7 @@ const struct game thegame = { free_ui, encode_ui, decode_ui, + NULL, /* game_request_keys */ game_changed_state, interpret_move, execute_move, diff --git a/pearl.c b/pearl.c index 4f3be50..07949b5 100644 --- a/pearl.c +++ b/pearl.c @@ -2621,6 +2621,7 @@ const struct game thegame = { free_ui, encode_ui, decode_ui, + NULL, /* game_request_keys */ game_changed_state, interpret_move, execute_move, diff --git a/pegs.c b/pegs.c index 565ba94..823d5c2 100644 --- a/pegs.c +++ b/pegs.c @@ -1316,6 +1316,7 @@ const struct game thegame = { free_ui, encode_ui, decode_ui, + NULL, /* game_request_keys */ game_changed_state, interpret_move, execute_move, diff --git a/puzzles.h b/puzzles.h index 6b6e6b3..2eb7647 100644 --- a/puzzles.h +++ b/puzzles.h @@ -217,6 +217,24 @@ void preset_menu_add_preset(struct preset_menu *menu, * want to organise their preset menu usage */ game_params *preset_menu_lookup_by_id(struct preset_menu *menu, int id); +/* + * Small structure specifying a UI button in a keyboardless front + * end. The button will have the text of "label" written on it, and + * pressing it causes the value "button" to be passed to + * midend_process_key() as if typed at the keyboard. + * + * If `label' is NULL (which it likely will be), a generic label can + * be generated with the button2label() function. + */ +typedef struct key_label { + /* What should be displayed to the user by the frontend. Backends + * can set this field to NULL and have it filled in by the midend + * with a generic label. Dynamically allocated, but frontends + * should probably use free_keys() to free instead. */ + char *label; + int button; /* passed to midend_process_key when button is pressed */ +} key_label; + /* * Platform routines */ @@ -301,6 +319,7 @@ void midend_new_game(midend *me); void midend_restart_game(midend *me); void midend_stop_anim(midend *me); int midend_process_key(midend *me, int x, int y, int button); +key_label *midend_request_keys(midend *me, int *nkeys); void midend_force_redraw(midend *me); void midend_redraw(midend *me); float *midend_colours(midend *me, int *ncolours); @@ -356,6 +375,7 @@ char *dupstr(const char *s); * misc.c */ void free_cfg(config_item *cfg); +void free_keys(key_label *keys, int nkeys); void obfuscate_bitmap(unsigned char *bmp, int bits, int decode); char *fgetline(FILE *fp); @@ -400,6 +420,11 @@ void draw_text_outline(drawing *dr, int x, int y, int fonttype, * less than buffer size. */ void copy_left_justified(char *buf, size_t sz, const char *str); +/* Returns a generic label based on the value of `button.' To be used + whenever a `label' field returned by the request_keys() game + function is NULL. Dynamically allocated, to be freed by caller. */ +char *button2label(int button); + /* * dsf.c */ @@ -610,6 +635,7 @@ struct game { void (*free_ui)(game_ui *ui); char *(*encode_ui)(const game_ui *ui); void (*decode_ui)(game_ui *ui, const char *encoding); + key_label *(*request_keys)(const game_params *params, int *nkeys); void (*changed_state)(game_ui *ui, const game_state *oldstate, const game_state *newstate); char *(*interpret_move)(const game_state *state, game_ui *ui, diff --git a/range.c b/range.c index 58d6e48..03bdf6d 100644 --- a/range.c +++ b/range.c @@ -1813,6 +1813,7 @@ struct game const thegame = { free_ui, encode_ui, decode_ui, + NULL, /* game_request_keys */ game_changed_state, interpret_move, execute_move, diff --git a/rect.c b/rect.c index 0140808..c3092f2 100644 --- a/rect.c +++ b/rect.c @@ -2974,6 +2974,7 @@ const struct game thegame = { free_ui, encode_ui, decode_ui, + NULL, /* game_request_keys */ game_changed_state, interpret_move, execute_move, diff --git a/samegame.c b/samegame.c index d7d0676..4dfc302 100644 --- a/samegame.c +++ b/samegame.c @@ -1656,6 +1656,7 @@ const struct game thegame = { free_ui, encode_ui, decode_ui, + NULL, /* game_request_keys */ game_changed_state, interpret_move, execute_move, diff --git a/signpost.c b/signpost.c index 72f3ab2..47da97f 100644 --- a/signpost.c +++ b/signpost.c @@ -2246,6 +2246,7 @@ const struct game thegame = { free_ui, encode_ui, decode_ui, + NULL, /* game_request_keys */ game_changed_state, interpret_move, execute_move, diff --git a/singles.c b/singles.c index 5929d82..35c5fce 100644 --- a/singles.c +++ b/singles.c @@ -1828,6 +1828,7 @@ const struct game thegame = { free_ui, encode_ui, decode_ui, + NULL, /* game_request_keys */ game_changed_state, interpret_move, execute_move, diff --git a/sixteen.c b/sixteen.c index 70cc433..b027e0b 100644 --- a/sixteen.c +++ b/sixteen.c @@ -1188,6 +1188,7 @@ const struct game thegame = { free_ui, encode_ui, decode_ui, + NULL, /* game_request_keys */ game_changed_state, interpret_move, execute_move, diff --git a/slant.c b/slant.c index 3fd6611..5eeb668 100644 --- a/slant.c +++ b/slant.c @@ -2164,6 +2164,7 @@ const struct game thegame = { free_ui, encode_ui, decode_ui, + NULL, /* game_request_keys */ game_changed_state, interpret_move, execute_move, diff --git a/solo.c b/solo.c index dae0998..69fd85b 100644 --- a/solo.c +++ b/solo.c @@ -3608,6 +3608,26 @@ static struct block_structure *gen_killer_cages(int cr, random_state *rs, return b; } +static key_label *game_request_keys(const game_params *params, int *nkeys) +{ + int i; + int cr = params->c * params->r; + key_label *keys = snewn(cr+1, key_label); + *nkeys = cr + 1; + + for (i = 0; i < cr; i++) { + if (i<9) keys[i].button = '1' + i; + else keys[i].button = 'a' + i - 9; + + keys[i].label = NULL; + } + keys[cr].button = '\b'; + keys[cr].label = NULL; + + + return keys; +} + static char *new_game_desc(const game_params *params, random_state *rs, char **aux, int interactive) { @@ -5579,6 +5599,7 @@ const struct game thegame = { free_ui, encode_ui, decode_ui, + game_request_keys, game_changed_state, interpret_move, execute_move, diff --git a/tents.c b/tents.c index 696eec1..2f0977e 100644 --- a/tents.c +++ b/tents.c @@ -2613,6 +2613,7 @@ const struct game thegame = { free_ui, encode_ui, decode_ui, + NULL, /* game_request_keys */ game_changed_state, interpret_move, execute_move, diff --git a/towers.c b/towers.c index 30ee4cb..d90167b 100644 --- a/towers.c +++ b/towers.c @@ -864,6 +864,25 @@ static const char *validate_desc(const game_params *params, const char *desc) return NULL; } +static key_label *game_request_keys(const game_params *params, int *nkeys) +{ + int i; + int w = params->w; + key_label *keys = snewn(w+1, key_label); + *nkeys = w + 1; + + for (i = 0; i < w; i++) { + if (i<9) keys[i].button = '1' + i; + else keys[i].button = 'a' + i - 9; + + keys[i].label = NULL; + } + keys[w].button = '\b'; + keys[w].label = NULL; + + return keys; +} + static game_state *new_game(midend *me, const game_params *params, const char *desc) { @@ -1993,6 +2012,7 @@ const struct game thegame = { free_ui, encode_ui, decode_ui, + game_request_keys, game_changed_state, interpret_move, execute_move, diff --git a/tracks.c b/tracks.c index b05fbd2..4e22c97 100644 --- a/tracks.c +++ b/tracks.c @@ -2646,6 +2646,7 @@ const struct game thegame = { free_ui, encode_ui, decode_ui, + NULL, /* game_request_keys */ game_changed_state, interpret_move, execute_move, diff --git a/twiddle.c b/twiddle.c index 1f2825f..f84d0cb 100644 --- a/twiddle.c +++ b/twiddle.c @@ -1291,6 +1291,7 @@ const struct game thegame = { free_ui, encode_ui, decode_ui, + NULL, /* game_request_keys */ game_changed_state, interpret_move, execute_move, diff --git a/undead.c b/undead.c index df0735e..03b014f 100644 --- a/undead.c +++ b/undead.c @@ -1314,6 +1314,26 @@ void num2grid(int num, int width, int height, int *x, int *y) { return; } +static key_label *game_request_keys(const game_params *params, int *nkeys) +{ + key_label *keys = snewn(4, key_label); + *nkeys = 4; + + keys[0].button = 'G'; + keys[0].label = dupstr("Ghost"); + + keys[1].button = 'V'; + keys[1].label = dupstr("Vampire"); + + keys[2].button = 'Z'; + keys[2].label = dupstr("Zombie"); + + keys[3].button = '\b'; + keys[3].label = NULL; + + return keys; +} + static game_state *new_game(midend *me, const game_params *params, const char *desc) { @@ -2716,6 +2736,7 @@ const struct game thegame = { free_ui, encode_ui, decode_ui, + game_request_keys, game_changed_state, interpret_move, execute_move, diff --git a/unequal.c b/unequal.c index cb477c9..b87415b 100644 --- a/unequal.c +++ b/unequal.c @@ -1280,6 +1280,25 @@ fail: return NULL; } +static key_label *game_request_keys(const game_params *params, int *nkeys) +{ + int order = params->order; + char off = (order > 9) ? '0' : '1'; + key_label *keys = snewn(order + 1, key_label); + *nkeys = order + 1; + + int i; + for(i = 0; i < order; i++) { + if (i==10) off = 'a'-10; + keys[i].button = i + off; + keys[i].label = NULL; + } + keys[order].button = '\b'; + keys[order].label = NULL; + + return keys; +} + static game_state *new_game(midend *me, const game_params *params, const char *desc) { @@ -2011,6 +2030,7 @@ const struct game thegame = { free_ui, encode_ui, decode_ui, + game_request_keys, game_changed_state, interpret_move, execute_move, diff --git a/unfinished/group.c b/unfinished/group.c index b812b04..72293dd 100644 --- a/unfinished/group.c +++ b/unfinished/group.c @@ -2081,6 +2081,7 @@ const struct game thegame = { free_ui, encode_ui, decode_ui, + NULL, /* game_request_keys */ game_changed_state, interpret_move, execute_move, diff --git a/unruly.c b/unruly.c index 9d2d592..b3057a7 100644 --- a/unruly.c +++ b/unruly.c @@ -1926,6 +1926,7 @@ const struct game thegame = { free_ui, encode_ui, decode_ui, + NULL, /* game_request_keys */ game_changed_state, interpret_move, execute_move, diff --git a/untangle.c b/untangle.c index a693378..b71cfa7 100644 --- a/untangle.c +++ b/untangle.c @@ -1470,6 +1470,7 @@ const struct game thegame = { free_ui, encode_ui, decode_ui, + NULL, /* game_request_keys */ game_changed_state, interpret_move, execute_move,