mirror of
git://git.tartarus.org/simon/puzzles.git
synced 2025-04-20 23:51:29 -07:00
Refine drawing API semantics to pass drawing *
instead of void *
This changes the drawing API so that implementations receive a `drawing *` pointer with each call, instead of a `void *` pointer as they did previously. The `void *` context pointer has been moved to be a member of the `drawing` structure (which has been made public), from which it can be retrieved via the new `GET_HANDLE_AS_TYPE()` macro. To signal this breaking change to downstream front end authors, I've added a version number to the `drawing_api` struct, which will hopefully force them to notice. The motivation for this change is the upcoming introduction of a draw_polygon_fallback() function, which will use a series of calls to draw_line() to perform software polygon rasterization on platforms without a native polygon fill primitive. This function is fairly large, so I desired that it not be included in the binary distribution, except on platforms which require it (e.g. my Rockbox port). One way to achieve this is via link-time optimization (LTO, a.k.a. "interprocedural optimization"/IPO), so that the code is unconditionally compiled (preventing bit-rot) but only included in the linked executable if it is actually referenced from elsewhere. Practically, this precludes the otherwise straightforward route of including a run-time check of the `draw_polygon` pointer in the drawing.c middleware. Instead, Simon recommended that a front end be able to set its `draw_polygon` field to point to draw_polygon_fallback(). However, the old drawing API's semantics of passing a `void *` pointer prevented this from working in practice, since draw_polygon_fallback(), implemented in middleware, would not be able to perform any drawing operations without a `drawing *` pointer; with the new API, this restriction is removed, clearing the way for that function's introduction. This is a breaking change for front ends, which must update their implementations of the drawing API to conform. The migration process is fairly straightforward: every drawing API function which previously took a `void *` context pointer should be updated to take a `drawing *` pointer in its place. Then, where each such function would have previously casted the `void *` pointer to a meaningful type, they now instead retrieve the context pointer from the `handle` field of the `drawing` structure. To make this transition easier, the `GET_HANDLE_AS_TYPE()` macro is introduced to wrap the context pointer retrieval (see below for usage). As an example, an old drawing API function implementation would have looked like this: void frontend_draw_func(void *handle, ...) { frontend *fe = (frontend *)handle; /* do stuff with fe */ } After this change, that function would be rewritten as: void frontend_draw_func(drawing *dr, ...) { frontend *fe = GET_HANDLE_AS_TYPE(dr, frontend); /* do stuff with fe */ } I have already made these changes to all the in-tree front ends, but out-of-tree front ends will need to follow the procedure outlined above. Simon pointed out that changing the drawing API function pointer signatures to take `drawing *` instead of `void *` results only in a compiler warning, not an outright error. Thus, I've introduced a version field to the beginning of the `drawing_api` struct, which will cause a compilation error and hopefully force front ends to notice this. This field should be set to 1 for now. Going forward, it will provide a clear means of communicating future breaking API changes.
This commit is contained in:

committed by
Simon Tatham

