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