Stop the analysis pass in Loopy's redraw routine from being
[sgt/puzzles] / devel.but
index 134cf13..970a73a 100644 (file)
--- a/devel.but
+++ b/devel.but
@@ -856,7 +856,7 @@ producing new \c{game_state}s.
 \S{backend-interpret-move} \cw{interpret_move()}
 
 \c char *(*interpret_move)(game_state *state, game_ui *ui,
 \S{backend-interpret-move} \cw{interpret_move()}
 
 \c char *(*interpret_move)(game_state *state, game_ui *ui,
-\c                         game_drawstate *ds,
+\c                         const game_drawstate *ds,
 \c                         int x, int y, int button);
 
 This function receives user input and processes it. Its input
 \c                         int x, int y, int button);
 
 This function receives user input and processes it. Its input
@@ -868,6 +868,11 @@ indicating an arrow or function key or a mouse event; when
 coordinates of the mouse pointer relative to the top left of the
 puzzle's drawing area.
 
 coordinates of the mouse pointer relative to the top left of the
 puzzle's drawing area.
 
+(The pointer to the \c{game_drawstate} is marked \c{const}, because
+\c{interpret_move} should not write to it. The normal use of that
+pointer will be to read the game's tile size parameter in order to
+divide mouse coordinates by it.)
+
 \cw{interpret_move()} may return in three different ways:
 
 \b Returning \cw{NULL} indicates that no action whatsoever occurred
 \cw{interpret_move()} may return in three different ways:
 
 \b Returning \cw{NULL} indicates that no action whatsoever occurred
@@ -1224,6 +1229,34 @@ a mine from the colour it uses when you complete the game. In order
 to achieve this, its \cw{flash_length()} function has to store a
 flag in the \c{game_ui} to indicate which flash type is required.)
 
 to achieve this, its \cw{flash_length()} function has to store a
 flag in the \c{game_ui} to indicate which flash type is required.)
 
