summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFranklin Wei <franklin@rockbox.org>2020-07-06 22:59:58 -0400
committerFranklin Wei <franklin@rockbox.org>2020-07-06 23:00:13 -0400
commit5094aaa4d49573c0491399e987c2c866c00796a5 (patch)
tree7d625f9982e3263e6c242babe26be7ad5474b357
parent443ad25e75a29d114bc6c7d34387e6ad1f873a81 (diff)
downloadrockbox-5094aaa4d49573c0491399e987c2c866c00796a5.tar.gz
rockbox-5094aaa4d49573c0491399e987c2c866c00796a5.zip
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
-rw-r--r--apps/plugins/puzzles/rockbox.c102
-rw-r--r--apps/plugins/puzzles/src/blackbox.c15
-rw-r--r--apps/plugins/puzzles/src/bridges.c15
-rw-r--r--apps/plugins/puzzles/src/cube.c22
-rw-r--r--apps/plugins/puzzles/src/devel.but76
-rw-r--r--apps/plugins/puzzles/src/dominosa.c15
-rw-r--r--apps/plugins/puzzles/src/fifteen.c12
-rw-r--r--apps/plugins/puzzles/src/filling.c15
-rw-r--r--apps/plugins/puzzles/src/flip.c15
-rw-r--r--apps/plugins/puzzles/src/flood.c15
-rw-r--r--apps/plugins/puzzles/src/galaxies.c45
-rw-r--r--apps/plugins/puzzles/src/guess.c15
-rw-r--r--apps/plugins/puzzles/src/inertia.c12
-rw-r--r--apps/plugins/puzzles/src/keen.c15
-rw-r--r--apps/plugins/puzzles/src/lightup.c14
-rw-r--r--apps/plugins/puzzles/src/loopy.c9
-rw-r--r--apps/plugins/puzzles/src/magnets.c14
-rw-r--r--apps/plugins/puzzles/src/map.c14
-rw-r--r--apps/plugins/puzzles/src/midend.c19
-rw-r--r--apps/plugins/puzzles/src/mines.c14
-rw-r--r--apps/plugins/puzzles/src/net.c15
-rw-r--r--apps/plugins/puzzles/src/netslide.c15
-rw-r--r--apps/plugins/puzzles/src/nullgame.c9
-rw-r--r--apps/plugins/puzzles/src/palisade.c14
-rw-r--r--apps/plugins/puzzles/src/pattern.c14
-rw-r--r--apps/plugins/puzzles/src/pearl.c14
-rw-r--r--apps/plugins/puzzles/src/pegs.c14
-rw-r--r--apps/plugins/puzzles/src/puzzles.h7
-rw-r--r--apps/plugins/puzzles/src/range.c14
-rw-r--r--apps/plugins/puzzles/src/rect.c14
-rw-r--r--apps/plugins/puzzles/src/samegame.c14
-rw-r--r--apps/plugins/puzzles/src/signpost.c14
-rw-r--r--apps/plugins/puzzles/src/singles.c14
-rw-r--r--apps/plugins/puzzles/src/sixteen.c14
-rw-r--r--apps/plugins/puzzles/src/slant.c14
-rw-r--r--apps/plugins/puzzles/src/solo.c14
-rw-r--r--apps/plugins/puzzles/src/tents.c14
-rw-r--r--apps/plugins/puzzles/src/towers.c14
-rw-r--r--apps/plugins/puzzles/src/tracks.c32
-rw-r--r--apps/plugins/puzzles/src/twiddle.c14
-rw-r--r--apps/plugins/puzzles/src/undead.c14
-rw-r--r--apps/plugins/puzzles/src/unequal.c14
-rw-r--r--apps/plugins/puzzles/src/unfinished/group.c9
-rw-r--r--apps/plugins/puzzles/src/unfinished/separate.c9
-rw-r--r--apps/plugins/puzzles/src/unfinished/slide.c9
-rw-r--r--apps/plugins/puzzles/src/unfinished/sokoban.c9
-rw-r--r--apps/plugins/puzzles/src/unruly.c14
-rw-r--r--apps/plugins/puzzles/src/untangle.c20
48 files changed, 834 insertions, 25 deletions
diff --git a/apps/plugins/puzzles/rockbox.c b/apps/plugins/puzzles/rockbox.c
index 04d7e6d6e4..51b5e0890b 100644
--- a/apps/plugins/puzzles/rockbox.c
+++ b/apps/plugins/puzzles/rockbox.c
@@ -37,6 +37,7 @@
37 37
38#include "src/puzzles.h" 38#include "src/puzzles.h"
39 39
40#include "lib/helper.h"
40#include "lib/keymaps.h" 41#include "lib/keymaps.h"
41#include "lib/playback_control.h" 42#include "lib/playback_control.h"
42#include "lib/simple_viewer.h" 43#include "lib/simple_viewer.h"
@@ -139,6 +140,7 @@ extern bool audiobuf_available; /* defined in rbmalloc.c */
139 140
140static fb_data *zoom_fb; /* dynamically allocated */ 141static fb_data *zoom_fb; /* dynamically allocated */
141static int zoom_x = -1, zoom_y = -1, zoom_w, zoom_h, zoom_clipu, zoom_clipd, zoom_clipl, zoom_clipr; 142static int zoom_x = -1, zoom_y = -1, zoom_w, zoom_h, zoom_clipu, zoom_clipd, zoom_clipl, zoom_clipr;
143static bool zoom_force_center;
142static int cur_font = FONT_UI; 144static int cur_font = FONT_UI;
143 145
144static bool need_draw_update = false; 146static bool need_draw_update = false;
@@ -171,7 +173,7 @@ static bool load_success;
171/* ...did I mention there's a secret debug menu? */ 173/* ...did I mention there's a secret debug menu? */
172static struct { 174static struct {
173 int slowmo_factor; 175 int slowmo_factor;
174 bool timerflash, clipoff, shortcuts, no_aa, polyanim; 176 bool timerflash, clipoff, shortcuts, no_aa, polyanim, highlight_cursor;
175} debug_settings; 177} debug_settings;
176 178
177// used in menu titles - make sure to initialize! 179// 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
337static void zoom_alpha_bitmap(const unsigned char *bits, int x, int y, int w, int h) 339static void zoom_alpha_bitmap(const unsigned char *bits, int x, int y, int w, int h)
338{ 340{
339 const unsigned char *ptr = bits; 341 const unsigned char *ptr = bits;
340 unsigned char buf; 342 unsigned char buf = 0;
341 int n_read = 0; /* how many 4-bit nibbles we've read (read new when even) */ 343 int n_read = 0; /* how many 4-bit nibbles we've read (read new when even) */
342 344
343 unsigned int pix = rb->lcd_get_foreground(); 345 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
751 dx = x1 - x0; 753 dx = x1 - x0;
752 dy = y1 - y0; 754 dy = y1 - y0;
753 755
754 if(!(dx << FRACBITS)) 756 if((dx << FRACBITS) == 0)
755 return; /* bail out */ 757 return; /* bail out */
756 758
757 long gradient = fp_div(dy << FRACBITS, dx << FRACBITS, FRACBITS); 759 long gradient = fp_div(dy << FRACBITS, dx << FRACBITS, FRACBITS);
@@ -1349,6 +1351,16 @@ static void rb_start_draw(void *handle)
1349static void rb_end_draw(void *handle) 1351static void rb_end_draw(void *handle)
1350{ 1352{
1351 (void) handle; 1353 (void) handle;
1354
1355 if(debug_settings.highlight_cursor)
1356 {
1357 rb->lcd_set_foreground(LCD_RGBPACK(255,0,255));
1358 int x, y, w, h;
1359 midend_get_cursor_location(me, &x, &y, &w, &h);
1360 if(x >= 0)
1361 rb->lcd_drawrect(x, y, w, h);
1362 }
1363
1352 /* we ignore the backend's redraw requests and just 1364 /* we ignore the backend's redraw requests and just
1353 * unconditionally update everything */ 1365 * unconditionally update everything */
1354#if 0 1366#if 0
@@ -1991,6 +2003,47 @@ static void zoom_clamp_panning(void) {
1991 zoom_x = zoom_w - LCD_WIDTH; 2003 zoom_x = zoom_w - LCD_WIDTH;
1992} 2004}
1993 2005
2006static bool point_in_rect(int px, int py,
2007 int rx, int ry,
2008 int rw, int rh) {
2009 return (rx <= px && px < rx + rw) && (ry <= py && py < ry + rh);
2010}
2011
2012
2013static void zoom_center_on_cursor(void) {
2014 /* get cursor bounding rectangle */
2015 int x, y, w, h;
2016
2017 midend_get_cursor_location(me, &x, &y, &w, &h);
2018
2019 /* no cursor */
2020 if(x < 0)
2021 return;
2022
2023 /* check if either of the top-left and bottom-right corners are
2024 * off-screen */
2025 bool off_screen = (!point_in_rect(x, y, zoom_x, zoom_y, LCD_WIDTH, LCD_HEIGHT) ||
2026 !point_in_rect(x + w, y + h, zoom_x, zoom_y, LCD_WIDTH, LCD_HEIGHT));
2027
2028 if(off_screen || zoom_force_center)
2029 {
2030 /* if so, recenter */
2031 int cx, cy;
2032 cx = x + w / 2;
2033 cy = y + h / 2;
2034
2035 bool x_pan = x < zoom_x || zoom_x + LCD_WIDTH <= x + w;
2036 if(x_pan || zoom_force_center)
2037 zoom_x = cx - LCD_WIDTH / 2;
2038
2039 bool y_pan = y < zoom_y || zoom_y + LCD_HEIGHT <= y + h;
2040 if(y_pan || zoom_force_center)
2041 zoom_y = cy - LCD_HEIGHT / 2;
2042
2043 zoom_clamp_panning();
2044 }
2045}
2046
1994/* This function handles zoom mode, where the user can either pan 2047/* This function handles zoom mode, where the user can either pan
1995 * around a zoomed-in image or play a zoomed-in version of the game. */ 2048 * around a zoomed-in image or play a zoomed-in version of the game. */
1996static void zoom(void) 2049static void zoom(void)
@@ -2137,6 +2190,8 @@ static void zoom(void)
2137 if(timer_on) 2190 if(timer_on)
2138 timer_cb(); 2191 timer_cb();
2139 2192
2193 zoom_center_on_cursor();
2194
2140 midend_redraw(me); 2195 midend_redraw(me);
2141 2196
2142 /* blit */ 2197 /* blit */
@@ -2624,6 +2679,7 @@ static void init_default_settings(void)
2624 debug_settings.shortcuts = false; 2679 debug_settings.shortcuts = false;
2625 debug_settings.no_aa = false; 2680 debug_settings.no_aa = false;
2626 debug_settings.polyanim = false; 2681 debug_settings.polyanim = false;
2682 debug_settings.highlight_cursor = false;
2627} 2683}
2628 2684
2629#ifdef DEBUG_MENU 2685#ifdef DEBUG_MENU
@@ -2672,6 +2728,8 @@ static void debug_menu(void)
2672 "Toggle send keys on release", 2728 "Toggle send keys on release",
2673 "Toggle ignore repeats", 2729 "Toggle ignore repeats",
2674 "Toggle right-click on hold vs. dragging", 2730 "Toggle right-click on hold vs. dragging",
2731 "Toggle highlight cursor region",
2732 "Toggle force zoom on center",
2675 "Back"); 2733 "Back");
2676 bool quit = false; 2734 bool quit = false;
2677 int sel = 0; 2735 int sel = 0;
@@ -2693,37 +2751,43 @@ static void debug_menu(void)
2693 break; 2751 break;
2694 } 2752 }
2695 case 2: 2753 case 2:
2696 debug_settings.timerflash = !debug_settings.timerflash; 2754 debug_settings.timerflash ^= true;
2697 break; 2755 break;
2698 case 3: 2756 case 3:
2699 debug_settings.clipoff = !debug_settings.clipoff; 2757 debug_settings.clipoff ^= true;
2700 break; 2758 break;
2701 case 4: 2759 case 4:
2702 debug_settings.shortcuts = !debug_settings.shortcuts; 2760 debug_settings.shortcuts ^= true;
2703 break; 2761 break;
2704 case 5: 2762 case 5:
2705 debug_settings.no_aa = !debug_settings.no_aa; 2763 debug_settings.no_aa ^= true;
2706 break; 2764 break;
2707 case 6: 2765 case 6:
2708 bench_aa(); 2766 bench_aa();
2709 break; 2767 break;
2710 case 7: 2768 case 7:
2711 debug_settings.polyanim = !debug_settings.polyanim; 2769 debug_settings.polyanim ^= true;
2712 break; 2770 break;
2713 case 8: 2771 case 8:
2714 mouse_mode = !mouse_mode; 2772 mouse_mode ^= true;
2715 break; 2773 break;
2716 case 9: 2774 case 9:
2717 input_settings.want_spacebar = !input_settings.want_spacebar; 2775 input_settings.want_spacebar ^= true;
2718 break; 2776 break;
2719 case 10: 2777 case 10:
2720 input_settings.falling_edge = !input_settings.falling_edge; 2778 input_settings.falling_edge ^= true;
2721 break; 2779 break;
2722 case 11: 2780 case 11:
2723 input_settings.ignore_repeats = !input_settings.ignore_repeats; 2781 input_settings.ignore_repeats ^= true;
2724 break; 2782 break;
2725 case 12: 2783 case 12:
2726 input_settings.rclick_on_hold = !input_settings.rclick_on_hold; 2784 input_settings.rclick_on_hold ^= true;
2785 break;
2786 case 13:
2787 debug_settings.highlight_cursor ^= true;
2788 break;
2789 case 14:
2790 zoom_force_center ^= true;
2727 break; 2791 break;
2728 default: 2792 default:
2729 quit = true; 2793 quit = true;
@@ -2952,10 +3016,10 @@ static void init_tlsf(void)
2952 init_memory_pool(giant_buffer_len, giant_buffer); 3016 init_memory_pool(giant_buffer_len, giant_buffer);
2953} 3017}
2954 3018
2955static int read_wrapper(void *ptr, void *buf, int len) 3019static bool read_wrapper(void *ptr, void *buf, int len)
2956{ 3020{
2957 int fd = (int) ptr; 3021 int fd = (int) ptr;
2958 return rb->read(fd, buf, len); 3022 return rb->read(fd, buf, len) == len;
2959} 3023}
2960 3024
2961static void write_wrapper(void *ptr, const void *buf, int len) 3025static void write_wrapper(void *ptr, const void *buf, int len)
@@ -3067,6 +3131,12 @@ static void tune_input(const char *name)
3067 }; 3131 };
3068 3132
3069 input_settings.numerical_chooser = string_in_list(name, number_chooser_games); 3133 input_settings.numerical_chooser = string_in_list(name, number_chooser_games);
3134
3135 static const char *force_center_games[] = {
3136 "Inertia",
3137 NULL
3138 };
3139 zoom_force_center = string_in_list(name, force_center_games);
3070} 3140}
3071 3141
3072static const char *init_for_game(const game *gm, int load_fd) 3142static const char *init_for_game(const game *gm, int load_fd)
@@ -3241,7 +3311,7 @@ static void save_fonts(void)
3241 final |= oldmask; 3311 final |= oldmask;
3242 uint32_t left = final >> 31; 3312 uint32_t left = final >> 31;
3243 uint32_t right = final & 0x7fffffff; 3313 uint32_t right = final & 0x7fffffff;
3244 rb->fdprintf(outfd, "%s:%lu:%lu\n", midend_which_game(me)->name, left, right); 3314 rb->fdprintf(outfd, "%s:%u:%u\n", midend_which_game(me)->name, (unsigned)left, (unsigned)right);
3245 rb->close(outfd); 3315 rb->close(outfd);
3246 rb->rename(FONT_TABLE ".tmp", FONT_TABLE); 3316 rb->rename(FONT_TABLE ".tmp", FONT_TABLE);
3247 } 3317 }
diff --git a/apps/plugins/puzzles/src/blackbox.c b/apps/plugins/puzzles/src/blackbox.c
index c52c50812d..a9c1f88261 100644
--- a/apps/plugins/puzzles/src/blackbox.c
+++ b/apps/plugins/puzzles/src/blackbox.c
@@ -1100,6 +1100,20 @@ badmove:
1100 return NULL; 1100 return NULL;
1101} 1101}
1102 1102
1103
1104static void game_get_cursor_location(const game_ui *ui,
1105 const game_drawstate *ds,
1106 const game_state *state,
1107 const game_params *params,
1108 int *x, int *y, int *w, int *h)
1109{
1110 if(ui->cur_visible) {
1111 *x = TODRAW(ui->cur_x);
1112 *y = TODRAW(ui->cur_y);
1113 *w = *h = TILE_SIZE;
1114 }
1115}
1116
1103/* ---------------------------------------------------------------------- 1117/* ----------------------------------------------------------------------
1104 * Drawing routines. 1118 * Drawing routines.
1105 */ 1119 */
@@ -1544,6 +1558,7 @@ const struct game thegame = {
1544 game_redraw, 1558 game_redraw,
1545 game_anim_length, 1559 game_anim_length,
1546 game_flash_length, 1560 game_flash_length,
1561 game_get_cursor_location,
1547 game_status, 1562 game_status,
1548 false, false, game_print_size, game_print, 1563 false, false, game_print_size, game_print,
1549 true, /* wants_statusbar */ 1564 true, /* wants_statusbar */
diff --git a/apps/plugins/puzzles/src/bridges.c b/apps/plugins/puzzles/src/bridges.c
index d12aa0bb6c..83086c9761 100644
--- a/apps/plugins/puzzles/src/bridges.c
+++ b/apps/plugins/puzzles/src/bridges.c
@@ -2146,6 +2146,20 @@ struct game_drawstate {
2146 bool started, dragging; 2146 bool started, dragging;
2147}; 2147};
2148 2148
2149
2150static void game_get_cursor_location(const game_ui *ui,
2151 const game_drawstate *ds,
2152 const game_state *state,
2153 const game_params *params,
2154 int *x, int *y, int *w, int *h)
2155{
2156 if(ui->cur_visible) {
2157 *x = COORD(ui->cur_x);
2158 *y = COORD(ui->cur_y);
2159 *w = *h = TILE_SIZE;
2160 }
2161}
2162
2149/* 2163/*
2150 * The contents of ds->grid are complicated, because of the circular 2164 * The contents of ds->grid are complicated, because of the circular
2151 * islands which overlap their own grid square into neighbouring 2165 * islands which overlap their own grid square into neighbouring
@@ -3267,6 +3281,7 @@ const struct game thegame = {
3267 game_redraw, 3281 game_redraw,
3268 game_anim_length, 3282 game_anim_length,
3269 game_flash_length, 3283 game_flash_length,
3284 game_get_cursor_location,
3270 game_status, 3285 game_status,
3271 true, false, game_print_size, game_print, 3286 true, false, game_print_size, game_print,
3272 false, /* wants_statusbar */ 3287 false, /* wants_statusbar */
diff --git a/apps/plugins/puzzles/src/cube.c b/apps/plugins/puzzles/src/cube.c
index bda7623f97..8c8c46faed 100644
--- a/apps/plugins/puzzles/src/cube.c
+++ b/apps/plugins/puzzles/src/cube.c
@@ -1535,6 +1535,27 @@ static void game_free_drawstate(drawing *dr, game_drawstate *ds)
1535 sfree(ds); 1535 sfree(ds);
1536} 1536}
1537 1537
1538static void game_get_cursor_location(const game_ui *ui,
1539 const game_drawstate *ds,
1540 const game_state *state,
1541 const game_params *params,
1542 int *x, int *y, int *w, int *h)
1543{
1544 struct bbox bb;
1545
1546 bb.l = 2.0F * (params->d1 + params->d2);
1547 bb.r = -2.0F * (params->d1 + params->d2);
1548 bb.u = 2.0F * (params->d1 + params->d2);
1549 bb.d = -2.0F * (params->d1 + params->d2);
1550
1551 find_bbox_callback(&bb, state->grid->squares + state->current);
1552
1553 *x = ((int)(bb.l * GRID_SCALE) + ds->ox);
1554 *y = ((int)(bb.u * GRID_SCALE) + ds->oy);
1555 *w = (bb.r - bb.l) * GRID_SCALE;
1556 *h = (bb.d - bb.u) * GRID_SCALE;
1557}
1558
1538static void game_redraw(drawing *dr, game_drawstate *ds, 1559static void game_redraw(drawing *dr, game_drawstate *ds,
1539 const game_state *oldstate, const game_state *state, 1560 const game_state *oldstate, const game_state *state,
1540 int dir, const game_ui *ui, 1561 int dir, const game_ui *ui,
@@ -1762,6 +1783,7 @@ const struct game thegame = {
1762 game_redraw, 1783 game_redraw,
1763 game_anim_length, 1784 game_anim_length,
1764 game_flash_length, 1785 game_flash_length,
1786 game_get_cursor_location,
1765 game_status, 1787 game_status,
1766 false, false, game_print_size, game_print, 1788 false, false, game_print_size, game_print,
1767 true, /* wants_statusbar */ 1789 true, /* wants_statusbar */
diff --git a/apps/plugins/puzzles/src/devel.but b/apps/plugins/puzzles/src/devel.but
index 9f95ad7dd4..741a7944ac 100644
--- a/apps/plugins/puzzles/src/devel.but
+++ b/apps/plugins/puzzles/src/devel.but
@@ -1289,6 +1289,50 @@ a mine from the colour it uses when you complete the game. In order
1289to achieve this, its \cw{flash_length()} function has to store a 1289to achieve this, its \cw{flash_length()} function has to store a
1290flag in the \c{game_ui} to indicate which flash type is required.) 1290flag in the \c{game_ui} to indicate which flash type is required.)
1291 1291
1292\S{backend-get-cursor-location} \cw{get_cursor_location()}
1293
1294\c void (*get_cursor_location)(const game_ui *ui,
1295\c const game_drawstate *ds,
1296\c const game_state *state,
1297\c const game_params *params,
1298\c int *x, int *y, int *w, int *h);
1299
1300This function queries the backend for the rectangular region
1301containing the cursor (in games that have one), or other region of
1302interest.
1303
1304This function is called by only
1305\cw{midend_get_cursor_location()}(\k{midend-get-cursor-location}). Its
1306purpose is to allow frontends to query the location of the backend's
1307cursor. With knowledge of this location, a frontend can, for example,
1308ensure that the region of interest remains visible if the puzzle is
1309too big to fit on the screen at once.
1310
1311On returning, \cw{*x}, \cw{*y} should be set to the X and Y
1312coordinates of the upper-left corner of the rectangular region of
1313interest, and \cw{*w} and \cw{*h} should be the width and height of
1314that region, respectively. All return values are in units of pixels in
1315screenspace coordinates. In the event that a cursor is not visible on
1316screen, this function should return and leave the return parameters
1317untouched \dash the mid-end will notice this. The backend need not
1318bother checking that \cw{x}, \cw{y}, \cw{w} and \cw{h} are
1319non-\cw{NULL} \dash the mid-end guarantees that they will not be.
1320
1321Defining what constitutes a \q{region of interest} is left up to the
1322backend. If a game provides a conventional cursor \dash such as Mines,
1323Solo, or any of the other grid-based games \dash the most logical choice
1324is of course the cursor location. However, in other cases such as Cube
1325or Inertia, there is no \q{cursor} in the conventional sense \dash the
1326player controls an object moving around the screen. In these cases, it
1327makes sense to define the region of interest as the bounding box of
1328the player or another sensible region \dash such as the grid square the
1329player is sitting on in Cube.
1330
1331If a backend does not provide a cursor mechanism at all, the backend
1332is free to provide an empty implementation of this function, or a
1333\cw{NULL} pointer in the \cw{game} structure \dash the mid-end will
1334notice either of these cases and behave appropriately.
1335
1292\S{backend-status} \cw{status()} 1336\S{backend-status} \cw{status()}
1293 1337
1294\c int (*status)(const game_state *state); 1338\c int (*status)(const game_state *state);
@@ -1586,10 +1630,10 @@ to the midend when the frontend deems appropriate.
1586The backend should set \cw{*nkeys} to the number of elements in the 1630The backend should set \cw{*nkeys} to the number of elements in the
1587returned array. 1631returned array.
1588 1632
1589The field for this function point in the \cw{game} structure might be 1633The field for this function pointer in the \cw{game} structure might
1590set to \cw{NULL} (and indeed it is for the majority of the games) to 1634be set to \cw{NULL} (and indeed it is for the majority of the games)
1591indicate that no additional buttons (apart from the cursor keys) are 1635to indicate that no additional buttons (apart from the cursor keys)
1592required to play the game. 1636are required to play the game.
1593 1637
1594This function should not be called directly by frontends. Instead, 1638This function should not be called directly by frontends. Instead,
1595frontends should use \cw{midend_request_keys()} 1639frontends should use \cw{midend_request_keys()}
@@ -3307,6 +3351,30 @@ The front end can expect its drawing API and/or
3307function. Some back ends require that \cw{midend_size()} 3351function. Some back ends require that \cw{midend_size()}
3308(\k{midend-size}) is called before \cw{midend_solve()}. 3352(\k{midend-size}) is called before \cw{midend_solve()}.
3309 3353
3354\H{midend-get-cursor-location} \cw{midend_get_cursor_location()}
3355
3356\c void midend_get_cursor_location(midend *me,
3357\c int *x, int *y, int *w, int *h);
3358
3359This function returns the location of the backend's on-screen cursor
3360or other region of interest in the parameters \cw{*x}, \cw{*y},
3361\cw{*w} and \cw{*h}, which describe a rectangle with an upper-left
3362corner at \cw{(*x,*y)} and a size of \cw{*w} pixels wide by \cw{*h}
3363pixels tall. The mid-end will ignore any return parameters that may be
3364equal to \cw{NULL}.
3365
3366What exactly this region contains is up to the backend, but in general
3367the region will be an area that the player is controlling with the
3368cursor keys \dash such as the player location in Cube and Inertia, or
3369the cursor in any of the conventional grid-based games. With knowledge
3370of this location, a frontend can, for example, ensure that the region
3371of interest remains visible even if the entire puzzle is too big to
3372fit on the screen.
3373
3374If there is no such region (if either the cursor is not visible, or if
3375the game does not have cursor support), both \cw{*x} and \cw{*y} will
3376be set to \cw{-1}.
3377
3310\H{midend-status} \cw{midend_status()} 3378\H{midend-status} \cw{midend_status()}
3311 3379
3312\c int midend_status(midend *me); 3380\c int midend_status(midend *me);
diff --git a/apps/plugins/puzzles/src/dominosa.c b/apps/plugins/puzzles/src/dominosa.c
index 67a1d69c91..758db4f506 100644
--- a/apps/plugins/puzzles/src/dominosa.c
+++ b/apps/plugins/puzzles/src/dominosa.c
@@ -3328,6 +3328,20 @@ static float game_flash_length(const game_state *oldstate,
3328 return 0.0F; 3328 return 0.0F;
3329} 3329}
3330 3330
3331static void game_get_cursor_location(const game_ui *ui,
3332 const game_drawstate *ds,
3333 const game_state *state,
3334 const game_params *params,
3335 int *x, int *y, int *w, int *h)
3336{
3337 if(ui->cur_visible)
3338 {
3339 *x = BORDER + ((2 * ui->cur_x + 1) * TILESIZE) / 4;
3340 *y = BORDER + ((2 * ui->cur_y + 1) * TILESIZE) / 4;
3341 *w = *h = TILESIZE / 2 + 2;
3342 }
3343}
3344
3331static int game_status(const game_state *state) 3345static int game_status(const game_state *state)
3332{ 3346{
3333 return state->completed ? +1 : 0; 3347 return state->completed ? +1 : 0;
@@ -3422,6 +3436,7 @@ const struct game thegame = {
3422 game_redraw, 3436 game_redraw,
3423 game_anim_length, 3437 game_anim_length,
3424 game_flash_length, 3438 game_flash_length,
3439 game_get_cursor_location,
3425 game_status, 3440 game_status,
3426 true, false, game_print_size, game_print, 3441 true, false, game_print_size, game_print,
3427 false, /* wants_statusbar */ 3442 false, /* wants_statusbar */
diff --git a/apps/plugins/puzzles/src/fifteen.c b/apps/plugins/puzzles/src/fifteen.c
index ba991e711a..4b877dc098 100644
--- a/apps/plugins/puzzles/src/fifteen.c
+++ b/apps/plugins/puzzles/src/fifteen.c
@@ -1061,6 +1061,17 @@ static float game_flash_length(const game_state *oldstate,
1061 return 0.0F; 1061 return 0.0F;
1062} 1062}
1063 1063
1064static void game_get_cursor_location(const game_ui *ui,
1065 const game_drawstate *ds,
1066 const game_state *state,
1067 const game_params *params,
1068 int *x, int *y, int *w, int *h)
1069{
1070 *x = COORD(X(state, state->gap_pos));
1071 *y = COORD(Y(state, state->gap_pos));
1072 *w = *h = TILE_SIZE;
1073}
1074
1064static int game_status(const game_state *state) 1075static int game_status(const game_state *state)
1065{ 1076{
1066 return state->completed ? +1 : 0; 1077 return state->completed ? +1 : 0;
@@ -1115,6 +1126,7 @@ const struct game thegame = {
1115 game_redraw, 1126 game_redraw,
1116 game_anim_length, 1127 game_anim_length,
1117 game_flash_length, 1128 game_flash_length,
1129 game_get_cursor_location,
1118 game_status, 1130 game_status,
1119 false, false, game_print_size, game_print, 1131 false, false, game_print_size, game_print,
1120 true, /* wants_statusbar */ 1132 true, /* wants_statusbar */
diff --git a/apps/plugins/puzzles/src/filling.c b/apps/plugins/puzzles/src/filling.c
index f67d9fadba..6d9beb5c28 100644
--- a/apps/plugins/puzzles/src/filling.c
+++ b/apps/plugins/puzzles/src/filling.c
@@ -2060,6 +2060,20 @@ static float game_flash_length(const game_state *oldstate,
2060 return 0.0F; 2060 return 0.0F;
2061} 2061}
2062 2062
2063static void game_get_cursor_location(const game_ui *ui,
2064 const game_drawstate *ds,
2065 const game_state *state,
2066 const game_params *params,
2067 int *x, int *y, int *w, int *h)
2068{
2069 if(ui->cur_visible)
2070 {
2071 *x = BORDER + ui->cur_x * TILE_SIZE;
2072 *y = BORDER + ui->cur_y * TILE_SIZE;
2073 *w = *h = TILE_SIZE;
2074 }
2075}
2076
2063static int game_status(const game_state *state) 2077static int game_status(const game_state *state)
2064{ 2078{
2065 return state->completed ? +1 : 0; 2079 return state->completed ? +1 : 0;
@@ -2165,6 +2179,7 @@ const struct game thegame = {
2165 game_redraw, 2179 game_redraw,
2166 game_anim_length, 2180 game_anim_length,
2167 game_flash_length, 2181 game_flash_length,
2182 game_get_cursor_location,
2168 game_status, 2183 game_status,
2169 true, false, game_print_size, game_print, 2184 true, false, game_print_size, game_print,
2170 false, /* wants_statusbar */ 2185 false, /* wants_statusbar */
diff --git a/apps/plugins/puzzles/src/flip.c b/apps/plugins/puzzles/src/flip.c
index 29c888edf2..5d4f2250aa 100644
--- a/apps/plugins/puzzles/src/flip.c
+++ b/apps/plugins/puzzles/src/flip.c
@@ -1290,6 +1290,20 @@ static float game_flash_length(const game_state *oldstate,
1290 return 0.0F; 1290 return 0.0F;
1291} 1291}
1292 1292
1293static void game_get_cursor_location(const game_ui *ui,
1294 const game_drawstate *ds,
1295 const game_state *state,
1296 const game_params *params,
1297 int *x, int *y, int *w, int *h)
1298{
1299 if(ui->cdraw)
1300 {
1301 *x = COORD(ui->cx);
1302 *y = COORD(ui->cy);
1303 *w = *h = TILE_SIZE;
1304 }
1305}
1306
1293static int game_status(const game_state *state) 1307static int game_status(const game_state *state)
1294{ 1308{
1295 return state->completed ? +1 : 0; 1309 return state->completed ? +1 : 0;
@@ -1344,6 +1358,7 @@ const struct game thegame = {
1344 game_redraw, 1358 game_redraw,
1345 game_anim_length, 1359 game_anim_length,
1346 game_flash_length, 1360 game_flash_length,
1361 game_get_cursor_location,
1347 game_status, 1362 game_status,
1348 false, false, game_print_size, game_print, 1363 false, false, game_print_size, game_print,
1349 true, /* wants_statusbar */ 1364 true, /* wants_statusbar */
diff --git a/apps/plugins/puzzles/src/flood.c b/apps/plugins/puzzles/src/flood.c
index 67c3225be4..74214a50b6 100644
--- a/apps/plugins/puzzles/src/flood.c
+++ b/apps/plugins/puzzles/src/flood.c
@@ -1279,6 +1279,20 @@ static float game_anim_length(const game_state *oldstate,
1279 return 0.0F; 1279 return 0.0F;
1280} 1280}
1281 1281
1282static void game_get_cursor_location(const game_ui *ui,
1283 const game_drawstate *ds,
1284 const game_state *state,
1285 const game_params *params,
1286 int *x, int *y, int *w, int *h)
1287{
1288 if(ui->cursor_visible)
1289 {
1290 *x = COORD(ui->cx);
1291 *y = COORD(ui->cy);
1292 *w = *h = TILESIZE;
1293 }
1294}
1295
1282static int game_status(const game_state *state) 1296static int game_status(const game_state *state)
1283{ 1297{
1284 if (state->complete && state->moves <= state->movelimit) { 1298 if (state->complete && state->moves <= state->movelimit) {
@@ -1361,6 +1375,7 @@ const struct game thegame = {
1361 game_redraw, 1375 game_redraw,
1362 game_anim_length, 1376 game_anim_length,
1363 game_flash_length, 1377 game_flash_length,
1378 game_get_cursor_location,
1364 game_status, 1379 game_status,
1365 false, false, game_print_size, game_print, 1380 false, false, game_print_size, game_print,
1366 true, /* wants_statusbar */ 1381 true, /* wants_statusbar */
diff --git a/apps/plugins/puzzles/src/galaxies.c b/apps/plugins/puzzles/src/galaxies.c
index fe7cd24ecf..9172b90e12 100644
--- a/apps/plugins/puzzles/src/galaxies.c
+++ b/apps/plugins/puzzles/src/galaxies.c
@@ -382,12 +382,15 @@ static bool ok_to_add_assoc_with_opposite(
382static void add_assoc_with_opposite(game_state *state, space *tile, space *dot) { 382static void add_assoc_with_opposite(game_state *state, space *tile, space *dot) {
383 space *opposite = space_opposite_dot(state, tile, dot); 383 space *opposite = space_opposite_dot(state, tile, dot);
384 384
385 assert(ok_to_add_assoc_with_opposite_internal(state, tile, opposite)); 385 if(opposite)
386 {
387 assert(ok_to_add_assoc_with_opposite_internal(state, tile, opposite));
386 388
387 remove_assoc_with_opposite(state, tile); 389 remove_assoc_with_opposite(state, tile);
388 add_assoc(state, tile, dot); 390 add_assoc(state, tile, dot);
389 remove_assoc_with_opposite(state, opposite); 391 remove_assoc_with_opposite(state, opposite);
390 add_assoc(state, opposite, dot); 392 add_assoc(state, opposite, dot);
393 }
391} 394}
392 395
393static space *sp2dot(const game_state *state, int x, int y) 396static space *sp2dot(const game_state *state, int x, int y)
@@ -3469,6 +3472,37 @@ static float game_flash_length(const game_state *oldstate,
3469 return 0.0F; 3472 return 0.0F;
3470} 3473}
3471 3474
3475static void game_get_cursor_location(const game_ui *ui,
3476 const game_drawstate *ds,
3477 const game_state *state,
3478 const game_params *params,
3479 int *x, int *y, int *w, int *h)
3480{
3481 if(ui->cur_visible) {
3482 space *sp = &SPACE(state, ui->cur_x, ui->cur_y);
3483
3484 if(sp->flags & F_DOT) {
3485 *x = SCOORD(ui->cur_x) - DOT_SIZE;
3486 *y = SCOORD(ui->cur_y) - DOT_SIZE;
3487 *w = *h = 2 * DOT_SIZE + 1;
3488 } else if(sp->type != s_tile) {
3489 int dx = (ui->cur_x % 2) ? CURSOR_SIZE : CURSOR_SIZE/3;
3490 int dy = (ui->cur_y % 2) ? CURSOR_SIZE : CURSOR_SIZE/3;
3491 int x1 = SCOORD(ui->cur_x)-dx, y1 = SCOORD(ui->cur_y)-dy;
3492 int xs = dx*2+1, ys = dy*2+1;
3493
3494 *x = x1;
3495 *y = y1;
3496 *w = xs;
3497 *h = ys;
3498 } else {
3499 *x = SCOORD(ui->cur_x) - CURSOR_SIZE;
3500 *y = SCOORD(ui->cur_y) - CURSOR_SIZE;
3501 *w = *h = 2 * CURSOR_SIZE + 1;
3502 }
3503 }
3504}
3505
3472static int game_status(const game_state *state) 3506static int game_status(const game_state *state)
3473{ 3507{
3474 return state->completed ? +1 : 0; 3508 return state->completed ? +1 : 0;
@@ -3695,6 +3729,7 @@ const struct game thegame = {
3695 game_redraw, 3729 game_redraw,
3696 game_anim_length, 3730 game_anim_length,
3697 game_flash_length, 3731 game_flash_length,
3732 game_get_cursor_location,
3698 game_status, 3733 game_status,
3699#ifdef EDITOR 3734#ifdef EDITOR
3700 false, false, NULL, NULL, 3735 false, false, NULL, NULL,
diff --git a/apps/plugins/puzzles/src/guess.c b/apps/plugins/puzzles/src/guess.c
index e5ebd5509b..a501579442 100644
--- a/apps/plugins/puzzles/src/guess.c
+++ b/apps/plugins/puzzles/src/guess.c
@@ -1448,6 +1448,20 @@ static float game_flash_length(const game_state *oldstate,
1448 return 0.0F; 1448 return 0.0F;
1449} 1449}
1450 1450
1451static void game_get_cursor_location(const game_ui *ui,
1452 const game_drawstate *ds,
1453 const game_state *state,
1454 const game_params *params,
1455 int *x, int *y, int *w, int *h)
1456{
1457 if(ui->display_cur && !state->solved) {
1458 *x = GUESS_X(state->next_go, ui->peg_cur) - CGAP;
1459 *y = GUESS_Y(state->next_go, ui->peg_cur) - CGAP;
1460
1461 *w = *h = 2 * (PEGRAD + CGAP) + 1;
1462 }
1463}
1464
1451static int game_status(const game_state *state) 1465static int game_status(const game_state *state)
1452{ 1466{
1453 /* 1467 /*
@@ -1508,6 +1522,7 @@ const struct game thegame = {
1508 game_redraw, 1522 game_redraw,
1509 game_anim_length, 1523 game_anim_length,
1510 game_flash_length, 1524 game_flash_length,
1525 game_get_cursor_location,
1511 game_status, 1526 game_status,
1512 false, false, game_print_size, game_print, 1527 false, false, game_print_size, game_print,
1513 false, /* wants_statusbar */ 1528 false, /* wants_statusbar */
diff --git a/apps/plugins/puzzles/src/inertia.c b/apps/plugins/puzzles/src/inertia.c
index 838f8dee96..726c89c7dd 100644
--- a/apps/plugins/puzzles/src/inertia.c
+++ b/apps/plugins/puzzles/src/inertia.c
@@ -2186,6 +2186,17 @@ static float game_flash_length(const game_state *oldstate,
2186 return 0.0F; 2186 return 0.0F;
2187} 2187}
2188 2188
2189static void game_get_cursor_location(const game_ui *ui,
2190 const game_drawstate *ds,
2191 const game_state *state,
2192 const game_params *params,
2193 int *x, int *y, int *w, int *h)
2194{
2195 *x = ds->pbgx;
2196 *y = ds->pbgy;
2197 *w = *h = TILESIZE;
2198}
2199
2189static int game_status(const game_state *state) 2200static int game_status(const game_state *state)
2190{ 2201{
2191 /* 2202 /*
@@ -2245,6 +2256,7 @@ const struct game thegame = {
2245 game_redraw, 2256 game_redraw,
2246 game_anim_length, 2257 game_anim_length,
2247 game_flash_length, 2258 game_flash_length,
2259 game_get_cursor_location,
2248 game_status, 2260 game_status,
2249 false, false, game_print_size, game_print, 2261 false, false, game_print_size, game_print,
2250 true, /* wants_statusbar */ 2262 true, /* wants_statusbar */
diff --git a/apps/plugins/puzzles/src/keen.c b/apps/plugins/puzzles/src/keen.c
index 70e3e5432c..6b9610dbcd 100644
--- a/apps/plugins/puzzles/src/keen.c
+++ b/apps/plugins/puzzles/src/keen.c
@@ -2198,6 +2198,20 @@ static float game_flash_length(const game_state *oldstate,
2198 return 0.0F; 2198 return 0.0F;
2199} 2199}
2200 2200
2201static void game_get_cursor_location(const game_ui *ui,
2202 const game_drawstate *ds,
2203 const game_state *state,
2204 const game_params *params,
2205 int *x, int *y, int *w, int *h)
2206{
2207 if(ui->hshow) {
2208 *x = BORDER + ui->hx * TILESIZE + 1 + GRIDEXTRA;
2209 *y = BORDER + ui->hy * TILESIZE + 1 + GRIDEXTRA;
2210
2211 *w = *h = TILESIZE-1-2*GRIDEXTRA;
2212 }
2213}
2214
2201static int game_status(const game_state *state) 2215static int game_status(const game_state *state)
2202{ 2216{
2203 return state->completed ? +1 : 0; 2217 return state->completed ? +1 : 0;
@@ -2480,6 +2494,7 @@ const struct game thegame = {
2480 game_redraw, 2494 game_redraw,
2481 game_anim_length, 2495 game_anim_length,
2482 game_flash_length, 2496 game_flash_length,
2497 game_get_cursor_location,
2483 game_status, 2498 game_status,
2484 true, false, game_print_size, game_print, 2499 true, false, game_print_size, game_print,
2485 false, /* wants_statusbar */ 2500 false, /* wants_statusbar */
diff --git a/apps/plugins/puzzles/src/lightup.c b/apps/plugins/puzzles/src/lightup.c
index 90811d5475..ab4be9ec87 100644
--- a/apps/plugins/puzzles/src/lightup.c
+++ b/apps/plugins/puzzles/src/lightup.c
@@ -2217,6 +2217,19 @@ static float game_flash_length(const game_state *oldstate,
2217 return 0.0F; 2217 return 0.0F;
2218} 2218}
2219 2219
2220static void game_get_cursor_location(const game_ui *ui,
2221 const game_drawstate *ds,
2222 const game_state *state,
2223 const game_params *params,
2224 int *x, int *y, int *w, int *h)
2225{
2226 if(ui->cur_visible) {
2227 *x = COORD(ui->cur_x);
2228 *y = COORD(ui->cur_y);
2229 *w = *h = TILE_SIZE;
2230 }
2231}
2232
2220static int game_status(const game_state *state) 2233static int game_status(const game_state *state)
2221{ 2234{
2222 return state->completed ? +1 : 0; 2235 return state->completed ? +1 : 0;
@@ -2325,6 +2338,7 @@ const struct game thegame = {
2325 game_redraw, 2338 game_redraw,
2326 game_anim_length, 2339 game_anim_length,
2327 game_flash_length, 2340 game_flash_length,
2341 game_get_cursor_location,
2328 game_status, 2342 game_status,
2329 true, false, game_print_size, game_print, 2343 true, false, game_print_size, game_print,
2330 false, /* wants_statusbar */ 2344 false, /* wants_statusbar */
diff --git a/apps/plugins/puzzles/src/loopy.c b/apps/plugins/puzzles/src/loopy.c
index f2875a2e93..176b56285c 100644
--- a/apps/plugins/puzzles/src/loopy.c
+++ b/apps/plugins/puzzles/src/loopy.c
@@ -3537,6 +3537,14 @@ static float game_flash_length(const game_state *oldstate,
3537 return 0.0F; 3537 return 0.0F;
3538} 3538}
3539 3539
3540static void game_get_cursor_location(const game_ui *ui,
3541 const game_drawstate *ds,
3542 const game_state *state,
3543 const game_params *params,
3544 int *x, int *y, int *w, int *h)
3545{
3546}
3547
3540static int game_status(const game_state *state) 3548static int game_status(const game_state *state)
3541{ 3549{
3542 return state->solved ? +1 : 0; 3550 return state->solved ? +1 : 0;
@@ -3675,6 +3683,7 @@ const struct game thegame = {
3675 game_redraw, 3683 game_redraw,
3676 game_anim_length, 3684 game_anim_length,
3677 game_flash_length, 3685 game_flash_length,
3686 game_get_cursor_location,
3678 game_status, 3687 game_status,
3679 true, false, game_print_size, game_print, 3688 true, false, game_print_size, game_print,
3680 false /* wants_statusbar */, 3689 false /* wants_statusbar */,
diff --git a/apps/plugins/puzzles/src/magnets.c b/apps/plugins/puzzles/src/magnets.c
index 1a5f37f1fd..edbb8490ad 100644
--- a/apps/plugins/puzzles/src/magnets.c
+++ b/apps/plugins/puzzles/src/magnets.c
@@ -2291,6 +2291,19 @@ static float game_flash_length(const game_state *oldstate,
2291 return 0.0F; 2291 return 0.0F;
2292} 2292}
2293 2293
2294static void game_get_cursor_location(const game_ui *ui,
2295 const game_drawstate *ds,
2296 const game_state *state,
2297 const game_params *params,
2298 int *x, int *y, int *w, int *h)
2299{
2300 if(ui->cur_visible) {
2301 *x = COORD(ui->cur_x);
2302 *y = COORD(ui->cur_y);
2303 *w = *h = TILE_SIZE;
2304 }
2305}
2306
2294static int game_status(const game_state *state) 2307static int game_status(const game_state *state)
2295{ 2308{
2296 return state->completed ? +1 : 0; 2309 return state->completed ? +1 : 0;
@@ -2432,6 +2445,7 @@ const struct game thegame = {
2432 game_redraw, 2445 game_redraw,
2433 game_anim_length, 2446 game_anim_length,
2434 game_flash_length, 2447 game_flash_length,
2448 game_get_cursor_location,
2435 game_status, 2449 game_status,
2436 true, false, game_print_size, game_print, 2450 true, false, game_print_size, game_print,
2437 false, /* wants_statusbar */ 2451 false, /* wants_statusbar */
diff --git a/apps/plugins/puzzles/src/map.c b/apps/plugins/puzzles/src/map.c
index 4e65aa91a6..9df2d22b52 100644
--- a/apps/plugins/puzzles/src/map.c
+++ b/apps/plugins/puzzles/src/map.c
@@ -3061,6 +3061,19 @@ static float game_flash_length(const game_state *oldstate,
3061 return 0.0F; 3061 return 0.0F;
3062} 3062}
3063 3063
3064static void game_get_cursor_location(const game_ui *ui,
3065 const game_drawstate *ds,
3066 const game_state *state,
3067 const game_params *params,
3068 int *x, int *y, int *w, int *h)
3069{
3070 if(ui->cur_visible) {
3071 *x = COORD(ui->cur_x);
3072 *y = COORD(ui->cur_y);
3073 *w = *h = TILESIZE;
3074 }
3075}
3076
3064static int game_status(const game_state *state) 3077static int game_status(const game_state *state)
3065{ 3078{
3066 return state->completed ? +1 : 0; 3079 return state->completed ? +1 : 0;
@@ -3260,6 +3273,7 @@ const struct game thegame = {
3260 game_redraw, 3273 game_redraw,
3261 game_anim_length, 3274 game_anim_length,
3262 game_flash_length, 3275 game_flash_length,
3276 game_get_cursor_location,
3263 game_status, 3277 game_status,
3264 true, true, game_print_size, game_print, 3278 true, true, game_print_size, game_print,
3265 false, /* wants_statusbar */ 3279 false, /* wants_statusbar */
diff --git a/apps/plugins/puzzles/src/midend.c b/apps/plugins/puzzles/src/midend.c
index a8dd179690..87605a9da8 100644
--- a/apps/plugins/puzzles/src/midend.c
+++ b/apps/plugins/puzzles/src/midend.c
@@ -1464,6 +1464,25 @@ void midend_request_id_changes(midend *me, void (*notify)(void *), void *ctx)
1464 me->game_id_change_notify_ctx = ctx; 1464 me->game_id_change_notify_ctx = ctx;
1465} 1465}
1466 1466
1467void midend_get_cursor_location(midend *me, int *x_out, int *y_out, int *w_out, int *h_out)
1468{
1469 int x, y, w, h;
1470 x = y = -1;
1471 w = h = 1;
1472
1473 if(me->ourgame->get_cursor_location)
1474 me->ourgame->get_cursor_location(me->ui, me->drawstate, me->states[me->statepos-1].state, me->params, &x, &y, &w, &h);
1475
1476 if(x_out)
1477 *x_out = x;
1478 if(y_out)
1479 *y_out = y;
1480 if(w_out)
1481 *w_out = w;
1482 if(h_out)
1483 *h_out = h;
1484}
1485
1467void midend_supersede_game_desc(midend *me, const char *desc, 1486void midend_supersede_game_desc(midend *me, const char *desc,
1468 const char *privdesc) 1487 const char *privdesc)
1469{ 1488{
diff --git a/apps/plugins/puzzles/src/mines.c b/apps/plugins/puzzles/src/mines.c
index ae717d3f37..706777c4f1 100644
--- a/apps/plugins/puzzles/src/mines.c
+++ b/apps/plugins/puzzles/src/mines.c
@@ -3152,6 +3152,19 @@ static float game_flash_length(const game_state *oldstate,
3152 return 0.0F; 3152 return 0.0F;
3153} 3153}
3154 3154
3155static void game_get_cursor_location(const game_ui *ui,
3156 const game_drawstate *ds,
3157 const game_state *state,
3158 const game_params *params,
3159 int *x, int *y, int *w, int *h)
3160{
3161 if(ui->cur_visible) {
3162 *x = COORD(ui->cur_x);
3163 *y = COORD(ui->cur_y);
3164 *w = *h = TILE_SIZE;
3165 }
3166}
3167
3155static int game_status(const game_state *state) 3168static int game_status(const game_state *state)
3156{ 3169{
3157 /* 3170 /*
@@ -3213,6 +3226,7 @@ const struct game thegame = {
3213 game_redraw, 3226 game_redraw,
3214 game_anim_length, 3227 game_anim_length,
3215 game_flash_length, 3228 game_flash_length,
3229 game_get_cursor_location,
3216 game_status, 3230 game_status,
3217 false, false, game_print_size, game_print, 3231 false, false, game_print_size, game_print,
3218 true, /* wants_statusbar */ 3232 true, /* wants_statusbar */
diff --git a/apps/plugins/puzzles/src/net.c b/apps/plugins/puzzles/src/net.c
index 36f8aca483..d3032b6fe2 100644
--- a/apps/plugins/puzzles/src/net.c
+++ b/apps/plugins/puzzles/src/net.c
@@ -3090,6 +3090,20 @@ static float game_flash_length(const game_state *oldstate,
3090 return 0.0F; 3090 return 0.0F;
3091} 3091}
3092 3092
3093static void game_get_cursor_location(const game_ui *ui,
3094 const game_drawstate *ds,
3095 const game_state *state,
3096 const game_params *params,
3097 int *x, int *y, int *w, int *h)
3098{
3099 if(ui->cur_visible) {
3100 *x = WINDOW_OFFSET + TILE_SIZE * ui->cur_x;
3101 *y = WINDOW_OFFSET + TILE_SIZE * ui->cur_y;
3102
3103 *w = *h = TILE_SIZE;
3104 }
3105}
3106
3093static int game_status(const game_state *state) 3107static int game_status(const game_state *state)
3094{ 3108{
3095 return state->completed ? +1 : 0; 3109 return state->completed ? +1 : 0;
@@ -3271,6 +3285,7 @@ const struct game thegame = {
3271 game_redraw, 3285 game_redraw,
3272 game_anim_length, 3286 game_anim_length,
3273 game_flash_length, 3287 game_flash_length,
3288 game_get_cursor_location,
3274 game_status, 3289 game_status,
3275 true, false, game_print_size, game_print, 3290 true, false, game_print_size, game_print,
3276 true, /* wants_statusbar */ 3291 true, /* wants_statusbar */
diff --git a/apps/plugins/puzzles/src/netslide.c b/apps/plugins/puzzles/src/netslide.c
index 727ff0a910..14af2a689d 100644
--- a/apps/plugins/puzzles/src/netslide.c
+++ b/apps/plugins/puzzles/src/netslide.c
@@ -1830,6 +1830,20 @@ static float game_flash_length(const game_state *oldstate,
1830 return 0.0F; 1830 return 0.0F;
1831} 1831}
1832 1832
1833static void game_get_cursor_location(const game_ui *ui,
1834 const game_drawstate *ds,
1835 const game_state *state,
1836 const game_params *params,
1837 int *x, int *y, int *w, int *h)
1838{
1839 if(ui->cur_visible) {
1840 *x = BORDER + WINDOW_OFFSET + TILE_SIZE * ui->cur_x;
1841 *y = BORDER + WINDOW_OFFSET + TILE_SIZE * ui->cur_y;
1842
1843 *w = *h = TILE_SIZE;
1844 }
1845}
1846
1833static int game_status(const game_state *state) 1847static int game_status(const game_state *state)
1834{ 1848{
1835 return state->completed ? +1 : 0; 1849 return state->completed ? +1 : 0;
@@ -1884,6 +1898,7 @@ const struct game thegame = {
1884 game_redraw, 1898 game_redraw,
1885 game_anim_length, 1899 game_anim_length,
1886 game_flash_length, 1900 game_flash_length,
1901 game_get_cursor_location,
1887 game_status, 1902 game_status,
1888 false, false, game_print_size, game_print, 1903 false, false, game_print_size, game_print,
1889 true, /* wants_statusbar */ 1904 true, /* wants_statusbar */
diff --git a/apps/plugins/puzzles/src/nullgame.c b/apps/plugins/puzzles/src/nullgame.c
index 2fa4da6ffd..d923bc8710 100644
--- a/apps/plugins/puzzles/src/nullgame.c
+++ b/apps/plugins/puzzles/src/nullgame.c
@@ -242,6 +242,14 @@ static float game_flash_length(const game_state *oldstate,
242 return 0.0F; 242 return 0.0F;
243} 243}
244 244
245static void game_get_cursor_location(const game_ui *ui,
246 const game_drawstate *ds,
247 const game_state *state,
248 const game_params *params,
249 int *x, int *y, int *w, int *h)
250{
251}
252
245static int game_status(const game_state *state) 253static int game_status(const game_state *state)
246{ 254{
247 return 0; 255 return 0;
@@ -296,6 +304,7 @@ const struct game thegame = {
296 game_redraw, 304 game_redraw,
297 game_anim_length, 305 game_anim_length,
298 game_flash_length, 306 game_flash_length,
307 game_get_cursor_location,
299 game_status, 308 game_status,
300 false, false, game_print_size, game_print, 309 false, false, game_print_size, game_print,
301 false, /* wants_statusbar */ 310 false, /* wants_statusbar */
diff --git a/apps/plugins/puzzles/src/palisade.c b/apps/plugins/puzzles/src/palisade.c
index 759fcaa75d..e941661a0e 100644
--- a/apps/plugins/puzzles/src/palisade.c
+++ b/apps/plugins/puzzles/src/palisade.c
@@ -1288,6 +1288,19 @@ static float game_flash_length(const game_state *oldstate,
1288 return 0.0F; 1288 return 0.0F;
1289} 1289}
1290 1290
1291static void game_get_cursor_location(const game_ui *ui,
1292 const game_drawstate *ds,
1293 const game_state *state,
1294 const game_params *params,
1295 int *x, int *y, int *w, int *h)
1296{
1297 if(ui->show) {
1298 *x = MARGIN + TILESIZE * ui->x;
1299 *y = MARGIN + TILESIZE * ui->y;
1300 *w = *h = TILESIZE;
1301 }
1302}
1303
1291static int game_status(const game_state *state) 1304static int game_status(const game_state *state)
1292{ 1305{
1293 return state->completed ? +1 : 0; 1306 return state->completed ? +1 : 0;
@@ -1401,6 +1414,7 @@ const struct game thegame = {
1401 game_redraw, 1414 game_redraw,
1402 game_anim_length, 1415 game_anim_length,
1403 game_flash_length, 1416 game_flash_length,
1417 game_get_cursor_location,
1404 game_status, 1418 game_status,
1405 true, false, game_print_size, game_print, 1419 true, false, game_print_size, game_print,
1406 true, /* wants_statusbar */ 1420 true, /* wants_statusbar */
diff --git a/apps/plugins/puzzles/src/pattern.c b/apps/plugins/puzzles/src/pattern.c
index a43982f452..df720b7d82 100644
--- a/apps/plugins/puzzles/src/pattern.c
+++ b/apps/plugins/puzzles/src/pattern.c
@@ -1918,6 +1918,19 @@ static float game_flash_length(const game_state *oldstate,
1918 return 0.0F; 1918 return 0.0F;
1919} 1919}
1920 1920
1921static void game_get_cursor_location(const game_ui *ui,
1922 const game_drawstate *ds,
1923 const game_state *state,
1924 const game_params *params,
1925 int *x, int *y, int *w, int *h)
1926{
1927 if(ui->cur_visible) {
1928 *x = TOCOORD(ds->w, ui->cur_x);
1929 *y = TOCOORD(ds->h, ui->cur_y);
1930 *w = *h = TILE_SIZE;
1931 }
1932}
1933
1921static int game_status(const game_state *state) 1934static int game_status(const game_state *state)
1922{ 1935{
1923 return state->completed ? +1 : 0; 1936 return state->completed ? +1 : 0;
@@ -2029,6 +2042,7 @@ const struct game thegame = {
2029 game_redraw, 2042 game_redraw,
2030 game_anim_length, 2043 game_anim_length,
2031 game_flash_length, 2044 game_flash_length,
2045 game_get_cursor_location,
2032 game_status, 2046 game_status,
2033 true, false, game_print_size, game_print, 2047 true, false, game_print_size, game_print,
2034 false, /* wants_statusbar */ 2048 false, /* wants_statusbar */
diff --git a/apps/plugins/puzzles/src/pearl.c b/apps/plugins/puzzles/src/pearl.c
index ccbba51897..2657d45a67 100644
--- a/apps/plugins/puzzles/src/pearl.c
+++ b/apps/plugins/puzzles/src/pearl.c
@@ -2542,6 +2542,19 @@ static float game_flash_length(const game_state *oldstate,
2542 return 0.0F; 2542 return 0.0F;
2543} 2543}
2544 2544
2545static void game_get_cursor_location(const game_ui *ui,
2546 const game_drawstate *ds,
2547 const game_state *state,
2548 const game_params *params,
2549 int *x, int *y, int *w, int *h)
2550{
2551 if(ui->cursor_active) {
2552 *x = COORD(ui->curx);
2553 *y = COORD(ui->cury);
2554 *w = *h = TILE_SIZE;
2555 }
2556}
2557
2545static int game_status(const game_state *state) 2558static int game_status(const game_state *state)
2546{ 2559{
2547 return state->completed ? +1 : 0; 2560 return state->completed ? +1 : 0;
@@ -2635,6 +2648,7 @@ const struct game thegame = {
2635 game_redraw, 2648 game_redraw,
2636 game_anim_length, 2649 game_anim_length,
2637 game_flash_length, 2650 game_flash_length,
2651 game_get_cursor_location,
2638 game_status, 2652 game_status,
2639 true, false, game_print_size, game_print, 2653 true, false, game_print_size, game_print,
2640 false, /* wants_statusbar */ 2654 false, /* wants_statusbar */
diff --git a/apps/plugins/puzzles/src/pegs.c b/apps/plugins/puzzles/src/pegs.c
index db9caf298f..ec12aa9552 100644
--- a/apps/plugins/puzzles/src/pegs.c
+++ b/apps/plugins/puzzles/src/pegs.c
@@ -1280,6 +1280,19 @@ static float game_flash_length(const game_state *oldstate,
1280 return 0.0F; 1280 return 0.0F;
1281} 1281}
1282 1282
1283static void game_get_cursor_location(const game_ui *ui,
1284 const game_drawstate *ds,
1285 const game_state *state,
1286 const game_params *params,
1287 int *x, int *y, int *w, int *h)
1288{
1289 if(ui->cur_visible) {
1290 *x = COORD(ui->cur_x);
1291 *y = COORD(ui->cur_y);
1292 *w = *h = TILESIZE;
1293 }
1294}
1295
1283static int game_status(const game_state *state) 1296static int game_status(const game_state *state)
1284{ 1297{
1285 /* 1298 /*
@@ -1338,6 +1351,7 @@ const struct game thegame = {
1338 game_redraw, 1351 game_redraw,
1339 game_anim_length, 1352 game_anim_length,
1340 game_flash_length, 1353 game_flash_length,
1354 game_get_cursor_location,
1341 game_status, 1355 game_status,
1342 false, false, game_print_size, game_print, 1356 false, false, game_print_size, game_print,
1343 false, /* wants_statusbar */ 1357 false, /* wants_statusbar */
diff --git a/apps/plugins/puzzles/src/puzzles.h b/apps/plugins/puzzles/src/puzzles.h
index 45ae321cc6..5560aabe03 100644
--- a/apps/plugins/puzzles/src/puzzles.h
+++ b/apps/plugins/puzzles/src/puzzles.h
@@ -347,6 +347,8 @@ const char *identify_game(char **name,
347 bool (*read)(void *ctx, void *buf, int len), 347 bool (*read)(void *ctx, void *buf, int len),
348 void *rctx); 348 void *rctx);
349void midend_request_id_changes(midend *me, void (*notify)(void *), void *ctx); 349void midend_request_id_changes(midend *me, void (*notify)(void *), void *ctx);
350void midend_get_cursor_location(midend *me, int *x, int *y, int *w, int *h);
351
350/* Printing functions supplied by the mid-end */ 352/* Printing functions supplied by the mid-end */
351const char *midend_print_puzzle(midend *me, document *doc, bool with_soln); 353const char *midend_print_puzzle(midend *me, document *doc, bool with_soln);
352int midend_tilesize(midend *me); 354int midend_tilesize(midend *me);
@@ -684,6 +686,11 @@ struct game {
684 const game_state *newstate, int dir, game_ui *ui); 686 const game_state *newstate, int dir, game_ui *ui);
685 float (*flash_length)(const game_state *oldstate, 687 float (*flash_length)(const game_state *oldstate,
686 const game_state *newstate, int dir, game_ui *ui); 688 const game_state *newstate, int dir, game_ui *ui);
689 void (*get_cursor_location)(const game_ui *ui,
690 const game_drawstate *ds,
691 const game_state *state,
692 const game_params *params,
693 int *x, int *y, int *w, int *h);
687 int (*status)(const game_state *state); 694 int (*status)(const game_state *state);
688 bool can_print, can_print_in_colour; 695 bool can_print, can_print_in_colour;
689 void (*print_size)(const game_params *params, float *x, float *y); 696 void (*print_size)(const game_params *params, float *x, float *y);
diff --git a/apps/plugins/puzzles/src/range.c b/apps/plugins/puzzles/src/range.c
index 64bd17da56..fc0a5405f1 100644
--- a/apps/plugins/puzzles/src/range.c
+++ b/apps/plugins/puzzles/src/range.c
@@ -1572,6 +1572,19 @@ static float game_flash_length(const game_state *from,
1572 return 0.0F; 1572 return 0.0F;
1573} 1573}
1574 1574
1575static void game_get_cursor_location(const game_ui *ui,
1576 const game_drawstate *ds,
1577 const game_state *state,
1578 const game_params *params,
1579 int *x, int *y, int *w, int *h)
1580{
1581 if(ui->cursor_show) {
1582 *x = BORDER + TILESIZE * ui->c;
1583 *y = BORDER + TILESIZE * ui->r;
1584 *w = *h = TILESIZE;
1585 }
1586}
1587
1575static int game_status(const game_state *state) 1588static int game_status(const game_state *state)
1576{ 1589{
1577 return state->was_solved ? +1 : 0; 1590 return state->was_solved ? +1 : 0;
@@ -1823,6 +1836,7 @@ struct game const thegame = {
1823 game_redraw, 1836 game_redraw,
1824 game_anim_length, 1837 game_anim_length,
1825 game_flash_length, 1838 game_flash_length,
1839 game_get_cursor_location,
1826 game_status, 1840 game_status,
1827 true, false, game_print_size, game_print, 1841 true, false, game_print_size, game_print,
1828 false, /* wants_statusbar */ 1842 false, /* wants_statusbar */
diff --git a/apps/plugins/puzzles/src/rect.c b/apps/plugins/puzzles/src/rect.c
index 2cb01bfa5d..b13de75fd4 100644
--- a/apps/plugins/puzzles/src/rect.c
+++ b/apps/plugins/puzzles/src/rect.c
@@ -2883,6 +2883,19 @@ static float game_flash_length(const game_state *oldstate,
2883 return 0.0F; 2883 return 0.0F;
2884} 2884}
2885 2885
2886static void game_get_cursor_location(const game_ui *ui,
2887 const game_drawstate *ds,
2888 const game_state *state,
2889 const game_params *params,
2890 int *x, int *y, int *w, int *h)
2891{
2892 if(ui->cur_visible) {
2893 *x = COORD(ui->cur_x);
2894 *y = COORD(ui->cur_y);
2895 *w = *h = TILE_SIZE;
2896 }
2897}
2898
2886static int game_status(const game_state *state) 2899static int game_status(const game_state *state)
2887{ 2900{
2888 return state->completed ? +1 : 0; 2901 return state->completed ? +1 : 0;
@@ -2994,6 +3007,7 @@ const struct game thegame = {
2994 game_redraw, 3007 game_redraw,
2995 game_anim_length, 3008 game_anim_length,
2996 game_flash_length, 3009 game_flash_length,
3010 game_get_cursor_location,
2997 game_status, 3011 game_status,
2998 true, false, game_print_size, game_print, 3012 true, false, game_print_size, game_print,
2999 true, /* wants_statusbar */ 3013 true, /* wants_statusbar */
diff --git a/apps/plugins/puzzles/src/samegame.c b/apps/plugins/puzzles/src/samegame.c
index 272c7b457d..615c60e0a5 100644
--- a/apps/plugins/puzzles/src/samegame.c
+++ b/apps/plugins/puzzles/src/samegame.c
@@ -1615,6 +1615,19 @@ static float game_flash_length(const game_state *oldstate,
1615 return 0.0F; 1615 return 0.0F;
1616} 1616}
1617 1617
1618static void game_get_cursor_location(const game_ui *ui,
1619 const game_drawstate *ds,
1620 const game_state *state,
1621 const game_params *params,
1622 int *x, int *y, int *w, int *h)
1623{
1624 if(ui->displaysel) {
1625 *x = COORD(ui->xsel);
1626 *y = COORD(ui->ysel);
1627 *w = *h = TILE_SIZE;
1628 }
1629}
1630
1618static int game_status(const game_state *state) 1631static int game_status(const game_state *state)
1619{ 1632{
1620 /* 1633 /*
@@ -1673,6 +1686,7 @@ const struct game thegame = {
1673 game_redraw, 1686 game_redraw,
1674 game_anim_length, 1687 game_anim_length,
1675 game_flash_length, 1688 game_flash_length,
1689 game_get_cursor_location,
1676 game_status, 1690 game_status,
1677 false, false, game_print_size, game_print, 1691 false, false, game_print_size, game_print,
1678 true, /* wants_statusbar */ 1692 true, /* wants_statusbar */
diff --git a/apps/plugins/puzzles/src/signpost.c b/apps/plugins/puzzles/src/signpost.c
index abf4c6a79c..9aee255ebe 100644
--- a/apps/plugins/puzzles/src/signpost.c
+++ b/apps/plugins/puzzles/src/signpost.c
@@ -2188,6 +2188,19 @@ static float game_flash_length(const game_state *oldstate,
2188 return 0.0F; 2188 return 0.0F;
2189} 2189}
2190 2190
2191static void game_get_cursor_location(const game_ui *ui,
2192 const game_drawstate *ds,
2193 const game_state *state,
2194 const game_params *params,
2195 int *x, int *y, int *w, int *h)
2196{
2197 if(ui->cshow) {
2198 *x = COORD(ui->cx);
2199 *y = COORD(ui->cy);
2200 *w = *h = TILE_SIZE;
2201 }
2202}
2203
2191static int game_status(const game_state *state) 2204static int game_status(const game_state *state)
2192{ 2205{
2193 return state->completed ? +1 : 0; 2206 return state->completed ? +1 : 0;
@@ -2275,6 +2288,7 @@ const struct game thegame = {
2275 game_redraw, 2288 game_redraw,
2276 game_anim_length, 2289 game_anim_length,
2277 game_flash_length, 2290 game_flash_length,
2291 game_get_cursor_location,
2278 game_status, 2292 game_status,
2279 true, false, game_print_size, game_print, 2293 true, false, game_print_size, game_print,
2280 false, /* wants_statusbar */ 2294 false, /* wants_statusbar */
diff --git a/apps/plugins/puzzles/src/singles.c b/apps/plugins/puzzles/src/singles.c
index 3dde8c2b87..202ce08b20 100644
--- a/apps/plugins/puzzles/src/singles.c
+++ b/apps/plugins/puzzles/src/singles.c
@@ -1758,6 +1758,19 @@ static float game_flash_length(const game_state *oldstate,
1758 return 0.0F; 1758 return 0.0F;
1759} 1759}
1760 1760
1761static void game_get_cursor_location(const game_ui *ui,
1762 const game_drawstate *ds,
1763 const game_state *state,
1764 const game_params *params,
1765 int *x, int *y, int *w, int *h)
1766{
1767 if(ui->cshow) {
1768 *x = COORD(ui->cx);
1769 *y = COORD(ui->cy);
1770 *w = *h = TILE_SIZE;
1771 }
1772}
1773
1761static int game_status(const game_state *state) 1774static int game_status(const game_state *state)
1762{ 1775{
1763 return state->completed ? +1 : 0; 1776 return state->completed ? +1 : 0;
@@ -1850,6 +1863,7 @@ const struct game thegame = {
1850 game_redraw, 1863 game_redraw,
1851 game_anim_length, 1864 game_anim_length,
1852 game_flash_length, 1865 game_flash_length,
1866 game_get_cursor_location,
1853 game_status, 1867 game_status,
1854 true, false, game_print_size, game_print, 1868 true, false, game_print_size, game_print,
1855 false, /* wants_statusbar */ 1869 false, /* wants_statusbar */
diff --git a/apps/plugins/puzzles/src/sixteen.c b/apps/plugins/puzzles/src/sixteen.c
index e47147a8a5..0b02038c43 100644
--- a/apps/plugins/puzzles/src/sixteen.c
+++ b/apps/plugins/puzzles/src/sixteen.c
@@ -1147,6 +1147,19 @@ static float game_flash_length(const game_state *oldstate,
1147 return 0.0F; 1147 return 0.0F;
1148} 1148}
1149 1149
1150static void game_get_cursor_location(const game_ui *ui,
1151 const game_drawstate *ds,
1152 const game_state *state,
1153 const game_params *params,
1154 int *x, int *y, int *w, int *h)
1155{
1156 if(ui->cur_visible) {
1157 *x = COORD(ui->cur_x);
1158 *y = COORD(ui->cur_y);
1159 *w = *h = TILE_SIZE;
1160 }
1161}
1162
1150static int game_status(const game_state *state) 1163static int game_status(const game_state *state)
1151{ 1164{
1152 return state->completed ? +1 : 0; 1165 return state->completed ? +1 : 0;
@@ -1201,6 +1214,7 @@ const struct game thegame = {
1201 game_redraw, 1214 game_redraw,
1202 game_anim_length, 1215 game_anim_length,
1203 game_flash_length, 1216 game_flash_length,
1217 game_get_cursor_location,
1204 game_status, 1218 game_status,
1205 false, false, game_print_size, game_print, 1219 false, false, game_print_size, game_print,
1206 true, /* wants_statusbar */ 1220 true, /* wants_statusbar */
diff --git a/apps/plugins/puzzles/src/slant.c b/apps/plugins/puzzles/src/slant.c
index 70b2585b81..ed290fe57d 100644
--- a/apps/plugins/puzzles/src/slant.c
+++ b/apps/plugins/puzzles/src/slant.c
@@ -2064,6 +2064,19 @@ static float game_flash_length(const game_state *oldstate,
2064 return 0.0F; 2064 return 0.0F;
2065} 2065}
2066 2066
2067static void game_get_cursor_location(const game_ui *ui,
2068 const game_drawstate *ds,
2069 const game_state *state,
2070 const game_params *params,
2071 int *x, int *y, int *w, int *h)
2072{
2073 if(ui->cur_visible) {
2074 *x = COORD(ui->cur_x);
2075 *y = COORD(ui->cur_y);
2076 *w = *h = TILESIZE;
2077 }
2078}
2079
2067static int game_status(const game_state *state) 2080static int game_status(const game_state *state)
2068{ 2081{
2069 return state->completed ? +1 : 0; 2082 return state->completed ? +1 : 0;
@@ -2181,6 +2194,7 @@ const struct game thegame = {
2181 game_redraw, 2194 game_redraw,
2182 game_anim_length, 2195 game_anim_length,
2183 game_flash_length, 2196 game_flash_length,
2197 game_get_cursor_location,
2184 game_status, 2198 game_status,
2185 true, false, game_print_size, game_print, 2199 true, false, game_print_size, game_print,
2186 false, /* wants_statusbar */ 2200 false, /* wants_statusbar */
diff --git a/apps/plugins/puzzles/src/solo.c b/apps/plugins/puzzles/src/solo.c
index cfe38c5bd9..49753f41dc 100644
--- a/apps/plugins/puzzles/src/solo.c
+++ b/apps/plugins/puzzles/src/solo.c
@@ -5297,6 +5297,19 @@ static float game_flash_length(const game_state *oldstate,
5297 return 0.0F; 5297 return 0.0F;
5298} 5298}
5299 5299
5300static void game_get_cursor_location(const game_ui *ui,
5301 const game_drawstate *ds,
5302 const game_state *state,
5303 const game_params *params,
5304 int *x, int *y, int *w, int *h)
5305{
5306 if(ui->hshow) {
5307 *x = BORDER + ui->hx * TILE_SIZE + 1 + GRIDEXTRA;
5308 *y = BORDER + ui->hy * TILE_SIZE + 1 + GRIDEXTRA;
5309 *w = *h = TILE_SIZE;
5310 }
5311}
5312
5300static int game_status(const game_state *state) 5313static int game_status(const game_state *state)
5301{ 5314{
5302 return state->completed ? +1 : 0; 5315 return state->completed ? +1 : 0;
@@ -5622,6 +5635,7 @@ const struct game thegame = {
5622 game_redraw, 5635 game_redraw,
5623 game_anim_length, 5636 game_anim_length,
5624 game_flash_length, 5637 game_flash_length,
5638 game_get_cursor_location,
5625 game_status, 5639 game_status,
5626 true, false, game_print_size, game_print, 5640 true, false, game_print_size, game_print,
5627 false, /* wants_statusbar */ 5641 false, /* wants_statusbar */
diff --git a/apps/plugins/puzzles/src/tents.c b/apps/plugins/puzzles/src/tents.c
index 1e601f5836..ee06172baf 100644
--- a/apps/plugins/puzzles/src/tents.c
+++ b/apps/plugins/puzzles/src/tents.c
@@ -2554,6 +2554,19 @@ static float game_flash_length(const game_state *oldstate,
2554 return 0.0F; 2554 return 0.0F;
2555} 2555}
2556 2556
2557static void game_get_cursor_location(const game_ui *ui,
2558 const game_drawstate *ds,
2559 const game_state *state,
2560 const game_params *params,
2561 int *x, int *y, int *w, int *h)
2562{
2563 if(ui->cdisp) {
2564 *x = COORD(ui->cx);
2565 *y = COORD(ui->cy);
2566 *w = *h = TILESIZE;
2567 }
2568}
2569
2557static int game_status(const game_state *state) 2570static int game_status(const game_state *state)
2558{ 2571{
2559 return state->completed ? +1 : 0; 2572 return state->completed ? +1 : 0;
@@ -2630,6 +2643,7 @@ const struct game thegame = {
2630 game_redraw, 2643 game_redraw,
2631 game_anim_length, 2644 game_anim_length,
2632 game_flash_length, 2645 game_flash_length,
2646 game_get_cursor_location,
2633 game_status, 2647 game_status,
2634 true, false, game_print_size, game_print, 2648 true, false, game_print_size, game_print,
2635 false, /* wants_statusbar */ 2649 false, /* wants_statusbar */
diff --git a/apps/plugins/puzzles/src/towers.c b/apps/plugins/puzzles/src/towers.c
index aee088fb54..13c652b819 100644
--- a/apps/plugins/puzzles/src/towers.c
+++ b/apps/plugins/puzzles/src/towers.c
@@ -1935,6 +1935,19 @@ static float game_flash_length(const game_state *oldstate,
1935 return 0.0F; 1935 return 0.0F;
1936} 1936}
1937 1937
1938static void game_get_cursor_location(const game_ui *ui,
1939 const game_drawstate *ds,
1940 const game_state *state,
1941 const game_params *params,
1942 int *x, int *y, int *w, int *h)
1943{
1944 if(ui->hshow) {
1945 *x = COORD(ui->hx);
1946 *y = COORD(ui->hy);
1947 *w = *h = TILESIZE;
1948 }
1949}
1950
1938static int game_status(const game_state *state) 1951static int game_status(const game_state *state)
1939{ 1952{
1940 return state->completed ? +1 : 0; 1953 return state->completed ? +1 : 0;
@@ -2060,6 +2073,7 @@ const struct game thegame = {
2060 game_redraw, 2073 game_redraw,
2061 game_anim_length, 2074 game_anim_length,
2062 game_flash_length, 2075 game_flash_length,
2076 game_get_cursor_location,
2063 game_status, 2077 game_status,
2064 true, false, game_print_size, game_print, 2078 true, false, game_print_size, game_print,
2065 false, /* wants_statusbar */ 2079 false, /* wants_statusbar */
diff --git a/apps/plugins/puzzles/src/tracks.c b/apps/plugins/puzzles/src/tracks.c
index 924836afa9..3cf5df70a1 100644
--- a/apps/plugins/puzzles/src/tracks.c
+++ b/apps/plugins/puzzles/src/tracks.c
@@ -2854,6 +2854,37 @@ static float game_flash_length(const game_state *oldstate, const game_state *new
2854 return 0.0F; 2854 return 0.0F;
2855} 2855}
2856 2856
2857static void game_get_cursor_location(const game_ui *ui,
2858 const game_drawstate *ds,
2859 const game_state *state,
2860 const game_params *params,
2861 int *x, int *y, int *w, int *h)
2862{
2863 if(ui->cursor_active) {
2864 int off = HALFSZ / 4;
2865 int cx = COORD(ui->curx / 2) + off;
2866 int cy = COORD(ui->cury / 2) + off;
2867 int cw, ch;
2868 cw = ch = TILE_SIZE - (2*off) + 1;
2869
2870 if(ui->curx % 2 == 0) {
2871 /* left border */
2872 cx -= off;
2873 cw = 2 * off + 1;
2874 }
2875 if(ui->cury % 2 == 0) {
2876 /* upper border */
2877 cy -= off;
2878 ch = 2 * off + 1;
2879 }
2880
2881 *x = cx;
2882 *y = cy;
2883 *w = cw;
2884 *h = ch;
2885 }
2886}
2887
2857static int game_status(const game_state *state) 2888static int game_status(const game_state *state)
2858{ 2889{
2859 return state->completed ? +1 : 0; 2890 return state->completed ? +1 : 0;
@@ -2948,6 +2979,7 @@ const struct game thegame = {
2948 game_redraw, 2979 game_redraw,
2949 game_anim_length, 2980 game_anim_length,
2950 game_flash_length, 2981 game_flash_length,
2982 game_get_cursor_location,
2951 game_status, 2983 game_status,
2952 true, false, game_print_size, game_print, 2984 true, false, game_print_size, game_print,
2953 false, /* wants_statusbar */ 2985 false, /* wants_statusbar */
diff --git a/apps/plugins/puzzles/src/twiddle.c b/apps/plugins/puzzles/src/twiddle.c
index 5f2ea02e6f..a107925aee 100644
--- a/apps/plugins/puzzles/src/twiddle.c
+++ b/apps/plugins/puzzles/src/twiddle.c
@@ -1092,6 +1092,19 @@ static float game_flash_length(const game_state *oldstate,
1092 return 0.0F; 1092 return 0.0F;
1093} 1093}
1094 1094
1095static void game_get_cursor_location(const game_ui *ui,
1096 const game_drawstate *ds,
1097 const game_state *state,
1098 const game_params *params,
1099 int *x, int *y, int *w, int *h)
1100{
1101 if(ui->cur_visible) {
1102 *x = COORD(ui->cur_x);
1103 *y = COORD(ui->cur_y);
1104 *w = *h = state->n * TILE_SIZE;
1105 }
1106}
1107
1095static int game_status(const game_state *state) 1108static int game_status(const game_state *state)
1096{ 1109{
1097 return state->completed ? +1 : 0; 1110 return state->completed ? +1 : 0;
@@ -1318,6 +1331,7 @@ const struct game thegame = {
1318 game_redraw, 1331 game_redraw,
1319 game_anim_length, 1332 game_anim_length,
1320 game_flash_length, 1333 game_flash_length,
1334 game_get_cursor_location,
1321 game_status, 1335 game_status,
1322 false, false, game_print_size, game_print, 1336 false, false, game_print_size, game_print,
1323 true, /* wants_statusbar */ 1337 true, /* wants_statusbar */
diff --git a/apps/plugins/puzzles/src/undead.c b/apps/plugins/puzzles/src/undead.c
index 781c15d791..4dba828d48 100644
--- a/apps/plugins/puzzles/src/undead.c
+++ b/apps/plugins/puzzles/src/undead.c
@@ -2727,6 +2727,19 @@ static float game_flash_length(const game_state *oldstate,
2727 !newstate->cheated) ? FLASH_TIME : 0.0F; 2727 !newstate->cheated) ? FLASH_TIME : 0.0F;
2728} 2728}
2729 2729
2730static void game_get_cursor_location(const game_ui *ui,
2731 const game_drawstate *ds,
2732 const game_state *state,
2733 const game_params *params,
2734 int *x, int *y, int *w, int *h)
2735{
2736 if(ui->hshow) {
2737 *x = BORDER + (ui->hx) * TILESIZE;
2738 *y = BORDER + (ui->hy + 1) * TILESIZE;
2739 *w = *h = TILESIZE;
2740 }
2741}
2742
2730static int game_status(const game_state *state) 2743static int game_status(const game_state *state)
2731{ 2744{
2732 return state->solved; 2745 return state->solved;
@@ -2781,6 +2794,7 @@ const struct game thegame = {
2781 game_redraw, 2794 game_redraw,
2782 game_anim_length, 2795 game_anim_length,
2783 game_flash_length, 2796 game_flash_length,
2797 game_get_cursor_location,
2784 game_status, 2798 game_status,
2785 false, false, game_print_size, game_print, 2799 false, false, game_print_size, game_print,
2786 false, /* wants_statusbar */ 2800 false, /* wants_statusbar */
diff --git a/apps/plugins/puzzles/src/unequal.c b/apps/plugins/puzzles/src/unequal.c
index 556cf01c45..153954e510 100644
--- a/apps/plugins/puzzles/src/unequal.c
+++ b/apps/plugins/puzzles/src/unequal.c
@@ -2041,6 +2041,19 @@ static float game_flash_length(const game_state *oldstate,
2041 return 0.0F; 2041 return 0.0F;
2042} 2042}
2043 2043
2044static void game_get_cursor_location(const game_ui *ui,
2045 const game_drawstate *ds,
2046 const game_state *state,
2047 const game_params *params,
2048 int *x, int *y, int *w, int *h)
2049{
2050 if(ui->hshow) {
2051 *x = COORD(ui->hx);
2052 *y = COORD(ui->hy);
2053 *w = *h = TILE_SIZE;
2054 }
2055}
2056
2044static int game_status(const game_state *state) 2057static int game_status(const game_state *state)
2045{ 2058{
2046 return state->completed ? +1 : 0; 2059 return state->completed ? +1 : 0;
@@ -2135,6 +2148,7 @@ const struct game thegame = {
2135 game_redraw, 2148 game_redraw,
2136 game_anim_length, 2149 game_anim_length,
2137 game_flash_length, 2150 game_flash_length,
2151 game_get_cursor_location,
2138 game_status, 2152 game_status,
2139 true, false, game_print_size, game_print, 2153 true, false, game_print_size, game_print,
2140 false, /* wants_statusbar */ 2154 false, /* wants_statusbar */
diff --git a/apps/plugins/puzzles/src/unfinished/group.c b/apps/plugins/puzzles/src/unfinished/group.c
index 006a9e0ee6..8e0185741e 100644
--- a/apps/plugins/puzzles/src/unfinished/group.c
+++ b/apps/plugins/puzzles/src/unfinished/group.c
@@ -2196,6 +2196,14 @@ static float game_flash_length(const game_state *oldstate,
2196 return 0.0F; 2196 return 0.0F;
2197} 2197}
2198 2198
2199static void game_get_cursor_location(const game_ui *ui,
2200 const game_drawstate *ds,
2201 const game_state *state,
2202 const game_params *params,
2203 int *x, int *y, int *w, int *h)
2204{
2205}
2206
2199static int game_status(const game_state *state) 2207static int game_status(const game_state *state)
2200{ 2208{
2201 return state->completed ? +1 : 0; 2209 return state->completed ? +1 : 0;
@@ -2320,6 +2328,7 @@ const struct game thegame = {
2320 game_redraw, 2328 game_redraw,
2321 game_anim_length, 2329 game_anim_length,
2322 game_flash_length, 2330 game_flash_length,
2331 game_get_cursor_location,
2323 game_status, 2332 game_status,
2324 true, false, game_print_size, game_print, 2333 true, false, game_print_size, game_print,
2325 false, /* wants_statusbar */ 2334 false, /* wants_statusbar */
diff --git a/apps/plugins/puzzles/src/unfinished/separate.c b/apps/plugins/puzzles/src/unfinished/separate.c
index 88dc8ed060..39243afb92 100644
--- a/apps/plugins/puzzles/src/unfinished/separate.c
+++ b/apps/plugins/puzzles/src/unfinished/separate.c
@@ -799,6 +799,14 @@ static float game_flash_length(const game_state *oldstate,
799 return 0.0F; 799 return 0.0F;
800} 800}
801 801
802static void game_get_cursor_location(const game_ui *ui,
803 const game_drawstate *ds,
804 const game_state *state,
805 const game_params *params,
806 int *x, int *y, int *w, int *h)
807{
808}
809
802static int game_status(const game_state *state) 810static int game_status(const game_state *state)
803{ 811{
804 return 0; 812 return 0;
@@ -853,6 +861,7 @@ const struct game thegame = {
853 game_redraw, 861 game_redraw,
854 game_anim_length, 862 game_anim_length,
855 game_flash_length, 863 game_flash_length,
864 game_get_cursor_location,
856 game_status, 865 game_status,
857 false, false, game_print_size, game_print, 866 false, false, game_print_size, game_print,
858 false, /* wants_statusbar */ 867 false, /* wants_statusbar */
diff --git a/apps/plugins/puzzles/src/unfinished/slide.c b/apps/plugins/puzzles/src/unfinished/slide.c
index 5ad1237d58..c7a3dcecf7 100644
--- a/apps/plugins/puzzles/src/unfinished/slide.c
+++ b/apps/plugins/puzzles/src/unfinished/slide.c
@@ -2297,6 +2297,14 @@ static float game_flash_length(const game_state *oldstate,
2297 return 0.0F; 2297 return 0.0F;
2298} 2298}
2299 2299
2300static void game_get_cursor_location(const game_ui *ui,
2301 const game_drawstate *ds,
2302 const game_state *state,
2303 const game_params *params,
2304 int *x, int *y, int *w, int *h)
2305{
2306}
2307
2300static int game_status(const game_state *state) 2308static int game_status(const game_state *state)
2301{ 2309{
2302 return state->completed ? +1 : 0; 2310 return state->completed ? +1 : 0;
@@ -2351,6 +2359,7 @@ const struct game thegame = {
2351 game_redraw, 2359 game_redraw,
2352 game_anim_length, 2360 game_anim_length,
2353 game_flash_length, 2361 game_flash_length,
2362 game_get_cursor_location,
2354 game_status, 2363 game_status,
2355 false, false, game_print_size, game_print, 2364 false, false, game_print_size, game_print,
2356 true, /* wants_statusbar */ 2365 true, /* wants_statusbar */
diff --git a/apps/plugins/puzzles/src/unfinished/sokoban.c b/apps/plugins/puzzles/src/unfinished/sokoban.c
index 7d42a12c5d..ecc222c906 100644
--- a/apps/plugins/puzzles/src/unfinished/sokoban.c
+++ b/apps/plugins/puzzles/src/unfinished/sokoban.c
@@ -1415,6 +1415,14 @@ static float game_flash_length(const game_state *oldstate,
1415 return 0.0F; 1415 return 0.0F;
1416} 1416}
1417 1417
1418static void game_get_cursor_location(const game_ui *ui,
1419 const game_drawstate *ds,
1420 const game_state *state,
1421 const game_params *params,
1422 int *x, int *y, int *w, int *h)
1423{
1424}
1425
1418static int game_status(const game_state *state) 1426static int game_status(const game_state *state)
1419{ 1427{
1420 return state->completed ? +1 : 0; 1428 return state->completed ? +1 : 0;
@@ -1469,6 +1477,7 @@ const struct game thegame = {
1469 game_redraw, 1477 game_redraw,
1470 game_anim_length, 1478 game_anim_length,
1471 game_flash_length, 1479 game_flash_length,
1480 game_get_cursor_location,
1472 game_status, 1481 game_status,
1473 false, false, game_print_size, game_print, 1482 false, false, game_print_size, game_print,
1474 false, /* wants_statusbar */ 1483 false, /* wants_statusbar */
diff --git a/apps/plugins/puzzles/src/unruly.c b/apps/plugins/puzzles/src/unruly.c
index e69c31b6b9..a1f06332e0 100644
--- a/apps/plugins/puzzles/src/unruly.c
+++ b/apps/plugins/puzzles/src/unruly.c
@@ -1860,6 +1860,19 @@ static float game_flash_length(const game_state *oldstate,
1860 return 0.0F; 1860 return 0.0F;
1861} 1861}
1862 1862
1863static void game_get_cursor_location(const game_ui *ui,
1864 const game_drawstate *ds,
1865 const game_state *state,
1866 const game_params *params,
1867 int *x, int *y, int *w, int *h)
1868{
1869 if(ui->cursor) {
1870 *x = COORD(ui->cx);
1871 *y = COORD(ui->cy);
1872 *w = *h = TILE_SIZE;
1873 }
1874}
1875
1863static int game_status(const game_state *state) 1876static int game_status(const game_state *state)
1864{ 1877{
1865 return state->completed ? +1 : 0; 1878 return state->completed ? +1 : 0;
@@ -1948,6 +1961,7 @@ const struct game thegame = {
1948 game_redraw, 1961 game_redraw,
1949 game_anim_length, 1962 game_anim_length,
1950 game_flash_length, 1963 game_flash_length,
1964 game_get_cursor_location,
1951 game_status, 1965 game_status,
1952 true, false, game_print_size, game_print, 1966 true, false, game_print_size, game_print,
1953 false, /* wants_statusbar */ 1967 false, /* wants_statusbar */
diff --git a/apps/plugins/puzzles/src/untangle.c b/apps/plugins/puzzles/src/untangle.c
index eff93382cd..0d3e0e6135 100644
--- a/apps/plugins/puzzles/src/untangle.c
+++ b/apps/plugins/puzzles/src/untangle.c
@@ -1581,6 +1581,25 @@ static float game_flash_length(const game_state *oldstate,
1581 return 0.0F; 1581 return 0.0F;
1582} 1582}
1583 1583
1584static void game_get_cursor_location(const game_ui *ui,
1585 const game_drawstate *ds,
1586 const game_state *state,
1587 const game_params *params,
1588 int *x, int *y, int *w, int *h)
1589{
1590 if(ui->dragpoint >= 0 || ui->cursorpoint >= 0) {
1591 int idx = (ui->dragpoint >= 0) ? ui->dragpoint : ui->cursorpoint;
1592
1593 int cx, cy;
1594 cx = ds->x[idx];
1595 cy = ds->y[idx];
1596
1597 *x = cx - CIRCLE_RADIUS;
1598 *y = cy - CIRCLE_RADIUS;
1599 *w = *h = 2 * CIRCLE_RADIUS + 1;
1600 }
1601}
1602
1584static int game_status(const game_state *state) 1603static int game_status(const game_state *state)
1585{ 1604{
1586 return state->completed ? +1 : 0; 1605 return state->completed ? +1 : 0;
@@ -1635,6 +1654,7 @@ const struct game thegame = {
1635 game_redraw, 1654 game_redraw,
1636 game_anim_length, 1655 game_anim_length,
1637 game_flash_length, 1656 game_flash_length,
1657 game_get_cursor_location,
1638 game_status, 1658 game_status,
1639 false, false, game_print_size, game_print, 1659 false, false, game_print_size, game_print,
1640 false, /* wants_statusbar */ 1660 false, /* wants_statusbar */