puzzles: improve frontend documentation

What it says on the tin.

Change-Id: Idf8f520e0c8c1fab98d292f4ad94e5231578f9ce
This commit is contained in:
Franklin Wei 2020-10-09 21:59:57 -04:00
parent 4b10aacbc2
commit 7c00e9b30b

View file

@ -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:
*
* <https://www.chiark.greenend.org.uk/~sgtatham/puzzles/devel/>.
*
* 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",