summaryrefslogtreecommitdiff
path: root/apps/plugins/puzzles
diff options
context:
space:
mode:
authorFranklin Wei <git@fwei.tk>2017-11-21 19:28:16 -0500
committerFranklin Wei <git@fwei.tk>2017-11-21 19:29:45 -0500
commite8e85c5762da65ef7fa6e49ee8cc61f132be6d34 (patch)
tree1fb2c9724e61d121fe0b4460e3e626908f94fcea /apps/plugins/puzzles
parentf4c42213062170ddfcc706b3c5ed19f47517c253 (diff)
downloadrockbox-e8e85c5762da65ef7fa6e49ee8cc61f132be6d34.tar.gz
rockbox-e8e85c5762da65ef7fa6e49ee8cc61f132be6d34.zip
puzzles: resync with upstream; add Loopy and Palisade, mouse mode
This brings a various small changes to the drawing and input code, as well as a brand new "mouse mode", where input goes to a virtual mouse cursor. Only Loopy has this mouse mode enabled by default, while other games have it hidden away under the debug menu. Some changes by me to Palisade were required to make it playable; those are included here as well. Right now, sgt-net is pushing the c200v2's upper limit on size and may have to be dropped in a future commit. Change-Id: I495d2a2125462c2985aec1ffbc54bbe3fe5133bd
Diffstat (limited to 'apps/plugins/puzzles')
-rw-r--r--apps/plugins/puzzles/SOURCES.games20
-rw-r--r--apps/plugins/puzzles/rbmalloc.c6
-rw-r--r--apps/plugins/puzzles/rockbox.c445
-rw-r--r--apps/plugins/puzzles/src/LICENCE5
-rw-r--r--apps/plugins/puzzles/src/filling.c2
-rw-r--r--apps/plugins/puzzles/src/grid.c96
-rw-r--r--apps/plugins/puzzles/src/grid.h1
-rw-r--r--apps/plugins/puzzles/src/loopy.c3
-rw-r--r--apps/plugins/puzzles/src/palisade.c21
9 files changed, 493 insertions, 106 deletions
diff --git a/apps/plugins/puzzles/SOURCES.games b/apps/plugins/puzzles/SOURCES.games
index 8893c3e9a4..2fbedae073 100644
--- a/apps/plugins/puzzles/SOURCES.games
+++ b/apps/plugins/puzzles/SOURCES.games
@@ -5,21 +5,20 @@ src/bridges.c
5src/cube.c 5src/cube.c
6src/dominosa.c 6src/dominosa.c
7src/fifteen.c 7src/fifteen.c
8/*src/filling.c*/ 8//src/filling.c /* requires numeric input */
9src/flip.c 9src/flip.c
10src/flood.c 10src/flood.c
11src/galaxies.c 11src/galaxies.c
12src/guess.c 12src/guess.c
13src/inertia.c 13src/inertia.c
14/*src/keen.c*/ 14//src/keen.c /* numeric input */
15src/lightup.c 15src/lightup.c
16/*src/loopy.c*/
17src/magnets.c 16src/magnets.c
18src/map.c 17src/map.c
19src/mines.c 18src/mines.c
20src/net.c 19src/net.c
21src/netslide.c 20src/netslide.c
22/*src/palisade.c*/ 21src/palisade.c
23src/pattern.c 22src/pattern.c
24src/pegs.c 23src/pegs.c
25src/range.c 24src/range.c
@@ -29,17 +28,16 @@ src/signpost.c
29src/singles.c 28src/singles.c
30src/sixteen.c 29src/sixteen.c
31src/slant.c 30src/slant.c
32/*src/solo.c*/
33src/tents.c 31src/tents.c
34/*src/towers.c*/ 32//src/towers.c /* numeric input */
35src/tracks.c 33src/tracks.c
36src/twiddle.c 34src/twiddle.c
37/*src/undead.c*/ 35//src/undead.c /* keyboard input */
38/*src/unequal.c*/ 36//src/unequal.c /* numeric input */
39src/unruly.c 37src/unruly.c
40src/untangle.c 38src/untangle.c
41 39
42/* disabled for now */ 40/* disabled for now (fix puzzles.make and CATEGORIES to accomodate these) */
43/*src/unfinished/group.c*/ 41/*src/unfinished/group.c*/
44/*src/unfinished/separate.c*/ 42/*src/unfinished/separate.c*/
45/*src/unfinished/slide.c*/ 43/*src/unfinished/slide.c*/
@@ -48,6 +46,6 @@ src/untangle.c
48/* no c200v2 */ 46/* no c200v2 */
49#if PLUGIN_BUFFER_SIZE > 0x14000 47#if PLUGIN_BUFFER_SIZE > 0x14000
50src/pearl.c 48src/pearl.c
51/*src/loopy.c*/ 49src/loopy.c /* mouse input */
52/*src/solo.c*/ 50//src/solo.c /* numeric input */
53#endif 51#endif
diff --git a/apps/plugins/puzzles/rbmalloc.c b/apps/plugins/puzzles/rbmalloc.c
index 1cb903ef4f..4baca79c3d 100644
--- a/apps/plugins/puzzles/rbmalloc.c
+++ b/apps/plugins/puzzles/rbmalloc.c
@@ -51,7 +51,7 @@ static bool grab_audiobuf(void)
51void *smalloc(size_t size) { 51void *smalloc(size_t size) {
52 void *p; 52 void *p;
53 p = malloc(size); 53 p = malloc(size);
54 LOGF("allocs: %d", ++allocs); 54 //LOGF("allocs: %d", ++allocs);
55 if (!p) 55 if (!p)
56 { 56 {
57 if(grab_audiobuf()) 57 if(grab_audiobuf())
@@ -67,7 +67,7 @@ void *smalloc(size_t size) {
67void sfree(void *p) { 67void sfree(void *p) {
68 if (p) { 68 if (p) {
69 ++frees; 69 ++frees;
70 LOGF("frees: %d, total outstanding: %d", frees, allocs - frees); 70 //LOGF("frees: %d, total outstanding: %d", frees, allocs - frees);
71 free(p); 71 free(p);
72 } 72 }
73} 73}
@@ -80,7 +80,7 @@ void *srealloc(void *p, size_t size) {
80 if (p) { 80 if (p) {
81 q = realloc(p, size); 81 q = realloc(p, size);
82 } else { 82 } else {
83 LOGF("allocs: %d", ++allocs); 83 //LOGF("allocs: %d", ++allocs);
84 q = malloc(size); 84 q = malloc(size);
85 } 85 }
86 if (!q) 86 if (!q)
diff --git a/apps/plugins/puzzles/rockbox.c b/apps/plugins/puzzles/rockbox.c
index 44df35027a..f77b538019 100644
--- a/apps/plugins/puzzles/rockbox.c
+++ b/apps/plugins/puzzles/rockbox.c
@@ -44,6 +44,8 @@
44 44
45#include "fixedpoint.h" 45#include "fixedpoint.h"
46 46
47#include "pluginbitmaps/puzzles_cursor.h"
48
47/* how many ticks between timer callbacks */ 49/* how many ticks between timer callbacks */
48#define TIMER_INTERVAL (HZ / 50) 50#define TIMER_INTERVAL (HZ / 50)
49 51
@@ -122,12 +124,14 @@ static int help_times = 0;
122 124
123/* clipping stuff */ 125/* clipping stuff */
124static struct viewport clip_rect; 126static struct viewport clip_rect;
125static bool clipped = false, zoom_enabled = false, view_mode = true; 127static bool clipped = false, zoom_enabled = false, view_mode = true, mouse_mode = false;
128
129static int mouse_x, mouse_y;
126 130
127extern bool audiobuf_available; /* defined in rbmalloc.c */ 131extern bool audiobuf_available; /* defined in rbmalloc.c */
128 132
129static fb_data *zoom_fb; /* dynamically allocated */ 133static fb_data *zoom_fb; /* dynamically allocated */
130static int zoom_w, zoom_h, zoom_clipu, zoom_clipd, zoom_clipl, zoom_clipr; 134static int zoom_x, zoom_y, zoom_w, zoom_h, zoom_clipu, zoom_clipd, zoom_clipl, zoom_clipr;
131static int cur_font = FONT_UI; 135static int cur_font = FONT_UI;
132 136
133static bool need_draw_update = false; 137static bool need_draw_update = false;
@@ -135,7 +139,12 @@ static int ud_l = 0, ud_u = 0, ud_r = LCD_WIDTH, ud_d = LCD_HEIGHT;
135 139
136static char *titlebar = NULL; 140static char *titlebar = NULL;
137 141
138static bool want_redraw = true, accept_input = true; 142/* how to process the input (custom, per-game) */
143static struct {
144 bool want_spacebar, falling_edge, ignore_repeats, rclick_on_hold;
145} input_settings;
146
147static bool accept_input = true;
139 148
140/* last timer call */ 149/* last timer call */
141static long last_tstamp; 150static long last_tstamp;
@@ -145,10 +154,10 @@ static bool load_success;
145 154
146/* debug settings */ 155/* debug settings */
147/* did I mention there's a secret debug menu? */ 156/* did I mention there's a secret debug menu? */
148static struct settings_t { 157static struct {
149 int slowmo_factor; 158 int slowmo_factor;
150 bool timerflash, clipoff, shortcuts, no_aa, polyanim; 159 bool timerflash, clipoff, shortcuts, no_aa, polyanim;
151} settings; 160} debug_settings;
152 161
153/* re-implementations of many rockbox primitives, adapted to draw into 162/* re-implementations of many rockbox primitives, adapted to draw into
154 * a custom framebuffer. */ 163 * a custom framebuffer. */
@@ -469,7 +478,7 @@ static void rb_clip(void *handle, int x, int y, int w, int h)
469{ 478{
470 if(!zoom_enabled) 479 if(!zoom_enabled)
471 { 480 {
472 if(!settings.clipoff) 481 if(!debug_settings.clipoff)
473 { 482 {
474 LOGF("rb_clip(%d %d %d %d)", x, y, w, h); 483 LOGF("rb_clip(%d %d %d %d)", x, y, w, h);
475 clip_rect.x = MAX(0, x); 484 clip_rect.x = MAX(0, x);
@@ -736,7 +745,7 @@ static void rb_draw_line(void *handle, int x1, int y1, int x2, int y2,
736 { 745 {
737 LOGF("rb_draw_line(%d, %d, %d, %d, %d)", x1, y1, x2, y2, color); 746 LOGF("rb_draw_line(%d, %d, %d, %d, %d)", x1, y1, x2, y2, color);
738 rb_color(color); 747 rb_color(color);
739 if(settings.no_aa) 748 if(debug_settings.no_aa)
740 { 749 {
741 offset_coords(&x1, &y1); 750 offset_coords(&x1, &y1);
742 offset_coords(&x2, &y2); 751 offset_coords(&x2, &y2);
@@ -968,7 +977,7 @@ static void rb_draw_poly(void *handle, int *coords, int npoints,
968 x3, y3); 977 x3, y3);
969 978
970#ifdef DEBUG_MENU 979#ifdef DEBUG_MENU
971 if(settings.polyanim) 980 if(debug_settings.polyanim)
972 { 981 {
973 rb->lcd_update(); 982 rb->lcd_update();
974 rb->sleep(HZ/4); 983 rb->sleep(HZ/4);
@@ -1014,7 +1023,7 @@ static void rb_draw_poly(void *handle, int *coords, int npoints,
1014 y1 = coords[2 * (i - 1) + 1]; 1023 y1 = coords[2 * (i - 1) + 1];
1015 x2 = coords[2 * i]; 1024 x2 = coords[2 * i];
1016 y2 = coords[2 * i + 1]; 1025 y2 = coords[2 * i + 1];
1017 if(settings.no_aa) 1026 if(debug_settings.no_aa)
1018 { 1027 {
1019 offset_coords(&x1, &y1); 1028 offset_coords(&x1, &y1);
1020 offset_coords(&x2, &y2); 1029 offset_coords(&x2, &y2);
@@ -1025,7 +1034,7 @@ static void rb_draw_poly(void *handle, int *coords, int npoints,
1025 draw_antialiased_line(rb->lcd_framebuffer, LCD_WIDTH, LCD_HEIGHT, x1, y1, x2, y2); 1034 draw_antialiased_line(rb->lcd_framebuffer, LCD_WIDTH, LCD_HEIGHT, x1, y1, x2, y2);
1026 1035
1027#ifdef DEBUG_MENU 1036#ifdef DEBUG_MENU
1028 if(settings.polyanim) 1037 if(debug_settings.polyanim)
1029 { 1038 {
1030 rb->lcd_update(); 1039 rb->lcd_update();
1031 rb->sleep(HZ/4); 1040 rb->sleep(HZ/4);
@@ -1038,7 +1047,7 @@ static void rb_draw_poly(void *handle, int *coords, int npoints,
1038 y1 = coords[1]; 1047 y1 = coords[1];
1039 x2 = coords[2 * (npoints - 1)]; 1048 x2 = coords[2 * (npoints - 1)];
1040 y2 = coords[2 * (npoints - 1) + 1]; 1049 y2 = coords[2 * (npoints - 1) + 1];
1041 if(settings.no_aa) 1050 if(debug_settings.no_aa)
1042 { 1051 {
1043 offset_coords(&x1, &y1); 1052 offset_coords(&x1, &y1);
1044 offset_coords(&x2, &y2); 1053 offset_coords(&x2, &y2);
@@ -1193,7 +1202,7 @@ static void rb_blitter_save(void *handle, blitter *bl, int x, int y)
1193#if defined(LCD_STRIDEFORMAT) && (LCD_STRIDEFORMAT == VERTICAL_STRIDE) 1202#if defined(LCD_STRIDEFORMAT) && (LCD_STRIDEFORMAT == VERTICAL_STRIDE)
1194#error no vertical stride 1203#error no vertical stride
1195#else 1204#else
1196 if(bl->bmp.data) 1205 if(bl && bl->bmp.data)
1197 { 1206 {
1198 int w = bl->bmp.width, h = bl->bmp.height; 1207 int w = bl->bmp.width, h = bl->bmp.height;
1199 int screen_w = zoom_enabled ? zoom_w : LCD_WIDTH; 1208 int screen_w = zoom_enabled ? zoom_w : LCD_WIDTH;
@@ -1282,6 +1291,8 @@ static void rb_start_draw(void *handle)
1282static void rb_end_draw(void *handle) 1291static void rb_end_draw(void *handle)
1283{ 1292{
1284 (void) handle; 1293 (void) handle;
1294 /* we ignore the backend's redraw requests and just unconditionally update everything */
1295#if 0
1285 if(!zoom_enabled) 1296 if(!zoom_enabled)
1286 { 1297 {
1287 LOGF("rb_end_draw"); 1298 LOGF("rb_end_draw");
@@ -1293,6 +1304,7 @@ static void rb_end_draw(void *handle)
1293 { 1304 {
1294 /* stubbed */ 1305 /* stubbed */
1295 } 1306 }
1307#endif
1296} 1308}
1297 1309
1298static void rb_status_bar(void *handle, const char *text) 1310static void rb_status_bar(void *handle, const char *text)
@@ -1347,6 +1359,54 @@ static void draw_title(bool clear_first)
1347 } 1359 }
1348} 1360}
1349 1361
1362#define MOUSE_W BMPWIDTH_puzzles_cursor
1363#define MOUSE_H BMPHEIGHT_puzzles_cursor
1364
1365static blitter *mouse_bl = NULL;
1366
1367static void clear_mouse(void)
1368{
1369 bool orig_clipped = clipped;
1370 if(!zoom_enabled)
1371 {
1372 if(orig_clipped)
1373 rb_unclip(NULL);
1374 }
1375
1376 if(mouse_bl)
1377 rb_blitter_load(NULL, mouse_bl, BLITTER_FROMSAVED, BLITTER_FROMSAVED);
1378
1379 if(!zoom_enabled)
1380 {
1381 if(orig_clipped)
1382 rb_clip(NULL, clip_rect.x, clip_rect.y, clip_rect.width, clip_rect.height);
1383 }
1384}
1385
1386static void draw_mouse(void)
1387{
1388 bool orig_clipped = clipped;
1389 if(!zoom_enabled)
1390 {
1391 if(orig_clipped)
1392 rb_unclip(NULL);
1393 }
1394
1395 if(!mouse_bl)
1396 mouse_bl = rb_blitter_new(NULL, MOUSE_W, MOUSE_H);
1397
1398 /* save area being covered (will be restored elsewhere) */
1399 rb_blitter_save(NULL, mouse_bl, mouse_x, mouse_y);
1400
1401 rb->lcd_bitmap_transparent(puzzles_cursor, mouse_x, mouse_y, BMPWIDTH_puzzles_cursor, BMPHEIGHT_puzzles_cursor);
1402
1403 if(!zoom_enabled)
1404 {
1405 if(orig_clipped)
1406 rb_clip(NULL, clip_rect.x, clip_rect.y, clip_rect.width, clip_rect.height);
1407 }
1408}
1409
1350static char *rb_text_fallback(void *handle, const char *const *strings, 1410static char *rb_text_fallback(void *handle, const char *const *strings,
1351 int nstrings) 1411 int nstrings)
1352{ 1412{
@@ -1403,7 +1463,7 @@ void get_random_seed(void **randseed, int *randseedsize)
1403static void timer_cb(void) 1463static void timer_cb(void)
1404{ 1464{
1405#if LCD_DEPTH != 24 1465#if LCD_DEPTH != 24
1406 if(settings.timerflash) 1466 if(debug_settings.timerflash)
1407 { 1467 {
1408 static bool what = false; 1468 static bool what = false;
1409 what = !what; 1469 what = !what;
@@ -1416,7 +1476,7 @@ static void timer_cb(void)
1416#endif 1476#endif
1417 1477
1418 LOGF("timer callback"); 1478 LOGF("timer callback");
1419 midend_timer(me, ((float)(*rb->current_tick - last_tstamp) / (float)HZ) / settings.slowmo_factor); 1479 midend_timer(me, ((float)(*rb->current_tick - last_tstamp) / (float)HZ) / debug_settings.slowmo_factor);
1420 last_tstamp = *rb->current_tick; 1480 last_tstamp = *rb->current_tick;
1421} 1481}
1422 1482
@@ -1440,12 +1500,27 @@ void frontend_default_color(frontend *fe, float *out)
1440 1500
1441/** frontend code -- mostly UI stuff **/ 1501/** frontend code -- mostly UI stuff **/
1442 1502
1443/* set do_pausemenu to false to just return -1 on BTN_PAUSE and do 1503static void send_click(int button, bool release)
1444 * nothing else. */ 1504{
1505 int x = (zoom_enabled ? zoom_x : 0) + mouse_x,
1506 y = (zoom_enabled ? zoom_y : 0) + mouse_y;
1507 assert(LEFT_BUTTON + 6 == LEFT_RELEASE);
1508
1509 midend_process_key(me, x, y, button);
1510
1511 if(release)
1512 midend_process_key(me, x, y, button + 6);
1513}
1514
1515/* This function handles most user input. It has specific workarounds
1516 * and fixes for certain games to allow them to work well on
1517 * Rockbox. It will either return a positive value that can be passed
1518 * to the midend, or a negative flag value. Set do_pausemenu to false
1519 * to just return -1 on BTN_PAUSE and do nothing else. */
1445static int process_input(int tmo, bool do_pausemenu) 1520static int process_input(int tmo, bool do_pausemenu)
1446{ 1521{
1447 LOGF("process_input start"); 1522 LOGF("process_input start");
1448 LOGF("------------------"); 1523 LOGF("-------------------");
1449 int state = 0; 1524 int state = 0;
1450 1525
1451#ifdef HAVE_ADJUSTABLE_CPU_FREQ 1526#ifdef HAVE_ADJUSTABLE_CPU_FREQ
@@ -1457,13 +1532,24 @@ static int process_input(int tmo, bool do_pausemenu)
1457 /* weird stuff */ 1532 /* weird stuff */
1458 exit_on_usb(button); 1533 exit_on_usb(button);
1459 1534
1460 /* these games require a second input on long-press */ 1535 /* See if the button is a long-press. */
1461 if(accept_input && (button == (BTN_FIRE | BUTTON_REPEAT)) && 1536 if(accept_input && (button == (BTN_FIRE | BUTTON_REPEAT)))
1462 (strcmp("Mines", midend_which_game(me)->name) != 0 ||
1463 strcmp("Magnets", midend_which_game(me)->name) != 0))
1464 { 1537 {
1538 LOGF("button is long-press, ignoring subsequent input until release");
1539 /* Ignore repeated long presses. */
1465 accept_input = false; 1540 accept_input = false;
1466 return ' '; 1541
1542 if(mouse_mode && input_settings.rclick_on_hold)
1543 {
1544 /* simulate right-click */
1545 LOGF("sending right click");
1546 send_click(RIGHT_BUTTON, true);
1547 return 0;
1548 }
1549
1550 /* These games want a spacebar in this event. */
1551 if(!mouse_mode && input_settings.want_spacebar)
1552 return ' ';
1467 } 1553 }
1468 1554
1469 button = rb->button_status(); 1555 button = rb->button_status();
@@ -1476,7 +1562,6 @@ static int process_input(int tmo, bool do_pausemenu)
1476 { 1562 {
1477 if(do_pausemenu) 1563 if(do_pausemenu)
1478 { 1564 {
1479 want_redraw = false;
1480 /* quick hack to preserve the clipping state */ 1565 /* quick hack to preserve the clipping state */
1481 bool orig_clipped = clipped; 1566 bool orig_clipped = clipped;
1482 if(orig_clipped) 1567 if(orig_clipped)
@@ -1496,13 +1581,99 @@ static int process_input(int tmo, bool do_pausemenu)
1496 return -1; 1581 return -1;
1497 } 1582 }
1498 1583
1499 /* these games require, for one reason or another, that events 1584 /* Mouse movement (if enabled). This goes here since none of the
1585 * following code is needed for mouse mode. */
1586 if(mouse_mode)
1587 {
1588 if(button & BTN_UP)
1589 state = CURSOR_UP;
1590 else if(button & BTN_DOWN)
1591 state = CURSOR_DOWN;
1592 else if(button & BTN_LEFT)
1593 state = CURSOR_LEFT;
1594 else if(button & BTN_RIGHT)
1595 state = CURSOR_RIGHT;
1596
1597 unsigned released = ~button & last_keystate,
1598 pressed = button & ~last_keystate;
1599
1600 last_keystate = button;
1601
1602 /* rclick on hold requires that we fire left-click on a
1603 * release, otherwise it's impossible to distinguish the
1604 * two. */
1605 if(input_settings.rclick_on_hold)
1606 {
1607 if(accept_input && released == BTN_FIRE)
1608 {
1609 LOGF("sending left click");
1610 send_click(LEFT_BUTTON, true); /* right-click is handled earlier */
1611 }
1612 }
1613 else
1614 {
1615 if(pressed & BTN_FIRE)
1616 send_click(LEFT_BUTTON, false);
1617 else if(released & BTN_FIRE)
1618 send_click(LEFT_RELEASE, false);
1619 else if(button & BTN_FIRE)
1620 send_click(LEFT_DRAG, false);
1621 }
1622
1623 static int last_mousedir = 0, held_count = 0, v = 0;
1624
1625 /* acceleration */
1626 if(state && state == last_mousedir)
1627 {
1628 if(++held_count % 5 == 0 && v < 15)
1629 v++;
1630 }
1631 else
1632 {
1633 if(!button)
1634 {
1635 LOGF("all keys released, accepting further input");
1636 accept_input = true;
1637 }
1638 last_mousedir = state;
1639 v = 1;
1640 held_count = 0;
1641 }
1642
1643 /* get the direction vector the cursor is moving in. */
1644 int new_x = mouse_x, new_y = mouse_y;
1645
1646 /* in src/misc.c */
1647 move_cursor(state, &new_x, &new_y, LCD_WIDTH, LCD_HEIGHT, FALSE);
1648
1649 int dx = new_x - mouse_x, dy = new_y - mouse_y;
1650
1651 mouse_x += dx * v;
1652 mouse_y += dy * v;
1653
1654 /* The % operator with negative operands is messy; this is much
1655 * simpler. */
1656 if(mouse_x < 0)
1657 mouse_x = 0;
1658 if(mouse_y < 0)
1659 mouse_y = 0;
1660
1661 if(mouse_x >= LCD_WIDTH)
1662 mouse_x = LCD_WIDTH - 1;
1663 if(mouse_y >= LCD_HEIGHT)
1664 mouse_y = LCD_HEIGHT - 1;
1665
1666 /* no buttons are sent to the midend in mouse mode */
1667 return 0;
1668 }
1669
1670 /* These games require, for one reason or another, that events
1500 * fire upon buttons being released rather than when they are 1671 * fire upon buttons being released rather than when they are
1501 * pressed */ 1672 * pressed. For Inertia, it is because it needs to be able to
1502 if(strcmp("Inertia", midend_which_game(me)->name) == 0 || 1673 * sense multiple simultaneous keypresses (to move diagonally),
1503 strcmp("Mines", midend_which_game(me)->name) == 0 || 1674 * and the others require a long press to map to a secondary
1504 strcmp("Magnets", midend_which_game(me)->name) == 0 || 1675 * "action" key. */
1505 strcmp("Map", midend_which_game(me)->name) == 0) 1676 if(input_settings.falling_edge)
1506 { 1677 {
1507 LOGF("received button 0x%08x", button); 1678 LOGF("received button 0x%08x", button);
1508 1679
@@ -1536,8 +1707,8 @@ static int process_input(int tmo, bool do_pausemenu)
1536 button |= released; 1707 button |= released;
1537 LOGF("accepting event 0x%08x", button); 1708 LOGF("accepting event 0x%08x", button);
1538 } 1709 }
1539 /* default is to ignore repeats except for untangle */ 1710 /* Ignore repeats in all games which are not Untangle. */
1540 else if(strcmp("Untangle", midend_which_game(me)->name) != 0) 1711 else if(input_settings.ignore_repeats)
1541 { 1712 {
1542 /* start accepting input again after a release */ 1713 /* start accepting input again after a release */
1543 if(!button) 1714 if(!button)
@@ -1545,10 +1716,11 @@ static int process_input(int tmo, bool do_pausemenu)
1545 accept_input = true; 1716 accept_input = true;
1546 return 0; 1717 return 0;
1547 } 1718 }
1548 /* ignore repeats */ 1719
1549 /* Untangle gets special treatment */ 1720 /* ignore repeats (in mouse mode, only ignore repeats of BTN_FIRE) */
1550 if(!accept_input) 1721 if(!accept_input)
1551 return 0; 1722 return 0;
1723
1552 accept_input = false; 1724 accept_input = false;
1553 } 1725 }
1554 1726
@@ -1604,7 +1776,7 @@ static int process_input(int tmo, bool do_pausemenu)
1604 break; 1776 break;
1605 } 1777 }
1606 1778
1607 if(settings.shortcuts) 1779 if(debug_settings.shortcuts)
1608 { 1780 {
1609 static bool shortcuts_ok = true; 1781 static bool shortcuts_ok = true;
1610 switch(button) 1782 switch(button)
@@ -1665,9 +1837,9 @@ static void zoom(void)
1665 /* draws go to the zoom framebuffer */ 1837 /* draws go to the zoom framebuffer */
1666 midend_force_redraw(me); 1838 midend_force_redraw(me);
1667 1839
1668 int x = 0, y = 0; 1840 zoom_x = zoom_y = 0;
1669 1841
1670 rb->lcd_bitmap_part(zoom_fb, x, y, STRIDE(SCREEN_MAIN, zoom_w, zoom_h), 1842 rb->lcd_bitmap_part(zoom_fb, zoom_x, zoom_y, STRIDE(SCREEN_MAIN, zoom_w, zoom_h),
1671 0, 0, LCD_WIDTH, LCD_HEIGHT); 1843 0, 0, LCD_WIDTH, LCD_HEIGHT);
1672 1844
1673 draw_title(false); /* false since we don't want to use more screen space than we need. */ 1845 draw_title(false); /* false since we don't want to use more screen space than we need. */
@@ -1693,16 +1865,16 @@ static void zoom(void)
1693 switch(button) 1865 switch(button)
1694 { 1866 {
1695 case BTN_UP: 1867 case BTN_UP:
1696 y -= PAN_Y; /* clamped later */ 1868 zoom_y -= PAN_Y; /* clamped later */
1697 break; 1869 break;
1698 case BTN_DOWN: 1870 case BTN_DOWN:
1699 y += PAN_Y; /* clamped later */ 1871 zoom_y += PAN_Y; /* clamped later */
1700 break; 1872 break;
1701 case BTN_LEFT: 1873 case BTN_LEFT:
1702 x -= PAN_X; /* clamped later */ 1874 zoom_x -= PAN_X; /* clamped later */
1703 break; 1875 break;
1704 case BTN_RIGHT: 1876 case BTN_RIGHT:
1705 x += PAN_X; /* clamped later */ 1877 zoom_x += PAN_X; /* clamped later */
1706 break; 1878 break;
1707 case BTN_PAUSE: 1879 case BTN_PAUSE:
1708 zoom_enabled = false; 1880 zoom_enabled = false;
@@ -1716,15 +1888,15 @@ static void zoom(void)
1716 break; 1888 break;
1717 } 1889 }
1718 1890
1719 if(y < 0) 1891 if(zoom_y < 0)
1720 y = 0; 1892 zoom_y = 0;
1721 if(x < 0) 1893 if(zoom_x < 0)
1722 x = 0; 1894 zoom_x = 0;
1723 1895
1724 if(y + LCD_HEIGHT >= zoom_h) 1896 if(zoom_y + LCD_HEIGHT >= zoom_h)
1725 y = zoom_h - LCD_HEIGHT; 1897 zoom_y = zoom_h - LCD_HEIGHT;
1726 if(x + LCD_WIDTH >= zoom_w) 1898 if(zoom_x + LCD_WIDTH >= zoom_w)
1727 x = zoom_w - LCD_WIDTH; 1899 zoom_x = zoom_w - LCD_WIDTH;
1728 1900
1729 if(timer_on) 1901 if(timer_on)
1730 timer_cb(); 1902 timer_cb();
@@ -1732,7 +1904,7 @@ static void zoom(void)
1732 /* goes to zoom_fb */ 1904 /* goes to zoom_fb */
1733 midend_redraw(me); 1905 midend_redraw(me);
1734 1906
1735 rb->lcd_bitmap_part(zoom_fb, x, y, STRIDE(SCREEN_MAIN, zoom_w, zoom_h), 1907 rb->lcd_bitmap_part(zoom_fb, zoom_x, zoom_y, STRIDE(SCREEN_MAIN, zoom_w, zoom_h),
1736 0, 0, LCD_WIDTH, LCD_HEIGHT); 1908 0, 0, LCD_WIDTH, LCD_HEIGHT);
1737 draw_title(false); 1909 draw_title(false);
1738 rb->lcd_update(); 1910 rb->lcd_update();
@@ -1755,13 +1927,26 @@ static void zoom(void)
1755 if(timer_on) 1927 if(timer_on)
1756 timer_cb(); 1928 timer_cb();
1757 1929
1758 if(want_redraw) 1930 midend_redraw(me);
1759 midend_redraw(me);
1760 1931
1761 rb->lcd_bitmap_part(zoom_fb, x, y, STRIDE(SCREEN_MAIN, zoom_w, zoom_h), 1932 /* blit */
1933 rb->lcd_bitmap_part(zoom_fb, zoom_x, zoom_y, STRIDE(SCREEN_MAIN, zoom_w, zoom_h),
1762 0, 0, LCD_WIDTH, LCD_HEIGHT); 1934 0, 0, LCD_WIDTH, LCD_HEIGHT);
1935
1763 draw_title(false); 1936 draw_title(false);
1937
1938 /* The cursor is always in screenspace coordinates; when
1939 * zoomed, this means the mouse is always restricted to
1940 * the bounds of the physical display, not the virtual
1941 * zoom framebuffer. */
1942 if(mouse_mode)
1943 draw_mouse();
1944
1764 rb->lcd_update(); 1945 rb->lcd_update();
1946
1947 if(mouse_mode)
1948 clear_mouse();
1949
1765 rb->yield(); 1950 rb->yield();
1766 } 1951 }
1767 } 1952 }
@@ -2202,12 +2387,12 @@ static void full_help(const char *name)
2202 2387
2203static void init_default_settings(void) 2388static void init_default_settings(void)
2204{ 2389{
2205 settings.slowmo_factor = 1; 2390 debug_settings.slowmo_factor = 1;
2206 settings.timerflash = false; 2391 debug_settings.timerflash = false;
2207 settings.clipoff = false; 2392 debug_settings.clipoff = false;
2208 settings.shortcuts = false; 2393 debug_settings.shortcuts = false;
2209 settings.no_aa = false; 2394 debug_settings.no_aa = false;
2210 settings.polyanim = false; 2395 debug_settings.polyanim = false;
2211} 2396}
2212 2397
2213#ifdef DEBUG_MENU 2398#ifdef DEBUG_MENU
@@ -2250,6 +2435,11 @@ static void debug_menu(void)
2250 "Toggle antialias", 2435 "Toggle antialias",
2251 "Benchmark antialias", 2436 "Benchmark antialias",
2252 "Toggle show poly steps", 2437 "Toggle show poly steps",
2438 "Toggle mouse mode",
2439 "Toggle spacebar on long click",
2440 "Toggle send keys on release",
2441 "Toggle ignore repeats",
2442 "Toggle right-click on hold vs. dragging",
2253 "Back"); 2443 "Back");
2254 bool quit = false; 2444 bool quit = false;
2255 int sel = 0; 2445 int sel = 0;
@@ -2258,7 +2448,7 @@ static void debug_menu(void)
2258 switch(rb->do_menu(&menu, &sel, NULL, false)) 2448 switch(rb->do_menu(&menu, &sel, NULL, false))
2259 { 2449 {
2260 case 0: 2450 case 0:
2261 rb->set_int("Slowmo factor", "", UNIT_INT, &settings.slowmo_factor, NULL, 1, 1, 15, NULL); 2451 rb->set_int("Slowmo factor", "", UNIT_INT, &debug_settings.slowmo_factor, NULL, 1, 1, 15, NULL);
2262 break; 2452 break;
2263 case 1: 2453 case 1:
2264 { 2454 {
@@ -2271,24 +2461,38 @@ static void debug_menu(void)
2271 break; 2461 break;
2272 } 2462 }
2273 case 2: 2463 case 2:
2274 settings.timerflash = !settings.timerflash; 2464 debug_settings.timerflash = !debug_settings.timerflash;
2275 break; 2465 break;
2276 case 3: 2466 case 3:
2277 settings.clipoff = !settings.clipoff; 2467 debug_settings.clipoff = !debug_settings.clipoff;
2278 break; 2468 break;
2279 case 4: 2469 case 4:
2280 settings.shortcuts = !settings.shortcuts; 2470 debug_settings.shortcuts = !debug_settings.shortcuts;
2281 break; 2471 break;
2282 case 5: 2472 case 5:
2283 settings.no_aa = !settings.no_aa; 2473 debug_settings.no_aa = !debug_settings.no_aa;
2284 break; 2474 break;
2285 case 6: 2475 case 6:
2286 bench_aa(); 2476 bench_aa();
2287 break; 2477 break;
2288 case 7: 2478 case 7:
2289 settings.polyanim = !settings.polyanim; 2479 debug_settings.polyanim = !debug_settings.polyanim;
2290 break; 2480 break;
2291 case 8: 2481 case 8:
2482 mouse_mode = !mouse_mode;
2483 break;
2484 case 9:
2485 input_settings.want_spacebar = !input_settings.want_spacebar;
2486 break;
2487 case 10:
2488 input_settings.falling_edge = !input_settings.falling_edge;
2489 break;
2490 case 11:
2491 input_settings.ignore_repeats = !input_settings.ignore_repeats;
2492 break;
2493 case 12:
2494 input_settings.rclick_on_hold = !input_settings.rclick_on_hold;
2495 break;
2292 default: 2496 default:
2293 quit = true; 2497 quit = true;
2294 break; 2498 break;
@@ -2316,8 +2520,6 @@ static int pausemenu_cb(int action, const struct menu_item_ex *this_item)
2316 if(!midend_which_game(me)->can_solve) 2520 if(!midend_which_game(me)->can_solve)
2317 return ACTION_EXIT_MENUITEM; 2521 return ACTION_EXIT_MENUITEM;
2318 break; 2522 break;
2319 case 7:
2320 break;
2321 case 9: 2523 case 9:
2322 if(audiobuf_available) 2524 if(audiobuf_available)
2323 break; 2525 break;
@@ -2349,10 +2551,18 @@ static int pausemenu_cb(int action, const struct menu_item_ex *this_item)
2349static void clear_and_draw(void) 2551static void clear_and_draw(void)
2350{ 2552{
2351 rb->lcd_clear_display(); 2553 rb->lcd_clear_display();
2352 rb->lcd_update();
2353 2554
2354 midend_force_redraw(me); 2555 midend_force_redraw(me);
2556
2355 draw_title(true); 2557 draw_title(true);
2558
2559 if(mouse_mode)
2560 draw_mouse();
2561
2562 rb->lcd_update();
2563
2564 if(mouse_mode)
2565 clear_mouse();
2356} 2566}
2357 2567
2358static void reset_drawing(void) 2568static void reset_drawing(void)
@@ -2482,8 +2692,8 @@ static int pause_menu(void)
2482 } 2692 }
2483 rb->lcd_set_background(BG_COLOR); 2693 rb->lcd_set_background(BG_COLOR);
2484 rb->lcd_clear_display(); 2694 rb->lcd_clear_display();
2485 rb->lcd_update();
2486 midend_force_redraw(me); 2695 midend_force_redraw(me);
2696 rb->lcd_update();
2487 return 0; 2697 return 0;
2488} 2698}
2489 2699
@@ -2542,6 +2752,72 @@ static void init_colors(void)
2542 sfree(floatcolors); 2752 sfree(floatcolors);
2543} 2753}
2544 2754
2755static bool string_in_list(const char *target, const char **list)
2756{
2757 /* list is terminated with NULL */
2758 const char *i;
2759
2760 while((i = *list++))
2761 {
2762 if(!strcmp(target, i))
2763 return true;
2764 }
2765
2766 return false;
2767}
2768
2769static void tune_input(const char *name)
2770{
2771 /* game-specific stuff */
2772
2773 static const char *want_spacebar[] = {
2774 "Magnets",
2775 "Mines",
2776 "Palisade",
2777 NULL
2778 };
2779
2780 /* these get a spacebar on long click */
2781 input_settings.want_spacebar = string_in_list(name, want_spacebar);
2782
2783 static const char *falling_edge[] = {
2784 "Inertia",
2785 "Magnets",
2786 "Map",
2787 "Mines",
2788 "Palisade",
2789 NULL
2790 };
2791
2792 /* wait until a key is released to send an action */
2793 input_settings.falling_edge = string_in_list(name, falling_edge);
2794
2795 /* in all games but untangle (mouse mode overrides this) */
2796 static const char *ignore_repeats[] = {
2797 "Untangle",
2798 NULL
2799 };
2800
2801 input_settings.ignore_repeats = !string_in_list(name, falling_edge);
2802
2803 /* set to false if you want dragging to be possible */
2804 static const char *rclick_on_hold[] = {
2805 "Map",
2806 "Signpost",
2807 "Untangle",
2808 NULL
2809 };
2810
2811 input_settings.rclick_on_hold = !string_in_list(name, falling_edge);
2812
2813 static const char *mouse_games[] = {
2814 "Loopy",
2815 NULL
2816 };
2817
2818 mouse_mode = string_in_list(name, mouse_games);
2819}
2820
2545static const char *init_for_game(const game *gm, int load_fd, bool draw) 2821static const char *init_for_game(const game *gm, int load_fd, bool draw)
2546{ 2822{
2547 me = midend_new(NULL, gm, &rb_drawing, NULL); 2823 me = midend_new(NULL, gm, &rb_drawing, NULL);
@@ -2555,6 +2831,11 @@ static const char *init_for_game(const game *gm, int load_fd, bool draw)
2555 return ret; 2831 return ret;
2556 } 2832 }
2557 2833
2834 tune_input(gm->name);
2835
2836 mouse_x = LCD_WIDTH / 2;
2837 mouse_y = LCD_HEIGHT / 2;
2838
2558 fix_size(); 2839 fix_size();
2559 2840
2560 init_colors(); 2841 init_colors();
@@ -2845,12 +3126,6 @@ enum plugin_status plugin_start(const void *param)
2845 3126
2846 init_tlsf(); 3127 init_tlsf();
2847 3128
2848 /* sanity check */
2849 if(fabs(sqrt(3)/2 - sin(PI/3)) > .01)
2850 {
2851 return PLUGIN_ERROR;
2852 }
2853
2854 init_default_settings(); 3129 init_default_settings();
2855 3130
2856 init_fonttab(); 3131 init_fonttab();
@@ -2964,12 +3239,6 @@ enum plugin_status plugin_start(const void *param)
2964 game_loop: 3239 game_loop:
2965 while(1) 3240 while(1)
2966 { 3241 {
2967 want_redraw = true;
2968
2969 int theight = get_titleheight();
2970 draw_title(true);
2971 rb->lcd_update_rect(0, LCD_HEIGHT - theight, LCD_WIDTH, theight);
2972
2973 int button = process_input(timer_on ? TIMER_INTERVAL : -1, true); 3242 int button = process_input(timer_on ? TIMER_INTERVAL : -1, true);
2974 3243
2975 if(button < 0) 3244 if(button < 0)
@@ -3008,16 +3277,20 @@ enum plugin_status plugin_start(const void *param)
3008 if(button) 3277 if(button)
3009 midend_process_key(me, 0, 0, button); 3278 midend_process_key(me, 0, 0, button);
3010 3279
3280 if(timer_on)
3281 timer_cb();
3282
3283 midend_redraw(me);
3284
3011 draw_title(true); /* will draw to fb */ 3285 draw_title(true); /* will draw to fb */
3012 3286
3013 if(want_redraw) 3287 if(mouse_mode)
3014 midend_redraw(me); 3288 draw_mouse();
3015 3289
3016 /* push title to screen as well */ 3290 rb->lcd_update();
3017 rb->lcd_update_rect(0, LCD_HEIGHT - theight, LCD_WIDTH, theight);
3018 3291
3019 if(timer_on) 3292 if(mouse_mode)
3020 timer_cb(); 3293 clear_mouse();
3021 3294
3022 rb->yield(); 3295 rb->yield();
3023 } 3296 }
diff --git a/apps/plugins/puzzles/src/LICENCE b/apps/plugins/puzzles/src/LICENCE
index 4235005ea7..ce0418e6cc 100644
--- a/apps/plugins/puzzles/src/LICENCE
+++ b/apps/plugins/puzzles/src/LICENCE
@@ -1,8 +1,9 @@
1This software is copyright (c) 2004-2014 Simon Tatham. 1This software is copyright (c) 2004-2014 Simon Tatham.
2 2
3Portions copyright Richard Boulton, James Harvey, Mike Pinna, Jonas 3Portions copyright Richard Boulton, James Harvey, Mike Pinna, Jonas
4Kölker, Dariusz Olszewski, Michael Schierl, Lambros Lambrou, Bernd 4Kölker, Dariusz Olszewski, Michael Schierl, Lambros Lambrou, Bernd
5Schmidt, Steffen Bauer, Lennard Sprong and Rogier Goossens. 5Schmidt, Steffen Bauer, Lennard Sprong, Rogier Goossens and Michael
6Quevillon.
6 7
7Permission is hereby granted, free of charge, to any person 8Permission is hereby granted, free of charge, to any person
8obtaining a copy of this software and associated documentation files 9obtaining a copy of this software and associated documentation files
diff --git a/apps/plugins/puzzles/src/filling.c b/apps/plugins/puzzles/src/filling.c
index 7e71eb25b4..231de4c079 100644
--- a/apps/plugins/puzzles/src/filling.c
+++ b/apps/plugins/puzzles/src/filling.c
@@ -1,6 +1,6 @@
1/* -*- tab-width: 8; indent-tabs-mode: t -*- 1/* -*- tab-width: 8; indent-tabs-mode: t -*-
2 * filling.c: An implementation of the Nikoli game fillomino. 2 * filling.c: An implementation of the Nikoli game fillomino.
3 * Copyright (C) 2007 Jonas Kölker. See LICENSE for the license. 3 * Copyright (C) 2007 Jonas Kölker. See LICENSE for the license.
4 */ 4 */
5 5
6/* TODO: 6/* TODO:
diff --git a/apps/plugins/puzzles/src/grid.c b/apps/plugins/puzzles/src/grid.c
index 52648e5a92..b5e6bb0937 100644
--- a/apps/plugins/puzzles/src/grid.c
+++ b/apps/plugins/puzzles/src/grid.c
@@ -2060,6 +2060,102 @@ static grid *grid_new_greathexagonal(int width, int height, const char *desc)
2060 return g; 2060 return g;
2061} 2061}
2062 2062
2063#define KAGOME_TILESIZE 18
2064/* Vector for side of triangle - ratio is close to sqrt(3) */
2065#define KAGOME_A 15
2066#define KAGOME_B 26
2067
2068static void grid_size_kagome(int width, int height,
2069 int *tilesize, int *xextent, int *yextent)
2070{
2071 int a = KAGOME_A;
2072 int b = KAGOME_B;
2073
2074 *tilesize = KAGOME_TILESIZE;
2075 *xextent = (4*a) * (width-1) + 6*a;
2076 *yextent = (2*b) * (height-1) + 2*b;
2077}
2078
2079static grid *grid_new_kagome(int width, int height, const char *desc)
2080{
2081 int x, y;
2082 int a = KAGOME_A;
2083 int b = KAGOME_B;
2084
2085 /* Upper bounds - don't have to be exact */
2086 int max_faces = 6 * (width + 1) * (height + 1);
2087 int max_dots = 6 * width * height;
2088
2089 tree234 *points;
2090
2091 grid *g = grid_empty();
2092 g->tilesize = KAGOME_TILESIZE;
2093 g->faces = snewn(max_faces, grid_face);
2094 g->dots = snewn(max_dots, grid_dot);
2095
2096 points = newtree234(grid_point_cmp_fn);
2097
2098 for (y = 0; y < height; y++) {
2099 for (x = 0; x < width; x++) {
2100 grid_dot *d;
2101 /* centre of hexagon */
2102 int px = (4*a) * x;
2103 int py = (2*b) * y;
2104 if (y % 2)
2105 px += 2*a;
2106
2107 /* hexagon */
2108 grid_face_add_new(g, 6);
2109 d = grid_get_dot(g, points, px + a, py - b); grid_face_set_dot(g, d, 0);
2110 d = grid_get_dot(g, points, px + 2*a, py ); grid_face_set_dot(g, d, 1);
2111 d = grid_get_dot(g, points, px + a, py + b); grid_face_set_dot(g, d, 2);
2112 d = grid_get_dot(g, points, px - a, py + b); grid_face_set_dot(g, d, 3);
2113 d = grid_get_dot(g, points, px - 2*a, py ); grid_face_set_dot(g, d, 4);
2114 d = grid_get_dot(g, points, px - a, py - b); grid_face_set_dot(g, d, 5);
2115
2116 /* Triangle above right */
2117 if ((x < width - 1) || (!(y % 2) && y)) {
2118 grid_face_add_new(g, 3);
2119 d = grid_get_dot(g, points, px + 3*a, py - b); grid_face_set_dot(g, d, 0);
2120 d = grid_get_dot(g, points, px + 2*a, py ); grid_face_set_dot(g, d, 1);
2121 d = grid_get_dot(g, points, px + a, py - b); grid_face_set_dot(g, d, 2);
2122 }
2123
2124 /* Triangle below right */
2125 if ((x < width - 1) || (!(y % 2) && (y < height - 1))) {
2126 grid_face_add_new(g, 3);
2127 d = grid_get_dot(g, points, px + 3*a, py + b); grid_face_set_dot(g, d, 0);
2128 d = grid_get_dot(g, points, px + a, py + b); grid_face_set_dot(g, d, 1);
2129 d = grid_get_dot(g, points, px + 2*a, py ); grid_face_set_dot(g, d, 2);
2130 }
2131
2132 /* Left triangles */
2133 if (!x && (y % 2)) {
2134 /* Triangle above left */
2135 grid_face_add_new(g, 3);
2136 d = grid_get_dot(g, points, px - a, py - b); grid_face_set_dot(g, d, 0);
2137 d = grid_get_dot(g, points, px - 2*a, py ); grid_face_set_dot(g, d, 1);
2138 d = grid_get_dot(g, points, px - 3*a, py - b); grid_face_set_dot(g, d, 2);
2139
2140 /* Triangle below left */
2141 if (y < height - 1) {
2142 grid_face_add_new(g, 3);
2143 d = grid_get_dot(g, points, px - a, py + b); grid_face_set_dot(g, d, 0);
2144 d = grid_get_dot(g, points, px - 3*a, py + b); grid_face_set_dot(g, d, 1);
2145 d = grid_get_dot(g, points, px - 2*a, py ); grid_face_set_dot(g, d, 2);
2146 }
2147 }
2148 }
2149 }
2150
2151 freetree234(points);
2152 assert(g->num_faces <= max_faces);
2153 assert(g->num_dots <= max_dots);
2154
2155 grid_make_consistent(g);
2156 return g;
2157}
2158
2063#define OCTAGONAL_TILESIZE 40 2159#define OCTAGONAL_TILESIZE 40
2064/* b/a approx sqrt(2) */ 2160/* b/a approx sqrt(2) */
2065#define OCTAGONAL_A 29 2161#define OCTAGONAL_A 29
diff --git a/apps/plugins/puzzles/src/grid.h b/apps/plugins/puzzles/src/grid.h
index 19079a44b8..26d0b16633 100644
--- a/apps/plugins/puzzles/src/grid.h
+++ b/apps/plugins/puzzles/src/grid.h
@@ -100,6 +100,7 @@ typedef struct grid {
100 A(SNUBSQUARE,snubsquare) \ 100 A(SNUBSQUARE,snubsquare) \
101 A(CAIRO,cairo) \ 101 A(CAIRO,cairo) \
102 A(GREATHEXAGONAL,greathexagonal) \ 102 A(GREATHEXAGONAL,greathexagonal) \
103 A(KAGOME,kagome) \
103 A(OCTAGONAL,octagonal) \ 104 A(OCTAGONAL,octagonal) \
104 A(KITE,kites) \ 105 A(KITE,kites) \
105 A(FLORET,floret) \ 106 A(FLORET,floret) \
diff --git a/apps/plugins/puzzles/src/loopy.c b/apps/plugins/puzzles/src/loopy.c
index 5f1940e945..c14412d6be 100644
--- a/apps/plugins/puzzles/src/loopy.c
+++ b/apps/plugins/puzzles/src/loopy.c
@@ -278,6 +278,7 @@ static void check_caches(const solver_state* sstate);
278 A("Penrose (kite/dart)",PENROSE_P2,3,3) \ 278 A("Penrose (kite/dart)",PENROSE_P2,3,3) \
279 A("Penrose (rhombs)",PENROSE_P3,3,3) \ 279 A("Penrose (rhombs)",PENROSE_P3,3,3) \
280 A("Great-Great-Dodecagonal",GREATGREATDODECAGONAL,2,2) \ 280 A("Great-Great-Dodecagonal",GREATGREATDODECAGONAL,2,2) \
281 A("Kagome",KAGOME,3,3) \
281 /* end of list */ 282 /* end of list */
282 283
283#define GRID_NAME(title,type,amin,omin) title, 284#define GRID_NAME(title,type,amin,omin) title,
@@ -544,6 +545,7 @@ static const game_params loopy_presets_more[] = {
544#ifdef SMALL_SCREEN 545#ifdef SMALL_SCREEN
545 { 7, 7, DIFF_HARD, LOOPY_GRID_HONEYCOMB }, 546 { 7, 7, DIFF_HARD, LOOPY_GRID_HONEYCOMB },
546 { 5, 4, DIFF_HARD, LOOPY_GRID_GREATHEXAGONAL }, 547 { 5, 4, DIFF_HARD, LOOPY_GRID_GREATHEXAGONAL },
548 { 5, 4, DIFF_HARD, LOOPY_GRID_KAGOME },
547 { 5, 5, DIFF_HARD, LOOPY_GRID_OCTAGONAL }, 549 { 5, 5, DIFF_HARD, LOOPY_GRID_OCTAGONAL },
548 { 3, 3, DIFF_HARD, LOOPY_GRID_FLORET }, 550 { 3, 3, DIFF_HARD, LOOPY_GRID_FLORET },
549 { 3, 3, DIFF_HARD, LOOPY_GRID_DODECAGONAL }, 551 { 3, 3, DIFF_HARD, LOOPY_GRID_DODECAGONAL },
@@ -552,6 +554,7 @@ static const game_params loopy_presets_more[] = {
552#else 554#else
553 { 10, 10, DIFF_HARD, LOOPY_GRID_HONEYCOMB }, 555 { 10, 10, DIFF_HARD, LOOPY_GRID_HONEYCOMB },
554 { 5, 4, DIFF_HARD, LOOPY_GRID_GREATHEXAGONAL }, 556 { 5, 4, DIFF_HARD, LOOPY_GRID_GREATHEXAGONAL },
557 { 5, 4, DIFF_HARD, LOOPY_GRID_KAGOME },
555 { 7, 7, DIFF_HARD, LOOPY_GRID_OCTAGONAL }, 558 { 7, 7, DIFF_HARD, LOOPY_GRID_OCTAGONAL },
556 { 5, 5, DIFF_HARD, LOOPY_GRID_FLORET }, 559 { 5, 5, DIFF_HARD, LOOPY_GRID_FLORET },
557 { 5, 4, DIFF_HARD, LOOPY_GRID_DODECAGONAL }, 560 { 5, 4, DIFF_HARD, LOOPY_GRID_DODECAGONAL },
diff --git a/apps/plugins/puzzles/src/palisade.c b/apps/plugins/puzzles/src/palisade.c
index 5227a1d56c..e495bbed2c 100644
--- a/apps/plugins/puzzles/src/palisade.c
+++ b/apps/plugins/puzzles/src/palisade.c
@@ -865,14 +865,16 @@ static char *game_text_format(const game_state *state)
865 865
866struct game_ui { 866struct game_ui {
867 int x, y; 867 int x, y;
868 unsigned int show: 1; 868 unsigned int show: 1;
869 unsigned int fake_ctrl: 1;
870 unsigned int fake_shift: 1;
869}; 871};
870 872
871static game_ui *new_ui(const game_state *state) 873static game_ui *new_ui(const game_state *state)
872{ 874{
873 game_ui *ui = snew(game_ui); 875 game_ui *ui = snew(game_ui);
874 ui->x = ui->y = 0; 876 ui->x = ui->y = 0;
875 ui->show = FALSE; 877 ui->show = ui->fake_ctrl = ui->fake_shift = FALSE;
876 return ui; 878 return ui;
877} 879}
878 880
@@ -916,7 +918,10 @@ static char *interpret_move(const game_state *state, game_ui *ui,
916 const game_drawstate *ds, int x, int y, int button) 918 const game_drawstate *ds, int x, int y, int button)
917{ 919{
918 int w = state->shared->params.w, h = state->shared->params.h; 920 int w = state->shared->params.w, h = state->shared->params.h;
919 int control = button & MOD_CTRL, shift = button & MOD_SHFT; 921 int control = (button & MOD_CTRL) | ui->fake_ctrl, shift = (button & MOD_SHFT) | ui->fake_shift;
922
923 /* reset */
924 ui->fake_ctrl = ui->fake_shift = FALSE;
920 925
921 button &= ~MOD_MASK; 926 button &= ~MOD_MASK;
922 927
@@ -999,6 +1004,16 @@ static char *interpret_move(const game_state *state, game_ui *ui,
999 return UI_UPDATE; 1004 return UI_UPDATE;
1000 } 1005 }
1001 } 1006 }
1007 else if(IS_CURSOR_SELECT(button)) {
1008 /* CURSOR_SELECT or CURSOR_SELECT2 tells us to toggle whether
1009 * the button press should be interpreted as having CTRL or
1010 * shift pressed along with it, respectively. */
1011 ui->show = TRUE;
1012 if(button == CURSOR_SELECT2)
1013 ui->fake_shift = !ui->fake_shift;
1014 else
1015 ui->fake_ctrl = !ui->fake_ctrl;
1016 }
1002 1017
1003 return NULL; 1018 return NULL;
1004} 1019}