diff --git a/blackbox.c b/blackbox.c index 1478943..f8d6b27 100644 --- a/blackbox.c +++ b/blackbox.c @@ -532,6 +532,41 @@ static void game_changed_state(game_ui *ui, const game_state *oldstate, ui->newmove = false; } +static const char *current_key_label(const game_ui *ui, + const game_state *state, int button) +{ + if (IS_CURSOR_SELECT(button) && ui->cur_visible && !state->reveal) { + int gx = ui->cur_x, gy = ui->cur_y, rangeno = -1; + if (gx == 0 && gy == 0 && button == CURSOR_SELECT) return "Check"; + if (gx >= 1 && gx <= state->w && gy >= 1 && gy <= state->h) { + /* Cursor somewhere in the arena. */ + if (button == CURSOR_SELECT && !(GRID(state, gx,gy) & BALL_LOCK)) + return (GRID(state, gx, gy) & BALL_GUESS) ? "Clear" : "Ball"; + if (button == CURSOR_SELECT2) + return (GRID(state, gx, gy) & BALL_LOCK) ? "Unlock" : "Lock"; + } + if (grid2range(state, gx, gy, &rangeno)) { + if (button == CURSOR_SELECT && + state->exits[rangeno] == LASER_EMPTY) + return "Fire"; + if (button == CURSOR_SELECT2) { + int n = 0; + /* Row or column lock or unlock. */ + if (gy == 0 || gy > state->h) { /* Column lock */ + for (gy = 1; gy <= state->h; gy++) + n += !!(GRID(state, gx, gy) & BALL_LOCK); + return n > state->h/2 ? "Unlock" : "Lock"; + } else { /* Row lock */ + for (gx = 1; gx <= state->w; gx++) + n += !!(GRID(state, gx, gy) & BALL_LOCK); + return n > state->w/2 ? "Unlock" : "Lock"; + } + } + } + } + return ""; +} + #define OFFSET(gx,gy,o) do { \ int off = (4 + (o) % 4) % 4; \ (gx) += offsets[off].x; \ @@ -1543,6 +1578,7 @@ const struct game thegame = { decode_ui, NULL, /* game_request_keys */ game_changed_state, + current_key_label, interpret_move, execute_move, PREFERRED_TILE_SIZE, game_compute_size, game_set_size, diff --git a/bridges.c b/bridges.c index 074940c..de95afe 100644 --- a/bridges.c +++ b/bridges.c @@ -2138,6 +2138,20 @@ static void game_changed_state(game_ui *ui, const game_state *oldstate, { } +static const char *current_key_label(const game_ui *ui, + const game_state *state, int button) +{ + if (IS_CURSOR_SELECT(button)) { + if (!ui->cur_visible) + return ""; /* Actually shows cursor. */ + if (ui->dragging || button == CURSOR_SELECT2) + return "Finished"; + if (GRID(state, ui->cur_x, ui->cur_y) & G_ISLAND) + return "Select"; + } + return ""; +} + struct game_drawstate { int tilesize; int w, h; @@ -3269,6 +3283,7 @@ const struct game thegame = { decode_ui, NULL, /* game_request_keys */ game_changed_state, + current_key_label, interpret_move, execute_move, PREFERRED_TILE_SIZE, game_compute_size, game_set_size, diff --git a/cube.c b/cube.c index 8c8c46f..32b7343 100644 --- a/cube.c +++ b/cube.c @@ -1774,6 +1774,7 @@ const struct game thegame = { decode_ui, NULL, /* game_request_keys */ game_changed_state, + NULL, /* current_key_label */ interpret_move, execute_move, PREFERRED_GRID_SCALE, game_compute_size, game_set_size, diff --git a/devel.but b/devel.but index 7f0e871..b545548 100644 --- a/devel.but +++ b/devel.but @@ -1692,6 +1692,43 @@ This function should not be called directly by frontends. Instead, frontends should use \cw{midend_request_keys()} (\k{midend-request-keys}). +\S{backend-current-key-label} \cw{current_key_label()} + +\c const char *(*current_key_label)(const game_ui *ui, +\c const game_state *state, int button); + +This function is called to ask the back-end how certain keys should be +labelled on platforms (such a feature phones) where this is +conventional. +These labels are expected to reflect what the keys will do right now, +so they can change depending on the game and UI state. + +The \c{ui} and \c{state} arguments describe the state of the game for +which key labels are required. +The \c{button} argument is the same as the one passed to +\cw{interpret_move()}. +At present, the only values of \c{button} that can be passed to +\cw{current_key_label()} are \cw{CURSOR_SELECT} and \cw{CURSOR_SELECT2}. +The return value is a short string describing what the requested key +will do if pressed. +Usually the string should be a static string constant. +If it's really necessary to use a dynamically-allocated string, it +should remain valid until the next call to \cw{current_key_label()} or +\cw{free_ui()} with the same \cw{game_ui} (so it can be referenced from +the \cw{game_ui} and freed at the next one of those calls). + +There's no fixed upper limit on the length of string that this +function can return, but more than about 12 characters is likely to +cause problems for front-ends. If two buttons have the same effect, +their labels should be identical so that the front end can detect +this. Similarly, keys that do different things should have different +labels. The label should be an empty string (\cw{""}) if the key does +nothing. + +Like \cw{request_keys()}, the \cw{current_key_label} pointer in the +\c{game} structure is allowed to be \cw{NULL}, in which case the +mid-end will treat it as though it always returned \cw{""}. + \S{backend-flags} \c{flags} \c int flags; @@ -3232,6 +3269,20 @@ 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-current-key-label} \cw{midend_current_key_label()} + +\c const char *midend_current_key_label(midend *me, int button); + +This is a thin wrapper around the backend's \cw{current_key_label()} +function (\k{backend-current-key-label}). Front ends that need to +label \cw{CURSOR_SELECT} or \cw{CURSOR_SELECT2} should call this +function after each move (at least after each call to +\cw{midend_process_key()}) to get the current labels. The front end +should arrange to copy the returned string somewhere before the next +call to the mid-end, just in case it's dynamically allocated. If the +button supplied does nothing, the label returned will be an empty +string. + \H{midend-colours} \cw{midend_colours()} \c float *midend_colours(midend *me, int *ncolours); diff --git a/dominosa.c b/dominosa.c index 2f8638c..0be1c68 100644 --- a/dominosa.c +++ b/dominosa.c @@ -2734,6 +2734,33 @@ static void game_changed_state(game_ui *ui, const game_state *oldstate, ui->cur_visible = false; } +static const char *current_key_label(const game_ui *ui, + const game_state *state, int button) +{ + if (IS_CURSOR_SELECT(button)) { + int d1, d2, w = state->w; + + if (!((ui->cur_x ^ ui->cur_y) & 1)) + return ""; /* must have exactly one dimension odd */ + d1 = (ui->cur_y / 2) * w + (ui->cur_x / 2); + d2 = ((ui->cur_y+1) / 2) * w + ((ui->cur_x+1) / 2); + + /* We can't mark an edge next to any domino. */ + if (button == CURSOR_SELECT2 && + (state->grid[d1] != d1 || state->grid[d2] != d2)) + return ""; + if (button == CURSOR_SELECT) { + if (state->grid[d1] == d2) return "Remove"; + return "Place"; + } else { + int edge = d2 == d1 + 1 ? EDGE_R : EDGE_B; + if (state->edges[d1] & edge) return "Remove"; + return "Line"; + } + } + return ""; +} + #define PREFERRED_TILESIZE 32 #define TILESIZE (ds->tilesize) #define BORDER (TILESIZE * 3 / 4) @@ -3417,6 +3444,7 @@ const struct game thegame = { decode_ui, NULL, /* game_request_keys */ game_changed_state, + current_key_label, interpret_move, execute_move, PREFERRED_TILESIZE, game_compute_size, game_set_size, diff --git a/fifteen.c b/fifteen.c index 41f9d41..fecd754 100644 --- a/fifteen.c +++ b/fifteen.c @@ -1110,6 +1110,7 @@ const struct game thegame = { decode_ui, NULL, /* game_request_keys */ game_changed_state, + NULL, /* current_key_label */ interpret_move, execute_move, PREFERRED_TILE_SIZE, game_compute_size, game_set_size, diff --git a/filling.c b/filling.c index 4b96648..879e916 100644 --- a/filling.c +++ b/filling.c @@ -1429,6 +1429,23 @@ static void game_changed_state(game_ui *ui, const game_state *oldstate, ui->keydragging = false; } +static const char *current_key_label(const game_ui *ui, + const game_state *state, int button) +{ + const int w = state->shared->params.w; + + if (IS_CURSOR_SELECT(button) && ui->cur_visible) { + if (button == CURSOR_SELECT) { + if (ui->keydragging) return "Stop"; + return "Multiselect"; + } + if (button == CURSOR_SELECT2 && + !state->shared->clues[w*ui->cur_y + ui->cur_x]) + return (ui->sel[w*ui->cur_y + ui->cur_x]) ? "Deselect" : "Select"; + } + return ""; +} + #define PREFERRED_TILE_SIZE 32 #define TILE_SIZE (ds->tilesize) #define BORDER (TILE_SIZE / 2) @@ -2166,6 +2183,7 @@ const struct game thegame = { decode_ui, game_request_keys, game_changed_state, + current_key_label, interpret_move, execute_move, PREFERRED_TILE_SIZE, game_compute_size, game_set_size, diff --git a/flip.c b/flip.c index ddc2294..e6ff0a2 100644 --- a/flip.c +++ b/flip.c @@ -928,6 +928,13 @@ static void game_changed_state(game_ui *ui, const game_state *oldstate, { } +static const char *current_key_label(const game_ui *ui, + const game_state *state, int button) +{ + if (IS_CURSOR_SELECT(button)) return "Flip"; + return ""; +} + struct game_drawstate { int w, h; bool started; @@ -1346,6 +1353,7 @@ const struct game thegame = { decode_ui, NULL, /* game_request_keys */ game_changed_state, + current_key_label, interpret_move, execute_move, PREFERRED_TILE_SIZE, game_compute_size, game_set_size, diff --git a/flood.c b/flood.c index 3d7eaa2..80f2ff6 100644 --- a/flood.c +++ b/flood.c @@ -793,6 +793,18 @@ static void game_changed_state(game_ui *ui, const game_state *oldstate, { } +static const char *current_key_label(const game_ui *ui, + const game_state *state, int button) +{ + if (button == CURSOR_SELECT && + state->grid[0] != state->grid[ui->cy*state->w+ui->cx]) + return "Fill"; + if (button == CURSOR_SELECT2 && + state->soln && state->solnpos < state->soln->nmoves) + return "Advance"; + return ""; +} + struct game_drawstate { bool started; int tilesize; @@ -1357,6 +1369,7 @@ const struct game thegame = { decode_ui, NULL, /* game_request_keys */ game_changed_state, + current_key_label, interpret_move, execute_move, PREFERRED_TILESIZE, game_compute_size, game_set_size, diff --git a/galaxies.c b/galaxies.c index 908b45a..36845d4 100644 --- a/galaxies.c +++ b/galaxies.c @@ -2539,6 +2539,33 @@ static bool edge_placement_legal(const game_state *state, int x, int y) return true; } +static const char *current_key_label(const game_ui *ui, + const game_state *state, int button) +{ + space *sp; + + if (IS_CURSOR_SELECT(button) && ui->cur_visible) { + sp = &SPACE(state, ui->cur_x, ui->cur_y); + if (ui->dragging) { + if (ui->cur_x == ui->srcx && ui->cur_y == ui->srcy) + return "Cancel"; + if (ok_to_add_assoc_with_opposite( + state, &SPACE(state, ui->cur_x, ui->cur_y), + &SPACE(state, ui->dotx, ui->doty))) + return "Place"; + return (ui->srcx == ui->dotx && ui->srcy == ui->doty) ? + "Cancel" : "Remove"; + } else if (sp->flags & F_DOT) + return "New arrow"; + else if (sp->flags & F_TILE_ASSOC) + return "Move arrow"; + else if (sp->type == s_edge && + edge_placement_legal(state, ui->cur_x, ui->cur_y)) + return (sp->flags & F_EDGE_SET) ? "Clear" : "Edge"; + } + return ""; +} + static char *interpret_move(const game_state *state, game_ui *ui, const game_drawstate *ds, int x, int y, int button) @@ -3813,6 +3840,11 @@ const struct game thegame = { decode_ui, NULL, /* game_request_keys */ game_changed_state, +#ifdef EDITOR + NULL, +#else + current_key_label, +#endif interpret_move, execute_move, PREFERRED_TILE_SIZE, game_compute_size, game_set_size, diff --git a/guess.c b/guess.c index 28fa0e0..9acda1e 100644 --- a/guess.c +++ b/guess.c @@ -510,6 +510,19 @@ static void game_changed_state(game_ui *ui, const game_state *oldstate, ui->peg_cur = 0; } +static const char *current_key_label(const game_ui *ui, + const game_state *state, int button) +{ + if (state->solved) return ""; + if (button == CURSOR_SELECT) { + if (ui->peg_cur == state->params.npegs) return "Submit"; + return "Place"; + } + if (button == CURSOR_SELECT2 && ui->peg_cur != state->params.npegs) + return "Hold"; + return ""; +} + #define PEGSZ (ds->pegsz) #define PEGOFF (ds->pegsz + ds->gapsz) #define HINTSZ (ds->hintsz) @@ -1522,6 +1535,7 @@ const struct game thegame = { decode_ui, NULL, /* game_request_keys */ game_changed_state, + current_key_label, interpret_move, execute_move, PEG_PREFER_SZ, game_compute_size, game_set_size, diff --git a/inertia.c b/inertia.c index ab90323..c0c383a 100644 --- a/inertia.c +++ b/inertia.c @@ -1545,6 +1545,15 @@ static void game_changed_state(game_ui *ui, const game_state *oldstate, ui->just_made_move = false; } +static const char *current_key_label(const game_ui *ui, + const game_state *state, int button) +{ + if (IS_CURSOR_SELECT(button) && + state->soln && state->solnpos < state->soln->len) + return "Advance"; + return ""; +} + struct game_drawstate { game_params p; int tilesize; @@ -2233,6 +2242,7 @@ const struct game thegame = { decode_ui, NULL, /* game_request_keys */ game_changed_state, + current_key_label, interpret_move, execute_move, PREFERRED_TILESIZE, game_compute_size, game_set_size, diff --git a/keen.c b/keen.c index 558c549..ddb8b64 100644 --- a/keen.c +++ b/keen.c @@ -1569,6 +1569,14 @@ static void game_changed_state(game_ui *ui, const game_state *oldstate, } } +static const char *current_key_label(const game_ui *ui, + const game_state *state, int button) +{ + if (ui->hshow && (button == CURSOR_SELECT)) + return ui->hpencil ? "Ink" : "Pencil"; + return ""; +} + #define PREFERRED_TILESIZE 48 #define TILESIZE (ds->tilesize) #define BORDER (TILESIZE / 2) @@ -2477,6 +2485,7 @@ const struct game thegame = { decode_ui, game_request_keys, game_changed_state, + current_key_label, interpret_move, execute_move, PREFERRED_TILESIZE, game_compute_size, game_set_size, diff --git a/lightup.c b/lightup.c index fe30c36..b559665 100644 --- a/lightup.c +++ b/lightup.c @@ -1855,6 +1855,26 @@ static void game_changed_state(game_ui *ui, const game_state *oldstate, ui->cur_visible = false; } +static const char *current_key_label(const game_ui *ui, + const game_state *state, int button) +{ + int cx = ui->cur_x, cy = ui->cur_y; + unsigned int flags = GRID(state, flags, cx, cy); + + if (!ui->cur_visible) return ""; + if (button == CURSOR_SELECT) { + if (flags & (F_BLACK | F_IMPOSSIBLE)) return ""; + if (flags & F_LIGHT) return "Clear"; + return "Light"; + } + if (button == CURSOR_SELECT2) { + if (flags & (F_BLACK | F_LIGHT)) return ""; + if (flags & F_IMPOSSIBLE) return "Clear"; + return "Mark"; + } + return ""; +} + #define DF_BLACK 1 /* black square */ #define DF_NUMBERED 2 /* black square with number */ #define DF_LIT 4 /* display (white) square lit up */ @@ -2324,6 +2344,7 @@ const struct game thegame = { decode_ui, NULL, /* game_request_keys */ game_changed_state, + current_key_label, interpret_move, execute_move, PREFERRED_TILE_SIZE, game_compute_size, game_set_size, diff --git a/loopy.c b/loopy.c index b7fb296..81699c2 100644 --- a/loopy.c +++ b/loopy.c @@ -3677,6 +3677,7 @@ const struct game thegame = { decode_ui, NULL, /* game_request_keys */ game_changed_state, + NULL, /* current_key_label */ interpret_move, execute_move, PREFERRED_TILE_SIZE, game_compute_size, game_set_size, diff --git a/magnets.c b/magnets.c index bca54c5..580f2a2 100644 --- a/magnets.c +++ b/magnets.c @@ -1745,6 +1745,36 @@ static void game_changed_state(game_ui *ui, const game_state *oldstate, ui->cur_visible = false; } +static const char *current_key_label(const game_ui *ui, + const game_state *state, int button) +{ + int idx; + + if (IS_CURSOR_SELECT(button)) { + if (!ui->cur_visible) return ""; + idx = ui->cur_y * state->w + ui->cur_x; + if (button == CURSOR_SELECT) { + if (state->grid[idx] == NEUTRAL && state->flags[idx] & GS_SET) + return ""; + switch (state->grid[idx]) { + case EMPTY: return "+"; + case POSITIVE: return "-"; + case NEGATIVE: return "Clear"; + } + } + if (button == CURSOR_SELECT2) { + if (state->grid[idx] != NEUTRAL) return ""; + if (state->flags[idx] & GS_SET) /* neutral */ + return "?"; + if (state->flags[idx] & GS_NOTNEUTRAL) /* !neutral */ + return "Clear"; + else + return "X"; + } + } + return ""; +} + struct game_drawstate { int tilesize; bool started, solved; @@ -2431,6 +2461,7 @@ const struct game thegame = { decode_ui, NULL, /* game_request_keys */ game_changed_state, + current_key_label, interpret_move, execute_move, PREFERRED_TILE_SIZE, game_compute_size, game_set_size, diff --git a/map.c b/map.c index 22b24d6..e55bf7b 100644 --- a/map.c +++ b/map.c @@ -2392,6 +2392,26 @@ static int region_from_ui_cursor(const game_state *state, const game_ui *ui) EPSILON_Y(ui->cur_lastmove)); } +static const char *current_key_label(const game_ui *ui, + const game_state *state, int button) +{ + int r; + + if (IS_CURSOR_SELECT(button) && ui->cur_visible) { + if (ui->drag_colour == -2) return "Pick"; + r = region_from_ui_cursor(state, ui); + if (state->map->immutable[r]) return "Cancel"; + if (!ui->cur_moved) return ui->drag_pencil ? "Cancel" : "Clear"; + if (button == CURSOR_SELECT2) { + if (state->colouring[r] >= 0) return "Cancel"; + if (ui->drag_colour >= 0) return "Stipple"; + } + if (ui->drag_pencil) return "Stipple"; + return ui->drag_colour >= 0 ? "Fill" : "Clear"; + } + return ""; +} + static char *interpret_move(const game_state *state, game_ui *ui, const game_drawstate *ds, int x, int y, int button) @@ -3257,6 +3277,7 @@ const struct game thegame = { decode_ui, NULL, /* game_request_keys */ game_changed_state, + current_key_label, interpret_move, execute_move, 20, game_compute_size, game_set_size, diff --git a/midend.c b/midend.c index a6f5e6b..1e03693 100644 --- a/midend.c +++ b/midend.c @@ -1220,6 +1220,15 @@ key_label *midend_request_keys(midend *me, int *n) return keys; } +/* Return a good label to show next to a key right now. */ +const char *midend_current_key_label(midend *me, int button) +{ + assert(IS_CURSOR_SELECT(button)); + if (!me->ourgame->current_key_label) return ""; + return me->ourgame->current_key_label( + me->ui, me->states[me->statepos-1].state, button); +} + void midend_redraw(midend *me) { assert(me->drawing); diff --git a/mines.c b/mines.c index 1103cc7..22332ba 100644 --- a/mines.c +++ b/mines.c @@ -2400,6 +2400,35 @@ static void game_changed_state(game_ui *ui, const game_state *oldstate, ui->completed = true; } +static const char *current_key_label(const game_ui *ui, + const game_state *state, int button) +{ + int cx = ui->cur_x, cy = ui->cur_y; + int v = state->grid[cy * state->w + cx]; + + if (state->dead || state->won || !ui->cur_visible) return ""; + if (button == CURSOR_SELECT2) { + if (v == -2) return "Mark"; + if (v == -1) return "Unmark"; + return ""; + } + if (button == CURSOR_SELECT) { + int dy, dx, n = 0; + if (v == -2 || v == -3) return "Uncover"; + if (v == 0) return ""; + /* Count mine markers. */ + for (dy = -1; dy <= +1; dy++) + for (dx = -1; dx <= +1; dx++) + if (cx+dx >= 0 && cx+dx < state->w && + cy+dy >= 0 && cy+dy < state->h) { + if (state->grid[(cy+dy)*state->w+(cx+dx)] == -1) + n++; + } + if (n == v) return "Clear"; + } + return ""; +} + struct game_drawstate { int w, h, tilesize, bg; bool started; @@ -3208,6 +3237,7 @@ const struct game thegame = { decode_ui, NULL, /* game_request_keys */ game_changed_state, + current_key_label, interpret_move, execute_move, PREFERRED_TILE_SIZE, game_compute_size, game_set_size, diff --git a/mosaic.c b/mosaic.c index da7f78d..d07859a 100644 --- a/mosaic.c +++ b/mosaic.c @@ -1033,6 +1033,26 @@ static void game_changed_state(game_ui *ui, const game_state *oldstate, { } +static const char *current_key_label(const game_ui *ui, + const game_state *state, int button) +{ + char *cell; + + if (IS_CURSOR_SELECT(button)) { + if (!ui->cur_visible || state->not_completed_clues == 0) return ""; + cell = get_coords(state, state->cells_contents, ui->cur_x, ui->cur_y); + switch (*cell & STATE_OK_NUM) { + case STATE_UNMARKED: + return button == CURSOR_SELECT ? "Black" : "White"; + case STATE_MARKED: + return button == CURSOR_SELECT ? "White" : "Empty"; + case STATE_BLANK: + return button == CURSOR_SELECT ? "Empty" : "Black"; + } + } + return ""; +} + static char *interpret_move(const game_state *state, game_ui *ui, const game_drawstate *ds, int x, int y, int button) @@ -1598,6 +1618,7 @@ const struct game thegame = { decode_ui, NULL, /* game_request_keys */ game_changed_state, + current_key_label, interpret_move, execute_move, DEFAULT_TILE_SIZE, game_compute_size, game_set_size, diff --git a/net.c b/net.c index 140494e..c1ff8e2 100644 --- a/net.c +++ b/net.c @@ -2053,6 +2053,18 @@ static void game_changed_state(game_ui *ui, const game_state *oldstate, { } +static const char *current_key_label(const game_ui *ui, + const game_state *state, int button) +{ + if (tile(state, ui->cur_x, ui->cur_y) & LOCKED) { + if (button == CURSOR_SELECT2) return "Unlock"; + } else { + if (button == CURSOR_SELECT) return "Rotate"; + if (button == CURSOR_SELECT2) return "Lock"; + } + return ""; +} + struct game_drawstate { int width, height; int tilesize; @@ -3254,6 +3266,7 @@ const struct game thegame = { decode_ui, NULL, /* game_request_keys */ game_changed_state, + current_key_label, interpret_move, execute_move, PREFERRED_TILE_SIZE, game_compute_size, game_set_size, diff --git a/netslide.c b/netslide.c index 88d0944..50ee099 100644 --- a/netslide.c +++ b/netslide.c @@ -1050,6 +1050,14 @@ struct game_drawstate { int cur_x, cur_y; }; +static const char *current_key_label(const game_ui *ui, + const game_state *state, int button) +{ + if (IS_CURSOR_SELECT(button) && ui->cur_visible) + return "Slide"; + return ""; +} + static char *interpret_move(const game_state *state, game_ui *ui, const game_drawstate *ds, int x, int y, int button) @@ -1875,6 +1883,7 @@ const struct game thegame = { decode_ui, NULL, /* game_request_keys */ game_changed_state, + current_key_label, interpret_move, execute_move, PREFERRED_TILE_SIZE, game_compute_size, game_set_size, diff --git a/nullgame.c b/nullgame.c index d973e17..5942038 100644 --- a/nullgame.c +++ b/nullgame.c @@ -287,6 +287,7 @@ const struct game thegame = { decode_ui, NULL, /* game_request_keys */ game_changed_state, + NULL, /* current_key_label */ interpret_move, execute_move, 20 /* FIXME */, game_compute_size, game_set_size, diff --git a/palisade.c b/palisade.c index fdd69cc..6661f5b 100644 --- a/palisade.c +++ b/palisade.c @@ -1392,6 +1392,7 @@ const struct game thegame = { decode_ui, NULL, /* game_request_keys */ game_changed_state, + NULL, /* current_key_label */ interpret_move, execute_move, 48, game_compute_size, game_set_size, diff --git a/pattern.c b/pattern.c index 7c00bf9..afe1768 100644 --- a/pattern.c +++ b/pattern.c @@ -1241,6 +1241,23 @@ static void game_changed_state(game_ui *ui, const game_state *oldstate, { } +static const char *current_key_label(const game_ui *ui, + const game_state *state, int button) +{ + if (IS_CURSOR_SELECT(button)) { + if (!ui->cur_visible) return ""; + switch (state->grid[ui->cur_y * state->common->w + ui->cur_x]) { + case GRID_UNKNOWN: + return button == CURSOR_SELECT ? "Black" : "White"; + case GRID_FULL: + return button == CURSOR_SELECT ? "White" : "Grey"; + case GRID_EMPTY: + return button == CURSOR_SELECT ? "Grey" : "Black"; + } + } + return ""; +} + struct game_drawstate { bool started; int w, h; @@ -2025,6 +2042,7 @@ const struct game thegame = { decode_ui, NULL, /* game_request_keys */ game_changed_state, + current_key_label, interpret_move, execute_move, PREFERRED_TILE_SIZE, game_compute_size, game_set_size, diff --git a/pearl.c b/pearl.c index 27b4860..5bd71d0 100644 --- a/pearl.c +++ b/pearl.c @@ -1847,6 +1847,20 @@ static void game_changed_state(game_ui *ui, const game_state *oldstate, { } +static const char *current_key_label(const game_ui *ui, + const game_state *state, int button) +{ + if (IS_CURSOR_SELECT(button) && ui->cursor_active) { + if (button == CURSOR_SELECT) { + if (ui->ndragcoords == -1) return "Start"; + return "Stop"; + } + if (button == CURSOR_SELECT2 && ui->ndragcoords >= 0) + return "Cancel"; + } + return ""; +} + #define PREFERRED_TILE_SIZE 31 #define HALFSZ (ds->halfsz) #define TILE_SIZE (ds->halfsz*2 + 1) @@ -2661,6 +2675,7 @@ const struct game thegame = { decode_ui, NULL, /* game_request_keys */ game_changed_state, + current_key_label, interpret_move, execute_move, PREFERRED_TILE_SIZE, game_compute_size, game_set_size, diff --git a/pegs.c b/pegs.c index 61fc38a..7a52719 100644 --- a/pegs.c +++ b/pegs.c @@ -800,6 +800,19 @@ static void game_changed_state(game_ui *ui, const game_state *oldstate, ui->cur_jumping = false; } +static const char *current_key_label(const game_ui *ui, + const game_state *state, int button) +{ + int w = state->w; + + if (IS_CURSOR_SELECT(button)) { + if (!ui->cur_visible) return ""; + if (ui->cur_jumping) return "Cancel"; + if (state->grid[ui->cur_y*w+ui->cur_x] == GRID_PEG) return "Select"; + } + return ""; +} + #define PREFERRED_TILE_SIZE 33 #define TILESIZE (ds->tilesize) #define BORDER (TILESIZE / 2) @@ -1338,6 +1351,7 @@ const struct game thegame = { decode_ui, NULL, /* game_request_keys */ game_changed_state, + current_key_label, interpret_move, execute_move, PREFERRED_TILE_SIZE, game_compute_size, game_set_size, diff --git a/puzzles.h b/puzzles.h index 675594d..9d02cd5 100644 --- a/puzzles.h +++ b/puzzles.h @@ -307,6 +307,7 @@ void midend_restart_game(midend *me); void midend_stop_anim(midend *me); bool midend_process_key(midend *me, int x, int y, int button, bool *handled); key_label *midend_request_keys(midend *me, int *nkeys); +const char *midend_current_key_label(midend *me, int button); void midend_force_redraw(midend *me); void midend_redraw(midend *me); float *midend_colours(midend *me, int *ncolours); @@ -657,6 +658,8 @@ struct game { key_label *(*request_keys)(const game_params *params, int *nkeys); void (*changed_state)(game_ui *ui, const game_state *oldstate, const game_state *newstate); + const char *(*current_key_label)(const game_ui *ui, + const game_state *state, int button); char *(*interpret_move)(const game_state *state, game_ui *ui, const game_drawstate *ds, int x, int y, int button); game_state *(*execute_move)(const game_state *state, const char *move); diff --git a/range.c b/range.c index 4106b15..103c136 100644 --- a/range.c +++ b/range.c @@ -1245,6 +1245,27 @@ static void decode_ui(game_ui *ui, const char *encoding) { } +static const char *current_key_label(const game_ui *ui, + const game_state *state, int button) +{ + int cell; + + if (IS_CURSOR_SELECT(button)) { + cell = state->grid[idx(ui->r, ui->c, state->params.w)]; + if (!ui->cursor_show || cell > 0) return ""; + switch (cell) { + case EMPTY: + return button == CURSOR_SELECT ? "Fill" : "Dot"; + case WHITE: + return button == CURSOR_SELECT ? "Empty" : "Fill"; + case BLACK: + return button == CURSOR_SELECT ? "Dot" : "Empty"; + } + } + return ""; + +} + typedef struct drawcell { puzzle_size value; bool error, cursor, flash; @@ -1818,6 +1839,7 @@ struct game const thegame = { decode_ui, NULL, /* game_request_keys */ game_changed_state, + current_key_label, interpret_move, execute_move, PREFERRED_TILE_SIZE, game_compute_size, game_set_size, diff --git a/rect.c b/rect.c index a262708..e2141fd 100644 --- a/rect.c +++ b/rect.c @@ -2374,6 +2374,21 @@ struct game_drawstate { unsigned long *visible; }; +static const char *current_key_label(const game_ui *ui, + const game_state *state, int button) +{ + if (IS_CURSOR_SELECT(button) && ui->cur_visible && + !(ui->drag_start_x >= 0 && !ui->cur_dragging)) { + if (ui->cur_dragging) { + if (!ui->dragged) return "Cancel"; + if ((button == CURSOR_SELECT2) == ui->erasing) return "Done"; + return "Cancel"; + } + return button == CURSOR_SELECT ? "Mark" : "Erase"; + } + return ""; +} + static char *interpret_move(const game_state *from, game_ui *ui, const game_drawstate *ds, int x, int y, int button) @@ -2992,6 +3007,7 @@ const struct game thegame = { decode_ui, NULL, /* game_request_keys */ game_changed_state, + current_key_label, interpret_move, execute_move, PREFERRED_TILE_SIZE, game_compute_size, game_set_size, diff --git a/samegame.c b/samegame.c index 22b4316..3597e19 100644 --- a/samegame.c +++ b/samegame.c @@ -1109,6 +1109,25 @@ static void game_changed_state(game_ui *ui, const game_state *oldstate, ui->displaysel = false; } +static const char *current_key_label(const game_ui *ui, + const game_state *state, int button) +{ + if (IS_CURSOR_SELECT(button)) { + int x = ui->xsel, y = ui->ysel, c = COL(state,x,y); + if (c == 0) return ""; + if (ISSEL(ui, x, y)) + return button == CURSOR_SELECT2 ? "Unselect" : "Remove"; + if ((x > 0 && COL(state,x-1,y) == c) || + (x+1 < state->params.w && COL(state,x+1,y) == c) || + (y > 0 && COL(state,x,y-1) == c) || + (y+1 < state->params.h && COL(state,x,y+1) == c)) + return "Select"; + /* Cursor is over a lone square, so we can't select it. */ + if (ui->nselected) return "Unselect"; + } + return ""; +} + static char *sel_movedesc(game_ui *ui, const game_state *state) { int i; @@ -1670,6 +1689,7 @@ const struct game thegame = { decode_ui, NULL, /* game_request_keys */ game_changed_state, + current_key_label, interpret_move, execute_move, PREFERRED_TILE_SIZE, game_compute_size, game_set_size, diff --git a/signpost.c b/signpost.c index b06b9fd..1c8cfee 100644 --- a/signpost.c +++ b/signpost.c @@ -1421,6 +1421,26 @@ static void game_changed_state(game_ui *ui, const game_state *oldstate, } } +static const char *current_key_label(const game_ui *ui, + const game_state *state, int button) +{ + if (IS_CURSOR_SELECT(button) && ui->cshow) { + if (ui->dragging) { + if (ui->drag_is_from) { + if (isvalidmove(state, false, ui->sx, ui->sy, ui->cx, ui->cy)) + return "To here"; + } else { + if (isvalidmove(state, false, ui->cx, ui->cy, ui->sx, ui->sy)) + return "From here"; + } + return "Cancel"; + } else { + return button == CURSOR_SELECT ? "From here" : "To here"; + } + } + return ""; +} + struct game_drawstate { int tilesize; bool started, solved; @@ -2277,6 +2297,7 @@ const struct game thegame = { decode_ui, NULL, /* game_request_keys */ game_changed_state, + current_key_label, interpret_move, execute_move, PREFERRED_TILE_SIZE, game_compute_size, game_set_size, diff --git a/singles.c b/singles.c index 2440513..6c35162 100644 --- a/singles.c +++ b/singles.c @@ -1473,6 +1473,18 @@ static void game_changed_state(game_ui *ui, const game_state *oldstate, ui->cshow = false; } +static const char *current_key_label(const game_ui *ui, + const game_state *state, int button) +{ + if (IS_CURSOR_SELECT(button) && ui->cshow) { + unsigned int f = state->flags[ui->cy * state->w + ui->cx]; + if (f & F_BLACK) return "Restore"; + if (f & F_CIRCLE) return "Remove"; + return button == CURSOR_SELECT ? "Black" : "Circle"; + } + return ""; +} + #define DS_BLACK 0x1 #define DS_CIRCLE 0x2 #define DS_CURSOR 0x4 @@ -1853,6 +1865,7 @@ const struct game thegame = { decode_ui, NULL, /* game_request_keys */ game_changed_state, + current_key_label, interpret_move, execute_move, PREFERRED_TILE_SIZE, game_compute_size, game_set_size, diff --git a/sixteen.c b/sixteen.c index 7a622a4..b44e925 100644 --- a/sixteen.c +++ b/sixteen.c @@ -591,6 +591,21 @@ static void game_changed_state(game_ui *ui, const game_state *oldstate, { } +static const char *current_key_label(const game_ui *ui, + const game_state *state, int button) +{ + if (IS_CURSOR_SELECT(button) && ui->cur_visible) { + if (ui->cur_x == -1 || ui->cur_x == state->w || + ui->cur_y == -1 || ui->cur_y == state->h) + return button == CURSOR_SELECT2 ? "Back" : "Slide"; + if (button == CURSOR_SELECT) + return ui->cur_mode == lock_tile ? "Unlock" : "Lock tile"; + if (button == CURSOR_SELECT2) + return ui->cur_mode == lock_position ? "Unlock" : "Lock pos"; + } + return ""; +} + struct game_drawstate { bool started; int w, h, bgcolour; @@ -1197,6 +1212,7 @@ const struct game thegame = { decode_ui, NULL, /* game_request_keys */ game_changed_state, + current_key_label, interpret_move, execute_move, PREFERRED_TILE_SIZE, game_compute_size, game_set_size, diff --git a/slant.c b/slant.c index 151e36f..25af496 100644 --- a/slant.c +++ b/slant.c @@ -1603,6 +1603,22 @@ static void game_changed_state(game_ui *ui, const game_state *oldstate, { } +static const char *current_key_label(const game_ui *ui, + const game_state *state, int button) +{ + if (IS_CURSOR_SELECT(button) && ui->cur_visible) { + switch (state->soln[ui->cur_y*state->p.w+ui->cur_x]) { + case 0: + return button == CURSOR_SELECT ? "\\" : "/"; + case -1: + return button == CURSOR_SELECT ? "/" : "Blank"; + case +1: + return button == CURSOR_SELECT ? "Blank" : "\\"; + } + } + return ""; +} + #define PREFERRED_TILESIZE 32 #define TILESIZE (ds->tilesize) #define BORDER TILESIZE @@ -2175,6 +2191,7 @@ const struct game thegame = { decode_ui, NULL, /* game_request_keys */ game_changed_state, + current_key_label, interpret_move, execute_move, PREFERRED_TILESIZE, game_compute_size, game_set_size, diff --git a/solo.c b/solo.c index aabfe39..a2d8a47 100644 --- a/solo.c +++ b/solo.c @@ -4594,6 +4594,14 @@ static void game_changed_state(game_ui *ui, const game_state *oldstate, } } +static const char *current_key_label(const game_ui *ui, + const game_state *state, int button) +{ + if (ui->hshow && (button == CURSOR_SELECT)) + return ui->hpencil ? "Ink" : "Pencil"; + return ""; +} + struct game_drawstate { bool started, xtype; int cr; @@ -5618,6 +5626,7 @@ const struct game thegame = { decode_ui, game_request_keys, game_changed_state, + current_key_label, interpret_move, execute_move, PREFERRED_TILE_SIZE, game_compute_size, game_set_size, diff --git a/tents.c b/tents.c index 8b93653..89e5dd2 100644 --- a/tents.c +++ b/tents.c @@ -1459,6 +1459,22 @@ static void game_changed_state(game_ui *ui, const game_state *oldstate, { } +static const char *current_key_label(const game_ui *ui, + const game_state *state, int button) +{ + int w = state->p.w; + int v = state->grid[ui->cy*w+ui->cx]; + + if (IS_CURSOR_SELECT(button) && ui->cdisp) { + switch (v) { + case BLANK: + return button == CURSOR_SELECT ? "Tent" : "Green"; + case TENT: case NONTENT: return "Clear"; + } + } + return ""; +} + struct game_drawstate { int tilesize; bool started; @@ -2626,6 +2642,7 @@ const struct game thegame = { decode_ui, NULL, /* game_request_keys */ game_changed_state, + current_key_label, interpret_move, execute_move, PREFERRED_TILESIZE, game_compute_size, game_set_size, diff --git a/towers.c b/towers.c index 839ba7f..57d6614 100644 --- a/towers.c +++ b/towers.c @@ -1198,6 +1198,14 @@ static void game_changed_state(game_ui *ui, const game_state *oldstate, } } +static const char *current_key_label(const game_ui *ui, + const game_state *state, int button) +{ + if (ui->hshow && (button == CURSOR_SELECT)) + return ui->hpencil ? "Ink" : "Pencil"; + return ""; +} + #define PREFERRED_TILESIZE 48 #define TILESIZE (ds->tilesize) #define BORDER (TILESIZE * 9 / 8) @@ -2048,6 +2056,7 @@ const struct game thegame = { decode_ui, game_request_keys, game_changed_state, + current_key_label, interpret_move, execute_move, PREFERRED_TILESIZE, game_compute_size, game_set_size, diff --git a/tracks.c b/tracks.c index 5392c25..9b5ef4c 100644 --- a/tracks.c +++ b/tracks.c @@ -2121,6 +2121,35 @@ static bool ui_can_flip_square(const game_state *state, int x, int y, bool notra return true; } +static const char *current_key_label(const game_ui *ui, + const game_state *state, int button) +{ + if (IS_CURSOR_SELECT(button) && ui->cursor_active) { + int gx = ui->curx / 2, gy = ui->cury / 2; + int w = state->p.w; + int direction = + ((ui->curx % 2) == 0) ? L : ((ui->cury % 2) == 0) ? U : 0; + if (direction && + ui_can_flip_edge(state, gx, gy, direction, + button == CURSOR_SELECT2)) { + unsigned ef = S_E_FLAGS(state, gx, gy, direction); + switch (button) { + case CURSOR_SELECT: return (ef & E_TRACK) ? "Clear" : "Track"; + case CURSOR_SELECT2: return (ef & E_NOTRACK) ? "Clear" : "X"; + } + } + if (!direction && + ui_can_flip_square(state, gx, gy, button == CURSOR_SELECT2)) { + unsigned sf = state->sflags[gy*w+gx]; + switch (button) { + case CURSOR_SELECT: return (sf & S_TRACK) ? "Clear" : "Track"; + case CURSOR_SELECT2: return (sf & S_NOTRACK) ? "Clear" : "X"; + } + } + } + return ""; +} + static char *edge_flip_str(const game_state *state, int x, int y, int dir, bool notrack, char *buf) { unsigned ef = S_E_FLAGS(state, x, y, dir); char c; @@ -2973,6 +3002,7 @@ const struct game thegame = { decode_ui, NULL, /* game_request_keys */ game_changed_state, + current_key_label, interpret_move, execute_move, PREFERRED_TILE_SIZE, game_compute_size, game_set_size, diff --git a/twiddle.c b/twiddle.c index 0a4ee17..8492242 100644 --- a/twiddle.c +++ b/twiddle.c @@ -637,6 +637,17 @@ static void game_changed_state(game_ui *ui, const game_state *oldstate, { } +static const char *current_key_label(const game_ui *ui, + const game_state *state, int button) +{ + if (!ui->cur_visible) return ""; + switch (button) { + case CURSOR_SELECT: return "Turn left"; + case CURSOR_SELECT2: return "Turn right"; + } + return ""; +} + struct game_drawstate { bool started; int w, h, bgcolour; @@ -1313,6 +1324,7 @@ const struct game thegame = { decode_ui, NULL, /* game_request_keys */ game_changed_state, + current_key_label, interpret_move, execute_move, PREFERRED_TILE_SIZE, game_compute_size, game_set_size, diff --git a/undead.c b/undead.c index 8d29be8..46ecce3 100644 --- a/undead.c +++ b/undead.c @@ -1681,6 +1681,20 @@ static void game_changed_state(game_ui *ui, const game_state *oldstate, } } +static const char *current_key_label(const game_ui *ui, + const game_state *state, int button) +{ + int xi; + + if (ui->hshow && button == CURSOR_SELECT) + return ui->hpencil ? "Ink" : "Pencil"; + if (button == CURSOR_SELECT2) { + xi = state->common->xinfo[ui->hx + ui->hy*(state->common->params.w+2)]; + if (xi >= 0 && !state->common->fixed[xi]) return "Clear"; + } + return ""; +} + struct game_drawstate { int tilesize; bool started, solved; @@ -2783,6 +2797,7 @@ const struct game thegame = { decode_ui, game_request_keys, game_changed_state, + current_key_label, interpret_move, execute_move, PREFERRED_TILE_SIZE, game_compute_size, game_set_size, diff --git a/unequal.c b/unequal.c index a515545..5e9994f 100644 --- a/unequal.c +++ b/unequal.c @@ -1470,6 +1470,14 @@ static void game_changed_state(game_ui *ui, const game_state *oldstate, } } +static const char *current_key_label(const game_ui *ui, + const game_state *state, int button) +{ + if (ui->hshow && IS_CURSOR_SELECT(button)) + return ui->hpencil ? "Ink" : "Pencil"; + return ""; +} + struct game_drawstate { int tilesize, order; bool started; @@ -2139,6 +2147,7 @@ const struct game thegame = { decode_ui, game_request_keys, game_changed_state, + current_key_label, interpret_move, execute_move, PREFERRED_TILE_SIZE, game_compute_size, game_set_size, diff --git a/unfinished/group.c b/unfinished/group.c index 562dcbb..e72e3ce 100644 --- a/unfinished/group.c +++ b/unfinished/group.c @@ -1326,6 +1326,26 @@ static void game_changed_state(game_ui *ui, const game_state *oldstate, } } +static const char *current_key_label(const game_ui *ui, + const game_state *state, int button) +{ + if (ui->hshow && button == CURSOR_SELECT) + return ui->hpencil ? "Ink" : "Pencil"; + if (ui->hshow && button == CURSOR_SELECT2) { + int w = state->par.w; + int i; + for (i = 0; i < ui->odn; i++) { + int x = state->sequence[ui->ohx + i*ui->odx]; + int y = state->sequence[ui->ohy + i*ui->ody]; + int index = y*w+x; + if (ui->hpencil && state->grid[index]) return ""; + if (state->common->immutable[index]) return ""; + } + return "Clear"; + } + return ""; +} + #define PREFERRED_TILESIZE 48 #define TILESIZE (ds->tilesize) #define BORDER (TILESIZE / 2) @@ -2311,6 +2331,7 @@ const struct game thegame = { decode_ui, NULL, /* game_request_keys */ game_changed_state, + current_key_label, interpret_move, execute_move, PREFERRED_TILESIZE, game_compute_size, game_set_size, diff --git a/unfinished/separate.c b/unfinished/separate.c index daa03c7..1a8edc6 100644 --- a/unfinished/separate.c +++ b/unfinished/separate.c @@ -845,6 +845,7 @@ const struct game thegame = { decode_ui, NULL, /* game_request_keys */ game_changed_state, + NULL, /* current_key_label */ interpret_move, execute_move, 20 /* FIXME */, game_compute_size, game_set_size, diff --git a/unfinished/slide.c b/unfinished/slide.c index 9c26acc..acddd93 100644 --- a/unfinished/slide.c +++ b/unfinished/slide.c @@ -2337,6 +2337,7 @@ const struct game thegame = { decode_ui, NULL, /* game_request_keys */ game_changed_state, + NULL, /* current_key_label */ interpret_move, execute_move, PREFERRED_TILESIZE, game_compute_size, game_set_size, diff --git a/unfinished/sokoban.c b/unfinished/sokoban.c index 4f37085..26c957c 100644 --- a/unfinished/sokoban.c +++ b/unfinished/sokoban.c @@ -1459,6 +1459,7 @@ const struct game thegame = { decode_ui, NULL, /* game_request_keys */ game_changed_state, + NULL, /* current_key_label */ interpret_move, execute_move, PREFERRED_TILESIZE, game_compute_size, game_set_size, diff --git a/unruly.c b/unruly.c index 9a0a6c9..7a5512e 100644 --- a/unruly.c +++ b/unruly.c @@ -1535,6 +1535,27 @@ static void game_changed_state(game_ui *ui, const game_state *oldstate, { } +static const char *current_key_label(const game_ui *ui, + const game_state *state, int button) +{ + int hx = ui->cx, hy = ui->cy; + int w2 = state->w2; + char i = state->grid[hy * w2 + hx]; + + if (ui->cursor && IS_CURSOR_SELECT(button)) { + if (state->common->immutable[hy * w2 + hx]) return ""; + switch (i) { + case EMPTY: + return button == CURSOR_SELECT ? "Black" : "White"; + case N_ONE: + return button == CURSOR_SELECT ? "White" : "Empty"; + case N_ZERO: + return button == CURSOR_SELECT ? "Empty" : "Black"; + } + } + return ""; +} + struct game_drawstate { int tilesize; int w2, h2; @@ -2021,6 +2042,7 @@ const struct game thegame = { decode_ui, NULL, /* game_request_keys */ game_changed_state, + current_key_label, interpret_move, execute_move, DEFAULT_TILE_SIZE, game_compute_size, game_set_size, diff --git a/untangle.c b/untangle.c index df30e1d..06ed90c 100644 --- a/untangle.c +++ b/untangle.c @@ -1481,6 +1481,7 @@ const struct game thegame = { decode_ui, NULL, /* game_request_keys */ game_changed_state, + NULL, /* current_key_label */ interpret_move, execute_move, PREFERRED_TILESIZE, game_compute_size, game_set_size,