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