]> asedeno.scripts.mit.edu Git - PuTTY.git/blobdiff - unix/gtkfont.c
Add an assortment of missing frees and closes.
[PuTTY.git] / unix / gtkfont.c
index cd0097b184ec9192ca820347043939da7c9d87ae..d9980880fcab421a47bda70911df15516612ee23 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>
 #endif
+
+#define MAY_REFER_TO_GTK_IN_HEADERS
+
+#include "putty.h"
+#include "gtkfont.h"
+#include "gtkcompat.h"
+#include "gtkmisc.h"
+#include "tree234.h"
+
 #ifndef NOT_X_WINDOWS
 #include <gdk/gdkx.h>
 #include <X11/Xlib.h>
 #include <X11/Xatom.h>
 #endif
 
-#include "putty.h"
-#include "gtkfont.h"
-#include "tree234.h"
-
 /*
  * 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)
@@ -87,11 +83,15 @@ struct unifont_vtable {
     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 (*draw_combining)(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,
                               int *flags, int resolve_aliases);
     char *(*scale_fontname)(GtkWidget *widget, const char *name, int size);
+    char *(*size_increment)(unifont *font, int increment);
 
     /*
      * `Static data members' of the `class'.
@@ -111,6 +111,9 @@ static int x11font_has_glyph(unifont *font, wchar_t glyph);
 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 void x11font_draw_combining(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);
@@ -122,6 +125,7 @@ static char *x11font_canonify_fontname(GtkWidget *widget, const char *name,
                                       int resolve_aliases);
 static char *x11font_scale_fontname(GtkWidget *widget, const char *name,
                                    int size);
+static char *x11font_size_increment(unifont *font, int increment);
 
 #ifdef DRAW_TEXT_CAIRO
 struct cairo_cached_glyph {
@@ -210,61 +214,128 @@ static const struct unifont_vtable x11font_vtable = {
     x11font_destroy,
     x11font_has_glyph,
     x11font_draw_text,
+    x11font_draw_combining,
     x11font_enum_fonts,
     x11font_canonify_fontname,
     x11font_scale_fontname,
+    x11font_size_increment,
     "server",
 };
 
+#define XLFD_STRING_PARTS_LIST(S,I)             \
+    S(foundry)                                  \
+    S(family_name)                              \
+    S(weight_name)                              \
+    S(slant)                                    \
+    S(setwidth_name)                            \
+    S(add_style_name)                           \
+    I(pixel_size)                               \
+    I(point_size)                               \
+    I(resolution_x)                             \
+    I(resolution_y)                             \
+    S(spacing)                                  \
+    I(average_width)                            \
+    S(charset_registry)                         \
+    S(charset_encoding)                         \
+    /* end of list */
+
+/* Special value for int fields that xlfd_recompose will render as "*" */
+#define XLFD_INT_WILDCARD INT_MIN
+
+struct xlfd_decomposed {
+#define STR_FIELD(f) const char *f;
+#define INT_FIELD(f) int f;
+    XLFD_STRING_PARTS_LIST(STR_FIELD, INT_FIELD)
+#undef STR_FIELD
+#undef INT_FIELD
+};
+
+static struct xlfd_decomposed *xlfd_decompose(const char *xlfd)
+{
+    void *mem;
+    char *p, *components[14];
+    struct xlfd_decomposed *dec;
+    int i;
+
+    if (!xlfd)
+        return NULL;
+
+    mem = smalloc(sizeof(struct xlfd_decomposed) + strlen(xlfd) + 1);
+    p = ((char *)mem) + sizeof(struct xlfd_decomposed);
+    strcpy(p, xlfd);
+    dec = (struct xlfd_decomposed *)mem;
+
+    for (i = 0; i < 14; i++) {
+        if (*p != '-') {
+            /* Malformed XLFD: not enough '-' */
+            sfree(mem);
+            return NULL;
+        }
+        *p++ = '\0';
+        components[i] = p;
+        p += strcspn(p, "-");
+    }
+    if (*p) {
+        /* Malformed XLFD: too many '-' */
+        sfree(mem);
+        return NULL;
+    }
+
+    i = 0;
+#define STORE_STR(f) dec->f = components[i++];
+#define STORE_INT(f) dec->f = atoi(components[i++]);
+    XLFD_STRING_PARTS_LIST(STORE_STR, STORE_INT)
+#undef STORE_STR
+#undef STORE_INT
+
+    return dec;
+}
+
+static char *xlfd_recompose(const struct xlfd_decomposed *dec)
+{
+#define FMT_STR(f) "-%s"
+#define ARG_STR(f) , dec->f
+#define FMT_INT(f) "%s%.*d"
+#define ARG_INT(f)                                      \
+        , dec->f == XLFD_INT_WILDCARD ? "-*" : "-"      \
+        , dec->f == XLFD_INT_WILDCARD ? 0 : 1           \
+        , dec->f == XLFD_INT_WILDCARD ? 0 : dec->f
+    return dupprintf(XLFD_STRING_PARTS_LIST(FMT_STR, FMT_INT)
+                     XLFD_STRING_PARTS_LIST(ARG_STR, ARG_INT));
+#undef FMT_STR
+#undef ARG_STR
+#undef FMT_INT
+#undef ARG_INT
+}
+
 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] == '-') {
-           const char *strings[13];
-           char *dupname, *extrafree = NULL, *ret;
-           char *p, *q;
-           int nstr;
-
-           p = q = dupname = dupstr(name); /* skip initial minus */
-           nstr = 0;
-
-           while (*p && nstr < lenof(strings)) {
-               if (*p == '-') {
-                   *p = '\0';
-                   strings[nstr++] = p+1;
-               }
-               p++;
-           }
-
-           if (nstr < lenof(strings)) {
-                sfree(dupname);
-               return NULL;           /* XLFD was malformed */
-            }
+        struct xlfd_decomposed *xlfd = xlfd_decompose(name);
+        if (!xlfd)
+            return NULL;
 
-           if (bold)
-               strings[2] = "bold";
+        if (bold)
+            xlfd->weight_name = "bold";
 
-           if (wide) {
-               /* 4 is `wideness', which obviously may have changed. */
-               /* 5 is additional style, which may be e.g. `ja' or `ko'. */
-               strings[4] = strings[5] = "*";
-               strings[11] = extrafree = dupprintf("%d", 2*atoi(strings[11]));
-           }
+        if (wide) {
+            /* Width name obviously may have changed. */
+            /* Additional style may now become e.g. `ja' or `ko'. */
+            xlfd->setwidth_name = xlfd->add_style_name = "*";
 
-           ret = dupcat("-", strings[ 0], "-", strings[ 1], "-", strings[ 2],
-                        "-", strings[ 3], "-", strings[ 4], "-", strings[ 5],
-                        "-", strings[ 6], "-", strings[ 7], "-", strings[ 8],
-                        "-", strings[ 9], "-", strings[10], "-", strings[11],
-                        "-", strings[12], NULL);
-           sfree(extrafree);
-           sfree(dupname);
+            /* Expect to double the average width. */
+            xlfd->average_width *= 2;
+        }
 
-           return ret;
-       }
+        {
+            char *ret = xlfd_recompose(xlfd);
+            sfree(xlfd);
+            return ret;
+        }
     }
     return NULL;
 }
