puzzles: Follow cursor in zoom mode and general code cleanup.

Frontends now have a way to retrieve the backend cursor position with some
changes I've submitted upstream. With this information, we can now follow
the cursor around in "interaction mode" while zoomed in, eliminating (most)
need for mode switching.

Also does some cleanup of the frontend code.

Change-Id: I1ba118f67564a3baed95435f5619b73cfa3ae87a
This commit is contained in:
Franklin Wei 2020-07-06 22:59:58 -04:00
parent 443ad25e75
commit 5094aaa4d4
48 changed files with 834 additions and 25 deletions

View file

@ -37,6 +37,7 @@
#include "src/puzzles.h"
#include "lib/helper.h"
#include "lib/keymaps.h"
#include "lib/playback_control.h"
#include "lib/simple_viewer.h"
@ -139,6 +140,7 @@ extern bool audiobuf_available; /* defined in rbmalloc.c */
static fb_data *zoom_fb; /* dynamically allocated */
static int zoom_x = -1, zoom_y = -1, zoom_w, zoom_h, zoom_clipu, zoom_clipd, zoom_clipl, zoom_clipr;
static bool zoom_force_center;
static int cur_font = FONT_UI;
static bool need_draw_update = false;
@ -171,7 +173,7 @@ static bool load_success;
/* ...did I mention there's a secret debug menu? */
static struct {
int slowmo_factor;
bool timerflash, clipoff, shortcuts, no_aa, polyanim;
bool timerflash, clipoff, shortcuts, no_aa, polyanim, highlight_cursor;
} debug_settings;
// used in menu titles - make sure to initialize!
@ -337,7 +339,7 @@ static void zoom_mono_bitmap(const unsigned char *bits, int x, int y, int w, int
static void zoom_alpha_bitmap(const unsigned char *bits, int x, int y, int w, int h)
{
const unsigned char *ptr = bits;
unsigned char buf;
unsigned char buf = 0;
int n_read = 0; /* how many 4-bit nibbles we've read (read new when even) */
unsigned int pix = rb->lcd_get_foreground();
@ -751,7 +753,7 @@ static void draw_antialiased_line(fb_data *fb, int w, int h, int x0, int y0, int
dx = x1 - x0;
dy = y1 - y0;
if(!(dx << FRACBITS))
if((dx << FRACBITS) == 0)
return; /* bail out */
long gradient = fp_div(dy << FRACBITS, dx << FRACBITS, FRACBITS);
@ -1349,6 +1351,16 @@ static void rb_start_draw(void *handle)
static void rb_end_draw(void *handle)
{
(void) handle;
if(debug_settings.highlight_cursor)
{
rb->lcd_set_foreground(LCD_RGBPACK(255,0,255));
int x, y, w, h;
midend_get_cursor_location(me, &x, &y, &w, &h);
if(x >= 0)
rb->lcd_drawrect(x, y, w, h);
}
/* we ignore the backend's redraw requests and just
* unconditionally update everything */
#if 0
@ -1991,6 +2003,47 @@ static void zoom_clamp_panning(void) {
zoom_x = zoom_w - LCD_WIDTH;
}
static bool point_in_rect(int px, int py,
int rx, int ry,
int rw, int rh) {
return (rx <= px && px < rx + rw) && (ry <= py && py < ry + rh);
}
static void zoom_center_on_cursor(void) {
/* get cursor bounding rectangle */
int x, y, w, h;
midend_get_cursor_location(me, &x, &y, &w, &h);
/* no cursor */
if(x < 0)
return;
/* check if either of the top-left and bottom-right corners are
* off-screen */
bool off_screen = (!point_in_rect(x, y, zoom_x, zoom_y, LCD_WIDTH, LCD_HEIGHT) ||
!point_in_rect(x + w, y + h, zoom_x, zoom_y, LCD_WIDTH, LCD_HEIGHT));
if(off_screen || zoom_force_center)
{
/* if so, recenter */
int cx, cy;
cx = x + w / 2;
cy = y + h / 2;
bool x_pan = x < zoom_x || zoom_x + LCD_WIDTH <= x + w;
if(x_pan || zoom_force_center)
zoom_x = cx - LCD_WIDTH / 2;
bool y_pan = y < zoom_y || zoom_y + LCD_HEIGHT <= y + h;
if(y_pan || zoom_force_center)
zoom_y = cy - LCD_HEIGHT / 2;
zoom_clamp_panning();
}
}
/* This function handles zoom mode, where the user can either pan
* around a zoomed-in image or play a zoomed-in version of the game. */
static void zoom(void)
@ -2137,6 +2190,8 @@ static void zoom(void)
if(timer_on)
timer_cb();
zoom_center_on_cursor();
midend_redraw(me);
/* blit */
@ -2624,6 +2679,7 @@ static void init_default_settings(void)
debug_settings.shortcuts = false;
debug_settings.no_aa = false;
debug_settings.polyanim = false;
debug_settings.highlight_cursor = false;
}
#ifdef DEBUG_MENU
@ -2672,6 +2728,8 @@ static void debug_menu(void)
"Toggle send keys on release",
"Toggle ignore repeats",
"Toggle right-click on hold vs. dragging",
"Toggle highlight cursor region",
"Toggle force zoom on center",
"Back");
bool quit = false;
int sel = 0;
@ -2693,37 +2751,43 @@ static void debug_menu(void)
break;
}
case 2:
debug_settings.timerflash = !debug_settings.timerflash;
debug_settings.timerflash ^= true;
break;
case 3:
debug_settings.clipoff = !debug_settings.clipoff;
debug_settings.clipoff ^= true;
break;
case 4:
debug_settings.shortcuts = !debug_settings.shortcuts;
debug_settings.shortcuts ^= true;
break;
case 5:
debug_settings.no_aa = !debug_settings.no_aa;
debug_settings.no_aa ^= true;
break;
case 6:
bench_aa();
break;
case 7:
debug_settings.polyanim = !debug_settings.polyanim;
debug_settings.polyanim ^= true;
break;
case 8:
mouse_mode = !mouse_mode;
mouse_mode ^= true;
break;
case 9:
input_settings.want_spacebar = !input_settings.want_spacebar;
input_settings.want_spacebar ^= true;
break;
case 10:
input_settings.falling_edge = !input_settings.falling_edge;
input_settings.falling_edge ^= true;
break;
case 11:
input_settings.ignore_repeats = !input_settings.ignore_repeats;
input_settings.ignore_repeats ^= true;
break;
case 12:
input_settings.rclick_on_hold = !input_settings.rclick_on_hold;
input_settings.rclick_on_hold ^= true;
break;
case 13:
debug_settings.highlight_cursor ^= true;
break;
case 14:
zoom_force_center ^= true;
break;
default:
quit = true;
@ -2952,10 +3016,10 @@ static void init_tlsf(void)
init_memory_pool(giant_buffer_len, giant_buffer);
}
static int read_wrapper(void *ptr, void *buf, int len)
static bool read_wrapper(void *ptr, void *buf, int len)
{
int fd = (int) ptr;
return rb->read(fd, buf, len);
return rb->read(fd, buf, len) == len;
}
static void write_wrapper(void *ptr, const void *buf, int len)
@ -3067,6 +3131,12 @@ static void tune_input(const char *name)
};
input_settings.numerical_chooser = string_in_list(name, number_chooser_games);
static const char *force_center_games[] = {
"Inertia",
NULL
};
zoom_force_center = string_in_list(name, force_center_games);
}
static const char *init_for_game(const game *gm, int load_fd)
@ -3241,7 +3311,7 @@ static void save_fonts(void)
final |= oldmask;
uint32_t left = final >> 31;
uint32_t right = final & 0x7fffffff;
rb->fdprintf(outfd, "%s:%lu:%lu\n", midend_which_game(me)->name, left, right);
rb->fdprintf(outfd, "%s:%u:%u\n", midend_which_game(me)->name, (unsigned)left, (unsigned)right);
rb->close(outfd);
rb->rename(FONT_TABLE ".tmp", FONT_TABLE);
}

