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:
Franklin Wei
2024-08-11 19:28:35 -04:00
committed by Simon Tatham
parent a993fd45eb
commit f37913002a
11 changed files with 459 additions and 342 deletions

128
devel.but
View File

@ -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} front end; instead, they are implemented centrally in \c{drawing.c}
and form a small piece of middleware. The drawing API as supplied by 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, the front end is a structure containing a set of function pointers,
plus a \cq{void *} handle which is passed to each of those plus a \cq{void *} handle which is indirectly passed to each of those
functions. This enables a single front end to switch between functions as a member of the same drawing object as used by the
multiple implementations of the drawing API if necessary. For backend. This enables a single front end to switch between multiple
example, the Windows API supplies a printing mechanism integrated implementations of the drawing API if necessary. For example, the
into the same GDI which deals with drawing in windows, and therefore Windows API supplies a printing mechanism integrated into the same GDI
the same API implementation can handle both drawing and printing; which deals with drawing in windows, and therefore the same API
but on Unix, the most common way for applications to print is by implementation can handle both drawing and printing; but on Unix, the
producing PostScript output directly, and although it would be most common way for applications to print is by producing PostScript
\e{possible} to write a single (say) \cw{draw_rect()} function which output directly, and although it would be \e{possible} to write a
checked a global flag to decide whether to do GTK drawing operations single (say) \cw{draw_rect()} function which checked a global flag to
or output PostScript to a file, it's much nicer to have two separate decide whether to do GTK drawing operations or output PostScript to a
functions and switch between them as appropriate. 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, When drawing, the puzzle window is indexed by pixel coordinates,
with the top left pixel defined as \cw{(0,0)} and the bottom right 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 implementation of it which outputs PostScript. Thus, any platform
which wants to do PS printing can do so with minimum fuss.) which wants to do PS printing can do so with minimum fuss.)
The following entries all describe function pointer fields in a The following entries (with the exception of the \cw{version} field)
structure called \c{drawing_api}. Each of the functions takes a all describe function pointer fields in a structure called
\cq{void *} context pointer, which it should internally cast back to \c{drawing_api}. Each of the functions takes a \cq{drawing *} pointer,
a more useful type. Thus, a drawing \e{object} (\c{drawing *)} which in turn contains a \cq{void *handle} member that the front end
suitable for passing to the back end redraw or printing functions can access and internally cast back to a more useful type. Since this
is constructed by passing a \c{drawing_api} and a \cq{void *} to the is a fairly common thing for front ends to do, the
function \cw{drawing_new()} (see \k{drawing-new}). \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()} \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 int fontsize, int align, int colour,
\c const char *text); \c const char *text);
@ -2708,7 +2730,7 @@ function; see \k{drawing-draw-text}.
\S{drawingapi-draw-rect} \cw{draw_rect()} \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); \c int colour);
This function behaves exactly like the back end \cw{draw_rect()} 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()} \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); \c int colour);
This function behaves exactly like the back end \cw{draw_line()} 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()} \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); \c int fillcolour, int outlinecolour);
This function behaves exactly like the back end \cw{draw_polygon()} 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()} \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); \c int fillcolour, int outlinecolour);
This function behaves exactly like the back end \cw{draw_circle()} 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()} \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()} This function behaves exactly like the back end \cw{draw_update()}
function; see \k{drawing-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()} \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()} This function behaves exactly like the back end \cw{clip()}
function; see \k{drawing-clip}. function; see \k{drawing-clip}.
\S{drawingapi-unclip} \cw{unclip()} \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()} This function behaves exactly like the back end \cw{unclip()}
function; see \k{drawing-unclip}. function; see \k{drawing-unclip}.
\S{drawingapi-start-draw} \cw{start_draw()} \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 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 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()} \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 This function is called at the end of drawing. It allows the front
end to do cleanup tasks such as deallocating device contexts and 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()} \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()} This function behaves exactly like the back end \cw{status_bar()}
function; see \k{drawing-status-bar}. function; see \k{drawing-status-bar}.
@ -2819,7 +2841,7 @@ called unless drawing is attempted.
\S{drawingapi-blitter-new} \cw{blitter_new()} \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()} This function behaves exactly like the back end \cw{blitter_new()}
function; see \k{drawing-blitter-new}. function; see \k{drawing-blitter-new}.
@ -2830,7 +2852,7 @@ called unless drawing is attempted.
\S{drawingapi-blitter-free} \cw{blitter_free()} \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()} This function behaves exactly like the back end \cw{blitter_free()}
function; see \k{drawing-blitter-free}. function; see \k{drawing-blitter-free}.
@ -2841,7 +2863,7 @@ called unless drawing is attempted.
\S{drawingapi-blitter-save} \cw{blitter_save()} \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()} This function behaves exactly like the back end \cw{blitter_save()}
function; see \k{drawing-blitter-save}. function; see \k{drawing-blitter-save}.
@ -2852,7 +2874,7 @@ called unless drawing is attempted.
\S{drawingapi-blitter-load} \cw{blitter_load()} \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()} This function behaves exactly like the back end \cw{blitter_load()}
function; see \k{drawing-blitter-load}. function; see \k{drawing-blitter-load}.
@ -2863,7 +2885,7 @@ called unless drawing is attempted.
\S{drawingapi-begin-doc} \cw{begin_doc()} \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 This function is called at the beginning of a printing run. It gives
the front end an opportunity to initialise any required printing 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()} \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 This function is called during printing, at the beginning of each
page. It gives the page number (numbered from 1 rather than 0, so 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()} \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); \c float ym, float yc, int pw, int ph, float wmm);
This function is called during printing, just before printing a 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()} \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 This function is called after the printing of a specific puzzle is
complete. complete.
@ -2941,7 +2963,7 @@ called unless printing is attempted.
\S{drawingapi-end-page} \cw{end_page()} \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. 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()} \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 This function is called after the printing of the entire document is
finished. This is the moment to close files, send things to the 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()} \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 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 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()} \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 This function is called to toggle drawing of dotted lines, during
printing only. printing only.
@ -2990,7 +3012,7 @@ called unless printing is attempted.
\S{drawingapi-text-fallback} \cw{text_fallback()} \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); \c int nstrings);
This function behaves exactly like the back end \cw{text_fallback()} 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); \c void *handle);
This function creates a drawing object. It is passed a This function creates a drawing object. It is passed a
\c{drawing_api}, which is a structure containing nothing but \c{drawing_api}, which is a structure containing function pointers and
function pointers; and also a \cq{void *} handle. The handle is a version field; and also a \cq{void *} handle. The handle is stored
passed back to each function pointer when it is called. 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 The \c{midend} parameter is used for rewriting the status bar
contents: \cw{status_bar()} (see \k{drawing-status-bar}) has to call 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 game is known not to call \cw{status_bar()}, this parameter may be
\cw{NULL}. \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()} \S{drawing-free} \cw{drawing_free()}
\c void drawing_free(drawing *dr); \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 second. This function is intended to be passed to \c{qsort()} for
sorting ints in ascending order. 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 \C{writing} How to write a new puzzle
This chapter gives a guide to how to actually write a new puzzle: This chapter gives a guide to how to actually write a new puzzle:

196
drawing.c
View File

