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