]> asedeno.scripts.mit.edu Git - PuTTY.git/blobdiff - unix/gtkfont.c
Introduce a config option for building on OS X GTK.
[PuTTY.git] / unix / gtkfont.c
index af577bda066aea6a6399305ef6c969f6a7a57d4c..1a9860ab0eee5c14216365d7c7daed9d9055fcac 100644 (file)
 #include <assert.h>
 #include <stdlib.h>
 #include <string.h>
+
 #include <gtk/gtk.h>
+#if !GTK_CHECK_VERSION(3,0,0)
 #include <gdk/gdkkeysyms.h>
-#include <gdk/gdkx.h>
-#include <X11/Xlib.h>
-#include <X11/Xutil.h>
-#include <X11/Xatom.h>
+#endif
 
 #include "putty.h"
 #include "gtkfont.h"
+#include "gtkcompat.h"
 #include "tree234.h"
 
+#ifndef NOT_X_WINDOWS
+#include <gdk/gdkx.h>
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include <X11/Xatom.h>
+#endif
+
 /*
  * Future work:
  * 
  *  - it would be nice to have a display of the current font name,
  *    and in particular whether it's client- or server-side,
  *    during the progress of the font selector.
- * 
- *  - it would be nice if we could move the processing of
- *    underline and VT100 double width into this module, so that
- *    instead of using the ghastly pixmap-stretching technique
- *    everywhere we could tell the Pango backend to scale its
- *    fonts to double size properly and at full resolution.
- *    However, this requires me to learn how to make Pango stretch
- *    text to an arbitrary aspect ratio (for double-width only
- *    text, which perversely is harder than DW+DH), and right now
- *    I haven't the energy.
  */
 
+#if !GLIB_CHECK_VERSION(1,3,7)
+#define g_ascii_strcasecmp g_strcasecmp
+#define g_ascii_strncasecmp g_strncasecmp
+#endif
+
 /*
  * Ad-hoc vtable mechanism to allow font structures to be
  * polymorphic.
@@ -75,9 +77,9 @@ struct unifont_vtable {
                                 int bold, int shadowoffset, int shadowalways);
     void (*destroy)(unifont *font);
     int (*has_glyph)(unifont *font, wchar_t glyph);
-    void (*draw_text)(GdkDrawable *target, GdkGC *gc, unifont *font,
-                     int x, int y, const wchar_t *string, int len, int wide,
-                     int bold, int cellwidth);
+    void (*draw_text)(unifont_drawctx *ctx, unifont *font,
+                      int x, int y, const wchar_t *string, int len,
+                      int wide, int bold, int cellwidth);
     void (*enum_fonts)(GtkWidget *widget,
                       fontsel_add_entry callback, void *callback_ctx);
     char *(*canonify_fontname)(GtkWidget *widget, const char *name, int *size,
@@ -90,14 +92,18 @@ struct unifont_vtable {
     const char *prefix;
 };
 
+#ifndef NOT_X_WINDOWS
+
 /* ----------------------------------------------------------------------
- * X11 font implementation, directly using Xlib calls.
+ * X11 font implementation, directly using Xlib calls. Conditioned out
+ * if X11 fonts aren't available at all (e.g. building with GTK3 for a
+ * back end other than X).
  */
 
 static int x11font_has_glyph(unifont *font, wchar_t glyph);
-static void x11font_draw_text(GdkDrawable *target, GdkGC *gc, unifont *font,
-                             int x, int y, const wchar_t *string, int len,
-                             int wide, int bold, int cellwidth);
+static void x11font_draw_text(unifont_drawctx *ctx, unifont *font,
+                              int x, int y, const wchar_t *string, int len,
+                              int wide, int bold, int cellwidth);
 static unifont *x11font_create(GtkWidget *widget, const char *name,
                               int wide, int bold,
                               int shadowoffset, int shadowalways);
@@ -110,20 +116,61 @@ static char *x11font_canonify_fontname(GtkWidget *widget, const char *name,
 static char *x11font_scale_fontname(GtkWidget *widget, const char *name,
                                    int size);
 
+#ifdef DRAW_TEXT_CAIRO
+struct cairo_cached_glyph {
+    cairo_surface_t *surface;
+    unsigned char *bitmap;
+};
+#endif
+
+/*
+ * Structure storing a single physical XFontStruct, plus associated
+ * data.
+ */
+typedef struct x11font_individual {
+    /* The XFontStruct itself. */
+    XFontStruct *xfs;
+
+    /*
+     * The `allocated' flag indicates whether we've tried to fetch
+     * this subfont already (thus distinguishing xfs==NULL because we
+     * haven't tried yet from xfs==NULL because we tried and failed,
+     * so that we don't keep trying and failing subsequently).
+     */
+    int allocated;
+
+#ifdef DRAW_TEXT_CAIRO
+    /*
+     * A cache of glyph bitmaps downloaded from the X server when
+     * we're in Cairo rendering mode. If glyphcache itself is
+     * non-NULL, then entries in [0,nglyphs) are expected to be
+     * initialised to either NULL or a bitmap pointer.
+     */
+    struct cairo_cached_glyph *glyphcache;
+    int nglyphs;
+
+    /*
+     * X server paraphernalia for actually downloading the glyphs.
+     */
+    Pixmap pixmap;
+    GC gc;
+    int pixwidth, pixheight, pixoriginx, pixoriginy;
+
+    /*
+     * Paraphernalia for loading the resulting bitmaps into Cairo.
+     */
+    int rowsize, allsize, indexflip;
+#endif
+
+} x11font_individual;
+
 struct x11font {
     struct unifont u;
     /*
-     * Actual font objects. We store a number of these, for
+     * Individual physical X fonts. We store a number of these, for
      * automatically guessed bold and wide variants.
-     * 
-     * The parallel array `allocated' indicates whether we've
-     * tried to fetch a subfont already (thus distinguishing NULL
-     * because we haven't tried yet from NULL because we tried and
-     * failed, so that we don't keep trying and failing
-     * subsequently).
      */
-    XFontStruct *fonts[4];
-    int allocated[4];
+    x11font_individual fonts[4];
     /*
      * `sixteen_bit' is true iff the font object is indexed by
      * values larger than a byte. That is, this flag tells us
@@ -164,13 +211,13 @@ static const struct unifont_vtable x11font_vtable = {
 
 static char *x11_guess_derived_font_name(XFontStruct *xfs, int bold, int wide)
 {
-    Display *disp = GDK_DISPLAY();
+    Display *disp = GDK_DISPLAY_XDISPLAY(gdk_display_get_default());
     Atom fontprop = XInternAtom(disp, "FONT", False);
     unsigned long ret;
     if (XGetFontProperty(xfs, fontprop, &ret)) {
        char *name = XGetAtomName(disp, (Atom)ret);
        if (name && name[0] == '-') {
-           char *strings[13];
+           const char *strings[13];
            char *dupname, *extrafree = NULL, *ret;
            char *p, *q;
            int nstr;
@@ -186,8 +233,10 @@ static char *x11_guess_derived_font_name(XFontStruct *xfs, int bold, int wide)
                p++;
            }
 
-           if (nstr < lenof(strings))
+           if (nstr < lenof(strings)) {
+                sfree(dupname);
                return NULL;           /* XLFD was malformed */
+            }
 
            if (bold)
                strings[2] = "bold";
@@ -280,6 +329,9 @@ static int x11_font_has_glyph(XFontStruct *xfs, int byte1, int byte2)
                   (xfs->max_char_or_byte2 - xfs->min_char_or_byte2 + 1)));
     }
 
+    if (!xfs->per_char)   /* per_char NULL => everything in range exists */
+        return TRUE;
+
     return (xfs->per_char[index].ascent + xfs->per_char[index].descent > 0 ||
             xfs->per_char[index].width > 0);
 }
