X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ian/git?a=blobdiff_plain;f=devel.but;h=af6920240119c4369a4a2b58024a1b53517a42e3;hb=3276376d1be74b66970b88c3e941dcedf8d22474;hp=c5a2c43aac29548063612c5720abc0b60183c96e;hpb=9fbb365684ef662fc183ebd45c2eeb50f40589b5;p=sgt-puzzles.git diff --git a/devel.but b/devel.but index c5a2c43..af69202 100644 --- a/devel.but +++ b/devel.but @@ -391,8 +391,9 @@ with the default values, and returns a pointer to it. \c int (*fetch_preset)(int i, char **name, game_params **params); -This function is used to populate the \q{Type} menu, which provides -a list of conveniently accessible preset parameters for most games. +This function is one of the two APIs a back end can provide to +populate the \q{Type} menu, which provides a list of conveniently +accessible preset parameters for most games. The function is called with \c{i} equal to the index of the preset required (numbering from zero). It returns \cw{FALSE} if that preset @@ -406,9 +407,36 @@ returns \cw{TRUE}. If the game does not wish to support any presets at all, this function is permitted to return \cw{FALSE} always. +If the game wants to return presets in the form of a hierarchical menu +instead of a flat list (and, indeed, even if it doesn't), then it may +set this function pointer to \cw{NULL}, and instead fill in the +alternative function pointer \cw{preset_menu} +(\k{backend-preset-menu}). + +\S{backend-preset-menu} \cw{preset_menu()} + +\c struct preset_menu *(*preset_menu)(void); + +This function is the more flexible of the two APIs by which a back end +can define a collection of preset game parameters. + +This function simply returns a complete menu hierarchy, in the form of +a \c{struct preset_menu} (see \k{midend-get-presets}) and further +submenus (if it wishes) dangling off it. There are utility functions +described in \k{utils-presets} to make it easy for the back end to +construct this menu. + +If the game has no need to return a hierarchy of menus, it may instead +opt to implement the \cw{fetch_preset()} function (see +\k{backend-fetch-preset}). + +The game need not fill in the \c{id} fields in the preset menu +structures. The mid-end will do that after it receives the structure +from the game, and before passing it on to the front end. + \S{backend-encode-params} \cw{encode_params()} -\c char *(*encode_params)(game_params *params, int full); +\c char *(*encode_params)(const game_params *params, int full); The job of this function is to take a \c{game_params}, and encode it in a string form for use in game IDs. The return value must be a @@ -493,7 +521,7 @@ allocations contained within it. \S{backend-dup-params} \cw{dup_params()} -\c game_params *(*dup_params)(game_params *params); +\c game_params *(*dup_params)(const game_params *params); This function allocates a new \c{game_params} structure and initialises it with an exact copy of the information in the one @@ -511,7 +539,7 @@ and \k{backend-custom-params} for more details. \S{backend-configure} \cw{configure()} -\c config_item *(*configure)(game_params *params); +\c config_item *(*configure)(const game_params *params); This function is called when the user requests a dialog box for custom parameter configuration. It returns a newly allocated array @@ -527,16 +555,15 @@ The \cw{config_item} structure contains the following elements: \c char *name; \c int type; -\c char *sval; -\c int ival; +\c union { /* type-specific fields */ } u; +\e iiiiiiiiiiiiiiiiiiiiiiiiii \c{name} is an ASCII string giving the textual label for a GUI control. It is \e{not} expected to be dynamically allocated. \c{type} contains one of a small number of \c{enum} values defining -what type of control is being described. The meaning of the \c{sval} -and \c{ival} fields depends on the value in \c{type}. The valid -values are: +what type of control is being described. The usable member of the +union field \c{u} depends on \c{type}. The valid type values are: \dt \c{C_STRING} @@ -544,38 +571,64 @@ values are: input. The back end does not bother informing the front end that the box is numeric rather than textual; some front ends do have the capacity to take this into account, but I decided it wasn't worth -the extra complexity in the interface.) For this type, \c{ival} is -unused, and \c{sval} contains a dynamically allocated string -representing the contents of the input box. +the extra complexity in the interface.) + +\lcont{ + +For controls of this type, \c{u.string} contains a single field + +\c char *sval; + +which stores a dynamically allocated string representing the contents +of the input box. + +} \dt \c{C_BOOLEAN} -\dd Describes a simple checkbox. For this type, \c{sval} is unused, -and \c{ival} is \cw{TRUE} or \cw{FALSE}. +\dd Describes a simple checkbox. + +\lcont{ + +For controls of this type, \c{u.boolean} contains a single field + +\c int bval; + +which is either \cw{TRUE} or \cw{FALSE}. + +} \dt \c{C_CHOICES} \dd Describes a drop-down list presenting one of a small number of -fixed choices. For this type, \c{sval} contains a list of strings -describing the choices; the very first character of \c{sval} is used -as a delimiter when processing the rest (so that the strings -\cq{:zero:one:two}, \cq{!zero!one!two} and \cq{xzeroxonextwo} all -define a three-element list containing \cq{zero}, \cq{one} and -\cq{two}). \c{ival} contains the index of the currently selected -element, numbering from zero (so that in the above example, 0 would -mean \cq{zero} and 2 would mean \cq{two}). +fixed choices. \lcont{ -Note that for this control type, \c{sval} is \e{not} dynamically -allocated, whereas it was for \c{C_STRING}. +For controls of this type, \c{u.choices} contains two fields: + +\c const char *choicenames; +\c int selected; + +\c{choicenames} contains a list of strings describing the choices. The +very first character of \c{sval} is used as a delimiter when +processing the rest (so that the strings \cq{:zero:one:two}, +\cq{!zero!one!two} and \cq{xzeroxonextwo} all define a three-element +list containing \cq{zero}, \cq{one} and \cq{two}). + +\c{selected} contains the index of the currently selected element, +numbering from zero (so that in the above example, 0 would mean +\cq{zero} and 2 would mean \cq{two}). + +Note that \c{u.choices.choicenames} is \e{not} dynamically allocated, +unlike \c{u.string.sval}. } \dt \c{C_END} -\dd Marks the end of the array of \c{config_item}s. All other fields -are unused. +\dd Marks the end of the array of \c{config_item}s. There is no +associated member of the union field \c{u} for this type. The array returned from this function is expected to have filled in the initial values of all the controls according to the input @@ -586,7 +639,7 @@ function is never called and need not do anything at all. \S{backend-custom-params} \cw{custom_params()} -\c game_params *(*custom_params)(config_item *cfg); +\c game_params *(*custom_params)(const config_item *cfg); This function is the counterpart to \cw{configure()} (\k{backend-configure}). It receives as input an array of @@ -611,7 +664,8 @@ function is never called and need not do anything at all. \S{backend-validate-params} \cw{validate_params()} -\c char *(*validate_params)(game_params *params, int full); +\c const char *(*validate_params)(const game_params *params, +\c int full); This function takes a \c{game_params} structure as input, and checks that the parameters described in it fall within sensible limits. (At @@ -655,7 +709,7 @@ a descriptive-format game ID. \S{backend-new-desc} \cw{new_desc()} -\c char *(*new_desc)(game_params *params, random_state *rs, +\c char *(*new_desc)(const game_params *params, random_state *rs, \c char **aux, int interactive); This function is where all the really hard work gets done. This is @@ -696,7 +750,8 @@ again in the game description. \S{backend-validate-desc} \cw{validate_desc()} -\c char *(*validate_desc)(game_params *params, char *desc); +\c const char *(*validate_desc)(const game_params *params, +\c const char *desc); This function is given a game description, and its job is to validate that it describes a puzzle which makes sense. @@ -720,8 +775,8 @@ non-dynamically-allocated C string containing an error message. \S{backend-new-game} \cw{new_game()} -\c game_state *(*new_game)(midend *me, game_params *params, -\c char *desc); +\c game_state *(*new_game)(midend *me, const game_params *params, +\c const char *desc); This function takes a game description as input, together with its accompanying \c{game_params}, and constructs a \c{game_state} @@ -749,7 +804,7 @@ game states and it had to go in one section or the other.) \S{backend-dup-game} \cw{dup_game()} -\c game_state *(*dup_game)(game_state *state); +\c game_state *(*dup_game)(const game_state *state); This function allocates a new \c{game_state} structure and initialises it with an exact copy of the information in the one @@ -766,7 +821,7 @@ allocations contained within it. \S{backend-new-ui} \cw{new_ui()} -\c game_ui *(*new_ui)(game_state *state); +\c game_ui *(*new_ui)(const game_state *state); This function allocates and returns a new \c{game_ui} structure for playing a particular puzzle. It is passed a pointer to the initial @@ -782,7 +837,7 @@ allocations contained within it. \S{backend-encode-ui} \cw{encode_ui()} -\c char *(*encode_ui)(game_ui *ui); +\c char *(*encode_ui)(const game_ui *ui); This function encodes any \e{important} data in a \c{game_ui} structure in string form. It is only called when saving a @@ -806,7 +861,7 @@ source.) \S{backend-decode-ui} \cw{decode_ui()} -\c void (*decode_ui)(game_ui *ui, char *encoding); +\c void (*decode_ui)(game_ui *ui, const char *encoding); This function parses a string previously output by \cw{encode_ui()}, and writes the decoded data back into the provided \c{game_ui} @@ -814,8 +869,8 @@ structure. \S{backend-changed-state} \cw{changed_state()} -\c void (*changed_state)(game_ui *ui, game_state *oldstate, -\c game_state *newstate); +\c void (*changed_state)(game_ui *ui, const game_state *oldstate, +\c const game_state *newstate); This function is called by the mid-end whenever the current game state changes, for any reason. Those reasons include: @@ -855,8 +910,8 @@ producing new \c{game_state}s. \S{backend-interpret-move} \cw{interpret_move()} -\c char *(*interpret_move)(game_state *state, game_ui *ui, -\c game_drawstate *ds, +\c char *(*interpret_move)(const game_state *state, game_ui *ui, +\c const game_drawstate *ds, \c int x, int y, int button); This function receives user input and processes it. Its input @@ -868,16 +923,21 @@ 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. +(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 in response to the input event; the puzzle was not interested in it at all. -\b Returning the empty string (\cw{""}) indicates that the input +\b Returning the special value \cw{UI_UPDATE} indicates that the input event has resulted in a change being made to the \c{game_ui} which -will require a redraw of the game window, but that no actual -\e{move} was made (i.e. no new \c{game_state} needs to be created). +will require a redraw of the game window, but that no actual \e{move} +was made (i.e. no new \c{game_state} needs to be created). \b Returning anything else indicates that a move was made and that a new \c{game_state} must be created. However, instead of actually @@ -892,7 +952,7 @@ strings can be written to disk when saving the game and fed to The return value from \cw{interpret_move()} is expected to be dynamically allocated if and only if it is not either \cw{NULL} -\e{or} the empty string. +\e{or} the special string constant \c{UI_UPDATE}. After this function is called, the back end is permitted to rely on some subsequent operations happening in sequence: @@ -970,7 +1030,7 @@ any input value. \S{backend-execute-move} \cw{execute_move()} -\c game_state *(*execute_move)(game_state *state, char *move); +\c game_state *(*execute_move)(const game_state *state, char *move); This function takes an input \c{game_state} and a move string as output from \cw{interpret_move()}. It returns a newly allocated @@ -994,8 +1054,8 @@ not even offer the \q{Solve} menu option. \S{backend-solve} \cw{solve()} -\c char *(*solve)(game_state *orig, game_state *curr, -\c char *aux, char **error); +\c char *(*solve)(const game_state *orig, const game_state *curr, +\c const char *aux, const char **error); This function is called when the user selects the \q{Solve} option from the menu. @@ -1015,9 +1075,11 @@ it may return \cw{NULL}. If it does this, it must also set \q{Solution not known for this puzzle}); that error message is not expected to be dynamically allocated. -If this function \e{does} produce a solution, it returns a move -string suitable for feeding to \cw{execute_move()} -(\k{backend-execute-move}). +If this function \e{does} produce a solution, it returns a move string +suitable for feeding to \cw{execute_move()} +(\k{backend-execute-move}). Like a (non-empty) string returned from +\cw{interpret_move()}, the returned string should be dynamically +allocated. \H{backend-drawing} Drawing the game graphics @@ -1026,7 +1088,8 @@ drawing. \S{backend-new-drawstate} \cw{new_drawstate()} -\c game_drawstate *(*new_drawstate)(drawing *dr, game_state *state); +\c game_drawstate *(*new_drawstate)(drawing *dr, +\c const game_state *state); This function allocates and returns a new \c{game_drawstate} structure for drawing a particular puzzle. It is passed a pointer to @@ -1076,7 +1139,7 @@ requesting a resize if that ever gets implemented). \S{backend-compute-size} \cw{compute_size()} -\c void (*compute_size)(game_params *params, int tilesize, +\c void (*compute_size)(const game_params *params, int tilesize, \c int *x, int *y); This function is passed a \c{game_params} structure and a tile size. @@ -1087,7 +1150,7 @@ at that tile size. \S{backend-set-size} \cw{set_size()} \c void (*set_size)(drawing *dr, game_drawstate *ds, -\c game_params *params, int tilesize); +\c const game_params *params, int tilesize); This function is responsible for setting up a \c{game_drawstate} to draw at a given tile size. Typically this will simply involve @@ -1136,7 +1199,8 @@ colour allocation policy. \S{backend-anim-length} \cw{anim_length()} -\c float (*anim_length)(game_state *oldstate, game_state *newstate, +\c float (*anim_length)(const game_state *oldstate, +\c const game_state *newstate, \c int dir, game_ui *ui); This function is called when a move is made, undone or redone. It is @@ -1180,7 +1244,8 @@ state. \S{backend-flash-length} \cw{flash_length()} -\c float (*flash_length)(game_state *oldstate, game_state *newstate, +\c float (*flash_length)(const game_state *oldstate, +\c const game_state *newstate, \c int dir, game_ui *ui); This function is called when a move is completed. (\q{Completed} @@ -1224,11 +1289,41 @@ 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.) +\S{backend-status} \cw{status()} + +\c int (*status)(const 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, -\c game_state *oldstate, game_state *newstate, int dir, -\c game_ui *ui, float anim_time, float flash_time); +\c const game_state *oldstate, +\c const game_state *newstate, +\c int dir, const game_ui *ui, +\c float anim_time, float flash_time); This function is responsible for actually drawing the contents of the game window, and for redrawing every time the game state or the @@ -1300,7 +1395,7 @@ ignored. \S{backend-print-size} \cw{print_size()} -\c void (*print_size)(game_params *params, float *x, float *y); +\c void (*print_size)(const game_params *params, float *x, float *y); This function is passed a \c{game_params} structure and a tile size. It returns, in \c{*x} and \c{*y}, the preferred size in @@ -1311,7 +1406,7 @@ called. \S{backend-print} \cw{print()} -\c void (*print)(drawing *dr, game_state *state, int tilesize); +\c void (*print)(drawing *dr, const game_state *state, int tilesize); This function is called when a puzzle is to be printed out on paper. It should use the drawing API functions (see \k{drawing}) to print @@ -1386,7 +1481,7 @@ and \cw{text_format()} (\k{backend-text-format}) are never called. \S{backend-can-format-as-text-now} \c{can_format_as_text_now()} -\c int (*can_format_as_text_now)(game_params *params); +\c int (*can_format_as_text_now)(const game_params *params); This function is passed a \c{game_params} and returns a boolean, which is \cw{TRUE} if the game can support ASCII text output for @@ -1412,7 +1507,7 @@ the game can be copied to the clipboard. Only the actual visible \S{backend-text-format} \cw{text_format()} -\c char *(*text_format)(game_state *state); +\c char *(*text_format)(const game_state *state); This function is passed a \c{game_state}, and returns a newly allocated C string containing an ASCII representation of that game @@ -1433,7 +1528,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.) -\S{backend-wants-statusbar} \cw{wants_statusbar()} +\S{backend-wants-statusbar} \cw{wants_statusbar} \c int wants_statusbar; @@ -1453,7 +1548,7 @@ called and need not do anything. \S{backend-timing-state} \cw{timing_state()} -\c int (*timing_state)(game_state *state, game_ui *ui); +\c int (*timing_state)(const game_state *state, game_ui *ui); This function is passed the current \c{game_state} and the local \c{game_ui}; it returns \cw{TRUE} if the game timer should currently @@ -1641,6 +1736,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}). +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. @@ -1715,8 +1847,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 -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. @@ -1753,7 +1886,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 -\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. @@ -1795,10 +1929,40 @@ interfering with other delicate graphics, you should probably use 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. + +If the specified thickness is less than 1.0, 1.0 is used. +This ensures that thin lines are visible even at small scales. + \S{drawing-draw-text} \cw{draw_text()} \c void draw_text(drawing *dr, int x, int y, int fonttype, -\c int fontsize, int align, int colour, char *text); +\c int fontsize, int align, int colour, +\c const char *text); Draws text in the puzzle window. @@ -1911,7 +2075,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 -\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; @@ -1957,7 +2123,7 @@ printing routines, that code may safely call \cw{draw_update()}.) \S{drawing-status-bar} \cw{status_bar()} -\c void status_bar(drawing *dr, char *text); +\c void status_bar(drawing *dr, const char *text); Sets the text in the game's status bar to \c{text}. The text is copied from the supplied buffer, so the caller is free to deallocate or @@ -2228,7 +2394,8 @@ function \cw{drawing_new()} (see \k{drawing-new}). \S{drawingapi-draw-text} \cw{draw_text()} \c void (*draw_text)(void *handle, int x, int y, int fonttype, -\c int fontsize, int align, int colour, char *text); +\c int fontsize, int align, int colour, +\c const char *text); This function behaves exactly like the back end \cw{draw_text()} function; see \k{drawing-draw-text}. @@ -2265,6 +2432,20 @@ function; see \k{drawing-draw-polygon}. 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); @@ -2317,7 +2498,7 @@ called unless drawing is attempted. \S{drawingapi-status-bar} \cw{status_bar()} -\c void (*status_bar)(void *handle, char *text); +\c void (*status_bar)(void *handle, const char *text); This function behaves exactly like the back end \cw{status_bar()} function; see \k{drawing-status-bar}. @@ -2605,14 +2786,12 @@ without closing the window...) 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 -current puzzle. - -\k{backend-preferred-tilesize} +current puzzle (\k{backend-preferred-tilesize}). \H{midend-set-params} \cw{midend_set_params()} @@ -2624,8 +2803,8 @@ these parameters until further notice. The usual way in which the front end will have an actual \c{game_params} structure to pass to this function is if it had -previously got it from \cw{midend_fetch_preset()} -(\k{midend-fetch-preset}). Thus, this function is usually called in +previously got it from \cw{midend_get_presets()} +(\k{midend-get-presets}). Thus, this function is usually called in response to the user making a selection from the presets menu. \H{midend-get-params} \cw{midend_get_params()} @@ -2697,6 +2876,25 @@ 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! +The midend relies on the frontend calling \cw{midend_new_game()} +(\k{midend-new-game}) before calling \cw{midend_size()}. + +\H{midend-reset-tilesize} \cw{midend_reset_tilesize()} + +\c void midend_reset_tilesize(midend *me); + +This function resets the midend's preferred tile size to that of the +standard puzzle. + +As discussed in \k{midend-size}, puzzle resizes are typically +'sticky', in that once the user has dragged the puzzle to a different +window size, the resulting tile size will be remembered and used when +the puzzle configuration changes. If you \e{don't} want that, e.g. if +you want to provide a command to explicitly reset the puzzle size back +to its default, then you can call this just before calling +\cw{midend_size()} (which, in turn, you would probably call with +\c{user_size} set to \cw{FALSE}). + \H{midend-new-game} \cw{midend_new_game()} \c void midend_new_game(midend *me); @@ -2733,7 +2931,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 -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()} @@ -2744,7 +2943,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 -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()} @@ -2755,7 +2955,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 -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()} @@ -2825,34 +3026,63 @@ One of the major purposes of timing in the mid-end is to perform move animation. Therefore, calling this function is very likely to result in calls back to the front end's drawing API. -\H{midend-num-presets} \cw{midend_num_presets()} +\H{midend-get-presets} \cw{midend_get_presets()} -\c int midend_num_presets(midend *me); +\c struct preset_menu *midend_get_presets(midend *me, int *id_limit); -Returns the number of game parameter presets supplied by this game. -Front ends should use this function and \cw{midend_fetch_preset()} -to configure their presets menu rather than calling the back end -directly, since the mid-end adds standard customisation facilities. -(At the time of writing, those customisation facilities are -implemented hackily by means of environment variables, but it's not -impossible that they may become more full and formal in future.) +Returns a data structure describing this game's collection of preset +game parameters, organised into a hierarchical structure of menus and +submenus. -\H{midend-fetch-preset} \cw{midend_fetch_preset()} +The return value is a pointer to a data structure containing the +following fields (among others, which are not intended for front end +use): -\c void midend_fetch_preset(midend *me, int n, -\c char **name, game_params **params); +\c struct preset_menu { +\c int n_entries; +\c struct preset_menu_entry *entries; +\c /* and other things */ +\e iiiiiiiiiiiiiiiiiiiiii +\c }; -Returns one of the preset game parameter structures for the game. On -input \c{n} must be a non-negative integer and less than the value -returned from \cw{midend_num_presets()}. On output, \c{*name} is set -to an ASCII string suitable for entering in the game's presets menu, -and \c{*params} is set to the corresponding \c{game_params} -structure. +Those fields describe the intended contents of one particular menu in +the hierarchy. \cq{entries} points to an array of \cq{n_entries} +items, each of which is a structure containing the following fields: + +\c struct preset_menu_entry { +\c char *title; +\c game_params *params; +\c struct preset_menu *submenu; +\c int id; +\c }; -Both of the two output values are dynamically allocated, but they -are owned by the mid-end structure: the front end should not ever -free them directly, because they will be freed automatically during -\cw{midend_free()}. +Of these fields, \cq{title} and \cq{id} are present in every entry, +giving (respectively) the textual name of the menu item and an integer +identifier for it. The integer id will correspond to the one returned +by \c{midend_which_preset} (\k{midend-which-preset}), when that preset +is the one selected. + +The other two fields are mutually exclusive. Each \c{struct +preset_menu_entry} will have one of those fields \cw{NULL} and the +other one non-null. If the menu item is an actual preset, then +\cq{params} will point to the set of game parameters that go with the +name; if it's a submenu, then \cq{submenu} instead will be non-null, +and will point at a subsidiary \c{struct preset_menu}. + +The complete hierarchy of these structures is owned by the mid-end, +and will be freed when the mid-end is freed. The front end should not +attempt to free any of it. + +The integer identifiers will be allocated densely from 0 upwards, so +that it's reasonable for the front end to allocate an array which uses +them as indices, if it needs to store information per preset menu +item. For this purpose, the front end may pass the second parameter +\cq{id_limit} to \cw{midend_get_presets} as the address of an \c{int} +variable, into which \cw{midend_get_presets} will write an integer one +larger than the largest id number actually used (i.e. the number of +elements the front end would need in the array). + +Submenu-type entries also have integer identifiers. \H{midend-which-preset} \cw{midend_which_preset()} @@ -2864,6 +3094,10 @@ no preset matches. Front ends could use this to maintain a tick beside one of the items in the menu (or tick the \q{Custom} option if the return value is less than zero). +The returned index value (if non-negative) will match the \c{id} field +of the corresponding \cw{struct preset_menu_entry} returned by +\c{midend_get_presets()} (\k{midend-get-presets}). + \H{midend-wants-statusbar} \cw{midend_wants_statusbar()} \c int midend_wants_statusbar(midend *me); @@ -2924,8 +3158,8 @@ will probably need to pass it to \cw{midend_set_config}.) \H{midend-set-config} \cw{midend_set_config()} -\c char *midend_set_config(midend *me, int which, -\c config_item *cfg); +\c const char *midend_set_config(midend *me, int which, +\c config_item *cfg); Passes the mid-end the results of a configuration dialog box. \c{which} should have the same value which it had when @@ -2946,7 +3180,7 @@ using \cw{midend_size()} and eventually perform a refresh using \H{midend-game-id} \cw{midend_game_id()} -\c char *midend_game_id(midend *me, char *id); +\c const char *midend_game_id(midend *me, const char *id); Passes the mid-end a string game ID (of any of the valid forms \cq{params}, \cq{params:description} or \cq{params#seed}) which the @@ -2973,6 +3207,17 @@ Returns a descriptive game ID (i.e. one in the form \cq{params:description}) describing the game currently active in the mid-end. The returned string is dynamically allocated. +\H{midend-get-random-seed} \cw{midend_get_random_seed()} + +\c char *midend_get_random_seed(midend *me) + +Returns a random game ID (i.e. one in the form \cq{params#seedstring}) +describing the game currently active in the mid-end, if there is one. +If the game was created by entering a description, no random seed will +currently exist and this function will return \cw{NULL}. + +The returned string, if it is non-\cw{NULL}, is dynamically allocated. + \H{midend-can-format-as-text-now} \cw{midend_can_format_as_text_now()} \c int midend_can_format_as_text_now(midend *me); @@ -3003,7 +3248,7 @@ conversion. \H{midend-solve} \cw{midend_solve()} -\c char *midend_solve(midend *me); +\c const char *midend_solve(midend *me); Requests the mid-end to perform a Solve operation. @@ -3013,7 +3258,40 @@ user. 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()} @@ -3042,9 +3320,8 @@ output string. \H{midend-deserialise} \cw{midend_deserialise()} -\c char *midend_deserialise(midend *me, -\c int (*read)(void *ctx, void *buf, int len), -\c void *rctx); +\c const char *midend_deserialise(midend *me, +\c int (*read)(void *ctx, void *buf, int len), void *rctx); This function is the counterpart to \cw{midend_serialise()}. It calls the supplied \cw{read} function repeatedly to read a quantity @@ -3072,12 +3349,62 @@ re-think the window size using \cw{midend_size()}, and probably cause a refresh using \cw{midend_redraw()}. Because each mid-end is tied to a specific game back end, this -function will fail if you attempt to read in a save file generated -by a different game from the one configured in this mid-end, even if -your application is a monolithic one containing all the puzzles. (It -would be pretty easy to write a function which would look at a save -file and determine which game it was for; any front end implementor -who needs such a function can probably be accommodated.) +function will fail if you attempt to read in a save file generated by +a different game from the one configured in this mid-end, even if your +application is a monolithic one containing all the puzzles. See +\k{identify-game} for a helper function which will allow you to +identify a save file before you instantiate your mid-end in the first +place. + +\H{identify-game} \cw{identify_game()} + +\c const char *identify_game(char **name, +\c int (*read)(void *ctx, void *buf, int len), void *rctx); + +This function examines a serialised midend stream, of the same kind +used by \cw{midend_serialise()} and \cw{midend_deserialise()}, and +returns the \cw{name} field of the game back end from which it was +saved. + +You might want this if your front end was a monolithic one containing +all the puzzles, and you wanted to be able to load an arbitrary save +file and automatically switch to the right game. Probably your next +step would be to iterate through \cw{gamelist} (\k{frontend-backend}) +looking for a game structure whose \cw{name} field matched the +returned string, and give an error if you didn't find one. + +On success, the return value of this function is \cw{NULL}, and the +game name string is written into \cw{*name}. The caller should free +that string after using it. + +On failure, \cw{*name} is \cw{NULL}, and the return value is an error +message (which does not need freeing at all). + +(This isn't strictly speaking a midend function, since it doesn't +accept or return a pointer to a midend. You'd probably call it just +\e{before} deciding what kind of midend you wanted to instantiate.) + +\H{midend-request-id-changes} \cw{midend_request_id_changes()} + +\c void midend_request_id_changes(midend *me, +\c void (*notify)(void *), void *ctx); + +This function is called by the front end to request notification by +the mid-end when the current game IDs (either descriptive or +random-seed) change. This can occur as a result of keypresses ('n' for +New Game, for example) or when a puzzle supersedes its game +description (see \k{backend-supersede}). After this function is +called, any change of the game ids will cause the mid-end to call +\cw{notify(ctx)} after the change. + +This is for use by puzzles which want to present the game description +to the user constantly (e.g. as an HTML hyperlink) instead of only +showing it when the user explicitly requests it. + +This is a function I anticipate few front ends needing to implement, +so I make it a callback rather than a static function in order to +relieve most front ends of the need to provide an empty +implementation. \H{frontend-backend} Direct reference to the back end structure by the front end @@ -3182,7 +3509,7 @@ calling \cw{midend_timer()}. \H{frontend-fatal} \cw{fatal()} -\c void fatal(char *fmt, ...); +\c void fatal(const char *fmt, ...); This is called by some utility functions if they encounter a genuinely fatal error such as running out of memory. It is a @@ -3299,6 +3626,63 @@ single element (typically measured using \c{sizeof}). \c{rs} is a \c{random_state} used to generate all the random numbers for the shuffling process. +\H{utils-presets} Presets menu management + +The function \c{midend_get_presets()} (\k{midend-get-presets}) returns +a data structure describing a menu hierarchy. Back ends can also +choose to provide such a structure to the mid-end, if they want to +group their presets hierarchically. To make this easy, there are a few +utility functions to construct preset menu structures, and also one +intended for front-end use. + +\S{utils-preset-menu-new} \cw{preset_menu_new()} + +\c struct preset_menu *preset_menu_new(void); + +Allocates a new \c{struct preset_menu}, and initialises it to hold no +menu items. + +\S{utils-preset-menu-add_submenu} \cw{preset_menu_add_submenu()} + +\c struct preset_menu *preset_menu_add_submenu +\c (struct preset_menu *parent, char *title); + +Adds a new submenu to the end of an existing preset menu, and returns +a pointer to a newly allocated \c{struct preset_menu} describing the +submenu. + +The string parameter \cq{title} must be dynamically allocated by the +caller. The preset-menu structure will take ownership of it, so the +caller must not free it. + +\S{utils-preset-menu-add-preset} \cw{preset_menu_add_preset()} + +\c void preset_menu_add_preset +\c (struct preset_menu *menu, char *title, game_params *params); + +Adds a preset game configuration to the end of a preset menu. + +Both the string parameter \cq{title} and the game parameter structure +\cq{params} itself must be dynamically allocated by the caller. The +preset-menu structure will take ownership of it, so the caller must +not free it. + +\S{utils-preset-menu-lookup-by-id} \cw{preset_menu_lookup_by_id()} + +\c game_params *preset_menu_lookup_by_id +\c (struct preset_menu *menu, int id); + +Given a numeric index, searches recursively through a preset menu +hierarchy to find the corresponding menu entry, and returns a pointer +to its existing \c{game_params} structure. + +This function is intended for front end use (but front ends need not +use it if they prefer to do things another way). If a front end finds +it inconvenient to store anything more than a numeric index alongside +each menu item, then this function provides an easy way for the front +end to get back the actual game parameters corresponding to a menu +item that the user has selected. + \H{utils-alloc} Memory allocation Puzzles has some central wrappers on the standard memory allocation @@ -3380,10 +3764,10 @@ quite everywhere.) \c void free_cfg(config_item *cfg); -This function correctly frees an array of \c{config_item}s, -including walking the array until it gets to the end and freeing -precisely those \c{sval} fields which are expected to be dynamically -allocated. +This function correctly frees an array of \c{config_item}s, including +walking the array until it gets to the end and freeing any subsidiary +data items in each \c{u} sub-union which are expected to be +dynamically allocated. (See \k{backend-configure} for details of the \c{config_item} structure.)