From 7c00e9b30bab95c51f002037efd55a69c916368b Mon Sep 17 00:00:00 2001 From: Franklin Wei Date: Fri, 9 Oct 2020 21:59:57 -0400 Subject: puzzles: improve frontend documentation What it says on the tin. Change-Id: Idf8f520e0c8c1fab98d292f4ad94e5231578f9ce --- apps/plugins/puzzles/rockbox.c | 202 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 195 insertions(+), 7 deletions(-) (limited to 'apps') diff --git a/apps/plugins/puzzles/rockbox.c b/apps/plugins/puzzles/rockbox.c index daeca3ccd7..9fabbf25d2 100644 --- a/apps/plugins/puzzles/rockbox.c +++ b/apps/plugins/puzzles/rockbox.c @@ -19,15 +19,194 @@ * ***************************************************************************/ -/* rockbox frontend for puzzles */ - -/* This file contains the majority of the rockbox-specific code for +/* ================================ + * Rockbox frontend for sgt-puzzles + * ================================ + * + * This file contains the majority of the rockbox-specific code for * the sgt-puzzles port. It implements a set of functions for the * backend to call to actually run the games, as well as rockbox UI * code (menus, input, etc). For a good overview of the rest of the * puzzles code, see: * * . + * + * For documentation of the contents of this file, read on. + * + * --------------------- + * Contents of this file + * --------------------- + * + * By rough order of appearnce in this file: + * + * 1) "Zoom" feature + * + * This file contains re-implementations of drawing primitives + * (lines, fills, text drawing, etc.) adapted to draw into a + * custom `zoom_fb' framebuffer at a magnification of ZOOM_FACTOR + * (compile-time constant, currently 3x). These are used if the + * global `zoom_enabled' switch is true. + * + * The zoom feature uses a modal interface with two modes: viewing + * mode, and interaction mode. + * + * Viewing mode is entered by default and is used to pan around the + * game viewport without interacting with the backend game at + * all. Pressing "select" while in viewing mode activates + * interaction mode. Instead of panning around, interaction mode + * sends keystrokes directly to the backend game. + * + * It used to be that the zoomed viewport would remain entirely + * static while the user was in interaction mode. This made the + * zoom feature rather klunky to use because it required frequent + * mode switching. In commit 5094aaa, this behavior was changed so + * that the frontend can now query the backend for the on-screen + * cursor location and move the viewport accordingly through the + * new midend_get_cursor_location() API (which is not yet merged + * into Simon's tree as of October 2020). + * + * 2) Font management + * + * Rockbox's bitmap fonts don't allow for easy rendering of text + * of arbitrary size. We work around this by shipping a "font + * pack" of pre-rendered fonts in a continuous size range spanning + * 10px to 36px. The code here facilitates dynamic + * loading/unloading of fonts from this font pack. + * + * Font loading efficiency is enhanced by a feature called the + * "font table" which remembers the set of fonts used by each + * individual puzzle to allow for pre-loading during subsequent + * runs. On targets with physical hard drives, this enhances + * startup time by loading the fonts while the disk is already + * spinning (from loading the plugin). + * + * 3) Drawing API + * + * The sgt-puzzles backend wants a set of function pointers to the + * usual drawing primitives. [1] If the `zoom_enabled' switch is + * on, these call upon the "zoomed" drawing routines in (1). + * + * In the normal un-zoomed case, these functions generally rely on + * the usual lcd_* or the pluginlib's xlcd_* API, with some + * exceptions: we implement a fixed-point antialiased line + * algorithm function and a hacky approximation of a polygon fill + * using triangles. [2] + * + * Some things to note: "blitters" are used to save and restore a + * rectangular region of the screen; "clipping" refers to + * temporarily bounding draw operations to a rectangular region + * (this is implemented with rockbox viewports). + * + * 4) Input tuning and game-specific modes + * + * The input schemes of most of the games in the sgt-puzzles + * collection are able to be played fairly well with only + * directional keys and a "select" button. Other games, however, + * need some special accommodations. These are enabled by + * `tune_input()' based on the game name and are listed below: + * + * a) Mouse mode + * + * This mode is designed to accommodate puzzles without a + * keyboard or cursor interface (currently only "Loopy"). We + * remap the cursor keys to move an on-screen cursor rather + * than sending arrow keys to the game. + * + * We also have the option of sending a right-click event when + * the "select" key is held; unfortunately, this conflicts with + * being able to drag the cursor while the virtual "mouse + * button" is depressed. This restriction is enforced by + * `tune_input()'. + * + * b) Numerical chooser spinbox + * + * Games that require keyboard input beyond the arrow keys and + * "select" (currently Filling, Keen, Solo, Towers, Undead, and + * Unequal) are accommodated via a spinbox-like interface. + * + * In these games, the user first uses the directional keys to + * position the cursor, and then presses "select" to activate + * the spinbox mode. Then, the left and right keys are remapped + * to scroll through the list of possible keystrokes accepted + * by the game (retrieved through the midend_request_keys() API + * call). Once the user is happy with their selection, they + * press "select" again to deactivate the chooser, and the + * arrow keys regain their original function. + * + * c) Force centering while zoomed + * + * (This isn't an input adaptation but it doesn't quite fit + * anywhere else.) + * + * In Inertia, we want to keep the ball centered on screen so + * that the user can see everything in all directions. + * + * d) Long-press maps to spacebar; chording; falling edge events + * + * These are grouped because the first features two are + * dependent on the last. This dependency is enforced with an + * `assert()'. + * + * Some games want a spacebar event -- so we send one on a + * long-press of "select". However, we usually send key events + * to the game immediately after the key is depressed, so we + * can't distinguish a hold vs. a short click. + * + * A similar issue arises when we want to allow chording of + * multiple keypresses (this is only used for Untangle, which + * allows diagonal movements by chording two arrow keys) -- if + * we detect that a key has just been pressed, we don't know if + * the user is going to press more keys later on to form a + * chorded input. + * + * In both of these scenarios we disambiguate the possible + * cases by waiting until a key has been released before we + * send the input keystroke(s) to the game. + * + * 5) Game configuration and preset management + * + * The backend games specify a hierarchy of user-adjustable game + * configuration parameters that control aspects of puzzle + * generation, etc. Also supplied are a set of "presets" that + * specify a predetermined set of configuration parameters. + * + * 6) In-game help + * + * The sgt-puzzles manual (src/puzzles.but) contains a chapter + * describing each game. To aid the user in learning each puzzle, + * each game plugin contains a compiled-in version of each + * puzzle's corresponding manual chapter. + * + * The compiled-in help text is automatically generated from the + * puzzles.but file with a system of shell scripts (genhelp.sh), + * which also performs LZ4 compression on the text to conserve + * memory on smaller targets. The output of this script is found + * in the help/ directory. On-target LZ4 decompression is handled + * by lz4tiny.c. + * + * 7) Debug menu + * + * The debug menu is activated by clicking "Quick help" five times + * in a row. Sorry, Android. This is helpful for benchmarking + * optimizations and selecting the activating the input + * accommodations described in (4). + * + * -------------------- + * Building and linking + * -------------------- + * + * Each sgt-*.rock executable is produced by statically compiling the + * backend (i.e. game-specific) source file and help file (see (6) + * above) against a set of common source files. + * + * The backend source files are listed in SOURCES.games; the common + * source files are in SOURCES. + * + * ---------- + * References + * ---------- + * [1]: https://www.chiark.greenend.org.uk/~sgtatham/puzzles/devel/drawing.html#drawing + * [2]: https://en.wikipedia.org/wiki/Xiaolin_Wu%27s_line_algorithm */ #include "plugin.h" @@ -1643,6 +1822,12 @@ static void send_click(int button, bool release) midend_process_key(me, x, y, button + 6); } +/* + * Numerical chooser ("spinbox") + * + * Let the user scroll through the options for the keys they can + * press. + */ static int choose_key(void) { int options = 0; @@ -3120,7 +3305,9 @@ static void tune_input(const char *name) NULL }; - /* wait until a key is released to send an action */ + /* wait until a key is released to send an action (useful for + * chording in Inertia; must be enabled if the game needs a + * spacebar) */ input_settings.falling_edge = string_in_list(name, falling_edge); /* For want_spacebar to work, events must be sent on the falling @@ -3136,15 +3323,16 @@ static void tune_input(const char *name) input_settings.ignore_repeats = !string_in_list(name, allow_repeats); - /* set to false if you want dragging to be possible */ - static const char *rclick_on_hold[] = { + /* disable right-click on hold if you want dragging in mouse + * mode */ + static const char *no_rclick_on_hold[] = { "Map", "Signpost", "Untangle", NULL }; - input_settings.rclick_on_hold = !string_in_list(name, rclick_on_hold); + input_settings.rclick_on_hold = !string_in_list(name, no_rclick_on_hold); static const char *mouse_games[] = { "Loopy", -- cgit v1.2.3