+static void x11font_cairo_cache_glyph(x11font_individual *xfi, int glyphindex)
+{
+ XImage *image;
+ int x, y;
+ unsigned char *bitmap;
+ Display *disp = GDK_DISPLAY_XDISPLAY(gdk_display_get_default());
+ const XCharStruct *xcs = x11_char_struct(xfi->xfs, glyphindex >> 8,
+ glyphindex & 0xFF);
+
+ bitmap = snewn(xfi->allsize, unsigned char);
+ memset(bitmap, 0, xfi->allsize);
+
+ image = XGetImage(disp, xfi->pixmap, 0, 0,
+ xfi->pixwidth, xfi->pixheight, AllPlanes, XYPixmap);
+ for (y = xfi->pixoriginy - xcs->ascent;
+ y < xfi->pixoriginy + xcs->descent; y++) {
+ for (x = xfi->pixoriginx + xcs->lbearing;
+ x < xfi->pixoriginx + xcs->rbearing; x++) {
+ unsigned long pixel = XGetPixel(image, x, y);
+ if (pixel) {
+ int byteindex = y * xfi->rowsize + x/8;
+ int bitindex = (x & 7) ^ xfi->indexflip;
+ bitmap[byteindex] |= 1U << bitindex;
+ }
+ }
+ }
+ XDestroyImage(image);
+
+ if (xfi->nglyphs <= glyphindex) {
+ /* Round up to the next multiple of 256 on the general
+ * principle that Unicode characters come in contiguous blocks
+ * often used together */
+ int old_nglyphs = xfi->nglyphs;
+ xfi->nglyphs = (glyphindex + 0x100) & ~0xFF;
+ xfi->glyphcache = sresize(xfi->glyphcache, xfi->nglyphs,
+ struct cairo_cached_glyph);
+
+ while (old_nglyphs < xfi->nglyphs) {
+ xfi->glyphcache[old_nglyphs].surface = NULL;
+ xfi->glyphcache[old_nglyphs].bitmap = NULL;
+ old_nglyphs++;
+ }
+ }
+ xfi->glyphcache[glyphindex].bitmap = bitmap;
+ xfi->glyphcache[glyphindex].surface = cairo_image_surface_create_for_data
+ (bitmap, CAIRO_FORMAT_A1, xfi->pixwidth, xfi->pixheight, xfi->rowsize);
+}
+
+static void x11font_cairo_draw_glyph(unifont_drawctx *ctx,
+ x11font_individual *xfi, int x, int y,
+ int glyphindex)
+{
+ if (xfi->glyphcache[glyphindex].surface) {
+ cairo_mask_surface(ctx->u.cairo.cr,
+ xfi->glyphcache[glyphindex].surface,
+ x - xfi->pixoriginx, y - xfi->pixoriginy);
+ }
+}
+
+static void x11font_cairo_draw_16(unifont_drawctx *ctx,
+ x11font_individual *xfi, int x, int y,
+ const void *vstring, int start, int length)
+{
+ Display *disp = GDK_DISPLAY_XDISPLAY(gdk_display_get_default());
+ const XChar2b *string = (const XChar2b *)vstring + start;
+ int i;
+ for (i = 0; i < length; i++) {
+ if (x11_font_has_glyph(xfi->xfs, string[i].byte1, string[i].byte2)) {
+ int glyphindex = (256 * (unsigned char)string[i].byte1 +
+ (unsigned char)string[i].byte2);
+ if (glyphindex >= xfi->nglyphs ||
+ !xfi->glyphcache[glyphindex].surface) {
+ XDrawImageString16(disp, xfi->pixmap, xfi->gc,
+ xfi->pixoriginx, xfi->pixoriginy,
+ string+i, 1);
+ x11font_cairo_cache_glyph(xfi, glyphindex);
+ }
+ x11font_cairo_draw_glyph(ctx, xfi, x, y, glyphindex);
+ x += XTextWidth16(xfi->xfs, string+i, 1);
+ }
+ }
+}
+
+static void x11font_cairo_draw_8(unifont_drawctx *ctx,
+ x11font_individual *xfi, int x, int y,
+ const void *vstring, int start, int length)
+{
+ Display *disp = GDK_DISPLAY_XDISPLAY(gdk_display_get_default());
+ const char *string = (const char *)vstring + start;
+ int i;
+ for (i = 0; i < length; i++) {
+ if (x11_font_has_glyph(xfi->xfs, 0, string[i])) {
+ int glyphindex = (unsigned char)string[i];
+ if (glyphindex >= xfi->nglyphs ||
+ !xfi->glyphcache[glyphindex].surface) {
+ XDrawImageString(disp, xfi->pixmap, xfi->gc,
+ xfi->pixoriginx, xfi->pixoriginy,
+ string+i, 1);
+ x11font_cairo_cache_glyph(xfi, glyphindex);
+ }
+ x11font_cairo_draw_glyph(ctx, xfi, x, y, glyphindex);
+ x += XTextWidth(xfi->xfs, string+i, 1);
+ }
+ }
+}
+#endif /* DRAW_TEXT_CAIRO */
+
+struct x11font_drawfuncs {
+ int (*width)(unifont_drawctx *ctx, x11font_individual *xfi,
+ const void *vstring, int start, int length);
+ void (*setup)(unifont_drawctx *ctx, x11font_individual *xfi);
+ void (*draw)(unifont_drawctx *ctx, x11font_individual *xfi, int x, int y,
+ const void *vstring, int start, int length);
+};
+
+/*
+ * This array has two entries per compiled-in drawtype; of each pair,
+ * the first is for an 8-bit font and the second for 16-bit.
+ */
+static const struct x11font_drawfuncs x11font_drawfuncs[2*DRAWTYPE_NTYPES] = {
+#ifdef DRAW_TEXT_GDK
+ /* gdk, 8-bit */
+ {
+ x11font_width_8,
+ x11font_gdk_setup,
+ x11font_gdk_draw_8,
+ },
+ /* gdk, 16-bit */
+ {
+ x11font_width_16,
+ x11font_gdk_setup,
+ x11font_gdk_draw_16,
+ },
+#endif
+#ifdef DRAW_TEXT_CAIRO
+ /* cairo, 8-bit */
+ {
+ x11font_width_8,
+ x11font_cairo_setup,
+ x11font_cairo_draw_8,
+ },
+ /* [3] cairo, 16-bit */
+ {
+ x11font_width_16,
+ x11font_cairo_setup,
+ x11font_cairo_draw_16,
+ },
+#endif
+};
+
+static void x11font_really_draw_text(const struct x11font_drawfuncs *dfns,
+ unifont_drawctx *ctx,
+ x11font_individual *xfi, int x, int y,
+ const void *string, int nchars,
+ int shadowoffset,
+ int fontvariable, int cellwidth)
+{
+ int start = 0, step, nsteps, centre;
+
+ if (fontvariable) {
+ /*
+ * In a variable-pitch font, we draw one character at a
+ * time, and centre it in the character cell.
+ */
+ step = 1;
+ nsteps = nchars;
+ centre = TRUE;
+ } else {
+ /*
+ * In a fixed-pitch font, we can draw the whole lot in one go.
+ */
+ step = nchars;
+ nsteps = 1;
+ centre = FALSE;
+ }
+
+ dfns->setup(ctx, xfi);
+
+ while (nsteps-- > 0) {
+ int X = x;
+ if (centre)
+ X += (cellwidth - dfns->width(ctx, xfi, string, start, step)) / 2;
+
+ dfns->draw(ctx, xfi, X, y, string, start, step);
+ if (shadowoffset)
+ dfns->draw(ctx, xfi, X + shadowoffset, y, string, start, step);
+
+ x += cellwidth;
+ start += step;
+ }
+}
+
+static void x11font_draw_text(unifont_drawctx *ctx, unifont *font,
+ int x, int y, const wchar_t *string, int len,