@@ -290,7 +342,7 @@ static unifont *x11font_create(GtkWidget *widget, const char *name,
 {
     struct x11font *xfont;
     XFontStruct *xfs;
-    Display *disp = GDK_DISPLAY();
+    Display *disp = GDK_DISPLAY_XDISPLAY(gdk_display_get_default());
     Atom charset_registry, charset_encoding, spacing;
     unsigned long registry_ret, encoding_ret, spacing_ret;
     int pubcs, realcs, sixteen_bit, variable;
@@ -363,9 +415,14 @@ static unifont *x11font_create(GtkWidget *widget, const char *name,
     xfont->u.height = xfont->u.ascent + xfont->u.descent;
     xfont->u.public_charset = pubcs;
     xfont->u.want_fallback = TRUE;
+#ifdef DRAW_TEXT_GDK
+    xfont->u.preferred_drawtype = DRAWTYPE_GDK;
+#elif defined DRAW_TEXT_CAIRO
+    xfont->u.preferred_drawtype = DRAWTYPE_CAIRO;
+#else
+#error No drawtype available at all
+#endif
     xfont->real_charset = realcs;
-    xfont->fonts[0] = xfs;
-    xfont->allocated[0] = TRUE;
     xfont->sixteen_bit = sixteen_bit;
     xfont->variable = variable;
     xfont->wide = wide;
@@ -373,35 +430,58 @@ static unifont *x11font_create(GtkWidget *widget, const char *name,
     xfont->shadowoffset = shadowoffset;
     xfont->shadowalways = shadowalways;
 
-    for (i = 1; i < lenof(xfont->fonts); i++) {
-       xfont->fonts[i] = NULL;
-       xfont->allocated[i] = FALSE;
+    for (i = 0; i < lenof(xfont->fonts); i++) {
+       xfont->fonts[i].xfs = NULL;
+       xfont->fonts[i].allocated = FALSE;
+#ifdef DRAW_TEXT_CAIRO
+       xfont->fonts[i].glyphcache = NULL;
+       xfont->fonts[i].nglyphs = 0;
+       xfont->fonts[i].pixmap = None;
+       xfont->fonts[i].gc = None;
+#endif
     }
+    xfont->fonts[0].xfs = xfs;
+    xfont->fonts[0].allocated = TRUE;
 
     return (unifont *)xfont;
 }
 
 static void x11font_destroy(unifont *font)
 {
-    Display *disp = GDK_DISPLAY();
+    Display *disp = GDK_DISPLAY_XDISPLAY(gdk_display_get_default());
     struct x11font *xfont = (struct x11font *)font;
     int i;
 
-    for (i = 0; i < lenof(xfont->fonts); i++)
-       if (xfont->fonts[i])
-           XFreeFont(disp, xfont->fonts[i]);
+    for (i = 0; i < lenof(xfont->fonts); i++) {
+       if (xfont->fonts[i].xfs)
+           XFreeFont(disp, xfont->fonts[i].xfs);
+#ifdef DRAW_TEXT_CAIRO
+       if (xfont->fonts[i].gc != None)
+           XFreeGC(disp, xfont->fonts[i].gc);
+       if (xfont->fonts[i].pixmap != None)
+           XFreePixmap(disp, xfont->fonts[i].pixmap);
+       if (xfont->fonts[i].glyphcache) {
+            int j;
+            for (j = 0; j < xfont->fonts[i].nglyphs; j++) {
+                cairo_surface_destroy(xfont->fonts[i].glyphcache[j].surface);
+                sfree(xfont->fonts[i].glyphcache[j].bitmap);
+            }
+            sfree(xfont->fonts[i].glyphcache);
+        }
+#endif
+    }
     sfree(font);
 }
 
 static void x11_alloc_subfont(struct x11font *xfont, int sfid)
 {
-    Display *disp = GDK_DISPLAY();
+    Display *disp = GDK_DISPLAY_XDISPLAY(gdk_display_get_default());
     char *derived_name = x11_guess_derived_font_name
-       (xfont->fonts[0], sfid & 1, !!(sfid & 2));
-    xfont->fonts[sfid] = XLoadQueryFont(disp, derived_name);
-    xfont->allocated[sfid] = TRUE;
+       (xfont->fonts[0].xfs, sfid & 1, !!(sfid & 2));
+    xfont->fonts[sfid].xfs = XLoadQueryFont(disp, derived_name);
+    xfont->fonts[sfid].allocated = TRUE;
     sfree(derived_name);
-    /* Note that xfont->fonts[sfid] may still be NULL, if XLQF failed. */
+    /* Note that xfont->fonts[sfid].xfs may still be NULL, if XLQF failed. */
 }
 
 static int x11font_has_glyph(unifont *font, wchar_t glyph)
@@ -413,7 +493,8 @@ static int x11font_has_glyph(unifont *font, wchar_t glyph)
         * 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], glyph >> 8, glyph & 0xFF);
+        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
@@ -422,67 +503,264 @@ static int x11font_has_glyph(unifont *font, wchar_t glyph)
         char sbstring[2];
         int sblen = wc_to_mb(xfont->real_charset, 0, &glyph, 1,
                              sbstring, 2, "", NULL, NULL);
-        if (!sbstring[0])
+        if (sblen == 0 || !sbstring[0])
             return FALSE;              /* not even in the charset */
 
-        return x11_font_has_glyph(xfont->fonts[0], 0, sbstring[0]);
+        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 void x11font_really_draw_text_16(GdkDrawable *target, XFontStruct *xfs,
-                                        GC gc, int x, int y,
-                                        const XChar2b *string, int nchars,
-                                        int shadowoffset,
-                                        int fontvariable, int cellwidth)
+static int x11font_width_16(unifont_drawctx *ctx, x11font_individual *xfi,
+                            const void *vstring, int start, int length)
 {
-    Display *disp = GDK_DISPLAY();
-    int step, nsteps, centre;
+    const XChar2b *string = (const XChar2b *)vstring;
+    return XTextWidth16(xfi->xfs, string+start, length);
+}
 
-    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;
+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);
     }
+}
 
-    while (nsteps-- > 0) {
-       int X = x;
-       if (centre)
-           X += (cellwidth - XTextWidth16(xfs, string, step)) / 2;
+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());
+
+    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 = 0; y < xfi->pixheight; y++) {
+        for (x = 0; x < xfi->pixwidth; 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);
+}
 
-        XDrawString16(disp, GDK_DRAWABLE_XID(target), gc,
-                      X, y, string, step);
-       if (shadowoffset)
-            XDrawString16(disp, GDK_DRAWABLE_XID(target), gc,
-                          X + shadowoffset, y, string, step);
+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);
+    }
+}
 