parent
a993fd45eb
commit
f37913002a
128
devel.but
128
devel.but
@ -1975,18 +1975,19 @@ In fact these global functions are not implemented directly by the
|
||||
front end; instead, they are implemented centrally in \c{drawing.c}
|
||||
and form a small piece of middleware. The drawing API as supplied by
|
||||
the front end is a structure containing a set of function pointers,
|
||||
plus a \cq{void *} handle which is passed to each of those
|
||||
functions. This enables a single front end to switch between
|
||||
multiple implementations of the drawing API if necessary. For
|
||||
example, the Windows API supplies a printing mechanism integrated
|
||||
into the same GDI which deals with drawing in windows, and therefore
|
||||
the same API implementation can handle both drawing and printing;
|
||||
but on Unix, the most common way for applications to print is by
|
||||
producing PostScript output directly, and although it would be
|
||||
\e{possible} to write a single (say) \cw{draw_rect()} function which
|
||||
checked a global flag to decide whether to do GTK drawing operations
|
||||
or output PostScript to a file, it's much nicer to have two separate
|
||||
functions and switch between them as appropriate.
|
||||
plus a \cq{void *} handle which is indirectly passed to each of those
|
||||
functions as a member of the same drawing object as used by the
|
||||
backend. This enables a single front end to switch between multiple
|
||||
implementations of the drawing API if necessary. For example, the
|
||||
Windows API supplies a printing mechanism integrated into the same GDI
|
||||
which deals with drawing in windows, and therefore the same API
|
||||
implementation can handle both drawing and printing; but on Unix, the
|
||||
most common way for applications to print is by producing PostScript
|
||||
output directly, and although it would be \e{possible} to write a
|
||||
single (say) \cw{draw_rect()} function which checked a global flag to
|
||||
decide whether to do GTK drawing operations or output PostScript to a
|
||||
file, it's much nicer to have two separate functions and switch
|
||||
between them as appropriate.
|
||||
|
||||
When drawing, the puzzle window is indexed by pixel coordinates,
|
||||
with the top left pixel defined as \cw{(0,0)} and the bottom right
|
||||
@ -2689,17 +2690,38 @@ API; the platform-independent module \c{ps.c} also provides an
|
||||
implementation of it which outputs PostScript. Thus, any platform
|
||||
which wants to do PS printing can do so with minimum fuss.)
|
||||
|
||||
The following entries all describe function pointer fields in a
|
||||
structure called \c{drawing_api}. Each of the functions takes a
|
||||
\cq{void *} context pointer, which it should internally cast back to
|
||||
a more useful type. Thus, a drawing \e{object} (\c{drawing *)}
|
||||
suitable for passing to the back end redraw or printing functions
|
||||
is constructed by passing a \c{drawing_api} and a \cq{void *} to the
|
||||
function \cw{drawing_new()} (see \k{drawing-new}).
|
||||
The following entries (with the exception of the \cw{version} field)
|
||||
all describe function pointer fields in a structure called
|
||||
\c{drawing_api}. Each of the functions takes a \cq{drawing *} pointer,
|
||||
which in turn contains a \cq{void *handle} member that the front end
|
||||
can access and internally cast back to a more useful type. Since this
|
||||
is a fairly common thing for front ends to do, the
|
||||
\cw{GET_HANDLE_AS_TYPE()} macro is provided to do this for you (see
|
||||
\k{utils-get-handle-as-type}).
|
||||
|
||||
A drawing \e{object} (\c{drawing *)} suitable for passing to the back
|
||||
end redraw or printing functions is constructed by passing a
|
||||
\c{drawing_api} and a \cq{void *} to the function \cw{drawing_new()}
|
||||
(see \k{drawing-new}).
|
||||
|
||||
\S{drawingapi-version} \cw{version}
|
||||
|
||||
\c int version;
|
||||
|
||||
This is an integer giving the version of the drawing API implemented
|
||||
by the front end. For the version of the API described in this
|
||||
document, this field should be \cw{1}.
|
||||
|
||||
On occasion, there may be a breaking change to the drawing API, such
|
||||
as the introduction of a new function or a change to the semantics of
|
||||
an existing function. The intent of this version field is to signal to
|
||||
front end authors when such a breaking change occurs. As such changes
|
||||
can be disruptive to downstream front ends, they should be done
|
||||
sparingly.
|
||||
|
||||
\S{drawingapi-draw-text} \cw{draw_text()}
|
||||
|
||||
\c void (*draw_text)(void *handle, int x, int y, int fonttype,
|
||||
\c void (*draw_text)(drawing *dr, int x, int y, int fonttype,
|
||||
\c int fontsize, int align, int colour,
|
||||
\c const char *text);
|
||||
|
||||
@ -2708,7 +2730,7 @@ function; see \k{drawing-draw-text}.
|
||||
|
||||
\S{drawingapi-draw-rect} \cw{draw_rect()}
|
||||
|
||||
\c void (*draw_rect)(void *handle, int x, int y, int w, int h,
|
||||
\c void (*draw_rect)(drawing *dr, int x, int y, int w, int h,
|
||||
\c int colour);
|
||||
|
||||
This function behaves exactly like the back end \cw{draw_rect()}
|
||||
@ -2716,7 +2738,7 @@ function; see \k{drawing-draw-rect}.
|
||||
|
||||
\S{drawingapi-draw-line} \cw{draw_line()}
|
||||
|
||||
\c void (*draw_line)(void *handle, int x1, int y1, int x2, int y2,
|
||||
\c void (*draw_line)(drawing *dr, int x1, int y1, int x2, int y2,
|
||||
\c int colour);
|
||||
|
||||
This function behaves exactly like the back end \cw{draw_line()}
|
||||
@ -2724,7 +2746,7 @@ function; see \k{drawing-draw-line}.
|
||||
|
||||
\S{drawingapi-draw-polygon} \cw{draw_polygon()}
|
||||
|
||||
\c void (*draw_polygon)(void *handle, const int *coords, int npoints,
|
||||
\c void (*draw_polygon)(drawing *dr, const int *coords, int npoints,
|
||||
\c int fillcolour, int outlinecolour);
|
||||
|
||||
This function behaves exactly like the back end \cw{draw_polygon()}
|
||||
@ -2732,7 +2754,7 @@ function; see \k{drawing-draw-polygon}.
|
||||
|
||||
\S{drawingapi-draw-circle} \cw{draw_circle()}
|
||||
|
||||
\c void (*draw_circle)(void *handle, int cx, int cy, int radius,
|
||||
\c void (*draw_circle)(drawing *dr, int cx, int cy, int radius,
|
||||
\c int fillcolour, int outlinecolour);
|
||||
|
||||
This function behaves exactly like the back end \cw{draw_circle()}
|
||||
@ -2754,7 +2776,7 @@ 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);
|
||||
\c void (*draw_update)(drawing *dr, int x, int y, int w, int h);
|
||||
|
||||
This function behaves exactly like the back end \cw{draw_update()}
|
||||
function; see \k{drawing-draw-update}.
|
||||
@ -2766,21 +2788,21 @@ than bothering to define an empty function. The middleware in
|
||||
|
||||
\S{drawingapi-clip} \cw{clip()}
|
||||
|
||||
\c void (*clip)(void *handle, int x, int y, int w, int h);
|
||||
\c void (*clip)(drawing *dr, int x, int y, int w, int h);
|
||||
|
||||
This function behaves exactly like the back end \cw{clip()}
|
||||
function; see \k{drawing-clip}.
|
||||
|
||||
\S{drawingapi-unclip} \cw{unclip()}
|
||||
|
||||
\c void (*unclip)(void *handle);
|
||||
\c void (*unclip)(drawing *dr);
|
||||
|
||||
This function behaves exactly like the back end \cw{unclip()}
|
||||
function; see \k{drawing-unclip}.
|
||||
|
||||
\S{drawingapi-start-draw} \cw{start_draw()}
|
||||
|
||||
\c void (*start_draw)(void *handle);
|
||||
\c void (*start_draw)(drawing *dr);
|
||||
|
||||
This function is called at the start of drawing. It allows the front
|
||||
end to initialise any temporary data required to draw with, such as
|
||||
@ -2792,7 +2814,7 @@ called unless drawing is attempted.
|
||||
|
||||
\S{drawingapi-end-draw} \cw{end_draw()}
|
||||
|
||||
\c void (*end_draw)(void *handle);
|
||||
\c void (*end_draw)(drawing *dr);
|
||||
|
||||
This function is called at the end of drawing. It allows the front
|
||||
end to do cleanup tasks such as deallocating device contexts and
|
||||
@ -2804,7 +2826,7 @@ called unless drawing is attempted.
|
||||
|
||||
\S{drawingapi-status-bar} \cw{status_bar()}
|
||||
|
||||
\c void (*status_bar)(void *handle, const char *text);
|
||||
\c void (*status_bar)(drawing *dr, const char *text);
|
||||
|
||||
This function behaves exactly like the back end \cw{status_bar()}
|
||||
function; see \k{drawing-status-bar}.
|
||||
@ -2819,7 +2841,7 @@ called unless drawing is attempted.
|
||||
|
||||
\S{drawingapi-blitter-new} \cw{blitter_new()}
|
||||
|
||||
\c blitter *(*blitter_new)(void *handle, int w, int h);
|
||||
\c blitter *(*blitter_new)(drawing *dr, int w, int h);
|
||||
|
||||
This function behaves exactly like the back end \cw{blitter_new()}
|
||||
function; see \k{drawing-blitter-new}.
|
||||
@ -2830,7 +2852,7 @@ called unless drawing is attempted.
|
||||
|
||||
\S{drawingapi-blitter-free} \cw{blitter_free()}
|
||||
|
||||
\c void (*blitter_free)(void *handle, blitter *bl);
|
||||
\c void (*blitter_free)(drawing *dr, blitter *bl);
|
||||
|
||||
This function behaves exactly like the back end \cw{blitter_free()}
|
||||
function; see \k{drawing-blitter-free}.
|
||||
@ -2841,7 +2863,7 @@ called unless drawing is attempted.
|
||||
|
||||
\S{drawingapi-blitter-save} \cw{blitter_save()}
|
||||
|
||||
\c void (*blitter_save)(void *handle, blitter *bl, int x, int y);
|
||||
\c void (*blitter_save)(drawing *dr, blitter *bl, int x, int y);
|
||||
|
||||
This function behaves exactly like the back end \cw{blitter_save()}
|
||||
function; see \k{drawing-blitter-save}.
|
||||
@ -2852,7 +2874,7 @@ called unless drawing is attempted.
|
||||
|
||||
\S{drawingapi-blitter-load} \cw{blitter_load()}
|
||||
|
||||
\c void (*blitter_load)(void *handle, blitter *bl, int x, int y);
|
||||
\c void (*blitter_load)(drawing *dr, blitter *bl, int x, int y);
|
||||
|
||||
This function behaves exactly like the back end \cw{blitter_load()}
|
||||
function; see \k{drawing-blitter-load}.
|
||||
@ -2863,7 +2885,7 @@ called unless drawing is attempted.
|
||||
|
||||
\S{drawingapi-begin-doc} \cw{begin_doc()}
|
||||
|
||||
\c void (*begin_doc)(void *handle, int pages);
|
||||
\c void (*begin_doc)(drawing *dr, int pages);
|
||||
|
||||
This function is called at the beginning of a printing run. It gives
|
||||
the front end an opportunity to initialise any required printing
|
||||
@ -2875,7 +2897,7 @@ called unless printing is attempted.
|
||||
|
||||
\S{drawingapi-begin-page} \cw{begin_page()}
|
||||
|
||||
\c void (*begin_page)(void *handle, int number);
|
||||
\c void (*begin_page)(drawing *dr, int number);
|
||||
|
||||
This function is called during printing, at the beginning of each
|
||||
page. It gives the page number (numbered from 1 rather than 0, so
|
||||
@ -2887,7 +2909,7 @@ called unless printing is attempted.
|
||||
|
||||
\S{drawingapi-begin-puzzle} \cw{begin_puzzle()}
|
||||
|
||||
\c void (*begin_puzzle)(void *handle, float xm, float xc,
|
||||
\c void (*begin_puzzle)(drawing *dr, float xm, float xc,
|
||||
\c float ym, float yc, int pw, int ph, float wmm);
|
||||
|
||||
This function is called during printing, just before printing a
|
||||
@ -2930,7 +2952,7 @@ called unless printing is attempted.
|
||||
|
||||
\S{drawingapi-end-puzzle} \cw{end_puzzle()}
|
||||
|
||||
\c void (*end_puzzle)(void *handle);
|
||||
\c void (*end_puzzle)(drawing *dr);
|
||||
|
||||
This function is called after the printing of a specific puzzle is
|
||||
complete.
|
||||
@ -2941,7 +2963,7 @@ called unless printing is attempted.
|
||||
|
||||
\S{drawingapi-end-page} \cw{end_page()}
|
||||
|
||||
\c void (*end_page)(void *handle, int number);
|
||||
\c void (*end_page)(drawing *dr, int number);
|
||||
|
||||
This function is called after the printing of a page is finished.
|
||||
|
||||
@ -2951,7 +2973,7 @@ called unless printing is attempted.
|
||||
|
||||
\S{drawingapi-end-doc} \cw{end_doc()}
|
||||
|
||||
\c void (*end_doc)(void *handle);
|
||||
\c void (*end_doc)(drawing *dr);
|
||||
|
||||
This function is called after the printing of the entire document is
|
||||
finished. This is the moment to close files, send things to the
|
||||
@ -2963,7 +2985,7 @@ called unless printing is attempted.
|
||||
|
||||
\S{drawingapi-line-width} \cw{line_width()}
|
||||
|
||||
\c void (*line_width)(void *handle, float width);
|
||||
\c void (*line_width)(drawing *dr, float width);
|
||||
|
||||
This function is called to set the line thickness, during printing
|
||||
only. Note that the width is a \cw{float} here, where it was an
|
||||
@ -2979,7 +3001,7 @@ called unless printing is attempted.
|
||||
|
||||
\S{drawingapi-line-dotted} \cw{line_dotted()}
|
||||
|
||||
\c void (*line_dotted)(void *handle, bool dotted);
|
||||
\c void (*line_dotted)(drawing *dr, bool dotted);
|
||||
|
||||
This function is called to toggle drawing of dotted lines, during
|
||||
printing only.
|
||||
@ -2990,7 +3012,7 @@ called unless printing is attempted.
|
||||
|
||||
\S{drawingapi-text-fallback} \cw{text_fallback()}
|
||||
|
||||
\c char *(*text_fallback)(void *handle, const char *const *strings,
|
||||
\c char *(*text_fallback)(drawing *dr, const char *const *strings,
|
||||
\c int nstrings);
|
||||
|
||||
This function behaves exactly like the back end \cw{text_fallback()}
|
||||
@ -3013,9 +3035,10 @@ implement. They are described in this section.
|
||||
\c void *handle);
|
||||
|
||||
This function creates a drawing object. It is passed a
|
||||
\c{drawing_api}, which is a structure containing nothing but
|
||||
function pointers; and also a \cq{void *} handle. The handle is
|
||||
passed back to each function pointer when it is called.
|
||||
\c{drawing_api}, which is a structure containing function pointers and
|
||||
a version field; and also a \cq{void *} handle. The handle is stored
|
||||
in the drawing object, from where it may be accessed by each function
|
||||
pointer when it is called.
|
||||
|
||||
The \c{midend} parameter is used for rewriting the status bar
|
||||
contents: \cw{status_bar()} (see \k{drawing-status-bar}) has to call
|
||||
@ -3024,6 +3047,10 @@ If the drawing object is to be used only for printing, or if the
|
||||
game is known not to call \cw{status_bar()}, this parameter may be
|
||||
\cw{NULL}.
|
||||
|
||||
A fatal error is produced if the \cw{version} field of the
|
||||
\c{drawing_api} does not match the expected version (see
|
||||
\k{drawingapi-version} for the expected version number).
|
||||
|
||||
\S{drawing-free} \cw{drawing_free()}
|
||||
|
||||
\c void drawing_free(drawing *dr);
|
||||
@ -5351,6 +5378,15 @@ argument is respectively less than, equal to, or greater than the
|
||||
second. This function is intended to be passed to \c{qsort()} for
|
||||
sorting ints in ascending order.
|
||||
|
||||
\S{utils-get-handle-as-type} \cw{GET_HANDLE_AS_TYPE()}
|
||||
|
||||
\c #define GET_HANDLE_AS_TYPE(dr, type) ((type*)((dr)->handle))
|
||||
|
||||
This macro, defined in the main Puzzles header file, retrieves the
|
||||
\c{handle} field from a \c{drawing *} pointer and casts it to \c{type
|
||||
*}. It is intended for use in implementations of drawing API
|
||||
functions, where this operation is idiomatic.
|
||||
|
||||
\C{writing} How to write a new puzzle
|
||||
|
||||
This chapter gives a guide to how to actually write a new puzzle:
|
||||
|
Reference in New Issue
Block a user