summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFranklin Wei <git@fwei.tk>2017-10-30 21:25:01 -0400
committerFranklin Wei <git@fwei.tk>2017-10-30 21:25:33 -0400
commit65e7617ab645b1680a31fbc6d8f231ca7408f05e (patch)
tree7a0e07c7677f241513f01651d0ddaf130d8bbad0
parent1f3e70aafcdeaf13eeb906fba97825b5d64ccde9 (diff)
downloadrockbox-65e7617ab645b1680a31fbc6d8f231ca7408f05e.tar.gz
rockbox-65e7617ab645b1680a31fbc6d8f231ca7408f05e.zip
puzzles: add an interaction mode to the "Zoom In" feature
This makes it possible to play the game while zoomed in. Read the manual entry if you want to know more. Change-Id: Iff8bab12f92ebd2798047c25d1fde7740aa543ce
-rw-r--r--apps/plugins/puzzles/rockbox.c635
-rw-r--r--manual/plugins/sgt-puzzles.tex32
2 files changed, 390 insertions, 277 deletions
diff --git a/apps/plugins/puzzles/rockbox.c b/apps/plugins/puzzles/rockbox.c
index b79070cba8..529c4ae5b4 100644
--- a/apps/plugins/puzzles/rockbox.c
+++ b/apps/plugins/puzzles/rockbox.c
@@ -82,9 +82,10 @@ static int help_times = 0;
82#endif 82#endif
83 83
84static void fix_size(void); 84static void fix_size(void);
85static int pause_menu(void);
85 86
86static struct viewport clip_rect; 87static struct viewport clip_rect;
87static bool clipped = false, zoom_enabled = false; 88static bool clipped = false, zoom_enabled = false, view_mode = true;
88 89
89extern bool audiobuf_available; 90extern bool audiobuf_available;
90 91
@@ -1269,33 +1270,47 @@ static void rb_status_bar(void *handle, const char *text)
1269 LOGF("game title is %s\n", text); 1270 LOGF("game title is %s\n", text);
1270} 1271}
1271 1272
1273static int get_titleheight(void)
1274{
1275 return rb->font_get(FONT_UI)->height;
1276}
1277
1272static void draw_title(void) 1278static void draw_title(void)
1273{ 1279{
1274 const char *str = NULL; 1280 const char *base;
1275 if(titlebar) 1281 if(titlebar)
1276 str = titlebar; 1282 base = titlebar;
1277 else 1283 else
1278 str = midend_which_game(me)->name; 1284 base = midend_which_game(me)->name;
1285
1286 char str[128];
1287 rb->snprintf(str, sizeof(str), "%s%s", base, zoom_enabled ? (view_mode ? " (viewing)" : " (interaction)") : "");
1279 1288
1280 /* quick hack */ 1289 /* quick hack */
1281 bool orig_clipped = clipped; 1290 bool orig_clipped;
1282 if(orig_clipped) 1291 if(!zoom_enabled)
1283 rb_unclip(NULL); 1292 {
1293 orig_clipped = clipped;
1294 if(orig_clipped)
1295 rb_unclip(NULL);
1296 }
1284 1297
1285 int h; 1298 int w, h;
1286 cur_font = FONT_UI; 1299 cur_font = FONT_UI;
1287 rb->lcd_setfont(cur_font); 1300 rb->lcd_setfont(cur_font);
1288 rb->lcd_getstringsize(str, NULL, &h); 1301 rb->lcd_getstringsize(str, &w, &h);
1289 1302
1290 rb->lcd_set_foreground(BG_COLOR); 1303 rb->lcd_set_foreground(BG_COLOR);
1291 rb->lcd_fillrect(0, LCD_HEIGHT - h, LCD_WIDTH, h); 1304 rb->lcd_fillrect(0, LCD_HEIGHT - h, w, h);
1292 1305
1293 rb->lcd_set_foreground(LCD_BLACK); 1306 rb->lcd_set_foreground(LCD_BLACK);
1294 rb->lcd_putsxy(0, LCD_HEIGHT - h, str); 1307 rb->lcd_putsxy(0, LCD_HEIGHT - h, str);
1295 rb->lcd_update_rect(0, LCD_HEIGHT - h, LCD_WIDTH, h);
1296 1308
1297 if(orig_clipped) 1309 if(!zoom_enabled)
1298 rb_clip(NULL, clip_rect.x, clip_rect.y, clip_rect.width, clip_rect.height); 1310 {
1311 if(orig_clipped)
1312 rb_clip(NULL, clip_rect.x, clip_rect.y, clip_rect.width, clip_rect.height);
1313 }
1299} 1314}
1300 1315
1301static char *rb_text_fallback(void *handle, const char *const *strings, 1316static char *rb_text_fallback(void *handle, const char *const *strings,
@@ -1326,6 +1341,236 @@ const drawing_api rb_drawing = {
1326 NULL, 1341 NULL,
1327}; 1342};
1328 1343
1344static bool want_redraw = true;
1345static bool accept_input = true;
1346
1347/* set do_pausemenu to false to just return -1 on BTN_PAUSE and do
1348 * nothing else. */
1349static int process_input(int tmo, bool do_pausemenu)
1350{
1351 LOGF("process_input start");
1352 LOGF("------------------");
1353 int state = 0;
1354
1355#ifdef HAVE_ADJUSTABLE_CPU_FREQ
1356 rb->cpu_boost(false); /* about to block for button input */
1357#endif
1358
1359 int button = rb->button_get_w_tmo(tmo);
1360
1361 /* weird stuff */
1362 exit_on_usb(button);
1363
1364 /* these games require a second input on long-press */
1365 if(accept_input && (button == (BTN_FIRE | BUTTON_REPEAT)) &&
1366 (strcmp("Mines", midend_which_game(me)->name) != 0 ||
1367 strcmp("Magnets", midend_which_game(me)->name) != 0))
1368 {
1369 accept_input = false;
1370 return ' ';
1371 }
1372
1373 button = rb->button_status();
1374
1375#ifdef HAVE_ADJUSTABLE_CPU_FREQ
1376 rb->cpu_boost(true);
1377#endif
1378
1379 if(button == BTN_PAUSE)
1380 {
1381 if(do_pausemenu)
1382 {
1383 want_redraw = false;
1384 /* quick hack to preserve the clipping state */
1385 bool orig_clipped = clipped;
1386 if(orig_clipped)
1387 rb_unclip(NULL);
1388
1389 int rc = pause_menu();
1390
1391 if(orig_clipped)
1392 rb_clip(NULL, clip_rect.x, clip_rect.y, clip_rect.width, clip_rect.height);
1393
1394 last_keystate = 0;
1395 accept_input = true;
1396
1397 return rc;
1398 }
1399 else
1400 return -1;
1401 }
1402
1403 /* these games require, for one reason or another, that events
1404 * fire upon buttons being released rather than when they are
1405 * pressed */
1406 if(strcmp("Inertia", midend_which_game(me)->name) == 0 ||
1407 strcmp("Mines", midend_which_game(me)->name) == 0 ||
1408 strcmp("Magnets", midend_which_game(me)->name) == 0 ||
1409 strcmp("Map", midend_which_game(me)->name) == 0)
1410 {
1411 LOGF("received button 0x%08x", button);
1412
1413 unsigned released = ~button & last_keystate;
1414
1415 last_keystate = button;
1416
1417 if(!button)
1418 {
1419 if(!accept_input)
1420 {
1421 LOGF("ignoring, all keys released but not accepting input before, can accept input later");
1422 accept_input = true;
1423 return 0;
1424 }
1425 }
1426
1427 if(!released || !accept_input)
1428 {
1429 LOGF("released keys detected: 0x%08x", released);
1430 LOGF("ignoring, either no keys released or not accepting input");
1431 return 0;
1432 }
1433
1434 if(button)
1435 {
1436 LOGF("ignoring input from now until all released");
1437 accept_input = false;
1438 }
1439
1440 button |= released;
1441 LOGF("accepting event 0x%08x", button);
1442 }
1443 /* default is to ignore repeats except for untangle */
1444 else if(strcmp("Untangle", midend_which_game(me)->name) != 0)
1445 {
1446 /* start accepting input again after a release */
1447 if(!button)
1448 {
1449 accept_input = true;
1450 return 0;
1451 }
1452 /* ignore repeats */
1453 /* Untangle gets special treatment */
1454 if(!accept_input)
1455 return 0;
1456 accept_input = false;
1457 }
1458
1459 switch(button)
1460 {
1461 case BTN_UP:
1462 state = CURSOR_UP;
1463 break;
1464 case BTN_DOWN:
1465 state = CURSOR_DOWN;
1466 break;
1467 case BTN_LEFT:
1468 state = CURSOR_LEFT;
1469 break;
1470 case BTN_RIGHT:
1471 state = CURSOR_RIGHT;
1472 break;
1473
1474 /* handle diagonals (mainly for Inertia) */
1475 case BTN_DOWN | BTN_LEFT:
1476#ifdef BTN_DOWN_LEFT
1477 case BTN_DOWN_LEFT:
1478#endif
1479 state = '1' | MOD_NUM_KEYPAD;
1480 break;
1481 case BTN_DOWN | BTN_RIGHT:
1482#ifdef BTN_DOWN_RIGHT
1483 case BTN_DOWN_RIGHT:
1484#endif
1485 state = '3' | MOD_NUM_KEYPAD;
1486 break;
1487 case BTN_UP | BTN_LEFT:
1488#ifdef BTN_UP_LEFT
1489 case BTN_UP_LEFT:
1490#endif
1491 state = '7' | MOD_NUM_KEYPAD;
1492 break;
1493 case BTN_UP | BTN_RIGHT:
1494#ifdef BTN_UP_RIGHT
1495 case BTN_UP_RIGHT:
1496#endif
1497 state = '9' | MOD_NUM_KEYPAD;
1498 break;
1499
1500 case BTN_FIRE:
1501 if(!strcmp("Fifteen", midend_which_game(me)->name))
1502 state = 'h'; /* hint */
1503 else
1504 state = CURSOR_SELECT;
1505 break;
1506
1507 default:
1508 break;
1509 }
1510
1511 if(settings.shortcuts)
1512 {
1513 static bool shortcuts_ok = true;
1514 switch(button)
1515 {
1516 case BTN_LEFT | BTN_FIRE:
1517 if(shortcuts_ok)
1518 midend_process_key(me, 0, 0, 'u');
1519 shortcuts_ok = false;
1520 break;
1521 case BTN_RIGHT | BTN_FIRE:
1522 if(shortcuts_ok)
1523 midend_process_key(me, 0, 0, 'r');
1524 shortcuts_ok = false;
1525 break;
1526 case 0:
1527 shortcuts_ok = true;
1528 break;
1529 default:
1530 break;
1531 }
1532 }
1533
1534 LOGF("process_input done");
1535 LOGF("------------------");
1536 return state;
1537}
1538
1539static long last_tstamp;
1540
1541static void timer_cb(void)
1542{
1543#if LCD_DEPTH != 24
1544 if(settings.timerflash)
1545 {
1546 static bool what = false;
1547 what = !what;
1548 if(what)
1549 rb->lcd_framebuffer[0] = LCD_BLACK;
1550 else
1551 rb->lcd_framebuffer[0] = LCD_WHITE;
1552 rb->lcd_update();
1553 }
1554#endif
1555
1556 LOGF("timer callback");
1557 midend_timer(me, ((float)(*rb->current_tick - last_tstamp) / (float)HZ) / settings.slowmo_factor);
1558 last_tstamp = *rb->current_tick;
1559}
1560
1561static volatile bool timer_on = false;
1562
1563void activate_timer(frontend *fe)
1564{
1565 last_tstamp = *rb->current_tick;
1566 timer_on = true;
1567}
1568
1569void deactivate_timer(frontend *fe)
1570{
1571 timer_on = false;
1572}
1573
1329/* render to a virtual framebuffer and let the user pan (but not make any moves) */ 1574/* render to a virtual framebuffer and let the user pan (but not make any moves) */
1330static void zoom(void) 1575static void zoom(void)
1331{ 1576{
@@ -1356,54 +1601,106 @@ static void zoom(void)
1356 1601
1357 zoom_enabled = true; 1602 zoom_enabled = true;
1358 1603
1359 /* draws go to the enlarged framebuffer */ 1604 /* draws go to the zoom framebuffer */
1360 midend_force_redraw(me); 1605 midend_force_redraw(me);
1361 1606
1362 int x = 0, y = 0; 1607 int x = 0, y = 0;
1363 1608
1364 rb->lcd_bitmap_part(zoom_fb, x, y, STRIDE(SCREEN_MAIN, zoom_w, zoom_h), 1609 rb->lcd_bitmap_part(zoom_fb, x, y, STRIDE(SCREEN_MAIN, zoom_w, zoom_h),
1365 0, 0, LCD_WIDTH, LCD_HEIGHT); 1610 0, 0, LCD_WIDTH, LCD_HEIGHT);
1611 draw_title();
1366 rb->lcd_update(); 1612 rb->lcd_update();
1367 1613
1614 /* Here's how this works: pressing select (or the target's
1615 * equivalent, it's whatever BTN_FIRE is) while in viewing mode
1616 * will toggle the mode to interaction mode. In interaction mode,
1617 * the buttons will behave as normal and be sent to the puzzle,
1618 * except for the pause/quit (BTN_PAUSE) button, which will return
1619 * to view mode. Finally, when in view mode, pause/quit will
1620 * return to the pause menu. */
1621
1622 view_mode = true;
1623
1368 /* pan around the image */ 1624 /* pan around the image */
1369 while(1) 1625 while(1)
1370 { 1626 {
1371 int button = rb->button_get(true); 1627 if(view_mode)
1372 switch(button)
1373 { 1628 {
1374 case BTN_UP: 1629 int button = rb->button_get_w_tmo(timer_on ? TIMER_INTERVAL : -1);
1375 y -= PAN_Y; /* clamped later */ 1630 switch(button)
1376 break; 1631 {
1377 case BTN_DOWN: 1632 case BTN_UP:
1378 y += PAN_Y; /* clamped later */ 1633 y -= PAN_Y; /* clamped later */
1379 break; 1634 break;
1380 case BTN_LEFT: 1635 case BTN_DOWN:
1381 x -= PAN_X; /* clamped later */ 1636 y += PAN_Y; /* clamped later */
1382 break; 1637 break;
1383 case BTN_RIGHT: 1638 case BTN_LEFT:
1384 x += PAN_X; /* clamped later */ 1639 x -= PAN_X; /* clamped later */
1385 break; 1640 break;
1386 case BTN_PAUSE: 1641 case BTN_RIGHT:
1387 zoom_enabled = false; 1642 x += PAN_X; /* clamped later */
1388 sfree(zoom_fb); 1643 break;
1389 fix_size(); 1644 case BTN_PAUSE:
1390 return; 1645 zoom_enabled = false;
1391 default: 1646 sfree(zoom_fb);
1392 break; 1647 fix_size();
1648 return;
1649 case BTN_FIRE:
1650 view_mode = false;
1651 continue;
1652 default:
1653 break;
1654 }
1655
1656 if(y < 0)
1657 y = 0;
1658 if(x < 0)
1659 x = 0;
1660
1661 if(y + LCD_HEIGHT >= zoom_h)
1662 y = zoom_h - LCD_HEIGHT;
1663 if(x + LCD_WIDTH >= zoom_w)
1664 x = zoom_w - LCD_WIDTH;
1665
1666 if(timer_on)
1667 timer_cb();
1668
1669 /* goes to zoom_fb */
1670 midend_redraw(me);
1671
1672 rb->lcd_bitmap_part(zoom_fb, x, y, STRIDE(SCREEN_MAIN, zoom_w, zoom_h),
1673 0, 0, LCD_WIDTH, LCD_HEIGHT);
1674 draw_title();
1675 rb->lcd_update();
1676 rb->yield();
1393 } 1677 }
1678 else
1679 {
1680 /* basically a copy-pasta'd main loop */
1681 int button = process_input(timer_on ? TIMER_INTERVAL : -1, false);
1394 1682
1395 if(y < 0) 1683 if(button < 0)
1396 y = 0; 1684 {
1397 if(x < 0) 1685 view_mode = true;
1398 x = 0; 1686 continue;
1399 if(y + LCD_HEIGHT >= zoom_h) 1687 }
1400 y = zoom_h - LCD_HEIGHT;
1401 if(x + LCD_WIDTH >= zoom_w)
1402 x = zoom_w - LCD_WIDTH;
1403 1688
1404 rb->lcd_bitmap_part(zoom_fb, x, y, STRIDE(SCREEN_MAIN, zoom_w, zoom_h), 1689 if(button)
1405 0, 0, LCD_WIDTH, LCD_HEIGHT); 1690 midend_process_key(me, 0, 0, button);
1406 rb->lcd_update(); 1691
1692 if(timer_on)
1693 timer_cb();
1694
1695 if(want_redraw)
1696 midend_redraw(me);
1697
1698 rb->lcd_bitmap_part(zoom_fb, x, y, STRIDE(SCREEN_MAIN, zoom_w, zoom_h),
1699 0, 0, LCD_WIDTH, LCD_HEIGHT);
1700 draw_title();
1701 rb->lcd_update();
1702 rb->yield();
1703 }
1407 } 1704 }
1408} 1705}
1409 1706
@@ -2163,229 +2460,6 @@ static int pause_menu(void)
2163 return 0; 2460 return 0;
2164} 2461}
2165 2462
2166static bool want_redraw = true;
2167static bool accept_input = true;
2168
2169static int process_input(int tmo)
2170{
2171 LOGF("process_input start");
2172 LOGF("------------------");
2173 int state = 0;
2174
2175#ifdef HAVE_ADJUSTABLE_CPU_FREQ
2176 rb->cpu_boost(false); /* about to block for button input */
2177#endif
2178
2179 int button = rb->button_get_w_tmo(tmo);
2180
2181 /* weird stuff */
2182 exit_on_usb(button);
2183
2184 /* these games require a second input on long-press */
2185 if(accept_input && (button == (BTN_FIRE | BUTTON_REPEAT)) &&
2186 (strcmp("Mines", midend_which_game(me)->name) != 0 ||
2187 strcmp("Magnets", midend_which_game(me)->name) != 0))
2188 {
2189 accept_input = false;
2190 return ' ';
2191 }
2192
2193 button = rb->button_status();
2194
2195#ifdef HAVE_ADJUSTABLE_CPU_FREQ
2196 rb->cpu_boost(true);
2197#endif
2198
2199 if(button == BTN_PAUSE)
2200 {
2201 want_redraw = false;
2202 /* quick hack to preserve the clipping state */
2203 bool orig_clipped = clipped;
2204 if(orig_clipped)
2205 rb_unclip(NULL);
2206
2207 int rc = pause_menu();
2208
2209 if(orig_clipped)
2210 rb_clip(NULL, clip_rect.x, clip_rect.y, clip_rect.width, clip_rect.height);
2211
2212 last_keystate = 0;
2213 accept_input = true;
2214
2215 return rc;
2216 }
2217
2218 /* these games require, for one reason or another, that events
2219 * fire upon buttons being released rather than when they are
2220 * pressed */
2221 if(strcmp("Inertia", midend_which_game(me)->name) == 0 ||
2222 strcmp("Mines", midend_which_game(me)->name) == 0 ||
2223 strcmp("Magnets", midend_which_game(me)->name) == 0 ||
2224 strcmp("Map", midend_which_game(me)->name) == 0)
2225 {
2226 LOGF("received button 0x%08x", button);
2227
2228 unsigned released = ~button & last_keystate;
2229
2230 last_keystate = button;
2231
2232 if(!button)
2233 {
2234 if(!accept_input)
2235 {
2236 LOGF("ignoring, all keys released but not accepting input before, can accept input later");
2237 accept_input = true;
2238 return 0;
2239 }
2240 }
2241
2242 if(!released || !accept_input)
2243 {
2244 LOGF("released keys detected: 0x%08x", released);
2245 LOGF("ignoring, either no keys released or not accepting input");
2246 return 0;
2247 }
2248
2249 if(button)
2250 {
2251 LOGF("ignoring input from now until all released");
2252 accept_input = false;
2253 }
2254
2255 button |= released;
2256 LOGF("accepting event 0x%08x", button);
2257 }
2258 /* default is to ignore repeats except for untangle */
2259 else if(strcmp("Untangle", midend_which_game(me)->name) != 0)
2260 {
2261 /* start accepting input again after a release */
2262 if(!button)
2263 {
2264 accept_input = true;
2265 return 0;
2266 }
2267 /* ignore repeats */
2268 /* Untangle gets special treatment */
2269 if(!accept_input)
2270 return 0;
2271 accept_input = false;
2272 }
2273
2274 switch(button)
2275 {
2276 case BTN_UP:
2277 state = CURSOR_UP;
2278 break;
2279 case BTN_DOWN:
2280 state = CURSOR_DOWN;
2281 break;
2282 case BTN_LEFT:
2283 state = CURSOR_LEFT;
2284 break;
2285 case BTN_RIGHT:
2286 state = CURSOR_RIGHT;
2287 break;
2288
2289 /* handle diagonals (mainly for Inertia) */
2290 case BTN_DOWN | BTN_LEFT:
2291#ifdef BTN_DOWN_LEFT
2292 case BTN_DOWN_LEFT:
2293#endif
2294 state = '1' | MOD_NUM_KEYPAD;
2295 break;
2296 case BTN_DOWN | BTN_RIGHT:
2297#ifdef BTN_DOWN_RIGHT
2298 case BTN_DOWN_RIGHT:
2299#endif
2300 state = '3' | MOD_NUM_KEYPAD;
2301 break;
2302 case BTN_UP | BTN_LEFT:
2303#ifdef BTN_UP_LEFT
2304 case BTN_UP_LEFT:
2305#endif
2306 state = '7' | MOD_NUM_KEYPAD;
2307 break;
2308 case BTN_UP | BTN_RIGHT:
2309#ifdef BTN_UP_RIGHT
2310 case BTN_UP_RIGHT:
2311#endif
2312 state = '9' | MOD_NUM_KEYPAD;
2313 break;
2314
2315 case BTN_FIRE:
2316 if(!strcmp("Fifteen", midend_which_game(me)->name))
2317 state = 'h'; /* hint */
2318 else
2319 state = CURSOR_SELECT;
2320 break;
2321
2322 default:
2323 break;
2324 }
2325
2326 if(settings.shortcuts)
2327 {
2328 static bool shortcuts_ok = true;
2329 switch(button)
2330 {
2331 case BTN_LEFT | BTN_FIRE:
2332 if(shortcuts_ok)
2333 midend_process_key(me, 0, 0, 'u');
2334 shortcuts_ok = false;
2335 break;
2336 case BTN_RIGHT | BTN_FIRE:
2337 if(shortcuts_ok)
2338 midend_process_key(me, 0, 0, 'r');
2339 shortcuts_ok = false;
2340 break;
2341 case 0:
2342 shortcuts_ok = true;
2343 break;
2344 default:
2345 break;
2346 }
2347 }
2348
2349 LOGF("process_input done");
2350 LOGF("------------------");
2351 return state;
2352}
2353
2354static long last_tstamp;
2355
2356static void timer_cb(void)
2357{
2358#if LCD_DEPTH != 24
2359 if(settings.timerflash)
2360 {
2361 static bool what = false;
2362 what = !what;
2363 if(what)
2364 rb->lcd_framebuffer[0] = LCD_BLACK;
2365 else
2366 rb->lcd_framebuffer[0] = LCD_WHITE;
2367 rb->lcd_update();
2368 }
2369#endif
2370
2371 LOGF("timer callback");
2372 midend_timer(me, ((float)(*rb->current_tick - last_tstamp) / (float)HZ) / settings.slowmo_factor);
2373 last_tstamp = *rb->current_tick;
2374}
2375
2376static volatile bool timer_on = false;
2377
2378void activate_timer(frontend *fe)
2379{
2380 last_tstamp = *rb->current_tick;
2381 timer_on = true;
2382}
2383
2384void deactivate_timer(frontend *fe)
2385{
2386 timer_on = false;
2387}
2388
2389/* points to pluginbuf */ 2463/* points to pluginbuf */
2390char *giant_buffer = NULL; 2464char *giant_buffer = NULL;
2391static size_t giant_buffer_len = 0; /* set on start */ 2465static size_t giant_buffer_len = 0; /* set on start */
@@ -2795,6 +2869,7 @@ enum plugin_status plugin_start(const void *param)
2795 2869
2796 bool quit = false; 2870 bool quit = false;
2797 int sel = 0; 2871 int sel = 0;
2872
2798 while(!quit) 2873 while(!quit)
2799 { 2874 {
2800 switch(rb->do_menu(&menu, &sel, NULL, false)) 2875 switch(rb->do_menu(&menu, &sel, NULL, false))
@@ -2866,9 +2941,11 @@ enum plugin_status plugin_start(const void *param)
2866 { 2941 {
2867 want_redraw = true; 2942 want_redraw = true;
2868 2943
2944 int theight = get_titleheight();
2869 draw_title(); 2945 draw_title();
2946 rb->lcd_update_rect(0, LCD_HEIGHT - theight, LCD_WIDTH, theight);
2870 2947
2871 int button = process_input(timer_on ? TIMER_INTERVAL : -1); 2948 int button = process_input(timer_on ? TIMER_INTERVAL : -1, true);
2872 2949
2873 if(button < 0) 2950 if(button < 0)
2874 { 2951 {
@@ -2881,35 +2958,39 @@ enum plugin_status plugin_start(const void *param)
2881 titlebar = NULL; 2958 titlebar = NULL;
2882 } 2959 }
2883 2960
2884 if(button == -1) 2961 switch(button)
2885 { 2962 {
2963 case -1:
2886 /* new game */ 2964 /* new game */
2887 midend_free(me); 2965 midend_free(me);
2888 break; 2966 break;
2889 } 2967 case -2:
2890 else if(button == -2)
2891 {
2892 /* quit without saving */ 2968 /* quit without saving */
2893 midend_free(me); 2969 midend_free(me);
2894 sfree(colors); 2970 sfree(colors);
2895 exit(PLUGIN_OK); 2971 exit(PLUGIN_OK);
2896 } 2972 case -3:
2897 else if(button == -3)
2898 {
2899 /* save and quit */ 2973 /* save and quit */
2900 save_game(); 2974 save_game();
2901 midend_free(me); 2975 midend_free(me);
2902 sfree(colors); 2976 sfree(colors);
2903 exit(PLUGIN_OK); 2977 exit(PLUGIN_OK);
2978 default:
2979 break;
2904 } 2980 }
2905 } 2981 }
2906 2982
2907 if(button) 2983 if(button)
2908 midend_process_key(me, 0, 0, button); 2984 midend_process_key(me, 0, 0, button);
2909 2985
2986 draw_title(); /* will draw to fb */
2987
2910 if(want_redraw) 2988 if(want_redraw)
2911 midend_redraw(me); 2989 midend_redraw(me);
2912 2990
2991 /* push title to screen as well */
2992 rb->lcd_update_rect(0, LCD_HEIGHT - theight, LCD_WIDTH, theight);
2993
2913 if(timer_on) 2994 if(timer_on)
2914 timer_cb(); 2995 timer_cb();
2915 2996
diff --git a/manual/plugins/sgt-puzzles.tex b/manual/plugins/sgt-puzzles.tex
index 5001a39aaf..0de99a1238 100644
--- a/manual/plugins/sgt-puzzles.tex
+++ b/manual/plugins/sgt-puzzles.tex
@@ -6,6 +6,10 @@ The games that begin with the ``sgt-'' prefix are ports of certain
6puzzles from Simon Tatham's Portable Puzzle Collection, an open source 6puzzles from Simon Tatham's Portable Puzzle Collection, an open source
7collection of single-player puzzle games. 7collection of single-player puzzle games.
8 8
9\note{Certain puzzles may crash when run with demanding
10 configurations. To prevent this, avoid setting extreme configuration
11 values.}
12
9\subsubsection{Puzzle Documentation} 13\subsubsection{Puzzle Documentation}
10For documentation on the games included, please see the ``Extensive 14For documentation on the games included, please see the ``Extensive
11Help'' menu option from inside the plugin to read puzzle-specific 15Help'' menu option from inside the plugin to read puzzle-specific
@@ -27,3 +31,31 @@ whenever needed.
27\note{On hard disk-based devices, this may cause a slight delay as the 31\note{On hard disk-based devices, this may cause a slight delay as the
28 disk spins up to load the fonts when a puzzle is first started, and 32 disk spins up to load the fonts when a puzzle is first started, and
29 after using the ``Extensive Help'' feature.} 33 after using the ``Extensive Help'' feature.}
34
35\subsubsection{``Zoom In'' Feature}
36The ``Zoom In'' feature is available as an option from the pause
37menu. It has two modes: viewing mode, and interaction mode. The
38current mode is indicated in the title bar at the bottom of the
39screen. This feature is most useful with low-resolution devices and
40large puzzles.
41
42Viewing mode is entered when the ``Zoom In'' option is selected, or
43when {\PluginCancel} is pressed in interaction mode. It allows you to
44pan around an enlarged version of the game. The directional keys pan
45the image by a small amount in their respective directions, and
46{\PluginSelect} should toggle interaction mode. To return to the pause
47menu from viewing mode, press {\PluginCancel}.
48
49In interaction mode, activated from viewer mode by pressing
50{\PluginSelect}, your device's buttons all function as they do in the
51normal gameplay mode, with the exception of {\PluginCancel}, which
52returns the game to viewing mode, whereas in the normal gameplay mode
53it would return directly to the pause menu. To return to the pause
54menu from interaction mode, press {\PluginCancel} twice.
55
56\note{Using certain features such as the ``Zoom In'' option may stop
57 audio playback. This is normal, as the game requires additional
58 memory from the system, which will automatically stop playback. The
59 ``Playback Control'' menu will be hidden whenever this
60 happens. Exiting the game will allow the resumption of audio
61 playback.}