-       x += cellwidth;
-       string += step;
+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_really_draw_text(GdkDrawable *target, XFontStruct *xfs,
-                                     GC gc, int x, int y,
-                                     const char *string, int nchars,
+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)
 {
-    Display *disp = GDK_DISPLAY();
-    int step, nsteps, centre;
+    int start = 0, step, nsteps, centre;
 
     if (fontvariable) {
        /*
@@ -501,32 +779,31 @@ static void x11font_really_draw_text(GdkDrawable *target, XFontStruct *xfs,
         centre = FALSE;
     }
 
+    dfns->setup(ctx, xfi);
+
     while (nsteps-- > 0) {
        int X = x;
        if (centre)
-           X += (cellwidth - XTextWidth(xfs, string, step)) / 2;
+           X += (cellwidth - dfns->width(ctx, xfi, string, start, step)) / 2;
 
-        XDrawString(disp, GDK_DRAWABLE_XID(target), gc,
-                    X, y, string, step);
+        dfns->draw(ctx, xfi, X, y, string, start, step);
        if (shadowoffset)
-            XDrawString(disp, GDK_DRAWABLE_XID(target), gc,
-                        X + shadowoffset, y, string, step);
+            dfns->draw(ctx, xfi, X + shadowoffset, y, string, start, step);
 
        x += cellwidth;
-       string += step;
+        start += step;
     }
 }
 
-static void x11font_draw_text(GdkDrawable *target, GdkGC *gdkgc, unifont *font,
+static void x11font_draw_text(unifont_drawctx *ctx, unifont *font,
                              int x, int y, const wchar_t *string, int len,
                              int wide, int bold, int cellwidth)
 {
-    Display *disp = GDK_DISPLAY();
     struct x11font *xfont = (struct x11font *)font;
-    GC gc = GDK_GC_XGC(gdkgc);
     int sfid;
     int shadowoffset = 0;
     int mult = (wide ? 2 : 1);
+    int index = 2 * (int)ctx->type;
 
     wide -= xfont->wide;
     bold -= xfont->bold;
@@ -540,21 +817,19 @@ static void x11font_draw_text(GdkDrawable *target, GdkGC *gdkgc, unifont *font,
        bold = 0;
     }
     sfid = 2 * wide + bold;
-    if (!xfont->allocated[sfid])
+    if (!xfont->fonts[sfid].allocated)
        x11_alloc_subfont(xfont, sfid);
-    if (bold && !xfont->fonts[sfid]) {
+    if (bold && !xfont->fonts[sfid].xfs) {
        bold = 0;
        shadowoffset = xfont->shadowoffset;
        sfid = 2 * wide + bold;
-       if (!xfont->allocated[sfid])
+       if (!xfont->fonts[sfid].allocated)
            x11_alloc_subfont(xfont, sfid);
     }
 
-    if (!xfont->fonts[sfid])
+    if (!xfont->fonts[sfid].xfs)
        return;                        /* we've tried our best, but no luck */
 
-    XSetFont(disp, gc, xfont->fonts[sfid]->fid);
-
     if (xfont->sixteen_bit) {
        /*
         * This X font has 16-bit character indices, which means
@@ -569,9 +844,10 @@ static void x11font_draw_text(GdkDrawable *target, GdkGC *gdkgc, unifont *font,
            xcs[i].byte2 = string[i];
        }
 
-       x11font_really_draw_text_16(target, xfont->fonts[sfid], gc, x, y,
-                                    xcs, len, shadowoffset,
-                                    xfont->variable, cellwidth * mult);
+       x11font_really_draw_text(x11font_drawfuncs + index + 1, ctx,
+                                 &xfont->fonts[sfid], x, y,
+                                 xcs, len, shadowoffset,
+                                 xfont->variable, cellwidth * mult);
        sfree(xcs);
     } else {
         /*
@@ -581,7 +857,8 @@ static void x11font_draw_text(GdkDrawable *target, GdkGC *gdkgc, unifont *font,
         char *sbstring = snewn(len+1, char);
         int sblen = wc_to_mb(xfont->real_charset, 0, string, len,
                              sbstring, len+1, ".", NULL, NULL);
-       x11font_really_draw_text(target, xfont->fonts[sfid], gc, x, y,
+       x11font_really_draw_text(x11font_drawfuncs + index + 0, ctx,
+                                 &xfont->fonts[sfid], x, y,
                                 sbstring, sblen, shadowoffset,
                                 xfont->variable, cellwidth * mult);
         sfree(sbstring);
@@ -591,13 +868,14 @@ static void x11font_draw_text(GdkDrawable *target, GdkGC *gdkgc, unifont *font,
 static void x11font_enum_fonts(GtkWidget *widget,
                               fontsel_add_entry callback, void *callback_ctx)
 {
+    Display *disp = GDK_DISPLAY_XDISPLAY(gdk_display_get_default());
     char **fontnames;
     char *tmp = NULL;
     int nnames, i, max, tmpsize;
 
     max = 32768;
     while (1) {
-       fontnames = XListFonts(GDK_DISPLAY(), "*", max, &nnames);
+       fontnames = XListFonts(disp, "*", max, &nnames);
        if (nnames >= max) {
            XFreeFontNames(fontnames);
            max *= 2;
@@ -657,21 +935,21 @@ static void x11font_enum_fonts(GtkWidget *widget,
            style = p;
            p += sprintf(p, "%s", components[2][0] ? components[2] :
                         "regular");
-           if (!g_strcasecmp(components[3], "i"))
+           if (!g_ascii_strcasecmp(components[3], "i"))
                p += sprintf(p, " italic");
-           else if (!g_strcasecmp(components[3], "o"))
+           else if (!g_ascii_strcasecmp(components[3], "o"))
                p += sprintf(p, " oblique");
-           else if (!g_strcasecmp(components[3], "ri"))
+           else if (!g_ascii_strcasecmp(components[3], "ri"))
                p += sprintf(p, " reverse italic");
-           else if (!g_strcasecmp(components[3], "ro"))
+           else if (!g_ascii_strcasecmp(components[3], "ro"))
                p += sprintf(p, " reverse oblique");
-           else if (!g_strcasecmp(components[3], "ot"))
+           else if (!g_ascii_strcasecmp(components[3], "ot"))
                p += sprintf(p, " other-slant");
-           if (components[4][0] && g_strcasecmp(components[4], "normal"))
+           if (components[4][0] && g_ascii_strcasecmp(components[4], "normal"))
                p += sprintf(p, " %s", components[4]);
-           if (!g_strcasecmp(components[10], "m"))
+           if (!g_ascii_strcasecmp(components[10], "m"))
                p += sprintf(p, " [M]");
-           if (!g_strcasecmp(components[10], "c"))
+           if (!g_ascii_strcasecmp(components[10], "c"))
                p += sprintf(p, " [C]");
            if (components[5][0])
                p += sprintf(p, " %s", components[5]);
@@ -683,23 +961,23 @@ static void x11font_enum_fonts(GtkWidget *widget,
             */
            p++;
            stylekey = p;
-           if (!g_strcasecmp(components[2], "medium") ||
-               !g_strcasecmp(components[2], "regular") ||
-               !g_strcasecmp(components[2], "normal") ||
-               !g_strcasecmp(components[2], "book"))
+           if (!g_ascii_strcasecmp(components[2], "medium") ||
+               !g_ascii_strcasecmp(components[2], "regular") ||
+               !g_ascii_strcasecmp(components[2], "normal") ||
+               !g_ascii_strcasecmp(components[2], "book"))
                weightkey = 0;
-           else if (!g_strncasecmp(components[2], "demi", 4) ||
-                    !g_strncasecmp(components[2], "semi", 4))
+           else if (!g_ascii_strncasecmp(components[2], "demi", 4) ||
+                    !g_ascii_strncasecmp(components[2], "semi", 4))
                weightkey = 1;
            else
                weightkey = 2;
-           if (!g_strcasecmp(components[3], "r"))
+           if (!g_ascii_strcasecmp(components[3], "r"))
                slantkey = 0;
-           else if (!g_strncasecmp(components[3], "r", 1))
+           else if (!g_ascii_strncasecmp(components[3], "r", 1))
                slantkey = 2;
            else
                slantkey = 1;
-           if (!g_strcasecmp(components[4], "normal"))
+           if (!g_ascii_strcasecmp(components[4], "normal"))
                setwidthkey = 0;
            else
                setwidthkey = 1;
@@ -772,7 +1050,7 @@ static char *x11font_canonify_fontname(GtkWidget *widget, const char *name,
      * selector treats them as worthwhile in their own right.
      */
     XFontStruct *xfs;
-    Display *disp = GDK_DISPLAY();
+    Display *disp = GDK_DISPLAY_XDISPLAY(gdk_display_get_default());
     Atom fontprop, fontprop2;
     unsigned long ret;
 
@@ -815,6 +1093,8 @@ static char *x11font_scale_fontname(GtkWidget *widget, const char *name,
     return NULL;                      /* shan't */
 }
 
+#endif /* NOT_X_WINDOWS */
+
 #if GTK_CHECK_VERSION(2,0,0)
 
 /* ----------------------------------------------------------------------
@@ -826,9 +1106,9 @@ static char *x11font_scale_fontname(GtkWidget *widget, const char *name,
 #endif
 
 static int pangofont_has_glyph(unifont *font, wchar_t glyph);
-static void pangofont_draw_text(GdkDrawable *target, GdkGC *gc, unifont *font,
-                               int x, int y, const wchar_t *string, int len,
-                               int wide, int bold, int cellwidth);
+static void pangofont_draw_text(unifont_drawctx *ctx, unifont *font,
+                                int x, int y, const wchar_t *string, int len,
+                                int wide, int bold, int cellwidth);
 static unifont *pangofont_create(GtkWidget *widget, const char *name,
                                 int wide, int bold,
                                 int shadowoffset, int shadowalways);
@@ -859,6 +1139,13 @@ struct pangofont {
      * Data passed in to unifont_create().
      */
     int bold, shadowoffset, shadowalways;
+    /*
+     * Cache of character widths, indexed by Unicode code point. In
+     * pixels; -1 means we haven't asked Pango about this character
+     * before.
+     */
+    int *widthcache;
+    unsigned nwidthcache;
 };
 
 static const struct unifont_vtable pangofont_vtable = {
@@ -911,8 +1198,8 @@ static int pangofont_check_desc_makes_sense(PangoContext *ctx,
 
     matched = FALSE;
     for (i = 0; i < nfamilies; i++) {
-       if (!g_strcasecmp(pango_font_family_get_name(families[i]),
-                         pango_font_description_get_family(desc))) {
+       if (!g_ascii_strcasecmp(pango_font_family_get_name(families[i]),
+                               pango_font_description_get_family(desc))) {
            matched = TRUE;
            break;
        }
@@ -967,6 +1254,13 @@ static unifont *pangofont_create_internal(GtkWidget *widget,
     pfont->u.descent = PANGO_PIXELS(pango_font_metrics_get_descent(metrics));
     pfont->u.height = pfont->u.ascent + pfont->u.descent;
     pfont->u.want_fallback = FALSE;
+#ifdef DRAW_TEXT_CAIRO
+    pfont->u.preferred_drawtype = DRAWTYPE_CAIRO;
+#elif defined DRAW_TEXT_GDK
+    pfont->u.preferred_drawtype = DRAWTYPE_GDK;
+#else
+#error No drawtype available at all
+#endif
     /* The Pango API is hardwired to UTF-8 */
     pfont->u.public_charset = CS_UTF8;
     pfont->desc = desc;
@@ -975,6 +1269,8 @@ static unifont *pangofont_create_internal(GtkWidget *widget,
     pfont->bold = bold;
     pfont->shadowoffset = shadowoffset;
     pfont->shadowalways = shadowalways;
+    pfont->widthcache = NULL;
+    pfont->nwidthcache = 0;
 
     pango_font_metrics_unref(metrics);
 
@@ -1028,19 +1324,66 @@ static void pangofont_destroy(unifont *font)
 {
     struct pangofont *pfont = (struct pangofont *)font;
     pango_font_description_free(pfont->desc);
+    sfree(pfont->widthcache);
     g_object_unref(pfont->fset);
     sfree(font);
 }
 
+static int pangofont_char_width(PangoLayout *layout, struct pangofont *pfont,
+                                wchar_t uchr, const char *utfchr, int utflen)
+{
+    /*
+     * Here we check whether a character has the same width as the
+     * character cell it'll be drawn in. Because profiling showed that
+     * pango_layout_get_pixel_extents() was a huge bottleneck when we
+     * were calling it every time we needed to know this, we instead
+     * call it only on characters we don't already know about, and
+     * cache the results.
+     */
+
+    if ((unsigned)uchr >= pfont->nwidthcache) {
+        unsigned newsize = ((int)uchr + 0x100) & ~0xFF;
+        pfont->widthcache = sresize(pfont->widthcache, newsize, int);
+        while (pfont->nwidthcache < newsize)
+            pfont->widthcache[pfont->nwidthcache++] = -1;
+    }
+
+    if (pfont->widthcache[uchr] < 0) {
+        PangoRectangle rect;
+        pango_layout_set_text(layout, utfchr, utflen);
+        pango_layout_get_pixel_extents(layout, NULL, &rect);
+        pfont->widthcache[uchr] = rect.width;
+    }
+
+    return pfont->widthcache[uchr];
+}
+
 static int pangofont_has_glyph(unifont *font, wchar_t glyph)
 {
     /* Pango implements font fallback, so assume it has everything */
     return TRUE;
 }
 
-static void pangofont_draw_text(GdkDrawable *target, GdkGC *gc, unifont *font,
-                               int x, int y, const wchar_t *string, int len,
-                               int wide, int bold, int cellwidth)
+#ifdef DRAW_TEXT_GDK
+static void pango_gdk_draw_layout(unifont_drawctx *ctx,
+                                  gint x, gint y, PangoLayout *layout)
+{
+    gdk_draw_layout(ctx->u.gdk.target, ctx->u.gdk.gc, x, y, layout);
+}
+#endif
+
+#ifdef DRAW_TEXT_CAIRO
+static void pango_cairo_draw_layout(unifont_drawctx *ctx,
+                                    gint x, gint y, PangoLayout *layout)
+{
+    cairo_move_to(ctx->u.cairo.cr, x, y);
+    pango_cairo_show_layout(ctx->u.cairo.cr, layout);
+}
+#endif
+
+static void pangofont_draw_text(unifont_drawctx *ctx, unifont *font,
+                                int x, int y, const wchar_t *string, int len,
+                                int wide, int bold, int cellwidth)
 {
     struct pangofont *pfont = (struct pangofont *)font;
     PangoLayout *layout;
@@ -1048,6 +1391,19 @@ static void pangofont_draw_text(GdkDrawable *target, GdkGC *gc, unifont *font,
     char *utfstring, *utfptr;
     int utflen;
     int shadowbold = FALSE;
+    void (*draw_layout)(unifont_drawctx *ctx,
+                        gint x, gint y, PangoLayout *layout) = NULL;
+
+#ifdef DRAW_TEXT_GDK
+    if (ctx->type == DRAWTYPE_GDK) {
+        draw_layout = pango_gdk_draw_layout;
+    }
+#endif
+#ifdef DRAW_TEXT_CAIRO
+    if (ctx->type == DRAWTYPE_CAIRO) {
+        draw_layout = pango_cairo_draw_layout;
+    }
+#endif
 
     if (wide)
        cellwidth *= 2;
@@ -1114,50 +1470,48 @@ static void pangofont_draw_text(GdkDrawable *target, GdkGC *gc, unifont *font,
            clen++;
        n = 1;
 
-        /*
-         * If it's a right-to-left character, we must display it on
-         * its own, to stop Pango helpfully re-reversing our already
-         * reversed text.
-         */
-        if (!is_rtl(string[0])) {
-
+        if (is_rtl(string[0]) ||
+            pangofont_char_width(layout, pfont, string[n-1],
+                                 utfptr, clen) != cellwidth) {
             /*
-             * See if that character has the width we expect.
+             * If this character is a right-to-left one, or has an
+             * unusual width, then we must display it on its own.
              */
-            pango_layout_set_text(layout, utfptr, clen);
-            pango_layout_get_pixel_extents(layout, NULL, &rect);
-
-            if (rect.width == cellwidth) {
-                /*
-                 * Try extracting more characters, for as long as they
-                 * stay well-behaved.
-                 */
-                while (clen < utflen) {
-                    int oldclen = clen;
-                    clen++;                   /* skip UTF-8 introducer byte */
-                    while (clen < utflen &&
-                           (unsigned char)utfptr[clen] >= 0x80 &&
-                           (unsigned char)utfptr[clen] < 0xC0)
-                        clen++;
-                    n++;
-                    pango_layout_set_text(layout, utfptr, clen);
-                    pango_layout_get_pixel_extents(layout, NULL, &rect);
-                    if (rect.width != n * cellwidth) {
-                        clen = oldclen;
-                        n--;
-                        break;
-                    }
+        } else {
+            /*
+             * Try to amalgamate a contiguous string of characters
+             * with the expected sensible width, for the common case
+             * in which we're using a monospaced font and everything
+             * works as expected.
+             */
+            while (clen < utflen) {
+                int oldclen = clen;
+                clen++;                       /* skip UTF-8 introducer byte */
+                while (clen < utflen &&
+                       (unsigned char)utfptr[clen] >= 0x80 &&
+                       (unsigned char)utfptr[clen] < 0xC0)
+                    clen++;
+                n++;
+                if (pangofont_char_width(layout, pfont,
+                                         string[n-1], utfptr + oldclen,
+                                         clen - oldclen) != cellwidth) {
+                    clen = oldclen;
+                    n--;
+                    break;
                 }
             }
         }
 
        pango_layout_set_text(layout, utfptr, clen);
        pango_layout_get_pixel_extents(layout, NULL, &rect);
-       gdk_draw_layout(target, gc, x + (n*cellwidth - rect.width)/2,
-                       y + (pfont->u.height - rect.height)/2, layout);
+        
+       draw_layout(ctx,
+                    x + (n*cellwidth - rect.width)/2,
+                    y + (pfont->u.height - rect.height)/2, layout);
        if (shadowbold)
-           gdk_draw_layout(target, gc, x + (n*cellwidth - rect.width)/2 + pfont->shadowoffset,
-                           y + (pfont->u.height - rect.height)/2, layout);
+           draw_layout(ctx,
+                        x + (n*cellwidth - rect.width)/2 + pfont->shadowoffset,
+                        y + (pfont->u.height - rect.height)/2, layout);
 
        utflen -= clen;
        utfptr += clen;
@@ -1436,7 +1790,9 @@ static const struct unifont_vtable *unifont_types[] = {
 #if GTK_CHECK_VERSION(2,0,0)
     &pangofont_vtable,
 #endif
+#ifndef NOT_X_WINDOWS
     &x11font_vtable,
+#endif
 };
 
 /*
@@ -1506,12 +1862,11 @@ void unifont_destroy(unifont *font)
     font->vt->destroy(font);
 }
 
-void unifont_draw_text(GdkDrawable *target, GdkGC *gc, unifont *font,
+void unifont_draw_text(unifont_drawctx *ctx, unifont *font,
                       int x, int y, const wchar_t *string, int len,
                       int wide, int bold, int cellwidth)
 {
-    font->vt->draw_text(target, gc, font, x, y, string, len,
-                       wide, bold, cellwidth);
+    font->vt->draw_text(ctx, font, x, y, string, len, wide, bold, cellwidth);
 }
 
 /* ----------------------------------------------------------------------
@@ -1527,9 +1882,9 @@ void unifont_draw_text(GdkDrawable *target, GdkGC *gc, unifont *font,
  * destroy.
  */
 
-static void multifont_draw_text(GdkDrawable *target, GdkGC *gc, unifont *font,
-                               int x, int y, const wchar_t *string, int len,
-                               int wide, int bold, int cellwidth);
+static void multifont_draw_text(unifont_drawctx *ctx, unifont *font,
+                                int x, int y, const wchar_t *string, int len,
+                                int wide, int bold, int cellwidth);
 static void multifont_destroy(unifont *font);
 
 struct multifont {
@@ -1563,6 +1918,7 @@ unifont *multifont_create(GtkWidget *widget, const char *name,
     if (!font)
         return NULL;
 
+    fallback = NULL;
     if (font->want_fallback) {
        for (i = 0; i < lenof(unifont_types); i++) {
             if (unifont_types[i]->create_fallback) {
@@ -1587,6 +1943,7 @@ unifont *multifont_create(GtkWidget *widget, const char *name,
     mfont->u.height = font->height;
     mfont->u.public_charset = font->public_charset;
     mfont->u.want_fallback = FALSE; /* shouldn't be needed, but just in case */
+    mfont->u.preferred_drawtype = font->preferred_drawtype;
     mfont->main = font;
     mfont->fallback = fallback;
 
@@ -1602,11 +1959,12 @@ static void multifont_destroy(unifont *font)
     sfree(font);
 }
 
-static void multifont_draw_text(GdkDrawable *target, GdkGC *gc, unifont *font,
-                               int x, int y, const wchar_t *string, int len,
-                               int wide, int bold, int cellwidth)
+static void multifont_draw_text(unifont_drawctx *ctx, unifont *font, int x,
+                                int y, const wchar_t *string, int len,
+                                int wide, int bold, int cellwidth)
 {
     struct multifont *mfont = (struct multifont *)font;
+    unifont *f;
     int ok, i;
 
     while (len > 0) {
@@ -1623,14 +1981,55 @@ static void multifont_draw_text(GdkDrawable *target, GdkGC *gc, unifont *font,
         /*
          * Now display it.
          */
-        unifont_draw_text(target, gc, ok ? mfont->main : mfont->fallback,
-                          x, y, string, i, wide, bold, cellwidth);
+        f = ok ? mfont->main : mfont->fallback;
+        if (f)
+            unifont_draw_text(ctx, f, x, y, string, i, wide, bold, cellwidth);
         string += i;
         len -= i;
         x += i * cellwidth;
     }
 }
 
+/* ----------------------------------------------------------------------
+ * Utility routine used by the code below, and also gtkdlg.c.
+ */
+
+void get_label_text_dimensions(const char *text, int *width, int *height)
+{
+    /*
+     * Determine the dimensions of a piece of text in the standard
+     * font used in GTK interface elements like labels. We do this by
+     * instantiating an actual GtkLabel, and then querying its size.
+     *
+     * But GTK2 and GTK3 require us to query the size completely
+     * differently. I'm sure there ought to be an easier approach than
+     * the way I'm doing this in GTK3, too!
+     */
+    GtkWidget *label = gtk_label_new(text);
+
+#if GTK_CHECK_VERSION(3,0,0)
+    PangoLayout *layout = gtk_label_get_layout(GTK_LABEL(label));
+    PangoRectangle logrect;
+    pango_layout_get_extents(layout, NULL, &logrect);
+    if (width)
+        *width = logrect.width / PANGO_SCALE;
+    if (height)
+        *height = logrect.height / PANGO_SCALE;
+#else
+    GtkRequisition req;
+    gtk_widget_size_request(label, &req);
+    if (width)
+        *width = req.width;
+    if (height)
+        *height = req.height;
+#endif
+
+    g_object_ref_sink(G_OBJECT(label));
+#if GTK_CHECK_VERSION(2,10,0)
+    g_object_unref(label);
+#endif
+}
+
 #if GTK_CHECK_VERSION(2,0,0)
 
 /* ----------------------------------------------------------------------
@@ -1646,8 +2045,11 @@ typedef struct unifontsel_internal {
     GtkListStore *family_model, *style_model, *size_model;
     GtkWidget *family_list, *style_list, *size_entry, *size_list;
     GtkWidget *filter_buttons[4];
+    int n_filter_buttons;
     GtkWidget *preview_area;
+#ifndef NO_BACKING_PIXMAPS
     GdkPixmap *preview_pixmap;
+#endif
     int preview_width, preview_height;
     GdkColor preview_fg, preview_bg;
     int filter_flags;
@@ -1708,7 +2110,7 @@ static int strnullcasecmp(const char *a, const char *b)
     /*
      * Otherwise, ordinary strcasecmp.
      */
-    return g_strcasecmp(a, b);
+    return g_ascii_strcasecmp(a, b);
 }
 
 static int fontinfo_realname_compare(void *av, void *bv)
@@ -1766,6 +2168,8 @@ static int fontinfo_selorder_compare(void *av, void *bv)
     return 0;
 }
 
+static void unifontsel_draw_preview_text(unifontsel_internal *fs);
+
 static void unifontsel_deselect(unifontsel_internal *fs)
 {
     fs->selected = NULL;
@@ -1773,6 +2177,7 @@ static void unifontsel_deselect(unifontsel_internal *fs)
     gtk_list_store_clear(fs->size_model);
     gtk_widget_set_sensitive(fs->u.ok_button, FALSE);
     gtk_widget_set_sensitive(fs->size_entry, FALSE);
+    unifontsel_draw_preview_text(fs);
 }
 
 static void unifontsel_setup_familylist(unifontsel_internal *fs)
@@ -1783,6 +2188,8 @@ static void unifontsel_setup_familylist(unifontsel_internal *fs)
     int currflags = -1;
     fontinfo *info;
 
+    fs->inhibit_response = TRUE;
+
     gtk_list_store_clear(fs->family_model);
     listindex = 0;
 
@@ -1833,6 +2240,8 @@ static void unifontsel_setup_familylist(unifontsel_internal *fs)
      */
     if (fs->selected && fs->selected->familyindex < 0)
        unifontsel_deselect(fs);
+
+    fs->inhibit_response = FALSE;
 }
 
 static void unifontsel_setup_stylelist(unifontsel_internal *fs,
@@ -1953,16 +2362,17 @@ static void unifontsel_set_filter_buttons(unifontsel_internal *fs)
 {
     int i;
 
-    for (i = 0; i < lenof(fs->filter_buttons); i++) {
-       int flagbit = GPOINTER_TO_INT(gtk_object_get_data
-                                     (GTK_OBJECT(fs->filter_buttons[i]),
+    for (i = 0; i < fs->n_filter_buttons; i++) {
+        int flagbit = GPOINTER_TO_INT(g_object_get_data
+                                      (G_OBJECT(fs->filter_buttons[i]),
                                       "user-data"));
        gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(fs->filter_buttons[i]),
                                     !!(fs->filter_flags & flagbit));
     }
 }
 
-static void unifontsel_draw_preview_text(unifontsel_internal *fs)
+static void unifontsel_draw_preview_text_inner(unifont_drawctx *dctx,
+                                               unifontsel_internal *fs)
 {
     unifont *font;
     char *sizename = NULL;
@@ -1977,74 +2387,132 @@ static void unifontsel_draw_preview_text(unifontsel_internal *fs)
     } else
        font = NULL;
 
-    if (fs->preview_pixmap) {
-       GdkGC *gc = gdk_gc_new(fs->preview_pixmap);
-       gdk_gc_set_foreground(gc, &fs->preview_bg);
-       gdk_draw_rectangle(fs->preview_pixmap, gc, 1, 0, 0,
-                          fs->preview_width, fs->preview_height);
-       gdk_gc_set_foreground(gc, &fs->preview_fg);
-       if (font) {
-           /*
-            * The pangram used here is rather carefully
-            * constructed: it contains a sequence of very narrow
-            * letters (`jil') and a pair of adjacent very wide
-            * letters (`wm').
-            *
-            * If the user selects a proportional font, it will be
-            * coerced into fixed-width character cells when used
-            * in the actual terminal window. We therefore display
-            * it the same way in the preview pane, so as to show
-            * it the way it will actually be displayed - and we
-            * deliberately pick a pangram which will show the
-            * resulting miskerning at its worst.
-            *
-            * We aren't trying to sell people these fonts; we're
-            * trying to let them make an informed choice. Better
-            * that they find out the problems with using
-            * proportional fonts in terminal windows here than
-            * that they go to the effort of selecting their font
-            * and _then_ realise it was a mistake.
-            */
-           info->fontclass->draw_text(fs->preview_pixmap, gc, font,
-                                      0, font->ascent,
-                                      L"bankrupt jilted showmen quiz convex fogey",
-                                      41, FALSE, FALSE, font->width);
-           info->fontclass->draw_text(fs->preview_pixmap, gc, font,
-                                      0, font->ascent + font->height,
-                                      L"BANKRUPT JILTED SHOWMEN QUIZ CONVEX FOGEY",
-                                      41, FALSE, FALSE, font->width);
-           /*
-            * The ordering of punctuation here is also selected
-            * with some specific aims in mind. I put ` and '
-            * together because some software (and people) still
-            * use them as matched quotes no matter what Unicode
-            * might say on the matter, so people can quickly
-            * check whether they look silly in a candidate font.
-            * The sequence #_@ is there to let people judge the
-            * suitability of the underscore as an effectively
-            * alphabetic character (since that's how it's often
-            * used in practice, at least by programmers).
-            */
-           info->fontclass->draw_text(fs->preview_pixmap, gc, font,
-                                      0, font->ascent + font->height * 2,
-                                      L"0123456789!?,.:;<>()[]{}\\/`'\"+*-=~#_@|%&^$",
-                                      42, FALSE, FALSE, font->width);
-       }
-       gdk_gc_unref(gc);
-       gdk_window_invalidate_rect(fs->preview_area->window, NULL, FALSE);
+#ifdef DRAW_TEXT_GDK
+    if (dctx->type == DRAWTYPE_GDK) {
+        gdk_gc_set_foreground(dctx->u.gdk.gc, &fs->preview_bg);
+        gdk_draw_rectangle(dctx->u.gdk.target, dctx->u.gdk.gc, 1, 0, 0,
+                           fs->preview_width, fs->preview_height);
+        gdk_gc_set_foreground(dctx->u.gdk.gc, &fs->preview_fg);
     }
-    if (font)
+#endif
+#ifdef DRAW_TEXT_CAIRO
+    if (dctx->type == DRAWTYPE_CAIRO) {
+        cairo_set_source_rgb(dctx->u.cairo.cr,
+                             fs->preview_bg.red / 65535.0,
+                             fs->preview_bg.green / 65535.0,
+                             fs->preview_bg.blue / 65535.0);
+        cairo_paint(dctx->u.cairo.cr);
+        cairo_set_source_rgb(dctx->u.cairo.cr,
+                             fs->preview_fg.red / 65535.0,
+                             fs->preview_fg.green / 65535.0,
+                             fs->preview_fg.blue / 65535.0);
+    }
+#endif
+
+    if (font) {
+        /*
+         * The pangram used here is rather carefully
+         * constructed: it contains a sequence of very narrow
+         * letters (`jil') and a pair of adjacent very wide
+         * letters (`wm').
+         *
+         * If the user selects a proportional font, it will be
+         * coerced into fixed-width character cells when used
+         * in the actual terminal window. We therefore display
+         * it the same way in the preview pane, so as to show
+         * it the way it will actually be displayed - and we
+         * deliberately pick a pangram which will show the
+         * resulting miskerning at its worst.
+         *
+         * We aren't trying to sell people these fonts; we're
+         * trying to let them make an informed choice. Better
+         * that they find out the problems with using
+         * proportional fonts in terminal windows here than
+         * that they go to the effort of selecting their font
+         * and _then_ realise it was a mistake.
+         */
+        info->fontclass->draw_text(dctx, font,
+                                   0, font->ascent,
+                                   L"bankrupt jilted showmen quiz convex fogey",
+                                   41, FALSE, FALSE, font->width);
+        info->fontclass->draw_text(dctx, font,
+                                   0, font->ascent + font->height,
+                                   L"BANKRUPT JILTED SHOWMEN QUIZ CONVEX FOGEY",
+                                   41, FALSE, FALSE, font->width);
+        /*
+         * The ordering of punctuation here is also selected
+         * with some specific aims in mind. I put ` and '
+         * together because some software (and people) still
+         * use them as matched quotes no matter what Unicode
+         * might say on the matter, so people can quickly
+         * check whether they look silly in a candidate font.
+         * The sequence #_@ is there to let people judge the
+         * suitability of the underscore as an effectively
+         * alphabetic character (since that's how it's often
+         * used in practice, at least by programmers).
+         */
+        info->fontclass->draw_text(dctx, font,
+                                   0, font->ascent + font->height * 2,
+                                   L"0123456789!?,.:;<>()[]{}\\/`'\"+*-=~#_@|%&^$",
+                                   42, FALSE, FALSE, font->width);
+
        info->fontclass->destroy(font);
+    }
 
     sfree(sizename);
 }
 
+static void unifontsel_draw_preview_text(unifontsel_internal *fs)
+{
+    unifont_drawctx dctx;
+    GdkWindow *target;
+
+#ifndef NO_BACKING_PIXMAPS
+    target = fs->preview_pixmap;
+#else
+    target = gtk_widget_get_window(fs->preview_area);
+#endif
+    if (!target) /* we may be called when we haven't created everything yet */
+        return;
+
+    dctx.type = DRAWTYPE_DEFAULT;
+#ifdef DRAW_TEXT_GDK
+    if (dctx.type == DRAWTYPE_GDK) {
+        dctx.u.gdk.target = target;
+        dctx.u.gdk.gc = gdk_gc_new(target);
+    }
+#endif
+#ifdef DRAW_TEXT_CAIRO
+    if (dctx.type == DRAWTYPE_CAIRO) {
+        dctx.u.cairo.widget = GTK_WIDGET(fs->preview_area);
+        dctx.u.cairo.cr = gdk_cairo_create(target);
+    }
+#endif
+
+    unifontsel_draw_preview_text_inner(&dctx, fs);
+
+#ifdef DRAW_TEXT_GDK
+    if (dctx.type == DRAWTYPE_GDK) {
+        gdk_gc_unref(dctx.u.gdk.gc);
+    }
+#endif
+#ifdef DRAW_TEXT_CAIRO
+    if (dctx.type == DRAWTYPE_CAIRO) {
+        cairo_destroy(dctx.u.cairo.cr);
+    }
+#endif
+
+    gdk_window_invalidate_rect(gtk_widget_get_window(fs->preview_area),
+                               NULL, FALSE);
+}
+
 static void unifontsel_select_font(unifontsel_internal *fs,
                                   fontinfo *info, int size, int leftlist,
                                   int size_is_explicit)
 {
     int index;
     int minval, maxval;
+    gboolean success;
     GtkTreePath *treepath;
     GtkTreeIter iter;
 
@@ -2085,7 +2553,9 @@ static void unifontsel_select_font(unifontsel_internal *fs,
         treepath);
     gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(fs->family_list),
                                 treepath, NULL, FALSE, 0.0, 0.0);
-    gtk_tree_model_get_iter(GTK_TREE_MODEL(fs->family_model), &iter, treepath);
+    success = gtk_tree_model_get_iter(GTK_TREE_MODEL(fs->family_model),
+                                      &iter, treepath);
+    assert(success);
     gtk_tree_path_free(treepath);
 
     /*
@@ -2164,7 +2634,8 @@ static void unifontsel_select_font(unifontsel_internal *fs,
      * Grey out the font size edit box if we're not using a
      * scalable font.
      */
-    gtk_entry_set_editable(GTK_ENTRY(fs->size_entry), fs->selected->size == 0);
+    gtk_editable_set_editable(GTK_EDITABLE(fs->size_entry),
+                              fs->selected->size == 0);
     gtk_widget_set_sensitive(fs->size_entry, fs->selected->size == 0);
 
     unifontsel_draw_preview_text(fs);
@@ -2177,8 +2648,8 @@ static void unifontsel_button_toggled(GtkToggleButton *tb, gpointer data)
     unifontsel_internal *fs = (unifontsel_internal *)data;
     int newstate = gtk_toggle_button_get_active(tb);
     int newflags;
-    int flagbit = GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(tb),
-                                                     "user-data"));
+    int flagbit = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(tb),
+                                                    "user-data"));
 
     if (newstate)
        newflags = fs->filter_flags | flagbit;
@@ -2278,6 +2749,8 @@ static fontinfo *update_for_intended_size(unifontsel_internal *fs,
      */
     below = findrelpos234(fs->fonts_by_selorder, &info2, NULL,
                          REL234_LE, &pos);
+    if (!below)
+        pos = -1;
     above = index234(fs->fonts_by_selorder, pos+1);
 
     /*
@@ -2285,7 +2758,7 @@ static fontinfo *update_for_intended_size(unifontsel_internal *fs,
      * case. If we have, it'll be in `below' and not `above',
      * because we did a REL234_LE rather than REL234_LT search.
      */
-    if (!fontinfo_selorder_compare(&info2, below))
+    if (below && !fontinfo_selorder_compare(&info2, below))
        return below;
 
     /*
@@ -2456,25 +2929,47 @@ static void alias_resolve(GtkTreeView *treeview, GtkTreePath *path,
     }
 }
 
+#if GTK_CHECK_VERSION(3,0,0)
+static gint unifontsel_draw_area(GtkWidget *widget, cairo_t *cr, gpointer data)
+{
+    unifontsel_internal *fs = (unifontsel_internal *)data;
+    unifont_drawctx dctx;
+
+    dctx.type = DRAWTYPE_CAIRO;
+    dctx.u.cairo.widget = widget;
+    dctx.u.cairo.cr = cr;
+    unifontsel_draw_preview_text_inner(&dctx, fs);
+
+    return TRUE;
+}
+#else
 static gint unifontsel_expose_area(GtkWidget *widget, GdkEventExpose *event,
                                   gpointer data)
 {
     unifontsel_internal *fs = (unifontsel_internal *)data;
 
+#ifndef NO_BACKING_PIXMAPS
     if (fs->preview_pixmap) {
-       gdk_draw_pixmap(widget->window,
-                       widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
+        gdk_draw_pixmap(gtk_widget_get_window(widget),
+                       (gtk_widget_get_style(widget)->fg_gc
+                         [gtk_widget_get_state(widget)]),
                        fs->preview_pixmap,
                        event->area.x, event->area.y,
                        event->area.x, event->area.y,
                        event->area.width, event->area.height);
     }
+#else
+    unifontsel_draw_preview_text(fs);
+#endif
+
     return TRUE;
 }
+#endif
 
 static gint unifontsel_configure_area(GtkWidget *widget,
                                      GdkEventConfigure *event, gpointer data)
 {
+#ifndef NO_BACKING_PIXMAPS
     unifontsel_internal *fs = (unifontsel_internal *)data;
     int ox, oy, nx, ny, x, y;
 
@@ -2491,14 +2986,16 @@ static gint unifontsel_configure_area(GtkWidget *widget,
        
        nx = (x > ox ? x : ox);
        ny = (y > oy ? y : oy);
-       fs->preview_pixmap = gdk_pixmap_new(widget->window, nx, ny, -1);
+       fs->preview_pixmap = gdk_pixmap_new(gtk_widget_get_window(widget),
+                                            nx, ny, -1);
        fs->preview_width = nx;
        fs->preview_height = ny;
 
        unifontsel_draw_preview_text(fs);
     }
+#endif
 
-    gdk_window_invalidate_rect(widget->window, NULL, FALSE);
+    gdk_window_invalidate_rect(gtk_widget_get_window(widget), NULL, FALSE);
 
     return TRUE;
 }
@@ -2516,27 +3013,22 @@ unifontsel *unifontsel_new(const char *wintitle)
     fs->selected = NULL;
 
     {
+        int width, height;
+
        /*
         * Invent some magic size constants.
         */
-       GtkRequisition req;
-       label = gtk_label_new("Quite Long Font Name (Foundry)");
-       gtk_widget_size_request(label, &req);
-       font_width = req.width;
-       lists_height = 14 * req.height;
-       preview_height = 5 * req.height;
-       gtk_label_set_text(GTK_LABEL(label), "Italic Extra Condensed");
-       gtk_widget_size_request(label, &req);
-       style_width = req.width;
-       gtk_label_set_text(GTK_LABEL(label), "48000");
-       gtk_widget_size_request(label, &req);
-       size_width = req.width;
-#if GTK_CHECK_VERSION(2,10,0)
-       g_object_ref_sink(label);
-       g_object_unref(label);
-#else
-        gtk_object_sink(GTK_OBJECT(label));
-#endif
+       get_label_text_dimensions("Quite Long Font Name (Foundry)",
+                                  &width, &height);
+       font_width = width;
+       lists_height = 14 * height;
+       preview_height = 5 * height;
+
+       get_label_text_dimensions("Italic Extra Condensed", &width, &height);
+       style_width = width;
+
+       get_label_text_dimensions("48000", &width, &height);
+       size_width = width;
     }
 
     /*
@@ -2547,18 +3039,23 @@ unifontsel *unifontsel_new(const char *wintitle)
     fs->u.window = GTK_WINDOW(gtk_dialog_new());
     gtk_window_set_title(fs->u.window, wintitle);
     fs->u.cancel_button = gtk_dialog_add_button
-       (GTK_DIALOG(fs->u.window), GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL);
+       (GTK_DIALOG(fs->u.window), STANDARD_CANCEL_LABEL, GTK_RESPONSE_CANCEL);
     fs->u.ok_button = gtk_dialog_add_button
-       (GTK_DIALOG(fs->u.window), GTK_STOCK_OK, GTK_RESPONSE_OK);
+       (GTK_DIALOG(fs->u.window), STANDARD_OK_LABEL, GTK_RESPONSE_OK);
     gtk_widget_grab_default(fs->u.ok_button);
 
     /*
      * Now set up the internal fields, including in particular all
      * the controls that actually allow the user to select fonts.
      */
+#if GTK_CHECK_VERSION(3,0,0)
+    table = gtk_grid_new();
+    gtk_grid_set_column_spacing(GTK_GRID(table), 8);
+#else
     table = gtk_table_new(8, 3, FALSE);
-    gtk_widget_show(table);
     gtk_table_set_col_spacings(GTK_TABLE(table), 8);
+#endif
+    gtk_widget_show(table);
 #if GTK_CHECK_VERSION(2,4,0)
     /* GtkAlignment seems to be the simplest way to put padding round things */
     w = gtk_alignment_new(0, 0, 1, 1);
@@ -2568,13 +3065,19 @@ unifontsel *unifontsel_new(const char *wintitle)
 #else
     w = table;
 #endif
-    gtk_box_pack_start(GTK_BOX(GTK_DIALOG(fs->u.window)->vbox),
+    gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area
+                               (GTK_DIALOG(fs->u.window))),
                       w, TRUE, TRUE, 0);
 
     label = gtk_label_new_with_mnemonic("_Font:");
     gtk_widget_show(label);
     gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.0);
+#if GTK_CHECK_VERSION(3,0,0)
+    gtk_grid_attach(GTK_GRID(table), label, 0, 0, 1, 1);
+    g_object_set(G_OBJECT(label), "hexpand", TRUE, (const char *)NULL);
+#else
     gtk_table_attach(GTK_TABLE(table), label, 0, 1, 0, 1, GTK_FILL, 0, 0, 0);
+#endif
 
     /*
      * The Font list box displays only a string, but additionally
@@ -2604,15 +3107,25 @@ unifontsel *unifontsel_new(const char *wintitle)
     gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll),
                                   GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
     gtk_widget_set_size_request(scroll, font_width, lists_height);
+#if GTK_CHECK_VERSION(3,0,0)
+    gtk_grid_attach(GTK_GRID(table), scroll, 0, 1, 1, 2);
+    g_object_set(G_OBJECT(scroll), "expand", TRUE, (const char *)NULL);
+#else
     gtk_table_attach(GTK_TABLE(table), scroll, 0, 1, 1, 3, GTK_FILL,
                     GTK_EXPAND | GTK_FILL, 0, 0);
+#endif
     fs->family_model = model;
     fs->family_list = w;
 
     label = gtk_label_new_with_mnemonic("_Style:");
     gtk_widget_show(label);
     gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.0);
+#if GTK_CHECK_VERSION(3,0,0)
+    gtk_grid_attach(GTK_GRID(table), label, 1, 0, 1, 1);
+    g_object_set(G_OBJECT(label), "hexpand", TRUE, (const char *)NULL);
+#else
     gtk_table_attach(GTK_TABLE(table), label, 1, 2, 0, 1, GTK_FILL, 0, 0, 0);
+#endif
 
     /*
      * The Style list box can contain insensitive elements
@@ -2641,15 +3154,25 @@ unifontsel *unifontsel_new(const char *wintitle)
     gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll),
                                   GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
     gtk_widget_set_size_request(scroll, style_width, lists_height);
+#if GTK_CHECK_VERSION(3,0,0)
+    gtk_grid_attach(GTK_GRID(table), scroll, 1, 1, 1, 2);
+    g_object_set(G_OBJECT(scroll), "expand", TRUE, (const char *)NULL);
+#else
     gtk_table_attach(GTK_TABLE(table), scroll, 1, 2, 1, 3, GTK_FILL,
                     GTK_EXPAND | GTK_FILL, 0, 0);
+#endif
     fs->style_model = model;
     fs->style_list = w;
 
     label = gtk_label_new_with_mnemonic("Si_ze:");
     gtk_widget_show(label);
     gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.0);
+#if GTK_CHECK_VERSION(3,0,0)
+    gtk_grid_attach(GTK_GRID(table), label, 2, 0, 1, 1);
+    g_object_set(G_OBJECT(label), "hexpand", TRUE, (const char *)NULL);
+#else
     gtk_table_attach(GTK_TABLE(table), label, 2, 3, 0, 1, GTK_FILL, 0, 0, 0);
+#endif
 
     /*
      * The Size label attaches primarily to a text input box so
@@ -2660,7 +3183,12 @@ unifontsel *unifontsel_new(const char *wintitle)
     gtk_label_set_mnemonic_widget(GTK_LABEL(label), w);
     gtk_widget_set_size_request(w, size_width, -1);
     gtk_widget_show(w);
+#if GTK_CHECK_VERSION(3,0,0)
+    gtk_grid_attach(GTK_GRID(table), w, 2, 1, 1, 1);
+    g_object_set(G_OBJECT(w), "hexpand", TRUE, (const char *)NULL);
+#else
     gtk_table_attach(GTK_TABLE(table), w, 2, 3, 1, 2, GTK_FILL, 0, 0, 0);
+#endif
     g_signal_connect(G_OBJECT(w), "changed", G_CALLBACK(size_entry_changed),
                     fs);
 
@@ -2683,8 +3211,13 @@ unifontsel *unifontsel_new(const char *wintitle)
     gtk_widget_show(scroll);
     gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll),
                                   GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
+#if GTK_CHECK_VERSION(3,0,0)
+    gtk_grid_attach(GTK_GRID(table), scroll, 2, 2, 1, 1);
+    g_object_set(G_OBJECT(scroll), "expand", TRUE, (const char *)NULL);
+#else
     gtk_table_attach(GTK_TABLE(table), scroll, 2, 3, 2, 3, GTK_FILL,
                     GTK_EXPAND | GTK_FILL, 0, 0);
+#endif
     fs->size_model = model;
     fs->size_list = w;
 
@@ -2692,20 +3225,29 @@ unifontsel *unifontsel_new(const char *wintitle)
      * Preview widget.
      */
     fs->preview_area = gtk_drawing_area_new();
+#ifndef NO_BACKING_PIXMAPS
     fs->preview_pixmap = NULL;
+#endif
     fs->preview_width = 0;
     fs->preview_height = 0;
     fs->preview_fg.pixel = fs->preview_bg.pixel = 0;
     fs->preview_fg.red = fs->preview_fg.green = fs->preview_fg.blue = 0x0000;
     fs->preview_bg.red = fs->preview_bg.green = fs->preview_bg.blue = 0xFFFF;
+#if !GTK_CHECK_VERSION(3,0,0)
     gdk_colormap_alloc_color(gdk_colormap_get_system(), &fs->preview_fg,
                             FALSE, FALSE);
     gdk_colormap_alloc_color(gdk_colormap_get_system(), &fs->preview_bg,
                             FALSE, FALSE);
-    gtk_signal_connect(GTK_OBJECT(fs->preview_area), "expose_event",
-                      GTK_SIGNAL_FUNC(unifontsel_expose_area), fs);
-    gtk_signal_connect(GTK_OBJECT(fs->preview_area), "configure_event",
-                      GTK_SIGNAL_FUNC(unifontsel_configure_area), fs);
+#endif
+#if GTK_CHECK_VERSION(3,0,0)
+    g_signal_connect(G_OBJECT(fs->preview_area), "draw",
+                     G_CALLBACK(unifontsel_draw_area), fs);
+#else
+    g_signal_connect(G_OBJECT(fs->preview_area), "expose_event",
+                     G_CALLBACK(unifontsel_expose_area), fs);
+#endif
+    g_signal_connect(G_OBJECT(fs->preview_area), "configure_event",
+                     G_CALLBACK(unifontsel_configure_area), fs);
     gtk_widget_set_size_request(fs->preview_area, 1, preview_height);
     gtk_widget_show(fs->preview_area);
     ww = fs->preview_area;
@@ -2724,44 +3266,77 @@ unifontsel *unifontsel_new(const char *wintitle)
     w = gtk_frame_new("Preview of font");
     gtk_container_add(GTK_CONTAINER(w), ww);
     gtk_widget_show(w);
+#if GTK_CHECK_VERSION(3,0,0)
+    gtk_grid_attach(GTK_GRID(table), w, 0, 3, 3, 1);
+    g_object_set(G_OBJECT(w), "expand", TRUE, (const char *)NULL);
+#else
     gtk_table_attach(GTK_TABLE(table), w, 0, 3, 3, 4,
                     GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 8);
+#endif
 
-    i = 0;
+    /*
+     * We only provide the checkboxes for client- and server-side
+     * fonts if we have the X11 back end available, because that's the
+     * only situation in which more than one class of font is
+     * available anyway.
+     */
+    fs->n_filter_buttons = 0;
+#ifndef NOT_X_WINDOWS
     w = gtk_check_button_new_with_label("Show client-side fonts");
-    gtk_object_set_data(GTK_OBJECT(w), "user-data",
-                       GINT_TO_POINTER(FONTFLAG_CLIENTSIDE));
-    gtk_signal_connect(GTK_OBJECT(w), "toggled",
-                      GTK_SIGNAL_FUNC(unifontsel_button_toggled), fs);
+    g_object_set_data(G_OBJECT(w), "user-data",
+                      GINT_TO_POINTER(FONTFLAG_CLIENTSIDE));
+    g_signal_connect(G_OBJECT(w), "toggled",
+                     G_CALLBACK(unifontsel_button_toggled), fs);
     gtk_widget_show(w);
-    fs->filter_buttons[i++] = w;
+    fs->filter_buttons[fs->n_filter_buttons++] = w;
+#if GTK_CHECK_VERSION(3,0,0)
+    gtk_grid_attach(GTK_GRID(table), w, 0, 4, 3, 1);
+    g_object_set(G_OBJECT(w), "hexpand", TRUE, (const char *)NULL);
+#else
     gtk_table_attach(GTK_TABLE(table), w, 0, 3, 4, 5, GTK_FILL, 0, 0, 0);
+#endif
     w = gtk_check_button_new_with_label("Show server-side fonts");
-    gtk_object_set_data(GTK_OBJECT(w), "user-data",
-                       GINT_TO_POINTER(FONTFLAG_SERVERSIDE));
-    gtk_signal_connect(GTK_OBJECT(w), "toggled",
-                      GTK_SIGNAL_FUNC(unifontsel_button_toggled), fs);
+    g_object_set_data(G_OBJECT(w), "user-data",
+                      GINT_TO_POINTER(FONTFLAG_SERVERSIDE));
+    g_signal_connect(G_OBJECT(w), "toggled",
+                     G_CALLBACK(unifontsel_button_toggled), fs);
     gtk_widget_show(w);
-    fs->filter_buttons[i++] = w;
+    fs->filter_buttons[fs->n_filter_buttons++] = w;
+#if GTK_CHECK_VERSION(3,0,0)
+    gtk_grid_attach(GTK_GRID(table), w, 0, 5, 3, 1);
+    g_object_set(G_OBJECT(w), "hexpand", TRUE, (const char *)NULL);
+#else
     gtk_table_attach(GTK_TABLE(table), w, 0, 3, 5, 6, GTK_FILL, 0, 0, 0);
+#endif
     w = gtk_check_button_new_with_label("Show server-side font aliases");
-    gtk_object_set_data(GTK_OBJECT(w), "user-data",
-                       GINT_TO_POINTER(FONTFLAG_SERVERALIAS));
-    gtk_signal_connect(GTK_OBJECT(w), "toggled",
-                      GTK_SIGNAL_FUNC(unifontsel_button_toggled), fs);
+    g_object_set_data(G_OBJECT(w), "user-data",
+                      GINT_TO_POINTER(FONTFLAG_SERVERALIAS));
+    g_signal_connect(G_OBJECT(w), "toggled",
+                     G_CALLBACK(unifontsel_button_toggled), fs);
     gtk_widget_show(w);
-    fs->filter_buttons[i++] = w;
+    fs->filter_buttons[fs->n_filter_buttons++] = w;
+#if GTK_CHECK_VERSION(3,0,0)
+    gtk_grid_attach(GTK_GRID(table), w, 0, 6, 3, 1);
+    g_object_set(G_OBJECT(w), "hexpand", TRUE, (const char *)NULL);
+#else
     gtk_table_attach(GTK_TABLE(table), w, 0, 3, 6, 7, GTK_FILL, 0, 0, 0);
+#endif
+#endif /* NOT_X_WINDOWS */
     w = gtk_check_button_new_with_label("Show non-monospaced fonts");
-    gtk_object_set_data(GTK_OBJECT(w), "user-data",
-                       GINT_TO_POINTER(FONTFLAG_NONMONOSPACED));
-    gtk_signal_connect(GTK_OBJECT(w), "toggled",
-                      GTK_SIGNAL_FUNC(unifontsel_button_toggled), fs);
+    g_object_set_data(G_OBJECT(w), "user-data",
+                      GINT_TO_POINTER(FONTFLAG_NONMONOSPACED));
+    g_signal_connect(G_OBJECT(w), "toggled",
+                     G_CALLBACK(unifontsel_button_toggled), fs);
     gtk_widget_show(w);
-    fs->filter_buttons[i++] = w;
+    fs->filter_buttons[fs->n_filter_buttons++] = w;
+#if GTK_CHECK_VERSION(3,0,0)
+    gtk_grid_attach(GTK_GRID(table), w, 0, 7, 3, 1);
+    g_object_set(G_OBJECT(w), "hexpand", TRUE, (const char *)NULL);
+#else
     gtk_table_attach(GTK_TABLE(table), w, 0, 3, 7, 8, GTK_FILL, 0, 0, 0);
+#endif
 
-    assert(i == lenof(fs->filter_buttons));
+    assert(fs->n_filter_buttons <= lenof(fs->filter_buttons));
     fs->filter_flags = FONTFLAG_CLIENTSIDE | FONTFLAG_SERVERSIDE |
        FONTFLAG_SERVERALIAS;
     unifontsel_set_filter_buttons(fs);
@@ -2792,8 +3367,10 @@ void unifontsel_destroy(unifontsel *fontsel)
     unifontsel_internal *fs = (unifontsel_internal *)fontsel;
     fontinfo *info;
 
+#ifndef NO_BACKING_PIXMAPS
     if (fs->preview_pixmap)
        gdk_pixmap_unref(fs->preview_pixmap);
+#endif
 
     freetree234(fs->fonts_by_selorder);
     while ((info = delpos234(fs->fonts_by_realname, 0)) != NULL)