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