+\S{backend-status} \cw{status()}
+
+\c int (*status)(game_state *state);
+
+This function returns a status value indicating whether the current
+game is still in play, or has been won, or has been conclusively lost.
+The mid-end uses this to implement \cw{midend_status()}
+(\k{midend-status}).
+
+The return value should be +1 if the game has been successfully
+solved. If the game has been lost in a situation where further play is
+unlikely, the return value should be -1. If neither is true (so play
+is still ongoing), return zero.
+
+Front ends may wish to use a non-zero status as a cue to proactively
+offer the option of starting a new game. Therefore, back ends should
+not return -1 if the game has been \e{technically} lost but undoing
+and continuing is still a realistic possibility.
+
+(For instance, games with hidden information such as Guess or Mines
+might well return a non-zero status whenever they reveal the solution,
+whether or not the player guessed it correctly, on the grounds that a
+player would be unlikely to hide the solution and continue playing
+after the answer was spoiled. On the other hand, games where you can
+merely get into a dead end such as Same Game or Inertia might choose
+to return 0 in that situation, on the grounds that the player would
+quite likely press Undo and carry on playing.)
+
 \S{backend-redraw} \cw{redraw()}
 
 \c void (*redraw)(drawing *dr, game_drawstate *ds,
 \S{backend-redraw} \cw{redraw()}
 
 \c void (*redraw)(drawing *dr, game_drawstate *ds,
@@ -1433,7 +1466,7 @@ them internally. (There are currently no puzzles which have a
 one-line ASCII representation, so there's no precedent yet for
 whether that should come with a newline or not.)
 
 one-line ASCII representation, so there's no precedent yet for
 whether that should come with a newline or not.)
 
-\S{backend-wants-statusbar} \cw{wants_statusbar()}
+\S{backend-wants-statusbar} \cw{wants_statusbar}
 
 \c int wants_statusbar;
 
 
 \c int wants_statusbar;
 
@@ -1641,6 +1674,43 @@ end does any drawing it informs the front end of which parts of the
 window it has accessed, and hence which parts need repainting. This
 is done by calling \cw{draw_update()} (\k{drawing-draw-update}).
 
 window it has accessed, and hence which parts need repainting. This
 is done by calling \cw{draw_update()} (\k{drawing-draw-update}).
 
+Persistence of old drawing is convenient. However, a puzzle should
+be very careful about how it updates its drawing area. The problem
+is that some front ends do anti-aliased drawing: rather than simply
+choosing between leaving each pixel untouched or painting it a
+specified colour, an antialiased drawing function will \e{blend} the
+original and new colours in pixels at a figure's boundary according
+to the proportion of the pixel occupied by the figure (probably
+modified by some heuristic fudge factors). All of this produces a
+smoother appearance for curves and diagonal lines.
+
+An unfortunate effect of drawing an anti-aliased figure repeatedly
+is that the pixels around the figure's boundary come steadily more
+saturated with \q{ink} and the boundary appears to \q{spread out}.
+Worse, redrawing a figure in a different colour won't fully paint
+over the old boundary pixels, so the end result is a rather ugly
+smudge.
+
+A good strategy to avoid unpleasant anti-aliasing artifacts is to
+identify a number of rectangular areas which need to be redrawn,
+clear them to the background colour, and then redraw their contents
+from scratch, being careful all the while not to stray beyond the
+boundaries of the original rectangles. The \cw{clip()} function
+(\k{drawing-clip}) comes in very handy here. Games based on a square
+grid can often do this fairly easily. Other games may need to be
+somewhat more careful. For example, Loopy's redraw function first
+identifies portions of the display which need to be updated. Then,
+if the changes are fairly well localised, it clears and redraws a
+rectangle containing each changed area. Otherwise, it gives up and
+redraws the entire grid from scratch.
+
+It is possible to avoid clearing to background and redrawing from
+scratch if one is very careful about which drawing functions one
+uses: if a function is documented as not anti-aliasing under some
+circumstances, you can rely on each pixel in a drawing either being
+left entirely alone or being set to the requested colour, with no
+blending being performed.
+
 In the following sections I first discuss the drawing API as seen by
 the back end, and then the \e{almost} identical function-pointer
 form seen by the front end.
 In the following sections I first discuss the drawing API as seen by
 the back end, and then the \e{almost} identical function-pointer
 form seen by the front end.
@@ -1715,8 +1785,9 @@ the back end function \cw{colours()} (\k{backend-colours}).
 
 Some platforms may perform anti-aliasing on this function.
 Therefore, do not assume that you can erase a line by drawing the
 
 Some platforms may perform anti-aliasing on this function.
 Therefore, do not assume that you can erase a line by drawing the
-same line over it in the background colour; anti-aliasing might
-lead to perceptible ghost artefacts around the vanished line.
+same line over it in the background colour; anti-aliasing might lead
+to perceptible ghost artefacts around the vanished line. Horizontal
+and vertical lines, however, are pixel-perfect and not anti-aliased.
 
 This function may be used for both drawing and printing.
 
 
 This function may be used for both drawing and printing.
 
@@ -1753,7 +1824,8 @@ same polygon over it in the background colour. Also, be prepared for
 the polygon to extend a pixel beyond its obvious bounding box as a
 result of this; if you really need it not to do this to avoid
 interfering with other delicate graphics, you should probably use
 the polygon to extend a pixel beyond its obvious bounding box as a
 result of this; if you really need it not to do this to avoid
 interfering with other delicate graphics, you should probably use
-\cw{clip()} (\k{drawing-clip}).
+\cw{clip()} (\k{drawing-clip}). You can rely on horizontal and
+vertical lines not being anti-aliased.
 
 This function may be used for both drawing and printing.
 
 
 This function may be used for both drawing and printing.
 
@@ -1795,6 +1867,32 @@ interfering with other delicate graphics, you should probably use
 
 This function may be used for both drawing and printing.
 
 
 This function may be used for both drawing and printing.
 
+\S{drawing-draw-thick-line} \cw{draw_thick_line()}
+
+\c void draw_thick_line(drawing *dr, float thickness,
+\c                      float x1, float y1, float x2, float y2,
+\c                      int colour)
+
+Draws a line in the puzzle window, giving control over the line's
+thickness.
+
+\c{x1} and \c{y1} give the coordinates of one end of the line.
+\c{x2} and \c{y2} give the coordinates of the other end.
+\c{thickness} gives the thickness of the line, in pixels.
+
+Note that the coordinates and thickness are floating-point: the
+continuous coordinate system is in effect here. It's important to
+be able to address points with better-than-pixel precision in this
+case, because one can't otherwise properly express the endpoints of
+lines with both odd and even thicknesses.
+
+Some platforms may perform anti-aliasing on this function. The
+precise pixels affected by a thick-line drawing operation may vary
+between platforms, and no particular guarantees are provided.
+Indeed, even horizontal or vertical lines may be anti-aliased.
+
+This function may be used for both drawing and printing.
+
 \S{drawing-draw-text} \cw{draw_text()}
 
 \c void draw_text(drawing *dr, int x, int y, int fonttype,
 \S{drawing-draw-text} \cw{draw_text()}
 
 \c void draw_text(drawing *dr, int x, int y, int fonttype,
@@ -1848,6 +1946,54 @@ the back end function \cw{colours()} (\k{backend-colours}).
 
 This function may be used for both drawing and printing.
 
 
 This function may be used for both drawing and printing.
 
+The character set used to encode the text passed to this function is
+specified \e{by the drawing object}, although it must be a superset
+of ASCII. If a puzzle wants to display text that is not contained in
+ASCII, it should use the \cw{text_fallback()} function
+(\k{drawing-text-fallback}) to query the drawing object for an
+appropriate representation of the characters it wants.
+
+\S{drawing-text-fallback} \cw{text_fallback()}
+
+\c char *text_fallback(drawing *dr, const char *const *strings,
+\c                     int nstrings);
+
+This function is used to request a translation of UTF-8 text into
+whatever character encoding is expected by the drawing object's
+implementation of \cw{draw_text()}.
+
+The input is a list of strings encoded in UTF-8: \cw{nstrings} gives
+the number of strings in the list, and \cw{strings[0]},
+\cw{strings[1]}, ..., \cw{strings[nstrings-1]} are the strings
+themselves.
+
+The returned string (which is dynamically allocated and must be
+freed when finished with) is derived from the first string in the
+list that the drawing object expects to be able to display reliably;
+it will consist of that string translated into the character set
+expected by \cw{draw_text()}.
+
+Drawing implementations are not required to handle anything outside
+ASCII, but are permitted to assume that \e{some} string will be
+successfully translated. So every call to this function must include
+a string somewhere in the list (presumably the last element) which
+consists of nothing but ASCII, to be used by any front end which
+cannot handle anything else.
+
+For example, if a puzzle wished to display a string including a
+multiplication sign (U+00D7 in Unicode, represented by the bytes C3
+97 in UTF-8), it might do something like this:
+
+\c static const char *const times_signs[] = { "\xC3\x97", "x" };
+\c char *times_sign = text_fallback(dr, times_signs, 2);
+\c sprintf(buffer, "%d%s%d", width, times_sign, height);
+\c draw_text(dr, x, y, font, size, align, colour, buffer);
+\c sfree(buffer);
+
+which would draw a string with a times sign in the middle on
+platforms that support it, and fall back to a simple ASCII \cq{x}
+where there was no alternative.
+
 \S{drawing-clip} \cw{clip()}
 
 \c void clip(drawing *dr, int x, int y, int w, int h);
 \S{drawing-clip} \cw{clip()}
 
 \c void clip(drawing *dr, int x, int y, int w, int h);
@@ -1863,7 +2009,9 @@ inclusive. (These are exactly the same semantics as
 
 After this call, no drawing operation will affect anything outside
 the specified rectangle. The effect can be reversed by calling
 
 After this call, no drawing operation will affect anything outside
 the specified rectangle. The effect can be reversed by calling
-\cw{unclip()} (\k{drawing-unclip}).
+\cw{unclip()} (\k{drawing-unclip}). The clipping rectangle is
+pixel-perfect: pixels within the rectangle are affected as usual by
+drawing functions; pixels outside are completely untouched.
 
 Back ends should not assume that a clipping rectangle will be
 automatically cleared up by the front end if it's left lying around;
 
 Back ends should not assume that a clipping rectangle will be
 automatically cleared up by the front end if it's left lying around;
@@ -2143,6 +2291,22 @@ however, that it is a hint only: the central printing system may
 choose to vary line thicknesses at user request or due to printer
 capabilities.
 
 choose to vary line thicknesses at user request or due to printer
 capabilities.
 
+\S{print-line-dotted} \cw{print_line_dotted()}
+
+\c void print_line_dotted(drawing *dr, int dotted);
+
+This function is called to toggle the drawing of dotted lines during
+printing. It is not supported during drawing.
+
+The parameter \cq{dotted} is a boolean; \cw{TRUE} means that future
+lines drawn by \cw{draw_line()}, \cw{draw_circle} and
+\cw{draw_polygon()} will be dotted, and \cw{FALSE} means that they
+will be solid.
+
+Some front ends may impose restrictions on the width of dotted
+lines. Asking for a dotted line via this front end will override any
+line width request if the front end requires it.
+
 \H{drawing-frontend} The drawing API as implemented by the front end
 
 This section describes the drawing API in the function-pointer form
 \H{drawing-frontend} The drawing API as implemented by the front end
 
 This section describes the drawing API in the function-pointer form
@@ -2201,6 +2365,20 @@ function; see \k{drawing-draw-polygon}.
 This function behaves exactly like the back end \cw{draw_circle()}
 function; see \k{drawing-draw-circle}.
 
 This function behaves exactly like the back end \cw{draw_circle()}
 function; see \k{drawing-draw-circle}.
 
+\S{drawingapi-draw-thick-line} \cw{draw_thick_line()}
+
+\c void draw_thick_line(drawing *dr, float thickness,
+\c                      float x1, float y1, float x2, float y2,
+\c                      int colour)
+
+This function behaves exactly like the back end
+\cw{draw_thick_line()} function; see \k{drawing-draw-thick-line}.
+
+An implementation of this API which doesn't provide high-quality
+rendering of thick lines is permitted to define this function
+pointer to be \cw{NULL}. The middleware in \cw{drawing.c} will notice
+and provide a low-quality alternative using \cw{draw_polygon()}.
+
 \S{drawingapi-draw-update} \cw{draw_update()}
 
 \c void (*draw_update)(void *handle, int x, int y, int w, int h);
 \S{drawingapi-draw-update} \cw{draw_update()}
 
 \c void (*draw_update)(void *handle, int x, int y, int w, int h);
@@ -2426,6 +2604,19 @@ Implementations of this API which do not provide printing services
 may define this function pointer to be \cw{NULL}; it will never be
 called unless printing is attempted.
 
 may define this function pointer to be \cw{NULL}; it will never be
 called unless printing is attempted.
 
+\S{drawingapi-text-fallback} \cw{text_fallback()}
+
+\c char *(*text_fallback)(void *handle, const char *const *strings,
+\c                        int nstrings);
+
+This function behaves exactly like the back end \cw{text_fallback()}
+function; see \k{drawing-text-fallback}.
+
+Implementations of this API which do not support any characters
+outside ASCII may define this function pointer to be \cw{NULL}, in
+which case the central code in \cw{drawing.c} will provide a default
+implementation.
+
 \H{drawingapi-frontend} The drawing API as called by the front end
 
 There are a small number of functions provided in \cw{drawing.c}
 \H{drawingapi-frontend} The drawing API as called by the front end
 
 There are a small number of functions provided in \cw{drawing.c}
@@ -2528,14 +2719,12 @@ without closing the window...)
 
 Frees a mid-end structure and all its associated data.
 
 
 Frees a mid-end structure and all its associated data.
 
-\H{midend-tilesize} 
+\H{midend-tilesize} \cw{midend_tilesize()}
 
 \c int midend_tilesize(midend *me);
 
 Returns the \cq{tilesize} parameter being used to display the
 
 \c int midend_tilesize(midend *me);
 
 Returns the \cq{tilesize} parameter being used to display the
-current puzzle.
-
-\k{backend-preferred-tilesize}
+current puzzle (\k{backend-preferred-tilesize}).
 
 \H{midend-set-params} \cw{midend_set_params()}
 
 
 \H{midend-set-params} \cw{midend_set_params()}
 
@@ -2620,6 +2809,9 @@ to use scroll bars for large puzzles), you can pass dimensions of
 \cw{INT_MAX} as input to this function. You should probably not do
 that \e{and} set the \c{user_size} flag, though!
 
 \cw{INT_MAX} as input to this function. You should probably not do
 that \e{and} set the \c{user_size} flag, though!
 
+The midend relies on the frontend calling \cw{midend_new_game()}
+(\k{midend-new-game}) before calling \cw{midend_size()}.
+
 \H{midend-new-game} \cw{midend_new_game()}
 
 \c void midend_new_game(midend *me);
 \H{midend-new-game} \cw{midend_new_game()}
 
 \c void midend_new_game(midend *me);
@@ -2656,7 +2848,8 @@ undo list (so that an accidental restart can be undone).
 
 This function automatically causes a redraw, i.e. the front end can
 expect its drawing API to be called from \e{within} a call to this
 
 This function automatically causes a redraw, i.e. the front end can
 expect its drawing API to be called from \e{within} a call to this
-function.
+function. Some back ends require that \cw{midend_size()}
+(\k{midend-size}) is called before \cw{midend_restart_game()}.
 
 \H{midend-force-redraw} \cw{midend_force_redraw()}
 
 
 \H{midend-force-redraw} \cw{midend_force_redraw()}
 
@@ -2667,7 +2860,8 @@ discarding the current \c{game_drawstate} and creating a new one
 from scratch before calling the game's \cw{redraw()} function.
 
 The front end can expect its drawing API to be called from within a
 from scratch before calling the game's \cw{redraw()} function.
 
 The front end can expect its drawing API to be called from within a
-call to this function.
+call to this function. Some back ends require that \cw{midend_size()}
+(\k{midend-size}) is called before \cw{midend_force_redraw()}.
 
 \H{midend-redraw} \cw{midend_redraw()}
 
 
 \H{midend-redraw} \cw{midend_redraw()}
 
@@ -2678,7 +2872,8 @@ calling the game's \cw{redraw()} function. (That is, the only things
 redrawn will be things that have changed since the last redraw.)
 
 The front end can expect its drawing API to be called from within a
 redrawn will be things that have changed since the last redraw.)
 
 The front end can expect its drawing API to be called from within a
