+ struct x11font *xfont = (struct x11font *)font;
+
+ if (xfont->sixteen_bit) {
+ /*
+ * This X font has 16-bit character indices, which means
+ * we can directly use our Unicode input value.
+ */
+ return x11_font_has_glyph(xfont->fonts[0].xfs,
+ glyph >> 8, glyph & 0xFF);
+ } else {
+ /*
+ * This X font has 8-bit indices, so we must convert to the
+ * appropriate character set.
+ */
+ char sbstring[2];
+ int sblen = wc_to_mb(xfont->real_charset, 0, &glyph, 1,
+ sbstring, 2, "", NULL, NULL);
+ if (sblen == 0 || !sbstring[0])
+ return FALSE; /* not even in the charset */
+
+ return x11_font_has_glyph(xfont->fonts[0].xfs, 0,
+ (unsigned char)sbstring[0]);
+ }
+}
+
+#if !GTK_CHECK_VERSION(2,0,0)
+#define GDK_DRAWABLE_XID(d) GDK_WINDOW_XWINDOW(d) /* GTK1's name for this */
+#elif GTK_CHECK_VERSION(3,0,0)
+#define GDK_DRAWABLE_XID(d) GDK_WINDOW_XID(d) /* GTK3's name for this */
+#endif
+
+static int x11font_width_16(unifont_drawctx *ctx, x11font_individual *xfi,
+ const void *vstring, int start, int length)
+{
+ const XChar2b *string = (const XChar2b *)vstring;
+ return XTextWidth16(xfi->xfs, string+start, length);
+}
+
+static int x11font_width_8(unifont_drawctx *ctx, x11font_individual *xfi,
+ const void *vstring, int start, int length)
+{
+ const char *string = (const char *)vstring;
+ return XTextWidth(xfi->xfs, string+start, length);
+}
+
+#ifdef DRAW_TEXT_GDK
+static void x11font_gdk_setup(unifont_drawctx *ctx, x11font_individual *xfi)
+{
+ Display *disp = GDK_DISPLAY_XDISPLAY(gdk_display_get_default());
+ XSetFont(disp, GDK_GC_XGC(ctx->u.gdk.gc), xfi->xfs->fid);
+}
+
+static void x11font_gdk_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;
+ XDrawString16(disp, GDK_DRAWABLE_XID(ctx->u.gdk.target),
+ GDK_GC_XGC(ctx->u.gdk.gc), x, y, string+start, length);
+}
+
+static void x11font_gdk_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;
+ XDrawString(disp, GDK_DRAWABLE_XID(ctx->u.gdk.target),
+ GDK_GC_XGC(ctx->u.gdk.gc), x, y, string+start, length);
+}
+#endif
+
+#ifdef DRAW_TEXT_CAIRO
+static void x11font_cairo_setup(unifont_drawctx *ctx, x11font_individual *xfi)
+{
+ if (xfi->pixmap == None) {
+ Display *disp = GDK_DISPLAY_XDISPLAY(gdk_display_get_default());
+ XGCValues gcvals;
+ GdkWindow *widgetwin = gtk_widget_get_window(ctx->u.cairo.widget);
+ int widgetscr = GDK_SCREEN_XNUMBER(gdk_window_get_screen(widgetwin));
+
+ xfi->pixwidth =
+ xfi->xfs->max_bounds.rbearing - xfi->xfs->min_bounds.lbearing;
+ xfi->pixheight =
+ xfi->xfs->max_bounds.ascent + xfi->xfs->max_bounds.descent;
+ xfi->pixoriginx = -xfi->xfs->min_bounds.lbearing;
+ xfi->pixoriginy = xfi->xfs->max_bounds.ascent;
+
+ xfi->rowsize = cairo_format_stride_for_width(CAIRO_FORMAT_A1,
+ xfi->pixwidth);
+ xfi->allsize = xfi->rowsize * xfi->pixheight;
+
+ {
+ /*
+ * Test host endianness and use it to set xfi->indexflip,
+ * which is XORed into our left-shift counts in order to
+ * implement the CAIRO_FORMAT_A1 specification, in which
+ * each bitmap byte is oriented LSB-first on little-endian
+ * platforms and MSB-first on big-endian ones.
+ *
+ * This is the same technique Cairo itself uses to test
+ * endianness, so hopefully it'll work in any situation
+ * where Cairo is usable at all.
+ */
+ static const int endianness_test = 1;
+ xfi->indexflip = (*((char *) &endianness_test) == 1) ? 0 : 7;
+ }
+
+ xfi->pixmap = XCreatePixmap
+ (disp,
+ GDK_DRAWABLE_XID(gtk_widget_get_window(ctx->u.cairo.widget)),
+ xfi->pixwidth, xfi->pixheight, 1);
+ gcvals.foreground = WhitePixel(disp, widgetscr);
+ gcvals.background = BlackPixel(disp, widgetscr);
+ gcvals.font = xfi->xfs->fid;
+ xfi->gc = XCreateGC(disp, xfi->pixmap,
+ GCForeground | GCBackground | GCFont, &gcvals);
+ }
+}
+
+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;