X-Git-Url: https://asedeno.scripts.mit.edu/gitweb/?a=blobdiff_plain;f=unix%2Fgtkfont.c;h=d9980880fcab421a47bda70911df15516612ee23;hb=12a080874fd9e5a4ae0a6dc39fb54b152927075d;hp=56ae031525e2ed02133efd41e97580b8a3726537;hpb=3d69dd2071839c73d1eb76aead16639b9f5bce1e;p=PuTTY.git diff --git a/unix/gtkfont.c b/unix/gtkfont.c index 56ae0315..d9980880 100644 --- a/unix/gtkfont.c +++ b/unix/gtkfont.c @@ -12,33 +12,33 @@ #include #include #include + #include +#if !GTK_CHECK_VERSION(3,0,0) #include -#include -#include -#include -#include +#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 +#include +#include +#include +#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) @@ -80,14 +80,18 @@ 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 (*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'. @@ -95,14 +99,21 @@ 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 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); @@ -114,21 +125,63 @@ 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 { + 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 @@ -161,59 +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] == '-') { - 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++; - } + struct xlfd_decomposed *xlfd = xlfd_decompose(name); + if (!xlfd) + return NULL; - if (nstr < lenof(strings)) - return NULL; /* XLFD was malformed */ + if (bold) + xlfd->weight_name = "bold"; - if (bold) - strings[2] = "bold"; + 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 = "*"; - 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])); - } - - 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; } @@ -230,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_ @@ -286,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, @@ -298,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; @@ -371,9 +501,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; @@ -381,35 +516,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) @@ -421,7 +579,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 @@ -433,65 +592,265 @@ static int x11font_has_glyph(unifont *font, wchar_t glyph) if (sblen == 0 || !sbstring[0]) return FALSE; /* not even in the charset */ - return x11_font_has_glyph(xfont->fonts[0], 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 int x11font_width_16(unifont_drawctx *ctx, x11font_individual *xfi, + const void *vstring, int start, int length) +{ + const XChar2b *string = (const XChar2b *)vstring; + return XTextWidth16(xfi->xfs, string+start, length); +} + +static int x11font_width_8(unifont_drawctx *ctx, x11font_individual *xfi, + const void *vstring, int start, int length) +{ + const char *string = (const char *)vstring; + return XTextWidth(xfi->xfs, string+start, length); +} + +#ifdef DRAW_TEXT_GDK +static void x11font_gdk_setup(unifont_drawctx *ctx, x11font_individual *xfi) +{ + Display *disp = GDK_DISPLAY_XDISPLAY(gdk_display_get_default()); + XSetFont(disp, GDK_GC_XGC(ctx->u.gdk.gc), xfi->xfs->fid); +} + +static void x11font_gdk_draw_16(unifont_drawctx *ctx, + x11font_individual *xfi, int x, int y, + const void *vstring, int start, int length) +{ + Display *disp = GDK_DISPLAY_XDISPLAY(gdk_display_get_default()); + const XChar2b *string = (const XChar2b *)vstring; + XDrawString16(disp, GDK_DRAWABLE_XID(ctx->u.gdk.target), + GDK_GC_XGC(ctx->u.gdk.gc), x, y, string+start, length); +} + +static void x11font_gdk_draw_8(unifont_drawctx *ctx, + x11font_individual *xfi, int x, int y, + const void *vstring, int start, int length) +{ + Display *disp = GDK_DISPLAY_XDISPLAY(gdk_display_get_default()); + const char *string = (const char *)vstring; + XDrawString(disp, GDK_DRAWABLE_XID(ctx->u.gdk.target), + GDK_GC_XGC(ctx->u.gdk.gc), x, y, string+start, length); +} #endif -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) +#ifdef DRAW_TEXT_CAIRO +static void x11font_cairo_setup(unifont_drawctx *ctx, x11font_individual *xfi) { - Display *disp = GDK_DISPLAY(); - int step, nsteps, centre; + 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; + } - 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; + 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()); + const XCharStruct *xcs = x11_char_struct(xfi->xfs, glyphindex >> 8, + glyphindex & 0xFF); + + bitmap = snewn(xfi->allsize, unsigned char); + memset(bitmap, 0, xfi->allsize); + + image = XGetImage(disp, xfi->pixmap, 0, 0, + xfi->pixwidth, xfi->pixheight, AllPlanes, XYPixmap); + for (y = xfi->pixoriginy - xcs->ascent; + y < xfi->pixoriginy + xcs->descent; y++) { + for (x = xfi->pixoriginx + xcs->lbearing; + x < xfi->pixoriginx + xcs->rbearing; x++) { + unsigned long pixel = XGetPixel(image, x, y); + if (pixel) { + int byteindex = y * xfi->rowsize + x/8; + int bitindex = (x & 7) ^ xfi->indexflip; + bitmap[byteindex] |= 1U << bitindex; + } + } + } + XDestroyImage(image); + + if (xfi->nglyphs <= glyphindex) { + /* Round up to the next multiple of 256 on the general + * principle that Unicode characters come in contiguous blocks + * often used together */ + int old_nglyphs = xfi->nglyphs; + xfi->nglyphs = (glyphindex + 0x100) & ~0xFF; + xfi->glyphcache = sresize(xfi->glyphcache, xfi->nglyphs, + struct cairo_cached_glyph); + + while (old_nglyphs < xfi->nglyphs) { + xfi->glyphcache[old_nglyphs].surface = NULL; + xfi->glyphcache[old_nglyphs].bitmap = NULL; + old_nglyphs++; + } + } + xfi->glyphcache[glyphindex].bitmap = bitmap; + xfi->glyphcache[glyphindex].surface = cairo_image_surface_create_for_data + (bitmap, CAIRO_FORMAT_A1, xfi->pixwidth, xfi->pixheight, xfi->rowsize); +} - 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) { /* @@ -510,32 +869,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; @@ -549,21 +907,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 @@ -578,9 +934,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 { /* @@ -590,23 +947,39 @@ 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); } } +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; @@ -617,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 @@ -692,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. @@ -760,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, @@ -781,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; @@ -824,6 +1188,96 @@ 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)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) /* ---------------------------------------------------------------------- @@ -835,9 +1289,13 @@ 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 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); @@ -852,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; @@ -868,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 = { @@ -876,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", }; @@ -976,6 +1444,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; @@ -984,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); @@ -1037,19 +1514,67 @@ 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 */ 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_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; @@ -1057,6 +1582,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; @@ -1087,6 +1625,7 @@ static void pangofont_draw_text(GdkDrawable *target, GdkGC *gc, 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 @@ -1112,34 +1651,39 @@ static void pangofont_draw_text(GdkDrawable *target, GdkGC *gc, 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) { /* - * Try extracting more characters, for as long as they - * stay well-behaved. + * If this character is a right-to-left one, or has an + * unusual width, then we must display it on its own. + */ + } 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; @@ -1149,9 +1693,10 @@ static void pangofont_draw_text(GdkDrawable *target, GdkGC *gc, 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; @@ -1162,11 +1707,14 @@ static void pangofont_draw_text(GdkDrawable *target, GdkGC *gc, unifont *font, 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; @@ -1179,6 +1727,37 @@ static void pangofont_draw_text(GdkDrawable *target, GdkGC *gc, 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 @@ -1426,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) */ /* ---------------------------------------------------------------------- @@ -1445,7 +2050,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 }; /* @@ -1515,12 +2122,24 @@ 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); +} + +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); } /* ---------------------------------------------------------------------- @@ -1536,10 +2155,15 @@ 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_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; @@ -1553,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", }; @@ -1597,6 +2223,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; @@ -1612,11 +2239,18 @@ 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) +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 cellinc, unifont_draw_func_t draw) { struct multifont *mfont = (struct multifont *)font; + unifont *f; int ok, i; while (len > 0) { @@ -1633,14 +2267,38 @@ 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) + 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) /* ---------------------------------------------------------------------- @@ -1656,8 +2314,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; @@ -1776,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; @@ -1783,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) @@ -1793,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; @@ -1843,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, @@ -1888,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) { @@ -1897,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; @@ -1963,16 +2631,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; @@ -1987,74 +2656,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; @@ -2095,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); /* @@ -2174,7 +2903,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); @@ -2187,8 +2917,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; @@ -2468,25 +3198,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; @@ -2503,14 +3255,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; } @@ -2528,27 +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; -#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; } /* @@ -2559,34 +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(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); + 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 @@ -2616,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))), @@ -2653,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 @@ -2672,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); @@ -2695,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; @@ -2704,27 +3504,41 @@ 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; 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); @@ -2732,48 +3546,82 @@ 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 - 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); @@ -2804,8 +3652,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) @@ -2827,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.