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