@ -42,9 +42,12 @@ struct print_colour {
float grey; float grey;
}; };
struct drawing { typedef struct drawing_internal {
const drawing_api *api; /* we implement data hiding by casting `struct drawing*` pointers
void *handle; * to `struct drawing_internal*` */
struct drawing pub;
/* private data */
struct print_colour *colours; struct print_colour *colours;
int ncolours, coloursize; int ncolours, coloursize;
float scale; float scale;
@ -52,53 +55,70 @@ struct drawing {
* this may set it to NULL. */ * this may set it to NULL. */
midend *me; midend *me;
char *laststatus; char *laststatus;
}; } drawing_internal;
#define PRIVATE_CAST(dr) ((drawing_internal*)(dr))
#define PUBLIC_CAST(dri) ((drawing*)(dri))
/* See puzzles.h for a description of the version number. */
#define DRAWING_API_VERSION 1
drawing *drawing_new(const drawing_api *api, midend *me, void *handle) drawing *drawing_new(const drawing_api *api, midend *me, void *handle)
{ {
drawing *dr = snew(drawing); if(api->version != DRAWING_API_VERSION) {
dr->api = api; fatal("Drawing API version mismatch: expected: %d, actual: %d\n", DRAWING_API_VERSION, api->version);
dr->handle = handle; /* shouldn't get here */
dr->colours = NULL; return NULL;
dr->ncolours = dr->coloursize = 0; }
dr->scale = 1.0F;
dr->me = me; drawing_internal *dri = snew(drawing_internal);
dr->laststatus = NULL; dri->pub.api = api;
return dr; dri->pub.handle = handle;
dri->colours = NULL;
dri->ncolours = dri->coloursize = 0;
dri->scale = 1.0F;
dri->me = me;
dri->laststatus = NULL;
return PUBLIC_CAST(dri);
} }
void drawing_free(drawing *dr) void drawing_free(drawing *dr)
{ {
sfree(dr->laststatus); drawing_internal *dri = PRIVATE_CAST(dr);
sfree(dr->colours); sfree(dri->laststatus);
sfree(dr); sfree(dri->colours);
sfree(dri);
} }
void draw_text(drawing *dr, int x, int y, int fonttype, int fontsize, void draw_text(drawing *dr, int x, int y, int fonttype, int fontsize,
int align, int colour, const char *text) int align, int colour, const char *text)
{ {
dr->api->draw_text(dr->handle, x, y, fonttype, fontsize, align, drawing_internal *dri = PRIVATE_CAST(dr);
colour, text); dri->pub.api->draw_text(dr, x, y, fonttype, fontsize, align,
colour, text);
} }
void draw_rect(drawing *dr, int x, int y, int w, int h, int colour) void draw_rect(drawing *dr, int x, int y, int w, int h, int colour)
{ {
dr->api->draw_rect(dr->handle, x, y, w, h, colour); drawing_internal *dri = PRIVATE_CAST(dr);
dri->pub.api->draw_rect(dr, x, y, w, h, colour);
} }
void draw_line(drawing *dr, int x1, int y1, int x2, int y2, int colour) void draw_line(drawing *dr, int x1, int y1, int x2, int y2, int colour)
{ {
dr->api->draw_line(dr->handle, x1, y1, x2, y2, colour); drawing_internal *dri = PRIVATE_CAST(dr);
dri->pub.api->draw_line(dr, x1, y1, x2, y2, colour);
} }
void draw_thick_line(drawing *dr, float thickness, void draw_thick_line(drawing *dr, float thickness,
float x1, float y1, float x2, float y2, int colour) float x1, float y1, float x2, float y2, int colour)
{ {
drawing_internal *dri = PRIVATE_CAST(dr);
if (thickness < 1.0F) if (thickness < 1.0F)
thickness = 1.0F; thickness = 1.0F;
if (dr->api->draw_thick_line) { if (dri->pub.api->draw_thick_line) {
dr->api->draw_thick_line(dr->handle, thickness, dri->pub.api->draw_thick_line(dr, thickness,
x1, y1, x2, y2, colour); x1, y1, x2, y2, colour);
} else { } else {
/* We'll fake it up with a filled polygon. The tweak to the /* We'll fake it up with a filled polygon. The tweak to the
* thickness empirically compensates for rounding errors, because * thickness empirically compensates for rounding errors, because
@ -117,59 +137,67 @@ void draw_thick_line(drawing *dr, float thickness,
p[5] = y2 - tvhatx; p[5] = y2 - tvhatx;
p[6] = x1 + tvhaty; p[6] = x1 + tvhaty;
p[7] = y1 - tvhatx; p[7] = y1 - tvhatx;
dr->api->draw_polygon(dr->handle, p, 4, colour, colour); dri->pub.api->draw_polygon(dr, p, 4, colour, colour);
} }
} }
void draw_polygon(drawing *dr, const int *coords, int npoints, void draw_polygon(drawing *dr, const int *coords, int npoints,
int fillcolour, int outlinecolour) int fillcolour, int outlinecolour)
{ {
dr->api->draw_polygon(dr->handle, coords, npoints, fillcolour, drawing_internal *dri = PRIVATE_CAST(dr);
outlinecolour); dri->pub.api->draw_polygon(dr, coords, npoints, fillcolour,
outlinecolour);
} }
void draw_circle(drawing *dr, int cx, int cy, int radius, void draw_circle(drawing *dr, int cx, int cy, int radius,
int fillcolour, int outlinecolour) int fillcolour, int outlinecolour)
{ {
dr->api->draw_circle(dr->handle, cx, cy, radius, fillcolour, drawing_internal *dri = PRIVATE_CAST(dr);
outlinecolour); dri->pub.api->draw_circle(dr, cx, cy, radius, fillcolour,
outlinecolour);
} }
void draw_update(drawing *dr, int x, int y, int w, int h) void draw_update(drawing *dr, int x, int y, int w, int h)
{ {
if (dr->api->draw_update) drawing_internal *dri = PRIVATE_CAST(dr);
dr->api->draw_update(dr->handle, x, y, w, h); if (dri->pub.api->draw_update)
dri->pub.api->draw_update(dr, x, y, w, h);
} }
void clip(drawing *dr, int x, int y, int w, int h) void clip(drawing *dr, int x, int y, int w, int h)
{ {
dr->api->clip(dr->handle, x, y, w, h); drawing_internal *dri = PRIVATE_CAST(dr);
dri->pub.api->clip(dr, x, y, w, h);
} }
void unclip(drawing *dr) void unclip(drawing *dr)
{ {
dr->api->unclip(dr->handle); drawing_internal *dri = PRIVATE_CAST(dr);
dri->pub.api->unclip(dr);
} }
void start_draw(drawing *dr) void start_draw(drawing *dr)
{ {
dr->api->start_draw(dr->handle); drawing_internal *dri = PRIVATE_CAST(dr);
dri->pub.api->start_draw(dr);
} }
void end_draw(drawing *dr) void end_draw(drawing *dr)
{ {
dr->api->end_draw(dr->handle); drawing_internal *dri = PRIVATE_CAST(dr);
dri->pub.api->end_draw(dr);
} }
char *text_fallback(drawing *dr, const char *const *strings, int nstrings) char *text_fallback(drawing *dr, const char *const *strings, int nstrings)
{ {
drawing_internal *dri = PRIVATE_CAST(dr);
int i; int i;
/* /*
* If the drawing implementation provides one of these, use it. * If the drawing implementation provides one of these, use it.
*/ */
if (dr && dr->api->text_fallback) if (dr && dri->pub.api->text_fallback)
return dr->api->text_fallback(dr->handle, strings, nstrings); return dri->pub.api->text_fallback(dr, strings, nstrings);
/* /*
* Otherwise, do the simple thing and just pick the first string * 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)
void status_bar(drawing *dr, const char *text) void status_bar(drawing *dr, const char *text)
{ {
drawing_internal *dri = PRIVATE_CAST(dr);
char *rewritten; char *rewritten;
if (!dr->api->status_bar) if (!dri->pub.api->status_bar)
return; return;
assert(dr->me); assert(dri->me);
rewritten = midend_rewrite_statusbar(dr->me, text); rewritten = midend_rewrite_statusbar(dri->me, text);
if (!dr->laststatus || strcmp(rewritten, dr->laststatus)) { if (!dri->laststatus || strcmp(rewritten, dri->laststatus)) {
dr->api->status_bar(dr->handle, rewritten); dri->pub.api->status_bar(dr, rewritten);
sfree(dr->laststatus); sfree(dri->laststatus);
dr->laststatus = rewritten; dri->laststatus = rewritten;
} else { } else {
sfree(rewritten); sfree(rewritten);
} }
@ -215,74 +244,85 @@ void status_bar(drawing *dr, const char *text)
blitter *blitter_new(drawing *dr, int w, int h) blitter *blitter_new(drawing *dr, int w, int h)
{ {
return dr->api->blitter_new(dr->handle, w, h); drawing_internal *dri = PRIVATE_CAST(dr);
return dri->pub.api->blitter_new(dr, w, h);
} }
void blitter_free(drawing *dr, blitter *bl) void blitter_free(drawing *dr, blitter *bl)
{ {
dr->api->blitter_free(dr->handle, bl); drawing_internal *dri = PRIVATE_CAST(dr);
dri->pub.api->blitter_free(dr, bl);
} }
void blitter_save(drawing *dr, blitter *bl, int x, int y) void blitter_save(drawing *dr, blitter *bl, int x, int y)
{ {
dr->api->blitter_save(dr->handle, bl, x, y); drawing_internal *dri = PRIVATE_CAST(dr);
dri->pub.api->blitter_save(dr, bl, x, y);
} }
void blitter_load(drawing *dr, blitter *bl, int x, int y) void blitter_load(drawing *dr, blitter *bl, int x, int y)
{ {
dr->api->blitter_load(dr->handle, bl, x, y); drawing_internal *dri = PRIVATE_CAST(dr);
dri->pub.api->blitter_load(dr, bl, x, y);
} }
void print_begin_doc(drawing *dr, int pages) void print_begin_doc(drawing *dr, int pages)
{ {
dr->api->begin_doc(dr->handle, pages); drawing_internal *dri = PRIVATE_CAST(dr);
dri->pub.api->begin_doc(dr, pages);
} }
void print_begin_page(drawing *dr, int number) void print_begin_page(drawing *dr, int number)
{ {
dr->api->begin_page(dr->handle, number); drawing_internal *dri = PRIVATE_CAST(dr);
dri->pub.api->begin_page(dr, number);
} }
void print_begin_puzzle(drawing *dr, float xm, float xc, void print_begin_puzzle(drawing *dr, float xm, float xc,
float ym, float yc, int pw, int ph, float wmm, float ym, float yc, int pw, int ph, float wmm,
float scale) float scale)
{ {
dr->scale = scale; drawing_internal *dri = PRIVATE_CAST(dr);
dr->ncolours = 0; dri->scale = scale;
dr->api->begin_puzzle(dr->handle, xm, xc, ym, yc, pw, ph, wmm); dri->ncolours = 0;
dri->pub.api->begin_puzzle(dr, xm, xc, ym, yc, pw, ph, wmm);
} }
void print_end_puzzle(drawing *dr) void print_end_puzzle(drawing *dr)
{ {
dr->api->end_puzzle(dr->handle); drawing_internal *dri = PRIVATE_CAST(dr);
dr->scale = 1.0F; dri->pub.api->end_puzzle(dr);
dri->scale = 1.0F;
} }
void print_end_page(drawing *dr, int number) void print_end_page(drawing *dr, int number)
{ {
dr->api->end_page(dr->handle, number); drawing_internal *dri = PRIVATE_CAST(dr);
dri->pub.api->end_page(dr, number);
} }
void print_end_doc(drawing *dr) void print_end_doc(drawing *dr)
{ {
dr->api->end_doc(dr->handle); drawing_internal *dri = PRIVATE_CAST(dr);
dri->pub.api->end_doc(dr);
} }
void print_get_colour(drawing *dr, int colour, bool printing_in_colour, void print_get_colour(drawing *dr, int colour, bool printing_in_colour,
int *hatch, float *r, float *g, float *b) int *hatch, float *r, float *g, float *b)
{ {
assert(colour >= 0 && colour < dr->ncolours); drawing_internal *dri = PRIVATE_CAST(dr);
if (dr->colours[colour].hatch_when == 2 || assert(colour >= 0 && colour < dri->ncolours);
(dr->colours[colour].hatch_when == 1 && !printing_in_colour)) { if (dri->colours[colour].hatch_when == 2 ||
*hatch = dr->colours[colour].hatch; (dri->colours[colour].hatch_when == 1 && !printing_in_colour)) {
*hatch = dri->colours[colour].hatch;
} else { } else {
*hatch = -1; *hatch = -1;
if (printing_in_colour) { if (printing_in_colour) {
*r = dr->colours[colour].r; *r = dri->colours[colour].r;
*g = dr->colours[colour].g; *g = dri->colours[colour].g;
*b = dr->colours[colour].b; *b = dri->colours[colour].b;
} else { } else {
*r = *g = *b = dr->colours[colour].grey; *r = *g = *b = dri->colours[colour].grey;
} }
} }
} }
@ -290,18 +330,19 @@ void print_get_colour(drawing *dr, int colour, bool printing_in_colour,
static int print_generic_colour(drawing *dr, float r, float g, float b, static int print_generic_colour(drawing *dr, float r, float g, float b,
float grey, int hatch, int hatch_when) float grey, int hatch, int hatch_when)
{ {
if (dr->ncolours >= dr->coloursize) { drawing_internal *dri = PRIVATE_CAST(dr);
dr->coloursize = dr->ncolours + 16; if (dri->ncolours >= dri->coloursize) {
dr->colours = sresize(dr->colours, dr->coloursize, dri->coloursize = dri->ncolours + 16;
dri->colours = sresize(dri->colours, dri->coloursize,
struct print_colour); struct print_colour);
} }
dr->colours[dr->ncolours].hatch = hatch; dri->colours[dri->ncolours].hatch = hatch;
dr->colours[dr->ncolours].hatch_when = hatch_when; dri->colours[dri->ncolours].hatch_when = hatch_when;
dr->colours[dr->ncolours].r = r; dri->colours[dri->ncolours].r = r;
dr->colours[dr->ncolours].g = g; dri->colours[dri->ncolours].g = g;
dr->colours[dr->ncolours].b = b; dri->colours[dri->ncolours].b = b;
dr->colours[dr->ncolours].grey = grey; dri->colours[dri->ncolours].grey = grey;
return dr->ncolours++; return dri->ncolours++;
} }
int print_mono_colour(drawing *dr, int grey) int 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)
void print_line_width(drawing *dr, int width) void print_line_width(drawing *dr, int width)
{ {
drawing_internal *dri = PRIVATE_CAST(dr);
/* /*
* I don't think it's entirely sensible to have line widths be * I don't think it's entirely sensible to have line widths be
* entirely relative to the puzzle size; there is a point * entirely relative to the puzzle size; there is a point
@ -348,10 +391,11 @@ void print_line_width(drawing *dr, int width)
* _square root_ of the main puzzle scale. Double the puzzle * _square root_ of the main puzzle scale. Double the puzzle
* size, and the line width multiplies by 1.4. * size, and the line width multiplies by 1.4.
*/ */
dr->api->line_width(dr->handle, (float)sqrt(dr->scale) * width); dri->pub.api->line_width(dr, (float)sqrt(dri->scale) * width);
} }
void print_line_dotted(drawing *dr, bool dotted) void print_line_dotted(drawing *dr, bool dotted)
{ {
dr->api->line_dotted(dr->handle, dotted); drawing_internal *dri = PRIVATE_CAST(dr);
dri->pub.api->line_dotted(dr, dotted);
} }

35
emcc.c
View File

@ -456,22 +456,22 @@ static void ids_changed(void *ignored)
* drawing functions. (Well, half of it; the other half is on the JS * drawing functions. (Well, half of it; the other half is on the JS
* side.) * side.)
*/ */
static void js_start_draw(void *handle) static void js_start_draw(drawing *dr)
{ {
js_canvas_start_draw(); js_canvas_start_draw();
} }
static void js_clip(void *handle, int x, int y, int w, int h) static void js_clip(drawing *dr, int x, int y, int w, int h)
{ {
js_canvas_clip_rect(x, y, w, h); js_canvas_clip_rect(x, y, w, h);
} }
static void js_unclip(void *handle) static void js_unclip(drawing *dr)
{ {
js_canvas_unclip(); js_canvas_unclip();
} }
static void js_draw_text(void *handle, int x, int y, int fonttype, static void js_draw_text(drawing *dr, int x, int y, int fonttype,
int fontsize, int align, int colour, int fontsize, int align, int colour,
const char *text) const char *text)
{ {
@ -491,31 +491,31 @@ static void js_draw_text(void *handle, int x, int y, int fonttype,
fontsize, fonttype == FONT_FIXED, text); fontsize, fonttype == FONT_FIXED, text);
} }
static void js_draw_rect(void *handle, int x, int y, int w, int h, int colour) static void js_draw_rect(drawing *dr, int x, int y, int w, int h, int colour)
{ {
js_canvas_draw_rect(x, y, w, h, colour); js_canvas_draw_rect(x, y, w, h, colour);
} }
static void js_draw_line(void *handle, int x1, int y1, int x2, int y2, static void js_draw_line(drawing *dr, int x1, int y1, int x2, int y2,
int colour) int colour)
{ {
js_canvas_draw_line(x1, y1, x2, y2, 1, colour); js_canvas_draw_line(x1, y1, x2, y2, 1, colour);
} }
static void js_draw_thick_line(void *handle, float thickness, static void js_draw_thick_line(drawing *dr, float thickness,
float x1, float y1, float x2, float y2, float x1, float y1, float x2, float y2,
int colour) int colour)
{ {
js_canvas_draw_line(x1, y1, x2, y2, thickness, colour); js_canvas_draw_line(x1, y1, x2, y2, thickness, colour);
} }
static void js_draw_poly(void *handle, const int *coords, int npoints, static void js_draw_poly(drawing *dr, const int *coords, int npoints,
int fillcolour, int outlinecolour) int fillcolour, int outlinecolour)
{ {
js_canvas_draw_poly(coords, npoints, fillcolour, outlinecolour); js_canvas_draw_poly(coords, npoints, fillcolour, outlinecolour);
} }
static void js_draw_circle(void *handle, int cx, int cy, int radius, static void js_draw_circle(drawing *dr, int cx, int cy, int radius,
int fillcolour, int outlinecolour) int fillcolour, int outlinecolour)
{ {
js_canvas_draw_circle(cx, cy, radius, fillcolour, outlinecolour); js_canvas_draw_circle(cx, cy, radius, fillcolour, outlinecolour);
@ -526,7 +526,7 @@ struct blitter {
int w, h; /* easier to retain here */ int w, h; /* easier to retain here */
}; };
static blitter *js_blitter_new(void *handle, int w, int h) static blitter *js_blitter_new(drawing *dr, int w, int h)
{ {
blitter *bl = snew(blitter); blitter *bl = snew(blitter);
bl->w = w; bl->w = w;
@ -535,7 +535,7 @@ static blitter *js_blitter_new(void *handle, int w, int h)
return bl; return bl;
} }
static void js_blitter_free(void *handle, blitter *bl) static void js_blitter_free(drawing *dr, blitter *bl)
{ {
js_canvas_free_blitter(bl->id); js_canvas_free_blitter(bl->id);
sfree(bl); sfree(bl);
@ -569,7 +569,7 @@ static void trim_rect(int *x, int *y, int *w, int *h)
*h = y1 - y0; *h = y1 - y0;
} }
static void js_blitter_save(void *handle, blitter *bl, int x, int y) static void js_blitter_save(drawing *dr, blitter *bl, int x, int y)
{ {
int w = bl->w, h = bl->h; int w = bl->w, h = bl->h;
trim_rect(&x, &y, &w, &h); trim_rect(&x, &y, &w, &h);
@ -577,7 +577,7 @@ static void js_blitter_save(void *handle, blitter *bl, int x, int y)
js_canvas_copy_to_blitter(bl->id, x, y, w, h); js_canvas_copy_to_blitter(bl->id, x, y, w, h);
} }
static void js_blitter_load(void *handle, blitter *bl, int x, int y) static void js_blitter_load(drawing *dr, blitter *bl, int x, int y)
{ {
int w = bl->w, h = bl->h; int w = bl->w, h = bl->h;
trim_rect(&x, &y, &w, &h); trim_rect(&x, &y, &w, &h);
@ -585,30 +585,31 @@ static void js_blitter_load(void *handle, blitter *bl, int x, int y)
js_canvas_copy_from_blitter(bl->id, x, y, w, h); js_canvas_copy_from_blitter(bl->id, x, y, w, h);
} }
static void js_draw_update(void *handle, int x, int y, int w, int h) static void js_draw_update(drawing *dr, int x, int y, int w, int h)
{ {
trim_rect(&x, &y, &w, &h); trim_rect(&x, &y, &w, &h);
if (w > 0 && h > 0) if (w > 0 && h > 0)
js_canvas_draw_update(x, y, w, h); js_canvas_draw_update(x, y, w, h);
} }
static void js_end_draw(void *handle) static void js_end_draw(drawing *dr)
{ {
js_canvas_end_draw(); js_canvas_end_draw();
} }
static void js_status_bar(void *handle, const char *text) static void js_status_bar(drawing *dr, const char *text)
{ {
js_canvas_set_statusbar(text); js_canvas_set_statusbar(text);
} }
static char *js_text_fallback(void *handle, const char *const *strings, static char *js_text_fallback(drawing *dr, const char *const *strings,
int nstrings) int nstrings)
{ {
return dupstr(strings[0]); /* Emscripten has no trouble with UTF-8 */ return dupstr(strings[0]); /* Emscripten has no trouble with UTF-8 */
} }
static const struct drawing_api js_drawing = { static const struct drawing_api js_drawing = {
1,
js_draw_text, js_draw_text,
js_draw_rect, js_draw_rect,
js_draw_line, js_draw_line,

View File

@ -65,7 +65,7 @@ static const char *fuzz_one(bool (*readfn)(void *, void *, int), void *rctx,
char *gamename; char *gamename;
int i, w, h; int i, w, h;
const game *ourgame = NULL; const game *ourgame = NULL;
static const drawing_api drapi = { NULL }; static const drawing_api drapi = { 1, NULL };
midend *me; midend *me;
err = identify_game(&gamename, readfn, rctx); err = identify_game(&gamename, readfn, rctx);

89
gtk.c
View File

@ -329,9 +329,9 @@ void frontend_default_colour(frontend *fe, float *output)
output[0] = output[1] = output[2] = 0.9F; output[0] = output[1] = output[2] = 0.9F;
} }
static void gtk_status_bar(void *handle, const char *text) static void gtk_status_bar(drawing *dr, const char *text)
{ {
frontend *fe = (frontend *)handle; frontend *fe = GET_HANDLE_AS_TYPE(dr, frontend);
if (fe->headless) if (fe->headless)
return; return;
@ -1164,9 +1164,9 @@ static void align_and_draw_text(int index, int align, int x, int y,
* The exported drawing functions. * The exported drawing functions.
*/ */
static void gtk_start_draw(void *handle) static void gtk_start_draw(drawing *dr)
{ {
frontend *fe = (frontend *)handle; frontend *fe = GET_HANDLE_AS_TYPE(dr, frontend);
fe->bbox_l = fe->w; fe->bbox_l = fe->w;
fe->bbox_r = 0; fe->bbox_r = 0;
fe->bbox_u = fe->h; fe->bbox_u = fe->h;
@ -1174,23 +1174,23 @@ static void gtk_start_draw(void *handle)
setup_drawing(fe); setup_drawing(fe);
} }
static void gtk_clip(void *handle, int x, int y, int w, int h) static void gtk_clip(drawing *dr, int x, int y, int w, int h)
{ {
frontend *fe = (frontend *)handle; frontend *fe = GET_HANDLE_AS_TYPE(dr, frontend);
do_clip(fe, x, y, w, h); do_clip(fe, x, y, w, h);
} }
static void gtk_unclip(void *handle) static void gtk_unclip(drawing *dr)
{ {
frontend *fe = (frontend *)handle; frontend *fe = GET_HANDLE_AS_TYPE(dr, frontend);
do_unclip(fe); do_unclip(fe);
} }
static void gtk_draw_text(void *handle, int x, int y, int fonttype, static void gtk_draw_text(drawing *dr, int x, int y, int fonttype,
int fontsize, int align, int colour, int fontsize, int align, int colour,
const char *text) const char *text)
{ {
frontend *fe = (frontend *)handle; frontend *fe = GET_HANDLE_AS_TYPE(dr, frontend);
int i; int i;
/* /*
@ -1220,45 +1220,45 @@ static void gtk_draw_text(void *handle, int x, int y, int fonttype,
align_and_draw_text(fe, i, align, x, y, text); align_and_draw_text(fe, i, align, x, y, text);
} }
static void gtk_draw_rect(void *handle, int x, int y, int w, int h, int colour) static void gtk_draw_rect(drawing *dr, int x, int y, int w, int h, int colour)
{ {
frontend *fe = (frontend *)handle; frontend *fe = GET_HANDLE_AS_TYPE(dr, frontend);
fe->dr_api->set_colour(fe, colour); fe->dr_api->set_colour(fe, colour);
do_draw_rect(fe, x, y, w, h); do_draw_rect(fe, x, y, w, h);
} }
static void gtk_draw_line(void *handle, int x1, int y1, int x2, int y2, static void gtk_draw_line(drawing *dr, int x1, int y1, int x2, int y2,
int colour) int colour)
{ {
frontend *fe = (frontend *)handle; frontend *fe = GET_HANDLE_AS_TYPE(dr, frontend);
fe->dr_api->set_colour(fe, colour); fe->dr_api->set_colour(fe, colour);
do_draw_line(fe, x1, y1, x2, y2); do_draw_line(fe, x1, y1, x2, y2);
} }
static void gtk_draw_thick_line(void *handle, float thickness, static void gtk_draw_thick_line(drawing *dr, float thickness,
float x1, float y1, float x2, float y2, float x1, float y1, float x2, float y2,
int colour) int colour)
{ {
frontend *fe = (frontend *)handle; frontend *fe = GET_HANDLE_AS_TYPE(dr, frontend);
fe->dr_api->set_colour(fe, colour); fe->dr_api->set_colour(fe, colour);
do_draw_thick_line(fe, thickness, x1, y1, x2, y2); do_draw_thick_line(fe, thickness, x1, y1, x2, y2);
} }
static void gtk_draw_poly(void *handle, const int *coords, int npoints, static void gtk_draw_poly(drawing *dr, const int *coords, int npoints,
int fillcolour, int outlinecolour) int fillcolour, int outlinecolour)
{ {
frontend *fe = (frontend *)handle; frontend *fe = GET_HANDLE_AS_TYPE(dr, frontend);
do_draw_poly(fe, coords, npoints, fillcolour, outlinecolour); do_draw_poly(fe, coords, npoints, fillcolour, outlinecolour);
} }
static void gtk_draw_circle(void *handle, int cx, int cy, int radius, static void gtk_draw_circle(drawing *dr, int cx, int cy, int radius,
int fillcolour, int outlinecolour) int fillcolour, int outlinecolour)
{ {
frontend *fe = (frontend *)handle; frontend *fe = GET_HANDLE_AS_TYPE(dr, frontend);
do_draw_circle(fe, cx, cy, radius, fillcolour, outlinecolour); do_draw_circle(fe, cx, cy, radius, fillcolour, outlinecolour);
} }
static blitter *gtk_blitter_new(void *handle, int w, int h) static blitter *gtk_blitter_new(drawing *dr, int w, int h)
{ {
blitter *bl = snew(blitter); blitter *bl = snew(blitter);
setup_blitter(bl, w, h); setup_blitter(bl, w, h);
@ -1267,23 +1267,23 @@ static blitter *gtk_blitter_new(void *handle, int w, int h)
return bl; return bl;
} }
static void gtk_blitter_free(void *handle, blitter *bl) static void gtk_blitter_free(drawing *dr, blitter *bl)
{ {
teardown_blitter(bl); teardown_blitter(bl);
sfree(bl); sfree(bl);
} }
static void gtk_blitter_save(void *handle, blitter *bl, int x, int y) static void gtk_blitter_save(drawing *dr, blitter *bl, int x, int y)
{ {
frontend *fe = (frontend *)handle; frontend *fe = GET_HANDLE_AS_TYPE(dr, frontend);
do_blitter_save(fe, bl, x, y); do_blitter_save(fe, bl, x, y);
bl->x = x; bl->x = x;
bl->y = y; bl->y = y;
} }
static void gtk_blitter_load(void *handle, blitter *bl, int x, int y) static void gtk_blitter_load(drawing *dr, blitter *bl, int x, int y)
{ {
frontend *fe = (frontend *)handle; frontend *fe = GET_HANDLE_AS_TYPE(dr, frontend);
if (x == BLITTER_FROMSAVED && y == BLITTER_FROMSAVED) { if (x == BLITTER_FROMSAVED && y == BLITTER_FROMSAVED) {
x = bl->x; x = bl->x;
y = bl->y; y = bl->y;
@ -1291,18 +1291,18 @@ static void gtk_blitter_load(void *handle, blitter *bl, int x, int y)
do_blitter_load(fe, bl, x, y); do_blitter_load(fe, bl, x, y);
} }
static void gtk_draw_update(void *handle, int x, int y, int w, int h) static void gtk_draw_update(drawing *dr, int x, int y, int w, int h)
{ {
frontend *fe = (frontend *)handle; frontend *fe = GET_HANDLE_AS_TYPE(dr, frontend);
if (fe->bbox_l > x ) fe->bbox_l = x ; if (fe->bbox_l > x ) fe->bbox_l = x ;
if (fe->bbox_r < x+w) fe->bbox_r = x+w; if (fe->bbox_r < x+w) fe->bbox_r = x+w;
if (fe->bbox_u > y ) fe->bbox_u = y ; if (fe->bbox_u > y ) fe->bbox_u = y ;
if (fe->bbox_d < y+h) fe->bbox_d = y+h; if (fe->bbox_d < y+h) fe->bbox_d = y+h;
} }
static void gtk_end_draw(void *handle) static void gtk_end_draw(drawing *dr)
{ {
frontend *fe = (frontend *)handle; frontend *fe = GET_HANDLE_AS_TYPE(dr, frontend);
teardown_drawing(fe); teardown_drawing(fe);
@ -1324,7 +1324,7 @@ static void gtk_end_draw(void *handle)
} }
#ifdef USE_PANGO #ifdef USE_PANGO
static char *gtk_text_fallback(void *handle, const char *const *strings, static char *gtk_text_fallback(drawing *dr, const char *const *strings,
int nstrings) int nstrings)
{ {
/* /*
@ -1336,20 +1336,20 @@ static char *gtk_text_fallback(void *handle, const char *const *strings,
#endif #endif
#ifdef USE_PRINTING #ifdef USE_PRINTING
static void gtk_begin_doc(void *handle, int pages) static void gtk_begin_doc(drawing *dr, int pages)
{ {
frontend *fe = (frontend *)handle; frontend *fe = GET_HANDLE_AS_TYPE(dr, frontend);
gtk_print_operation_set_n_pages(fe->printop, pages); gtk_print_operation_set_n_pages(fe->printop, pages);
} }
static void gtk_begin_page(void *handle, int number) static void gtk_begin_page(drawing *dr, int number)
{ {
} }
static void gtk_begin_puzzle(void *handle, float xm, float xc, static void gtk_begin_puzzle(drawing *dr, float xm, float xc,
float ym, float yc, int pw, int ph, float wmm) float ym, float yc, int pw, int ph, float wmm)
{ {
frontend *fe = (frontend *)handle; frontend *fe = GET_HANDLE_AS_TYPE(dr, frontend);
double ppw, pph, pox, poy, dpmmx, dpmmy; double ppw, pph, pox, poy, dpmmx, dpmmy;
double scale; double scale;
@ -1385,29 +1385,29 @@ static void gtk_begin_puzzle(void *handle, float xm, float xc,
fe->hatchspace = 1.0 * pw / wmm; fe->hatchspace = 1.0 * pw / wmm;
} }
static void gtk_end_puzzle(void *handle) static void gtk_end_puzzle(drawing *dr)
{ {
frontend *fe = (frontend *)handle; frontend *fe = GET_HANDLE_AS_TYPE(dr, frontend);
cairo_restore(fe->cr); cairo_restore(fe->cr);
} }
static void gtk_end_page(void *handle, int number) static void gtk_end_page(drawing *dr, int number)
{ {
} }
static void gtk_end_doc(void *handle) static void gtk_end_doc(drawing *dr)
{ {
} }
static void gtk_line_width(void *handle, float width) static void gtk_line_width(drawing *dr, float width)
{ {
frontend *fe = (frontend *)handle; frontend *fe = GET_HANDLE_AS_TYPE(dr, frontend);
cairo_set_line_width(fe->cr, width); cairo_set_line_width(fe->cr, width);
} }
static void gtk_line_dotted(void *handle, bool dotted) static void gtk_line_dotted(drawing *dr, bool dotted)
{ {
frontend *fe = (frontend *)handle; frontend *fe = GET_HANDLE_AS_TYPE(dr, frontend);
if (dotted) { if (dotted) {
const double dash = 35.0; const double dash = 35.0;
@ -1435,6 +1435,7 @@ static const struct internal_drawing_api internal_printing = {
#endif #endif
static const struct drawing_api gtk_drawing = { static const struct drawing_api gtk_drawing = {
1,
gtk_draw_text, gtk_draw_text,
gtk_draw_rect, gtk_draw_rect,
gtk_draw_line, gtk_draw_line,

View File

@ -54,59 +54,59 @@ void frontend_default_colour(frontend *fe, float *output)
output[0] = output[1]= output[2] = 0.8f; output[0] = output[1]= output[2] = 0.8f;
} }
void nestedvm_status_bar(void *handle, const char *text) void nestedvm_status_bar(drawing *dr, const char *text)
{ {
_call_java(4,0,(int)text,0); _call_java(4,0,(int)text,0);
} }
void nestedvm_start_draw(void *handle) void nestedvm_start_draw(drawing *dr)
{ {
frontend *fe = (frontend *)handle; frontend *fe = GET_HANDLE_AS_TYPE(dr, frontend);
_call_java(5, 0, fe->w, fe->h); _call_java(5, 0, fe->w, fe->h);
_call_java(4, 1, fe->ox, fe->oy); _call_java(4, 1, fe->ox, fe->oy);
} }
void nestedvm_clip(void *handle, int x, int y, int w, int h) void nestedvm_clip(drawing *dr, int x, int y, int w, int h)
{ {
frontend *fe = (frontend *)handle; frontend *fe = GET_HANDLE_AS_TYPE(dr, frontend);
_call_java(5, w, h, 0); _call_java(5, w, h, 0);
_call_java(4, 3, x + fe->ox, y + fe->oy); _call_java(4, 3, x + fe->ox, y + fe->oy);
} }
void nestedvm_unclip(void *handle) void nestedvm_unclip(drawing *dr)
{ {
frontend *fe = (frontend *)handle; frontend *fe = GET_HANDLE_AS_TYPE(dr, frontend);
_call_java(4, 4, fe->ox, fe->oy); _call_java(4, 4, fe->ox, fe->oy);
} }
void nestedvm_draw_text(void *handle, int x, int y, int fonttype, int fontsize, void nestedvm_draw_text(drawing *dr, int x, int y, int fonttype, int fontsize,
int align, int colour, const char *text) int align, int colour, const char *text)
{ {
frontend *fe = (frontend *)handle; frontend *fe = GET_HANDLE_AS_TYPE(dr, frontend);
_call_java(5, x + fe->ox, y + fe->oy, _call_java(5, x + fe->ox, y + fe->oy,
(fonttype == FONT_FIXED ? 0x10 : 0x0) | align); (fonttype == FONT_FIXED ? 0x10 : 0x0) | align);
_call_java(7, fontsize, colour, (int)text); _call_java(7, fontsize, colour, (int)text);
} }
void nestedvm_draw_rect(void *handle, int x, int y, int w, int h, int colour) void nestedvm_draw_rect(drawing *dr, int x, int y, int w, int h, int colour)
{ {
frontend *fe = (frontend *)handle; frontend *fe = GET_HANDLE_AS_TYPE(dr, frontend);
_call_java(5, w, h, colour); _call_java(5, w, h, colour);
_call_java(4, 5, x + fe->ox, y + fe->oy); _call_java(4, 5, x + fe->ox, y + fe->oy);
} }
void nestedvm_draw_line(void *handle, int x1, int y1, int x2, int y2, void nestedvm_draw_line(drawing *dr, int x1, int y1, int x2, int y2,
int colour) int colour)
{ {
frontend *fe = (frontend *)handle; frontend *fe = GET_HANDLE_AS_TYPE(dr, frontend);
_call_java(5, x2 + fe->ox, y2 + fe->oy, colour); _call_java(5, x2 + fe->ox, y2 + fe->oy, colour);
_call_java(4, 6, x1 + fe->ox, y1 + fe->oy); _call_java(4, 6, x1 + fe->ox, y1 + fe->oy);
} }
void nestedvm_draw_poly(void *handle, int *coords, int npoints, void nestedvm_draw_poly(drawing *dr, int *coords, int npoints,
int fillcolour, int outlinecolour) int fillcolour, int outlinecolour)
{ {
frontend *fe = (frontend *)handle; frontend *fe = GET_HANDLE_AS_TYPE(dr, frontend);
int i; int i;
_call_java(4, 7, npoints, 0); _call_java(4, 7, npoints, 0);
for (i = 0; i < npoints; i++) { for (i = 0; i < npoints; i++) {
@ -115,10 +115,10 @@ void nestedvm_draw_poly(void *handle, int *coords, int npoints,
_call_java(4, 8, outlinecolour, fillcolour); _call_java(4, 8, outlinecolour, fillcolour);
} }
void nestedvm_draw_circle(void *handle, int cx, int cy, int radius, void nestedvm_draw_circle(drawing *dr, int cx, int cy, int radius,
int fillcolour, int outlinecolour) int fillcolour, int outlinecolour)
{ {
frontend *fe = (frontend *)handle; frontend *fe = GET_HANDLE_AS_TYPE(dr, frontend);
_call_java(5, cx+fe->ox, cy+fe->oy, radius); _call_java(5, cx+fe->ox, cy+fe->oy, radius);
_call_java(4, 9, outlinecolour, fillcolour); _call_java(4, 9, outlinecolour, fillcolour);
} }
@ -127,7 +127,7 @@ struct blitter {
int handle, w, h, x, y; int handle, w, h, x, y;
}; };
blitter *nestedvm_blitter_new(void *handle, int w, int h) blitter *nestedvm_blitter_new(drawing *dr, int w, int h)
{ {
blitter *bl = snew(blitter); blitter *bl = snew(blitter);
bl->handle = -1; bl->handle = -1;
@ -136,16 +136,16 @@ blitter *nestedvm_blitter_new(void *handle, int w, int h)
return bl; return bl;
} }
void nestedvm_blitter_free(void *handle, blitter *bl) void nestedvm_blitter_free(drawing *dr, blitter *bl)
{ {
if (bl->handle != -1) if (bl->handle != -1)
_call_java(4, 11, bl->handle, 0); _call_java(4, 11, bl->handle, 0);
sfree(bl); sfree(bl);
} }
void nestedvm_blitter_save(void *handle, blitter *bl, int x, int y) void nestedvm_blitter_save(drawing *dr, blitter *bl, int x, int y)
{ {
frontend *fe = (frontend *)handle; frontend *fe = GET_HANDLE_AS_TYPE(dr, frontend);
if (bl->handle == -1) if (bl->handle == -1)
bl->handle = _call_java(4,10,bl->w, bl->h); bl->handle = _call_java(4,10,bl->w, bl->h);
bl->x = x; bl->x = x;
@ -153,9 +153,9 @@ void nestedvm_blitter_save(void *handle, blitter *bl, int x, int y)
_call_java(8, bl->handle, x + fe->ox, y + fe->oy); _call_java(8, bl->handle, x + fe->ox, y + fe->oy);
} }
void nestedvm_blitter_load(void *handle, blitter *bl, int x, int y) void nestedvm_blitter_load(drawing *dr, blitter *bl, int x, int y)
{ {
frontend *fe = (frontend *)handle; frontend *fe = GET_HANDLE_AS_TYPE(dr, frontend);
assert(bl->handle != -1); assert(bl->handle != -1);
if (x == BLITTER_FROMSAVED && y == BLITTER_FROMSAVED) { if (x == BLITTER_FROMSAVED && y == BLITTER_FROMSAVED) {
x = bl->x; x = bl->x;
@ -164,12 +164,12 @@ void nestedvm_blitter_load(void *handle, blitter *bl, int x, int y)
_call_java(9, bl->handle, x + fe->ox, y + fe->oy); _call_java(9, bl->handle, x + fe->ox, y + fe->oy);
} }
void nestedvm_end_draw(void *handle) void nestedvm_end_draw(drawing *dr)
{ {
_call_java(4,2,0,0); _call_java(4,2,0,0);
} }
char *nestedvm_text_fallback(void *handle, const char *const *strings, char *nestedvm_text_fallback(drawing *dr, const char *const *strings,
int nstrings) int nstrings)
{ {
/* /*
@ -180,6 +180,7 @@ char *nestedvm_text_fallback(void *handle, const char *const *strings,
} }
const struct drawing_api nestedvm_drawing = { const struct drawing_api nestedvm_drawing = {
1,
nestedvm_draw_text, nestedvm_draw_text,
nestedvm_draw_rect, nestedvm_draw_rect,
nestedvm_draw_line, nestedvm_draw_line,

View File

@ -13,7 +13,6 @@ void get_random_seed(void **randseed, int *randseedsize)
{ char *c = snewn(1, char); *c = 0; *randseed = c; *randseedsize = 1; } { char *c = snewn(1, char); *c = 0; *randseed = c; *randseedsize = 1; }
void deactivate_timer(frontend *fe) {} void deactivate_timer(frontend *fe) {}
void activate_timer(frontend *fe) {} void activate_timer(frontend *fe) {}
struct drawing { char dummy; };
drawing *drawing_new(const drawing_api *api, midend *me, void *handle) drawing *drawing_new(const drawing_api *api, midend *me, void *handle)
{ return snew(drawing); } { return snew(drawing); }
void drawing_free(drawing *dr) { sfree(dr); } void drawing_free(drawing *dr) { sfree(dr); }

63
osx.m
View File

@ -1507,10 +1507,10 @@ struct frontend {
/* /*
* Drawing routines called by the midend. * Drawing routines called by the midend.
*/ */
static void osx_draw_polygon(void *handle, const int *coords, int npoints, static void osx_draw_polygon(drawing *dr, const int *coords, int npoints,
int fillcolour, int outlinecolour) int fillcolour, int outlinecolour)
{ {
frontend *fe = (frontend *)handle; frontend *fe = GET_HANDLE_AS_TYPE(dr, frontend);
NSBezierPath *path = [NSBezierPath bezierPath]; NSBezierPath *path = [NSBezierPath bezierPath];
int i; int i;
@ -1536,10 +1536,10 @@ static void osx_draw_polygon(void *handle, const int *coords, int npoints,
[fe->colours[outlinecolour] set]; [fe->colours[outlinecolour] set];
[path stroke]; [path stroke];
} }
static void osx_draw_circle(void *handle, int cx, int cy, int radius, static void osx_draw_circle(drawing *dr, int cx, int cy, int radius,
int fillcolour, int outlinecolour) int fillcolour, int outlinecolour)
{ {
frontend *fe = (frontend *)handle; frontend *fe = GET_HANDLE_AS_TYPE(dr, frontend);
NSBezierPath *path = [NSBezierPath bezierPath]; NSBezierPath *path = [NSBezierPath bezierPath];
[[NSGraphicsContext currentContext] setShouldAntialias:YES]; [[NSGraphicsContext currentContext] setShouldAntialias:YES];
@ -1559,9 +1559,9 @@ static void osx_draw_circle(void *handle, int cx, int cy, int radius,
[fe->colours[outlinecolour] set]; [fe->colours[outlinecolour] set];
[path stroke]; [path stroke];
} }
static void osx_draw_line(void *handle, int x1, int y1, int x2, int y2, int colour) static void osx_draw_line(drawing *dr, int x1, int y1, int x2, int y2, int colour)
{ {
frontend *fe = (frontend *)handle; frontend *fe = GET_HANDLE_AS_TYPE(dr, frontend);
NSBezierPath *path = [NSBezierPath bezierPath]; NSBezierPath *path = [NSBezierPath bezierPath];
NSPoint p1 = { x1 + 0.5, fe->h - y1 - 0.5 }; NSPoint p1 = { x1 + 0.5, fe->h - y1 - 0.5 };
NSPoint p2 = { x2 + 0.5, fe->h - y2 - 0.5 }; NSPoint p2 = { x2 + 0.5, fe->h - y2 - 0.5 };
@ -1579,12 +1579,12 @@ static void osx_draw_line(void *handle, int x1, int y1, int x2, int y2, int colo
} }
static void osx_draw_thick_line( static void osx_draw_thick_line(
void *handle, float thickness, drawing *dr, float thickness,
float x1, float y1, float x1, float y1,
float x2, float y2, float x2, float y2,
int colour) int colour)
{ {
frontend *fe = (frontend *)handle; frontend *fe = GET_HANDLE_AS_TYPE(dr, frontend);
NSBezierPath *path = [NSBezierPath bezierPath]; NSBezierPath *path = [NSBezierPath bezierPath];
assert(colour >= 0 && colour < fe->ncolours); assert(colour >= 0 && colour < fe->ncolours);
@ -1597,9 +1597,9 @@ static void osx_draw_thick_line(
[path stroke]; [path stroke];
} }
static void osx_draw_rect(void *handle, int x, int y, int w, int h, int colour) static void osx_draw_rect(drawing *dr, int x, int y, int w, int h, int colour)
{ {
frontend *fe = (frontend *)handle; frontend *fe = GET_HANDLE_AS_TYPE(dr, frontend);
NSRect r = { {x, fe->h - y - h}, {w,h} }; NSRect r = { {x, fe->h - y - h}, {w,h} };
[[NSGraphicsContext currentContext] setShouldAntialias:NO]; [[NSGraphicsContext currentContext] setShouldAntialias:NO];
@ -1609,11 +1609,11 @@ static void osx_draw_rect(void *handle, int x, int y, int w, int h, int colour)
NSRectFill(r); NSRectFill(r);
} }
static void osx_draw_text(void *handle, int x, int y, int fonttype, static void osx_draw_text(drawing *dr, int x, int y, int fonttype,
int fontsize, int align, int colour, int fontsize, int align, int colour,
const char *text) const char *text)
{ {
frontend *fe = (frontend *)handle; frontend *fe = GET_HANDLE_AS_TYPE(dr, frontend);
NSString *string = [NSString stringWithUTF8String:text]; NSString *string = [NSString stringWithUTF8String:text];
NSDictionary *attr; NSDictionary *attr;
NSFont *font; NSFont *font;
@ -1646,7 +1646,7 @@ static void osx_draw_text(void *handle, int x, int y, int fonttype,
[string drawAtPoint:point withAttributes:attr]; [string drawAtPoint:point withAttributes:attr];
} }
static char *osx_text_fallback(void *handle, const char *const *strings, static char *osx_text_fallback(drawing *dr, const char *const *strings,
int nstrings) int nstrings)
{ {
/* /*
@ -1660,7 +1660,7 @@ struct blitter {
int x, y; int x, y;
NSImage *img; NSImage *img;
}; };
static blitter *osx_blitter_new(void *handle, int w, int h) static blitter *osx_blitter_new(drawing *dr, int w, int h)
{ {
blitter *bl = snew(blitter); blitter *bl = snew(blitter);
bl->x = bl->y = -1; bl->x = bl->y = -1;
@ -1669,14 +1669,14 @@ static blitter *osx_blitter_new(void *handle, int w, int h)
bl->img = [[NSImage alloc] initWithSize:NSMakeSize(w, h)]; bl->img = [[NSImage alloc] initWithSize:NSMakeSize(w, h)];
return bl; return bl;
} }
static void osx_blitter_free(void *handle, blitter *bl) static void osx_blitter_free(drawing *dr, blitter *bl)
{ {
[bl->img release]; [bl->img release];
sfree(bl); sfree(bl);
} }
static void osx_blitter_save(void *handle, blitter *bl, int x, int y) static void osx_blitter_save(drawing *dr, blitter *bl, int x, int y)
{ {
frontend *fe = (frontend *)handle; frontend *fe = GET_HANDLE_AS_TYPE(dr, frontend);
int sx, sy, sX, sY, dx, dy, dX, dY; int sx, sy, sX, sY, dx, dy, dX, dY;
[fe->image unlockFocus]; [fe->image unlockFocus];
[bl->img lockFocus]; [bl->img lockFocus];
@ -1718,9 +1718,9 @@ static void osx_blitter_save(void *handle, blitter *bl, int x, int y)
bl->x = x; bl->x = x;
bl->y = y; bl->y = y;
} }
static void osx_blitter_load(void *handle, blitter *bl, int x, int y) static void osx_blitter_load(drawing *dr, blitter *bl, int x, int y)
{ {
frontend *fe = (frontend *)handle; frontend *fe = GET_HANDLE_AS_TYPE(dr, frontend);
if (x == BLITTER_FROMSAVED && y == BLITTER_FROMSAVED) { if (x == BLITTER_FROMSAVED && y == BLITTER_FROMSAVED) {
x = bl->x; x = bl->x;
y = bl->y; y = bl->y;
@ -1729,14 +1729,14 @@ static void osx_blitter_load(void *handle, blitter *bl, int x, int y)
fromRect:NSMakeRect(0, 0, bl->w, bl->h) fromRect:NSMakeRect(0, 0, bl->w, bl->h)
operation:NSCompositeCopy fraction:1.0]; operation:NSCompositeCopy fraction:1.0];
} }
static void osx_draw_update(void *handle, int x, int y, int w, int h) static void osx_draw_update(drawing *dr, int x, int y, int w, int h)
{ {
frontend *fe = (frontend *)handle; frontend *fe = GET_HANDLE_AS_TYPE(dr, frontend);
[fe->view setNeedsDisplayInRect:NSMakeRect(x, fe->h - y - h, w, h)]; [fe->view setNeedsDisplayInRect:NSMakeRect(x, fe->h - y - h, w, h)];
} }
static void osx_clip(void *handle, int x, int y, int w, int h) static void osx_clip(drawing *dr, int x, int y, int w, int h)
{ {
frontend *fe = (frontend *)handle; frontend *fe = GET_HANDLE_AS_TYPE(dr, frontend);
NSRect r = { {x, fe->h - y - h}, {w, h} }; NSRect r = { {x, fe->h - y - h}, {w, h} };
if (!fe->clipped) if (!fe->clipped)
@ -1744,31 +1744,32 @@ static void osx_clip(void *handle, int x, int y, int w, int h)
[NSBezierPath clipRect:r]; [NSBezierPath clipRect:r];
fe->clipped = true; fe->clipped = true;
} }
static void osx_unclip(void *handle) static void osx_unclip(drawing *dr)
{ {
frontend *fe = (frontend *)handle; frontend *fe = GET_HANDLE_AS_TYPE(dr, frontend);
if (fe->clipped) if (fe->clipped)
[[NSGraphicsContext currentContext] restoreGraphicsState]; [[NSGraphicsContext currentContext] restoreGraphicsState];
fe->clipped = false; fe->clipped = false;
} }
static void osx_start_draw(void *handle) static void osx_start_draw(drawing *dr)
{ {
frontend *fe = (frontend *)handle; frontend *fe = GET_HANDLE_AS_TYPE(dr, frontend);
[fe->image lockFocus]; [fe->image lockFocus];
fe->clipped = false; fe->clipped = false;
} }
static void osx_end_draw(void *handle) static void osx_end_draw(drawing *dr)
{ {
frontend *fe = (frontend *)handle; frontend *fe = GET_HANDLE_AS_TYPE(dr, frontend);
[fe->image unlockFocus]; [fe->image unlockFocus];
} }
static void osx_status_bar(void *handle, const char *text) static void osx_status_bar(drawing *dr, const char *text)
{ {
frontend *fe = (frontend *)handle; frontend *fe = GET_HANDLE_AS_TYPE(dr, frontend);
[fe->window setStatusLine:text]; [fe->window setStatusLine:text];
} }
const struct drawing_api osx_drawing = { const struct drawing_api osx_drawing = {
1,
osx_draw_text, osx_draw_text,
osx_draw_rect, osx_draw_rect,
osx_draw_line, osx_draw_line,

65
ps.c
View File

@ -99,11 +99,11 @@ static void ps_stroke(psdata *ps, int colour)
ps_setcolour_internal(ps, colour, " stroke"); ps_setcolour_internal(ps, colour, " stroke");
} }
static void ps_draw_text(void *handle, int x, int y, int fonttype, static void ps_draw_text(drawing *dr, int x, int y, int fonttype,
int fontsize, int align, int colour, int fontsize, int align, int colour,
const char *text) const char *text)
{ {
psdata *ps = (psdata *)handle; psdata *ps = GET_HANDLE_AS_TYPE(dr, psdata);
y = ps->ytop - y; y = ps->ytop - y;
ps_setcolour(ps, colour); ps_setcolour(ps, colour);
@ -133,9 +133,9 @@ static void ps_draw_text(void *handle, int x, int y, int fonttype,
ps_printf(ps, "show\n"); ps_printf(ps, "show\n");
} }
static void ps_draw_rect(void *handle, int x, int y, int w, int h, int colour) static void ps_draw_rect(drawing *dr, int x, int y, int w, int h, int colour)
{ {
psdata *ps = (psdata *)handle; psdata *ps = GET_HANDLE_AS_TYPE(dr, psdata);
y = ps->ytop - y; y = ps->ytop - y;
/* /*
@ -146,10 +146,10 @@ static void ps_draw_rect(void *handle, int x, int y, int w, int h, int colour)
ps_fill(ps, colour); ps_fill(ps, colour);
} }
static void ps_draw_line(void *handle, int x1, int y1, int x2, int y2, static void ps_draw_line(drawing *dr, int x1, int y1, int x2, int y2,
int colour) int colour)
{ {
psdata *ps = (psdata *)handle; psdata *ps = GET_HANDLE_AS_TYPE(dr, psdata);
y1 = ps->ytop - y1; y1 = ps->ytop - y1;
y2 = ps->ytop - y2; y2 = ps->ytop - y2;
@ -157,10 +157,10 @@ static void ps_draw_line(void *handle, int x1, int y1, int x2, int y2,
ps_stroke(ps, colour); ps_stroke(ps, colour);
} }
static void ps_draw_polygon(void *handle, const int *coords, int npoints, static void ps_draw_polygon(drawing *dr, const int *coords, int npoints,
int fillcolour, int outlinecolour) int fillcolour, int outlinecolour)
{ {
psdata *ps = (psdata *)handle; psdata *ps = GET_HANDLE_AS_TYPE(dr, psdata);
int i; int i;
@ -179,10 +179,10 @@ static void ps_draw_polygon(void *handle, const int *coords, int npoints,
ps_stroke(ps, outlinecolour); ps_stroke(ps, outlinecolour);
} }
static void ps_draw_circle(void *handle, int cx, int cy, int radius, static void ps_draw_circle(drawing *dr, int cx, int cy, int radius,
int fillcolour, int outlinecolour) int fillcolour, int outlinecolour)
{ {
psdata *ps = (psdata *)handle; psdata *ps = GET_HANDLE_AS_TYPE(dr, psdata);
cy = ps->ytop - cy; cy = ps->ytop - cy;
@ -196,21 +196,21 @@ static void ps_draw_circle(void *handle, int cx, int cy, int radius,
ps_stroke(ps, outlinecolour); ps_stroke(ps, outlinecolour);
} }
static void ps_unclip(void *handle) static void ps_unclip(drawing *dr)
{ {
psdata *ps = (psdata *)handle; psdata *ps = GET_HANDLE_AS_TYPE(dr, psdata);
assert(ps->clipped); assert(ps->clipped);
ps_printf(ps, "grestore\n"); ps_printf(ps, "grestore\n");
ps->clipped = false; ps->clipped = false;
} }
static void ps_clip(void *handle, int x, int y, int w, int h) static void ps_clip(drawing *dr, int x, int y, int w, int h)
{ {
psdata *ps = (psdata *)handle; psdata *ps = GET_HANDLE_AS_TYPE(dr, psdata);
if (ps->clipped) if (ps->clipped)
ps_unclip(ps); ps_unclip(dr);
y = ps->ytop - y; y = ps->ytop - y;
/* /*
@ -223,16 +223,16 @@ static void ps_clip(void *handle, int x, int y, int w, int h)
ps->clipped = true; ps->clipped = true;
} }
static void ps_line_width(void *handle, float width) static void ps_line_width(drawing *dr, float width)
{ {
psdata *ps = (psdata *)handle; psdata *ps = GET_HANDLE_AS_TYPE(dr, psdata);
ps_printf(ps, "%g setlinewidth\n", width); ps_printf(ps, "%g setlinewidth\n", width);
} }
static void ps_line_dotted(void *handle, bool dotted) static void ps_line_dotted(drawing *dr, bool dotted)
{ {
psdata *ps = (psdata *)handle; psdata *ps = GET_HANDLE_AS_TYPE(dr, psdata);
if (dotted) { if (dotted) {
ps_printf(ps, "[ currentlinewidth 3 mul ] 0 setdash\n"); ps_printf(ps, "[ currentlinewidth 3 mul ] 0 setdash\n");
@ -241,7 +241,7 @@ static void ps_line_dotted(void *handle, bool dotted)
} }
} }
static char *ps_text_fallback(void *handle, const char *const *strings, static char *ps_text_fallback(drawing *dr, const char *const *strings,
int nstrings) int nstrings)
{ {
/* /*
@ -284,9 +284,9 @@ static char *ps_text_fallback(void *handle, const char *const *strings,
return NULL; return NULL;
} }
static void ps_begin_doc(void *handle, int pages) static void ps_begin_doc(drawing *dr, int pages)
{ {
psdata *ps = (psdata *)handle; psdata *ps = GET_HANDLE_AS_TYPE(dr, psdata);
fputs("%!PS-Adobe-3.0\n", ps->fp); fputs("%!PS-Adobe-3.0\n", ps->fp);
fputs("%%Creator: Simon Tatham's Portable Puzzle Collection\n", ps->fp); fputs("%%Creator: Simon Tatham's Portable Puzzle Collection\n", ps->fp);
@ -331,18 +331,18 @@ static void ps_begin_doc(void *handle, int pages)
fputs("%%EndProlog\n", ps->fp); fputs("%%EndProlog\n", ps->fp);
} }
static void ps_begin_page(void *handle, int number) static void ps_begin_page(drawing *dr, int number)
{ {
psdata *ps = (psdata *)handle; psdata *ps = GET_HANDLE_AS_TYPE(dr, psdata);
fprintf(ps->fp, "%%%%Page: %d %d\ngsave save\n%g dup scale\n", fprintf(ps->fp, "%%%%Page: %d %d\ngsave save\n%g dup scale\n",
number, number, 72.0 / 25.4); number, number, 72.0 / 25.4);
} }
static void ps_begin_puzzle(void *handle, float xm, float xc, static void ps_begin_puzzle(drawing *dr, float xm, float xc,
float ym, float yc, int pw, int ph, float wmm) float ym, float yc, int pw, int ph, float wmm)
{ {
psdata *ps = (psdata *)handle; psdata *ps = GET_HANDLE_AS_TYPE(dr, psdata);
fprintf(ps->fp, "gsave\n" fprintf(ps->fp, "gsave\n"
"clippath flattenpath pathbbox pop pop translate\n" "clippath flattenpath pathbbox pop pop translate\n"
@ -358,28 +358,29 @@ static void ps_begin_puzzle(void *handle, float xm, float xc,
ps->hatchspace = 1.0 * pw / wmm; ps->hatchspace = 1.0 * pw / wmm;
} }
static void ps_end_puzzle(void *handle) static void ps_end_puzzle(drawing *dr)
{ {
psdata *ps = (psdata *)handle; psdata *ps = GET_HANDLE_AS_TYPE(dr, psdata);
fputs("grestore\n", ps->fp); fputs("grestore\n", ps->fp);
} }
static void ps_end_page(void *handle, int number) static void ps_end_page(drawing *dr, int number)
{ {
psdata *ps = (psdata *)handle; psdata *ps = GET_HANDLE_AS_TYPE(dr, psdata);
fputs("restore grestore showpage\n", ps->fp); fputs("restore grestore showpage\n", ps->fp);
} }
static void ps_end_doc(void *handle) static void ps_end_doc(drawing *dr)
{ {
psdata *ps = (psdata *)handle; psdata *ps = GET_HANDLE_AS_TYPE(dr, psdata);
fputs("%%EOF\n", ps->fp); fputs("%%EOF\n", ps->fp);
} }
static const struct drawing_api ps_drawing = { static const struct drawing_api ps_drawing = {
1,
ps_draw_text, ps_draw_text,
ps_draw_rect, ps_draw_rect,
ps_draw_line, ps_draw_line,

View File

@ -755,43 +755,75 @@ struct game {
int flags; int flags;
}; };
#define GET_HANDLE_AS_TYPE(dr, type) ((type*)((dr)->handle))
struct drawing {
const drawing_api *api;
void *handle;
};
/* /*
* Data structure containing the drawing API implemented by the * Data structure containing the drawing API implemented by the
* front end and also by cross-platform printing modules such as * front end and also by cross-platform printing modules such as
* PostScript. * PostScript.
*/ */
struct drawing_api { struct drawing_api {
void (*draw_text)(void *handle, int x, int y, int fonttype, int fontsize, /*
* API version. Increment this when there is a breaking change to
* this API which requires front ends to change.
*
* There is expliclty not a public LATEST_API_VERSION define, so
* that front end authors will need to manually intervene when the
* version number changes. Naturally, this should be done
* sparingly.
*
* If a function is ever added to this API, please move this field
* to the _end_ of the structure, so that changes thereafter will
* shift the position of the version and lead to a compilation
* error if old implementations are not updated. Then remove this
* comment.
*
* The latest version number is 1.
*
* Change log:
*
* Version 1 (2024-08-14): Introduction of version number, in
* conjunction with changing every API function to take `drawing *`
* instead of `void *`.
*/
int version;
void (*draw_text)(drawing *dr, int x, int y, int fonttype, int fontsize,
int align, int colour, const char *text); int align, int colour, const char *text);
void (*draw_rect)(void *handle, int x, int y, int w, int h, int colour); void (*draw_rect)(drawing *dr, int x, int y, int w, int h, int colour);
void (*draw_line)(void *handle, int x1, int y1, int x2, int y2, void (*draw_line)(drawing *dr, int x1, int y1, int x2, int y2,
int colour); int colour);
void (*draw_polygon)(void *handle, const int *coords, int npoints, void (*draw_polygon)(drawing *dr, const int *coords, int npoints,
int fillcolour, int outlinecolour); int fillcolour, int outlinecolour);
void (*draw_circle)(void *handle, int cx, int cy, int radius, void (*draw_circle)(drawing *dr, int cx, int cy, int radius,
int fillcolour, int outlinecolour); int fillcolour, int outlinecolour);
void (*draw_update)(void *handle, int x, int y, int w, int h); void (*draw_update)(drawing *dr, int x, int y, int w, int h);
void (*clip)(void *handle, int x, int y, int w, int h); void (*clip)(drawing *dr, int x, int y, int w, int h);
void (*unclip)(void *handle); void (*unclip)(drawing *dr);
void (*start_draw)(void *handle); void (*start_draw)(drawing *dr);
void (*end_draw)(void *handle); void (*end_draw)(drawing *dr);
void (*status_bar)(void *handle, const char *text); void (*status_bar)(drawing *dr, const char *text);
blitter *(*blitter_new)(void *handle, int w, int h); blitter *(*blitter_new)(drawing *dr, int w, int h);
void (*blitter_free)(void *handle, blitter *bl); void (*blitter_free)(drawing *dr, blitter *bl);
void (*blitter_save)(void *handle, blitter *bl, int x, int y); void (*blitter_save)(drawing *dr, blitter *bl, int x, int y);
void (*blitter_load)(void *handle, blitter *bl, int x, int y); void (*blitter_load)(drawing *dr, blitter *bl, int x, int y);
void (*begin_doc)(void *handle, int pages); void (*begin_doc)(drawing *dr, int pages);
void (*begin_page)(void *handle, int number); void (*begin_page)(drawing *dr, int number);
void (*begin_puzzle)(void *handle, float xm, float xc, void (*begin_puzzle)(drawing *dr, float xm, float xc,
float ym, float yc, int pw, int ph, float wmm); float ym, float yc, int pw, int ph, float wmm);
void (*end_puzzle)(void *handle); void (*end_puzzle)(drawing *dr);
void (*end_page)(void *handle, int number); void (*end_page)(drawing *dr, int number);
void (*end_doc)(void *handle); void (*end_doc)(drawing *dr);
void (*line_width)(void *handle, float width); void (*line_width)(drawing *dr, float width);
void (*line_dotted)(void *handle, bool dotted); void (*line_dotted)(drawing *dr, bool dotted);
char *(*text_fallback)(void *handle, const char *const *strings, char *(*text_fallback)(drawing *dr, const char *const *strings,
int nstrings); int nstrings);
void (*draw_thick_line)(void *handle, float thickness, void (*draw_thick_line)(drawing *dr, float thickness,
float x1, float y1, float x2, float y2, float x1, float y1, float x2, float y2,
int colour); int colour);
}; };

View File

@ -253,14 +253,14 @@ void get_random_seed(void **randseed, int *randseedsize)
*randseedsize = sizeof(SYSTEMTIME); *randseedsize = sizeof(SYSTEMTIME);
} }
static void win_status_bar(void *handle, const char *text) static void win_status_bar(drawing *dr, const char *text)
{ {
frontend *fe = (frontend *)handle; frontend *fe = GET_HANDLE_AS_TYPE(dr, frontend);
SetWindowText(fe->statusbar, text); SetWindowText(fe->statusbar, text);
} }
static blitter *win_blitter_new(void *handle, int w, int h) static blitter *win_blitter_new(drawing *dr, int w, int h)
{ {
blitter *bl = snew(blitter); blitter *bl = snew(blitter);
@ -272,7 +272,7 @@ static blitter *win_blitter_new(void *handle, int w, int h)
return bl; return bl;
} }
static void win_blitter_free(void *handle, blitter *bl) static void win_blitter_free(drawing *dr, blitter *bl)
{ {
if (bl->bitmap) DeleteObject(bl->bitmap); if (bl->bitmap) DeleteObject(bl->bitmap);
sfree(bl); sfree(bl);
@ -287,9 +287,9 @@ static void blitter_mkbitmap(frontend *fe, blitter *bl)
/* BitBlt(dstDC, dstX, dstY, dstW, dstH, srcDC, srcX, srcY, dType) */ /* BitBlt(dstDC, dstX, dstY, dstW, dstH, srcDC, srcX, srcY, dType) */
static void win_blitter_save(void *handle, blitter *bl, int x, int y) static void win_blitter_save(drawing *dr, blitter *bl, int x, int y)
{ {
frontend *fe = (frontend *)handle; frontend *fe = GET_HANDLE_AS_TYPE(dr, frontend);
HDC hdc_win, hdc_blit; HDC hdc_win, hdc_blit;
HBITMAP prev_blit; HBITMAP prev_blit;
@ -316,9 +316,9 @@ static void win_blitter_save(void *handle, blitter *bl, int x, int y)
ReleaseDC(fe->hwnd, hdc_win); ReleaseDC(fe->hwnd, hdc_win);
} }
static void win_blitter_load(void *handle, blitter *bl, int x, int y) static void win_blitter_load(drawing *dr, blitter *bl, int x, int y)
{ {
frontend *fe = (frontend *)handle; frontend *fe = GET_HANDLE_AS_TYPE(dr, frontend);
HDC hdc_win, hdc_blit; HDC hdc_win, hdc_blit;
HBITMAP prev_blit; HBITMAP prev_blit;
@ -463,9 +463,9 @@ static void win_reset_pen(frontend *fe)
DeleteObject(pen); DeleteObject(pen);
} }
static void win_clip(void *handle, int x, int y, int w, int h) static void win_clip(drawing *dr, int x, int y, int w, int h)
{ {
frontend *fe = (frontend *)handle; frontend *fe = GET_HANDLE_AS_TYPE(dr, frontend);
POINT p, q; POINT p, q;
if (fe->drawstatus == NOTHING) if (fe->drawstatus == NOTHING)
@ -476,9 +476,9 @@ static void win_clip(void *handle, int x, int y, int w, int h)
IntersectClipRect(fe->hdc, p.x, p.y, q.x, q.y); IntersectClipRect(fe->hdc, p.x, p.y, q.x, q.y);
} }
static void win_unclip(void *handle) static void win_unclip(drawing *dr)
{ {
frontend *fe = (frontend *)handle; frontend *fe = GET_HANDLE_AS_TYPE(dr, frontend);
if (fe->drawstatus == NOTHING) if (fe->drawstatus == NOTHING)
return; return;
@ -486,11 +486,11 @@ static void win_unclip(void *handle)
SelectClipRgn(fe->hdc, NULL); SelectClipRgn(fe->hdc, NULL);
} }
static void win_draw_text(void *handle, int x, int y, int fonttype, static void win_draw_text(drawing *dr, int x, int y, int fonttype,
int fontsize, int align, int colour, int fontsize, int align, int colour,
const char *text) const char *text)
{ {
frontend *fe = (frontend *)handle; frontend *fe = GET_HANDLE_AS_TYPE(dr, frontend);
POINT xy; POINT xy;
int i; int i;
LOGFONT lf; LOGFONT lf;
@ -566,9 +566,9 @@ static void win_draw_text(void *handle, int x, int y, int fonttype,
} }
} }
static void win_draw_rect(void *handle, int x, int y, int w, int h, int colour) static void win_draw_rect(drawing *dr, int x, int y, int w, int h, int colour)
{ {
frontend *fe = (frontend *)handle; frontend *fe = GET_HANDLE_AS_TYPE(dr, frontend);
POINT p, q; POINT p, q;
if (fe->drawstatus == NOTHING) if (fe->drawstatus == NOTHING)
@ -593,9 +593,9 @@ static void win_draw_rect(void *handle, int x, int y, int w, int h, int colour)
} }
} }
static void win_draw_line(void *handle, int x1, int y1, int x2, int y2, int colour) static void win_draw_line(drawing *dr, int x1, int y1, int x2, int y2, int colour)
{ {
frontend *fe = (frontend *)handle; frontend *fe = GET_HANDLE_AS_TYPE(dr, frontend);
POINT pp[2]; POINT pp[2];
if (fe->drawstatus == NOTHING) if (fe->drawstatus == NOTHING)
@ -610,10 +610,10 @@ static void win_draw_line(void *handle, int x1, int y1, int x2, int y2, int colo
win_reset_pen(fe); win_reset_pen(fe);
} }
static void win_draw_circle(void *handle, int cx, int cy, int radius, static void win_draw_circle(drawing *dr, int cx, int cy, int radius,
int fillcolour, int outlinecolour) int fillcolour, int outlinecolour)
{ {
frontend *fe = (frontend *)handle; frontend *fe = GET_HANDLE_AS_TYPE(dr, frontend);
POINT p, q; POINT p, q;
assert(outlinecolour >= 0); assert(outlinecolour >= 0);
@ -634,10 +634,10 @@ static void win_draw_circle(void *handle, int cx, int cy, int radius,
win_reset_pen(fe); win_reset_pen(fe);
} }
static void win_draw_polygon(void *handle, const int *coords, int npoints, static void win_draw_polygon(drawing *dr, const int *coords, int npoints,
int fillcolour, int outlinecolour) int fillcolour, int outlinecolour)
{ {
frontend *fe = (frontend *)handle; frontend *fe = GET_HANDLE_AS_TYPE(dr, frontend);
POINT *pts; POINT *pts;
int i; int i;
@ -668,9 +668,9 @@ static void win_draw_polygon(void *handle, const int *coords, int npoints,
sfree(pts); sfree(pts);
} }
static void win_start_draw(void *handle) static void win_start_draw(drawing *dr)
{ {
frontend *fe = (frontend *)handle; frontend *fe = GET_HANDLE_AS_TYPE(dr, frontend);
HDC hdc_win; HDC hdc_win;
assert(fe->drawstatus == NOTHING); assert(fe->drawstatus == NOTHING);
@ -684,9 +684,9 @@ static void win_start_draw(void *handle)
fe->drawstatus = DRAWING; fe->drawstatus = DRAWING;
} }
static void win_draw_update(void *handle, int x, int y, int w, int h) static void win_draw_update(drawing *dr, int x, int y, int w, int h)
{ {
frontend *fe = (frontend *)handle; frontend *fe = GET_HANDLE_AS_TYPE(dr, frontend);
RECT r; RECT r;
if (fe->drawstatus != DRAWING) if (fe->drawstatus != DRAWING)
@ -701,9 +701,9 @@ static void win_draw_update(void *handle, int x, int y, int w, int h)
InvalidateRect(fe->hwnd, &r, false); InvalidateRect(fe->hwnd, &r, false);
} }
static void win_end_draw(void *handle) static void win_end_draw(drawing *dr)
{ {
frontend *fe = (frontend *)handle; frontend *fe = GET_HANDLE_AS_TYPE(dr, frontend);
assert(fe->drawstatus == DRAWING); assert(fe->drawstatus == DRAWING);
SelectObject(fe->hdc, fe->prevbm); SelectObject(fe->hdc, fe->prevbm);
DeleteDC(fe->hdc); DeleteDC(fe->hdc);
@ -714,9 +714,9 @@ static void win_end_draw(void *handle)
fe->drawstatus = NOTHING; fe->drawstatus = NOTHING;
} }
static void win_line_width(void *handle, float width) static void win_line_width(drawing *dr, float width)
{ {
frontend *fe = (frontend *)handle; frontend *fe = GET_HANDLE_AS_TYPE(dr, frontend);
assert(fe->drawstatus != DRAWING); assert(fe->drawstatus != DRAWING);
if (fe->drawstatus == NOTHING) if (fe->drawstatus == NOTHING)
@ -725,9 +725,9 @@ static void win_line_width(void *handle, float width)
fe->linewidth = (int)(width * fe->printpixelscale); fe->linewidth = (int)(width * fe->printpixelscale);
} }
static void win_line_dotted(void *handle, bool dotted) static void win_line_dotted(drawing *dr, bool dotted)
{ {
frontend *fe = (frontend *)handle; frontend *fe = GET_HANDLE_AS_TYPE(dr, frontend);
assert(fe->drawstatus != DRAWING); assert(fe->drawstatus != DRAWING);
if (fe->drawstatus == NOTHING) if (fe->drawstatus == NOTHING)
@ -736,9 +736,9 @@ static void win_line_dotted(void *handle, bool dotted)
fe->linedotted = dotted; fe->linedotted = dotted;
} }
static void win_begin_doc(void *handle, int pages) static void win_begin_doc(drawing *dr, int pages)
{ {
frontend *fe = (frontend *)handle; frontend *fe = GET_HANDLE_AS_TYPE(dr, frontend);
assert(fe->drawstatus != DRAWING); assert(fe->drawstatus != DRAWING);
if (fe->drawstatus == NOTHING) if (fe->drawstatus == NOTHING)
@ -761,9 +761,9 @@ static void win_begin_doc(void *handle, int pages)
fe->fontstart = fe->nfonts; fe->fontstart = fe->nfonts;
} }
static void win_begin_page(void *handle, int number) static void win_begin_page(drawing *dr, int number)
{ {
frontend *fe = (frontend *)handle; frontend *fe = GET_HANDLE_AS_TYPE(dr, frontend);
assert(fe->drawstatus != DRAWING); assert(fe->drawstatus != DRAWING);
if (fe->drawstatus == NOTHING) if (fe->drawstatus == NOTHING)
@ -778,10 +778,10 @@ static void win_begin_page(void *handle, int number)
} }
} }
static void win_begin_puzzle(void *handle, float xm, float xc, static void win_begin_puzzle(drawing *dr, float xm, float xc,
float ym, float yc, int pw, int ph, float wmm) float ym, float yc, int pw, int ph, float wmm)
{ {
frontend *fe = (frontend *)handle; frontend *fe = GET_HANDLE_AS_TYPE(dr, frontend);
int ppw, pph, pox, poy; int ppw, pph, pox, poy;
float mmpw, mmph, mmox, mmoy; float mmpw, mmph, mmox, mmoy;
float scale; float scale;
@ -829,14 +829,14 @@ static void win_begin_puzzle(void *handle, float xm, float xc,
fe->linedotted = false; fe->linedotted = false;
} }
static void win_end_puzzle(void *handle) static void win_end_puzzle(drawing *dr)
{ {
/* Nothing needs to be done here. */ /* Nothing needs to be done here. */
} }
static void win_end_page(void *handle, int number) static void win_end_page(drawing *dr, int number)
{ {
frontend *fe = (frontend *)handle; frontend *fe = GET_HANDLE_AS_TYPE(dr, frontend);
assert(fe->drawstatus != DRAWING); assert(fe->drawstatus != DRAWING);
@ -852,9 +852,9 @@ static void win_end_page(void *handle, int number)
} }
} }
static void win_end_doc(void *handle) static void win_end_doc(drawing *dr)
{ {
frontend *fe = (frontend *)handle; frontend *fe = GET_HANDLE_AS_TYPE(dr, frontend);
assert(fe->drawstatus != DRAWING); assert(fe->drawstatus != DRAWING);
@ -885,7 +885,7 @@ static void win_end_doc(void *handle)
} }
} }
char *win_text_fallback(void *handle, const char *const *strings, int nstrings) char *win_text_fallback(drawing *dr, const char *const *strings, int nstrings)
{ {
/* /*
* We assume Windows can cope with any UTF-8 likely to be * We assume Windows can cope with any UTF-8 likely to be
@ -895,6 +895,7 @@ char *win_text_fallback(void *handle, const char *const *strings, int nstrings)
} }
const struct drawing_api win_drawing = { const struct drawing_api win_drawing = {
1,
win_draw_text, win_draw_text,
win_draw_rect, win_draw_rect,
win_draw_line, win_draw_line,