View file

@ -1100,6 +1100,20 @@ badmove:
return NULL;
}
static void game_get_cursor_location(const game_ui *ui,
const game_drawstate *ds,
const game_state *state,
const game_params *params,
int *x, int *y, int *w, int *h)
{
if(ui->cur_visible) {
*x = TODRAW(ui->cur_x);
*y = TODRAW(ui->cur_y);
*w = *h = TILE_SIZE;
}
}
/* ----------------------------------------------------------------------
* Drawing routines.
*/
@ -1544,6 +1558,7 @@ const struct game thegame = {
game_redraw,
game_anim_length,
game_flash_length,
game_get_cursor_location,
game_status,
false, false, game_print_size, game_print,
true, /* wants_statusbar */

View file

@ -2146,6 +2146,20 @@ struct game_drawstate {
bool started, dragging;
};
static void game_get_cursor_location(const game_ui *ui,
const game_drawstate *ds,
const game_state *state,
const game_params *params,
int *x, int *y, int *w, int *h)
{
if(ui->cur_visible) {
*x = COORD(ui->cur_x);
*y = COORD(ui->cur_y);
*w = *h = TILE_SIZE;
}
}
/*
* The contents of ds->grid are complicated, because of the circular
* islands which overlap their own grid square into neighbouring
@ -3267,6 +3281,7 @@ const struct game thegame = {
game_redraw,
game_anim_length,
game_flash_length,
game_get_cursor_location,
game_status,
true, false, game_print_size, game_print,
false, /* wants_statusbar */

View file

@ -1535,6 +1535,27 @@ static void game_free_drawstate(drawing *dr, game_drawstate *ds)
sfree(ds);
}
static void game_get_cursor_location(const game_ui *ui,
const game_drawstate *ds,
const game_state *state,
const game_params *params,
int *x, int *y, int *w, int *h)
{
struct bbox bb;
bb.l = 2.0F * (params->d1 + params->d2);
bb.r = -2.0F * (params->d1 + params->d2);
bb.u = 2.0F * (params->d1 + params->d2);
bb.d = -2.0F * (params->d1 + params->d2);
find_bbox_callback(&bb, state->grid->squares + state->current);
*x = ((int)(bb.l * GRID_SCALE) + ds->ox);
*y = ((int)(bb.u * GRID_SCALE) + ds->oy);
*w = (bb.r - bb.l) * GRID_SCALE;
*h = (bb.d - bb.u) * GRID_SCALE;
}
static void game_redraw(drawing *dr, game_drawstate *ds,
const game_state *oldstate, const game_state *state,
int dir, const game_ui *ui,
@ -1762,6 +1783,7 @@ const struct game thegame = {
game_redraw,
game_anim_length,
game_flash_length,
game_get_cursor_location,
game_status,
false, false, game_print_size, game_print,
true, /* wants_statusbar */

View file

@ -1289,6 +1289,50 @@ a mine from the colour it uses when you complete the game. In order
to achieve this, its \cw{flash_length()} function has to store a
flag in the \c{game_ui} to indicate which flash type is required.)
\S{backend-get-cursor-location} \cw{get_cursor_location()}
\c void (*get_cursor_location)(const game_ui *ui,
\c const game_drawstate *ds,
\c const game_state *state,
\c const game_params *params,
\c int *x, int *y, int *w, int *h);
This function queries the backend for the rectangular region
containing the cursor (in games that have one), or other region of
interest.
This function is called by only
\cw{midend_get_cursor_location()}(\k{midend-get-cursor-location}). Its
purpose is to allow frontends to query the location of the backend's
cursor. With knowledge of this location, a frontend can, for example,
ensure that the region of interest remains visible if the puzzle is
too big to fit on the screen at once.
On returning, \cw{*x}, \cw{*y} should be set to the X and Y
coordinates of the upper-left corner of the rectangular region of
interest, and \cw{*w} and \cw{*h} should be the width and height of
that region, respectively. All return values are in units of pixels in
screenspace coordinates. In the event that a cursor is not visible on
screen, this function should return and leave the return parameters
untouched \dash the mid-end will notice this. The backend need not
bother checking that \cw{x}, \cw{y}, \cw{w} and \cw{h} are
non-\cw{NULL} \dash the mid-end guarantees that they will not be.
Defining what constitutes a \q{region of interest} is left up to the
backend. If a game provides a conventional cursor \dash such as Mines,
Solo, or any of the other grid-based games \dash the most logical choice
is of course the cursor location. However, in other cases such as Cube
or Inertia, there is no \q{cursor} in the conventional sense \dash the
player controls an object moving around the screen. In these cases, it
makes sense to define the region of interest as the bounding box of
the player or another sensible region \dash such as the grid square the
player is sitting on in Cube.
If a backend does not provide a cursor mechanism at all, the backend
is free to provide an empty implementation of this function, or a
\cw{NULL} pointer in the \cw{game} structure \dash the mid-end will
notice either of these cases and behave appropriately.
\S{backend-status} \cw{status()}
\c int (*status)(const game_state *state);
@ -1586,10 +1630,10 @@ 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.
The field for this function pointer 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()}
@ -3307,6 +3351,30 @@ The front end can expect its drawing API and/or
function. Some back ends require that \cw{midend_size()}
(\k{midend-size}) is called before \cw{midend_solve()}.
\H{midend-get-cursor-location} \cw{midend_get_cursor_location()}
\c void midend_get_cursor_location(midend *me,
\c int *x, int *y, int *w, int *h);
This function returns the location of the backend's on-screen cursor
or other region of interest in the parameters \cw{*x}, \cw{*y},
\cw{*w} and \cw{*h}, which describe a rectangle with an upper-left
corner at \cw{(*x,*y)} and a size of \cw{*w} pixels wide by \cw{*h}
pixels tall. The mid-end will ignore any return parameters that may be
equal to \cw{NULL}.
What exactly this region contains is up to the backend, but in general
the region will be an area that the player is controlling with the
cursor keys \dash such as the player location in Cube and Inertia, or
the cursor in any of the conventional grid-based games. With knowledge
of this location, a frontend can, for example, ensure that the region
of interest remains visible even if the entire puzzle is too big to
fit on the screen.
If there is no such region (if either the cursor is not visible, or if
the game does not have cursor support), both \cw{*x} and \cw{*y} will
be set to \cw{-1}.
\H{midend-status} \cw{midend_status()}
\c int midend_status(midend *me);