-call to this function.
+call to this function. Some back ends require that \cw{midend_size()}
+(\k{midend-size}) is called before \cw{midend_redraw()}.
 
 \H{midend-process-key} \cw{midend_process_key()}
 
 
 \H{midend-process-key} \cw{midend_process_key()}
 
@@ -2936,7 +3131,40 @@ user.
 
 The front end can expect its drawing API and/or
 \cw{activate_timer()} to be called from within a call to this
 
 The front end can expect its drawing API and/or
 \cw{activate_timer()} to be called from within a call to this
-function.
+function.  Some back ends require that \cw{midend_size()}
+(\k{midend-size}) is called before \cw{midend_solve()}.
+
+\H{midend-status} \cw{midend_status()}
+
+\c int midend_status(midend *me);
+
+This function returns +1 if the midend is currently displaying a game
+in a solved state, -1 if the game is in a permanently lost state, or 0
+otherwise. This function just calls the back end's \cw{status()}
+function. Front ends may wish to use this as a cue to proactively
+offer the option of starting a new game.
+
+(See \k{backend-status} for more detail about the back end's
+\cw{status()} function and discussion of what should count as which
+status code.)
+
+\H{midend-can-undo} \cw{midend_can_undo()}
+
+\c int midend_can_undo(midend *me);
+
+Returns \cw{TRUE} if the midend is currently in a state where the undo
+operation is meaningful (i.e. at least one position exists on the undo
+chain before the present one). Front ends may wish to use this to
+visually activate and deactivate an undo button.
+
+\H{midend-can-redo} \cw{midend_can_redo()}
+
+\c int midend_can_redo(midend *me);
+
+Returns \cw{TRUE} if the midend is currently in a state where the redo
+operation is meaningful (i.e. at least one position exists on the redo
+chain after the present one). Front ends may wish to use this to
+visually activate and deactivate a redo button.
 
 \H{midend-serialise} \cw{midend_serialise()}
 
 
 \H{midend-serialise} \cw{midend_serialise()}