diff options
| author | Franklin Wei <me@fwei.tk> | 2018-04-17 16:18:16 -0400 |
|---|---|---|
| committer | Simon Tatham <anakin@pobox.com> | 2018-04-22 17:04:50 +0100 |
| commit | 60a929a250cf4f7f87ac082e5705f9a838a7f8c8 (patch) | |
| tree | 33a8a715f6320d2a2d58532b04e33ef111f31507 | |
| parent | 3d04dd3335a2c4c6007ff4e2a58a2855c7a9c52a (diff) | |
| download | puzzles-60a929a250cf4f7f87ac082e5705f9a838a7f8c8.zip puzzles-60a929a250cf4f7f87ac082e5705f9a838a7f8c8.tar.gz puzzles-60a929a250cf4f7f87ac082e5705f9a838a7f8c8.tar.bz2 puzzles-60a929a250cf4f7f87ac082e5705f9a838a7f8c8.tar.xz | |
Add a request_keys() function with a midend wrapper.
This function gives the front end a way to find out what keys the back
end requires; and as such it is mostly useful for ports without a
keyboard. It is based on changes originally found in Chris Boyle's
Android port, though some modifications were needed to make it more
flexible.
| -rw-r--r-- | blackbox.c | 1 | ||||
| -rw-r--r-- | bridges.c | 1 | ||||
| -rw-r--r-- | cube.c | 1 | ||||
| -rw-r--r-- | devel.but | 65 | ||||
| -rw-r--r-- | dominosa.c | 1 | ||||
| -rw-r--r-- | fifteen.c | 1 | ||||
| -rw-r--r-- | filling.c | 19 | ||||
| -rw-r--r-- | flip.c | 1 | ||||
| -rw-r--r-- | flood.c | 1 | ||||
| -rw-r--r-- | galaxies.c | 1 | ||||
| -rw-r--r-- | guess.c | 1 | ||||
| -rw-r--r-- | inertia.c | 1 | ||||
| -rw-r--r-- | keen.c | 22 | ||||
| -rw-r--r-- | lightup.c | 1 | ||||
| -rw-r--r-- | loopy.c | 1 | ||||
| -rw-r--r-- | magnets.c | 1 | ||||
| -rw-r--r-- | map.c | 1 | ||||
| -rw-r--r-- | midend.c | 21 | ||||
| -rw-r--r-- | mines.c | 1 | ||||
| -rw-r--r-- | misc.c | 45 | ||||
| -rw-r--r-- | net.c | 1 | ||||
| -rw-r--r-- | netslide.c | 1 | ||||
| -rw-r--r-- | nullgame.c | 1 | ||||
| -rw-r--r-- | palisade.c | 1 | ||||
| -rw-r--r-- | pattern.c | 1 | ||||
| -rw-r--r-- | pearl.c | 1 | ||||
| -rw-r--r-- | pegs.c | 1 | ||||
| -rw-r--r-- | puzzles.h | 26 | ||||
| -rw-r--r-- | range.c | 1 | ||||
| -rw-r--r-- | rect.c | 1 | ||||
| -rw-r--r-- | samegame.c | 1 | ||||
| -rw-r--r-- | signpost.c | 1 | ||||
| -rw-r--r-- | singles.c | 1 | ||||
| -rw-r--r-- | sixteen.c | 1 | ||||
| -rw-r--r-- | slant.c | 1 | ||||
| -rw-r--r-- | solo.c | 21 | ||||
| -rw-r--r-- | tents.c | 1 | ||||
| -rw-r--r-- | towers.c | 20 | ||||
| -rw-r--r-- | tracks.c | 1 | ||||
| -rw-r--r-- | twiddle.c | 1 | ||||
| -rw-r--r-- | undead.c | 21 | ||||
| -rw-r--r-- | unequal.c | 20 | ||||
| -rw-r--r-- | unfinished/group.c | 1 | ||||
| -rw-r--r-- | unruly.c | 1 | ||||
| -rw-r--r-- | untangle.c | 1 |
45 files changed, 315 insertions, 0 deletions
@@ -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, @@ -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, @@ -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, @@ -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: @@ -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, @@ -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, @@ -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, @@ -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, @@ -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, @@ -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, @@ -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, @@ -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, @@ -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, @@ -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, @@ -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, @@ -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, @@ -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, @@ -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); @@ -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, @@ -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: */ @@ -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, @@ -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, @@ -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, @@ -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, @@ -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, @@ -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, @@ -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, @@ -218,6 +218,24 @@ void preset_menu_add_preset(struct preset_menu *menu, 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, @@ -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, @@ -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, @@ -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, @@ -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, @@ -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, @@ -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, @@ -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, @@ -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, @@ -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, @@ -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, @@ -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, @@ -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, @@ -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, @@ -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, @@ -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, @@ -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, |