fontutils.c revision 94644356
1/* $XTermId: fontutils.c,v 1.448 2015/03/02 13:19:36 tom Exp $ */
2
3/*
4 * Copyright 1998-2014,2015 by Thomas E. Dickey
5 *
6 *                         All Rights Reserved
7 *
8 * Permission is hereby granted, free of charge, to any person obtaining a
9 * copy of this software and associated documentation files (the
10 * "Software"), to deal in the Software without restriction, including
11 * without limitation the rights to use, copy, modify, merge, publish,
12 * distribute, sublicense, and/or sell copies of the Software, and to
13 * permit persons to whom the Software is furnished to do so, subject to
14 * the following conditions:
15 *
16 * The above copyright notice and this permission notice shall be included
17 * in all copies or substantial portions of the Software.
18 *
19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
20 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
21 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
22 * IN NO EVENT SHALL THE ABOVE LISTED COPYRIGHT HOLDER(S) BE LIABLE FOR ANY
23 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
24 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
25 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
26 *
27 * Except as contained in this notice, the name(s) of the above copyright
28 * holders shall not be used in advertising or otherwise to promote the
29 * sale, use or other dealings in this Software without prior written
30 * authorization.
31 */
32
33/*
34 * A portion of this module (for FontNameProperties) was adapted from EMU 1.3;
35 * it constructs font names with specific properties changed, e.g., for bold
36 * and double-size characters.
37 */
38
39#define RES_OFFSET(field)	XtOffsetOf(SubResourceRec, field)
40
41#include <fontutils.h>
42#include <X11/Xmu/Drawing.h>
43#include <X11/Xmu/CharSet.h>
44
45#include <main.h>
46#include <data.h>
47#include <menu.h>
48#include <xstrings.h>
49#include <xterm.h>
50
51#include <stdio.h>
52#include <ctype.h>
53
54#define SetFontWidth(screen,dst,src)  (dst)->f_width = (src)
55#define SetFontHeight(screen,dst,src) (dst)->f_height = dimRound((screen)->scale_height * (float) (src))
56
57/* from X11/Xlibint.h - not all vendors install this file */
58#define CI_NONEXISTCHAR(cs) (((cs)->width == 0) && \
59			     (((cs)->rbearing|(cs)->lbearing| \
60			       (cs)->ascent|(cs)->descent) == 0))
61
62#define CI_GET_CHAR_INFO_1D(fs,col,cs) \
63{ \
64    cs = 0; \
65    if (col >= fs->min_char_or_byte2 && col <= fs->max_char_or_byte2) { \
66	if (fs->per_char == NULL) { \
67	    cs = &fs->min_bounds; \
68	} else { \
69	    cs = &fs->per_char[(col - fs->min_char_or_byte2)]; \
70	} \
71	if (CI_NONEXISTCHAR(cs)) cs = 0; \
72    } \
73}
74
75#define CI_GET_CHAR_INFO_2D(fs,row,col,cs) \
76{ \
77    cs = 0; \
78    if (row >= fs->min_byte1 && row <= fs->max_byte1 && \
79	col >= fs->min_char_or_byte2 && col <= fs->max_char_or_byte2) { \
80	if (fs->per_char == NULL) { \
81	    cs = &fs->min_bounds; \
82	} else { \
83	    cs = &fs->per_char[((row - fs->min_byte1) * \
84				(fs->max_char_or_byte2 - \
85				 fs->min_char_or_byte2 + 1)) + \
86			       (col - fs->min_char_or_byte2)]; \
87	} \
88	if (CI_NONEXISTCHAR(cs)) cs = 0; \
89    } \
90}
91
92#define FREE_FNAME(field) \
93	    if (fonts == 0 || myfonts.field != fonts->field) { \
94		FREE_STRING(myfonts.field); \
95		myfonts.field = 0; \
96	    }
97
98/*
99 * A structure to hold the relevant properties from a font
100 * we need to make a well formed font name for it.
101 */
102typedef struct {
103    /* registry, foundry, family */
104    const char *beginning;
105    /* weight */
106    const char *weight;
107    /* slant */
108    const char *slant;
109    /* wideness */
110    const char *wideness;
111    /* add style */
112    const char *add_style;
113    int pixel_size;
114    const char *point_size;
115    int res_x;
116    int res_y;
117    const char *spacing;
118    int average_width;
119    /* charset registry, charset encoding */
120    char *end;
121} FontNameProperties;
122
123#if OPT_RENDERFONT
124static void fillInFaceSize(XtermWidget, int);
125#endif
126
127#if OPT_SHIFT_FONTS
128static int lookupOneFontSize(XtermWidget, int);
129#endif
130
131#if OPT_REPORT_FONTS || OPT_WIDE_CHARS
132static unsigned
133countGlyphs(XFontStruct *fp)
134{
135    unsigned count = 0;
136
137    if (fp != 0) {
138	if (fp->min_byte1 == 0 && fp->max_byte1 == 0) {
139	    count = fp->max_char_or_byte2 - fp->min_char_or_byte2 + 1;
140	} else if (fp->min_char_or_byte2 < 256
141		   && fp->max_char_or_byte2 < 256) {
142	    unsigned first = (fp->min_byte1 << 8) + fp->min_char_or_byte2;
143	    unsigned last = (fp->max_byte1 << 8) + fp->max_char_or_byte2;
144	    count = last + 1 - first;
145	}
146    }
147    return count;
148}
149#endif
150
151#if OPT_WIDE_CHARS
152/*
153 * Verify that the wide-bold font is at least a bold font with roughly as many
154 * glyphs as the wide font.  The counts should be the same, but settle for
155 * filtering out the worst of the font mismatches.
156 */
157static Bool
158compatibleWideCounts(XFontStruct *wfs, XFontStruct *wbfs)
159{
160    unsigned count_w = countGlyphs(wfs);
161    unsigned count_wb = countGlyphs(wbfs);
162    if (count_w <= 256 ||
163	count_wb <= 256 ||
164	((count_w / 4) * 3) > count_wb) {
165	TRACE(("...font server lied (count wide %u vs wide-bold %u)\n",
166	       count_w, count_wb));
167	return False;
168    }
169    return True;
170}
171#endif /* OPT_WIDE_CHARS */
172
173#if OPT_BOX_CHARS
174static void
175setupPackedFonts(XtermWidget xw)
176{
177    TScreen *screen = TScreenOf(xw);
178    Bool value = False;
179
180#if OPT_RENDERFONT
181#define MIXED(name) screen->name[fontnum].map.mixed
182    if (xw->work.render_font == True) {
183	int fontnum = screen->menu_font_number;
184
185	screen->allow_packing = (Boolean) (MIXED(renderFontNorm)
186					   || MIXED(renderFontBold)
187					   || MIXED(renderFontItal)
188#if OPT_RENDERWIDE
189					   || MIXED(renderWideNorm)
190					   || MIXED(renderWideBold)
191					   || MIXED(renderWideItal)
192#endif
193	    );
194#undef MIXED
195    }
196#endif /* OPT_RENDERFONT */
197
198    value = screen->allow_packing;
199
200    SetItemSensitivity(fontMenuEntries[fontMenu_font_packedfont].widget, value);
201}
202#endif
203
204/*
205 * Returns the fields from start to stop in a dash- separated string.  This
206 * function will modify the source, putting '\0's in the appropriate place and
207 * moving the beginning forward to after the '\0'
208 *
209 * This will NOT work for the last field (but we won't need it).
210 */
211static char *
212n_fields(char **source, int start, int stop)
213{
214    int i;
215    char *str, *str1;
216
217    /*
218     * find the start-1th dash
219     */
220    for (i = start - 1, str = *source; i; i--, str++) {
221	if ((str = strchr(str, '-')) == 0)
222	    return 0;
223    }
224
225    /*
226     * find the stopth dash
227     */
228    for (i = stop - start + 1, str1 = str; i; i--, str1++) {
229	if ((str1 = strchr(str1, '-')) == 0)
230	    return 0;
231    }
232
233    /*
234     * put a \0 at the end of the fields
235     */
236    *(str1 - 1) = '\0';
237
238    /*
239     * move source forward
240     */
241    *source = str1;
242
243    return str;
244}
245
246static Boolean
247check_fontname(const char *name)
248{
249    Boolean result = True;
250
251    if (IsEmpty(name)) {
252	TRACE(("fontname missing\n"));
253	result = False;
254    }
255    return result;
256}
257
258/*
259 * Gets the font properties from a given font structure.  We use the FONT name
260 * to find them out, since that seems easier.
261 *
262 * Returns a pointer to a static FontNameProperties structure
263 * or NULL on error.
264 */
265static FontNameProperties *
266get_font_name_props(Display *dpy, XFontStruct *fs, char **result)
267{
268    static FontNameProperties props;
269    static char *last_name;
270
271    XFontProp *fp;
272    int i;
273    Atom fontatom = XInternAtom(dpy, "FONT", False);
274    char *name = 0;
275    char *str;
276
277    /*
278     * first get the full font name
279     */
280    if (fontatom != 0) {
281	for (i = 0, fp = fs->properties; i < fs->n_properties; i++, fp++) {
282	    if (fp->name == fontatom) {
283		name = XGetAtomName(dpy, fp->card32);
284		break;
285	    }
286	}
287    }
288
289    if (name == 0)
290	return 0;
291
292    /*
293     * XGetAtomName allocates memory - don't leak
294     */
295    if (last_name != 0)
296	XFree(last_name);
297    last_name = name;
298
299    if (result != 0) {
300	if (!check_fontname(name))
301	    return 0;
302	if (*result != 0)
303	    free(*result);
304	*result = x_strdup(name);
305    }
306
307    /*
308     * Now split it up into parts and put them in
309     * their places. Since we are using parts of
310     * the original string, we must not free the Atom Name
311     */
312
313    /* registry, foundry, family */
314    if ((props.beginning = n_fields(&name, 1, 3)) == 0)
315	return 0;
316
317    /* weight is the next */
318    if ((props.weight = n_fields(&name, 1, 1)) == 0)
319	return 0;
320
321    /* slant */
322    if ((props.slant = n_fields(&name, 1, 1)) == 0)
323	return 0;
324
325    /* width */
326    if ((props.wideness = n_fields(&name, 1, 1)) == 0)
327	return 0;
328
329    /* add style */
330    if ((props.add_style = n_fields(&name, 1, 1)) == 0)
331	return 0;
332
333    /* pixel size */
334    if ((str = n_fields(&name, 1, 1)) == 0)
335	return 0;
336    if ((props.pixel_size = atoi(str)) == 0)
337	return 0;
338
339    /* point size */
340    if ((props.point_size = n_fields(&name, 1, 1)) == 0)
341	return 0;
342
343    /* res_x */
344    if ((str = n_fields(&name, 1, 1)) == 0)
345	return 0;
346    if ((props.res_x = atoi(str)) == 0)
347	return 0;
348
349    /* res_y */
350    if ((str = n_fields(&name, 1, 1)) == 0)
351	return 0;
352    if ((props.res_y = atoi(str)) == 0)
353	return 0;
354
355    /* spacing */
356    if ((props.spacing = n_fields(&name, 1, 1)) == 0)
357	return 0;
358
359    /* average width */
360    if ((str = n_fields(&name, 1, 1)) == 0)
361	return 0;
362    if ((props.average_width = atoi(str)) == 0)
363	return 0;
364
365    /* the rest: charset registry and charset encoding */
366    props.end = name;
367
368    return &props;
369}
370
371#define ALLOCHUNK(n) ((n | 127) + 1)
372
373static void
374alloca_fontname(char **result, size_t next)
375{
376    size_t last = (*result != 0) ? strlen(*result) : 0;
377    size_t have = (*result != 0) ? ALLOCHUNK(last) : 0;
378    size_t want = last + next + 2;
379
380    if (want >= have) {
381	want = ALLOCHUNK(want);
382	if (last != 0) {
383	    char *save = *result;
384	    *result = TypeRealloc(char, want, *result);
385	    if (*result == 0)
386		free(save);
387	} else {
388	    if ((*result = TypeMallocN(char, want)) != 0)
389		**result = '\0';
390	}
391    }
392}
393
394static void
395append_fontname_str(char **result, const char *value)
396{
397    if (value == 0)
398	value = "*";
399    alloca_fontname(result, strlen(value));
400    if (*result != 0) {
401	if (**result != '\0')
402	    strcat(*result, "-");
403	strcat(*result, value);
404    }
405}
406
407static void
408append_fontname_num(char **result, int value)
409{
410    if (value < 0) {
411	append_fontname_str(result, "*");
412    } else {
413	char temp[100];
414	sprintf(temp, "%d", value);
415	append_fontname_str(result, temp);
416    }
417}
418
419/*
420 * Take the given font props and try to make a well formed font name specifying
421 * the same base font and size and everything, but with different weight/width
422 * according to the parameters.  The return value is allocated, should be freed
423 * by the caller.
424 */
425static char *
426derive_font_name(FontNameProperties *props,
427		 const char *use_weight,
428		 int use_average_width,
429		 const char *use_encoding)
430{
431    char *result = 0;
432
433    append_fontname_str(&result, props->beginning);
434    append_fontname_str(&result, use_weight);
435    append_fontname_str(&result, props->slant);
436    append_fontname_str(&result, 0);
437    append_fontname_str(&result, 0);
438    append_fontname_num(&result, props->pixel_size);
439    append_fontname_str(&result, props->point_size);
440    append_fontname_num(&result, props->res_x);
441    append_fontname_num(&result, props->res_y);
442    append_fontname_str(&result, props->spacing);
443    append_fontname_num(&result, use_average_width);
444    append_fontname_str(&result, use_encoding);
445
446    return result;
447}
448
449static char *
450bold_font_name(FontNameProperties *props, int use_average_width)
451{
452    return derive_font_name(props, "bold", use_average_width, props->end);
453}
454
455#if OPT_WIDE_ATTRS
456static char *
457italic_font_name(FontNameProperties *props, int use_average_width)
458{
459    FontNameProperties myprops = *props;
460    myprops.slant = "o";
461    return derive_font_name(&myprops, props->weight, use_average_width, props->end);
462}
463#endif
464
465#if OPT_WIDE_CHARS
466#define derive_wide_font(props, weight) \
467	derive_font_name(props, weight, props->average_width * 2, "ISO10646-1")
468
469static char *
470wide_font_name(FontNameProperties *props)
471{
472    return derive_wide_font(props, "medium");
473}
474
475static char *
476widebold_font_name(FontNameProperties *props)
477{
478    return derive_wide_font(props, "bold");
479}
480#endif /* OPT_WIDE_CHARS */
481
482#if OPT_DEC_CHRSET
483/*
484 * Take the given font props and try to make a well formed font name specifying
485 * the same base font but changed depending on the given attributes and chrset.
486 *
487 * For double width fonts, we just double the X-resolution, for double height
488 * fonts we double the pixel-size and Y-resolution
489 */
490char *
491xtermSpecialFont(TScreen *screen, unsigned attr_flags, unsigned draw_flags, unsigned chrset)
492{
493#if OPT_TRACE
494    static char old_spacing[80];
495    static FontNameProperties old_props;
496#endif
497    FontNameProperties *props;
498    char *result = 0;
499    const char *weight;
500    int pixel_size;
501    int res_x;
502    int res_y;
503
504    props = get_font_name_props(screen->display, screen->fnts[fNorm].fs, 0);
505    if (props == 0)
506	return result;
507
508    pixel_size = props->pixel_size;
509    res_x = props->res_x;
510    res_y = props->res_y;
511    if (attr_flags & BOLD)
512	weight = "bold";
513    else
514	weight = props->weight;
515
516    if (CSET_DOUBLE(chrset))
517	res_x *= 2;
518
519    if (chrset == CSET_DHL_TOP
520	|| chrset == CSET_DHL_BOT) {
521	res_y *= 2;
522	pixel_size *= 2;
523    }
524#if OPT_TRACE
525    if (old_props.res_x != res_x
526	|| old_props.res_x != res_y
527	|| old_props.pixel_size != pixel_size
528	|| strcmp(old_props.spacing, props->spacing)) {
529	TRACE(("xtermSpecialFont(atts = %#x, draw = %#x, chrset = %#x)\n",
530	       attr_flags, draw_flags, chrset));
531	TRACE(("res_x      = %d\n", res_x));
532	TRACE(("res_y      = %d\n", res_y));
533	TRACE(("point_size = %s\n", props->point_size));
534	TRACE(("pixel_size = %d\n", pixel_size));
535	TRACE(("spacing    = %s\n", props->spacing));
536	old_props.res_x = res_x;
537	old_props.res_x = res_y;
538	old_props.pixel_size = pixel_size;
539	old_props.spacing = old_spacing;
540	sprintf(old_spacing, "%.*s", (int) sizeof(old_spacing) - 2, props->spacing);
541    }
542#endif
543
544    append_fontname_str(&result, props->beginning);
545    append_fontname_str(&result, weight);
546    append_fontname_str(&result, props->slant);
547    append_fontname_str(&result, props->wideness);
548    append_fontname_str(&result, props->add_style);
549    append_fontname_num(&result, pixel_size);
550    append_fontname_str(&result, props->point_size);
551    append_fontname_num(&result, (draw_flags & NORESOLUTION) ? -1 : res_x);
552    append_fontname_num(&result, (draw_flags & NORESOLUTION) ? -1 : res_y);
553    append_fontname_str(&result, props->spacing);
554    append_fontname_str(&result, 0);
555    append_fontname_str(&result, props->end);
556
557    return result;
558}
559#endif /* OPT_DEC_CHRSET */
560
561/*
562 * Case-independent comparison for font-names, including wildcards.
563 * XLFD allows '?' as a wildcard, but we do not handle that (no one seems
564 * to use it).
565 */
566static Bool
567same_font_name(const char *pattern, const char *match)
568{
569    Bool result = False;
570
571    if (pattern && match) {
572	while (*pattern && *match) {
573	    if (*pattern == *match) {
574		pattern++;
575		match++;
576	    } else if (*pattern == '*' || *match == '*') {
577		if (same_font_name(pattern + 1, match)) {
578		    return True;
579		} else if (same_font_name(pattern, match + 1)) {
580		    return True;
581		} else {
582		    return False;
583		}
584	    } else {
585		int p = x_toupper(*pattern++);
586		int m = x_toupper(*match++);
587		if (p != m)
588		    return False;
589	    }
590	}
591	result = (*pattern == *match);	/* both should be NUL */
592    }
593    return result;
594}
595
596/*
597 * Double-check the fontname that we asked for versus what the font server
598 * actually gave us.  The larger fixed fonts do not always have a matching bold
599 * font, and the font server may try to scale another font or otherwise
600 * substitute a mismatched font.
601 *
602 * If we cannot get what we requested, we will fallback to the original
603 * behavior, which simulates bold by overstriking each character at one pixel
604 * offset.
605 */
606static int
607got_bold_font(Display *dpy, XFontStruct *fs, String requested)
608{
609    char *actual = 0;
610    int got;
611
612    if (get_font_name_props(dpy, fs, &actual) == 0)
613	got = 0;
614    else
615	got = same_font_name(requested, actual);
616    free(actual);
617    return got;
618}
619
620/*
621 * Check normal/bold (or wide/wide-bold) font pairs to see if we will be able
622 * to check for missing glyphs in a comparable manner.
623 */
624static int
625comparable_metrics(XFontStruct *normal, XFontStruct *bold)
626{
627#define DATA "comparable_metrics: "
628    int result = 0;
629
630    if (normal->all_chars_exist) {
631	if (bold->all_chars_exist) {
632	    result = 1;
633	} else {
634	    TRACE((DATA "all chars exist in normal font, but not in bold\n"));
635	}
636    } else if (normal->per_char != 0) {
637	if (bold->per_char != 0) {
638	    result = 1;
639	} else {
640	    TRACE((DATA "normal font has per-char metrics, but not bold\n"));
641	}
642    } else {
643	TRACE((DATA "normal font is not very good!\n"));
644	result = 1;		/* give in (we're not going in reverse) */
645    }
646    return result;
647#undef DATA
648}
649
650/*
651 * If the font server tries to adjust another font, it may not adjust it
652 * properly.  Check that the bounding boxes are compatible.  Otherwise we'll
653 * leave trash on the display when we mix normal and bold fonts.
654 */
655static int
656same_font_size(XtermWidget xw, XFontStruct *nfs, XFontStruct *bfs)
657{
658    TScreen *screen = TScreenOf(xw);
659    TRACE(("same_font_size height %d/%d, min %d/%d max %d/%d\n",
660	   nfs->ascent + nfs->descent,
661	   bfs->ascent + bfs->descent,
662	   nfs->min_bounds.width, bfs->min_bounds.width,
663	   nfs->max_bounds.width, bfs->max_bounds.width));
664    return screen->free_bold_box
665	|| ((nfs->ascent + nfs->descent) == (bfs->ascent + bfs->descent)
666	    && (nfs->min_bounds.width == bfs->min_bounds.width
667		|| nfs->min_bounds.width == bfs->min_bounds.width + 1)
668	    && (nfs->max_bounds.width == bfs->max_bounds.width
669		|| nfs->max_bounds.width == bfs->max_bounds.width + 1));
670}
671
672/*
673 * Check if the font looks like it has fixed width
674 */
675static int
676is_fixed_font(XFontStruct *fs)
677{
678    if (fs)
679	return (fs->min_bounds.width == fs->max_bounds.width);
680    return 1;
681}
682
683/*
684 * Check if the font looks like a double width font (i.e. contains
685 * characters of width X and 2X
686 */
687#if OPT_WIDE_CHARS
688static int
689is_double_width_font(XFontStruct *fs)
690{
691    return ((2 * fs->min_bounds.width) == fs->max_bounds.width);
692}
693#else
694#define is_double_width_font(fs) 0
695#endif
696
697#if OPT_WIDE_CHARS && OPT_RENDERFONT && defined(HAVE_TYPE_FCCHAR32)
698#define HALF_WIDTH_TEST_STRING "1234567890"
699
700/* '1234567890' in Chinese characters in UTF-8 */
701#define FULL_WIDTH_TEST_STRING "\xe4\xb8\x80\xe4\xba\x8c\xe4\xb8\x89" \
702                               "\xe5\x9b\x9b\xe4\xba\x94" \
703			       "\xef\xa7\x91\xe4\xb8\x83\xe5\x85\xab" \
704			       "\xe4\xb9\x9d\xef\xa6\xb2"
705
706/* '1234567890' in Korean script in UTF-8 */
707#define FULL_WIDTH_TEST_STRING2 "\xec\x9d\xbc\xec\x9d\xb4\xec\x82\xbc" \
708                                "\xec\x82\xac\xec\x98\xa4" \
709			        "\xec\x9c\xa1\xec\xb9\xa0\xed\x8c\x94" \
710			        "\xea\xb5\xac\xec\x98\x81"
711
712#define HALF_WIDTH_CHAR1  0x0031	/* '1' */
713#define HALF_WIDTH_CHAR2  0x0057	/* 'W' */
714#define FULL_WIDTH_CHAR1  0x4E00	/* CJK Ideograph 'number one' */
715#define FULL_WIDTH_CHAR2  0xAC00	/* Korean script syllable 'Ka' */
716
717static Bool
718is_double_width_font_xft(Display *dpy, XftFont *font)
719{
720    XGlyphInfo gi1, gi2;
721    FcChar32 c1 = HALF_WIDTH_CHAR1, c2 = HALF_WIDTH_CHAR2;
722    String fwstr = FULL_WIDTH_TEST_STRING;
723    String hwstr = HALF_WIDTH_TEST_STRING;
724
725    /* Some Korean fonts don't have Chinese characters at all. */
726    if (!XftCharExists(dpy, font, FULL_WIDTH_CHAR1)) {
727	if (!XftCharExists(dpy, font, FULL_WIDTH_CHAR2))
728	    return False;	/* Not a CJK font */
729	else			/* a Korean font without CJK Ideographs */
730	    fwstr = FULL_WIDTH_TEST_STRING2;
731    }
732
733    XftTextExtents32(dpy, font, &c1, 1, &gi1);
734    XftTextExtents32(dpy, font, &c2, 1, &gi2);
735    if (gi1.xOff != gi2.xOff)	/* Not a fixed-width font */
736	return False;
737
738    XftTextExtentsUtf8(dpy,
739		       font,
740		       (_Xconst FcChar8 *) hwstr,
741		       (int) strlen(hwstr),
742		       &gi1);
743    XftTextExtentsUtf8(dpy,
744		       font,
745		       (_Xconst FcChar8 *) fwstr,
746		       (int) strlen(fwstr),
747		       &gi2);
748
749    /*
750     * fontconfig and Xft prior to 2.2(?) set the width of half-width
751     * characters identical to that of full-width character in CJK double-width
752     * (bi-width / monospace) font even though the former is half as wide as
753     * the latter.  This was fixed sometime before the release of fontconfig
754     * 2.2 in early 2003.  See
755     *  http://bugzilla.mozilla.org/show_bug.cgi?id=196312
756     * In the meantime, we have to check both possibilities.
757     */
758    return ((2 * gi1.xOff == gi2.xOff) || (gi1.xOff == gi2.xOff));
759}
760#else
761#define is_double_width_font_xft(dpy, xftfont) 0
762#endif
763
764#define EmptyFont(fs) (fs != 0 \
765		   && ((fs)->ascent + (fs)->descent == 0 \
766		    || (fs)->max_bounds.width == 0))
767
768#define FontSize(fs) (((fs)->ascent + (fs)->descent) \
769		    *  (fs)->max_bounds.width)
770
771const VTFontNames *
772xtermFontName(const char *normal)
773{
774    static VTFontNames data;
775    FREE_STRING(data.f_n);
776    memset(&data, 0, sizeof(data));
777    data.f_n = x_strdup(normal);
778    return &data;
779}
780
781static void
782cache_menu_font_name(TScreen *screen, int fontnum, int which, const char *name)
783{
784    if (name != 0) {
785	String last = screen->menu_font_names[fontnum][which];
786	if (last != 0) {
787	    if (strcmp(last, name)) {
788		FREE_STRING(last);
789		TRACE(("caching menu fontname %d.%d %s\n", fontnum, which, name));
790		screen->menu_font_names[fontnum][which] = x_strdup(name);
791	    }
792	} else {
793	    TRACE(("caching menu fontname %d.%d %s\n", fontnum, which, name));
794	    screen->menu_font_names[fontnum][which] = x_strdup(name);
795	}
796    }
797}
798
799/*
800 * Open the given font and verify that it is non-empty.  Return a null on
801 * failure.
802 */
803Bool
804xtermOpenFont(XtermWidget xw,
805	      const char *name,
806	      XTermFonts * result,
807	      fontWarningTypes warn,
808	      Bool force)
809{
810    Bool code = False;
811    TScreen *screen = TScreenOf(xw);
812
813    if (!IsEmpty(name)) {
814	if ((result->fs = XLoadQueryFont(screen->display, name)) != 0) {
815	    code = True;
816	    if (EmptyFont(result->fs)) {
817		(void) xtermCloseFont(xw, result);
818		code = False;
819	    } else {
820		result->fn = x_strdup(name);
821	    }
822	} else if (XmuCompareISOLatin1(name, DEFFONT) != 0) {
823	    if (warn <= xw->misc.fontWarnings
824#if OPT_RENDERFONT
825		&& !UsingRenderFont(xw)
826#endif
827		) {
828		TRACE(("OOPS: cannot load font %s\n", name));
829		xtermWarning("cannot load font '%s'\n", name);
830#if OPT_RENDERFONT
831		/*
832		 * Do a sanity check in case someone's mixed up xterm with
833		 * one of those programs that read their resource data from
834		 * xterm's namespace.
835		 */
836		if (strchr(name, ':') != 0 || strchr(name, '=') != 0) {
837		    xtermWarning("Use the \"-fa\" option for the Xft fonts\n");
838		}
839#endif
840	    } else {
841		TRACE(("xtermOpenFont: cannot load font '%s'\n", name));
842	    }
843	    if (force) {
844		code = xtermOpenFont(xw, DEFFONT, result, fwAlways, True);
845	    }
846	}
847    }
848    return code;
849}
850
851/*
852 * Close the font and free the font info.
853 */
854XTermFonts *
855xtermCloseFont(XtermWidget xw, XTermFonts * fnt)
856{
857    if (fnt != 0 && fnt->fs != 0) {
858	TScreen *screen = TScreenOf(xw);
859
860	clrCgsFonts(xw, WhichVWin(screen), fnt);
861	XFreeFont(screen->display, fnt->fs);
862	xtermFreeFontInfo(fnt);
863    }
864    return 0;
865}
866
867/*
868 * Close the listed fonts, noting that some may use copies of the pointer.
869 */
870void
871xtermCloseFonts(XtermWidget xw, XTermFonts * fnts)
872{
873    int j, k;
874
875    for (j = 0; j < fMAX; ++j) {
876	/*
877	 * Need to save the pointer since xtermCloseFont zeroes it
878	 */
879	XFontStruct *thisFont = fnts[j].fs;
880	if (thisFont != 0) {
881	    xtermCloseFont(xw, &fnts[j]);
882	    for (k = j + 1; k < fMAX; ++k) {
883		if (thisFont == fnts[k].fs)
884		    xtermFreeFontInfo(&fnts[k]);
885	    }
886	}
887    }
888}
889
890/*
891 * Make a copy of the source, assuming the XFontStruct's to be unique, but
892 * ensuring that the names are reallocated to simplify freeing.
893 */
894void
895xtermCopyFontInfo(XTermFonts * target, XTermFonts * source)
896{
897    xtermFreeFontInfo(target);
898    target->chrset = source->chrset;
899    target->flags = source->flags;
900    target->fn = x_strdup(source->fn);
901    target->fs = source->fs;
902}
903
904void
905xtermFreeFontInfo(XTermFonts * target)
906{
907    target->chrset = 0;
908    target->flags = 0;
909    if (target->fn != 0) {
910	free(target->fn);
911	target->fn = 0;
912    }
913    target->fs = 0;
914}
915
916#if OPT_REPORT_FONTS
917static void
918reportXCharStruct(const char *tag, XCharStruct * cs)
919{
920    printf("\t\t%s:\n", tag);
921    printf("\t\t\tlbearing: %d\n", cs->lbearing);
922    printf("\t\t\trbearing: %d\n", cs->rbearing);
923    printf("\t\t\twidth:    %d\n", cs->width);
924    printf("\t\t\tascent:   %d\n", cs->ascent);
925    printf("\t\t\tdescent:  %d\n", cs->descent);
926}
927
928static void
929reportOneVTFont(const char *tag,
930		XTermFonts * fnt)
931{
932    if (!IsEmpty(fnt->fn)) {
933	XFontStruct *fs = fnt->fs;
934	unsigned first_char = 0;
935	unsigned last_char = 0;
936	unsigned ch;
937
938	if (fs->max_byte1 == 0) {
939	    first_char = fs->min_char_or_byte2;
940	    last_char = fs->max_char_or_byte2;
941	} else {
942	    first_char = (fs->min_byte1 * 256) + fs->min_char_or_byte2;
943	    last_char = (fs->max_byte1 * 256) + fs->max_char_or_byte2;
944	}
945
946	printf("\t%s: %s\n", tag, NonNull(fnt->fn));
947	printf("\t\tall chars:     %s\n", fs->all_chars_exist ? "yes" : "no");
948	printf("\t\tdefault char:  %d\n", fs->default_char);
949	printf("\t\tdirection:     %d\n", fs->direction);
950	printf("\t\tascent:        %d\n", fs->ascent);
951	printf("\t\tdescent:       %d\n", fs->descent);
952	printf("\t\tfirst char:    %u\n", first_char);
953	printf("\t\tlast char:     %u\n", last_char);
954	printf("\t\tmaximum-chars: %u\n", countGlyphs(fs));
955	if (FontLacksMetrics(fnt)) {
956	    printf("\t\tmissing-chars: ?\n");
957	    printf("\t\tpresent-chars: ?\n");
958	} else {
959	    unsigned missing = 0;
960	    for (ch = first_char; ch <= last_char; ++ch) {
961		if (xtermMissingChar(ch, fnt)) {
962		    ++missing;
963		}
964	    }
965	    printf("\t\tmissing-chars: %u\n", missing);
966	    printf("\t\tpresent-chars: %u\n", countGlyphs(fs) - missing);
967	}
968	printf("\t\tmin_byte1:     %d\n", fs->min_byte1);
969	printf("\t\tmax_byte1:     %d\n", fs->max_byte1);
970	printf("\t\tproperties:    %d\n", fs->n_properties);
971	reportXCharStruct("min_bounds", &(fs->min_bounds));
972	reportXCharStruct("max_bounds", &(fs->max_bounds));
973	/* TODO: report fs->properties and fs->per_char */
974    }
975}
976
977static void
978reportVTFontInfo(XtermWidget xw, int fontnum)
979{
980    if (resource.reportFonts) {
981	TScreen *screen = TScreenOf(xw);
982
983	if (fontnum) {
984	    printf("Loaded VTFonts(font%d)\n", fontnum);
985	} else {
986	    printf("Loaded VTFonts(default)\n");
987	}
988	reportOneVTFont("fNorm", &screen->fnts[fNorm]);
989	reportOneVTFont("fBold", &screen->fnts[fBold]);
990#if OPT_WIDE_CHARS
991	reportOneVTFont("fWide", &screen->fnts[fWide]);
992	reportOneVTFont("fWBold", &screen->fnts[fWBold]);
993#endif
994    }
995}
996#endif
997
998void
999xtermUpdateFontGCs(XtermWidget xw, XTermFonts * fnts)
1000{
1001    TScreen *screen = TScreenOf(xw);
1002    VTwin *win = WhichVWin(screen);
1003    Pixel new_normal = getXtermForeground(xw, xw->flags, xw->cur_foreground);
1004    Pixel new_revers = getXtermBackground(xw, xw->flags, xw->cur_background);
1005
1006    setCgsFore(xw, win, gcNorm, new_normal);
1007    setCgsBack(xw, win, gcNorm, new_revers);
1008    setCgsFont(xw, win, gcNorm, &(fnts[fNorm]));
1009
1010    copyCgs(xw, win, gcBold, gcNorm);
1011    setCgsFont(xw, win, gcBold, &(fnts[fBold]));
1012
1013    setCgsFore(xw, win, gcNormReverse, new_revers);
1014    setCgsBack(xw, win, gcNormReverse, new_normal);
1015    setCgsFont(xw, win, gcNormReverse, &(fnts[fNorm]));
1016
1017    copyCgs(xw, win, gcBoldReverse, gcNormReverse);
1018    setCgsFont(xw, win, gcBoldReverse, &(fnts[fBold]));
1019
1020    if_OPT_WIDE_CHARS(screen, {
1021	if (fnts[fWide].fs != 0
1022	    && fnts[fWBold].fs != 0) {
1023	    setCgsFore(xw, win, gcWide, new_normal);
1024	    setCgsBack(xw, win, gcWide, new_revers);
1025	    setCgsFont(xw, win, gcWide, &(fnts[fWide]));
1026
1027	    copyCgs(xw, win, gcWBold, gcWide);
1028	    setCgsFont(xw, win, gcWBold, &(fnts[fWBold]));
1029
1030	    setCgsFore(xw, win, gcWideReverse, new_revers);
1031	    setCgsBack(xw, win, gcWideReverse, new_normal);
1032	    setCgsFont(xw, win, gcWideReverse, &(fnts[fWide]));
1033
1034	    copyCgs(xw, win, gcWBoldReverse, gcWideReverse);
1035	    setCgsFont(xw, win, gcWBoldReverse, &(fnts[fWBold]));
1036	}
1037    });
1038}
1039
1040#if OPT_TRACE
1041static void
1042show_font_misses(const char *name, XTermFonts * fp)
1043{
1044    if (fp->fs != 0) {
1045	if (FontLacksMetrics(fp)) {
1046	    TRACE(("%s font lacks metrics\n", name));
1047	} else if (FontIsIncomplete(fp)) {
1048	    TRACE(("%s font is incomplete\n", name));
1049	} else {
1050	    TRACE(("%s font is complete\n", name));
1051	}
1052    } else {
1053	TRACE(("%s font is missing\n", name));
1054    }
1055}
1056#endif
1057
1058int
1059xtermLoadFont(XtermWidget xw,
1060	      const VTFontNames * fonts,
1061	      Bool doresize,
1062	      int fontnum)
1063{
1064    TScreen *screen = TScreenOf(xw);
1065    VTwin *win = WhichVWin(screen);
1066
1067    VTFontNames myfonts;
1068    FontNameProperties *fp;
1069    XTermFonts fnts[fMAX];
1070    char *tmpname = NULL;
1071    char *normal = NULL;
1072    Boolean proportional = False;
1073    fontWarningTypes warn[fMAX];
1074    int j;
1075
1076    memset(&myfonts, 0, sizeof(myfonts));
1077    memset(fnts, 0, sizeof(fnts));
1078
1079    if (fonts != 0)
1080	myfonts = *fonts;
1081    if (!check_fontname(myfonts.f_n))
1082	return 0;
1083
1084    /*
1085     * Check the font names against the resource values, to see which were
1086     * derived in a previous call.  If so, we'll only warn about those if
1087     * the warning level is set to "always".
1088     */
1089    for (j = 0; j < fMAX; ++j) {
1090	warn[j] = fwAlways;
1091    }
1092#define CmpResource(field, index) \
1093    if (same_font_name(screen->menu_font_names[fontnum][index], myfonts.field)) \
1094	warn[index] = fwResource
1095
1096    CmpResource(f_n, fNorm);
1097    if (fontnum == fontMenu_default) {
1098	CmpResource(f_b, fBold);
1099#if OPT_WIDE_CHARS
1100	CmpResource(f_b, fWide);
1101	CmpResource(f_b, fWBold);
1102#endif
1103    }
1104
1105    if (fontnum == fontMenu_fontescape
1106	&& myfonts.f_n != screen->MenuFontName(fontnum)) {
1107	if ((tmpname = x_strdup(myfonts.f_n)) == 0)
1108	    return 0;
1109    }
1110
1111    TRACE(("Begin Cgs - xtermLoadFont(%s)\n", myfonts.f_n));
1112    releaseWindowGCs(xw, win);
1113
1114#define DbgResource(name, field, index) \
1115    TRACE(("xtermLoadFont #%d "name" %s%s\n", \
1116    	   fontnum, \
1117	   (warn[index] == fwResource) ? "*" : " ", \
1118	   NonNull(myfonts.field)))
1119    DbgResource("normal", f_n, fNorm);
1120    DbgResource("bold  ", f_b, fBold);
1121#if OPT_WIDE_CHARS
1122    DbgResource("wide  ", f_w, fWide);
1123    DbgResource("w/bold", f_wb, fWBold);
1124#endif
1125
1126    /*
1127     * If we are opening the default font, and it happens to be missing, force
1128     * that to the compiled-in default font, e.g., "fixed".  If we cannot open
1129     * the font, disable it from the menu.
1130     */
1131    if (!xtermOpenFont(xw,
1132		       myfonts.f_n,
1133		       &fnts[fNorm],
1134		       warn[fNorm],
1135		       (fontnum == fontMenu_default))) {
1136	if (fontnum != fontMenu_fontsel) {
1137	    SetItemSensitivity(fontMenuEntries[fontnum].widget, False);
1138	}
1139	goto bad;
1140    }
1141
1142    normal = x_strdup(myfonts.f_n);
1143    if (!check_fontname(myfonts.f_b)) {
1144	warn[fBold] = fwAlways;
1145	fp = get_font_name_props(screen->display, fnts[fNorm].fs, &normal);
1146	if (fp != 0) {
1147	    FREE_FNAME(f_b);
1148	    myfonts.f_b = bold_font_name(fp, fp->average_width);
1149	    if (!xtermOpenFont(xw, myfonts.f_b, &fnts[fBold], fwAlways, False)) {
1150		FREE_FNAME(f_b);
1151		myfonts.f_b = bold_font_name(fp, -1);
1152		xtermOpenFont(xw, myfonts.f_b, &fnts[fBold], fwAlways, False);
1153	    }
1154	    TRACE(("...derived bold '%s'\n", NonNull(myfonts.f_b)));
1155	}
1156	if (fp == 0 || fnts[fBold].fs == 0) {
1157	    xtermCopyFontInfo(&fnts[fBold], &fnts[fNorm]);
1158	    TRACE(("...cannot load a matching bold font\n"));
1159	} else if (comparable_metrics(fnts[fNorm].fs, fnts[fBold].fs)
1160		   && same_font_size(xw, fnts[fNorm].fs, fnts[fBold].fs)
1161		   && got_bold_font(screen->display, fnts[fBold].fs, myfonts.f_b)) {
1162	    TRACE(("...got a matching bold font\n"));
1163	    cache_menu_font_name(screen, fontnum, fBold, myfonts.f_b);
1164	} else {
1165	    xtermCloseFont(xw, &fnts[fBold]);
1166	    fnts[fBold] = fnts[fNorm];
1167	    TRACE(("...did not get a matching bold font\n"));
1168	}
1169    } else if (!xtermOpenFont(xw, myfonts.f_b, &fnts[fBold], warn[fBold], False)) {
1170	xtermCopyFontInfo(&fnts[fBold], &fnts[fNorm]);
1171	warn[fBold] = fwAlways;
1172	TRACE(("...cannot load bold font '%s'\n", NonNull(myfonts.f_b)));
1173    } else {
1174	cache_menu_font_name(screen, fontnum, fBold, myfonts.f_b);
1175    }
1176
1177    /*
1178     * If there is no widefont specified, fake it by doubling AVERAGE_WIDTH
1179     * of normal fonts XLFD, and asking for it.  This plucks out 18x18ja
1180     * and 12x13ja as the corresponding fonts for 9x18 and 6x13.
1181     */
1182    if_OPT_WIDE_CHARS(screen, {
1183	Boolean derived;
1184	char *bold = NULL;
1185
1186	if (check_fontname(myfonts.f_w)) {
1187	    cache_menu_font_name(screen, fontnum, fWide, myfonts.f_w);
1188	} else if (screen->utf8_fonts && !is_double_width_font(fnts[fNorm].fs)) {
1189	    FREE_FNAME(f_w);
1190	    fp = get_font_name_props(screen->display, fnts[fNorm].fs, &normal);
1191	    if (fp != 0) {
1192		myfonts.f_w = wide_font_name(fp);
1193		warn[fWide] = fwAlways;
1194		TRACE(("...derived wide %s\n", NonNull(myfonts.f_w)));
1195		cache_menu_font_name(screen, fontnum, fWide, myfonts.f_w);
1196	    }
1197	}
1198
1199	if (check_fontname(myfonts.f_w)) {
1200	    (void) xtermOpenFont(xw, myfonts.f_w, &fnts[fWide], warn[fWide], False);
1201	} else {
1202	    xtermCopyFontInfo(&fnts[fWide], &fnts[fNorm]);
1203	    warn[fWide] = fwAlways;
1204	}
1205
1206	derived = False;
1207	if (!check_fontname(myfonts.f_wb)) {
1208	    fp = get_font_name_props(screen->display, fnts[fBold].fs, &bold);
1209	    if (fp != 0) {
1210		myfonts.f_wb = widebold_font_name(fp);
1211		warn[fWBold] = fwAlways;
1212		derived = True;
1213	    }
1214	}
1215
1216	if (check_fontname(myfonts.f_wb)) {
1217
1218	    xtermOpenFont(xw,
1219			  myfonts.f_wb,
1220			  &fnts[fWBold],
1221			  (screen->utf8_fonts
1222			   ? warn[fWBold]
1223			   : (fontWarningTypes) (xw->misc.fontWarnings + 1)),
1224			  False);
1225
1226	    if (derived
1227		&& !compatibleWideCounts(fnts[fWide].fs, fnts[fWBold].fs)) {
1228		xtermCloseFont(xw, &fnts[fWBold]);
1229	    }
1230	    if (fnts[fWBold].fs == 0) {
1231		FREE_FNAME(f_wb);
1232		if (IsEmpty(myfonts.f_w)) {
1233		    myfonts.f_wb = x_strdup(myfonts.f_b);
1234		    warn[fWBold] = fwAlways;
1235		    xtermCopyFontInfo(&fnts[fWBold], &fnts[fBold]);
1236		    TRACE(("...cannot load wide-bold, use bold %s\n",
1237			   NonNull(myfonts.f_b)));
1238		} else {
1239		    myfonts.f_wb = x_strdup(myfonts.f_w);
1240		    warn[fWBold] = fwAlways;
1241		    xtermCopyFontInfo(&fnts[fWBold], &fnts[fWide]);
1242		    TRACE(("...cannot load wide-bold, use wide %s\n",
1243			   NonNull(myfonts.f_w)));
1244		}
1245	    } else {
1246		TRACE(("...%s wide/bold %s\n",
1247		       derived ? "derived" : "given",
1248		       NonNull(myfonts.f_wb)));
1249		cache_menu_font_name(screen, fontnum, fWBold, myfonts.f_wb);
1250	    }
1251	} else if (is_double_width_font(fnts[fBold].fs)) {
1252	    xtermCopyFontInfo(&fnts[fWBold], &fnts[fBold]);
1253	    warn[fWBold] = fwAlways;
1254	    TRACE(("...bold font is double-width, use it %s\n", NonNull(myfonts.f_b)));
1255	} else {
1256	    xtermCopyFontInfo(&fnts[fWBold], &fnts[fWide]);
1257	    warn[fWBold] = fwAlways;
1258	    TRACE(("...cannot load wide bold font, use wide %s\n", NonNull(myfonts.f_w)));
1259	}
1260
1261	free(bold);
1262
1263	if (EmptyFont(fnts[fWBold].fs))
1264	    goto bad;		/* can't use a 0-sized font */
1265    });
1266
1267    /*
1268     * Most of the time this call to load the font will succeed, even if
1269     * there is no wide font :  the X server doubles the width of the
1270     * normal font, or similar.
1271     *
1272     * But if it did fail for some reason, then nevermind.
1273     */
1274    if (EmptyFont(fnts[fBold].fs))
1275	goto bad;		/* can't use a 0-sized font */
1276
1277    if (!same_font_size(xw, fnts[fNorm].fs, fnts[fBold].fs)
1278	&& (is_fixed_font(fnts[fNorm].fs) && is_fixed_font(fnts[fBold].fs))) {
1279	TRACE(("...ignoring mismatched normal/bold fonts\n"));
1280	xtermCloseFont(xw, &fnts[fBold]);
1281	xtermCopyFontInfo(&fnts[fBold], &fnts[fNorm]);
1282    }
1283
1284    if_OPT_WIDE_CHARS(screen, {
1285	if (fnts[fWide].fs != 0
1286	    && fnts[fWBold].fs != 0
1287	    && (!comparable_metrics(fnts[fWide].fs, fnts[fWBold].fs)
1288		|| (!same_font_size(xw, fnts[fWide].fs, fnts[fWBold].fs)
1289		    && is_fixed_font(fnts[fWide].fs)
1290		    && is_fixed_font(fnts[fWBold].fs)))) {
1291	    TRACE(("...ignoring mismatched normal/bold wide fonts\n"));
1292	    xtermCloseFont(xw, &fnts[fWBold]);
1293	    xtermCopyFontInfo(&fnts[fWBold], &fnts[fWide]);
1294	}
1295    });
1296
1297    /*
1298     * Normal/bold fonts should be the same width.  Also, the min/max
1299     * values should be the same.
1300     */
1301    if (!is_fixed_font(fnts[fNorm].fs)
1302	|| !is_fixed_font(fnts[fBold].fs)
1303	|| fnts[fNorm].fs->max_bounds.width != fnts[fBold].fs->max_bounds.width) {
1304	TRACE(("Proportional font! normal %d/%d, bold %d/%d\n",
1305	       fnts[fNorm].fs->min_bounds.width,
1306	       fnts[fNorm].fs->max_bounds.width,
1307	       fnts[fBold].fs->min_bounds.width,
1308	       fnts[fBold].fs->max_bounds.width));
1309	proportional = True;
1310    }
1311
1312    if_OPT_WIDE_CHARS(screen, {
1313	if (fnts[fWide].fs != 0
1314	    && fnts[fWBold].fs != 0
1315	    && (!is_fixed_font(fnts[fWide].fs)
1316		|| !is_fixed_font(fnts[fWBold].fs)
1317		|| fnts[fWide].fs->max_bounds.width != fnts[fWBold].fs->max_bounds.width)) {
1318	    TRACE(("Proportional font! wide %d/%d, wide bold %d/%d\n",
1319		   fnts[fWide].fs->min_bounds.width,
1320		   fnts[fWide].fs->max_bounds.width,
1321		   fnts[fWBold].fs->min_bounds.width,
1322		   fnts[fWBold].fs->max_bounds.width));
1323	    proportional = True;
1324	}
1325    });
1326
1327    /* TODO : enforce that the width of the wide font is 2* the width
1328       of the narrow font */
1329
1330    /*
1331     * If we're switching fonts, free the old ones.  Otherwise we'll leak
1332     * the memory that is associated with the old fonts.  The
1333     * XLoadQueryFont call allocates a new XFontStruct.
1334     */
1335    xtermCloseFonts(xw, screen->fnts);
1336#if OPT_WIDE_ATTRS
1337    xtermCloseFonts(xw, screen->ifnts);
1338    screen->ifnts_ok = False;
1339#endif
1340
1341    xtermCopyFontInfo(&(screen->fnts[fNorm]), &fnts[fNorm]);
1342    xtermCopyFontInfo(&(screen->fnts[fBold]), &fnts[fBold]);
1343#if OPT_WIDE_CHARS
1344    xtermCopyFontInfo(&(screen->fnts[fWide]), &fnts[fWide]);
1345    if (fnts[fWBold].fs == NULL)
1346	xtermCopyFontInfo(&fnts[fWBold], &fnts[fWide]);
1347    xtermCopyFontInfo(&(screen->fnts[fWBold]), &fnts[fWBold]);
1348#endif
1349
1350    xtermUpdateFontGCs(xw, screen->fnts);
1351
1352#if OPT_BOX_CHARS
1353    screen->allow_packing = proportional;
1354    setupPackedFonts(xw);
1355#endif
1356    screen->fnt_prop = (Boolean) (proportional && !(screen->force_packed));
1357    screen->fnt_boxes = True;
1358
1359#if OPT_BOX_CHARS
1360    /*
1361     * xterm uses character positions 1-31 of a font for the line-drawing
1362     * characters.  Check that they are all present.  The null character
1363     * (0) is special, and is not used.
1364     */
1365#if OPT_RENDERFONT
1366    if (UsingRenderFont(xw)) {
1367	/*
1368	 * FIXME: we shouldn't even be here if we're using Xft.
1369	 */
1370	screen->fnt_boxes = False;
1371	TRACE(("assume Xft missing line-drawing chars\n"));
1372    } else
1373#endif
1374    {
1375	unsigned ch;
1376
1377#if OPT_TRACE
1378#define TRACE_MISS(index) show_font_misses(#index, &fnts[index])
1379	TRACE_MISS(fNorm);
1380	TRACE_MISS(fBold);
1381#if OPT_WIDE_CHARS
1382	TRACE_MISS(fWide);
1383	TRACE_MISS(fWBold);
1384#endif
1385#endif
1386
1387	for (ch = 1; ch < 32; ch++) {
1388	    unsigned n = ch;
1389#if OPT_WIDE_CHARS
1390	    if (screen->utf8_mode || screen->unicode_font) {
1391		n = dec2ucs(ch);
1392		if (n == UCS_REPL)
1393		    continue;
1394	    }
1395#endif
1396	    if (IsXtermMissingChar(screen, n, &fnts[fNorm])) {
1397		TRACE(("missing normal char #%d\n", n));
1398		screen->fnt_boxes = False;
1399		break;
1400	    }
1401	    if (IsXtermMissingChar(screen, n, &fnts[fBold])) {
1402		TRACE(("missing bold char #%d\n", n));
1403		screen->fnt_boxes = False;
1404		break;
1405	    }
1406	}
1407    }
1408    TRACE(("Will %suse internal line-drawing characters\n",
1409	   screen->fnt_boxes ? "not " : ""));
1410#endif
1411
1412    if (screen->always_bold_mode) {
1413	screen->enbolden = screen->bold_mode;
1414    } else {
1415	screen->enbolden = screen->bold_mode
1416	    && ((fnts[fNorm].fs == fnts[fBold].fs)
1417		|| same_font_name(normal, myfonts.f_b));
1418    }
1419    TRACE(("Will %suse 1-pixel offset/overstrike to simulate bold\n",
1420	   screen->enbolden ? "" : "not "));
1421
1422    set_menu_font(False);
1423    screen->menu_font_number = fontnum;
1424    set_menu_font(True);
1425    if (tmpname) {		/* if setting escape or sel */
1426	if (screen->MenuFontName(fontnum))
1427	    FREE_STRING(screen->MenuFontName(fontnum));
1428	screen->MenuFontName(fontnum) = tmpname;
1429	if (fontnum == fontMenu_fontescape) {
1430	    update_font_escape();
1431	}
1432#if OPT_SHIFT_FONTS
1433	screen->menu_font_sizes[fontnum] = FontSize(fnts[fNorm].fs);
1434#endif
1435    }
1436    if (normal)
1437	free(normal);
1438    set_cursor_gcs(xw);
1439    xtermUpdateFontInfo(xw, doresize);
1440    TRACE(("Success Cgs - xtermLoadFont\n"));
1441#if OPT_REPORT_FONTS
1442    reportVTFontInfo(xw, fontnum);
1443#endif
1444    FREE_FNAME(f_n);
1445    FREE_FNAME(f_b);
1446#if OPT_WIDE_CHARS
1447    FREE_FNAME(f_w);
1448    FREE_FNAME(f_wb);
1449#endif
1450    if (fnts[fNorm].fn == fnts[fBold].fn) {
1451	free(fnts[fNorm].fn);
1452    } else {
1453	free(fnts[fNorm].fn);
1454	free(fnts[fBold].fn);
1455    }
1456#if OPT_WIDE_CHARS
1457    free(fnts[fWide].fn);
1458    free(fnts[fWBold].fn);
1459#endif
1460    return 1;
1461
1462  bad:
1463    if (normal)
1464	free(normal);
1465    if (tmpname)
1466	free(tmpname);
1467
1468#if OPT_RENDERFONT
1469    if ((fontnum == fontMenu_fontsel) && (fontnum != screen->menu_font_number)) {
1470	int old_fontnum = screen->menu_font_number;
1471#if OPT_TOOLBAR
1472	SetItemSensitivity(fontMenuEntries[fontnum].widget, True);
1473#endif
1474	Bell(xw, XkbBI_MinorError, 0);
1475	myfonts.f_n = screen->MenuFontName(old_fontnum);
1476	return xtermLoadFont(xw, &myfonts, doresize, old_fontnum);
1477    } else if (x_strcasecmp(myfonts.f_n, DEFFONT)) {
1478	int code;
1479
1480	myfonts.f_n = DEFFONT;
1481	TRACE(("...recovering for TrueType fonts\n"));
1482	code = xtermLoadFont(xw, &myfonts, doresize, fontnum);
1483	if (code) {
1484	    if (fontnum != fontMenu_fontsel) {
1485		SetItemSensitivity(fontMenuEntries[fontnum].widget,
1486				   UsingRenderFont(xw));
1487	    }
1488	    TRACE(("...recovered size %dx%d\n",
1489		   FontHeight(screen),
1490		   FontWidth(screen)));
1491	}
1492	return code;
1493    }
1494#endif
1495
1496    releaseWindowGCs(xw, win);
1497
1498    xtermCloseFonts(xw, fnts);
1499    TRACE(("Fail Cgs - xtermLoadFont\n"));
1500    return 0;
1501}
1502
1503#if OPT_WIDE_ATTRS
1504/*
1505 * (Attempt to) load matching italics for the current normal/bold/etc fonts.
1506 * If the attempt fails for a given style, use the non-italic font.
1507 */
1508void
1509xtermLoadItalics(XtermWidget xw)
1510{
1511    TScreen *screen = TScreenOf(xw);
1512    FontNameProperties *fp;
1513    char *name;
1514    int n;
1515
1516    if (!screen->ifnts_ok) {
1517	screen->ifnts_ok = True;
1518	for (n = 0; n < fMAX; ++n) {
1519	    /*
1520	     * FIXME - need to handle font-leaks
1521	     */
1522	    screen->ifnts[n].fs = 0;
1523	    if (screen->fnts[n].fs != 0 &&
1524		(fp = get_font_name_props(screen->display,
1525					  screen->fnts[n].fs,
1526					  0)) != 0) {
1527		if ((name = italic_font_name(fp, fp->average_width)) != 0) {
1528		    TRACE(("xtermLoadItalics #%d %s\n", n, name));
1529		    (void) xtermOpenFont(xw,
1530					 name,
1531					 &(screen->ifnts[n]),
1532					 fwResource,
1533					 False);
1534#if OPT_TRACE
1535		    {
1536			XFontStruct *fs =
1537			screen->ifnts[n].fs;
1538			if (fs != 0) {
1539			    TRACE(("...actual size %dx%d (ascent %d, descent %d)\n",
1540				   fs->ascent +
1541				   fs->descent,
1542				   fs->max_bounds.width,
1543				   fs->ascent,
1544				   fs->descent));
1545			}
1546		    }
1547#endif
1548		    free(name);
1549		}
1550	    }
1551	}
1552    }
1553}
1554#endif
1555
1556#if OPT_LOAD_VTFONTS || OPT_WIDE_CHARS
1557/*
1558 * Collect font-names that we can modify with the load-vt-fonts() action.
1559 */
1560#define MERGE_SUBFONT(src,dst,name) \
1561	if (IsEmpty(dst.name)) { \
1562	    TRACE(("MERGE_SUBFONT " #dst "." #name " merge %s\n", NonNull(src.name))); \
1563	    dst.name = x_strdup(src.name); \
1564	} else { \
1565	    TRACE(("MERGE_SUBFONT " #dst "." #name " found %s\n", NonNull(dst.name))); \
1566	}
1567
1568#define INFER_SUBFONT(src,dst,name) \
1569	if (IsEmpty(dst.name)) { \
1570	    TRACE(("INFER_SUBFONT " #dst "." #name " will infer\n")); \
1571	    dst.name = x_strdup(""); \
1572	} else { \
1573	    TRACE(("INFER_SUBFONT " #dst "." #name " found %s\n", NonNull(dst.name))); \
1574	}
1575
1576#define FREE_MENU_FONTS(dst) \
1577	TRACE(("FREE_MENU_FONTS " #dst "\n")); \
1578	for (n = fontMenu_default; n <= fontMenu_lastBuiltin; ++n) { \
1579	    for (m = 0; m < fMAX; ++m) { \
1580		FREE_STRING(dst.menu_font_names[n][m]); \
1581		dst.menu_font_names[n][m] = 0; \
1582	    } \
1583	}
1584
1585#define COPY_MENU_FONTS(src,dst) \
1586	TRACE(("COPY_MENU_FONTS " #src " to " #dst "\n")); \
1587	for (n = fontMenu_default; n <= fontMenu_lastBuiltin; ++n) { \
1588	    for (m = 0; m < fMAX; ++m) { \
1589		FREE_STRING(dst.menu_font_names[n][m]); \
1590		dst.menu_font_names[n][m] = x_strdup(src.menu_font_names[n][m]); \
1591	    } \
1592	    TRACE((".. " #dst ".menu_fonts_names[%d] = %s\n", n, NonNull(dst.menu_font_names[n][fNorm]))); \
1593	}
1594
1595#define COPY_DEFAULT_FONTS(target, source) \
1596	xtermCopyVTFontNames(&target.default_font, &source.default_font)
1597
1598static void
1599xtermCopyVTFontNames(VTFontNames * target, VTFontNames * source)
1600{
1601    target->f_n = x_strdup(source->f_n);
1602    target->f_b = x_strdup(source->f_b);
1603#if OPT_WIDE_CHARS
1604    target->f_w = x_strdup(source->f_w);
1605    target->f_wb = x_strdup(source->f_wb);
1606#endif
1607}
1608
1609void
1610xtermSaveVTFonts(XtermWidget xw)
1611{
1612    TScreen *screen = TScreenOf(xw);
1613    Cardinal n, m;
1614
1615    if (!screen->savedVTFonts) {
1616
1617	screen->savedVTFonts = True;
1618	TRACE(("xtermSaveVTFonts saving original\n"));
1619	COPY_DEFAULT_FONTS(screen->cacheVTFonts, xw->misc);
1620	COPY_MENU_FONTS(xw->screen, screen->cacheVTFonts);
1621    }
1622}
1623
1624#define SAME_STRING(x,y) ((x) == (y) || ((x) && (y) && !strcmp(x, y)))
1625#define SAME_MEMBER(n)   SAME_STRING(a->n, b->n)
1626
1627static Boolean
1628sameSubResources(SubResourceRec * a, SubResourceRec * b)
1629{
1630    Boolean result = True;
1631    int n;
1632
1633    if (!SAME_MEMBER(default_font.f_n)
1634	|| !SAME_MEMBER(default_font.f_b)
1635#if OPT_WIDE_CHARS
1636	|| !SAME_MEMBER(default_font.f_w)
1637	|| !SAME_MEMBER(default_font.f_wb)
1638#endif
1639	) {
1640	TRACE(("sameSubResources: default_font differs\n"));
1641	result = False;
1642    } else {
1643	for (n = 0; n < NMENUFONTS; ++n) {
1644	    if (!SAME_MEMBER(menu_font_names[n][fNorm])) {
1645		TRACE(("sameSubResources: menu_font_names[%d] differs\n", n));
1646		result = False;
1647		break;
1648	    }
1649	}
1650    }
1651
1652    return result;
1653}
1654
1655/*
1656 * Load the "VT" font names from the given subresource name/class.  These
1657 * correspond to the VT100 resources.
1658 */
1659static Bool
1660xtermLoadVTFonts(XtermWidget xw, String myName, String myClass)
1661{
1662    SubResourceRec subresourceRec;
1663    SubResourceRec referenceRec;
1664
1665    /*
1666     * These are duplicates of the VT100 font resources, but with a special
1667     * application/classname passed in to distinguish them.
1668     */
1669    static XtResource font_resources[] =
1670    {
1671	Sres(XtNfont, XtCFont, default_font.f_n, DEFFONT),
1672	Sres(XtNboldFont, XtCBoldFont, default_font.f_b, DEFBOLDFONT),
1673#if OPT_WIDE_CHARS
1674	Sres(XtNwideFont, XtCWideFont, default_font.f_w, DEFWIDEFONT),
1675	Sres(XtNwideBoldFont, XtCWideBoldFont, default_font.f_wb, DEFWIDEBOLDFONT),
1676#endif
1677	Sres(XtNfont1, XtCFont1, MenuFontName(fontMenu_font1), NULL),
1678	Sres(XtNfont2, XtCFont2, MenuFontName(fontMenu_font2), NULL),
1679	Sres(XtNfont3, XtCFont3, MenuFontName(fontMenu_font3), NULL),
1680	Sres(XtNfont4, XtCFont4, MenuFontName(fontMenu_font4), NULL),
1681	Sres(XtNfont5, XtCFont5, MenuFontName(fontMenu_font5), NULL),
1682	Sres(XtNfont6, XtCFont6, MenuFontName(fontMenu_font6), NULL),
1683    };
1684    Cardinal n, m;
1685    Bool status = True;
1686    TScreen *screen = TScreenOf(xw);
1687
1688    TRACE(("called xtermLoadVTFonts(name=%s, class=%s)\n",
1689	   NonNull(myName), NonNull(myClass)));
1690
1691    xtermSaveVTFonts(xw);
1692
1693    if (IsEmpty(myName)) {
1694	TRACE(("xtermLoadVTFonts restoring original\n"));
1695	COPY_DEFAULT_FONTS(xw->misc, screen->cacheVTFonts);
1696	FREE_MENU_FONTS(xw->screen);
1697	COPY_MENU_FONTS(screen->cacheVTFonts, xw->screen);
1698    } else {
1699	TRACE(("xtermLoadVTFonts(%s, %s)\n", myName, myClass));
1700
1701	memset(&referenceRec, 0, sizeof(referenceRec));
1702	memset(&subresourceRec, 0, sizeof(subresourceRec));
1703	XtGetSubresources((Widget) xw, (XtPointer) &subresourceRec,
1704			  myName, myClass,
1705			  font_resources,
1706			  (Cardinal) XtNumber(font_resources),
1707			  NULL, (Cardinal) 0);
1708
1709	/*
1710	 * XtGetSubresources returns no status, so we compare the returned
1711	 * data against a zero'd struct to see if any data is returned.
1712	 */
1713	if (memcmp(&referenceRec, &subresourceRec, sizeof(referenceRec))
1714	    && !sameSubResources(&(screen->cacheVTFonts), &subresourceRec)) {
1715
1716	    screen->mergedVTFonts = True;
1717
1718	    /*
1719	     * To make it simple, reallocate the strings returned by
1720	     * XtGetSubresources.  We can free our own strings, but not theirs.
1721	     */
1722	    ALLOC_STRING(subresourceRec.default_font.f_n);
1723	    ALLOC_STRING(subresourceRec.default_font.f_b);
1724#if OPT_WIDE_CHARS
1725	    ALLOC_STRING(subresourceRec.default_font.f_w);
1726	    ALLOC_STRING(subresourceRec.default_font.f_wb);
1727#endif
1728	    for (n = fontMenu_font1; n <= fontMenu_lastBuiltin; ++n) {
1729		ALLOC_STRING(subresourceRec.MenuFontName(n));
1730	    }
1731
1732	    /*
1733	     * If a particular resource value was not found, use the original.
1734	     */
1735	    MERGE_SUBFONT(xw->misc, subresourceRec, default_font.f_n);
1736	    INFER_SUBFONT(xw->misc, subresourceRec, default_font.f_b);
1737#if OPT_WIDE_CHARS
1738	    INFER_SUBFONT(xw->misc, subresourceRec, default_font.f_w);
1739	    INFER_SUBFONT(xw->misc, subresourceRec, default_font.f_wb);
1740#endif
1741	    for (n = fontMenu_font1; n <= fontMenu_lastBuiltin; ++n) {
1742		MERGE_SUBFONT(xw->screen, subresourceRec, MenuFontName(n));
1743	    }
1744
1745	    /*
1746	     * Finally, copy the subresource data to the widget.
1747	     */
1748	    COPY_DEFAULT_FONTS(xw->misc, subresourceRec);
1749	    FREE_MENU_FONTS(xw->screen);
1750	    COPY_MENU_FONTS(subresourceRec, xw->screen);
1751
1752	    FREE_STRING(screen->MenuFontName(fontMenu_default));
1753	    FREE_STRING(screen->menu_font_names[0][fBold]);
1754	    screen->MenuFontName(fontMenu_default) = x_strdup(xw->misc.default_font.f_n);
1755	    screen->menu_font_names[0][fBold] = x_strdup(xw->misc.default_font.f_b);
1756#if OPT_WIDE_CHARS
1757	    FREE_STRING(screen->menu_font_names[0][fWide]);
1758	    FREE_STRING(screen->menu_font_names[0][fWBold]);
1759	    screen->menu_font_names[0][fWide] = x_strdup(xw->misc.default_font.f_w);
1760	    screen->menu_font_names[0][fWBold] = x_strdup(xw->misc.default_font.f_wb);
1761#endif
1762	    /*
1763	     * And remove our copies of strings.
1764	     */
1765	    FREE_STRING(subresourceRec.default_font.f_n);
1766	    FREE_STRING(subresourceRec.default_font.f_b);
1767#if OPT_WIDE_CHARS
1768	    FREE_STRING(subresourceRec.default_font.f_w);
1769	    FREE_STRING(subresourceRec.default_font.f_wb);
1770#endif
1771	    for (n = fontMenu_font1; n <= fontMenu_lastBuiltin; ++n) {
1772		FREE_STRING(subresourceRec.MenuFontName(n));
1773	    }
1774	} else {
1775	    TRACE(("...no resources found\n"));
1776	    status = False;
1777	}
1778    }
1779    return status;
1780}
1781
1782#if OPT_WIDE_CHARS
1783static Bool
1784isWideFont(XFontStruct *fp, const char *tag, Bool nullOk)
1785{
1786    Bool result = False;
1787
1788    (void) tag;
1789    if (okFont(fp)) {
1790	unsigned count = countGlyphs(fp);
1791	TRACE(("isWideFont(%s) found %d cells\n", tag, count));
1792	result = (count > 256) ? True : False;
1793    } else {
1794	result = nullOk;
1795    }
1796    return result;
1797}
1798
1799/*
1800 * If the current fonts are not wide, load the UTF8 fonts.
1801 *
1802 * Called during initialization (for wide-character mode), the fonts have not
1803 * been setup, so we pass nullOk=True to isWideFont().
1804 *
1805 * Called after initialization, e.g., in response to the UTF-8 menu entry
1806 * (starting from narrow character mode), it checks if the fonts are not wide.
1807 */
1808Bool
1809xtermLoadWideFonts(XtermWidget xw, Bool nullOk)
1810{
1811    TScreen *screen = TScreenOf(xw);
1812    Bool result;
1813
1814    if (EmptyFont(screen->fnts[fWide].fs)) {
1815	result = (isWideFont(screen->fnts[fNorm].fs, "normal", nullOk)
1816		  && isWideFont(screen->fnts[fBold].fs, "bold", nullOk));
1817    } else {
1818	result = (isWideFont(screen->fnts[fWide].fs, "wide", nullOk)
1819		  && isWideFont(screen->fnts[fWBold].fs, "wide-bold", nullOk));
1820	if (result && !screen->utf8_latin1) {
1821	    result = (isWideFont(screen->fnts[fNorm].fs, "normal", nullOk)
1822		      && isWideFont(screen->fnts[fBold].fs, "bold", nullOk));
1823	}
1824    }
1825    if (!result) {
1826	TRACE(("current fonts are not all wide%s\n", nullOk ? " nullOk" : ""));
1827	result = xtermLoadVTFonts(xw, XtNutf8Fonts, XtCUtf8Fonts);
1828    }
1829    TRACE(("xtermLoadWideFonts:%d\n", result));
1830    return result;
1831}
1832#endif /* OPT_WIDE_CHARS */
1833
1834/*
1835 * Restore the default fonts, i.e., if we had switched to wide-fonts.
1836 */
1837Bool
1838xtermLoadDefaultFonts(XtermWidget xw)
1839{
1840    Bool result;
1841    result = xtermLoadVTFonts(xw, NULL, NULL);
1842    TRACE(("xtermLoadDefaultFonts:%d\n", result));
1843    return result;
1844}
1845#endif /* OPT_LOAD_VTFONTS || OPT_WIDE_CHARS */
1846
1847#if OPT_LOAD_VTFONTS
1848void
1849HandleLoadVTFonts(Widget w,
1850		  XEvent *event GCC_UNUSED,
1851		  String *params GCC_UNUSED,
1852		  Cardinal *param_count GCC_UNUSED)
1853{
1854    static char empty[] = "";	/* appease strict compilers */
1855
1856    XtermWidget xw;
1857
1858    if ((xw = getXtermWidget(w)) != 0) {
1859	TScreen *screen = TScreenOf(xw);
1860	char name_buf[80];
1861	char class_buf[80];
1862	String name = (String) ((*param_count > 0) ? params[0] : empty);
1863	char *myName = MyStackAlloc(strlen(name) + 1, name_buf);
1864
1865	TRACE(("HandleLoadVTFonts(%d)\n", *param_count));
1866	if (myName != 0) {
1867	    String convert = (String) ((*param_count > 1) ? params[1] : myName);
1868	    char *myClass = MyStackAlloc(strlen(convert) + 1, class_buf);
1869	    int n;
1870
1871	    strcpy(myName, name);
1872	    if (myClass != 0) {
1873		strcpy(myClass, convert);
1874		if (*param_count == 1)
1875		    myClass[0] = x_toupper(myClass[0]);
1876
1877		if (xtermLoadVTFonts(xw, myName, myClass)) {
1878		    /*
1879		     * When switching fonts, try to preserve the font-menu
1880		     * selection, since it is less surprising to do that (if
1881		     * the font-switching can be undone) than to switch to
1882		     * "Default".
1883		     */
1884		    int font_number = screen->menu_font_number;
1885		    if (font_number > fontMenu_lastBuiltin)
1886			font_number = fontMenu_lastBuiltin;
1887		    for (n = 0; n < NMENUFONTS; ++n) {
1888			screen->menu_font_sizes[n] = 0;
1889		    }
1890		    SetVTFont(xw, font_number, True,
1891			      ((font_number == fontMenu_default)
1892			       ? &(xw->misc.default_font)
1893			       : NULL));
1894		}
1895		MyStackFree(myClass, class_buf);
1896	    }
1897	    MyStackFree(myName, name_buf);
1898	}
1899    }
1900}
1901#endif /* OPT_LOAD_VTFONTS */
1902
1903/*
1904 * Set the limits for the box that outlines the cursor.
1905 */
1906void
1907xtermSetCursorBox(TScreen *screen)
1908{
1909    static XPoint VTbox[NBOX];
1910    XPoint *vp;
1911    int fw = FontWidth(screen) - 1;
1912    int fh = FontHeight(screen) - 1;
1913    int ww = isCursorBar(screen) ? 1 : fw;
1914    int hh = isCursorUnderline(screen) ? 1 : fh;
1915
1916    vp = &VTbox[1];
1917    (vp++)->x = (short) ww;
1918    (vp++)->y = (short) hh;
1919    (vp++)->x = (short) -ww;
1920    vp->y = (short) -hh;
1921
1922    screen->box = VTbox;
1923}
1924
1925#define CACHE_XFT(dst,src) if (src != 0) {\
1926	    checkXft(xw, &(dst[fontnum]), src);\
1927	    TRACE(("Xft metrics %s[%d] = %d (%d,%d)%s advance %d, actual %d%s\n",\
1928		#dst,\
1929	    	fontnum,\
1930		src->height,\
1931		src->ascent,\
1932		src->descent,\
1933		((src->ascent + src->descent) > src->height ? "*" : ""),\
1934		src->max_advance_width,\
1935		dst[fontnum].map.min_width,\
1936		dst[fontnum].map.mixed ? " mixed" : ""));\
1937	}
1938
1939#if OPT_RENDERFONT
1940
1941static FcChar32
1942xtermXftFirstChar(XftFont *xft)
1943{
1944    FcChar32 map[FC_CHARSET_MAP_SIZE];
1945    FcChar32 next;
1946    FcChar32 first;
1947    int i;
1948
1949    first = FcCharSetFirstPage(xft->charset, map, &next);
1950    for (i = 0; i < FC_CHARSET_MAP_SIZE; i++) {
1951	if (map[i]) {
1952	    FcChar32 bits = map[i];
1953	    first += (FcChar32) i *32;
1954	    while (!(bits & 0x1)) {
1955		bits >>= 1;
1956		first++;
1957	    }
1958	    break;
1959	}
1960    }
1961    return first;
1962}
1963
1964static FcChar32
1965xtermXftLastChar(XftFont *xft)
1966{
1967    FcChar32 this, last, next;
1968    FcChar32 map[FC_CHARSET_MAP_SIZE];
1969    int i;
1970    last = FcCharSetFirstPage(xft->charset, map, &next);
1971    while ((this = FcCharSetNextPage(xft->charset, map, &next)) != FC_CHARSET_DONE)
1972	last = this;
1973    last &= (FcChar32) ~ 0xff;
1974    for (i = FC_CHARSET_MAP_SIZE - 1; i >= 0; i--) {
1975	if (map[i]) {
1976	    FcChar32 bits = map[i];
1977	    last += (FcChar32) i *32 + 31;
1978	    while (!(bits & 0x80000000)) {
1979		last--;
1980		bits <<= 1;
1981	    }
1982	    break;
1983	}
1984    }
1985    return (long) last;
1986}
1987
1988#if OPT_TRACE > 1
1989static void
1990dumpXft(XtermWidget xw, XTermXftFonts *data)
1991{
1992    XftFont *xft = data->font;
1993    TScreen *screen = TScreenOf(xw);
1994    VTwin *win = WhichVWin(screen);
1995
1996    FcChar32 c;
1997    FcChar32 first = xtermXftFirstChar(xft);
1998    FcChar32 last = xtermXftLastChar(xft);
1999    unsigned count = 0;
2000    unsigned outside = 0;
2001
2002    TRACE(("dumpXft {{\n"));
2003    TRACE(("   data range %#6x..%#6x\n", first, last));
2004    for (c = first; c <= last; ++c) {
2005	if (FcCharSetHasChar(xft->charset, c)) {
2006	    int width = my_wcwidth((int) c);
2007	    XGlyphInfo extents;
2008
2009	    XftTextExtents32(XtDisplay(xw), xft, &c, 1, &extents);
2010	    TRACE(("%#6x  %2d  %.1f\n", c, width,
2011		   ((double) extents.width) / win->f_width));
2012	    if (extents.width > win->f_width)
2013		++outside;
2014	    ++count;
2015	}
2016    }
2017    TRACE(("}} %u total, %u outside\n", count, outside));
2018}
2019#define DUMP_XFT(xw, data) dumpXft(xw, data)
2020#else
2021#define DUMP_XFT(xw, data)	/* nothing */
2022#endif
2023
2024static void
2025checkXft(XtermWidget xw, XTermXftFonts *data, XftFont *xft)
2026{
2027    FcChar32 c;
2028    Dimension width = 0;
2029
2030    data->font = xft;
2031    data->map.min_width = 0;
2032    data->map.max_width = (Dimension) xft->max_advance_width;
2033
2034    /*
2035     * For each ASCII or ISO-8859-1 printable code, ask what its width is.
2036     * Given the maximum width for those, we have a reasonable estimate of
2037     * the single-column width.
2038     *
2039     * Ignore control characters - their extent information is misleading.
2040     */
2041    for (c = 32; c < 256; ++c) {
2042	if (c >= 127 && c <= 159)
2043	    continue;
2044	if (FcCharSetHasChar(xft->charset, c)) {
2045	    XGlyphInfo extents;
2046
2047	    XftTextExtents32(XtDisplay(xw), xft, &c, 1, &extents);
2048	    if (width < extents.width && extents.width <= data->map.max_width) {
2049		width = extents.width;
2050	    }
2051	}
2052    }
2053    data->map.min_width = width;
2054    data->map.mixed = (data->map.max_width >= (data->map.min_width + 1));
2055}
2056
2057static void
2058reportXftFonts(XtermWidget xw,
2059	       XftFont *fp,
2060	       const char *name,
2061	       const char *tag,
2062	       XftPattern *match)
2063{
2064    if (resource.reportFonts) {
2065	char buffer[1024];
2066	FcChar32 first_char = xtermXftFirstChar(fp);
2067	FcChar32 last_char = xtermXftLastChar(fp);
2068	FcChar32 ch;
2069	unsigned missing = 0;
2070
2071	printf("Loaded XftFonts(%s[%s])\n", name, tag);
2072
2073	for (ch = first_char; ch <= last_char; ++ch) {
2074	    if (xtermXftMissing(xw, fp, ch)) {
2075		++missing;
2076	    }
2077	}
2078	printf("\t\tfirst char:    %u\n", first_char);
2079	printf("\t\tlast char:     %u\n", last_char);
2080	printf("\t\tmissing-chars: %u\n", missing);
2081	printf("\t\tpresent-chars: %u\n", (last_char - first_char) + 1 - missing);
2082
2083	if (XftNameUnparse(match, buffer, (int) sizeof(buffer))) {
2084	    char *target;
2085	    char *source = buffer;
2086	    while ((target = strtok(source, ":")) != 0) {
2087		printf("\t%s\n", target);
2088		source = 0;
2089	    }
2090	}
2091    }
2092}
2093
2094static XftFont *
2095xtermOpenXft(XtermWidget xw, const char *name, XftPattern *pat, const char *tag)
2096{
2097    TScreen *screen = TScreenOf(xw);
2098    Display *dpy = screen->display;
2099    XftPattern *match;
2100    XftResult status;
2101    XftFont *result = 0;
2102
2103    if (pat != 0) {
2104	match = XftFontMatch(dpy, DefaultScreen(dpy), pat, &status);
2105	if (match != 0) {
2106	    result = XftFontOpenPattern(dpy, match);
2107	    if (result != 0) {
2108		TRACE(("...matched %s font\n", tag));
2109		reportXftFonts(xw, result, name, tag, match);
2110	    } else {
2111		TRACE(("...could did not open %s font\n", tag));
2112		XftPatternDestroy(match);
2113		if (xw->misc.fontWarnings >= fwAlways) {
2114		    TRACE(("OOPS cannot open %s font \"%s\"\n", tag, name));
2115		    xtermWarning("cannot open %s font \"%s\"\n", tag, name);
2116		}
2117	    }
2118	} else {
2119	    TRACE(("...did not match %s font\n", tag));
2120	    if (xw->misc.fontWarnings >= fwResource) {
2121		TRACE(("OOPS: cannot match %s font \"%s\"\n", tag, name));
2122		xtermWarning("cannot match %s font \"%s\"\n", tag, name);
2123	    }
2124	}
2125    }
2126    return result;
2127}
2128#endif
2129
2130#if OPT_RENDERFONT
2131#if OPT_SHIFT_FONTS
2132/*
2133 * Don't make a dependency on the math library for a single function.
2134 * (Newton Raphson).
2135 */
2136static double
2137dimSquareRoot(double value)
2138{
2139    double result = 0.0;
2140    if (value > 0.0) {
2141	int n;
2142	double older = value;
2143	for (n = 0; n < 10; ++n) {
2144	    double delta = (older * older - value) / (2.0 * older);
2145	    double newer = older - delta;
2146	    older = newer;
2147	    result = newer;
2148	    if (delta > -0.001 && delta < 0.001)
2149		break;
2150	}
2151    }
2152    return result;
2153}
2154#endif
2155
2156/*
2157 * Given the Xft font metrics, determine the actual font size.  This is used
2158 * for each font to ensure that normal, bold and italic fonts follow the same
2159 * rule.
2160 */
2161static void
2162setRenderFontsize(TScreen *screen, VTwin *win, XftFont *font, const char *tag)
2163{
2164    if (font != 0) {
2165	int width, height, ascent, descent;
2166
2167	(void) screen;
2168
2169	width = font->max_advance_width;
2170	height = font->height;
2171	ascent = font->ascent;
2172	descent = font->descent;
2173	if (height < ascent + descent) {
2174	    TRACE(("...increase height from %d\n", height));
2175	    height = ascent + descent;
2176	}
2177	if (is_double_width_font_xft(screen->display, font)) {
2178	    TRACE(("...reduced width from %d\n", width));
2179	    width >>= 1;
2180	}
2181	if (tag == 0) {
2182	    SetFontWidth(screen, win, width);
2183	    SetFontHeight(screen, win, height);
2184	    win->f_ascent = ascent;
2185	    win->f_descent = descent;
2186	    TRACE(("setRenderFontsize result %dx%d (%d+%d)\n",
2187		   width, height, ascent, descent));
2188	} else if (win->f_width < width ||
2189		   win->f_height < height ||
2190		   win->f_ascent < ascent ||
2191		   win->f_descent < descent) {
2192	    TRACE(("setRenderFontsize %s changed %dx%d (%d+%d) to %dx%d (%d+%d)\n",
2193		   tag,
2194		   win->f_width, win->f_height, win->f_ascent, win->f_descent,
2195		   width, height, ascent, descent));
2196
2197	    SetFontWidth(screen, win, width);
2198	    SetFontHeight(screen, win, height);
2199	    win->f_ascent = ascent;
2200	    win->f_descent = descent;
2201	} else {
2202	    TRACE(("setRenderFontsize %s unchanged\n", tag));
2203	}
2204    }
2205}
2206#endif
2207
2208static void
2209checkFontInfo(int value, const char *tag)
2210{
2211    if (value == 0) {
2212	xtermWarning("Selected font has no non-zero %s for ISO-8859-1 encoding\n", tag);
2213	exit(1);
2214    }
2215}
2216
2217#if OPT_RENDERFONT
2218void
2219xtermCloseXft(TScreen *screen, XTermXftFonts *pub)
2220{
2221    if (pub->font != 0) {
2222	XftFontClose(screen->display, pub->font);
2223	pub->font = 0;
2224    }
2225}
2226
2227/*
2228 * Get the faceName/faceDoublesize resource setting.  Strip off "xft:", which
2229 * is not recognized by XftNameParse().
2230 */
2231String
2232getFaceName(XtermWidget xw, Bool wideName GCC_UNUSED)
2233{
2234#if OPT_RENDERWIDE
2235    String result = (wideName
2236		     ? xw->misc.face_wide_name
2237		     : xw->misc.face_name);
2238#else
2239    String result = xw->misc.face_name;
2240#endif
2241    if (!IsEmpty(result) && !strncmp(result, "xft:", (size_t) 4))
2242	result += 4;
2243    return x_nonempty(result);
2244}
2245
2246/*
2247 * If we change the faceName, we'll have to re-acquire all of the fonts that
2248 * are derived from it.
2249 */
2250void
2251setFaceName(XtermWidget xw, const char *value)
2252{
2253    TScreen *screen = TScreenOf(xw);
2254    int n;
2255    Boolean changed = (Boolean) ((xw->misc.face_name == 0)
2256				 || strcmp(xw->misc.face_name, value));
2257
2258    if (changed) {
2259	xw->misc.face_name = x_strdup(value);
2260	for (n = 0; n < NMENUFONTS; ++n) {
2261	    xw->misc.face_size[n] = -1.0;
2262	    xtermCloseXft(screen, &(screen->renderFontNorm[n]));
2263	    xtermCloseXft(screen, &(screen->renderFontBold[n]));
2264	    xtermCloseXft(screen, &(screen->renderFontBold[n]));
2265#if OPT_RENDERWIDE
2266	    xtermCloseXft(screen, &(screen->renderWideNorm[n]));
2267	    xtermCloseXft(screen, &(screen->renderWideBold[n]));
2268	    xtermCloseXft(screen, &(screen->renderWideItal[n]));
2269#endif
2270	}
2271    }
2272}
2273#endif
2274
2275/*
2276 * Compute useful values for the font/window sizes
2277 */
2278void
2279xtermComputeFontInfo(XtermWidget xw,
2280		     VTwin *win,
2281		     XFontStruct *font,
2282		     int sbwidth)
2283{
2284    TScreen *screen = TScreenOf(xw);
2285
2286    int i, j, width, height;
2287#if OPT_RENDERFONT
2288    int fontnum = screen->menu_font_number;
2289#endif
2290
2291#if OPT_RENDERFONT
2292    /*
2293     * xterm contains a lot of references to fonts, assuming they are fixed
2294     * size.  This chunk of code overrides the actual font-selection (see
2295     * drawXtermText()), if the user has selected render-font.  All of the
2296     * font-loading for fixed-fonts still goes on whether or not this chunk
2297     * overrides it.
2298     */
2299    if (UsingRenderFont(xw) && fontnum >= 0) {
2300	String face_name = getFaceName(xw, False);
2301	XftFont *norm = screen->renderFontNorm[fontnum].font;
2302	XftFont *bold = screen->renderFontBold[fontnum].font;
2303	XftFont *ital = screen->renderFontItal[fontnum].font;
2304#if OPT_RENDERWIDE
2305	XftFont *wnorm = screen->renderWideNorm[fontnum].font;
2306	XftFont *wbold = screen->renderWideBold[fontnum].font;
2307	XftFont *wital = screen->renderWideItal[fontnum].font;
2308#endif
2309
2310	if (norm == 0 && face_name) {
2311	    XftPattern *pat;
2312	    double face_size;
2313
2314	    TRACE(("xtermComputeFontInfo font %d: norm(face %s, size %.1f)\n",
2315		   fontnum, face_name,
2316		   xw->misc.face_size[fontnum]));
2317
2318	    fillInFaceSize(xw, fontnum);
2319	    face_size = xw->misc.face_size[fontnum];
2320
2321	    /*
2322	     * By observation (there is no documentation), XftPatternBuild is
2323	     * cumulative.  Build the bold- and italic-patterns on top of the
2324	     * normal pattern.
2325	     */
2326#define NormXftPattern \
2327	    XFT_FAMILY, XftTypeString, "mono", \
2328	    XFT_SIZE, XftTypeDouble, face_size, \
2329	    XFT_SPACING, XftTypeInteger, XFT_MONO
2330
2331#define BoldXftPattern(norm) \
2332	    XFT_WEIGHT, XftTypeInteger, XFT_WEIGHT_BOLD, \
2333	    XFT_CHAR_WIDTH, XftTypeInteger, norm->max_advance_width
2334
2335#define ItalXftPattern(norm) \
2336	    XFT_SLANT, XftTypeInteger, XFT_SLANT_ITALIC, \
2337	    XFT_CHAR_WIDTH, XftTypeInteger, norm->max_advance_width
2338
2339#if OPT_WIDE_ATTRS
2340#define HAVE_ITALICS 1
2341#define FIND_ITALICS ((pat = XftNameParse(face_name)) != 0)
2342#elif OPT_ISO_COLORS
2343#define HAVE_ITALICS 1
2344#define FIND_ITALICS (screen->italicULMode && (pat = XftNameParse(face_name)) != 0)
2345#else
2346#define HAVE_ITALICS 0
2347#endif
2348
2349	    if ((pat = XftNameParse(face_name)) != 0) {
2350#define OPEN_XFT(tag) xtermOpenXft(xw, face_name, pat, tag)
2351		XftPatternBuild(pat,
2352				NormXftPattern,
2353				(void *) 0);
2354		norm = OPEN_XFT("normal");
2355
2356		if (norm != 0) {
2357		    XftPatternBuild(pat,
2358				    BoldXftPattern(norm),
2359				    (void *) 0);
2360		    bold = OPEN_XFT("bold");
2361
2362#if HAVE_ITALICS
2363		    if (FIND_ITALICS) {
2364			XftPatternBuild(pat,
2365					NormXftPattern,
2366					ItalXftPattern(norm),
2367					(void *) 0);
2368			ital = OPEN_XFT("italic");
2369		    }
2370#endif
2371#undef OPEN_XFT
2372
2373		    /*
2374		     * FIXME:  just assume that the corresponding font has no
2375		     * graphics characters.
2376		     */
2377		    if (screen->fnt_boxes) {
2378			screen->fnt_boxes = False;
2379			TRACE(("Xft opened - will %suse internal line-drawing characters\n",
2380			       screen->fnt_boxes ? "not " : ""));
2381		    }
2382		}
2383
2384		XftPatternDestroy(pat);
2385	    }
2386
2387	    CACHE_XFT(screen->renderFontNorm, norm);
2388	    CACHE_XFT(screen->renderFontBold, bold);
2389	    CACHE_XFT(screen->renderFontItal, ital);
2390
2391	    /*
2392	     * See xtermXftDrawString().
2393	     */
2394#if OPT_RENDERWIDE
2395	    if (norm != 0 && screen->wide_chars) {
2396		int char_width = norm->max_advance_width * 2;
2397#ifdef FC_ASPECT
2398		double aspect = ((xw->misc.face_wide_name
2399				  || screen->renderFontNorm[fontnum].map.mixed)
2400				 ? 1.0
2401				 : 2.0);
2402#endif
2403
2404		face_name = getFaceName(xw, True);
2405		TRACE(("xtermComputeFontInfo wide(face %s, char_width %d)\n",
2406		       NonNull(face_name),
2407		       char_width));
2408
2409#define WideXftPattern \
2410		XFT_FAMILY, XftTypeString, "mono", \
2411		XFT_SIZE, XftTypeDouble, face_size, \
2412		XFT_SPACING, XftTypeInteger, XFT_MONO
2413
2414		if (face_name && (pat = XftNameParse(face_name)) != 0) {
2415#define OPEN_XFT(tag) xtermOpenXft(xw, face_name, pat, tag)
2416		    XftPatternBuild(pat,
2417				    WideXftPattern,
2418				    XFT_CHAR_WIDTH, XftTypeInteger, char_width,
2419#ifdef FC_ASPECT
2420				    FC_ASPECT, XftTypeDouble, aspect,
2421#endif
2422				    (void *) 0);
2423		    wnorm = OPEN_XFT("wide");
2424
2425		    if (wnorm != 0) {
2426			XftPatternBuild(pat,
2427					WideXftPattern,
2428					BoldXftPattern(wnorm),
2429					(void *) 0);
2430			wbold = OPEN_XFT("wide-bold");
2431
2432#if HAVE_ITALICS
2433			if (FIND_ITALICS) {
2434			    XftPatternBuild(pat,
2435					    WideXftPattern,
2436					    ItalXftPattern(wnorm),
2437					    (void *) 0);
2438			    wital = OPEN_XFT("wide-italic");
2439			}
2440#endif
2441#undef OPEN_XFT
2442		    }
2443		    XftPatternDestroy(pat);
2444		}
2445
2446		CACHE_XFT(screen->renderWideNorm, wnorm);
2447		CACHE_XFT(screen->renderWideBold, wbold);
2448		CACHE_XFT(screen->renderWideItal, wital);
2449	    }
2450#endif /* OPT_RENDERWIDE */
2451	}
2452	if (norm == 0) {
2453	    TRACE(("...no TrueType font found for number %d, disable menu entry\n", fontnum));
2454	    xw->work.render_font = False;
2455	    update_font_renderfont();
2456	    /* now we will fall through into the bitmap fonts */
2457	} else {
2458	    setRenderFontsize(screen, win, norm, NULL);
2459	    setRenderFontsize(screen, win, bold, "bold");
2460	    setRenderFontsize(screen, win, ital, "ital");
2461#if OPT_BOX_CHARS
2462	    setupPackedFonts(xw);
2463
2464	    if (screen->force_packed) {
2465		XTermXftFonts *use = &(screen->renderFontNorm[fontnum]);
2466		SetFontHeight(screen, win, use->font->ascent + use->font->descent);
2467		SetFontWidth(screen, win, use->map.min_width);
2468		TRACE(("...packed TrueType font %dx%d vs %d\n",
2469		       win->f_height,
2470		       win->f_width,
2471		       use->map.max_width));
2472	    }
2473#endif
2474	    DUMP_XFT(xw, &(screen->renderFontNorm[fontnum]));
2475	}
2476    }
2477    /*
2478     * Are we handling a bitmap font?
2479     */
2480    else
2481#endif /* OPT_RENDERFONT */
2482    {
2483	if (is_double_width_font(font) && !(screen->fnt_prop)) {
2484	    SetFontWidth(screen, win, font->min_bounds.width);
2485	} else {
2486	    SetFontWidth(screen, win, font->max_bounds.width);
2487	}
2488	SetFontHeight(screen, win, font->ascent + font->descent);
2489	win->f_ascent = font->ascent;
2490	win->f_descent = font->descent;
2491    }
2492    i = 2 * screen->border + sbwidth;
2493    j = 2 * screen->border;
2494    width = MaxCols(screen) * win->f_width + i;
2495    height = MaxRows(screen) * win->f_height + j;
2496    win->fullwidth = (Dimension) width;
2497    win->fullheight = (Dimension) height;
2498    win->width = width - i;
2499    win->height = height - j;
2500
2501    TRACE(("xtermComputeFontInfo window %dx%d (full %dx%d), fontsize %dx%d (asc %d, dsc %d)\n",
2502	   win->height,
2503	   win->width,
2504	   win->fullheight,
2505	   win->fullwidth,
2506	   win->f_height,
2507	   win->f_width,
2508	   win->f_ascent,
2509	   win->f_descent));
2510
2511    checkFontInfo(win->f_height, "height");
2512    checkFontInfo(win->f_width, "width");
2513}
2514
2515/* save this information as a side-effect for double-sized characters */
2516void
2517xtermSaveFontInfo(TScreen *screen, XFontStruct *font)
2518{
2519    screen->fnt_wide = (Dimension) (font->max_bounds.width);
2520    screen->fnt_high = (Dimension) (font->ascent + font->descent);
2521    TRACE(("xtermSaveFontInfo %dx%d\n", screen->fnt_high, screen->fnt_wide));
2522}
2523
2524/*
2525 * After loading a new font, update the structures that use its size.
2526 */
2527void
2528xtermUpdateFontInfo(XtermWidget xw, Bool doresize)
2529{
2530    TScreen *screen = TScreenOf(xw);
2531
2532    int scrollbar_width;
2533    VTwin *win = &(screen->fullVwin);
2534
2535    scrollbar_width = (xw->misc.scrollbar
2536		       ? (screen->scrollWidget->core.width +
2537			  BorderWidth(screen->scrollWidget))
2538		       : 0);
2539    xtermComputeFontInfo(xw, win, screen->fnts[fNorm].fs, scrollbar_width);
2540    xtermSaveFontInfo(screen, screen->fnts[fNorm].fs);
2541
2542    if (doresize) {
2543	if (VWindow(screen)) {
2544	    xtermClear(xw);
2545	}
2546	TRACE(("xtermUpdateFontInfo {{\n"));
2547	DoResizeScreen(xw);	/* set to the new natural size */
2548	ResizeScrollBar(xw);
2549	Redraw();
2550	TRACE(("... }} xtermUpdateFontInfo\n"));
2551#ifdef SCROLLBAR_RIGHT
2552	updateRightScrollbar(xw);
2553#endif
2554    }
2555    xtermSetCursorBox(screen);
2556}
2557
2558#if OPT_BOX_CHARS || OPT_REPORT_FONTS
2559
2560/*
2561 * Returns true if the given character is missing from the specified font.
2562 */
2563Bool
2564xtermMissingChar(unsigned ch, XTermFonts * font)
2565{
2566    Bool result = False;
2567    XFontStruct *fs = font->fs;
2568    XCharStruct *pc = 0;
2569
2570    if (fs->max_byte1 == 0) {
2571#if OPT_WIDE_CHARS
2572	if (ch < 256)
2573#endif
2574	{
2575	    CI_GET_CHAR_INFO_1D(fs, E2A(ch), pc);
2576	}
2577    }
2578#if OPT_WIDE_CHARS
2579    else {
2580	unsigned row = (ch >> 8);
2581	unsigned col = (ch & 0xff);
2582	CI_GET_CHAR_INFO_2D(fs, row, col, pc);
2583    }
2584#endif
2585
2586    if (pc == 0 || CI_NONEXISTCHAR(pc)) {
2587	TRACE(("xtermMissingChar %#04x (!exists)\n", ch));
2588	result = True;
2589    }
2590    if (ch < KNOWN_MISSING) {
2591	font->known_missing[ch] = (Char) (result ? 2 : 1);
2592    }
2593    return result;
2594}
2595#endif
2596
2597#if OPT_BOX_CHARS
2598/*
2599 * The grid is arbitrary, enough resolution that nothing's lost in
2600 * initialization.
2601 */
2602#define BOX_HIGH 60
2603#define BOX_WIDE 60
2604
2605#define MID_HIGH (BOX_HIGH/2)
2606#define MID_WIDE (BOX_WIDE/2)
2607
2608#define CHR_WIDE ((9*BOX_WIDE)/10)
2609#define CHR_HIGH ((9*BOX_HIGH)/10)
2610
2611/*
2612 * ...since we'll scale the values anyway.
2613 */
2614#define SCALED_X(n) ((int)(n) * (((int) font_width) - 1)) / (BOX_WIDE-1)
2615#define SCALED_Y(n) ((int)(n) * (((int) font_height) - 1)) / (BOX_HIGH-1)
2616#define SCALE_X(n) n = SCALED_X(n)
2617#define SCALE_Y(n) n = SCALED_Y(n)
2618
2619#define SEG(x0,y0,x1,y1) x0,y0, x1,y1
2620
2621/*
2622 * Draw the given graphic character, if it is simple enough (i.e., a
2623 * line-drawing character).
2624 */
2625void
2626xtermDrawBoxChar(XtermWidget xw,
2627		 unsigned ch,
2628		 unsigned attr_flags,
2629		 unsigned draw_flags,
2630		 GC gc,
2631		 int x,
2632		 int y,
2633		 int cells)
2634{
2635    TScreen *screen = TScreenOf(xw);
2636    /* *INDENT-OFF* */
2637    static const short glyph_ht[] = {
2638	SEG(1*BOX_WIDE/10,  0,		1*BOX_WIDE/10,5*MID_HIGH/6),	/* H */
2639	SEG(6*BOX_WIDE/10,  0,		6*BOX_WIDE/10,5*MID_HIGH/6),
2640	SEG(1*BOX_WIDE/10,5*MID_HIGH/12,6*BOX_WIDE/10,5*MID_HIGH/12),
2641	SEG(2*BOX_WIDE/10,  MID_HIGH,	  CHR_WIDE,	MID_HIGH),	/* T */
2642	SEG(6*BOX_WIDE/10,  MID_HIGH,	6*BOX_WIDE/10,	CHR_HIGH),
2643	-1
2644    }, glyph_ff[] = {
2645	SEG(1*BOX_WIDE/10,  0,		6*BOX_WIDE/10,	0),		/* F */
2646	SEG(1*BOX_WIDE/10,5*MID_HIGH/12,6*CHR_WIDE/12,5*MID_HIGH/12),
2647	SEG(1*BOX_WIDE/10,  0,		0*BOX_WIDE/3, 5*MID_HIGH/6),
2648	SEG(1*BOX_WIDE/3,   MID_HIGH,	  CHR_WIDE,	MID_HIGH),	/* F */
2649	SEG(1*BOX_WIDE/3, 8*MID_HIGH/6,10*CHR_WIDE/12,8*MID_HIGH/6),
2650	SEG(1*BOX_WIDE/3,   MID_HIGH,	1*BOX_WIDE/3,	CHR_HIGH),
2651	-1
2652    }, glyph_lf[] = {
2653	SEG(1*BOX_WIDE/10,  0,		1*BOX_WIDE/10,9*MID_HIGH/12),	/* L */
2654	SEG(1*BOX_WIDE/10,9*MID_HIGH/12,6*BOX_WIDE/10,9*MID_HIGH/12),
2655	SEG(1*BOX_WIDE/3,   MID_HIGH,	  CHR_WIDE,	MID_HIGH),	/* F */
2656	SEG(1*BOX_WIDE/3, 8*MID_HIGH/6,10*CHR_WIDE/12,8*MID_HIGH/6),
2657	SEG(1*BOX_WIDE/3,   MID_HIGH,	1*BOX_WIDE/3,	CHR_HIGH),
2658	-1
2659    }, glyph_nl[] = {
2660	SEG(1*BOX_WIDE/10,5*MID_HIGH/6, 1*BOX_WIDE/10,	0),		/* N */
2661	SEG(1*BOX_WIDE/10,  0,		5*BOX_WIDE/6, 5*MID_HIGH/6),
2662	SEG(5*BOX_WIDE/6, 5*MID_HIGH/6, 5*BOX_WIDE/6,	0),
2663	SEG(1*BOX_WIDE/3,   MID_HIGH,	1*BOX_WIDE/3,	CHR_HIGH),	/* L */
2664	SEG(1*BOX_WIDE/3,   CHR_HIGH,	  CHR_WIDE,	CHR_HIGH),
2665	-1
2666    }, glyph_vt[] = {
2667	SEG(1*BOX_WIDE/10,   0,		5*BOX_WIDE/12,5*MID_HIGH/6),	/* V */
2668	SEG(5*BOX_WIDE/12,5*MID_HIGH/6, 5*BOX_WIDE/6,	0),
2669	SEG(2*BOX_WIDE/10,  MID_HIGH,	  CHR_WIDE,	MID_HIGH),	/* T */
2670	SEG(6*BOX_WIDE/10,  MID_HIGH,	6*BOX_WIDE/10,	CHR_HIGH),
2671	-1
2672    }, plus_or_minus[] =
2673    {
2674	SEG(  0,	  5*BOX_HIGH/6,	  CHR_WIDE,   5*BOX_HIGH/6),
2675	SEG(  MID_WIDE,	  2*BOX_HIGH/6,	  MID_WIDE,   4*BOX_HIGH/6),
2676	SEG(  0,	  3*BOX_HIGH/6,	  CHR_WIDE,   3*BOX_HIGH/6),
2677	-1
2678    }, lower_right_corner[] =
2679    {
2680	SEG(  0,	    MID_HIGH,	  MID_WIDE,	MID_HIGH),
2681	SEG(  MID_WIDE,	    MID_HIGH,	  MID_WIDE,	0),
2682	-1
2683    }, upper_right_corner[] =
2684    {
2685	SEG(  0,	    MID_HIGH,	  MID_WIDE,	MID_HIGH),
2686	SEG( MID_WIDE,	    MID_HIGH,	  MID_WIDE,	BOX_HIGH),
2687	-1
2688    }, upper_left_corner[] =
2689    {
2690	SEG(  MID_WIDE,	    MID_HIGH,	  BOX_WIDE,	MID_HIGH),
2691	SEG(  MID_WIDE,	    MID_HIGH,	  MID_WIDE,	BOX_HIGH),
2692	-1
2693    }, lower_left_corner[] =
2694    {
2695	SEG(  MID_WIDE,	    0,		  MID_WIDE,	MID_HIGH),
2696	SEG(  MID_WIDE,	    MID_WIDE,	  BOX_WIDE,	MID_HIGH),
2697	-1
2698    }, cross[] =
2699    {
2700	SEG(  0,	    MID_HIGH,	  BOX_WIDE,	MID_HIGH),
2701	SEG(  MID_WIDE,	    0,		  MID_WIDE,	BOX_HIGH),
2702	-1
2703    }, scan_line_1[] =
2704    {
2705	SEG(  0,	    0,		  BOX_WIDE,	0),
2706	-1
2707    }, scan_line_3[] =
2708    {
2709	SEG(  0,	    BOX_HIGH/4,	  BOX_WIDE,	BOX_HIGH/4),
2710	-1
2711    }, scan_line_7[] =
2712    {
2713	SEG( 0,		    MID_HIGH,	  BOX_WIDE,	MID_HIGH),
2714	-1
2715    }, scan_line_9[] =
2716    {
2717	SEG(  0,	  3*BOX_HIGH/4,	  BOX_WIDE,   3*BOX_HIGH/4),
2718	-1
2719    }, horizontal_line[] =
2720    {
2721	SEG(  0,	    BOX_HIGH,	  BOX_WIDE,	BOX_HIGH),
2722	-1
2723    }, left_tee[] =
2724    {
2725	SEG(  MID_WIDE,	    0,		  MID_WIDE,	BOX_HIGH),
2726	SEG(  MID_WIDE,	    MID_HIGH,	  BOX_WIDE,	MID_HIGH),
2727	-1
2728    }, right_tee[] =
2729    {
2730	SEG(  MID_WIDE,	    0,		  MID_WIDE,	BOX_HIGH),
2731	SEG(  MID_WIDE,	    MID_HIGH,	  0,		MID_HIGH),
2732	-1
2733    }, bottom_tee[] =
2734    {
2735	SEG(  0,	    MID_HIGH,	  BOX_WIDE,	MID_HIGH),
2736	SEG(  MID_WIDE,	    0,		  MID_WIDE,	MID_HIGH),
2737	-1
2738    }, top_tee[] =
2739    {
2740	SEG(  0,	    MID_HIGH,	  BOX_WIDE,	MID_HIGH),
2741	SEG(  MID_WIDE,	    MID_HIGH,	  MID_WIDE,	BOX_HIGH),
2742	-1
2743    }, vertical_line[] =
2744    {
2745	SEG(  MID_WIDE,	    0,		  MID_WIDE,	BOX_HIGH),
2746	-1
2747    }, less_than_or_equal[] =
2748    {
2749	SEG(  CHR_WIDE,	    BOX_HIGH/3,	  0,		MID_HIGH),
2750	SEG(  CHR_WIDE,	  2*BOX_HIGH/3,	  0,		MID_HIGH),
2751	SEG(  0,	  3*BOX_HIGH/4,	  CHR_WIDE,   3*BOX_HIGH/4),
2752	-1
2753    }, greater_than_or_equal[] =
2754    {
2755	SEG(  0,	    BOX_HIGH/3,	  CHR_WIDE,	MID_HIGH),
2756	SEG(  0,	  2*BOX_HIGH/3,	  CHR_WIDE,	MID_HIGH),
2757	SEG(  0,	  3*BOX_HIGH/4,	  CHR_WIDE,   3*BOX_HIGH/4),
2758	-1
2759    }, greek_pi[] =
2760    {
2761	SEG(  0,	    MID_HIGH,	  CHR_WIDE,	MID_HIGH),
2762	SEG(5*CHR_WIDE/6,   MID_HIGH,	5*CHR_WIDE/6,	CHR_HIGH),
2763	SEG(2*CHR_WIDE/6,   MID_HIGH,	2*CHR_WIDE/6,	CHR_HIGH),
2764	-1
2765    }, not_equal_to[] =
2766    {
2767	SEG(2*BOX_WIDE/3, 1*BOX_HIGH/3, 1*BOX_WIDE/3,	CHR_HIGH),
2768	SEG(  0,	  2*BOX_HIGH/3,	  CHR_WIDE,   2*BOX_HIGH/3),
2769	SEG(  0,	    MID_HIGH,	  CHR_WIDE,	MID_HIGH),
2770	-1
2771    };
2772    /* *INDENT-ON* */
2773
2774    static const short *lines[] =
2775    {
2776	0,			/* 00 (unused) */
2777	0,			/* 01 diamond */
2778	0,			/* 02 box */
2779	glyph_ht,		/* 03 HT */
2780	glyph_ff,		/* 04 FF */
2781	0,			/* 05 CR */
2782	glyph_lf,		/* 06 LF */
2783	0,			/* 07 degrees (small circle) */
2784	plus_or_minus,		/* 08 */
2785	glyph_nl,		/* 09 */
2786	glyph_vt,		/* 0A */
2787	lower_right_corner,	/* 0B */
2788	upper_right_corner,	/* 0C */
2789	upper_left_corner,	/* 0D */
2790	lower_left_corner,	/* 0E */
2791	cross,			/* 0F */
2792	scan_line_1,		/* 10 */
2793	scan_line_3,		/* 11 */
2794	scan_line_7,		/* 12 */
2795	scan_line_9,		/* 13 */
2796	horizontal_line,	/* 14 */
2797	left_tee,		/* 15 */
2798	right_tee,		/* 16 */
2799	bottom_tee,		/* 17 */
2800	top_tee,		/* 18 */
2801	vertical_line,		/* 19 */
2802	less_than_or_equal,	/* 1A */
2803	greater_than_or_equal,	/* 1B */
2804	greek_pi,		/* 1C */
2805	not_equal_to,		/* 1D */
2806	0,			/* 1E LB */
2807	0,			/* 1F bullet */
2808    };
2809
2810    GC gc2;
2811    CgsEnum cgsId = (ch == 2) ? gcDots : gcLine;
2812    VTwin *cgsWin = WhichVWin(screen);
2813    const short *p;
2814    unsigned font_width = (unsigned) (((draw_flags & DOUBLEWFONT) ? 2 : 1)
2815				      * screen->fnt_wide);
2816    unsigned font_height = (unsigned) (((draw_flags & DOUBLEHFONT) ? 2 : 1)
2817				       * screen->fnt_high);
2818
2819    if (cells > 1)
2820	font_width *= (unsigned) cells;
2821
2822#if OPT_WIDE_CHARS
2823    /*
2824     * Try to show line-drawing characters if we happen to be in UTF-8
2825     * mode, but have gotten an old-style font.
2826     */
2827    if (screen->utf8_mode
2828#if OPT_RENDERFONT
2829	&& !UsingRenderFont(xw)
2830#endif
2831	&& (ch > 127)
2832	&& (ch != UCS_REPL)) {
2833	unsigned n;
2834	for (n = 1; n < 32; n++) {
2835	    if (dec2ucs(n) == ch
2836		&& !((attr_flags & BOLD)
2837		     ? IsXtermMissingChar(screen, n, &screen->fnts[fBold])
2838		     : IsXtermMissingChar(screen, n, &screen->fnts[fNorm]))) {
2839		TRACE(("...use xterm-style linedrawing\n"));
2840		ch = n;
2841		break;
2842	    }
2843	}
2844    }
2845#endif
2846
2847    TRACE(("DRAW_BOX(%d) cell %dx%d at %d,%d%s\n",
2848	   ch, font_height, font_width, y, x,
2849	   (ch >= (sizeof(lines) / sizeof(lines[0]))
2850	    ? "-BAD"
2851	    : "")));
2852
2853    if (cgsId == gcDots) {
2854	setCgsFont(xw, cgsWin, cgsId, getCgsFont(xw, cgsWin, gc));
2855	setCgsFore(xw, cgsWin, cgsId, getCgsFore(xw, cgsWin, gc));
2856	setCgsBack(xw, cgsWin, cgsId, getCgsBack(xw, cgsWin, gc));
2857    } else {
2858	setCgsFont(xw, cgsWin, cgsId, getCgsFont(xw, cgsWin, gc));
2859	setCgsFore(xw, cgsWin, cgsId, getCgsBack(xw, cgsWin, gc));
2860	setCgsBack(xw, cgsWin, cgsId, getCgsBack(xw, cgsWin, gc));
2861    }
2862    gc2 = getCgsGC(xw, cgsWin, cgsId);
2863
2864    if (!(draw_flags & NOBACKGROUND)) {
2865	XFillRectangle(screen->display, VDrawable(screen), gc2, x, y,
2866		       font_width,
2867		       font_height);
2868    }
2869
2870    setCgsFont(xw, cgsWin, cgsId, getCgsFont(xw, cgsWin, gc));
2871    setCgsFore(xw, cgsWin, cgsId, getCgsFore(xw, cgsWin, gc));
2872    setCgsBack(xw, cgsWin, cgsId, getCgsBack(xw, cgsWin, gc));
2873    gc2 = getCgsGC(xw, cgsWin, cgsId);
2874
2875    XSetLineAttributes(screen->display, gc2,
2876		       (attr_flags & BOLD)
2877		       ? ((font_height > 12)
2878			  ? font_height / 12
2879			  : 1)
2880		       : ((font_height > 16)
2881			  ? font_height / 16
2882			  : 1),
2883		       LineSolid,
2884		       CapProjecting,
2885		       JoinMiter);
2886
2887    if (ch == 1) {		/* diamond */
2888	XPoint points[5];
2889	int npoints = 5, n;
2890
2891	points[0].x = MID_WIDE;
2892	points[0].y = BOX_HIGH / 4;
2893
2894	points[1].x = 8 * BOX_WIDE / 8;
2895	points[1].y = MID_HIGH;
2896
2897	points[2].x = points[0].x;
2898	points[2].y = 3 * BOX_HIGH / 4;
2899
2900	points[3].x = 0 * BOX_WIDE / 8;
2901	points[3].y = points[1].y;
2902
2903	points[4].x = points[0].x;
2904	points[4].y = points[0].y;
2905
2906	for (n = 0; n < npoints; ++n) {
2907	    points[n].x = (short) SCALED_X(points[n].x);
2908	    points[n].y = (short) SCALED_Y(points[n].y);
2909	    points[n].x = (short) (points[n].x + x);
2910	    points[n].y = (short) (points[n].y + y);
2911	}
2912
2913	XFillPolygon(screen->display,
2914		     VDrawable(screen), gc2,
2915		     points, npoints,
2916		     Convex, CoordModeOrigin);
2917    } else if (ch == 7) {	/* degrees */
2918	unsigned width = (BOX_WIDE / 3);
2919	int x_coord = MID_WIDE - (int) (width / 2);
2920	int y_coord = MID_HIGH - (int) width;
2921
2922	SCALE_X(x_coord);
2923	SCALE_Y(y_coord);
2924	width = (unsigned) SCALED_X(width);
2925
2926	XDrawArc(screen->display,
2927		 VDrawable(screen), gc2,
2928		 x + x_coord, y + y_coord, width, width,
2929		 0,
2930		 360 * 64);
2931    } else if (ch == 0x1f) {	/* bullet */
2932	unsigned width = 7 * BOX_WIDE / 10;
2933	int x_coord = MID_WIDE - (int) (width / 3);
2934	int y_coord = MID_HIGH - (int) (width / 3);
2935
2936	SCALE_X(x_coord);
2937	SCALE_Y(y_coord);
2938	width = (unsigned) SCALED_X(width);
2939
2940	XDrawArc(screen->display,
2941		 VDrawable(screen), gc2,
2942		 x + x_coord, y + y_coord, width, width,
2943		 0,
2944		 360 * 64);
2945    } else if (ch < (sizeof(lines) / sizeof(lines[0]))
2946	       && (p = lines[ch]) != 0) {
2947	int coord[4];
2948	int n = 0;
2949	while (*p >= 0) {
2950	    coord[n++] = *p++;
2951	    if (n == 4) {
2952		SCALE_X(coord[0]);
2953		SCALE_Y(coord[1]);
2954		SCALE_X(coord[2]);
2955		SCALE_Y(coord[3]);
2956		XDrawLine(screen->display,
2957			  VDrawable(screen), gc2,
2958			  x + coord[0], y + coord[1],
2959			  x + coord[2], y + coord[3]);
2960		n = 0;
2961	    }
2962	}
2963    } else if (screen->force_all_chars) {
2964	/* bounding rectangle, for debugging */
2965	XDrawRectangle(screen->display, VDrawable(screen), gc2, x, y,
2966		       font_width - 1,
2967		       font_height - 1);
2968    }
2969}
2970#endif /* OPT_BOX_CHARS */
2971
2972#if OPT_RENDERFONT
2973
2974/*
2975 * Check if the given character has a glyph known to Xft.
2976 *
2977 * see xc/lib/Xft/xftglyphs.c
2978 */
2979Bool
2980xtermXftMissing(XtermWidget xw, XftFont *font, unsigned wc)
2981{
2982    Bool result = False;
2983
2984    if (font != 0) {
2985	TScreen *screen = TScreenOf(xw);
2986	if (!XftGlyphExists(screen->display, font, wc)) {
2987#if OPT_WIDE_CHARS
2988	    TRACE(("xtermXftMissing %d (dec=%#x, ucs=%#x)\n",
2989		   wc, ucs2dec(wc), dec2ucs(wc)));
2990#else
2991	    TRACE(("xtermXftMissing %d\n", wc));
2992#endif
2993	    result = True;
2994	}
2995    }
2996    return result;
2997}
2998#endif /* OPT_RENDERFONT */
2999
3000#if OPT_WIDE_CHARS
3001#define MY_UCS(ucs,dec) case ucs: result = dec; break
3002unsigned
3003ucs2dec(unsigned ch)
3004{
3005    unsigned result = ch;
3006    if ((ch > 127)
3007	&& (ch != UCS_REPL)) {
3008	switch (ch) {
3009	    MY_UCS(0x25ae, 0);	/* black vertical rectangle                   */
3010	    MY_UCS(0x25c6, 1);	/* black diamond                              */
3011	    MY_UCS(0x2592, 2);	/* medium shade                               */
3012	    MY_UCS(0x2409, 3);	/* symbol for horizontal tabulation           */
3013	    MY_UCS(0x240c, 4);	/* symbol for form feed                       */
3014	    MY_UCS(0x240d, 5);	/* symbol for carriage return                 */
3015	    MY_UCS(0x240a, 6);	/* symbol for line feed                       */
3016	    MY_UCS(0x00b0, 7);	/* degree sign                                */
3017	    MY_UCS(0x00b1, 8);	/* plus-minus sign                            */
3018	    MY_UCS(0x2424, 9);	/* symbol for newline                         */
3019	    MY_UCS(0x240b, 10);	/* symbol for vertical tabulation             */
3020	    MY_UCS(0x2518, 11);	/* box drawings light up and left             */
3021	    MY_UCS(0x2510, 12);	/* box drawings light down and left           */
3022	    MY_UCS(0x250c, 13);	/* box drawings light down and right          */
3023	    MY_UCS(0x2514, 14);	/* box drawings light up and right            */
3024	    MY_UCS(0x253c, 15);	/* box drawings light vertical and horizontal */
3025	    MY_UCS(0x23ba, 16);	/* box drawings scan 1                        */
3026	    MY_UCS(0x23bb, 17);	/* box drawings scan 3                        */
3027	    MY_UCS(0x2500, 18);	/* box drawings light horizontal              */
3028	    MY_UCS(0x23bc, 19);	/* box drawings scan 7                        */
3029	    MY_UCS(0x23bd, 20);	/* box drawings scan 9                        */
3030	    MY_UCS(0x251c, 21);	/* box drawings light vertical and right      */
3031	    MY_UCS(0x2524, 22);	/* box drawings light vertical and left       */
3032	    MY_UCS(0x2534, 23);	/* box drawings light up and horizontal       */
3033	    MY_UCS(0x252c, 24);	/* box drawings light down and horizontal     */
3034	    MY_UCS(0x2502, 25);	/* box drawings light vertical                */
3035	    MY_UCS(0x2264, 26);	/* less-than or equal to                      */
3036	    MY_UCS(0x2265, 27);	/* greater-than or equal to                   */
3037	    MY_UCS(0x03c0, 28);	/* greek small letter pi                      */
3038	    MY_UCS(0x2260, 29);	/* not equal to                               */
3039	    MY_UCS(0x00a3, 30);	/* pound sign                                 */
3040	    MY_UCS(0x00b7, 31);	/* middle dot                                 */
3041	}
3042    }
3043    return result;
3044}
3045
3046#undef  MY_UCS
3047#define MY_UCS(ucs,dec) case dec: result = ucs; break
3048
3049unsigned
3050dec2ucs(unsigned ch)
3051{
3052    unsigned result = ch;
3053    if (xtermIsDecGraphic(ch)) {
3054	switch (ch) {
3055	    MY_UCS(0x25ae, 0);	/* black vertical rectangle                   */
3056	    MY_UCS(0x25c6, 1);	/* black diamond                              */
3057	    MY_UCS(0x2592, 2);	/* medium shade                               */
3058	    MY_UCS(0x2409, 3);	/* symbol for horizontal tabulation           */
3059	    MY_UCS(0x240c, 4);	/* symbol for form feed                       */
3060	    MY_UCS(0x240d, 5);	/* symbol for carriage return                 */
3061	    MY_UCS(0x240a, 6);	/* symbol for line feed                       */
3062	    MY_UCS(0x00b0, 7);	/* degree sign                                */
3063	    MY_UCS(0x00b1, 8);	/* plus-minus sign                            */
3064	    MY_UCS(0x2424, 9);	/* symbol for newline                         */
3065	    MY_UCS(0x240b, 10);	/* symbol for vertical tabulation             */
3066	    MY_UCS(0x2518, 11);	/* box drawings light up and left             */
3067	    MY_UCS(0x2510, 12);	/* box drawings light down and left           */
3068	    MY_UCS(0x250c, 13);	/* box drawings light down and right          */
3069	    MY_UCS(0x2514, 14);	/* box drawings light up and right            */
3070	    MY_UCS(0x253c, 15);	/* box drawings light vertical and horizontal */
3071	    MY_UCS(0x23ba, 16);	/* box drawings scan 1                        */
3072	    MY_UCS(0x23bb, 17);	/* box drawings scan 3                        */
3073	    MY_UCS(0x2500, 18);	/* box drawings light horizontal              */
3074	    MY_UCS(0x23bc, 19);	/* box drawings scan 7                        */
3075	    MY_UCS(0x23bd, 20);	/* box drawings scan 9                        */
3076	    MY_UCS(0x251c, 21);	/* box drawings light vertical and right      */
3077	    MY_UCS(0x2524, 22);	/* box drawings light vertical and left       */
3078	    MY_UCS(0x2534, 23);	/* box drawings light up and horizontal       */
3079	    MY_UCS(0x252c, 24);	/* box drawings light down and horizontal     */
3080	    MY_UCS(0x2502, 25);	/* box drawings light vertical                */
3081	    MY_UCS(0x2264, 26);	/* less-than or equal to                      */
3082	    MY_UCS(0x2265, 27);	/* greater-than or equal to                   */
3083	    MY_UCS(0x03c0, 28);	/* greek small letter pi                      */
3084	    MY_UCS(0x2260, 29);	/* not equal to                               */
3085	    MY_UCS(0x00a3, 30);	/* pound sign                                 */
3086	    MY_UCS(0x00b7, 31);	/* middle dot                                 */
3087	}
3088    }
3089    return result;
3090}
3091
3092#endif /* OPT_WIDE_CHARS */
3093
3094#if OPT_RENDERFONT || OPT_SHIFT_FONTS
3095static int
3096lookupOneFontSize(XtermWidget xw, int fontnum)
3097{
3098    TScreen *screen = TScreenOf(xw);
3099
3100    if (screen->menu_font_sizes[fontnum] == 0) {
3101	XTermFonts fnt;
3102
3103	memset(&fnt, 0, sizeof(fnt));
3104	screen->menu_font_sizes[fontnum] = -1;
3105	if (xtermOpenFont(xw,
3106			  screen->MenuFontName(fontnum),
3107			  &fnt,
3108			  ((fontnum <= fontMenu_lastBuiltin)
3109			   ? fwAlways
3110			   : fwResource),
3111			  True)) {
3112	    if (fontnum <= fontMenu_lastBuiltin
3113		|| strcmp(fnt.fn, DEFFONT)) {
3114		screen->menu_font_sizes[fontnum] = FontSize(fnt.fs);
3115		if (screen->menu_font_sizes[fontnum] <= 0)
3116		    screen->menu_font_sizes[fontnum] = -1;
3117	    }
3118	    xtermCloseFont(xw, &fnt);
3119	}
3120    }
3121    return (screen->menu_font_sizes[fontnum] > 0);
3122}
3123
3124/*
3125 * Cache the font-sizes so subsequent larger/smaller font actions will go fast.
3126 */
3127static void
3128lookupFontSizes(XtermWidget xw)
3129{
3130    int n;
3131
3132    for (n = 0; n < NMENUFONTS; n++) {
3133	(void) lookupOneFontSize(xw, n);
3134    }
3135}
3136#endif /* OPT_RENDERFONT || OPT_SHIFT_FONTS */
3137
3138#if OPT_RENDERFONT
3139static double
3140defaultFaceSize(void)
3141{
3142    double result;
3143    float value;
3144
3145    if (sscanf(DEFFACESIZE, "%f", &value) == 1)
3146	result = value;
3147    else
3148	result = 14.0;
3149    return result;
3150}
3151
3152static void
3153fillInFaceSize(XtermWidget xw, int fontnum)
3154{
3155    TScreen *screen = TScreenOf(xw);
3156    double face_size = xw->misc.face_size[fontnum];
3157
3158    if (face_size <= 0.0) {
3159#if OPT_SHIFT_FONTS
3160	/*
3161	 * If the user is switching font-sizes, make it follow by
3162	 * default the same ratios to the default as the fixed fonts
3163	 * would, for easy comparison.  There will be some differences
3164	 * since the fixed fonts have a variety of height/width ratios,
3165	 * but this is simpler than adding another resource value - and
3166	 * as noted above, the data for the fixed fonts are available.
3167	 */
3168	(void) lookupOneFontSize(xw, 0);
3169	if (fontnum == fontMenu_default) {
3170	    face_size = defaultFaceSize();
3171	} else if (lookupOneFontSize(xw, fontnum)
3172		   && (screen->menu_font_sizes[0]
3173		       != screen->menu_font_sizes[fontnum])) {
3174	    double ratio;
3175	    long num = screen->menu_font_sizes[fontnum];
3176	    long den = screen->menu_font_sizes[0];
3177
3178	    if (den <= 0)
3179		den = 1;
3180	    ratio = dimSquareRoot((double) num / (double) den);
3181
3182	    face_size = (ratio * xw->misc.face_size[0]);
3183	    TRACE(("scaled[%d] using %3ld/%ld = %.2f -> %f\n",
3184		   fontnum, num, den, ratio, face_size));
3185	} else
3186#endif
3187	{
3188#define LikeBitmap(s) (((s) / 78.0) * xw->misc.face_size[fontMenu_default])
3189	    switch (fontnum) {
3190	    case fontMenu_font1:
3191		face_size = LikeBitmap(2.0);
3192		break;
3193	    case fontMenu_font2:
3194		face_size = LikeBitmap(35.0);
3195		break;
3196	    case fontMenu_font3:
3197		face_size = LikeBitmap(60.0);
3198		break;
3199	    default:
3200		face_size = defaultFaceSize();
3201		break;
3202	    case fontMenu_font4:
3203		face_size = LikeBitmap(90.0);
3204		break;
3205	    case fontMenu_font5:
3206		face_size = LikeBitmap(135.0);
3207		break;
3208	    case fontMenu_font6:
3209		face_size = LikeBitmap(200.0);
3210		break;
3211	    }
3212	    TRACE(("builtin[%d] -> %f\n", fontnum, face_size));
3213	}
3214	xw->misc.face_size[fontnum] = (float) face_size;
3215    }
3216}
3217
3218/* no selection or escape */
3219#define NMENU_RENDERFONTS (fontMenu_lastBuiltin + 1)
3220
3221/*
3222 * Workaround for breakage in font-packages - check if all of the bitmap font
3223 * sizes are the same, and if we're using TrueType fonts.
3224 */
3225static Boolean
3226useFaceSizes(XtermWidget xw)
3227{
3228    Boolean result = False;
3229    int n;
3230
3231    TRACE(("useFaceSizes {{\n"));
3232    if (UsingRenderFont(xw)) {
3233	Boolean nonzero = True;
3234
3235	for (n = 0; n < NMENU_RENDERFONTS; ++n) {
3236	    if (xw->misc.face_size[n] <= 0.0) {
3237		nonzero = False;
3238		break;
3239	    }
3240	}
3241	if (!nonzero) {
3242	    Boolean broken_fonts = True;
3243	    TScreen *screen = TScreenOf(xw);
3244	    long first;
3245
3246	    lookupFontSizes(xw);
3247	    first = screen->menu_font_sizes[0];
3248	    for (n = 0; n < NMENUFONTS; n++) {
3249		if (screen->menu_font_sizes[n] > 0
3250		    && screen->menu_font_sizes[n] != first) {
3251		    broken_fonts = False;
3252		    break;
3253		}
3254	    }
3255
3256	    if (broken_fonts) {
3257
3258		TRACE(("bitmap fonts are broken - set faceSize resources\n"));
3259		for (n = 0; n < NMENUFONTS; n++) {
3260		    fillInFaceSize(xw, n);
3261		}
3262
3263	    }
3264	}
3265	result = True;
3266    }
3267    TRACE(("...}}useFaceSizes %d\n", result));
3268    return result;
3269}
3270#endif /* OPT_RENDERFONT */
3271
3272#if OPT_SHIFT_FONTS
3273/*
3274 * Find the index of a larger/smaller font (according to the sign of 'relative'
3275 * and its magnitude), starting from the 'old' index.
3276 */
3277int
3278lookupRelativeFontSize(XtermWidget xw, int old, int relative)
3279{
3280    TScreen *screen = TScreenOf(xw);
3281    int n, m = -1;
3282
3283    TRACE(("lookupRelativeFontSize(old=%d, relative=%d)\n", old, relative));
3284    if (!IsIcon(screen)) {
3285#if OPT_RENDERFONT
3286	if (useFaceSizes(xw)) {
3287	    TRACE(("...using FaceSize\n"));
3288	    if (relative != 0) {
3289		for (n = 0; n < NMENU_RENDERFONTS; ++n) {
3290		    fillInFaceSize(xw, n);
3291		    if (xw->misc.face_size[n] > 0 &&
3292			xw->misc.face_size[n] != xw->misc.face_size[old]) {
3293			int cmp_0 = ((xw->misc.face_size[n] >
3294				      xw->misc.face_size[old])
3295				     ? relative
3296				     : -relative);
3297			int cmp_m = ((m < 0)
3298				     ? 1
3299				     : ((xw->misc.face_size[n] <
3300					 xw->misc.face_size[m])
3301					? relative
3302					: -relative));
3303			if (cmp_0 > 0 && cmp_m > 0) {
3304			    m = n;
3305			}
3306		    }
3307		}
3308	    }
3309	} else
3310#endif
3311	{
3312	    TRACE(("...using bitmap areas\n"));
3313	    lookupFontSizes(xw);
3314	    if (relative != 0) {
3315		for (n = 0; n < NMENUFONTS; ++n) {
3316		    if (screen->menu_font_sizes[n] > 0 &&
3317			screen->menu_font_sizes[n] !=
3318			screen->menu_font_sizes[old]) {
3319			int cmp_0 = ((screen->menu_font_sizes[n] >
3320				      screen->menu_font_sizes[old])
3321				     ? relative
3322				     : -relative);
3323			int cmp_m = ((m < 0)
3324				     ? 1
3325				     : ((screen->menu_font_sizes[n] <
3326					 screen->menu_font_sizes[m])
3327					? relative
3328					: -relative));
3329			if (cmp_0 > 0 && cmp_m > 0) {
3330			    m = n;
3331			}
3332		    }
3333		}
3334	    }
3335	}
3336	TRACE(("...new index %d\n", m));
3337	if (m >= 0) {
3338	    if (relative > 1)
3339		m = lookupRelativeFontSize(xw, m, relative - 1);
3340	    else if (relative < -1)
3341		m = lookupRelativeFontSize(xw, m, relative + 1);
3342	}
3343    }
3344    return m;
3345}
3346
3347/* ARGSUSED */
3348void
3349HandleLargerFont(Widget w GCC_UNUSED,
3350		 XEvent *event GCC_UNUSED,
3351		 String *params GCC_UNUSED,
3352		 Cardinal *param_count GCC_UNUSED)
3353{
3354    XtermWidget xw;
3355
3356    TRACE(("Handle larger-vt-font for %p\n", (void *) w));
3357    if ((xw = getXtermWidget(w)) != 0) {
3358	if (xw->misc.shift_fonts) {
3359	    TScreen *screen = TScreenOf(xw);
3360	    int m;
3361
3362	    m = lookupRelativeFontSize(xw, screen->menu_font_number, 1);
3363	    if (m >= 0) {
3364		SetVTFont(xw, m, True, NULL);
3365	    } else {
3366		Bell(xw, XkbBI_MinorError, 0);
3367	    }
3368	}
3369    }
3370}
3371
3372/* ARGSUSED */
3373void
3374HandleSmallerFont(Widget w GCC_UNUSED,
3375		  XEvent *event GCC_UNUSED,
3376		  String *params GCC_UNUSED,
3377		  Cardinal *param_count GCC_UNUSED)
3378{
3379    XtermWidget xw;
3380
3381    TRACE(("Handle smaller-vt-font for %p\n", (void *) w));
3382    if ((xw = getXtermWidget(w)) != 0) {
3383	if (xw->misc.shift_fonts) {
3384	    TScreen *screen = TScreenOf(xw);
3385	    int m;
3386
3387	    m = lookupRelativeFontSize(xw, screen->menu_font_number, -1);
3388	    if (m >= 0) {
3389		SetVTFont(xw, m, True, NULL);
3390	    } else {
3391		Bell(xw, XkbBI_MinorError, 0);
3392	    }
3393	}
3394    }
3395}
3396#endif /* OPT_SHIFT_FONTS */
3397
3398int
3399xtermGetFont(const char *param)
3400{
3401    int fontnum;
3402
3403    switch (param[0]) {
3404    case 'd':
3405    case 'D':
3406    case '0':
3407	fontnum = fontMenu_default;
3408	break;
3409    case '1':
3410	fontnum = fontMenu_font1;
3411	break;
3412    case '2':
3413	fontnum = fontMenu_font2;
3414	break;
3415    case '3':
3416	fontnum = fontMenu_font3;
3417	break;
3418    case '4':
3419	fontnum = fontMenu_font4;
3420	break;
3421    case '5':
3422	fontnum = fontMenu_font5;
3423	break;
3424    case '6':
3425	fontnum = fontMenu_font6;
3426	break;
3427    case 'e':
3428    case 'E':
3429	fontnum = fontMenu_fontescape;
3430	break;
3431    case 's':
3432    case 'S':
3433	fontnum = fontMenu_fontsel;
3434	break;
3435    default:
3436	fontnum = -1;
3437	break;
3438    }
3439    return fontnum;
3440}
3441
3442/* ARGSUSED */
3443void
3444HandleSetFont(Widget w GCC_UNUSED,
3445	      XEvent *event GCC_UNUSED,
3446	      String *params,
3447	      Cardinal *param_count)
3448{
3449    XtermWidget xw;
3450
3451    if ((xw = getXtermWidget(w)) != 0) {
3452	int fontnum;
3453	VTFontNames fonts;
3454
3455	memset(&fonts, 0, sizeof(fonts));
3456
3457	if (*param_count == 0) {
3458	    fontnum = fontMenu_default;
3459	} else {
3460	    Cardinal maxparams = 1;	/* total number of params allowed */
3461	    int result = xtermGetFont(params[0]);
3462
3463	    switch (result) {
3464	    case fontMenu_default:	/* FALLTHRU */
3465	    case fontMenu_font1:	/* FALLTHRU */
3466	    case fontMenu_font2:	/* FALLTHRU */
3467	    case fontMenu_font3:	/* FALLTHRU */
3468	    case fontMenu_font4:	/* FALLTHRU */
3469	    case fontMenu_font5:	/* FALLTHRU */
3470	    case fontMenu_font6:	/* FALLTHRU */
3471		break;
3472	    case fontMenu_fontescape:
3473#if OPT_WIDE_CHARS
3474		maxparams = 5;
3475#else
3476		maxparams = 3;
3477#endif
3478		break;
3479	    case fontMenu_fontsel:
3480		maxparams = 2;
3481		break;
3482	    default:
3483		Bell(xw, XkbBI_MinorError, 0);
3484		return;
3485	    }
3486	    fontnum = result;
3487
3488	    if (*param_count > maxparams) {	/* see if extra args given */
3489		Bell(xw, XkbBI_MinorError, 0);
3490		return;
3491	    }
3492	    switch (*param_count) {	/* assign 'em */
3493#if OPT_WIDE_CHARS
3494	    case 5:
3495		fonts.f_wb = params[4];
3496		/* FALLTHRU */
3497	    case 4:
3498		fonts.f_w = params[3];
3499		/* FALLTHRU */
3500#endif
3501	    case 3:
3502		fonts.f_b = params[2];
3503		/* FALLTHRU */
3504	    case 2:
3505		fonts.f_n = params[1];
3506		break;
3507	    }
3508	}
3509
3510	SetVTFont(xw, fontnum, True, &fonts);
3511    }
3512}
3513
3514void
3515SetVTFont(XtermWidget xw,
3516	  int which,
3517	  Bool doresize,
3518	  const VTFontNames * fonts)
3519{
3520    TScreen *screen = TScreenOf(xw);
3521
3522    TRACE(("SetVTFont(which=%d, f_n=%s, f_b=%s)\n", which,
3523	   (fonts && fonts->f_n) ? fonts->f_n : "<null>",
3524	   (fonts && fonts->f_b) ? fonts->f_b : "<null>"));
3525
3526    if (IsIcon(screen)) {
3527	Bell(xw, XkbBI_MinorError, 0);
3528    } else if (which >= 0 && which < NMENUFONTS) {
3529	VTFontNames myfonts;
3530
3531	memset(&myfonts, 0, sizeof(myfonts));
3532	if (fonts != 0)
3533	    myfonts = *fonts;
3534
3535	if (which == fontMenu_fontsel) {	/* go get the selection */
3536	    FindFontSelection(xw, myfonts.f_n, False);
3537	} else {
3538	    int oldFont = screen->menu_font_number;
3539
3540#define USE_CACHED(field, name) \
3541	    if (myfonts.field == 0) { \
3542		myfonts.field = x_strdup(screen->menu_font_names[which][name]); \
3543		TRACE(("set myfonts." #field " from menu_font_names[%d][" #name "] %s\n", \
3544		       which, NonNull(myfonts.field))); \
3545	    } else { \
3546		TRACE(("set myfonts." #field " reused\n")); \
3547	    }
3548#define SAVE_FNAME(field, name) \
3549	    if (myfonts.field != 0) { \
3550		if (screen->menu_font_names[which][name] == 0 \
3551		 || strcmp(screen->menu_font_names[which][name], myfonts.field)) { \
3552		    TRACE(("updating menu_font_names[%d][" #name "] to %s\n", \
3553			   which, myfonts.field)); \
3554		    FREE_STRING(screen->menu_font_names[which][name]); \
3555		    screen->menu_font_names[which][name] = x_strdup(myfonts.field); \
3556		} \
3557	    }
3558
3559	    USE_CACHED(f_n, fNorm);
3560	    USE_CACHED(f_b, fBold);
3561#if OPT_WIDE_CHARS
3562	    USE_CACHED(f_w, fWide);
3563	    USE_CACHED(f_wb, fWBold);
3564#endif
3565	    if (xtermLoadFont(xw,
3566			      &myfonts,
3567			      doresize, which)) {
3568		/*
3569		 * If successful, save the data so that a subsequent query via
3570		 * OSC-50 will return the expected values.
3571		 */
3572		SAVE_FNAME(f_n, fNorm);
3573		SAVE_FNAME(f_b, fBold);
3574#if OPT_WIDE_CHARS
3575		SAVE_FNAME(f_w, fWide);
3576		SAVE_FNAME(f_wb, fWBold);
3577#endif
3578	    } else {
3579		(void) xtermLoadFont(xw,
3580				     xtermFontName(screen->MenuFontName(oldFont)),
3581				     doresize, oldFont);
3582		Bell(xw, XkbBI_MinorError, 0);
3583	    }
3584	    FREE_FNAME(f_n);
3585	    FREE_FNAME(f_b);
3586#if OPT_WIDE_CHARS
3587	    FREE_FNAME(f_w);
3588	    FREE_FNAME(f_wb);
3589#endif
3590	}
3591    } else {
3592	Bell(xw, XkbBI_MinorError, 0);
3593    }
3594    return;
3595}
3596