summaryrefslogtreecommitdiff
path: root/apps/plugins/puzzles/src
diff options
context:
space:
mode:
authorFranklin Wei <franklin@rockbox.org>2024-08-11 23:34:57 -0400
committerFranklin Wei <franklin@rockbox.org>2024-08-16 16:31:28 -0400
commitea0e3704a8aacf87f20ba87e3b33bc4b3966c731 (patch)
tree8db0a329800cffd661f6a80767ce2f956eb57ab8 /apps/plugins/puzzles/src
parent903e8c5b32285e50907e6525388162bd459cbef8 (diff)
downloadrockbox-ea0e3704a8aacf87f20ba87e3b33bc4b3966c731.tar.gz
rockbox-ea0e3704a8aacf87f20ba87e3b33bc4b3966c731.zip
puzzles: resync with upstream 262f709.
This is the first resync with a fully unmodified upstream repo. This includes a new scanline polygon renderer in the upstream puzzles distribution. This allows us to get rid of the monstrosity of a polygon renderer we had been shipping in rockbox.c. Change-Id: I23628c74bb5ff7a9e7932bf16d68a1c867c49969
Diffstat (limited to 'apps/plugins/puzzles/src')
-rw-r--r--apps/plugins/puzzles/src/CMakeLists.txt10
-rw-r--r--apps/plugins/puzzles/src/draw-poly.c302
-rw-r--r--apps/plugins/puzzles/src/drawing.c196
-rw-r--r--apps/plugins/puzzles/src/inertia.c21
-rw-r--r--apps/plugins/puzzles/src/mines.c12
-rw-r--r--apps/plugins/puzzles/src/misc.c11
-rw-r--r--apps/plugins/puzzles/src/puzzles.but20
-rw-r--r--apps/plugins/puzzles/src/puzzles.h88
-rw-r--r--apps/plugins/puzzles/src/signpost.c5
-rw-r--r--apps/plugins/puzzles/src/twiddle.c14
-rw-r--r--apps/plugins/puzzles/src/unequal.c24
11 files changed, 536 insertions, 167 deletions
diff --git a/apps/plugins/puzzles/src/CMakeLists.txt b/apps/plugins/puzzles/src/CMakeLists.txt
index 4153efc246..ce3ce3d3df 100644
--- a/apps/plugins/puzzles/src/CMakeLists.txt
+++ b/apps/plugins/puzzles/src/CMakeLists.txt
@@ -6,13 +6,17 @@ project(puzzles
6include(cmake/setup.cmake) 6include(cmake/setup.cmake)
7 7
8add_library(core_obj OBJECT 8add_library(core_obj OBJECT
9 combi.c divvy.c drawing.c dsf.c findloop.c grid.c latin.c 9 combi.c divvy.c draw-poly.c drawing.c dsf.c findloop.c grid.c
10 laydomino.c loopgen.c malloc.c matching.c midend.c misc.c penrose.c 10 latin.c laydomino.c loopgen.c malloc.c matching.c midend.c misc.c
11 penrose-legacy.c ps.c random.c sort.c tdq.c tree234.c version.c 11 penrose.c penrose-legacy.c ps.c random.c sort.c tdq.c tree234.c
12 version.c
12 ${platform_common_sources}) 13 ${platform_common_sources})
13add_library(core $<TARGET_OBJECTS:core_obj>) 14add_library(core $<TARGET_OBJECTS:core_obj>)
14add_library(common $<TARGET_OBJECTS:core_obj> hat.c spectre.c) 15add_library(common $<TARGET_OBJECTS:core_obj> hat.c spectre.c)
15 16
17cliprogram(polygon-test draw-poly.c
18 SDL2_LIB COMPILE_DEFINITIONS STANDALONE_POLYGON)
19
16include_directories(${CMAKE_CURRENT_SOURCE_DIR}) 20include_directories(${CMAKE_CURRENT_SOURCE_DIR})
17 21
18puzzle(blackbox 22puzzle(blackbox
diff --git a/apps/plugins/puzzles/src/draw-poly.c b/apps/plugins/puzzles/src/draw-poly.c
new file mode 100644
index 0000000000..d4c9975d97
--- /dev/null
+++ b/apps/plugins/puzzles/src/draw-poly.c
@@ -0,0 +1,302 @@
1/*
2 * draw-poly.c: Fallback polygon drawing function.
3 */
4
5#include <assert.h>
6
7#include "puzzles.h"
8
9struct edge {
10 int x1, y1;
11 int x2, y2;
12 bool active;
13
14 /* whether y1 is a closed endpoint (i.e. this edge should be
15 * active when y == y1) */
16 bool closed_y1;
17
18 /* (x2 - x1) / (y2 - y1) as 16.16 signed fixed point; precomputed
19 * for speed */
20 long inverse_slope;
21};
22
23#define FRACBITS 16
24#define ONEHALF (1 << (FRACBITS-1))
25
26void draw_polygon_fallback(drawing *dr, const int *coords, int npoints,
27 int fillcolour, int outlinecolour)
28{
29 struct edge *edges;
30 int min_y = INT_MAX, max_y = INT_MIN, i, y;
31 int n_edges = 0;
32 int *intersections;
33
34 if(npoints < 3)
35 return;
36
37 if(fillcolour < 0)
38 goto draw_outline;
39
40 /* This uses a basic scanline rasterization algorithm for polygon
41 * filling. First, an "edge table" is constructed for each pair of
42 * neighboring points. Horizontal edges are excluded. Then, the
43 * algorithm iterates a horizontal "scan line" over the vertical
44 * (Y) extent of the polygon. At each Y level, it maintains a set
45 * of "active" edges, which are those which intersect the scan
46 * line at the current Y level. The X coordinates where the scan
47 * line intersects each active edge are then computed via
48 * fixed-point arithmetic and stored. Finally, horizontal lines
49 * are drawn between each successive pair of intersection points,
50 * in the order of ascending X coordinate. This has the effect of
51 * "even-odd" filling when the polygon is self-intersecting.
52 *
53 * I (vaguely) based this implementation off the slides below:
54 *
55 * https://www.khoury.northeastern.edu/home/fell/CS4300/Lectures/CS4300F2012-9-ScanLineFill.pdf
56 *
57 * I'm fairly confident that this current implementation is
58 * correct (i.e. draws the right polygon, free from artifacts),
59 * but it isn't quite as efficient as it could be. Namely, it
60 * currently maintains the active edge set by setting the `active`
61 * flag in the `edge` array, which is quite inefficient. Perhaps
62 * future optimization could see this replaced with a tree
63 * set. Additionally, one could perhaps replace the current linear
64 * search for edge endpoints (i.e. the inner loop over `edges`) by
65 * sorting the edge table by upper and lower Y coordinate.
66 *
67 * A final caveat comes from the use of fixed point arithmetic,
68 * which is motivated by performance considerations on FPU-less
69 * platforms (e.g. most Rockbox devices, and maybe others?). I'm
70 * currently using 16 fractional bits to compute the edge
71 * intersections, which (in the case of a 32-bit int) limits the
72 * acceptable range of coordinates to roughly (-2^14, +2^14). This
73 * ought to be acceptable for the forseeable future, but
74 * ultra-high DPI screens might one day exceed this. In that case,
75 * options include switching to int64_t (but that comes with its
76 * own portability headaches), reducing the number of fractional
77 * bits, or just giving up and using floating point.
78 */
79
80 /* Build edge table from coords. Horizontal edges are filtered
81 * out, so n_edges <= n_points in general. */
82 edges = smalloc(npoints * sizeof(struct edge));
83
84 for(i = 0; i < npoints; i++) {
85 int x1, y1, x2, y2;
86
87 x1 = coords[(2*i+0)];
88 y1 = coords[(2*i+1)];
89 x2 = coords[(2*i+2) % (npoints * 2)];
90 y2 = coords[(2*i+3) % (npoints * 2)];
91
92 if(y1 < min_y)
93 min_y = y1;
94 if(y1 > max_y)
95 max_y = y1;
96
97#define COORD_LIMIT (1<<(sizeof(int)*CHAR_BIT-2 - FRACBITS))
98 /* Prevent integer overflow when computing `inverse_slope',
99 * which shifts the coordinates left by FRACBITS, and for
100 * which we'd like to avoid relying on `long long'. */
101 /* If this ever causes issues, see the above comment about
102 possible solutions. */
103 assert(x1 < COORD_LIMIT && y1 < COORD_LIMIT);
104
105 /* Only create non-horizontal edges, and require y1 < y2. */
106 if(y1 != y2) {
107 bool swap = y1 > y2;
108 int lower_endpoint = swap ? (i + 1) : i;
109
110 /* Compute index of the point adjacent to lower end of
111 * this edge (which is not the upper end of this edge). */
112 int lower_neighbor = swap ? (lower_endpoint + 1) % npoints : (lower_endpoint + npoints - 1) % npoints;
113
114 struct edge *edge = edges + (n_edges++);
115
116 edge->active = false;
117 edge->x1 = swap ? x2 : x1;
118 edge->y1 = swap ? y2 : y1;
119 edge->x2 = swap ? x1 : x2;
120 edge->y2 = swap ? y1 : y2;
121 edge->inverse_slope = ((edge->x2 - edge->x1) << FRACBITS) / (edge->y2 - edge->y1);
122 edge->closed_y1 = edge->y1 < coords[2*lower_neighbor+1];
123 }
124 }
125
126 /* a generous upper bound on number of intersections is n_edges */
127 intersections = smalloc(n_edges * sizeof(int));
128
129 for(y = min_y; y <= max_y; y++) {
130 int n_intersections = 0;
131 for(i = 0; i < n_edges; i++) {
132 struct edge *edge = edges + i;
133 /* Update active edge set. These conditions are mutually
134 * exclusive because of the invariant that y1 < y2. */
135 if(edge->y1 + (edge->closed_y1 ? 0 : 1) == y)
136 edge->active = true;
137 else if(edge->y2 + 1 == y)
138 edge->active = false;
139
140 if(edge->active) {
141 int x = edges[i].x1;
142 x += (edges[i].inverse_slope * (y - edges[i].y1) + ONEHALF) >> FRACBITS;
143 intersections[n_intersections++] = x;
144 }
145 }
146
147 qsort(intersections, n_intersections, sizeof(int), compare_integers);
148
149 assert(n_intersections % 2 == 0);
150 assert(n_intersections <= n_edges);
151
152 /* Draw horizontal lines between successive pairs of
153 * intersections of the scanline with active edges. */
154 for(i = 0; i + 1 < n_intersections; i += 2) {
155 draw_line(dr,
156 intersections[i], y,
157 intersections[i+1], y,
158 fillcolour);
159 }
160 }
161
162 sfree(intersections);
163 sfree(edges);
164
165draw_outline:
166 assert(outlinecolour >= 0);
167 for (i = 0; i < 2 * npoints; i += 2)
168 draw_line(dr,
169 coords[i], coords[i+1],
170 coords[(i+2)%(2*npoints)], coords[(i+3)%(2*npoints)],
171 outlinecolour);
172}
173
174#ifdef STANDALONE_POLYGON
175
176/*
177 * Standalone program to test draw_polygon_fallback(). By default,
178 * creates a window and allows clicking points to build a
179 * polygon. Optionally, can draw a randomly growing polygon in
180 * "screensaver" mode.
181 */
182
183#include <SDL.h>
184
185void draw_line(drawing *dr, int x1, int y1, int x2, int y2, int colour)
186{
187 SDL_Renderer *renderer = GET_HANDLE_AS_TYPE(dr, SDL_Renderer);
188 SDL_RenderDrawLine(renderer, x1, y1, x2, y2);
189}
190
191#define WINDOW_WIDTH 800
192#define WINDOW_HEIGHT 600
193#define MAX_SCREENSAVER_POINTS 1000
194
195int main(int argc, char *argv[]) {
196 SDL_Window* window = NULL;
197 SDL_Event event;
198 SDL_Renderer *renderer = NULL;
199 int running = 1;
200 int i;
201 drawing dr;
202 bool screensaver = false;
203
204 if(argc >= 2) {
205 if(!strcmp(argv[1], "--screensaver"))
206 screensaver = true;
207 else
208 printf("usage: %s [--screensaver]\n", argv[0]);
209 }
210
211 int *poly = NULL;
212 int n_points = 0;
213
214 /* Initialize SDL */
215 if (SDL_Init(SDL_INIT_VIDEO) < 0) {
216 printf("SDL could not initialize! SDL_Error: %s\n", SDL_GetError());
217 return 1;
218 }
219
220 /* Create window */
221 window = SDL_CreateWindow("Polygon Drawing Test", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, WINDOW_WIDTH, WINDOW_HEIGHT, SDL_WINDOW_SHOWN);
222 if (!window) {
223 printf("Window could not be created! SDL_Error: %s\n", SDL_GetError());
224 SDL_Quit();
225 return 1;
226 }
227
228 /* Create renderer */
229 renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);
230 if (!renderer) {
231 printf("Renderer could not be created! SDL_Error: %s\n", SDL_GetError());
232 SDL_DestroyWindow(window);
233 SDL_Quit();
234 return 1;
235 }
236
237 dr.handle = renderer;
238
239 if(!screensaver)
240 printf("Click points in the window to create vertices. Pressing C resets.\n");
241
242 while (running) {
243 while (SDL_PollEvent(&event) != 0) {
244 if (event.type == SDL_QUIT) {
245 running = 0;
246 }
247 else if (event.type == SDL_MOUSEBUTTONDOWN) {
248 if (event.button.button == SDL_BUTTON_LEFT) {
249 int mouseX = event.button.x;
250 int mouseY = event.button.y;
251
252 poly = realloc(poly, ++n_points * 2 * sizeof(int));
253 poly[2 * (n_points - 1)] = mouseX;
254 poly[2 * (n_points - 1) + 1] = mouseY;
255 }
256 }
257 else if (event.type == SDL_KEYDOWN) {
258 if (event.key.keysym.sym == SDLK_c) {
259 free(poly);
260 poly = NULL;
261 n_points = 0;
262 }
263 }
264 }
265
266 if(screensaver) {
267 poly = realloc(poly, ++n_points * 2 * sizeof(int));
268 poly[2 * (n_points - 1)] = rand() % WINDOW_WIDTH;
269 poly[2 * (n_points - 1) + 1] = rand() % WINDOW_HEIGHT;
270
271 if(n_points >= MAX_SCREENSAVER_POINTS) {
272 free(poly);
273 poly = NULL;
274 n_points = 0;
275 }
276 }
277
278 /* Clear the screen with a black color */
279 SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
280 SDL_RenderClear(renderer);
281
282 /* Set draw color to white */
283 SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255);
284
285 draw_polygon_fallback(&dr, poly, n_points, 1, 1);
286
287 SDL_SetRenderDrawColor(renderer, 255, 0, 0, 255);
288 for(i = 0; i < 2*n_points; i+=2)
289 SDL_RenderDrawPoint(renderer, poly[i], poly[i+1]);
290
291 /* Present the back buffer */
292 SDL_RenderPresent(renderer);
293 }
294
295 /* Clean up and quit SDL */
296 SDL_DestroyRenderer(renderer);
297 SDL_DestroyWindow(window);
298 SDL_Quit();
299
300 return 0;
301}
302#endif
diff --git a/apps/plugins/puzzles/src/drawing.c b/apps/plugins/puzzles/src/drawing.c
index 2c8816c31a..a754b068f3 100644
--- a/apps/plugins/puzzles/src/drawing.c
+++ b/apps/plugins/puzzles/src/drawing.c
@@ -42,9 +42,12 @@ struct print_colour {
42 float grey; 42 float grey;
43}; 43};
44 44
45struct drawing { 45typedef struct drawing_internal {
46 const drawing_api *api; 46 /* we implement data hiding by casting `struct drawing*` pointers
47 void *handle; 47 * to `struct drawing_internal*` */
48 struct drawing pub;
49
50 /* private data */
48 struct print_colour *colours; 51 struct print_colour *colours;
49 int ncolours, coloursize; 52 int ncolours, coloursize;
50 float scale; 53 float scale;
@@ -52,53 +55,70 @@ struct drawing {
52 * this may set it to NULL. */ 55 * this may set it to NULL. */
53 midend *me; 56 midend *me;
54 char *laststatus; 57 char *laststatus;
55}; 58} drawing_internal;
59
60#define PRIVATE_CAST(dr) ((drawing_internal*)(dr))
61#define PUBLIC_CAST(dri) ((drawing*)(dri))
62
63/* See puzzles.h for a description of the version number. */
64#define DRAWING_API_VERSION 1
56 65
57drawing *drawing_new(const drawing_api *api, midend *me, void *handle) 66drawing *drawing_new(const drawing_api *api, midend *me, void *handle)
58{ 67{
59 drawing *dr = snew(drawing); 68 if(api->version != DRAWING_API_VERSION) {
60 dr->api = api; 69 fatal("Drawing API version mismatch: expected: %d, actual: %d\n", DRAWING_API_VERSION, api->version);
61 dr->handle = handle; 70 /* shouldn't get here */
62 dr->colours = NULL; 71 return NULL;
63 dr->ncolours = dr->coloursize = 0; 72 }
64 dr->scale = 1.0F; 73
65 dr->me = me; 74 drawing_internal *dri = snew(drawing_internal);
66 dr->laststatus = NULL; 75 dri->pub.api = api;
67 return dr; 76 dri->pub.handle = handle;
77 dri->colours = NULL;
78 dri->ncolours = dri->coloursize = 0;
79 dri->scale = 1.0F;
80 dri->me = me;
81 dri->laststatus = NULL;
82 return PUBLIC_CAST(dri);
68} 83}
69 84
70void drawing_free(drawing *dr) 85void drawing_free(drawing *dr)
71{ 86{
72 sfree(dr->laststatus); 87 drawing_internal *dri = PRIVATE_CAST(dr);
73 sfree(dr->colours); 88 sfree(dri->laststatus);
74 sfree(dr); 89 sfree(dri->colours);
90 sfree(dri);
75} 91}
76 92
77void draw_text(drawing *dr, int x, int y, int fonttype, int fontsize, 93void draw_text(drawing *dr, int x, int y, int fonttype, int fontsize,
78 int align, int colour, const char *text) 94 int align, int colour, const char *text)
79{ 95{
80 dr->api->draw_text(dr->handle, x, y, fonttype, fontsize, align, 96 drawing_internal *dri = PRIVATE_CAST(dr);
81 colour, text); 97 dri->pub.api->draw_text(dr, x, y, fonttype, fontsize, align,
98 colour, text);
82} 99}
83 100
84void draw_rect(drawing *dr, int x, int y, int w, int h, int colour) 101void draw_rect(drawing *dr, int x, int y, int w, int h, int colour)
85{ 102{
86 dr->api->draw_rect(dr->handle, x, y, w, h, colour); 103 drawing_internal *dri = PRIVATE_CAST(dr);
104 dri->pub.api->draw_rect(dr, x, y, w, h, colour);
87} 105}
88 106
89void draw_line(drawing *dr, int x1, int y1, int x2, int y2, int colour) 107void draw_line(drawing *dr, int x1, int y1, int x2, int y2, int colour)
90{ 108{
91 dr->api->draw_line(dr->handle, x1, y1, x2, y2, colour); 109 drawing_internal *dri = PRIVATE_CAST(dr);
110 dri->pub.api->draw_line(dr, x1, y1, x2, y2, colour);
92} 111}
93 112
94void draw_thick_line(drawing *dr, float thickness, 113void draw_thick_line(drawing *dr, float thickness,
95 float x1, float y1, float x2, float y2, int colour) 114 float x1, float y1, float x2, float y2, int colour)
96{ 115{
116 drawing_internal *dri = PRIVATE_CAST(dr);
97 if (thickness < 1.0F) 117 if (thickness < 1.0F)
98 thickness = 1.0F; 118 thickness = 1.0F;
99 if (dr->api->draw_thick_line) { 119 if (dri->pub.api->draw_thick_line) {
100 dr->api->draw_thick_line(dr->handle, thickness, 120 dri->pub.api->draw_thick_line(dr, thickness,
101 x1, y1, x2, y2, colour); 121 x1, y1, x2, y2, colour);
102 } else { 122 } else {
103 /* We'll fake it up with a filled polygon. The tweak to the 123 /* We'll fake it up with a filled polygon. The tweak to the
104 * thickness empirically compensates for rounding errors, because 124 * thickness empirically compensates for rounding errors, because
@@ -117,59 +137,67 @@ void draw_thick_line(drawing *dr, float thickness,
117 p[5] = y2 - tvhatx; 137 p[5] = y2 - tvhatx;
118 p[6] = x1 + tvhaty; 138 p[6] = x1 + tvhaty;
119 p[7] = y1 - tvhatx; 139 p[7] = y1 - tvhatx;
120 dr->api->draw_polygon(dr->handle, p, 4, colour, colour); 140 dri->pub.api->draw_polygon(dr, p, 4, colour, colour);
121 } 141 }
122} 142}
123 143
124void draw_polygon(drawing *dr, const int *coords, int npoints, 144void draw_polygon(drawing *dr, const int *coords, int npoints,
125 int fillcolour, int outlinecolour) 145 int fillcolour, int outlinecolour)
126{ 146{
127 dr->api->draw_polygon(dr->handle, coords, npoints, fillcolour, 147 drawing_internal *dri = PRIVATE_CAST(dr);
128 outlinecolour); 148 dri->pub.api->draw_polygon(dr, coords, npoints, fillcolour,
149 outlinecolour);
129} 150}
130 151
131void draw_circle(drawing *dr, int cx, int cy, int radius, 152void draw_circle(drawing *dr, int cx, int cy, int radius,
132 int fillcolour, int outlinecolour) 153 int fillcolour, int outlinecolour)
133{ 154{
134 dr->api->draw_circle(dr->handle, cx, cy, radius, fillcolour, 155 drawing_internal *dri = PRIVATE_CAST(dr);
135 outlinecolour); 156 dri->pub.api->draw_circle(dr, cx, cy, radius, fillcolour,
157 outlinecolour);
136} 158}
137 159
138void draw_update(drawing *dr, int x, int y, int w, int h) 160void draw_update(drawing *dr, int x, int y, int w, int h)
139{ 161{
140 if (dr->api->draw_update) 162 drawing_internal *dri = PRIVATE_CAST(dr);
141 dr->api->draw_update(dr->handle, x, y, w, h); 163 if (dri->pub.api->draw_update)
164 dri->pub.api->draw_update(dr, x, y, w, h);
142} 165}
143 166
144void clip(drawing *dr, int x, int y, int w, int h) 167void clip(drawing *dr, int x, int y, int w, int h)
145{ 168{
146 dr->api->clip(dr->handle, x, y, w, h); 169 drawing_internal *dri = PRIVATE_CAST(dr);
170 dri->pub.api->clip(dr, x, y, w, h);
147} 171}
148 172
149void unclip(drawing *dr) 173void unclip(drawing *dr)
150{ 174{
151 dr->api->unclip(dr->handle); 175 drawing_internal *dri = PRIVATE_CAST(dr);
176 dri->pub.api->unclip(dr);
152} 177}
153 178
154void start_draw(drawing *dr) 179void start_draw(drawing *dr)
155{ 180{
156 dr->api->start_draw(dr->handle); 181 drawing_internal *dri = PRIVATE_CAST(dr);
182 dri->pub.api->start_draw(dr);
157} 183}
158 184
159void end_draw(drawing *dr) 185void end_draw(drawing *dr)
160{ 186{
161 dr->api->end_draw(dr->handle); 187 drawing_internal *dri = PRIVATE_CAST(dr);
188 dri->pub.api->end_draw(dr);
162} 189}
163 190
164char *text_fallback(drawing *dr, const char *const *strings, int nstrings) 191char *text_fallback(drawing *dr, const char *const *strings, int nstrings)
165{ 192{
193 drawing_internal *dri = PRIVATE_CAST(dr);
166 int i; 194 int i;
167 195
168 /* 196 /*
169 * If the drawing implementation provides one of these, use it. 197 * If the drawing implementation provides one of these, use it.
170 */ 198 */
171 if (dr && dr->api->text_fallback) 199 if (dr && dri->pub.api->text_fallback)
172 return dr->api->text_fallback(dr->handle, strings, nstrings); 200 return dri->pub.api->text_fallback(dr, strings, nstrings);
173 201
174 /* 202 /*
175 * Otherwise, do the simple thing and just pick the first string 203 * Otherwise, do the simple thing and just pick the first string
@@ -196,18 +224,19 @@ char *text_fallback(drawing *dr, const char *const *strings, int nstrings)
196 224
197void status_bar(drawing *dr, const char *text) 225void status_bar(drawing *dr, const char *text)
198{ 226{
227 drawing_internal *dri = PRIVATE_CAST(dr);
199 char *rewritten; 228 char *rewritten;
200 229
201 if (!dr->api->status_bar) 230 if (!dri->pub.api->status_bar)
202 return; 231 return;
203 232
204 assert(dr->me); 233 assert(dri->me);
205 234
206 rewritten = midend_rewrite_statusbar(dr->me, text); 235 rewritten = midend_rewrite_statusbar(dri->me, text);
207 if (!dr->laststatus || strcmp(rewritten, dr->laststatus)) { 236 if (!dri->laststatus || strcmp(rewritten, dri->laststatus)) {
208 dr->api->status_bar(dr->handle, rewritten); 237 dri->pub.api->status_bar(dr, rewritten);
209 sfree(dr->laststatus); 238 sfree(dri->laststatus);
210 dr->laststatus = rewritten; 239 dri->laststatus = rewritten;
211 } else { 240 } else {
212 sfree(rewritten); 241 sfree(rewritten);
213 } 242 }
@@ -215,74 +244,85 @@ void status_bar(drawing *dr, const char *text)
215 244
216blitter *blitter_new(drawing *dr, int w, int h) 245blitter *blitter_new(drawing *dr, int w, int h)
217{ 246{
218 return dr->api->blitter_new(dr->handle, w, h); 247 drawing_internal *dri = PRIVATE_CAST(dr);
248 return dri->pub.api->blitter_new(dr, w, h);
219} 249}
220 250
221void blitter_free(drawing *dr, blitter *bl) 251void blitter_free(drawing *dr, blitter *bl)
222{ 252{
223 dr->api->blitter_free(dr->handle, bl); 253 drawing_internal *dri = PRIVATE_CAST(dr);
254 dri->pub.api->blitter_free(dr, bl);
224} 255}
225 256
226void blitter_save(drawing *dr, blitter *bl, int x, int y) 257void blitter_save(drawing *dr, blitter *bl, int x, int y)
227{ 258{
228 dr->api->blitter_save(dr->handle, bl, x, y); 259 drawing_internal *dri = PRIVATE_CAST(dr);
260 dri->pub.api->blitter_save(dr, bl, x, y);
229} 261}
230 262
231void blitter_load(drawing *dr, blitter *bl, int x, int y) 263void blitter_load(drawing *dr, blitter *bl, int x, int y)
232{ 264{
233 dr->api->blitter_load(dr->handle, bl, x, y); 265 drawing_internal *dri = PRIVATE_CAST(dr);
266 dri->pub.api->blitter_load(dr, bl, x, y);
234} 267}
235 268
236void print_begin_doc(drawing *dr, int pages) 269void print_begin_doc(drawing *dr, int pages)
237{ 270{
238 dr->api->begin_doc(dr->handle, pages); 271 drawing_internal *dri = PRIVATE_CAST(dr);
272 dri->pub.api->begin_doc(dr, pages);
239} 273}
240 274
241void print_begin_page(drawing *dr, int number) 275void print_begin_page(drawing *dr, int number)
242{ 276{
243 dr->api->begin_page(dr->handle, number); 277 drawing_internal *dri = PRIVATE_CAST(dr);
278 dri->pub.api->begin_page(dr, number);
244} 279}
245 280
246void print_begin_puzzle(drawing *dr, float xm, float xc, 281void print_begin_puzzle(drawing *dr, float xm, float xc,
247 float ym, float yc, int pw, int ph, float wmm, 282 float ym, float yc, int pw, int ph, float wmm,
248 float scale) 283 float scale)
249{ 284{
250 dr->scale = scale; 285 drawing_internal *dri = PRIVATE_CAST(dr);
251 dr->ncolours = 0; 286 dri->scale = scale;
252 dr->api->begin_puzzle(dr->handle, xm, xc, ym, yc, pw, ph, wmm); 287 dri->ncolours = 0;
288 dri->pub.api->begin_puzzle(dr, xm, xc, ym, yc, pw, ph, wmm);
253} 289}
254 290
255void print_end_puzzle(drawing *dr) 291void print_end_puzzle(drawing *dr)
256{ 292{
257 dr->api->end_puzzle(dr->handle); 293 drawing_internal *dri = PRIVATE_CAST(dr);
258 dr->scale = 1.0F; 294 dri->pub.api->end_puzzle(dr);
295 dri->scale = 1.0F;
259} 296}
260 297
261void print_end_page(drawing *dr, int number) 298void print_end_page(drawing *dr, int number)
262{ 299{
263 dr->api->end_page(dr->handle, number); 300 drawing_internal *dri = PRIVATE_CAST(dr);
301 dri->pub.api->end_page(dr, number);
264} 302}
265 303
266void print_end_doc(drawing *dr) 304void print_end_doc(drawing *dr)
267{ 305{
268 dr->api->end_doc(dr->handle); 306 drawing_internal *dri = PRIVATE_CAST(dr);
307 dri->pub.api->end_doc(dr);
269} 308}
270 309
271void print_get_colour(drawing *dr, int colour, bool printing_in_colour, 310void print_get_colour(drawing *dr, int colour, bool printing_in_colour,
272 int *hatch, float *r, float *g, float *b) 311 int *hatch, float *r, float *g, float *b)
273{ 312{
274 assert(colour >= 0 && colour < dr->ncolours); 313 drawing_internal *dri = PRIVATE_CAST(dr);
275 if (dr->colours[colour].hatch_when == 2 || 314 assert(colour >= 0 && colour < dri->ncolours);
276 (dr->colours[colour].hatch_when == 1 && !printing_in_colour)) { 315 if (dri->colours[colour].hatch_when == 2 ||
277 *hatch = dr->colours[colour].hatch; 316 (dri->colours[colour].hatch_when == 1 && !printing_in_colour)) {
317 *hatch = dri->colours[colour].hatch;
278 } else { 318 } else {
279 *hatch = -1; 319 *hatch = -1;
280 if (printing_in_colour) { 320 if (printing_in_colour) {
281 *r = dr->colours[colour].r; 321 *r = dri->colours[colour].r;
282 *g = dr->colours[colour].g; 322 *g = dri->colours[colour].g;
283 *b = dr->colours[colour].b; 323 *b = dri->colours[colour].b;
284 } else { 324 } else {
285 *r = *g = *b = dr->colours[colour].grey; 325 *r = *g = *b = dri->colours[colour].grey;
286 } 326 }
287 } 327 }
288} 328}
@@ -290,18 +330,19 @@ void print_get_colour(drawing *dr, int colour, bool printing_in_colour,
290static int print_generic_colour(drawing *dr, float r, float g, float b, 330static int print_generic_colour(drawing *dr, float r, float g, float b,
291 float grey, int hatch, int hatch_when) 331 float grey, int hatch, int hatch_when)
292{ 332{
293 if (dr->ncolours >= dr->coloursize) { 333 drawing_internal *dri = PRIVATE_CAST(dr);
294 dr->coloursize = dr->ncolours + 16; 334 if (dri->ncolours >= dri->coloursize) {
295 dr->colours = sresize(dr->colours, dr->coloursize, 335 dri->coloursize = dri->ncolours + 16;
336 dri->colours = sresize(dri->colours, dri->coloursize,
296 struct print_colour); 337 struct print_colour);
297 } 338 }
298 dr->colours[dr->ncolours].hatch = hatch; 339 dri->colours[dri->ncolours].hatch = hatch;
299 dr->colours[dr->ncolours].hatch_when = hatch_when; 340 dri->colours[dri->ncolours].hatch_when = hatch_when;
300 dr->colours[dr->ncolours].r = r; 341 dri->colours[dri->ncolours].r = r;
301 dr->colours[dr->ncolours].g = g; 342 dri->colours[dri->ncolours].g = g;
302 dr->colours[dr->ncolours].b = b; 343 dri->colours[dri->ncolours].b = b;
303 dr->colours[dr->ncolours].grey = grey; 344 dri->colours[dri->ncolours].grey = grey;
304 return dr->ncolours++; 345 return dri->ncolours++;
305} 346}
306 347
307int print_mono_colour(drawing *dr, int grey) 348int print_mono_colour(drawing *dr, int grey)
@@ -336,6 +377,8 @@ int print_rgb_hatched_colour(drawing *dr, float r, float g, float b, int hatch)
336 377
337void print_line_width(drawing *dr, int width) 378void print_line_width(drawing *dr, int width)
338{ 379{
380 drawing_internal *dri = PRIVATE_CAST(dr);
381
339 /* 382 /*
340 * I don't think it's entirely sensible to have line widths be 383 * I don't think it's entirely sensible to have line widths be
341 * entirely relative to the puzzle size; there is a point 384 * entirely relative to the puzzle size; there is a point
@@ -348,10 +391,11 @@ void print_line_width(drawing *dr, int width)
348 * _square root_ of the main puzzle scale. Double the puzzle 391 * _square root_ of the main puzzle scale. Double the puzzle
349 * size, and the line width multiplies by 1.4. 392 * size, and the line width multiplies by 1.4.
350 */ 393 */
351 dr->api->line_width(dr->handle, (float)sqrt(dr->scale) * width); 394 dri->pub.api->line_width(dr, (float)sqrt(dri->scale) * width);
352} 395}
353 396
354void print_line_dotted(drawing *dr, bool dotted) 397void print_line_dotted(drawing *dr, bool dotted)
355{ 398{
356 dr->api->line_dotted(dr->handle, dotted); 399 drawing_internal *dri = PRIVATE_CAST(dr);
400 dri->pub.api->line_dotted(dr, dotted);
357} 401}
diff --git a/apps/plugins/puzzles/src/inertia.c b/apps/plugins/puzzles/src/inertia.c
index 1d5a028add..fe174f3b56 100644
--- a/apps/plugins/puzzles/src/inertia.c
+++ b/apps/plugins/puzzles/src/inertia.c
@@ -722,18 +722,6 @@ static int move_goes_to(int w, int h, char *grid, int x, int y, int d)
722 return (y*w+x)*DP1+dr; 722 return (y*w+x)*DP1+dr;
723} 723}
724 724
725static int compare_integers(const void *av, const void *bv)
726{
727 const int *a = (const int *)av;
728 const int *b = (const int *)bv;
729 if (*a < *b)
730 return -1;
731 else if (*a > *b)
732 return +1;
733 else
734 return 0;
735}
736
737static char *solve_game(const game_state *state, const game_state *currstate, 725static char *solve_game(const game_state *state, const game_state *currstate,
738 const char *aux, const char **error) 726 const char *aux, const char **error)
739{ 727{
@@ -1886,11 +1874,6 @@ static void draw_player(drawing *dr, game_drawstate *ds, int x, int y,
1886 coords[d*4+2] = x + TILESIZE/2 + (int)((TILESIZE*3/7) * x2); 1874 coords[d*4+2] = x + TILESIZE/2 + (int)((TILESIZE*3/7) * x2);
1887 coords[d*4+3] = y + TILESIZE/2 + (int)((TILESIZE*3/7) * y2); 1875 coords[d*4+3] = y + TILESIZE/2 + (int)((TILESIZE*3/7) * y2);
1888 } 1876 }
1889 /* rockbox hack */
1890 int tmp[2] = { coords[0], coords[1] };
1891 memmove(coords, coords + 2, sizeof(int) * DIRECTIONS * 4 - 2);
1892 memcpy(coords + DIRECTIONS * 4 - 2, tmp, 2 * sizeof(int));
1893
1894 draw_polygon(dr, coords, DIRECTIONS*2, COL_DEAD_PLAYER, COL_OUTLINE); 1877 draw_polygon(dr, coords, DIRECTIONS*2, COL_DEAD_PLAYER, COL_OUTLINE);
1895 } else { 1878 } else {
1896 draw_circle(dr, x + TILESIZE/2, y + TILESIZE/2, 1879 draw_circle(dr, x + TILESIZE/2, y + TILESIZE/2,
@@ -1906,6 +1889,8 @@ static void draw_player(drawing *dr, game_drawstate *ds, int x, int y,
1906 int coords[14], *c; 1889 int coords[14], *c;
1907 1890
1908 c = coords; 1891 c = coords;
1892 *c++ = ox + px/9;
1893 *c++ = oy + py/9;
1909 *c++ = ox + px/9 + ax*2/3; 1894 *c++ = ox + px/9 + ax*2/3;
1910 *c++ = oy + py/9 + ay*2/3; 1895 *c++ = oy + py/9 + ay*2/3;
1911 *c++ = ox + px/3 + ax*2/3; 1896 *c++ = ox + px/3 + ax*2/3;
@@ -1918,8 +1903,6 @@ static void draw_player(drawing *dr, game_drawstate *ds, int x, int y,
1918 *c++ = oy - py/9 + ay*2/3; 1903 *c++ = oy - py/9 + ay*2/3;
1919 *c++ = ox - px/9; 1904 *c++ = ox - px/9;
1920 *c++ = oy - py/9; 1905 *c++ = oy - py/9;
1921 *c++ = ox + px/9;
1922 *c++ = oy + py/9;
1923 draw_polygon(dr, coords, 7, COL_HINT, COL_OUTLINE); 1906 draw_polygon(dr, coords, 7, COL_HINT, COL_OUTLINE);
1924 } 1907 }
1925 1908
diff --git a/apps/plugins/puzzles/src/mines.c b/apps/plugins/puzzles/src/mines.c
index eec898ac6f..e1441fae13 100644
--- a/apps/plugins/puzzles/src/mines.c
+++ b/apps/plugins/puzzles/src/mines.c
@@ -2867,12 +2867,12 @@ static void draw_tile(drawing *dr, game_drawstate *ds,
2867 coords[(n)*2+0] = x + (int)(TILE_SIZE * (dx)); \ 2867 coords[(n)*2+0] = x + (int)(TILE_SIZE * (dx)); \
2868 coords[(n)*2+1] = y + (int)(TILE_SIZE * (dy)); \ 2868 coords[(n)*2+1] = y + (int)(TILE_SIZE * (dy)); \
2869} while (0) 2869} while (0)
2870 SETCOORD(0, 0.6F, 0.7F); 2870 SETCOORD(0, 0.6F, 0.35F);
2871 SETCOORD(1, 0.8F, 0.8F); 2871 SETCOORD(1, 0.6F, 0.7F);
2872 SETCOORD(2, 0.25F, 0.8F); 2872 SETCOORD(2, 0.8F, 0.8F);
2873 SETCOORD(3, 0.55F, 0.7F); 2873 SETCOORD(3, 0.25F, 0.8F);
2874 SETCOORD(4, 0.55F, 0.35F); 2874 SETCOORD(4, 0.55F, 0.7F);
2875 SETCOORD(5, 0.6F, 0.35F); 2875 SETCOORD(5, 0.55F, 0.35F);
2876 draw_polygon(dr, coords, 6, COL_FLAGBASE, COL_FLAGBASE); 2876 draw_polygon(dr, coords, 6, COL_FLAGBASE, COL_FLAGBASE);
2877 2877
2878 SETCOORD(0, 0.6F, 0.2F); 2878 SETCOORD(0, 0.6F, 0.2F);
diff --git a/apps/plugins/puzzles/src/misc.c b/apps/plugins/puzzles/src/misc.c
index f9eaf5d9b8..8e96d67039 100644
--- a/apps/plugins/puzzles/src/misc.c
+++ b/apps/plugins/puzzles/src/misc.c
@@ -351,6 +351,17 @@ void draw_rect_corners(drawing *dr, int cx, int cy, int r, int col)
351 draw_line(dr, cx + r, cy + r, cx + r/2, cy + r, col); 351 draw_line(dr, cx + r, cy + r, cx + r/2, cy + r, col);
352} 352}
353 353
354int compare_integers(const void *av, const void *bv) {
355 const int *a = (const int *)av;
356 const int *b = (const int *)bv;
357 if (*a < *b)
358 return -1;
359 else if (*a > *b)
360 return +1;
361 else
362 return 0;
363}
364
354char *move_cursor(int button, int *x, int *y, int maxw, int maxh, bool wrap, 365char *move_cursor(int button, int *x, int *y, int maxw, int maxh, bool wrap,
355 bool *visible) 366 bool *visible)
356{ 367{
diff --git a/apps/plugins/puzzles/src/puzzles.but b/apps/plugins/puzzles/src/puzzles.but
index fa9384252e..0eb3511cc0 100644
--- a/apps/plugins/puzzles/src/puzzles.but
+++ b/apps/plugins/puzzles/src/puzzles.but
@@ -3442,7 +3442,7 @@ real challenge, set this value to 0 and then try to solve a grid in
3442 3442
3443} 3443}
3444 3444
3445\C{tracks} \i{Train Tracks} 3445\C{tracks} \i{Tracks}
3446 3446
3447\cfg{winhelp-topic}{games.tracks} 3447\cfg{winhelp-topic}{games.tracks}
3448 3448
@@ -3454,9 +3454,9 @@ clues to the top and right of the grid.
3454There are only straight and 90 degree curved rails, and the track may not 3454There are only straight and 90 degree curved rails, and the track may not
3455cross itself. 3455cross itself.
3456 3456
3457Train Tracks was contributed to this collection by James Harvey. 3457Tracks was contributed to this collection by James Harvey.
3458 3458
3459\H{tracks-controls} \I{controls, for Tracks}Train Tracks controls 3459\H{tracks-controls} \I{controls, for Tracks}Tracks controls
3460 3460
3461Left-clicking on an edge between two squares adds a track segment between 3461Left-clicking on an edge between two squares adds a track segment between
3462the two squares. Right-clicking on an edge adds a cross on the edge, 3462the two squares. Right-clicking on an edge adds a cross on the edge,
@@ -3473,7 +3473,7 @@ columns to match the clue.
3473 3473
3474(All the actions described in \k{common-actions} are also available.) 3474(All the actions described in \k{common-actions} are also available.)
3475 3475
3476\H{tracks-parameters} \I{parameters, for Tracks}Train Tracks parameters 3476\H{tracks-parameters} \I{parameters, for Tracks}Tracks parameters
3477 3477
3478These parameters are available from the \q{Custom...} option on the 3478These parameters are available from the \q{Custom...} option on the
3479\q{Type} menu. 3479\q{Type} menu.
@@ -3490,12 +3490,12 @@ that would lead to impossible crossings later.
3490 3490
3491\dt \e{Disallow consecutive 1 clues} 3491\dt \e{Disallow consecutive 1 clues}
3492 3492
3493\dd Controls whether the Train Tracks game generation permits two 3493\dd Controls whether the Tracks game generation permits two adjacent
3494adjacent rows or columns to have a 1 clue, or permits the row or 3494rows or columns to have a 1 clue, or permits the row or column of the
3495column of the track's endpoint to have a 1 clue. By default this is 3495track's endpoint to have a 1 clue. By default this is not permitted,
3496not permitted, to avoid long straight boring segments of track and 3496to avoid long straight boring segments of track and make the games
3497make the games more twiddly and interesting. If you want to restore 3497more twiddly and interesting. If you want to restore the possibility,
3498the possibility, turn this option off. 3498turn this option off.
3499 3499
3500 3500
3501\C{palisade} \i{Palisade} 3501\C{palisade} \i{Palisade}
diff --git a/apps/plugins/puzzles/src/puzzles.h b/apps/plugins/puzzles/src/puzzles.h
index 2c52bc6cde..44a056d78f 100644
--- a/apps/plugins/puzzles/src/puzzles.h
+++ b/apps/plugins/puzzles/src/puzzles.h
@@ -266,6 +266,8 @@ void draw_rect(drawing *dr, int x, int y, int w, int h, int colour);
266void draw_line(drawing *dr, int x1, int y1, int x2, int y2, int colour); 266void draw_line(drawing *dr, int x1, int y1, int x2, int y2, int colour);
267void draw_polygon(drawing *dr, const int *coords, int npoints, 267void draw_polygon(drawing *dr, const int *coords, int npoints,
268 int fillcolour, int outlinecolour); 268 int fillcolour, int outlinecolour);
269void draw_polygon_fallback(drawing *dr, const int *coords, int npoints,
270 int fillcolour, int outlinecolour);
269void draw_circle(drawing *dr, int cx, int cy, int radius, 271void draw_circle(drawing *dr, int cx, int cy, int radius,
270 int fillcolour, int outlinecolour); 272 int fillcolour, int outlinecolour);
271void draw_thick_line(drawing *dr, float thickness, 273void draw_thick_line(drawing *dr, float thickness,
@@ -454,6 +456,9 @@ char *button2label(int button);
454 * standard per clause 7.26.11.1.) */ 456 * standard per clause 7.26.11.1.) */
455void swap_regions(void *av, void *bv, size_t size); 457void swap_regions(void *av, void *bv, size_t size);
456 458
459/* comparator for sorting ints with qsort() */
460int compare_integers(const void *av, const void *bv);
461
457/* 462/*
458 * dsf.c 463 * dsf.c
459 */ 464 */
@@ -752,43 +757,76 @@ struct game {
752 int flags; 757 int flags;
753}; 758};
754 759
760#define GET_HANDLE_AS_TYPE(dr, type) ((type*)((dr)->handle))
761
762struct drawing {
763 const drawing_api *api;
764 void *handle;
765};
766
755/* 767/*
756 * Data structure containing the drawing API implemented by the 768 * Data structure containing the drawing API implemented by the
757 * front end and also by cross-platform printing modules such as 769 * front end and also by cross-platform printing modules such as
758 * PostScript. 770 * PostScript.
759 */ 771 */
760struct drawing_api { 772struct drawing_api {
761 void (*draw_text)(void *handle, int x, int y, int fonttype, int fontsize, 773 /*
774 * API version. Increment this when there is a breaking change to
775 * this API which requires front ends to change.
776 *
777 * There is expliclty not a public LATEST_API_VERSION define, so
778 * that front end authors will need to manually intervene when the
779 * version number changes. Naturally, this should be done
780 * sparingly.
781 *
782 * If a function is ever added to this API, please move this field
783 * to the _end_ of the structure, so that changes thereafter will
784 * shift the position of the version and lead to a compilation
785 * error if old implementations are not updated. Then remove this
786 * comment.
787 *
788 * The latest version number is 1.
789 *
790 * Change log:
791 *
792 * Version 1 (2024-08-14): Introduction of version number, in
793 * conjunction with changing every API function to take `drawing
794 * *` instead of `void *`. See commit f379130 for the detailed
795 * rationale behind this change.
796 */
797 int version;
798
799 void (*draw_text)(drawing *dr, int x, int y, int fonttype, int fontsize,
762 int align, int colour, const char *text); 800 int align, int colour, const char *text);
763 void (*draw_rect)(void *handle, int x, int y, int w, int h, int colour); 801 void (*draw_rect)(drawing *dr, int x, int y, int w, int h, int colour);
764 void (*draw_line)(void *handle, int x1, int y1, int x2, int y2, 802 void (*draw_line)(drawing *dr, int x1, int y1, int x2, int y2,
765 int colour); 803 int colour);
766 void (*draw_polygon)(void *handle, const int *coords, int npoints, 804 void (*draw_polygon)(drawing *dr, const int *coords, int npoints,
767 int fillcolour, int outlinecolour); 805 int fillcolour, int outlinecolour);
768 void (*draw_circle)(void *handle, int cx, int cy, int radius, 806 void (*draw_circle)(drawing *dr, int cx, int cy, int radius,
769 int fillcolour, int outlinecolour); 807 int fillcolour, int outlinecolour);
770 void (*draw_update)(void *handle, int x, int y, int w, int h); 808 void (*draw_update)(drawing *dr, int x, int y, int w, int h);
771 void (*clip)(void *handle, int x, int y, int w, int h); 809 void (*clip)(drawing *dr, int x, int y, int w, int h);
772 void (*unclip)(void *handle); 810 void (*unclip)(drawing *dr);
773 void (*start_draw)(void *handle); 811 void (*start_draw)(drawing *dr);
774 void (*end_draw)(void *handle); 812 void (*end_draw)(drawing *dr);
775 void (*status_bar)(void *handle, const char *text); 813 void (*status_bar)(drawing *dr, const char *text);
776 blitter *(*blitter_new)(void *handle, int w, int h); 814 blitter *(*blitter_new)(drawing *dr, int w, int h);
777 void (*blitter_free)(void *handle, blitter *bl); 815 void (*blitter_free)(drawing *dr, blitter *bl);
778 void (*blitter_save)(void *handle, blitter *bl, int x, int y); 816 void (*blitter_save)(drawing *dr, blitter *bl, int x, int y);
779 void (*blitter_load)(void *handle, blitter *bl, int x, int y); 817 void (*blitter_load)(drawing *dr, blitter *bl, int x, int y);
780 void (*begin_doc)(void *handle, int pages); 818 void (*begin_doc)(drawing *dr, int pages);
781 void (*begin_page)(void *handle, int number); 819 void (*begin_page)(drawing *dr, int number);
782 void (*begin_puzzle)(void *handle, float xm, float xc, 820 void (*begin_puzzle)(drawing *dr, float xm, float xc,
783 float ym, float yc, int pw, int ph, float wmm); 821 float ym, float yc, int pw, int ph, float wmm);
784 void (*end_puzzle)(void *handle); 822 void (*end_puzzle)(drawing *dr);
785 void (*end_page)(void *handle, int number); 823 void (*end_page)(drawing *dr, int number);
786 void (*end_doc)(void *handle); 824 void (*end_doc)(drawing *dr);
787 void (*line_width)(void *handle, float width); 825 void (*line_width)(drawing *dr, float width);
788 void (*line_dotted)(void *handle, bool dotted); 826 void (*line_dotted)(drawing *dr, bool dotted);
789 char *(*text_fallback)(void *handle, const char *const *strings, 827 char *(*text_fallback)(drawing *dr, const char *const *strings,
790 int nstrings); 828 int nstrings);
791 void (*draw_thick_line)(void *handle, float thickness, 829 void (*draw_thick_line)(drawing *dr, float thickness,
792 float x1, float y1, float x2, float y2, 830 float x1, float y1, float x2, float y2,
793 int colour); 831 int colour);
794}; 832};
diff --git a/apps/plugins/puzzles/src/signpost.c b/apps/plugins/puzzles/src/signpost.c
index 4d2f5849fd..9aed67bb4a 100644
--- a/apps/plugins/puzzles/src/signpost.c
+++ b/apps/plugins/puzzles/src/signpost.c
@@ -1895,9 +1895,8 @@ static void draw_star(drawing *dr, int cx, int cy, int rad, int npoints,
1895 coords = snewn(npoints * 2 * 2, int); 1895 coords = snewn(npoints * 2 * 2, int);
1896 1896
1897 for (n = 0; n < npoints * 2; n++) { 1897 for (n = 0; n < npoints * 2; n++) {
1898 /* hack to accomodate rockbox's concave polygon drawing */ 1898 a = 2.0 * PI * ((double)n / ((double)npoints * 2.0)) + angle_offset;
1899 a = 2.0 * PI * ((double)n / ((double)npoints * 2.0)) + angle_offset - PI / npoints; 1899 r = (n % 2) ? (double)rad/2.0 : (double)rad;
1900 r = (n % 2 == 0) ? (double)rad/2.0 : (double)rad;
1901 1900
1902 /* We're rotating the point at (0, -r) by a degrees */ 1901 /* We're rotating the point at (0, -r) by a degrees */
1903 coords[2*n+0] = cx + (int)( r * sin(a)); 1902 coords[2*n+0] = cx + (int)( r * sin(a));
diff --git a/apps/plugins/puzzles/src/twiddle.c b/apps/plugins/puzzles/src/twiddle.c
index b3aa06f1d5..49d8db7825 100644
--- a/apps/plugins/puzzles/src/twiddle.c
+++ b/apps/plugins/puzzles/src/twiddle.c
@@ -532,18 +532,6 @@ static void free_game(game_state *state)
532 sfree(state); 532 sfree(state);
533} 533}
534 534
535static int compare_int(const void *av, const void *bv)
536{
537 const int *a = (const int *)av;
538 const int *b = (const int *)bv;
539 if (*a < *b)
540 return -1;
541 else if (*a > *b)
542 return +1;
543 else
544 return 0;
545}
546
547static char *solve_game(const game_state *state, const game_state *currstate, 535static char *solve_game(const game_state *state, const game_state *currstate,
548 const char *aux, const char **error) 536 const char *aux, const char **error)
549{ 537{
@@ -758,7 +746,7 @@ static game_state *execute_move(const game_state *from, const char *move)
758 * conveniently being able to get hold of a clean state from 746 * conveniently being able to get hold of a clean state from
759 * which to practise manoeuvres. 747 * which to practise manoeuvres.
760 */ 748 */
761 qsort(ret->grid, ret->w*ret->h, sizeof(int), compare_int); 749 qsort(ret->grid, ret->w*ret->h, sizeof(int), compare_integers);
762 for (i = 0; i < ret->w*ret->h; i++) 750 for (i = 0; i < ret->w*ret->h; i++)
763 ret->grid[i] &= ~3; 751 ret->grid[i] &= ~3;
764 ret->used_solve = true; 752 ret->used_solve = true;
diff --git a/apps/plugins/puzzles/src/unequal.c b/apps/plugins/puzzles/src/unequal.c
index 008dab3a0b..ab32e4ab79 100644
--- a/apps/plugins/puzzles/src/unequal.c
+++ b/apps/plugins/puzzles/src/unequal.c
@@ -1844,18 +1844,18 @@ static void draw_gt(drawing *dr, int ox, int oy,
1844{ 1844{
1845 int coords[12]; 1845 int coords[12];
1846 int xdx = (dx1+dx2 ? 0 : 1), xdy = (dx1+dx2 ? 1 : 0); 1846 int xdx = (dx1+dx2 ? 0 : 1), xdy = (dx1+dx2 ? 1 : 0);
1847 coords[0] = ox + xdx + dx1; 1847 coords[0] = ox + xdx;
1848 coords[1] = oy + xdy + dy1; 1848 coords[1] = oy + xdy;
1849 coords[2] = ox + xdx + dx1 + dx2; 1849 coords[2] = ox + xdx + dx1;
1850 coords[3] = oy + xdy + dy1 + dy2; 1850 coords[3] = oy + xdy + dy1;
1851 coords[4] = ox - xdx + dx1 + dx2; 1851 coords[4] = ox + xdx + dx1 + dx2;
1852 coords[5] = oy - xdy + dy1 + dy2; 1852 coords[5] = oy + xdy + dy1 + dy2;
1853 coords[6] = ox - xdx + dx1; 1853 coords[6] = ox - xdx + dx1 + dx2;
1854 coords[7] = oy - xdy + dy1; 1854 coords[7] = oy - xdy + dy1 + dy2;
1855 coords[8] = ox - xdx; 1855 coords[8] = ox - xdx + dx1;
1856 coords[9] = oy - xdy; 1856 coords[9] = oy - xdy + dy1;
1857 coords[10] = ox + xdx; 1857 coords[10] = ox - xdx;
1858 coords[11] = oy + xdy; 1858 coords[11] = oy - xdy;
1859 draw_polygon(dr, coords, 6, col, col); 1859 draw_polygon(dr, coords, 6, col, col);
1860} 1860}
1861 1861