fontutils.c revision d522f475
1/* $XTermId: fontutils.c,v 1.272 2008/04/17 23:23:37 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 Bool
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 = char2lower(*pattern++);
509	    int m = char2lower(*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	    && islower(CharOf(myClass[0])))
1306	    myClass[0] = toupper(CharOf(myClass[0]));
1307
1308	if (xtermLoadVTFonts(xw, myName, myClass)) {
1309	    /*
1310	     * When switching fonts, try to preserve the font-menu selection, since
1311	     * it is less surprising to do that (if the font-switching can be
1312	     * undone) than to switch to "Default".
1313	     */
1314	    int font_number = xw->screen.menu_font_number;
1315	    if (font_number > fontMenu_lastBuiltin)
1316		font_number = fontMenu_lastBuiltin;
1317	    for (n = 0; n < NMENUFONTS; ++n)
1318		xw->screen.menu_font_sizes[n] = 0;
1319	    SetVTFont(xw, font_number, True,
1320		      ((font_number == fontMenu_default)
1321		       ? &(xw->misc.default_font)
1322		       : NULL));
1323	}
1324
1325	MyStackFree(myClass, buf);
1326    }
1327}
1328#endif /* OPT_LOAD_VTFONTS */
1329
1330/*
1331 * Set the limits for the box that outlines the cursor.
1332 */
1333void
1334xtermSetCursorBox(TScreen * screen)
1335{
1336    static XPoint VTbox[NBOX];
1337    XPoint *vp;
1338
1339    vp = &VTbox[1];
1340    (vp++)->x = FontWidth(screen) - 1;
1341    (vp++)->y = FontHeight(screen) - 1;
1342    (vp++)->x = -(FontWidth(screen) - 1);
1343    vp->y = -(FontHeight(screen) - 1);
1344    screen->box = VTbox;
1345}
1346
1347#define CACHE_XFT(dst,src) if (src != 0) {\
1348	    dst[fontnum] = src;\
1349	    TRACE(("%s[%d] = %d (%d,%d) by %d\n",\
1350		#dst,\
1351	    	fontnum,\
1352		src->height,\
1353		src->ascent,\
1354		src->descent,\
1355		src->max_advance_width));\
1356	}
1357
1358#if OPT_RENDERFONT
1359static XftFont *
1360xtermOpenXft(Display * dpy, XftPattern * pat, const char *tag GCC_UNUSED)
1361{
1362    XftPattern *match;
1363    XftResult status;
1364    XftFont *result = 0;
1365
1366    if (pat != 0) {
1367	match = XftFontMatch(dpy, DefaultScreen(dpy), pat, &status);
1368	if (match != 0) {
1369	    result = XftFontOpenPattern(dpy, match);
1370	    if (result != 0) {
1371		TRACE(("...matched %s font\n", tag));
1372	    } else {
1373		TRACE(("...could did not open %s font\n", tag));
1374		XftPatternDestroy(match);
1375	    }
1376	} else {
1377	    TRACE(("...did not match %s font\n", tag));
1378	}
1379    }
1380    return result;
1381}
1382#endif
1383
1384#if OPT_RENDERFONT
1385#if OPT_SHIFT_FONTS
1386/*
1387 * Don't make a dependency on the math library for a single function.
1388 * (Newton Raphson).
1389 */
1390static double
1391mySquareRoot(double value)
1392{
1393    double result = 0.0;
1394    if (value > 0.0) {
1395	int n;
1396	double older = value;
1397	for (n = 0; n < 10; ++n) {
1398	    double delta = (older * older - value) / (2.0 * older);
1399	    double newer = older - delta;
1400	    older = newer;
1401	    result = newer;
1402	    if (delta > -0.001 && delta < 0.001)
1403		break;
1404	}
1405    }
1406    return result;
1407}
1408#endif
1409
1410/*
1411 * Given the Xft font metrics, determine the actual font size.  This is used
1412 * for each font to ensure that normal, bold and italic fonts follow the same
1413 * rule.
1414 */
1415static void
1416setRenderFontsize(TScreen * screen, VTwin * win, XftFont * font, const char *tag)
1417{
1418    if (font != 0) {
1419	int width, height, ascent, descent;
1420
1421	(void) screen;
1422
1423	width = font->max_advance_width;
1424	height = font->height;
1425	ascent = font->ascent;
1426	descent = font->descent;
1427	if (height < ascent + descent) {
1428	    TRACE(("...increase height from %d\n", height));
1429	    height = ascent + descent;
1430	}
1431	if (is_double_width_font_xft(screen->display, font)) {
1432	    TRACE(("...reduced width from %d\n", width));
1433	    width >>= 1;
1434	}
1435	if (tag == 0) {
1436	    win->f_width = width;
1437	    win->f_height = height;
1438	    win->f_ascent = ascent;
1439	    win->f_descent = descent;
1440	    TRACE(("setRenderFontsize result %dx%d (%d+%d)\n",
1441		   width, height, ascent, descent));
1442	} else if (win->f_width < width ||
1443		   win->f_height < height ||
1444		   win->f_ascent < ascent ||
1445		   win->f_descent < descent) {
1446	    TRACE(("setRenderFontsize %s changed %dx%d (%d+%d) to %dx%d (%d+%d)\n",
1447		   tag,
1448		   win->f_width, win->f_height, win->f_ascent, win->f_descent,
1449		   width, height, ascent, descent));
1450
1451	    win->f_width = width;
1452	    win->f_height = height;
1453	    win->f_ascent = ascent;
1454	    win->f_descent = descent;
1455	} else {
1456	    TRACE(("setRenderFontsize %s unchanged\n", tag));
1457	}
1458    }
1459}
1460#endif
1461
1462/*
1463 * Compute useful values for the font/window sizes
1464 */
1465void
1466xtermComputeFontInfo(XtermWidget xw,
1467		     VTwin * win,
1468		     XFontStruct * font,
1469		     int sbwidth)
1470{
1471    TScreen *screen = &(xw->screen);
1472
1473    int i, j, width, height;
1474
1475#if OPT_RENDERFONT
1476    /*
1477     * xterm contains a lot of references to fonts, assuming they are fixed
1478     * size.  This chunk of code overrides the actual font-selection (see
1479     * drawXtermText()), if the user has selected render-font.  All of the
1480     * font-loading for fixed-fonts still goes on whether or not this chunk
1481     * overrides it.
1482     */
1483    if (xw->misc.render_font && !IsIconWin(screen, win)) {
1484	Display *dpy = screen->display;
1485	int fontnum = screen->menu_font_number;
1486	XftFont *norm = screen->renderFontNorm[fontnum];
1487	XftFont *bold = screen->renderFontBold[fontnum];
1488	XftFont *ital = screen->renderFontItal[fontnum];
1489#if OPT_RENDERWIDE
1490	XftFont *wnorm = screen->renderWideNorm[fontnum];
1491	XftFont *wbold = screen->renderWideBold[fontnum];
1492	XftFont *wital = screen->renderWideItal[fontnum];
1493#endif
1494
1495	if (norm == 0 && xw->misc.face_name) {
1496	    XftPattern *pat;
1497	    double face_size = xw->misc.face_size[fontnum];
1498
1499	    TRACE(("xtermComputeFontInfo norm(face %s, size %f)\n",
1500		   xw->misc.face_name,
1501		   xw->misc.face_size[fontnum]));
1502
1503	    if (face_size <= 0.0) {
1504#if OPT_SHIFT_FONTS
1505		/*
1506		 * If the user is switching font-sizes, make it follow by
1507		 * default the same ratios to the default as the fixed fonts
1508		 * would, for easy comparison.  There will be some differences
1509		 * since the fixed fonts have a variety of height/width ratios,
1510		 * but this is simpler than adding another resource value - and
1511		 * as noted above, the data for the fixed fonts are available.
1512		 */
1513		lookupOneFontSize(xw, 0);
1514		lookupOneFontSize(xw, fontnum);
1515		if (fontnum == fontMenu_default) {
1516		    face_size = 14.0;
1517		} else {
1518		    double ratio;
1519		    int num = screen->menu_font_sizes[fontnum];
1520		    int den = screen->menu_font_sizes[0];
1521
1522		    if (den <= 0)
1523			den = 1;
1524		    ratio = mySquareRoot((1.0 * num) / den);
1525
1526		    face_size = (ratio * xw->misc.face_size[0]);
1527		    TRACE(("scaled using %3d/%d = %.2f -> %f\n",
1528			   num, den, ratio, face_size));
1529		}
1530#else
1531		switch (fontnum) {
1532		case fontMenu_font1:
1533		    face_size = 8.0;
1534		    break;
1535		case fontMenu_font2:
1536		    face_size = 10.0;
1537		    break;
1538		case fontMenu_font3:
1539		    face_size = 12.0;
1540		    break;
1541		default:
1542		    face_size = 14.0;
1543		    break;
1544		case fontMenu_font4:
1545		    face_size = 16.0;
1546		    break;
1547		case fontMenu_font5:
1548		    face_size = 18.0;
1549		    break;
1550		case fontMenu_font6:
1551		    face_size = 20.0;
1552		    break;
1553		}
1554#endif
1555		xw->misc.face_size[fontnum] = face_size;
1556	    }
1557
1558	    /*
1559	     * By observation (there is no documentation), XftPatternBuild is
1560	     * cumulative.  Build the bold- and italic-patterns on top of the
1561	     * normal pattern.
1562	     */
1563#define NormXftPattern \
1564	    XFT_FAMILY, XftTypeString, "mono", \
1565	    XFT_SIZE, XftTypeDouble, face_size, \
1566	    XFT_SPACING, XftTypeInteger, XFT_MONO
1567
1568#define BoldXftPattern(norm) \
1569	    XFT_WEIGHT, XftTypeInteger, XFT_WEIGHT_BOLD, \
1570	    XFT_CHAR_WIDTH, XftTypeInteger, norm->max_advance_width
1571
1572#define ItalXftPattern(norm) \
1573	    XFT_SLANT, XftTypeInteger, XFT_SLANT_ITALIC, \
1574	    XFT_CHAR_WIDTH, XftTypeInteger, norm->max_advance_width
1575
1576	    if ((pat = XftNameParse(xw->misc.face_name)) != 0) {
1577		XftPatternBuild(pat,
1578				NormXftPattern,
1579				(void *) 0);
1580		norm = xtermOpenXft(dpy, pat, "normal");
1581
1582		if (norm != 0) {
1583		    XftPatternBuild(pat,
1584				    BoldXftPattern(norm),
1585				    (void *) 0);
1586		    bold = xtermOpenXft(dpy, pat, "bold");
1587
1588#if OPT_ISO_COLORS
1589		    if (screen->italicULMode
1590			&& (pat = XftNameParse(xw->misc.face_name)) != 0) {
1591			XftPatternBuild(pat,
1592					NormXftPattern,
1593					ItalXftPattern(norm),
1594					(void *) 0);
1595			ital = xtermOpenXft(dpy, pat, "italic");
1596		    }
1597#endif /* OPT_ISO_COLORS */
1598
1599		    /*
1600		     * FIXME:  just assume that the corresponding font has no
1601		     * graphics characters.
1602		     */
1603		    if (screen->fnt_boxes) {
1604			screen->fnt_boxes = False;
1605			TRACE(("Xft opened - will %suse internal line-drawing characters\n",
1606			       screen->fnt_boxes ? "not " : ""));
1607		    }
1608		}
1609
1610		XftPatternDestroy(pat);
1611	    }
1612
1613	    CACHE_XFT(screen->renderFontNorm, norm);
1614	    CACHE_XFT(screen->renderFontBold, bold);
1615	    CACHE_XFT(screen->renderFontItal, ital);
1616
1617	    /*
1618	     * See xtermXftDrawString().
1619	     */
1620#if OPT_RENDERWIDE
1621	    if (norm != 0 && screen->wide_chars) {
1622		char *face_name = (xw->misc.face_wide_name
1623				   ? xw->misc.face_wide_name
1624				   : xw->misc.face_name);
1625		int char_width = norm->max_advance_width * 2;
1626
1627		TRACE(("xtermComputeFontInfo wide(face %s, char_width %d)\n",
1628		       face_name,
1629		       char_width));
1630
1631#define WideXftPattern \
1632		XFT_FAMILY, XftTypeString, "mono", \
1633		XFT_SIZE, XftTypeDouble, face_size, \
1634		XFT_SPACING, XftTypeInteger, XFT_MONO
1635
1636		if ((pat = XftNameParse(face_name)) != 0) {
1637		    XftPatternBuild(pat,
1638				    WideXftPattern,
1639				    XFT_CHAR_WIDTH, XftTypeInteger, char_width,
1640				    (void *) 0);
1641		    wnorm = xtermOpenXft(dpy, pat, "wide");
1642
1643		    if (wnorm != 0) {
1644			XftPatternBuild(pat,
1645					WideXftPattern,
1646					BoldXftPattern(wnorm),
1647					(void *) 0);
1648			wbold = xtermOpenXft(dpy, pat, "wide-bold");
1649
1650#if OPT_ISO_COLORS
1651			if (screen->italicULMode
1652			    && (pat = XftNameParse(face_name)) != 0) {
1653			    XftPatternBuild(pat,
1654					    WideXftPattern,
1655					    ItalXftPattern(wnorm),
1656					    (void *) 0);
1657			    wital = xtermOpenXft(dpy, pat, "wide-italic");
1658			}
1659#endif
1660		    }
1661		    XftPatternDestroy(pat);
1662		}
1663
1664		CACHE_XFT(screen->renderWideNorm, wnorm);
1665		CACHE_XFT(screen->renderWideBold, wbold);
1666		CACHE_XFT(screen->renderWideItal, wital);
1667	    }
1668#endif /* OPT_RENDERWIDE */
1669	}
1670	if (norm == 0) {
1671	    xw->misc.render_font = False;
1672	    update_font_renderfont();
1673	    /* now we will fall through into the bitmap fonts */
1674	} else {
1675	    setRenderFontsize(screen, win, norm, NULL);
1676	    setRenderFontsize(screen, win, bold, "bold");
1677	    setRenderFontsize(screen, win, ital, "ital");
1678	}
1679    }
1680    /*
1681     * Are we handling a bitmap font?
1682     */
1683    if (!xw->misc.render_font || IsIconWin(screen, win))
1684#endif /* OPT_RENDERFONT */
1685    {
1686	if (is_double_width_font(font)) {
1687	    win->f_width = (font->min_bounds.width);
1688	} else {
1689	    win->f_width = (font->max_bounds.width);
1690	}
1691	win->f_height = (font->ascent + font->descent);
1692	win->f_ascent = font->ascent;
1693	win->f_descent = font->descent;
1694    }
1695    i = 2 * screen->border + sbwidth;
1696    j = 2 * screen->border;
1697    width = MaxCols(screen) * win->f_width + i;
1698    height = MaxRows(screen) * win->f_height + j;
1699    win->fullwidth = width;
1700    win->fullheight = height;
1701    win->width = width - i;
1702    win->height = height - j;
1703
1704    TRACE(("xtermComputeFontInfo window %dx%d (full %dx%d), fontsize %dx%d (asc %d, dsc %d)\n",
1705	   win->height,
1706	   win->width,
1707	   win->fullheight,
1708	   win->fullwidth,
1709	   win->f_height,
1710	   win->f_width,
1711	   win->f_ascent,
1712	   win->f_descent));
1713}
1714
1715/* save this information as a side-effect for double-sized characters */
1716void
1717xtermSaveFontInfo(TScreen * screen, XFontStruct * font)
1718{
1719    screen->fnt_wide = (font->max_bounds.width);
1720    screen->fnt_high = (font->ascent + font->descent);
1721    TRACE(("xtermSaveFontInfo %dx%d\n", screen->fnt_high, screen->fnt_wide));
1722}
1723
1724/*
1725 * After loading a new font, update the structures that use its size.
1726 */
1727void
1728xtermUpdateFontInfo(XtermWidget xw, Bool doresize)
1729{
1730    TScreen *screen = &(xw->screen);
1731
1732    int scrollbar_width;
1733    VTwin *win = &(screen->fullVwin);
1734
1735    scrollbar_width = (xw->misc.scrollbar
1736		       ? (screen->scrollWidget->core.width +
1737			  BorderWidth(screen->scrollWidget))
1738		       : 0);
1739    xtermComputeFontInfo(xw, win, screen->fnts[fNorm].fs, scrollbar_width);
1740    xtermSaveFontInfo(screen, screen->fnts[fNorm].fs);
1741
1742    if (doresize) {
1743	if (VWindow(screen)) {
1744	    xtermClear(xw);
1745	}
1746	TRACE(("xtermUpdateFontInfo {{\n"));
1747	DoResizeScreen(xw);	/* set to the new natural size */
1748	ResizeScrollBar(xw);
1749	Redraw();
1750	TRACE(("... }} xtermUpdateFontInfo\n"));
1751#ifdef SCROLLBAR_RIGHT
1752	updateRightScrollbar(xw);
1753#endif
1754    }
1755    xtermSetCursorBox(screen);
1756}
1757
1758#if OPT_BOX_CHARS
1759
1760/*
1761 * Returns true if the given character is missing from the specified font.
1762 */
1763Bool
1764xtermMissingChar(XtermWidget xw, unsigned ch, XFontStruct * font)
1765{
1766    if (font != 0
1767	&& font->per_char != 0
1768	&& !font->all_chars_exist) {
1769	static XCharStruct dft, *tmp = &dft, *pc = 0;
1770
1771	if (font->max_byte1 == 0) {
1772#if OPT_WIDE_CHARS
1773	    if (ch > 255) {
1774		TRACE(("xtermMissingChar %#04x (row)\n", ch));
1775		return True;
1776	    }
1777#endif
1778	    CI_GET_CHAR_INFO_1D(font, E2A(ch), tmp, pc);
1779	}
1780#if OPT_WIDE_CHARS
1781	else {
1782	    CI_GET_CHAR_INFO_2D(font, HI_BYTE(ch), LO_BYTE(ch), tmp, pc);
1783	}
1784#else
1785
1786	if (!pc)
1787	    return False;	/* Urgh! */
1788#endif
1789
1790	if (CI_NONEXISTCHAR(pc)) {
1791	    TRACE(("xtermMissingChar %#04x (!exists)\n", ch));
1792	    return True;
1793	}
1794    }
1795    if (xtermIsDecGraphic(ch)
1796	&& xw->screen.force_box_chars) {
1797	TRACE(("xtermMissingChar %#04x (forced off)\n", ch));
1798	return True;
1799    }
1800    return False;
1801}
1802
1803/*
1804 * The grid is arbitrary, enough resolution that nothing's lost in
1805 * initialization.
1806 */
1807#define BOX_HIGH 60
1808#define BOX_WIDE 60
1809
1810#define MID_HIGH (BOX_HIGH/2)
1811#define MID_WIDE (BOX_WIDE/2)
1812
1813#define CHR_WIDE ((9*BOX_WIDE)/10)
1814#define CHR_HIGH ((9*BOX_HIGH)/10)
1815
1816/*
1817 * ...since we'll scale the values anyway.
1818 */
1819#define SCALE_X(n) n = (n * (font_width-1)) / (BOX_WIDE-1)
1820#define SCALE_Y(n) n = (n * (font_height-1)) / (BOX_HIGH-1)
1821
1822#define SEG(x0,y0,x1,y1) x0,y0, x1,y1
1823
1824/*
1825 * Draw the given graphic character, if it is simple enough (i.e., a
1826 * line-drawing character).
1827 */
1828void
1829xtermDrawBoxChar(XtermWidget xw,
1830		 unsigned ch,
1831		 unsigned flags,
1832		 GC gc,
1833		 int x,
1834		 int y,
1835		 int cells)
1836{
1837    TScreen *screen = &(xw->screen);
1838    /* *INDENT-OFF* */
1839    static const short glyph_ht[] = {
1840	SEG(1*BOX_WIDE/10,  0,		1*BOX_WIDE/10,5*MID_HIGH/6),	/* H */
1841	SEG(6*BOX_WIDE/10,  0,		6*BOX_WIDE/10,5*MID_HIGH/6),
1842	SEG(1*BOX_WIDE/10,5*MID_HIGH/12,6*BOX_WIDE/10,5*MID_HIGH/12),
1843	SEG(2*BOX_WIDE/10,  MID_HIGH,	  CHR_WIDE,	MID_HIGH),	/* T */
1844	SEG(6*BOX_WIDE/10,  MID_HIGH,	6*BOX_WIDE/10,	CHR_HIGH),
1845	-1
1846    }, glyph_ff[] = {
1847	SEG(1*BOX_WIDE/10,  0,		6*BOX_WIDE/10,	0),		/* F */
1848	SEG(1*BOX_WIDE/10,5*MID_HIGH/12,6*CHR_WIDE/12,5*MID_HIGH/12),
1849	SEG(1*BOX_WIDE/10,  0,		0*BOX_WIDE/3, 5*MID_HIGH/6),
1850	SEG(1*BOX_WIDE/3,   MID_HIGH,	  CHR_WIDE,	MID_HIGH),	/* F */
1851	SEG(1*BOX_WIDE/3, 8*MID_HIGH/6,10*CHR_WIDE/12,8*MID_HIGH/6),
1852	SEG(1*BOX_WIDE/3,   MID_HIGH,	1*BOX_WIDE/3,	CHR_HIGH),
1853	-1
1854    }, glyph_lf[] = {
1855	SEG(1*BOX_WIDE/10,  0,		1*BOX_WIDE/10,9*MID_HIGH/12),	/* L */
1856	SEG(1*BOX_WIDE/10,9*MID_HIGH/12,6*BOX_WIDE/10,9*MID_HIGH/12),
1857	SEG(1*BOX_WIDE/3,   MID_HIGH,	  CHR_WIDE,	MID_HIGH),	/* F */
1858	SEG(1*BOX_WIDE/3, 8*MID_HIGH/6,10*CHR_WIDE/12,8*MID_HIGH/6),
1859	SEG(1*BOX_WIDE/3,   MID_HIGH,	1*BOX_WIDE/3,	CHR_HIGH),
1860	-1
1861    }, glyph_nl[] = {
1862	SEG(1*BOX_WIDE/10,5*MID_HIGH/6, 1*BOX_WIDE/10,	0),		/* N */
1863	SEG(1*BOX_WIDE/10,  0,		5*BOX_WIDE/6, 5*MID_HIGH/6),
1864	SEG(5*BOX_WIDE/6, 5*MID_HIGH/6, 5*BOX_WIDE/6,	0),
1865	SEG(1*BOX_WIDE/3,   MID_HIGH,	1*BOX_WIDE/3,	CHR_HIGH),	/* L */
1866	SEG(1*BOX_WIDE/3,   CHR_HIGH,	  CHR_WIDE,	CHR_HIGH),
1867	-1
1868    }, glyph_vt[] = {
1869	SEG(1*BOX_WIDE/10,   0,		5*BOX_WIDE/12,5*MID_HIGH/6),	/* V */
1870	SEG(5*BOX_WIDE/12,5*MID_HIGH/6, 5*BOX_WIDE/6,	0),
1871	SEG(2*BOX_WIDE/10,  MID_HIGH,	  CHR_WIDE,	MID_HIGH),	/* T */
1872	SEG(6*BOX_WIDE/10,  MID_HIGH,	6*BOX_WIDE/10,	CHR_HIGH),
1873	-1
1874    }, plus_or_minus[] =
1875    {
1876	SEG(  0,	  5*BOX_HIGH/6,	  CHR_WIDE,   5*BOX_HIGH/6),
1877	SEG(  MID_WIDE,	  2*BOX_HIGH/6,	  MID_WIDE,   4*BOX_HIGH/6),
1878	SEG(  0,	  3*BOX_HIGH/6,	  CHR_WIDE,   3*BOX_HIGH/6),
1879	-1
1880    }, lower_right_corner[] =
1881    {
1882	SEG(  0,	    MID_HIGH,	  MID_WIDE,	MID_HIGH),
1883	SEG(  MID_WIDE,	    MID_HIGH,	  MID_WIDE,	0),
1884	-1
1885    }, upper_right_corner[] =
1886    {
1887	SEG(  0,	    MID_HIGH,	  MID_WIDE,	MID_HIGH),
1888	SEG( MID_WIDE,	    MID_HIGH,	  MID_WIDE,	BOX_HIGH),
1889	-1
1890    }, upper_left_corner[] =
1891    {
1892	SEG(  MID_WIDE,	    MID_HIGH,	  BOX_WIDE,	MID_HIGH),
1893	SEG(  MID_WIDE,	    MID_HIGH,	  MID_WIDE,	BOX_HIGH),
1894	-1
1895    }, lower_left_corner[] =
1896    {
1897	SEG(  MID_WIDE,	    0,		  MID_WIDE,	MID_HIGH),
1898	SEG(  MID_WIDE,	    MID_WIDE,	  BOX_WIDE,	MID_HIGH),
1899	-1
1900    }, cross[] =
1901    {
1902	SEG(  0,	    MID_HIGH,	  BOX_WIDE,	MID_HIGH),
1903	SEG(  MID_WIDE,	    0,		  MID_WIDE,	BOX_HIGH),
1904	-1
1905    }, scan_line_1[] =
1906    {
1907	SEG(  0,	    0,		  BOX_WIDE,	0),
1908	-1
1909    }, scan_line_3[] =
1910    {
1911	SEG(  0,	    BOX_HIGH/4,	  BOX_WIDE,	BOX_HIGH/4),
1912	-1
1913    }, scan_line_7[] =
1914    {
1915	SEG( 0,		    MID_HIGH,	  BOX_WIDE,	MID_HIGH),
1916	-1
1917    }, scan_line_9[] =
1918    {
1919	SEG(  0,	  3*BOX_HIGH/4,	  BOX_WIDE,   3*BOX_HIGH/4),
1920	-1
1921    }, horizontal_line[] =
1922    {
1923	SEG(  0,	    BOX_HIGH,	  BOX_WIDE,	BOX_HIGH),
1924	-1
1925    }, left_tee[] =
1926    {
1927	SEG(  MID_WIDE,	    0,		  MID_WIDE,	BOX_HIGH),
1928	SEG(  MID_WIDE,	    MID_HIGH,	  BOX_WIDE,	MID_HIGH),
1929	-1
1930    }, right_tee[] =
1931    {
1932	SEG(  MID_WIDE,	    0,		  MID_WIDE,	BOX_HIGH),
1933	SEG(  MID_WIDE,	    MID_HIGH,	  0,		MID_HIGH),
1934	-1
1935    }, bottom_tee[] =
1936    {
1937	SEG(  0,	    MID_HIGH,	  BOX_WIDE,	MID_HIGH),
1938	SEG(  MID_WIDE,	    0,		  MID_WIDE,	MID_HIGH),
1939	-1
1940    }, top_tee[] =
1941    {
1942	SEG(  0,	    MID_HIGH,	  BOX_WIDE,	MID_HIGH),
1943	SEG(  MID_WIDE,	    MID_HIGH,	  MID_WIDE,	BOX_HIGH),
1944	-1
1945    }, vertical_line[] =
1946    {
1947	SEG(  MID_WIDE,	    0,		  MID_WIDE,	BOX_HIGH),
1948	-1
1949    }, less_than_or_equal[] =
1950    {
1951	SEG(  CHR_WIDE,	    BOX_HIGH/3,	  0,		MID_HIGH),
1952	SEG(  CHR_WIDE,	  2*BOX_HIGH/3,	  0,		MID_HIGH),
1953	SEG(  0,	  3*BOX_HIGH/4,	  CHR_WIDE,   3*BOX_HIGH/4),
1954	-1
1955    }, greater_than_or_equal[] =
1956    {
1957	SEG(  0,	    BOX_HIGH/3,	  CHR_WIDE,	MID_HIGH),
1958	SEG(  0,	  2*BOX_HIGH/3,	  CHR_WIDE,	MID_HIGH),
1959	SEG(  0,	  3*BOX_HIGH/4,	  CHR_WIDE,   3*BOX_HIGH/4),
1960	-1
1961    }, greek_pi[] =
1962    {
1963	SEG(  0,	    MID_HIGH,	  CHR_WIDE,	MID_HIGH),
1964	SEG(5*CHR_WIDE/6,   MID_HIGH,	5*CHR_WIDE/6,	CHR_HIGH),
1965	SEG(2*CHR_WIDE/6,   MID_HIGH,	2*CHR_WIDE/6,	CHR_HIGH),
1966	-1
1967    }, not_equal_to[] =
1968    {
1969	SEG(2*BOX_WIDE/3, 1*BOX_HIGH/3, 1*BOX_WIDE/3,	CHR_HIGH),
1970	SEG(  0,	  2*BOX_HIGH/3,	  CHR_WIDE,   2*BOX_HIGH/3),
1971	SEG(  0,	    MID_HIGH,	  CHR_WIDE,	MID_HIGH),
1972	-1
1973    };
1974    /* *INDENT-ON* */
1975
1976    static const short *lines[] =
1977    {
1978	0,			/* 00 (unused) */
1979	0,			/* 01 diamond */
1980	0,			/* 02 box */
1981	glyph_ht,		/* 03 HT */
1982	glyph_ff,		/* 04 FF */
1983	0,			/* 05 CR */
1984	glyph_lf,		/* 06 LF */
1985	0,			/* 07 degrees (small circle) */
1986	plus_or_minus,		/* 08 */
1987	glyph_nl,		/* 09 */
1988	glyph_vt,		/* 0A */
1989	lower_right_corner,	/* 0B */
1990	upper_right_corner,	/* 0C */
1991	upper_left_corner,	/* 0D */
1992	lower_left_corner,	/* 0E */
1993	cross,			/* 0F */
1994	scan_line_1,		/* 10 */
1995	scan_line_3,		/* 11 */
1996	scan_line_7,		/* 12 */
1997	scan_line_9,		/* 13 */
1998	horizontal_line,	/* 14 */
1999	left_tee,		/* 15 */
2000	right_tee,		/* 16 */
2001	bottom_tee,		/* 17 */
2002	top_tee,		/* 18 */
2003	vertical_line,		/* 19 */
2004	less_than_or_equal,	/* 1A */
2005	greater_than_or_equal,	/* 1B */
2006	greek_pi,		/* 1C */
2007	not_equal_to,		/* 1D */
2008	0,			/* 1E LB */
2009	0,			/* 1F bullet */
2010    };
2011
2012    GC gc2;
2013    CgsEnum cgsId = (ch == 2) ? gcDots : gcLine;
2014    VTwin *cgsWin = WhichVWin(screen);
2015    const short *p;
2016    unsigned font_width = ((flags & DOUBLEWFONT) ? 2 : 1) * screen->fnt_wide;
2017    unsigned font_height = ((flags & DOUBLEHFONT) ? 2 : 1) * screen->fnt_high;
2018
2019    if (cells > 1)
2020	font_width *= cells;
2021
2022#if OPT_WIDE_CHARS
2023    /*
2024     * Try to show line-drawing characters if we happen to be in UTF-8
2025     * mode, but have gotten an old-style font.
2026     */
2027    if (screen->utf8_mode
2028#if OPT_RENDERFONT
2029	&& !UsingRenderFont(xw)
2030#endif
2031	&& (ch > 127)
2032	&& (ch != UCS_REPL)) {
2033	unsigned n;
2034	for (n = 1; n < 32; n++) {
2035	    if (dec2ucs(n) == ch
2036		&& !xtermMissingChar(xw, n,
2037				     ((flags & BOLD)
2038				      ? screen->fnts[fBold].fs
2039				      : screen->fnts[fNorm].fs))) {
2040		TRACE(("...use xterm-style linedrawing\n"));
2041		ch = n;
2042		break;
2043	    }
2044	}
2045    }
2046#endif
2047
2048    TRACE(("DRAW_BOX(%d) cell %dx%d at %d,%d%s\n",
2049	   ch, font_height, font_width, y, x,
2050	   (ch >= (sizeof(lines) / sizeof(lines[0]))
2051	    ? "-BAD"
2052	    : "")));
2053
2054    if (cgsId == gcDots) {
2055	setCgsFont(xw, cgsWin, cgsId, getCgsFont(xw, cgsWin, gc));
2056	setCgsFore(xw, cgsWin, cgsId, getCgsFore(xw, cgsWin, gc));
2057	setCgsBack(xw, cgsWin, cgsId, getCgsBack(xw, cgsWin, gc));
2058    } else {
2059	setCgsFont(xw, cgsWin, cgsId, getCgsFont(xw, cgsWin, gc));
2060	setCgsFore(xw, cgsWin, cgsId, getCgsBack(xw, cgsWin, gc));
2061	setCgsBack(xw, cgsWin, cgsId, getCgsBack(xw, cgsWin, gc));
2062    }
2063    gc2 = getCgsGC(xw, cgsWin, cgsId);
2064
2065    if (!(flags & NOBACKGROUND)) {
2066	XFillRectangle(screen->display, VWindow(screen), gc2, x, y,
2067		       font_width,
2068		       font_height);
2069    }
2070
2071    setCgsFont(xw, cgsWin, cgsId, getCgsFont(xw, cgsWin, gc));
2072    setCgsFore(xw, cgsWin, cgsId, getCgsFore(xw, cgsWin, gc));
2073    setCgsBack(xw, cgsWin, cgsId, getCgsBack(xw, cgsWin, gc));
2074    gc2 = getCgsGC(xw, cgsWin, cgsId);
2075
2076    XSetLineAttributes(screen->display, gc2,
2077		       (flags & BOLD)
2078		       ? ((font_height > 12)
2079			  ? font_height / 12
2080			  : 1)
2081		       : ((font_height > 16)
2082			  ? font_height / 16
2083			  : 1),
2084		       LineSolid,
2085		       CapProjecting,
2086		       JoinMiter);
2087
2088    if (ch == 1) {		/* diamond */
2089	XPoint points[5];
2090	int npoints = 5, n;
2091
2092	points[0].x = MID_WIDE;
2093	points[0].y = BOX_HIGH / 4;
2094
2095	points[1].x = 8 * BOX_WIDE / 8;
2096	points[1].y = MID_HIGH;
2097
2098	points[2].x = points[0].x;
2099	points[2].y = 3 * BOX_HIGH / 4;
2100
2101	points[3].x = 0 * BOX_WIDE / 8;
2102	points[3].y = points[1].y;
2103
2104	points[4].x = points[0].x;
2105	points[4].y = points[0].y;
2106
2107	for (n = 0; n < npoints; ++n) {
2108	    SCALE_X(points[n].x);
2109	    SCALE_Y(points[n].y);
2110	    points[n].x += x;
2111	    points[n].y += y;
2112	}
2113
2114	XFillPolygon(screen->display,
2115		     VWindow(screen), gc2,
2116		     points, npoints,
2117		     Convex, CoordModeOrigin);
2118    } else if (ch == 7) {	/* degrees */
2119	unsigned width = (BOX_WIDE / 3);
2120	int x_coord = MID_WIDE - (width / 2);
2121	int y_coord = MID_HIGH - width;
2122
2123	SCALE_X(x_coord);
2124	SCALE_Y(y_coord);
2125	SCALE_X(width);
2126
2127	XDrawArc(screen->display,
2128		 VWindow(screen), gc2,
2129		 x + x_coord, y + y_coord, width, width,
2130		 0,
2131		 360 * 64);
2132    } else if (ch == 0x1f) {	/* bullet */
2133	unsigned width = 7 * BOX_WIDE / 10;
2134	int x_coord = MID_WIDE - (width / 3);
2135	int y_coord = MID_HIGH - (width / 3);
2136
2137	SCALE_X(x_coord);
2138	SCALE_Y(y_coord);
2139	SCALE_X(width);
2140
2141	XDrawArc(screen->display,
2142		 VWindow(screen), gc2,
2143		 x + x_coord, y + y_coord, width, width,
2144		 0,
2145		 360 * 64);
2146    } else if (ch < (sizeof(lines) / sizeof(lines[0]))
2147	       && (p = lines[ch]) != 0) {
2148	int coord[4];
2149	int n = 0;
2150	while (*p >= 0) {
2151	    coord[n++] = *p++;
2152	    if (n == 4) {
2153		SCALE_X(coord[0]);
2154		SCALE_Y(coord[1]);
2155		SCALE_X(coord[2]);
2156		SCALE_Y(coord[3]);
2157		XDrawLine(screen->display,
2158			  VWindow(screen), gc2,
2159			  x + coord[0], y + coord[1],
2160			  x + coord[2], y + coord[3]);
2161		n = 0;
2162	    }
2163	}
2164    } else if (screen->force_all_chars) {
2165	/* bounding rectangle, for debugging */
2166	XDrawRectangle(screen->display, VWindow(screen), gc2, x, y,
2167		       font_width - 1,
2168		       font_height - 1);
2169    }
2170}
2171
2172#if OPT_RENDERFONT
2173
2174/*
2175 * Check if the given character has a glyph known to Xft.
2176 *
2177 * see xc/lib/Xft/xftglyphs.c
2178 */
2179Bool
2180xtermXftMissing(XtermWidget xw, XftFont * font, unsigned wc)
2181{
2182    Bool result = False;
2183
2184    if (font != 0) {
2185	if (!XftGlyphExists(xw->screen.display, font, wc)) {
2186#if OPT_WIDE_CHARS
2187	    TRACE(("xtermXftMissing %d (dec=%#x, ucs=%#x)\n",
2188		   wc, ucs2dec(wc), dec2ucs(wc)));
2189#else
2190	    TRACE(("xtermXftMissing %d\n", wc));
2191#endif
2192	    result = True;
2193	}
2194    }
2195    return result;
2196}
2197#endif /* OPT_RENDERFONT && OPT_WIDE_CHARS */
2198
2199#endif /* OPT_BOX_CHARS */
2200
2201#if OPT_WIDE_CHARS
2202#define MY_UCS(ucs,dec) case ucs: result = dec; break
2203unsigned
2204ucs2dec(unsigned ch)
2205{
2206    unsigned result = ch;
2207    if ((ch > 127)
2208	&& (ch != UCS_REPL)) {
2209	switch (ch) {
2210	    MY_UCS(0x25ae, 0);	/* black vertical rectangle                   */
2211	    MY_UCS(0x25c6, 1);	/* black diamond                              */
2212	    MY_UCS(0x2592, 2);	/* medium shade                               */
2213	    MY_UCS(0x2409, 3);	/* symbol for horizontal tabulation           */
2214	    MY_UCS(0x240c, 4);	/* symbol for form feed                       */
2215	    MY_UCS(0x240d, 5);	/* symbol for carriage return                 */
2216	    MY_UCS(0x240a, 6);	/* symbol for line feed                       */
2217	    MY_UCS(0x00b0, 7);	/* degree sign                                */
2218	    MY_UCS(0x00b1, 8);	/* plus-minus sign                            */
2219	    MY_UCS(0x2424, 9);	/* symbol for newline                         */
2220	    MY_UCS(0x240b, 10);	/* symbol for vertical tabulation             */
2221	    MY_UCS(0x2518, 11);	/* box drawings light up and left             */
2222	    MY_UCS(0x2510, 12);	/* box drawings light down and left           */
2223	    MY_UCS(0x250c, 13);	/* box drawings light down and right          */
2224	    MY_UCS(0x2514, 14);	/* box drawings light up and right            */
2225	    MY_UCS(0x253c, 15);	/* box drawings light vertical and horizontal */
2226	    MY_UCS(0x23ba, 16);	/* box drawings scan 1                        */
2227	    MY_UCS(0x23bb, 17);	/* box drawings scan 3                        */
2228	    MY_UCS(0x2500, 18);	/* box drawings light horizontal              */
2229	    MY_UCS(0x23bc, 19);	/* box drawings scan 7                        */
2230	    MY_UCS(0x23bd, 20);	/* box drawings scan 9                        */
2231	    MY_UCS(0x251c, 21);	/* box drawings light vertical and right      */
2232	    MY_UCS(0x2524, 22);	/* box drawings light vertical and left       */
2233	    MY_UCS(0x2534, 23);	/* box drawings light up and horizontal       */
2234	    MY_UCS(0x252c, 24);	/* box drawings light down and horizontal     */
2235	    MY_UCS(0x2502, 25);	/* box drawings light vertical                */
2236	    MY_UCS(0x2264, 26);	/* less-than or equal to                      */
2237	    MY_UCS(0x2265, 27);	/* greater-than or equal to                   */
2238	    MY_UCS(0x03c0, 28);	/* greek small letter pi                      */
2239	    MY_UCS(0x2260, 29);	/* not equal to                               */
2240	    MY_UCS(0x00a3, 30);	/* pound sign                                 */
2241	    MY_UCS(0x00b7, 31);	/* middle dot                                 */
2242	}
2243    }
2244    return result;
2245}
2246
2247#undef  MY_UCS
2248#define MY_UCS(ucs,dec) case dec: result = ucs; break
2249
2250unsigned
2251dec2ucs(unsigned ch)
2252{
2253    unsigned result = ch;
2254    if (xtermIsDecGraphic(ch)) {
2255	switch (ch) {
2256	    MY_UCS(0x25ae, 0);	/* black vertical rectangle                   */
2257	    MY_UCS(0x25c6, 1);	/* black diamond                              */
2258	    MY_UCS(0x2592, 2);	/* medium shade                               */
2259	    MY_UCS(0x2409, 3);	/* symbol for horizontal tabulation           */
2260	    MY_UCS(0x240c, 4);	/* symbol for form feed                       */
2261	    MY_UCS(0x240d, 5);	/* symbol for carriage return                 */
2262	    MY_UCS(0x240a, 6);	/* symbol for line feed                       */
2263	    MY_UCS(0x00b0, 7);	/* degree sign                                */
2264	    MY_UCS(0x00b1, 8);	/* plus-minus sign                            */
2265	    MY_UCS(0x2424, 9);	/* symbol for newline                         */
2266	    MY_UCS(0x240b, 10);	/* symbol for vertical tabulation             */
2267	    MY_UCS(0x2518, 11);	/* box drawings light up and left             */
2268	    MY_UCS(0x2510, 12);	/* box drawings light down and left           */
2269	    MY_UCS(0x250c, 13);	/* box drawings light down and right          */
2270	    MY_UCS(0x2514, 14);	/* box drawings light up and right            */
2271	    MY_UCS(0x253c, 15);	/* box drawings light vertical and horizontal */
2272	    MY_UCS(0x23ba, 16);	/* box drawings scan 1                        */
2273	    MY_UCS(0x23bb, 17);	/* box drawings scan 3                        */
2274	    MY_UCS(0x2500, 18);	/* box drawings light horizontal              */
2275	    MY_UCS(0x23bc, 19);	/* box drawings scan 7                        */
2276	    MY_UCS(0x23bd, 20);	/* box drawings scan 9                        */
2277	    MY_UCS(0x251c, 21);	/* box drawings light vertical and right      */
2278	    MY_UCS(0x2524, 22);	/* box drawings light vertical and left       */
2279	    MY_UCS(0x2534, 23);	/* box drawings light up and horizontal       */
2280	    MY_UCS(0x252c, 24);	/* box drawings light down and horizontal     */
2281	    MY_UCS(0x2502, 25);	/* box drawings light vertical                */
2282	    MY_UCS(0x2264, 26);	/* less-than or equal to                      */
2283	    MY_UCS(0x2265, 27);	/* greater-than or equal to                   */
2284	    MY_UCS(0x03c0, 28);	/* greek small letter pi                      */
2285	    MY_UCS(0x2260, 29);	/* not equal to                               */
2286	    MY_UCS(0x00a3, 30);	/* pound sign                                 */
2287	    MY_UCS(0x00b7, 31);	/* middle dot                                 */
2288	}
2289    }
2290    return result;
2291}
2292
2293#endif /* OPT_WIDE_CHARS */
2294
2295#if OPT_SHIFT_FONTS
2296static void
2297lookupOneFontSize(XtermWidget xw, int fontnum)
2298{
2299    TScreen *screen = TScreenOf(xw);
2300
2301    if (screen->menu_font_sizes[fontnum] == 0) {
2302	XTermFonts fnt;
2303
2304	memset(&fnt, 0, sizeof(fnt));
2305	screen->menu_font_sizes[fontnum] = -1;
2306	if (xtermOpenFont(xw, screen->MenuFontName(fontnum), &fnt)) {
2307	    screen->menu_font_sizes[fontnum] = FontSize(fnt.fs);
2308	    TRACE(("menu_font_sizes[%d] = %ld\n", fontnum,
2309		   screen->menu_font_sizes[fontnum]));
2310	    xtermCloseFont(xw, &fnt);
2311	}
2312    }
2313}
2314
2315/*
2316 * Cache the font-sizes so subsequent larger/smaller font actions will go fast.
2317 */
2318static void
2319lookupFontSizes(XtermWidget xw)
2320{
2321    int n;
2322
2323    for (n = 0; n < NMENUFONTS; n++) {
2324	lookupOneFontSize(xw, n);
2325    }
2326}
2327
2328/*
2329 * Find the index of a larger/smaller font (according to the sign of 'relative'
2330 * and its magnitude), starting from the 'old' index.
2331 */
2332int
2333lookupRelativeFontSize(XtermWidget xw, int old, int relative)
2334{
2335    TScreen *screen = TScreenOf(xw);
2336    int n, m = -1;
2337
2338    if (!IsIcon(screen)) {
2339	lookupFontSizes(xw);
2340	if (relative != 0) {
2341	    for (n = 0; n < NMENUFONTS; ++n) {
2342		if (screen->menu_font_sizes[n] > 0 &&
2343		    screen->menu_font_sizes[n] != screen->menu_font_sizes[old]) {
2344		    int cmp_0 = ((screen->menu_font_sizes[n] >
2345				  screen->menu_font_sizes[old])
2346				 ? relative
2347				 : -relative);
2348		    int cmp_m = ((m < 0)
2349				 ? 1
2350				 : ((screen->menu_font_sizes[n] <
2351				     screen->menu_font_sizes[m])
2352				    ? relative
2353				    : -relative));
2354		    if (cmp_0 > 0 && cmp_m > 0) {
2355			m = n;
2356		    }
2357		}
2358	    }
2359	    if (m >= 0) {
2360		if (relative > 1)
2361		    m = lookupRelativeFontSize(xw, m, relative - 1);
2362		else if (relative < -1)
2363		    m = lookupRelativeFontSize(xw, m, relative + 1);
2364	    }
2365	}
2366    }
2367    return m;
2368}
2369
2370/* ARGSUSED */
2371void
2372HandleLargerFont(Widget w GCC_UNUSED,
2373		 XEvent * event GCC_UNUSED,
2374		 String * params GCC_UNUSED,
2375		 Cardinal *param_count GCC_UNUSED)
2376{
2377    if (IsXtermWidget(w)) {
2378	XtermWidget xw = (XtermWidget) w;
2379
2380	if (xw->misc.shift_fonts) {
2381	    TScreen *screen = &xw->screen;
2382	    int m;
2383
2384	    m = lookupRelativeFontSize(xw, screen->menu_font_number, 1);
2385	    if (m >= 0) {
2386		SetVTFont(xw, m, True, NULL);
2387	    } else {
2388		Bell(XkbBI_MinorError, 0);
2389	    }
2390	}
2391    }
2392}
2393
2394/* ARGSUSED */
2395void
2396HandleSmallerFont(Widget w GCC_UNUSED,
2397		  XEvent * event GCC_UNUSED,
2398		  String * params GCC_UNUSED,
2399		  Cardinal *param_count GCC_UNUSED)
2400{
2401    if (IsXtermWidget(w)) {
2402	XtermWidget xw = (XtermWidget) w;
2403
2404	if (xw->misc.shift_fonts) {
2405	    TScreen *screen = &xw->screen;
2406	    int m;
2407
2408	    m = lookupRelativeFontSize(xw, screen->menu_font_number, -1);
2409	    if (m >= 0) {
2410		SetVTFont(xw, m, True, NULL);
2411	    } else {
2412		Bell(XkbBI_MinorError, 0);
2413	    }
2414	}
2415    }
2416}
2417#endif
2418
2419int
2420xtermGetFont(const char *param)
2421{
2422    int fontnum;
2423
2424    switch (param[0]) {
2425    case 'd':
2426    case 'D':
2427    case '0':
2428	fontnum = fontMenu_default;
2429	break;
2430    case '1':
2431	fontnum = fontMenu_font1;
2432	break;
2433    case '2':
2434	fontnum = fontMenu_font2;
2435	break;
2436    case '3':
2437	fontnum = fontMenu_font3;
2438	break;
2439    case '4':
2440	fontnum = fontMenu_font4;
2441	break;
2442    case '5':
2443	fontnum = fontMenu_font5;
2444	break;
2445    case '6':
2446	fontnum = fontMenu_font6;
2447	break;
2448    case 'e':
2449    case 'E':
2450	fontnum = fontMenu_fontescape;
2451	break;
2452    case 's':
2453    case 'S':
2454	fontnum = fontMenu_fontsel;
2455	break;
2456    default:
2457	fontnum = -1;
2458	break;
2459    }
2460    return fontnum;
2461}
2462
2463/* ARGSUSED */
2464void
2465HandleSetFont(Widget w GCC_UNUSED,
2466	      XEvent * event GCC_UNUSED,
2467	      String * params,
2468	      Cardinal *param_count)
2469{
2470    if (IsXtermWidget(w)) {
2471	int fontnum;
2472	VTFontNames fonts;
2473
2474	memset(&fonts, 0, sizeof(fonts));
2475
2476	if (*param_count == 0) {
2477	    fontnum = fontMenu_default;
2478	} else {
2479	    Cardinal maxparams = 1;	/* total number of params allowed */
2480	    int result = xtermGetFont(params[0]);
2481
2482	    switch (result) {
2483	    case fontMenu_default:	/* FALLTHRU */
2484	    case fontMenu_font1:	/* FALLTHRU */
2485	    case fontMenu_font2:	/* FALLTHRU */
2486	    case fontMenu_font3:	/* FALLTHRU */
2487	    case fontMenu_font4:	/* FALLTHRU */
2488	    case fontMenu_font5:	/* FALLTHRU */
2489	    case fontMenu_font6:	/* FALLTHRU */
2490		break;
2491	    case fontMenu_fontescape:
2492#if OPT_WIDE_CHARS
2493		maxparams = 5;
2494#else
2495		maxparams = 3;
2496#endif
2497		break;
2498	    case fontMenu_fontsel:
2499		maxparams = 2;
2500		break;
2501	    default:
2502		Bell(XkbBI_MinorError, 0);
2503		return;
2504	    }
2505	    fontnum = result;
2506
2507	    if (*param_count > maxparams) {	/* see if extra args given */
2508		Bell(XkbBI_MinorError, 0);
2509		return;
2510	    }
2511	    switch (*param_count) {	/* assign 'em */
2512#if OPT_WIDE_CHARS
2513	    case 5:
2514		fonts.f_wb = params[4];
2515		/* FALLTHRU */
2516	    case 4:
2517		fonts.f_w = params[3];
2518		/* FALLTHRU */
2519#endif
2520	    case 3:
2521		fonts.f_b = params[2];
2522		/* FALLTHRU */
2523	    case 2:
2524		fonts.f_n = params[1];
2525		break;
2526	    }
2527	}
2528
2529	SetVTFont((XtermWidget) w, fontnum, True, &fonts);
2530    }
2531}
2532
2533void
2534SetVTFont(XtermWidget xw,
2535	  int which,
2536	  Bool doresize,
2537	  const VTFontNames * fonts)
2538{
2539    TScreen *screen = &xw->screen;
2540
2541    TRACE(("SetVTFont(which=%d, f_n=%s, f_b=%s)\n", which,
2542	   (fonts && fonts->f_n) ? fonts->f_n : "<null>",
2543	   (fonts && fonts->f_b) ? fonts->f_b : "<null>"));
2544
2545    if (IsIcon(screen)) {
2546	Bell(XkbBI_MinorError, 0);
2547    } else if (which >= 0 && which < NMENUFONTS) {
2548	VTFontNames myfonts;
2549
2550	memset(&myfonts, 0, sizeof(myfonts));
2551	if (fonts != 0)
2552	    myfonts = *fonts;
2553
2554	if (which == fontMenu_fontsel) {	/* go get the selection */
2555	    FindFontSelection(xw, myfonts.f_n, False);
2556	    return;
2557	} else {
2558	    int oldFont = screen->menu_font_number;
2559
2560#define USE_CACHED(field, name) \
2561	    if (myfonts.field == 0) { \
2562		myfonts.field = screen->menu_font_names[which][name]; \
2563		TRACE(("set myfonts." #field " from menu_font_names[%d][" #name "] %s\n", \
2564		       which, NonNull(myfonts.field))); \
2565	    } else { \
2566		TRACE(("set myfonts." #field " reused\n")); \
2567	    }
2568	    USE_CACHED(f_n, fNorm);
2569	    USE_CACHED(f_b, fBold);
2570#if OPT_WIDE_CHARS
2571	    USE_CACHED(f_w, fWide);
2572	    USE_CACHED(f_wb, fWBold);
2573#endif
2574	    if (xtermLoadFont(xw,
2575			      &myfonts,
2576			      doresize, which)) {
2577		return;
2578	    } else {
2579		xtermLoadFont(xw,
2580			      xtermFontName(screen->MenuFontName(oldFont)),
2581			      doresize, oldFont);
2582	    }
2583	}
2584    }
2585
2586    Bell(XkbBI_MinorError, 0);
2587    return;
2588}
2589