diff options
Diffstat (limited to 'apps')
-rw-r--r-- | apps/plugins/puzzles/rockbox.c | 202 |
1 files changed, 195 insertions, 7 deletions
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 @@ | |||
19 | * | 19 | * |
20 | ***************************************************************************/ | 20 | ***************************************************************************/ |
21 | 21 | ||
22 | /* rockbox frontend for puzzles */ | 22 | /* ================================ |
23 | 23 | * Rockbox frontend for sgt-puzzles | |
24 | /* This file contains the majority of the rockbox-specific code for | 24 | * ================================ |
25 | * | ||
26 | * This file contains the majority of the rockbox-specific code for | ||
25 | * the sgt-puzzles port. It implements a set of functions for the | 27 | * the sgt-puzzles port. It implements a set of functions for the |
26 | * backend to call to actually run the games, as well as rockbox UI | 28 | * backend to call to actually run the games, as well as rockbox UI |
27 | * code (menus, input, etc). For a good overview of the rest of the | 29 | * code (menus, input, etc). For a good overview of the rest of the |
28 | * puzzles code, see: | 30 | * puzzles code, see: |
29 | * | 31 | * |
30 | * <https://www.chiark.greenend.org.uk/~sgtatham/puzzles/devel/>. | 32 | * <https://www.chiark.greenend.org.uk/~sgtatham/puzzles/devel/>. |
33 | * | ||
34 | * For documentation of the contents of this file, read on. | ||
35 | * | ||
36 | * --------------------- | ||
37 | * Contents of this file | ||
38 | * --------------------- | ||
39 | * | ||
40 | * By rough order of appearnce in this file: | ||
41 | * | ||
42 | * 1) "Zoom" feature | ||
43 | * | ||
44 | * This file contains re-implementations of drawing primitives | ||
45 | * (lines, fills, text drawing, etc.) adapted to draw into a | ||
46 | * custom `zoom_fb' framebuffer at a magnification of ZOOM_FACTOR | ||
47 | * (compile-time constant, currently 3x). These are used if the | ||
48 | * global `zoom_enabled' switch is true. | ||
49 | * | ||
50 | * The zoom feature uses a modal interface with two modes: viewing | ||
51 | * mode, and interaction mode. | ||
52 | * | ||
53 | * Viewing mode is entered by default and is used to pan around the | ||
54 | * game viewport without interacting with the backend game at | ||
55 | * all. Pressing "select" while in viewing mode activates | ||
56 | * interaction mode. Instead of panning around, interaction mode | ||
57 | * sends keystrokes directly to the backend game. | ||
58 | * | ||
59 | * It used to be that the zoomed viewport would remain entirely | ||
60 | * static while the user was in interaction mode. This made the | ||
61 | * zoom feature rather klunky to use because it required frequent | ||
62 | * mode switching. In commit 5094aaa, this behavior was changed so | ||
63 | * that the frontend can now query the backend for the on-screen | ||
64 | * cursor location and move the viewport accordingly through the | ||
65 | * new midend_get_cursor_location() API (which is not yet merged | ||
66 | * into Simon's tree as of October 2020). | ||
67 | * | ||
68 | * 2) Font management | ||
69 | * | ||
70 | * Rockbox's bitmap fonts don't allow for easy rendering of text | ||
71 | * of arbitrary size. We work around this by shipping a "font | ||
72 | * pack" of pre-rendered fonts in a continuous size range spanning | ||
73 | * 10px to 36px. The code here facilitates dynamic | ||
74 | * loading/unloading of fonts from this font pack. | ||
75 | * | ||
76 | * Font loading efficiency is enhanced by a feature called the | ||
77 | * "font table" which remembers the set of fonts used by each | ||
78 | * individual puzzle to allow for pre-loading during subsequent | ||
79 | * runs. On targets with physical hard drives, this enhances | ||
80 | * startup time by loading the fonts while the disk is already | ||
81 | * spinning (from loading the plugin). | ||
82 | * | ||
83 | * 3) Drawing API | ||
84 | * | ||
85 | * The sgt-puzzles backend wants a set of function pointers to the | ||
86 | * usual drawing primitives. [1] If the `zoom_enabled' switch is | ||
87 | * on, these call upon the "zoomed" drawing routines in (1). | ||
88 | * | ||
89 | * In the normal un-zoomed case, these functions generally rely on | ||
90 | * the usual lcd_* or the pluginlib's xlcd_* API, with some | ||
91 | * exceptions: we implement a fixed-point antialiased line | ||
92 | * algorithm function and a hacky approximation of a polygon fill | ||
93 | * using triangles. [2] | ||
94 | * | ||
95 | * Some things to note: "blitters" are used to save and restore a | ||
96 | * rectangular region of the screen; "clipping" refers to | ||
97 | * temporarily bounding draw operations to a rectangular region | ||
98 | * (this is implemented with rockbox viewports). | ||
99 | * | ||
100 | * 4) Input tuning and game-specific modes | ||
101 | * | ||
102 | * The input schemes of most of the games in the sgt-puzzles | ||
103 | * collection are able to be played fairly well with only | ||
104 | * directional keys and a "select" button. Other games, however, | ||
105 | * need some special accommodations. These are enabled by | ||
106 | * `tune_input()' based on the game name and are listed below: | ||
107 | * | ||
108 | * a) Mouse mode | ||
109 | * | ||
110 | * This mode is designed to accommodate puzzles without a | ||
111 | * keyboard or cursor interface (currently only "Loopy"). We | ||
112 | * remap the cursor keys to move an on-screen cursor rather | ||
113 | * than sending arrow keys to the game. | ||
114 | * | ||
115 | * We also have the option of sending a right-click event when | ||
116 | * the "select" key is held; unfortunately, this conflicts with | ||
117 | * being able to drag the cursor while the virtual "mouse | ||
118 | * button" is depressed. This restriction is enforced by | ||
119 | * `tune_input()'. | ||
120 | * | ||
121 | * b) Numerical chooser spinbox | ||
122 | * | ||
123 | * Games that require keyboard input beyond the arrow keys and | ||
124 | * "select" (currently Filling, Keen, Solo, Towers, Undead, and | ||
125 | * Unequal) are accommodated via a spinbox-like interface. | ||
126 | * | ||
127 | * In these games, the user first uses the directional keys to | ||
128 | * position the cursor, and then presses "select" to activate | ||
129 | * the spinbox mode. Then, the left and right keys are remapped | ||
130 | * to scroll through the list of possible keystrokes accepted | ||
131 | * by the game (retrieved through the midend_request_keys() API | ||
132 | * call). Once the user is happy with their selection, they | ||
133 | * press "select" again to deactivate the chooser, and the | ||
134 | * arrow keys regain their original function. | ||
135 | * | ||
136 | * c) Force centering while zoomed | ||
137 | * | ||
138 | * (This isn't an input adaptation but it doesn't quite fit | ||
139 | * anywhere else.) | ||
140 | * | ||
141 | * In Inertia, we want to keep the ball centered on screen so | ||
142 | * that the user can see everything in all directions. | ||
143 | * | ||
144 | * d) Long-press maps to spacebar; chording; falling edge events | ||
145 | * | ||
146 | * These are grouped because the first features two are | ||
147 | * dependent on the last. This dependency is enforced with an | ||
148 | * `assert()'. | ||
149 | * | ||
150 | * Some games want a spacebar event -- so we send one on a | ||
151 | * long-press of "select". However, we usually send key events | ||
152 | * to the game immediately after the key is depressed, so we | ||
153 | * can't distinguish a hold vs. a short click. | ||
154 | * | ||
155 | * A similar issue arises when we want to allow chording of | ||
156 | * multiple keypresses (this is only used for Untangle, which | ||
157 | * allows diagonal movements by chording two arrow keys) -- if | ||
158 | * we detect that a key has just been pressed, we don't know if | ||
159 | * the user is going to press more keys later on to form a | ||
160 | * chorded input. | ||
161 | * | ||
162 | * In both of these scenarios we disambiguate the possible | ||
163 | * cases by waiting until a key has been released before we | ||
164 | * send the input keystroke(s) to the game. | ||
165 | * | ||
166 | * 5) Game configuration and preset management | ||
167 | * | ||
168 | * The backend games specify a hierarchy of user-adjustable game | ||
169 | * configuration parameters that control aspects of puzzle | ||
170 | * generation, etc. Also supplied are a set of "presets" that | ||
171 | * specify a predetermined set of configuration parameters. | ||
172 | * | ||
173 | * 6) In-game help | ||
174 | * | ||
175 | * The sgt-puzzles manual (src/puzzles.but) contains a chapter | ||
176 | * describing each game. To aid the user in learning each puzzle, | ||
177 | * each game plugin contains a compiled-in version of each | ||
178 | * puzzle's corresponding manual chapter. | ||
179 | * | ||
180 | * The compiled-in help text is automatically generated from the | ||
181 | * puzzles.but file with a system of shell scripts (genhelp.sh), | ||
182 | * which also performs LZ4 compression on the text to conserve | ||
183 | * memory on smaller targets. The output of this script is found | ||
184 | * in the help/ directory. On-target LZ4 decompression is handled | ||
185 | * by lz4tiny.c. | ||
186 | * | ||
187 | * 7) Debug menu | ||
188 | * | ||
189 | * The debug menu is activated by clicking "Quick help" five times | ||
190 | * in a row. Sorry, Android. This is helpful for benchmarking | ||
191 | * optimizations and selecting the activating the input | ||
192 | * accommodations described in (4). | ||
193 | * | ||
194 | * -------------------- | ||
195 | * Building and linking | ||
196 | * -------------------- | ||
197 | * | ||
198 | * Each sgt-*.rock executable is produced by statically compiling the | ||
199 | * backend (i.e. game-specific) source file and help file (see (6) | ||
200 | * above) against a set of common source files. | ||
201 | * | ||
202 | * The backend source files are listed in SOURCES.games; the common | ||
203 | * source files are in SOURCES. | ||
204 | * | ||
205 | * ---------- | ||
206 | * References | ||
207 | * ---------- | ||
208 | * [1]: https://www.chiark.greenend.org.uk/~sgtatham/puzzles/devel/drawing.html#drawing | ||
209 | * [2]: https://en.wikipedia.org/wiki/Xiaolin_Wu%27s_line_algorithm | ||
31 | */ | 210 | */ |
32 | 211 | ||
33 | #include "plugin.h" | 212 | #include "plugin.h" |
@@ -1643,6 +1822,12 @@ static void send_click(int button, bool release) | |||
1643 | midend_process_key(me, x, y, button + 6); | 1822 | midend_process_key(me, x, y, button + 6); |
1644 | } | 1823 | } |
1645 | 1824 | ||
1825 | /* | ||
1826 | * Numerical chooser ("spinbox") | ||
1827 | * | ||
1828 | * Let the user scroll through the options for the keys they can | ||
1829 | * press. | ||
1830 | */ | ||
1646 | static int choose_key(void) | 1831 | static int choose_key(void) |
1647 | { | 1832 | { |
1648 | int options = 0; | 1833 | int options = 0; |
@@ -3120,7 +3305,9 @@ static void tune_input(const char *name) | |||
3120 | NULL | 3305 | NULL |
3121 | }; | 3306 | }; |
3122 | 3307 | ||
3123 | /* wait until a key is released to send an action */ | 3308 | /* wait until a key is released to send an action (useful for |
3309 | * chording in Inertia; must be enabled if the game needs a | ||
3310 | * spacebar) */ | ||
3124 | input_settings.falling_edge = string_in_list(name, falling_edge); | 3311 | input_settings.falling_edge = string_in_list(name, falling_edge); |
3125 | 3312 | ||
3126 | /* For want_spacebar to work, events must be sent on the falling | 3313 | /* For want_spacebar to work, events must be sent on the falling |
@@ -3136,15 +3323,16 @@ static void tune_input(const char *name) | |||
3136 | 3323 | ||
3137 | input_settings.ignore_repeats = !string_in_list(name, allow_repeats); | 3324 | input_settings.ignore_repeats = !string_in_list(name, allow_repeats); |
3138 | 3325 | ||
3139 | /* set to false if you want dragging to be possible */ | 3326 | /* disable right-click on hold if you want dragging in mouse |
3140 | static const char *rclick_on_hold[] = { | 3327 | * mode */ |
3328 | static const char *no_rclick_on_hold[] = { | ||
3141 | "Map", | 3329 | "Map", |
3142 | "Signpost", | 3330 | "Signpost", |
3143 | "Untangle", | 3331 | "Untangle", |
3144 | NULL | 3332 | NULL |
3145 | }; | 3333 | }; |
3146 | 3334 | ||
3147 | input_settings.rclick_on_hold = !string_in_list(name, rclick_on_hold); | 3335 | input_settings.rclick_on_hold = !string_in_list(name, no_rclick_on_hold); |
3148 | 3336 | ||
3149 | static const char *mouse_games[] = { | 3337 | static const char *mouse_games[] = { |
3150 | "Loopy", | 3338 | "Loopy", |