@@ -281,22 +352,12 @@ static int x11_font_width(XFontStruct *xfs, int sixteen_bit)
     }
 }
 
-static int x11_font_has_glyph(XFontStruct *xfs, int byte1, int byte2)
+static const XCharStruct *x11_char_struct(
+    XFontStruct *xfs, unsigned char byte1, unsigned char byte2)
 {
     int index;
 
     /*
-     * Not to be confused with x11font_has_glyph, which is a method of
-     * the x11font 'class' and hence takes a unifont as argument. This
-     * is the low-level function which grubs about in an actual
-     * XFontStruct to see if a given glyph exists.
-     *
-     * We must do this ourselves rather than letting Xlib's
-     * XTextExtents16 do the job, because XTextExtents will helpfully
-     * substitute the font's default_char for any missing glyph and
-     * not tell us it did so, which precisely won't help us find out
-     * which glyphs _are_ missing.
-     *
      * The man page for XQueryFont is rather confusing about how the
      * per_char array in the XFontStruct is laid out, because it gives
      * formulae for determining the two-byte X character code _from_
@@ -337,10 +398,28 @@ static int x11_font_has_glyph(XFontStruct *xfs, int byte1, int byte2)
     }
 
     if (!xfs->per_char)   /* per_char NULL => everything in range exists */
-        return TRUE;
+        return &xfs->max_bounds;
 
-    return (xfs->per_char[index].ascent + xfs->per_char[index].descent > 0 ||
-            xfs->per_char[index].width > 0);
+    return &xfs->per_char[index];
+}
+
+static int x11_font_has_glyph(
+    XFontStruct *xfs, unsigned char byte1, unsigned char byte2)
+{
+    /*
+     * Not to be confused with x11font_has_glyph, which is a method of
+     * the x11font 'class' and hence takes a unifont as argument. This
+     * is the low-level function which grubs about in an actual
+     * XFontStruct to see if a given glyph exists.
+     *
+     * We must do this ourselves rather than letting Xlib's
+     * XTextExtents16 do the job, because XTextExtents will helpfully
+     * substitute the font's default_char for any missing glyph and
+     * not tell us it did so, which precisely won't help us find out
+     * which glyphs _are_ missing.
+     */
+    const XCharStruct *xcs = x11_char_struct(xfs, byte1, byte2);
+    return xcs && (xcs->ascent + xcs->descent > 0 || xcs->width > 0);
 }
 
 static unifont *x11font_create(GtkWidget *widget, const char *name,
@@ -349,7 +428,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;
@@ -455,7 +534,7 @@ static unifont *x11font_create(GtkWidget *widget, const char *name,
 
 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;
 
@@ -482,7 +561,7 @@ static void x11font_destroy(unifont *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].xfs, sfid & 1, !!(sfid & 2));
     xfont->fonts[sfid].xfs = XLoadQueryFont(disp, derived_name);
@@ -520,6 +599,8 @@ static int x11font_has_glyph(unifont *font, wchar_t glyph)
 
 #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,
@@ -539,15 +620,17 @@ static int x11font_width_8(unifont_drawctx *ctx, x11font_individual *xfi,
 #ifdef DRAW_TEXT_GDK
 static void x11font_gdk_setup(unifont_drawctx *ctx, x11font_individual *xfi)
 {
-    XSetFont(GDK_DISPLAY(), GDK_GC_XGC(ctx->u.gdk.gc), xfi->xfs->fid);
+    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(GDK_DISPLAY(), GDK_DRAWABLE_XID(ctx->u.gdk.target),
+    XDrawString16(disp, GDK_DRAWABLE_XID(ctx->u.gdk.target),
                   GDK_GC_XGC(ctx->u.gdk.gc), x, y, string+start, length);
 }
 
@@ -555,8 +638,9 @@ 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(GDK_DISPLAY(), GDK_DRAWABLE_XID(ctx->u.gdk.target),
+    XDrawString(disp, GDK_DRAWABLE_XID(ctx->u.gdk.target),
                 GDK_GC_XGC(ctx->u.gdk.gc), x, y, string+start, length);
 }
 #endif
@@ -565,6 +649,7 @@ static void x11font_gdk_draw_8(unifont_drawctx *ctx,
 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));
@@ -597,13 +682,13 @@ static void x11font_cairo_setup(unifont_drawctx *ctx, x11font_individual *xfi)
         }
 
         xfi->pixmap = XCreatePixmap
-            (GDK_DISPLAY(),
+            (disp,
              GDK_DRAWABLE_XID(gtk_widget_get_window(ctx->u.cairo.widget)),
              xfi->pixwidth, xfi->pixheight, 1);
-        gcvals.foreground = WhitePixel(GDK_DISPLAY(), widgetscr);
-        gcvals.background = BlackPixel(GDK_DISPLAY(), widgetscr);
+        gcvals.foreground = WhitePixel(disp, widgetscr);
+        gcvals.background = BlackPixel(disp, widgetscr);
         gcvals.font = xfi->xfs->fid;
-        xfi->gc = XCreateGC(GDK_DISPLAY(), xfi->pixmap,
+        xfi->gc = XCreateGC(disp, xfi->pixmap,
                             GCForeground | GCBackground | GCFont, &gcvals);
     }
 }
@@ -613,14 +698,19 @@ 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(GDK_DISPLAY(), xfi->pixmap, 0, 0,
+    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++) {
+    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;
@@ -636,7 +726,7 @@ static void x11font_cairo_cache_glyph(x11font_individual *xfi, int glyphindex)
          * principle that Unicode characters come in contiguous blocks
          * often used together */
         int old_nglyphs = xfi->nglyphs;
-        xfi->nglyphs = (glyphindex + 0xFF) & ~0xFF;
+        xfi->nglyphs = (glyphindex + 0x100) & ~0xFF;
         xfi->glyphcache = sresize(xfi->glyphcache, xfi->nglyphs,
                                   struct cairo_cached_glyph);
 
@@ -666,6 +756,7 @@ 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++) {
@@ -674,7 +765,7 @@ static void x11font_cairo_draw_16(unifont_drawctx *ctx,
                               (unsigned char)string[i].byte2);
             if (glyphindex >= xfi->nglyphs ||
                 !xfi->glyphcache[glyphindex].surface) {
-                XDrawImageString16(GDK_DISPLAY(), xfi->pixmap, xfi->gc,
+                XDrawImageString16(disp, xfi->pixmap, xfi->gc,
                                    xfi->pixoriginx, xfi->pixoriginy,
                                    string+i, 1);
                 x11font_cairo_cache_glyph(xfi, glyphindex);
@@ -689,6 +780,7 @@ 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++) {
@@ -696,7 +788,7 @@ static void x11font_cairo_draw_8(unifont_drawctx *ctx,
             int glyphindex = (unsigned char)string[i];
             if (glyphindex >= xfi->nglyphs ||
                 !xfi->glyphcache[glyphindex].surface) {
-                XDrawImageString(GDK_DISPLAY(), xfi->pixmap, xfi->gc,
+                XDrawImageString(disp, xfi->pixmap, xfi->gc,
                                  xfi->pixoriginx, xfi->pixoriginy,
                                  string+i, 1);
                 x11font_cairo_cache_glyph(xfi, glyphindex);
@@ -863,16 +955,31 @@ static void x11font_draw_text(unifont_drawctx *ctx, unifont *font,
     }
 }
 
+static void x11font_draw_combining(unifont_drawctx *ctx, unifont *font,
+                                   int x, int y, const wchar_t *string,
+                                   int len, int wide, int bold, int cellwidth)
+{
+    /*
+     * For server-side fonts, there's no sophisticated system for
+     * combining characters intelligently, so the best we can do is to
+     * overprint them on each other in the obvious way.
+     */
+    int i;
+    for (i = 0; i < len; i++)
+        x11font_draw_text(ctx, font, x, y, string+i, 1, wide, bold, cellwidth);
+}
+
 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;
@@ -883,73 +990,65 @@ static void x11font_enum_fonts(GtkWidget *widget,
     tmpsize = 0;
 
     for (i = 0; i < nnames; i++) {
-       if (fontnames[i][0] == '-') {
+        struct xlfd_decomposed *xlfd = xlfd_decompose(fontnames[i]);
+       if (xlfd) {
+            char *p, *font, *style, *stylekey, *charset;
+            int weightkey, slantkey, setwidthkey;
+            int thistmpsize;
+
            /*
-            * Dismember an XLFD and convert it into the format
-            * we'll be using in the font selector.
+            * Convert a dismembered XLFD into the format we'll be
+            * using in the font selector.
             */
-           char *components[14];
-           char *p, *font, *style, *stylekey, *charset;
-           int j, weightkey, slantkey, setwidthkey;
-           int thistmpsize, fontsize, flags;
-
-           thistmpsize = 4 * strlen(fontnames[i]) + 256;
-           if (tmpsize < thistmpsize) {
-               tmpsize = thistmpsize;
-               tmp = sresize(tmp, tmpsize, char);
-           }
-           strcpy(tmp, fontnames[i]);
 
+            thistmpsize = 4 * strlen(fontnames[i]) + 256;
+            if (tmpsize < thistmpsize) {
+                tmpsize = thistmpsize;
+                tmp = sresize(tmp, tmpsize, char);
+            }
            p = tmp;
-           for (j = 0; j < 14; j++) {
-               if (*p)
-                   *p++ = '\0';
-               components[j] = p;
-               while (*p && *p != '-')
-                   p++;
-           }
-           *p++ = '\0';
 
            /*
-            * Font name is made up of fields 0 and 1, in reverse
-            * order with parentheses. (This is what the GTK 1.2 X
-            * font selector does, and it seems to come out
-            * looking reasonably sensible.)
+            * Font name is in the form "family (foundry)". (This is
+            * what the GTK 1.2 X font selector does, and it seems to
+            * come out looking reasonably sensible.)
             */
            font = p;
-           p += 1 + sprintf(p, "%s (%s)", components[1], components[0]);
+           p += 1 + sprintf(p, "%s (%s)", xlfd->family_name, xlfd->foundry);
 
            /*
-            * Charset is made up of fields 12 and 13.
+            * Character set.
             */
            charset = p;
-           p += 1 + sprintf(p, "%s-%s", components[12], components[13]);
+           p += 1 + sprintf(p, "%s-%s", xlfd->charset_registry,
+                             xlfd->charset_encoding);
 
            /*
             * Style is a mixture of quite a lot of the fields,
             * with some strange formatting.
             */
            style = p;
-           p += sprintf(p, "%s", components[2][0] ? components[2] :
+           p += sprintf(p, "%s", xlfd->weight_name[0] ? xlfd->weight_name :
                         "regular");
-           if (!g_ascii_strcasecmp(components[3], "i"))
+           if (!g_ascii_strcasecmp(xlfd->slant, "i"))
                p += sprintf(p, " italic");
-           else if (!g_ascii_strcasecmp(components[3], "o"))
+           else if (!g_ascii_strcasecmp(xlfd->slant, "o"))
                p += sprintf(p, " oblique");
-           else if (!g_ascii_strcasecmp(components[3], "ri"))
+           else if (!g_ascii_strcasecmp(xlfd->slant, "ri"))
                p += sprintf(p, " reverse italic");
-           else if (!g_ascii_strcasecmp(components[3], "ro"))
+           else if (!g_ascii_strcasecmp(xlfd->slant, "ro"))
                p += sprintf(p, " reverse oblique");
-           else if (!g_ascii_strcasecmp(components[3], "ot"))
+           else if (!g_ascii_strcasecmp(xlfd->slant, "ot"))
                p += sprintf(p, " other-slant");
-           if (components[4][0] && g_ascii_strcasecmp(components[4], "normal"))
-               p += sprintf(p, " %s", components[4]);
-           if (!g_ascii_strcasecmp(components[10], "m"))
+           if (xlfd->setwidth_name[0] &&
+                g_ascii_strcasecmp(xlfd->setwidth_name, "normal"))
+               p += sprintf(p, " %s", xlfd->setwidth_name);
+           if (!g_ascii_strcasecmp(xlfd->spacing, "m"))
                p += sprintf(p, " [M]");
-           if (!g_ascii_strcasecmp(components[10], "c"))
+           if (!g_ascii_strcasecmp(xlfd->spacing, "c"))
                p += sprintf(p, " [C]");
-           if (components[5][0])
-               p += sprintf(p, " %s", components[5]);
+           if (xlfd->add_style_name[0])
+               p += sprintf(p, " %s", xlfd->add_style_name);
 
            /*
             * Style key is the same stuff as above, but with a
@@ -958,63 +1057,59 @@ static void x11font_enum_fonts(GtkWidget *widget,
             */
            p++;
            stylekey = p;
-           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"))
+           if (!g_ascii_strcasecmp(xlfd->weight_name, "medium") ||
+               !g_ascii_strcasecmp(xlfd->weight_name, "regular") ||
+               !g_ascii_strcasecmp(xlfd->weight_name, "normal") ||
+               !g_ascii_strcasecmp(xlfd->weight_name, "book"))
                weightkey = 0;
-           else if (!g_ascii_strncasecmp(components[2], "demi", 4) ||
-                    !g_ascii_strncasecmp(components[2], "semi", 4))
+           else if (!g_ascii_strncasecmp(xlfd->weight_name, "demi", 4) ||
+                    !g_ascii_strncasecmp(xlfd->weight_name, "semi", 4))
                weightkey = 1;
            else
                weightkey = 2;
-           if (!g_ascii_strcasecmp(components[3], "r"))
+           if (!g_ascii_strcasecmp(xlfd->slant, "r"))
                slantkey = 0;
-           else if (!g_ascii_strncasecmp(components[3], "r", 1))
+           else if (!g_ascii_strncasecmp(xlfd->slant, "r", 1))
                slantkey = 2;
            else
                slantkey = 1;
-           if (!g_ascii_strcasecmp(components[4], "normal"))
+           if (!g_ascii_strcasecmp(xlfd->setwidth_name, "normal"))
                setwidthkey = 0;
            else
                setwidthkey = 1;
 
-           p += sprintf(p, "%04d%04d%s%04d%04d%s%04d%04d%s%04d%s%04d%s",
-                        weightkey,
-                        (int)strlen(components[2]), components[2],
-                        slantkey,
-                        (int)strlen(components[3]), components[3],
-                        setwidthkey,
-                        (int)strlen(components[4]), components[4],
-                        (int)strlen(components[10]), components[10],
-                        (int)strlen(components[5]), components[5]);
+           p += sprintf(
+                p, "%04d%04d%s%04d%04d%s%04d%04d%s%04d%s%04d%s",
+                weightkey, (int)strlen(xlfd->weight_name), xlfd->weight_name,
+                slantkey, (int)strlen(xlfd->slant), xlfd->slant,
+                setwidthkey,
+                (int)strlen(xlfd->setwidth_name), xlfd->setwidth_name,
+                (int)strlen(xlfd->spacing), xlfd->spacing,
+                (int)strlen(xlfd->add_style_name), xlfd->add_style_name);
 
            assert(p - tmp < thistmpsize);
 
-           /*
-            * Size is in pixels, for our application, so we
-            * derive it directly from the pixel size field,
-            * number 6.
-            */
-           fontsize = atoi(components[6]);
-
            /*
             * Flags: we need to know whether this is a monospaced
             * font, which we do by examining the spacing field
             * again.
             */
            flags = FONTFLAG_SERVERSIDE;
-           if (!strchr("CcMm", components[10][0]))
+           if (!strchr("CcMm", xlfd->spacing[0]))
                flags |= FONTFLAG_NONMONOSPACED;
 
            /*
-            * Not sure why, but sometimes the X server will
-            * deliver dummy font types in which fontsize comes
-            * out as zero. Filter those out.
+            * Some fonts have a pixel size of zero, meaning they're
+            * treated as scalable. For these purposes, we only want
+            * fonts whose pixel size we actually know, so filter
+            * those out.
             */
-           if (fontsize)
-               callback(callback_ctx, fontnames[i], font, charset,
-                        style, stylekey, fontsize, flags, &x11font_vtable);
+           if (xlfd->pixel_size)
+                callback(callback_ctx, fontnames[i], font, charset,
+                         style, stylekey, xlfd->pixel_size, flags,
+                         &x11font_vtable);
+
+            sfree(xlfd);
        } else {
            /*
             * This isn't an XLFD, so it must be an alias.
@@ -1026,9 +1121,12 @@ static void x11font_enum_fonts(GtkWidget *widget,
             */
            callback(callback_ctx, fontnames[i], fontnames[i], NULL,
                     NULL, NULL, 0, FONTFLAG_SERVERALIAS, &x11font_vtable);
+
+            sfree(xlfd);
        }
     }
     XFreeFontNames(fontnames);
+    sfree(tmp);
 }
 
 static char *x11font_canonify_fontname(GtkWidget *widget, const char *name,
@@ -1047,7 +1145,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;
 
@@ -1090,6 +1188,94 @@ static char *x11font_scale_fontname(GtkWidget *widget, const char *name,
     return NULL;                      /* shan't */
 }
 
+static char *x11font_size_increment(unifont *font, int increment)
+{
+    struct x11font *xfont = (struct x11font *)font;
+    Display *disp = GDK_DISPLAY_XDISPLAY(gdk_display_get_default());
+    Atom fontprop = XInternAtom(disp, "FONT", False);
+    char *returned_name = NULL;
+    unsigned long ret;
+    if (XGetFontProperty(xfont->fonts[0].xfs, fontprop, &ret)) {
+        struct xlfd_decomposed *xlfd;
+        struct xlfd_decomposed *xlfd_best;
+        char *wc;
+        char **fontnames;
+        int nnames, i, max;
+
+        xlfd = xlfd_decompose(XGetAtomName(disp, (Atom)ret));
+        if (!xlfd)
+            return NULL;
+
+        /*
+         * Form a wildcard consisting of everything in the
+         * original XLFD except for the size-related fields.
+         */
+        {
+            struct xlfd_decomposed xlfd_wc = *xlfd; /* structure copy */
+            xlfd_wc.pixel_size = XLFD_INT_WILDCARD;
+            xlfd_wc.point_size = XLFD_INT_WILDCARD;
+            xlfd_wc.average_width = XLFD_INT_WILDCARD;
+            wc = xlfd_recompose(&xlfd_wc);
+        }
+
+        /*
+         * Fetch all the font names matching that wildcard.
+         */
+        max = 32768;
+        while (1) {
+            fontnames = XListFonts(disp, wc, max, &nnames);
+            if (nnames >= max) {
+                XFreeFontNames(fontnames);
+                max *= 2;
+            } else
+                break;
+        }
+
+        sfree(wc);
+
+        /*
+         * Iterate over those to find the one closest in size to the
+         * original font, in the correct direction.
+         */
+
+#define FLIPPED_SIZE(xlfd) \
+        (((xlfd)->pixel_size + (xlfd)->point_size) * \
+         (increment < 0 ? -1 : +1))
+
+        xlfd_best = NULL;
+        for (i = 0; i < nnames; i++) {
+            struct xlfd_decomposed *xlfd2 = xlfd_decompose(fontnames[i]);
+            if (!xlfd2)
+                continue;
+
+            if (xlfd2->pixel_size != 0 &&
+                FLIPPED_SIZE(xlfd2) > FLIPPED_SIZE(xlfd) &&
+                (!xlfd_best || FLIPPED_SIZE(xlfd2)<FLIPPED_SIZE(xlfd_best))) {
+                sfree(xlfd_best);
+                xlfd_best = xlfd2;
+                xlfd2 = NULL;
+            }
+
+            sfree(xlfd2);
+        }
+
+#undef FLIPPED_SIZE
+
+        if (xlfd_best) {
+            char *bare_returned_name = xlfd_recompose(xlfd_best);
+            returned_name = dupcat(
+                xfont->u.vt->prefix, ":", bare_returned_name,
+                (const char *)NULL);
+            sfree(bare_returned_name);
+        }
+
+        XFreeFontNames(fontnames);
+        sfree(xlfd);
+        sfree(xlfd_best);
+    }
+    return returned_name;
+}
+
 #endif /* NOT_X_WINDOWS */
 
 #if GTK_CHECK_VERSION(2,0,0)
@@ -1106,6 +1292,10 @@ static int pangofont_has_glyph(unifont *font, wchar_t glyph);
 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 void pangofont_draw_combining(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);
@@ -1120,6 +1310,7 @@ static char *pangofont_canonify_fontname(GtkWidget *widget, const char *name,
                                         int resolve_aliases);
 static char *pangofont_scale_fontname(GtkWidget *widget, const char *name,
                                      int size);
+static char *pangofont_size_increment(unifont *font, int increment);
 
 struct pangofont {
     struct unifont u;
@@ -1136,6 +1327,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 = {
@@ -1144,9 +1342,11 @@ static const struct unifont_vtable pangofont_vtable = {
     pangofont_destroy,
     pangofont_has_glyph,
     pangofont_draw_text,
+    pangofont_draw_combining,
     pangofont_enum_fonts,
     pangofont_canonify_fontname,
     pangofont_scale_fontname,
+    pangofont_size_increment,
     "client",
 };
 
@@ -1259,6 +1459,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);
 
@@ -1312,10 +1514,40 @@ 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
+     * asking Pango for text sizes 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_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 */
@@ -1339,9 +1571,10 @@ static void pango_cairo_draw_layout(unifont_drawctx *ctx,
 }
 #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)
+static void pangofont_draw_internal(unifont_drawctx *ctx, unifont *font,
+                                    int x, int y, const wchar_t *string,
+                                    int len, int wide, int bold, int cellwidth,
+                                    int combining)
 {
     struct pangofont *pfont = (struct pangofont *)font;
     PangoLayout *layout;
@@ -1392,6 +1625,7 @@ static void pangofont_draw_text(unifont_drawctx *ctx, unifont *font,
     utfptr = utfstring;
     while (utflen > 0) {
        int clen, n;
+        int desired = cellwidth * PANGO_SCALE;
 
        /*
         * We want to display every character from this string in
@@ -1417,34 +1651,39 @@ static void pangofont_draw_text(unifont_drawctx *ctx, unifont *font,
         * them to do that.
         */
 
-       /*
-        * Start by extracting a single UTF-8 character from the
-        * string.
-        */
-       clen = 1;
-       while (clen < utflen &&
-              (unsigned char)utfptr[clen] >= 0x80 &&
-              (unsigned char)utfptr[clen] < 0xC0)
-           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 (combining) {
             /*
-             * See if that character has the width we expect.
+             * For a character with combining stuff, we just dump the
+             * whole lot in one go, and expect it to take up just one
+             * character cell.
              */
-            pango_layout_set_text(layout, utfptr, clen);
-            pango_layout_get_pixel_extents(layout, NULL, &rect);
-
-            if (rect.width == cellwidth) {
+            clen = utflen;
+            n = 1;
+        } else {
+            /*
+             * Start by extracting a single UTF-8 character from the
+             * string.
+             */
+            clen = 1;
+            while (clen < utflen &&
+                   (unsigned char)utfptr[clen] >= 0x80 &&
+                   (unsigned char)utfptr[clen] < 0xC0)
+                clen++;
+            n = 1;
+
+            if (is_rtl(string[0]) ||
+                pangofont_char_width(layout, pfont, string[n-1],
+                                     utfptr, clen) != desired) {
+                /*
+                 * If this character is a right-to-left one, or has an
+                 * unusual width, then we must display it on its own.
+                 */
+            } else {
                 /*
-                 * Try extracting more characters, for as long as they
-                 * stay well-behaved.
+                 * 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;
@@ -1454,9 +1693,10 @@ static void pangofont_draw_text(unifont_drawctx *ctx, unifont *font,
                            (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) {
+                    if (is_rtl(string[n-1]) ||
+                        pangofont_char_width(layout, pfont,
+                                             string[n-1], utfptr + oldclen,
+                                             clen - oldclen) != desired) {
                         clen = oldclen;
                         n--;
                         break;
@@ -1487,6 +1727,37 @@ static void pangofont_draw_text(unifont_drawctx *ctx, unifont *font,
     g_object_unref(layout);
 }
 
+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)
+{
+    pangofont_draw_internal(ctx, font, x, y, string, len, wide, bold,
+                            cellwidth, FALSE);
+}
+
+static void pangofont_draw_combining(unifont_drawctx *ctx, unifont *font,
+                                     int x, int y, const wchar_t *string,
+                                     int len, int wide, int bold,
+                                     int cellwidth)
+{
+    wchar_t *tmpstring = NULL;
+    if (mk_wcwidth(string[0]) == 0) {
+        /*
+         * If we've been told to draw a sequence of _only_ combining
+         * characters, prefix a space so that they have something to
+         * combine with.
+         */
+        tmpstring = snewn(len+1, wchar_t);
+        memcpy(tmpstring+1, string, len * sizeof(wchar_t));
+        tmpstring[0] = L' ';
+        string = tmpstring;
+        len++;
+    }
+    pangofont_draw_internal(ctx, font, x, y, string, len, wide, bold,
+                            cellwidth, TRUE);
+    sfree(tmpstring);
+}
+
 /*
  * Dummy size value to be used when converting a
  * PangoFontDescription of a scalable font to a string for
@@ -1734,6 +2005,32 @@ static char *pangofont_scale_fontname(GtkWidget *widget, const char *name,
     return retname;
 }
 
+static char *pangofont_size_increment(unifont *font, int increment)
+{
+    struct pangofont *pfont = (struct pangofont *)font;
+    PangoFontDescription *desc;
+    int size;
+    char *newname, *retname;
+
+    desc = pango_font_description_copy_static(pfont->desc);
+
+    size = pango_font_description_get_size(desc);
+    size += PANGO_SCALE * increment;
+
+    if (size <= 0) {
+        retname = NULL;
+    } else {
+        pango_font_description_set_size(desc, size);
+        newname = pango_font_description_to_string(desc);
+        retname = dupcat(pfont->u.vt->prefix, ":",
+                         newname, (const char *)NULL);
+        g_free(newname);
+    }
+
+    pango_font_description_free(desc);
+    return retname;
+}
+
 #endif /* GTK_CHECK_VERSION(2,0,0) */
 
 /* ----------------------------------------------------------------------
@@ -1832,6 +2129,19 @@ void unifont_draw_text(unifont_drawctx *ctx, unifont *font,
     font->vt->draw_text(ctx, font, x, y, string, len, wide, bold, cellwidth);
 }
 
+void unifont_draw_combining(unifont_drawctx *ctx, unifont *font,
+                            int x, int y, const wchar_t *string, int len,
+                            int wide, int bold, int cellwidth)
+{
+    font->vt->draw_combining(ctx, font, x, y, string, len, wide, bold,
+                             cellwidth);
+}
+
+char *unifont_size_increment(unifont *font, int increment)
+{
+    return font->vt->size_increment(font, increment);
+}
+
 /* ----------------------------------------------------------------------
  * Multiple-font wrapper. This is a type of unifont which encapsulates
  * up to two other unifonts, permitting missing glyphs in the main
@@ -1848,7 +2158,12 @@ void unifont_draw_text(unifont_drawctx *ctx, unifont *font,
 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_draw_combining(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);
+static char *multifont_size_increment(unifont *font, int increment);
 
 struct multifont {
     struct unifont u;
@@ -1862,9 +2177,11 @@ static const struct unifont_vtable multifont_vtable = {
     multifont_destroy,
     NULL,
     multifont_draw_text,
+    multifont_draw_combining,
     NULL,
     NULL,
     NULL,
+    multifont_size_increment,
     "client",
 };
 
@@ -1922,9 +2239,15 @@ static void multifont_destroy(unifont *font)
     sfree(font);
 }
 
-static void multifont_draw_text(unifont_drawctx *ctx, unifont *font, int x,
+typedef void (*unifont_draw_func_t)(unifont_drawctx *ctx, unifont *font,
+                                    int x, int y, const wchar_t *string,
+                                    int len, int wide, int bold,
+                                    int cellwidth);
+
+static void multifont_draw_main(unifont_drawctx *ctx, unifont *font, int x,
                                 int y, const wchar_t *string, int len,
-                                int wide, int bold, int cellwidth)
+                                int wide, int bold, int cellwidth,
+                                int cellinc, unifont_draw_func_t draw)
 {
     struct multifont *mfont = (struct multifont *)font;
     unifont *f;
@@ -1946,13 +2269,36 @@ static void multifont_draw_text(unifont_drawctx *ctx, unifont *font, int x,
          */
         f = ok ? mfont->main : mfont->fallback;
         if (f)
-            unifont_draw_text(ctx, f, x, y, string, i, wide, bold, cellwidth);
+            draw(ctx, f, x, y, string, i, wide, bold, cellwidth);
         string += i;
         len -= i;
-        x += i * cellwidth;
+        x += i * cellinc;
     }
 }
 
+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)
+{
+    multifont_draw_main(ctx, font, x, y, string, len, wide, bold,
+                        cellwidth, cellwidth, unifont_draw_text);
+}
+
+static void multifont_draw_combining(unifont_drawctx *ctx, unifont *font,
+                                     int x, int y, const wchar_t *string,
+                                     int len, int wide, int bold,
+                                     int cellwidth)
+{
+    multifont_draw_main(ctx, font, x, y, string, len, wide, bold,
+                        cellwidth, 0, unifont_draw_combining);
+}
+
+static char *multifont_size_increment(unifont *font, int increment)
+{
+    struct multifont *mfont = (struct multifont *)font;
+    return unifont_size_increment(mfont->main, increment);
+}
+
 #if GTK_CHECK_VERSION(2,0,0)
 
 /* ----------------------------------------------------------------------
@@ -2091,6 +2437,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;
@@ -2098,6 +2446,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)
@@ -2108,6 +2457,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;
 
@@ -2158,6 +2509,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,
@@ -2203,7 +2556,7 @@ static void unifontsel_setup_stylelist(unifontsel_internal *fs,
                gtk_list_store_append(fs->style_model, &iter);
                gtk_list_store_set(fs->style_model, &iter,
                                   0, currstyle, 1, minpos, 2, maxpos+1,
-                                  3, TRUE, -1);
+                                  3, TRUE, 4, PANGO_WEIGHT_NORMAL, -1);
                listindex++;
            }
            if (info) {
@@ -2212,7 +2565,7 @@ static void unifontsel_setup_stylelist(unifontsel_internal *fs,
                    gtk_list_store_append(fs->style_model, &iter);
                    gtk_list_store_set(fs->style_model, &iter,
                                       0, info->charset, 1, -1, 2, -1,
-                                      3, FALSE, -1);
+                                      3, FALSE, 4, PANGO_WEIGHT_BOLD, -1);
                    listindex++;
                }
                currcs = info->charset;
@@ -2303,29 +2656,29 @@ static void unifontsel_draw_preview_text_inner(unifont_drawctx *dctx,
     } else
        font = NULL;
 
-    if (font) {
 #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 (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);
+    }
 #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);
-        }
+    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
@@ -2428,6 +2781,7 @@ static void unifontsel_select_font(unifontsel_internal *fs,
 {
     int index;
     int minval, maxval;
+    gboolean success;
     GtkTreePath *treepath;
     GtkTreeIter iter;
 
@@ -2468,7 +2822,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);
 
     /*
@@ -2842,6 +3198,20 @@ 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)
 {
@@ -2863,6 +3233,7 @@ static gint unifontsel_expose_area(GtkWidget *widget, GdkEventExpose *event,
 
     return TRUE;
 }
+#endif
 
 static gint unifontsel_configure_area(GtkWidget *widget,
                                      GdkEventConfigure *event, gpointer data)
@@ -2911,25 +3282,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;
-        g_object_ref_sink(G_OBJECT(label));
-#if GTK_CHECK_VERSION(2,10,0)
-       g_object_unref(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;
     }
 
     /*
@@ -2940,35 +3308,53 @@ 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);
-#if GTK_CHECK_VERSION(2,4,0)
+#endif
+    gtk_widget_show(table);
+
+#if GTK_CHECK_VERSION(3,0,0)
+    /* GtkAlignment has become deprecated and we use the "margin"
+     * property */
+    w = table;
+    g_object_set(G_OBJECT(w), "margin", 8, (const char *)NULL);
+#elif 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);
     gtk_alignment_set_padding(GTK_ALIGNMENT(w), 8, 8, 8, 8);
     gtk_container_add(GTK_CONTAINER(w), table);
     gtk_widget_show(w);
 #else
+    /* In GTK < 2.4, even that isn't available */
     w = table;
 #endif
+
     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);
+    align_label_left(GTK_LABEL(label));
+#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
@@ -2998,30 +3384,42 @@ 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);
+    align_label_left(GTK_LABEL(label));
+#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
-     * (character set headings for server-side fonts), so we add
-     * an extra column to the list store to hold that information.
+     * The Style list box can contain insensitive elements (character
+     * set headings for server-side fonts), so we add an extra column
+     * to the list store to hold that information. Also, since GTK3 at
+     * least doesn't seem to display insensitive elements differently
+     * by default, we add a further column to change their style.
      */
-    model = gtk_list_store_new(4, G_TYPE_STRING, G_TYPE_INT, G_TYPE_INT,
-                              G_TYPE_BOOLEAN);
+    model = gtk_list_store_new(5, G_TYPE_STRING, G_TYPE_INT, G_TYPE_INT,
+                              G_TYPE_BOOLEAN, G_TYPE_INT);
     w = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
     gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(w), FALSE);
     gtk_label_set_mnemonic_widget(GTK_LABEL(label), w);
     gtk_widget_show(w);
     column = gtk_tree_view_column_new_with_attributes
        ("Style", gtk_cell_renderer_text_new(),
-        "text", 0, "sensitive", 3, (char *)NULL);
+        "text", 0, "sensitive", 3, "weight", 4, (char *)NULL);
     gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
     gtk_tree_view_append_column(GTK_TREE_VIEW(w), column);
     g_signal_connect(G_OBJECT(gtk_tree_view_get_selection(GTK_TREE_VIEW(w))),
@@ -3035,15 +3433,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);
+    align_label_left(GTK_LABEL(label));
+#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
@@ -3054,7 +3462,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);
 
@@ -3077,8 +3490,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;
 
@@ -3094,12 +3512,19 @@ unifontsel *unifontsel_new(const char *wintitle)
     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);
+#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);
@@ -3108,7 +3533,12 @@ unifontsel *unifontsel_new(const char *wintitle)
     w = gtk_frame_new(NULL);
     gtk_container_add(GTK_CONTAINER(w), ww);
     gtk_widget_show(w);
-#if GTK_CHECK_VERSION(2,4,0)
+
+#if GTK_CHECK_VERSION(3,0,0)
+    /* GtkAlignment has become deprecated and we use the "margin"
+     * property */
+    g_object_set(G_OBJECT(w), "margin", 8, (const char *)NULL);
+#elif GTK_CHECK_VERSION(2,4,0)
     ww = w;
     /* GtkAlignment seems to be the simplest way to put padding round things */
     w = gtk_alignment_new(0, 0, 1, 1);
@@ -3116,12 +3546,18 @@ unifontsel *unifontsel_new(const char *wintitle)
     gtk_container_add(GTK_CONTAINER(w), ww);
     gtk_widget_show(w);
 #endif
+
     ww = w;
     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
 
     /*
      * We only provide the checkboxes for client- and server-side
@@ -3138,7 +3574,12 @@ unifontsel *unifontsel_new(const char *wintitle)
                      G_CALLBACK(unifontsel_button_toggled), fs);
     gtk_widget_show(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");
     g_object_set_data(G_OBJECT(w), "user-data",
                       GINT_TO_POINTER(FONTFLAG_SERVERSIDE));
@@ -3146,7 +3587,12 @@ unifontsel *unifontsel_new(const char *wintitle)
                      G_CALLBACK(unifontsel_button_toggled), fs);
     gtk_widget_show(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");
     g_object_set_data(G_OBJECT(w), "user-data",
                       GINT_TO_POINTER(FONTFLAG_SERVERALIAS));
@@ -3154,8 +3600,13 @@ unifontsel *unifontsel_new(const char *wintitle)
                      G_CALLBACK(unifontsel_button_toggled), fs);
     gtk_widget_show(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");
     g_object_set_data(G_OBJECT(w), "user-data",
                       GINT_TO_POINTER(FONTFLAG_NONMONOSPACED));
@@ -3163,7 +3614,12 @@ unifontsel *unifontsel_new(const char *wintitle)
                      G_CALLBACK(unifontsel_button_toggled), fs);
     gtk_widget_show(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(fs->n_filter_buttons <= lenof(fs->filter_buttons));
     fs->filter_flags = FONTFLAG_CLIENTSIDE | FONTFLAG_SERVERSIDE |
@@ -3221,7 +3677,7 @@ void unifontsel_set_name(unifontsel *fontsel, const char *fontname)
      * Provide a default if given an empty or null font name.
      */
     if (!fontname || !*fontname)
-       fontname = "server:fixed";
+       fontname = DEFAULT_GTK_FONT;
 
     /*
      * Call the canonify_fontname function.