View file

@ -3328,6 +3328,20 @@ static float game_flash_length(const game_state *oldstate,
return 0.0F;
}
static void game_get_cursor_location(const game_ui *ui,
const game_drawstate *ds,
const game_state *state,
const game_params *params,
int *x, int *y, int *w, int *h)
{
if(ui->cur_visible)
{
*x = BORDER + ((2 * ui->cur_x + 1) * TILESIZE) / 4;
*y = BORDER + ((2 * ui->cur_y + 1) * TILESIZE) / 4;
*w = *h = TILESIZE / 2 + 2;
}
}
static int game_status(const game_state *state)
{
return state->completed ? +1 : 0;
@ -3422,6 +3436,7 @@ const struct game thegame = {
game_redraw,
game_anim_length,
game_flash_length,
game_get_cursor_location,
game_status,
true, false, game_print_size, game_print,
false, /* wants_statusbar */

View file

@ -1061,6 +1061,17 @@ static float game_flash_length(const game_state *oldstate,
return 0.0F;
}
static void game_get_cursor_location(const game_ui *ui,
const game_drawstate *ds,
const game_state *state,
const game_params *params,
int *x, int *y, int *w, int *h)
{
*x = COORD(X(state, state->gap_pos));
*y = COORD(Y(state, state->gap_pos));
*w = *h = TILE_SIZE;
}
static int game_status(const game_state *state)
{
return state->completed ? +1 : 0;
@ -1115,6 +1126,7 @@ const struct game thegame = {
game_redraw,
game_anim_length,
game_flash_length,
game_get_cursor_location,
game_status,
false, false, game_print_size, game_print,
true, /* wants_statusbar */

View file

@ -2060,6 +2060,20 @@ static float game_flash_length(const game_state *oldstate,
return 0.0F;
}
static void game_get_cursor_location(const game_ui *ui,
const game_drawstate *ds,
const game_state *state,
const game_params *params,
int *x, int *y, int *w, int *h)
{
if(ui->cur_visible)
{
*x = BORDER + ui->cur_x * TILE_SIZE;
*y = BORDER + ui->cur_y * TILE_SIZE;
*w = *h = TILE_SIZE;
}
}
static int game_status(const game_state *state)
{
return state->completed ? +1 : 0;
@ -2165,6 +2179,7 @@ const struct game thegame = {
game_redraw,
game_anim_length,
game_flash_length,
game_get_cursor_location,
game_status,
true, false, game_print_size, game_print,
false, /* wants_statusbar */

View file

@ -1290,6 +1290,20 @@ static float game_flash_length(const game_state *oldstate,
return 0.0F;
}
static void game_get_cursor_location(const game_ui *ui,
const game_drawstate *ds,
const game_state *state,
const game_params *params,
int *x, int *y, int *w, int *h)
{
if(ui->cdraw)
{
*x = COORD(ui->cx);
*y = COORD(ui->cy);
*w = *h = TILE_SIZE;
}
}
static int game_status(const game_state *state)
{
return state->completed ? +1 : 0;
@ -1344,6 +1358,7 @@ const struct game thegame = {
game_redraw,
game_anim_length,
game_flash_length,
game_get_cursor_location,
game_status,
false, false, game_print_size, game_print,
true, /* wants_statusbar */

View file

@ -1279,6 +1279,20 @@ static float game_anim_length(const game_state *oldstate,
return 0.0F;
}
static void game_get_cursor_location(const game_ui *ui,
const game_drawstate *ds,
const game_state *state,
const game_params *params,
int *x, int *y, int *w, int *h)
{
if(ui->cursor_visible)
{
*x = COORD(ui->cx);
*y = COORD(ui->cy);
*w = *h = TILESIZE;
}
}
static int game_status(const game_state *state)
{
if (state->complete && state->moves <= state->movelimit) {
@ -1361,6 +1375,7 @@ const struct game thegame = {
game_redraw,
game_anim_length,
game_flash_length,
game_get_cursor_location,
game_status,
false, false, game_print_size, game_print,
true, /* wants_statusbar */

View file

@ -382,12 +382,15 @@ static bool ok_to_add_assoc_with_opposite(
static void add_assoc_with_opposite(game_state *state, space *tile, space *dot) {
space *opposite = space_opposite_dot(state, tile, dot);
assert(ok_to_add_assoc_with_opposite_internal(state, tile, opposite));
if(opposite)
{
assert(ok_to_add_assoc_with_opposite_internal(state, tile, opposite));
remove_assoc_with_opposite(state, tile);
add_assoc(state, tile, dot);
remove_assoc_with_opposite(state, opposite);
add_assoc(state, opposite, dot);
remove_assoc_with_opposite(state, tile);
add_assoc(state, tile, dot);
remove_assoc_with_opposite(state, opposite);
add_assoc(state, opposite, dot);
}
}
static space *sp2dot(const game_state *state, int x, int y)
@ -3469,6 +3472,37 @@ static float game_flash_length(const game_state *oldstate,
return 0.0F;
}
static void game_get_cursor_location(const game_ui *ui,
const game_drawstate *ds,
const game_state *state,
const game_params *params,
int *x, int *y, int *w, int *h)
{
if(ui->cur_visible) {
space *sp = &SPACE(state, ui->cur_x, ui->cur_y);
if(sp->flags & F_DOT) {
*x = SCOORD(ui->cur_x) - DOT_SIZE;
*y = SCOORD(ui->cur_y) - DOT_SIZE;
*w = *h = 2 * DOT_SIZE + 1;
} else if(sp->type != s_tile) {
int dx = (ui->cur_x % 2) ? CURSOR_SIZE : CURSOR_SIZE/3;
int dy = (ui->cur_y % 2) ? CURSOR_SIZE : CURSOR_SIZE/3;
int x1 = SCOORD(ui->cur_x)-dx, y1 = SCOORD(ui->cur_y)-dy;
int xs = dx*2+1, ys = dy*2+1;
*x = x1;
*y = y1;
*w = xs;
*h = ys;
} else {
*x = SCOORD(ui->cur_x) - CURSOR_SIZE;
*y = SCOORD(ui->cur_y) - CURSOR_SIZE;
*w = *h = 2 * CURSOR_SIZE + 1;
}
}
}
static int game_status(const game_state *state)
{
return state->completed ? +1 : 0;
@ -3695,6 +3729,7 @@ const struct game thegame = {
game_redraw,
game_anim_length,
game_flash_length,
game_get_cursor_location,
game_status,
#ifdef EDITOR
false, false, NULL, NULL,

View file

@ -1448,6 +1448,20 @@ static float game_flash_length(const game_state *oldstate,
return 0.0F;
}
static void game_get_cursor_location(const game_ui *ui,
const game_drawstate *ds,
const game_state *state,
const game_params *params,
int *x, int *y, int *w, int *h)
{
if(ui->display_cur && !state->solved) {
*x = GUESS_X(state->next_go, ui->peg_cur) - CGAP;
*y = GUESS_Y(state->next_go, ui->peg_cur) - CGAP;
*w = *h = 2 * (PEGRAD + CGAP) + 1;
}
}
static int game_status(const game_state *state)
{
/*
@ -1508,6 +1522,7 @@ const struct game thegame = {
game_redraw,
game_anim_length,
game_flash_length,
game_get_cursor_location,
game_status,
false, false, game_print_size, game_print,
false, /* wants_statusbar */

View file

@ -2186,6 +2186,17 @@ static float game_flash_length(const game_state *oldstate,
return 0.0F;
}
static void game_get_cursor_location(const game_ui *ui,
const game_drawstate *ds,
const game_state *state,
const game_params *params,
int *x, int *y, int *w, int *h)
{
*x = ds->pbgx;
*y = ds->pbgy;
*w = *h = TILESIZE;
}
static int game_status(const game_state *state)
{
/*
@ -2245,6 +2256,7 @@ const struct game thegame = {
game_redraw,
game_anim_length,
game_flash_length,
game_get_cursor_location,
game_status,
false, false, game_print_size, game_print,
true, /* wants_statusbar */

View file

@ -2198,6 +2198,20 @@ static float game_flash_length(const game_state *oldstate,
return 0.0F;
}
static void game_get_cursor_location(const game_ui *ui,
const game_drawstate *ds,
const game_state *state,
const game_params *params,
int *x, int *y, int *w, int *h)
{
if(ui->hshow) {
*x = BORDER + ui->hx * TILESIZE + 1 + GRIDEXTRA;
*y = BORDER + ui->hy * TILESIZE + 1 + GRIDEXTRA;
*w = *h = TILESIZE-1-2*GRIDEXTRA;
}
}
static int game_status(const game_state *state)
{
return state->completed ? +1 : 0;
@ -2480,6 +2494,7 @@ const struct game thegame = {
game_redraw,
game_anim_length,
game_flash_length,
game_get_cursor_location,
game_status,
true, false, game_print_size, game_print,
false, /* wants_statusbar */

View file

@ -2217,6 +2217,19 @@ static float game_flash_length(const game_state *oldstate,
return 0.0F;
}
static void game_get_cursor_location(const game_ui *ui,
const game_drawstate *ds,
const game_state *state,
const game_params *params,
int *x, int *y, int *w, int *h)
{
if(ui->cur_visible) {
*x = COORD(ui->cur_x);
*y = COORD(ui->cur_y);
*w = *h = TILE_SIZE;
}
}
static int game_status(const game_state *state)
{
return state->completed ? +1 : 0;
@ -2325,6 +2338,7 @@ const struct game thegame = {
game_redraw,
game_anim_length,
game_flash_length,
game_get_cursor_location,
game_status,
true, false, game_print_size, game_print,
false, /* wants_statusbar */

View file

@ -3537,6 +3537,14 @@ static float game_flash_length(const game_state *oldstate,
return 0.0F;
}
static void game_get_cursor_location(const game_ui *ui,
const game_drawstate *ds,
const game_state *state,
const game_params *params,
int *x, int *y, int *w, int *h)
{
}
static int game_status(const game_state *state)
{
return state->solved ? +1 : 0;
@ -3675,6 +3683,7 @@ const struct game thegame = {
game_redraw,
game_anim_length,
game_flash_length,
game_get_cursor_location,
game_status,
true, false, game_print_size, game_print,
false /* wants_statusbar */,

View file

@ -2291,6 +2291,19 @@ static float game_flash_length(const game_state *oldstate,
return 0.0F;
}
static void game_get_cursor_location(const game_ui *ui,
const game_drawstate *ds,
const game_state *state,
const game_params *params,
int *x, int *y, int *w, int *h)
{
if(ui->cur_visible) {
*x = COORD(ui->cur_x);
*y = COORD(ui->cur_y);
*w = *h = TILE_SIZE;
}
}
static int game_status(const game_state *state)
{
return state->completed ? +1 : 0;
@ -2432,6 +2445,7 @@ const struct game thegame = {
game_redraw,
game_anim_length,
game_flash_length,
game_get_cursor_location,
game_status,
true, false, game_print_size, game_print,
false, /* wants_statusbar */

View file

@ -3061,6 +3061,19 @@ static float game_flash_length(const game_state *oldstate,
return 0.0F;
}
static void game_get_cursor_location(const game_ui *ui,
const game_drawstate *ds,
const game_state *state,
const game_params *params,
int *x, int *y, int *w, int *h)
{
if(ui->cur_visible) {
*x = COORD(ui->cur_x);
*y = COORD(ui->cur_y);
*w = *h = TILESIZE;
}
}
static int game_status(const game_state *state)
{
return state->completed ? +1 : 0;
@ -3260,6 +3273,7 @@ const struct game thegame = {
game_redraw,
game_anim_length,
game_flash_length,
game_get_cursor_location,
game_status,
true, true, game_print_size, game_print,
false, /* wants_statusbar */

View file

@ -1464,6 +1464,25 @@ void midend_request_id_changes(midend *me, void (*notify)(void *), void *ctx)
me->game_id_change_notify_ctx = ctx;
}
void midend_get_cursor_location(midend *me, int *x_out, int *y_out, int *w_out, int *h_out)
{
int x, y, w, h;
x = y = -1;
w = h = 1;
if(me->ourgame->get_cursor_location)
me->ourgame->get_cursor_location(me->ui, me->drawstate, me->states[me->statepos-1].state, me->params, &x, &y, &w, &h);
if(x_out)
*x_out = x;
if(y_out)
*y_out = y;
if(w_out)
*w_out = w;
if(h_out)
*h_out = h;
}
void midend_supersede_game_desc(midend *me, const char *desc,
const char *privdesc)
{

View file

@ -3152,6 +3152,19 @@ static float game_flash_length(const game_state *oldstate,
return 0.0F;
}
static void game_get_cursor_location(const game_ui *ui,
const game_drawstate *ds,
const game_state *state,
const game_params *params,
int *x, int *y, int *w, int *h)
{
if(ui->cur_visible) {
*x = COORD(ui->cur_x);
*y = COORD(ui->cur_y);
*w = *h = TILE_SIZE;
}
}
static int game_status(const game_state *state)
{
/*
@ -3213,6 +3226,7 @@ const struct game thegame = {
game_redraw,
game_anim_length,
game_flash_length,
game_get_cursor_location,
game_status,
false, false, game_print_size, game_print,
true, /* wants_statusbar */

View file

@ -3090,6 +3090,20 @@ static float game_flash_length(const game_state *oldstate,
return 0.0F;
}
static void game_get_cursor_location(const game_ui *ui,
const game_drawstate *ds,
const game_state *state,
const game_params *params,
int *x, int *y, int *w, int *h)
{
if(ui->cur_visible) {
*x = WINDOW_OFFSET + TILE_SIZE * ui->cur_x;
*y = WINDOW_OFFSET + TILE_SIZE * ui->cur_y;
*w = *h = TILE_SIZE;
}
}
static int game_status(const game_state *state)
{
return state->completed ? +1 : 0;
@ -3271,6 +3285,7 @@ const struct game thegame = {
game_redraw,
game_anim_length,
game_flash_length,
game_get_cursor_location,
game_status,
true, false, game_print_size, game_print,
true, /* wants_statusbar */

View file

@ -1830,6 +1830,20 @@ static float game_flash_length(const game_state *oldstate,
return 0.0F;
}
static void game_get_cursor_location(const game_ui *ui,
const game_drawstate *ds,
const game_state *state,
const game_params *params,
int *x, int *y, int *w, int *h)
{
if(ui->cur_visible) {
*x = BORDER + WINDOW_OFFSET + TILE_SIZE * ui->cur_x;
*y = BORDER + WINDOW_OFFSET + TILE_SIZE * ui->cur_y;
*w = *h = TILE_SIZE;
}
}
static int game_status(const game_state *state)
{
return state->completed ? +1 : 0;
@ -1884,6 +1898,7 @@ const struct game thegame = {
game_redraw,
game_anim_length,
game_flash_length,
game_get_cursor_location,
game_status,
false, false, game_print_size, game_print,
true, /* wants_statusbar */

View file

@ -242,6 +242,14 @@ static float game_flash_length(const game_state *oldstate,
return 0.0F;
}
static void game_get_cursor_location(const game_ui *ui,
const game_drawstate *ds,
const game_state *state,
const game_params *params,
int *x, int *y, int *w, int *h)
{
}
static int game_status(const game_state *state)
{
return 0;
@ -296,6 +304,7 @@ const struct game thegame = {
game_redraw,
game_anim_length,
game_flash_length,
game_get_cursor_location,
game_status,
false, false, game_print_size, game_print,
false, /* wants_statusbar */

View file

@ -1288,6 +1288,19 @@ static float game_flash_length(const game_state *oldstate,
return 0.0F;
}
static void game_get_cursor_location(const game_ui *ui,
const game_drawstate *ds,
const game_state *state,
const game_params *params,
int *x, int *y, int *w, int *h)
{
if(ui->show) {
*x = MARGIN + TILESIZE * ui->x;
*y = MARGIN + TILESIZE * ui->y;
*w = *h = TILESIZE;
}
}
static int game_status(const game_state *state)
{
return state->completed ? +1 : 0;
@ -1401,6 +1414,7 @@ const struct game thegame = {
game_redraw,
game_anim_length,
game_flash_length,
game_get_cursor_location,
game_status,
true, false, game_print_size, game_print,
true, /* wants_statusbar */

View file

@ -1918,6 +1918,19 @@ static float game_flash_length(const game_state *oldstate,
return 0.0F;
}
static void game_get_cursor_location(const game_ui *ui,
const game_drawstate *ds,
const game_state *state,
const game_params *params,
int *x, int *y, int *w, int *h)
{
if(ui->cur_visible) {
*x = TOCOORD(ds->w, ui->cur_x);
*y = TOCOORD(ds->h, ui->cur_y);
*w = *h = TILE_SIZE;
}
}
static int game_status(const game_state *state)
{
return state->completed ? +1 : 0;
@ -2029,6 +2042,7 @@ const struct game thegame = {
game_redraw,
game_anim_length,
game_flash_length,
game_get_cursor_location,
game_status,
true, false, game_print_size, game_print,
false, /* wants_statusbar */

View file

@ -2542,6 +2542,19 @@ static float game_flash_length(const game_state *oldstate,
return 0.0F;
}
static void game_get_cursor_location(const game_ui *ui,
const game_drawstate *ds,
const game_state *state,
const game_params *params,
int *x, int *y, int *w, int *h)
{
if(ui->cursor_active) {
*x = COORD(ui->curx);
*y = COORD(ui->cury);
*w = *h = TILE_SIZE;
}
}
static int game_status(const game_state *state)
{
return state->completed ? +1 : 0;
@ -2635,6 +2648,7 @@ const struct game thegame = {
game_redraw,
game_anim_length,
game_flash_length,
game_get_cursor_location,
game_status,
true, false, game_print_size, game_print,
false, /* wants_statusbar */

View file

@ -1280,6 +1280,19 @@ static float game_flash_length(const game_state *oldstate,
return 0.0F;
}
static void game_get_cursor_location(const game_ui *ui,
const game_drawstate *ds,
const game_state *state,
const game_params *params,
int *x, int *y, int *w, int *h)
{
if(ui->cur_visible) {
*x = COORD(ui->cur_x);
*y = COORD(ui->cur_y);
*w = *h = TILESIZE;
}
}
static int game_status(const game_state *state)
{
/*
@ -1338,6 +1351,7 @@ const struct game thegame = {
game_redraw,
game_anim_length,
game_flash_length,
game_get_cursor_location,
game_status,
false, false, game_print_size, game_print,
false, /* wants_statusbar */

View file

@ -347,6 +347,8 @@ const char *identify_game(char **name,
bool (*read)(void *ctx, void *buf, int len),
void *rctx);
void midend_request_id_changes(midend *me, void (*notify)(void *), void *ctx);
void midend_get_cursor_location(midend *me, int *x, int *y, int *w, int *h);
/* Printing functions supplied by the mid-end */
const char *midend_print_puzzle(midend *me, document *doc, bool with_soln);
int midend_tilesize(midend *me);
@ -684,6 +686,11 @@ struct game {
const game_state *newstate, int dir, game_ui *ui);
float (*flash_length)(const game_state *oldstate,
const game_state *newstate, int dir, game_ui *ui);
void (*get_cursor_location)(const game_ui *ui,
const game_drawstate *ds,
const game_state *state,
const game_params *params,
int *x, int *y, int *w, int *h);
int (*status)(const game_state *state);
bool can_print, can_print_in_colour;
void (*print_size)(const game_params *params, float *x, float *y);

View file

@ -1572,6 +1572,19 @@ static float game_flash_length(const game_state *from,
return 0.0F;
}
static void game_get_cursor_location(const game_ui *ui,
const game_drawstate *ds,
const game_state *state,
const game_params *params,
int *x, int *y, int *w, int *h)
{
if(ui->cursor_show) {
*x = BORDER + TILESIZE * ui->c;
*y = BORDER + TILESIZE * ui->r;
*w = *h = TILESIZE;
}
}
static int game_status(const game_state *state)
{
return state->was_solved ? +1 : 0;
@ -1823,6 +1836,7 @@ struct game const thegame = {
game_redraw,
game_anim_length,
game_flash_length,
game_get_cursor_location,
game_status,
true, false, game_print_size, game_print,
false, /* wants_statusbar */

View file

@ -2883,6 +2883,19 @@ static float game_flash_length(const game_state *oldstate,
return 0.0F;
}
static void game_get_cursor_location(const game_ui *ui,
const game_drawstate *ds,
const game_state *state,
const game_params *params,
int *x, int *y, int *w, int *h)
{
if(ui->cur_visible) {
*x = COORD(ui->cur_x);
*y = COORD(ui->cur_y);
*w = *h = TILE_SIZE;
}
}
static int game_status(const game_state *state)
{
return state->completed ? +1 : 0;
@ -2994,6 +3007,7 @@ const struct game thegame = {
game_redraw,
game_anim_length,
game_flash_length,
game_get_cursor_location,
game_status,
true, false, game_print_size, game_print,
true, /* wants_statusbar */

View file

@ -1615,6 +1615,19 @@ static float game_flash_length(const game_state *oldstate,
return 0.0F;
}
static void game_get_cursor_location(const game_ui *ui,
const game_drawstate *ds,
const game_state *state,
const game_params *params,
int *x, int *y, int *w, int *h)
{
if(ui->displaysel) {
*x = COORD(ui->xsel);
*y = COORD(ui->ysel);
*w = *h = TILE_SIZE;
}
}
static int game_status(const game_state *state)
{
/*
@ -1673,6 +1686,7 @@ const struct game thegame = {
game_redraw,
game_anim_length,
game_flash_length,
game_get_cursor_location,
game_status,
false, false, game_print_size, game_print,
true, /* wants_statusbar */

View file

@ -2188,6 +2188,19 @@ static float game_flash_length(const game_state *oldstate,
return 0.0F;
}
static void game_get_cursor_location(const game_ui *ui,
const game_drawstate *ds,
const game_state *state,
const game_params *params,
int *x, int *y, int *w, int *h)
{
if(ui->cshow) {
*x = COORD(ui->cx);
*y = COORD(ui->cy);
*w = *h = TILE_SIZE;
}
}
static int game_status(const game_state *state)
{
return state->completed ? +1 : 0;
@ -2275,6 +2288,7 @@ const struct game thegame = {
game_redraw,
game_anim_length,
game_flash_length,
game_get_cursor_location,
game_status,
true, false, game_print_size, game_print,
false, /* wants_statusbar */

View file

@ -1758,6 +1758,19 @@ static float game_flash_length(const game_state *oldstate,
return 0.0F;
}
static void game_get_cursor_location(const game_ui *ui,
const game_drawstate *ds,
const game_state *state,
const game_params *params,
int *x, int *y, int *w, int *h)
{
if(ui->cshow) {
*x = COORD(ui->cx);
*y = COORD(ui->cy);
*w = *h = TILE_SIZE;
}
}
static int game_status(const game_state *state)
{
return state->completed ? +1 : 0;
@ -1850,6 +1863,7 @@ const struct game thegame = {
game_redraw,
game_anim_length,
game_flash_length,
game_get_cursor_location,
game_status,
true, false, game_print_size, game_print,
false, /* wants_statusbar */

View file

@ -1147,6 +1147,19 @@ static float game_flash_length(const game_state *oldstate,
return 0.0F;
}
static void game_get_cursor_location(const game_ui *ui,
const game_drawstate *ds,
const game_state *state,
const game_params *params,
int *x, int *y, int *w, int *h)
{
if(ui->cur_visible) {
*x = COORD(ui->cur_x);
*y = COORD(ui->cur_y);
*w = *h = TILE_SIZE;
}
}
static int game_status(const game_state *state)
{
return state->completed ? +1 : 0;
@ -1201,6 +1214,7 @@ const struct game thegame = {
game_redraw,
game_anim_length,
game_flash_length,
game_get_cursor_location,
game_status,
false, false, game_print_size, game_print,
true, /* wants_statusbar */

View file

@ -2064,6 +2064,19 @@ static float game_flash_length(const game_state *oldstate,
return 0.0F;
}
static void game_get_cursor_location(const game_ui *ui,
const game_drawstate *ds,
const game_state *state,
const game_params *params,
int *x, int *y, int *w, int *h)
{
if(ui->cur_visible) {
*x = COORD(ui->cur_x);
*y = COORD(ui->cur_y);
*w = *h = TILESIZE;
}
}
static int game_status(const game_state *state)
{
return state->completed ? +1 : 0;
@ -2181,6 +2194,7 @@ const struct game thegame = {
game_redraw,
game_anim_length,
game_flash_length,
game_get_cursor_location,
game_status,
true, false, game_print_size, game_print,
false, /* wants_statusbar */

View file

@ -5297,6 +5297,19 @@ static float game_flash_length(const game_state *oldstate,
return 0.0F;
}
static void game_get_cursor_location(const game_ui *ui,
const game_drawstate *ds,
const game_state *state,
const game_params *params,
int *x, int *y, int *w, int *h)
{
if(ui->hshow) {
*x = BORDER + ui->hx * TILE_SIZE + 1 + GRIDEXTRA;
*y = BORDER + ui->hy * TILE_SIZE + 1 + GRIDEXTRA;
*w = *h = TILE_SIZE;
}
}
static int game_status(const game_state *state)
{
return state->completed ? +1 : 0;
@ -5622,6 +5635,7 @@ const struct game thegame = {
game_redraw,
game_anim_length,
game_flash_length,
game_get_cursor_location,
game_status,
true, false, game_print_size, game_print,
false, /* wants_statusbar */

View file

@ -2554,6 +2554,19 @@ static float game_flash_length(const game_state *oldstate,
return 0.0F;
}
static void game_get_cursor_location(const game_ui *ui,
const game_drawstate *ds,
const game_state *state,
const game_params *params,
int *x, int *y, int *w, int *h)
{
if(ui->cdisp) {
*x = COORD(ui->cx);
*y = COORD(ui->cy);
*w = *h = TILESIZE;
}
}
static int game_status(const game_state *state)
{
return state->completed ? +1 : 0;
@ -2630,6 +2643,7 @@ const struct game thegame = {
game_redraw,
game_anim_length,
game_flash_length,
game_get_cursor_location,
game_status,
true, false, game_print_size, game_print,
false, /* wants_statusbar */

View file

@ -1935,6 +1935,19 @@ static float game_flash_length(const game_state *oldstate,
return 0.0F;
}
static void game_get_cursor_location(const game_ui *ui,
const game_drawstate *ds,
const game_state *state,
const game_params *params,
int *x, int *y, int *w, int *h)
{
if(ui->hshow) {
*x = COORD(ui->hx);
*y = COORD(ui->hy);
*w = *h = TILESIZE;
}
}
static int game_status(const game_state *state)
{
return state->completed ? +1 : 0;
@ -2060,6 +2073,7 @@ const struct game thegame = {
game_redraw,
game_anim_length,
game_flash_length,
game_get_cursor_location,
game_status,
true, false, game_print_size, game_print,
false, /* wants_statusbar */

View file

@ -2854,6 +2854,37 @@ static float game_flash_length(const game_state *oldstate, const game_state *new
return 0.0F;
}
static void game_get_cursor_location(const game_ui *ui,
const game_drawstate *ds,
const game_state *state,
const game_params *params,
int *x, int *y, int *w, int *h)
{
if(ui->cursor_active) {
int off = HALFSZ / 4;
int cx = COORD(ui->curx / 2) + off;
int cy = COORD(ui->cury / 2) + off;
int cw, ch;
cw = ch = TILE_SIZE - (2*off) + 1;
if(ui->curx % 2 == 0) {
/* left border */
cx -= off;
cw = 2 * off + 1;
}
if(ui->cury % 2 == 0) {
/* upper border */
cy -= off;
ch = 2 * off + 1;
}
*x = cx;
*y = cy;
*w = cw;
*h = ch;
}
}
static int game_status(const game_state *state)
{
return state->completed ? +1 : 0;
@ -2948,6 +2979,7 @@ const struct game thegame = {
game_redraw,
game_anim_length,
game_flash_length,
game_get_cursor_location,
game_status,
true, false, game_print_size, game_print,
false, /* wants_statusbar */

View file

@ -1092,6 +1092,19 @@ static float game_flash_length(const game_state *oldstate,
return 0.0F;
}
static void game_get_cursor_location(const game_ui *ui,
const game_drawstate *ds,
const game_state *state,
const game_params *params,
int *x, int *y, int *w, int *h)
{
if(ui->cur_visible) {
*x = COORD(ui->cur_x);
*y = COORD(ui->cur_y);
*w = *h = state->n * TILE_SIZE;
}
}
static int game_status(const game_state *state)
{
return state->completed ? +1 : 0;
@ -1318,6 +1331,7 @@ const struct game thegame = {
game_redraw,
game_anim_length,
game_flash_length,
game_get_cursor_location,
game_status,
false, false, game_print_size, game_print,
true, /* wants_statusbar */

View file

@ -2727,6 +2727,19 @@ static float game_flash_length(const game_state *oldstate,
!newstate->cheated) ? FLASH_TIME : 0.0F;
}
static void game_get_cursor_location(const game_ui *ui,
const game_drawstate *ds,
const game_state *state,
const game_params *params,
int *x, int *y, int *w, int *h)
{
if(ui->hshow) {
*x = BORDER + (ui->hx) * TILESIZE;
*y = BORDER + (ui->hy + 1) * TILESIZE;
*w = *h = TILESIZE;
}
}
static int game_status(const game_state *state)
{
return state->solved;
@ -2781,6 +2794,7 @@ const struct game thegame = {
game_redraw,
game_anim_length,
game_flash_length,
game_get_cursor_location,
game_status,
false, false, game_print_size, game_print,
false, /* wants_statusbar */

View file

@ -2041,6 +2041,19 @@ static float game_flash_length(const game_state *oldstate,
return 0.0F;
}
static void game_get_cursor_location(const game_ui *ui,
const game_drawstate *ds,
const game_state *state,
const game_params *params,
int *x, int *y, int *w, int *h)
{
if(ui->hshow) {
*x = COORD(ui->hx);
*y = COORD(ui->hy);
*w = *h = TILE_SIZE;
}
}
static int game_status(const game_state *state)
{
return state->completed ? +1 : 0;
@ -2135,6 +2148,7 @@ const struct game thegame = {
game_redraw,
game_anim_length,
game_flash_length,
game_get_cursor_location,
game_status,
true, false, game_print_size, game_print,
false, /* wants_statusbar */

View file

@ -2196,6 +2196,14 @@ static float game_flash_length(const game_state *oldstate,
return 0.0F;
}
static void game_get_cursor_location(const game_ui *ui,
const game_drawstate *ds,
const game_state *state,
const game_params *params,
int *x, int *y, int *w, int *h)
{
}
static int game_status(const game_state *state)
{
return state->completed ? +1 : 0;
@ -2320,6 +2328,7 @@ const struct game thegame = {
game_redraw,
game_anim_length,
game_flash_length,
game_get_cursor_location,
game_status,
true, false, game_print_size, game_print,
false, /* wants_statusbar */

View file

@ -799,6 +799,14 @@ static float game_flash_length(const game_state *oldstate,
return 0.0F;
}
static void game_get_cursor_location(const game_ui *ui,
const game_drawstate *ds,
const game_state *state,
const game_params *params,
int *x, int *y, int *w, int *h)
{
}
static int game_status(const game_state *state)
{
return 0;
@ -853,6 +861,7 @@ const struct game thegame = {
game_redraw,
game_anim_length,
game_flash_length,
game_get_cursor_location,
game_status,
false, false, game_print_size, game_print,
false, /* wants_statusbar */

View file

@ -2297,6 +2297,14 @@ static float game_flash_length(const game_state *oldstate,
return 0.0F;
}
static void game_get_cursor_location(const game_ui *ui,
const game_drawstate *ds,
const game_state *state,
const game_params *params,
int *x, int *y, int *w, int *h)
{
}
static int game_status(const game_state *state)
{
return state->completed ? +1 : 0;
@ -2351,6 +2359,7 @@ const struct game thegame = {
game_redraw,
game_anim_length,
game_flash_length,
game_get_cursor_location,
game_status,
false, false, game_print_size, game_print,
true, /* wants_statusbar */

View file

@ -1415,6 +1415,14 @@ static float game_flash_length(const game_state *oldstate,
return 0.0F;
}
static void game_get_cursor_location(const game_ui *ui,
const game_drawstate *ds,
const game_state *state,
const game_params *params,
int *x, int *y, int *w, int *h)
{
}
static int game_status(const game_state *state)
{
return state->completed ? +1 : 0;
@ -1469,6 +1477,7 @@ const struct game thegame = {
game_redraw,
game_anim_length,
game_flash_length,
game_get_cursor_location,
game_status,
false, false, game_print_size, game_print,
false, /* wants_statusbar */

View file

@ -1860,6 +1860,19 @@ static float game_flash_length(const game_state *oldstate,
return 0.0F;
}
static void game_get_cursor_location(const game_ui *ui,
const game_drawstate *ds,
const game_state *state,
const game_params *params,
int *x, int *y, int *w, int *h)
{
if(ui->cursor) {
*x = COORD(ui->cx);
*y = COORD(ui->cy);
*w = *h = TILE_SIZE;
}
}
static int game_status(const game_state *state)
{
return state->completed ? +1 : 0;
@ -1948,6 +1961,7 @@ const struct game thegame = {
game_redraw,
game_anim_length,
game_flash_length,
game_get_cursor_location,
game_status,
true, false, game_print_size, game_print,
false, /* wants_statusbar */

View file

@ -1581,6 +1581,25 @@ static float game_flash_length(const game_state *oldstate,
return 0.0F;
}
static void game_get_cursor_location(const game_ui *ui,
const game_drawstate *ds,
const game_state *state,
const game_params *params,
int *x, int *y, int *w, int *h)
{
if(ui->dragpoint >= 0 || ui->cursorpoint >= 0) {
int idx = (ui->dragpoint >= 0) ? ui->dragpoint : ui->cursorpoint;
int cx, cy;
cx = ds->x[idx];
cy = ds->y[idx];
*x = cx - CIRCLE_RADIUS;
*y = cy - CIRCLE_RADIUS;
*w = *h = 2 * CIRCLE_RADIUS + 1;
}
}
static int game_status(const game_state *state)
{
return state->completed ? +1 : 0;
@ -1635,6 +1654,7 @@ const struct game thegame = {
game_redraw,
game_anim_length,
game_flash_length,
game_get_cursor_location,
game_status,
false, false, game_print_size, game_print,
false, /* wants_statusbar */