1 /* $XTermId: fontutils.c,v 1.709 2021/11/10 00:36:27 Rajeev.V.Pillai Exp $ */
2 
3 /*
4  * Copyright 1998-2020,2021 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 NoFontWarning(data) (data)->warn = fwAlways
55 
56 #define SetFontWidth(screen,dst,src)  (dst)->f_width = (src)
57 #define SetFontHeight(screen,dst,src) (dst)->f_height = dimRound((double)((screen)->scale_height * (float) (src)))
58 
59 /* from X11/Xlibint.h - not all vendors install this file */
60 #define CI_NONEXISTCHAR(cs) (((cs)->width == 0) && \
61 			     (((cs)->rbearing|(cs)->lbearing| \
62 			       (cs)->ascent|(cs)->descent) == 0))
63 
64 #define CI_GET_CHAR_INFO_1D(fs,col,cs) \
65 { \
66     cs = 0; \
67     if (col >= fs->min_char_or_byte2 && col <= fs->max_char_or_byte2) { \
68 	if (fs->per_char == NULL) { \
69 	    cs = &fs->min_bounds; \
70 	} else { \
71 	    cs = &fs->per_char[(col - fs->min_char_or_byte2)]; \
72 	} \
73 	if (CI_NONEXISTCHAR(cs)) cs = 0; \
74     } \
75 }
76 
77 #define CI_GET_CHAR_INFO_2D(fs,row,col,cs) \
78 { \
79     cs = 0; \
80     if (row >= fs->min_byte1 && row <= fs->max_byte1 && \
81 	col >= fs->min_char_or_byte2 && col <= fs->max_char_or_byte2) { \
82 	if (fs->per_char == NULL) { \
83 	    cs = &fs->min_bounds; \
84 	} else { \
85 	    cs = &fs->per_char[((row - fs->min_byte1) * \
86 				(fs->max_char_or_byte2 - \
87 				 fs->min_char_or_byte2 + 1)) + \
88 			       (col - fs->min_char_or_byte2)]; \
89 	} \
90 	if (CI_NONEXISTCHAR(cs)) cs = 0; \
91     } \
92 }
93 
94 #define FREE_FNAME(field) \
95 	    if (fonts == 0 || myfonts.field != fonts->field) { \
96 		FREE_STRING(myfonts.field); \
97 		myfonts.field = 0; \
98 	    }
99 
100 /*
101  * A structure to hold the relevant properties from a font
102  * we need to make a well formed font name for it.
103  */
104 typedef struct {
105     /* registry, foundry, family */
106     const char *beginning;
107     /* weight */
108     const char *weight;
109     /* slant */
110     const char *slant;
111     /* wideness */
112     const char *wideness;
113     /* add style */
114     const char *add_style;
115     int pixel_size;
116     const char *point_size;
117     int res_x;
118     int res_y;
119     const char *spacing;
120     int average_width;
121     /* charset registry, charset encoding */
122     char *end;
123 } FontNameProperties;
124 
125 #if OPT_WIDE_CHARS && (OPT_RENDERFONT || (OPT_TRACE > 1))
126 #define MY_UCS(code,high,wide,name) { code, high, wide, #name }
127 static const struct {
128     unsigned code, high, wide;
129     const char *name;
130 } unicode_boxes[] = {
131 
132     MY_UCS(0x2500, 0, 1, box drawings light horizontal),
133 	MY_UCS(0x2502, 1, 0, box drawings light vertical),
134 	MY_UCS(0x250c, 2, 2, box drawings light down and right),
135 	MY_UCS(0x2510, 2, 2, box drawings light down and left),
136 	MY_UCS(0x2514, 2, 2, box drawings light up and right),
137 	MY_UCS(0x2518, 2, 2, box drawings light up and left),
138 	MY_UCS(0x251c, 1, 2, box drawings light vertical and right),
139 	MY_UCS(0x2524, 1, 2, box drawings light vertical and left),
140 	MY_UCS(0x252c, 2, 1, box drawings light down and horizontal),
141 	MY_UCS(0x2534, 2, 1, box drawings light up and horizontal),
142 	MY_UCS(0x253c, 1, 1, box drawings light vertical and horizontal),
143     {
144 	0, 0, 0, NULL
145     }
146 };
147 
148 #undef MY_UCS
149 #endif /* OPT_WIDE_CHARS */
150 
151 #if OPT_LOAD_VTFONTS || OPT_WIDE_CHARS
152 static Boolean merge_sublist(char ***, char **);
153 #endif
154 
155 static void save2FontList(XtermWidget, const char *, XtermFontNames *,
156 			  VTFontEnum, const char *, Bool);
157 
158 #if OPT_RENDERFONT
159 static void fillInFaceSize(XtermWidget, int);
160 #endif
161 
162 #if OPT_SHIFT_FONTS
163 static int lookupOneFontSize(XtermWidget, int);
164 #endif
165 
166 #if OPT_TRACE
167 static void
set_font_height(TScreen * screen,VTwin * win,int height)168 set_font_height(TScreen *screen, VTwin *win, int height)
169 {
170     SetFontHeight(screen, win, height);
171     TRACE(("SetFontHeight %d\n", win->f_height));
172 #undef SetFontHeight
173 #define SetFontHeight(screen, win, height) set_font_height(screen, win, height)
174 }
175 
176 static void
set_font_width(TScreen * screen,VTwin * win,int width)177 set_font_width(TScreen *screen, VTwin *win, int width)
178 {
179     (void) screen;
180     SetFontWidth(screen, win, width);
181     TRACE(("SetFontWidth  %d\n", win->f_width));
182 #undef  SetFontWidth
183 #define SetFontWidth(screen, win, width) set_font_width(screen, win, width)
184 }
185 #endif
186 
187 #if OPT_REPORT_FONTS || OPT_WIDE_CHARS
188 static unsigned
countGlyphs(XFontStruct * fp)189 countGlyphs(XFontStruct *fp)
190 {
191     unsigned count = 0;
192 
193     if (fp != 0) {
194 	if (fp->min_byte1 == 0 && fp->max_byte1 == 0) {
195 	    count = fp->max_char_or_byte2 - fp->min_char_or_byte2 + 1;
196 	} else if (fp->min_char_or_byte2 < 256
197 		   && fp->max_char_or_byte2 < 256) {
198 	    unsigned first = (fp->min_byte1 << 8) + fp->min_char_or_byte2;
199 	    unsigned last = (fp->max_byte1 << 8) + fp->max_char_or_byte2;
200 	    count = last + 1 - first;
201 	}
202     }
203     return count;
204 }
205 #endif
206 
207 #if OPT_WIDE_CHARS
208 /*
209  * Verify that the wide-bold font is at least a bold font with roughly as many
210  * glyphs as the wide font.  The counts should be the same, but settle for
211  * filtering out the worst of the font mismatches.
212  */
213 static Bool
compatibleWideCounts(XFontStruct * wfs,XFontStruct * wbfs)214 compatibleWideCounts(XFontStruct *wfs, XFontStruct *wbfs)
215 {
216     unsigned count_w = countGlyphs(wfs);
217     unsigned count_wb = countGlyphs(wbfs);
218     if (count_w <= 256 ||
219 	count_wb <= 256 ||
220 	((count_w / 4) * 3) > count_wb) {
221 	TRACE(("...font server lied (count wide %u vs wide-bold %u)\n",
222 	       count_w, count_wb));
223 	return False;
224     }
225     return True;
226 }
227 #endif /* OPT_WIDE_CHARS */
228 
229 #if OPT_BOX_CHARS
230 static void
setupPackedFonts(XtermWidget xw)231 setupPackedFonts(XtermWidget xw)
232 {
233     TScreen *screen = TScreenOf(xw);
234     Bool value = False;
235 
236 #if OPT_RENDERFONT
237     if (xw->work.render_font == True) {
238 	int e;
239 
240 	for (e = 0; e < fMAX; ++e) {
241 	    XTermXftFonts *data = getMyXftFont(xw, e, screen->menu_font_number);
242 	    if (data != 0) {
243 		if (data->map.mixed) {
244 		    screen->allow_packing = True;
245 		    break;
246 		}
247 	    }
248 	}
249     }
250 #endif /* OPT_RENDERFONT */
251 
252     value = screen->allow_packing;
253 
254     SetItemSensitivity(fontMenuEntries[fontMenu_font_packedfont].widget, value);
255 }
256 #endif
257 
258 typedef struct _nameList {
259     struct _nameList *next;
260     char *name;
261 } NameList;
262 
263 static NameList *derived_fonts;
264 
265 static Boolean
is_derived_font_name(const char * name)266 is_derived_font_name(const char *name)
267 {
268     Boolean result = False;
269     NameList *list;
270     if (!IsEmpty(name)) {
271 	for (list = derived_fonts; list != 0; list = list->next) {
272 	    if (!x_strcasecmp(name, list->name)) {
273 		result = True;
274 		break;
275 	    }
276 	}
277     }
278     return result;
279 }
280 
281 void
xtermDerivedFont(const char * name)282 xtermDerivedFont(const char *name)
283 {
284     if (!IsEmpty(name) && !is_derived_font_name(name)) {
285 	NameList *list = TypeCalloc(NameList);
286 	list->name = x_strdup(name);
287 	list->next = derived_fonts;
288 	derived_fonts = list;
289     }
290 }
291 
292 /*
293  * Returns the fields from start to stop in a dash- separated string.  This
294  * function will modify the source, putting '\0's in the appropriate place and
295  * moving the beginning forward to after the '\0'
296  *
297  * This will NOT work for the last field (but we won't need it).
298  */
299 static char *
n_fields(char ** source,int start,int stop)300 n_fields(char **source, int start, int stop)
301 {
302     int i;
303     char *str, *str1;
304 
305     /*
306      * find the start-1th dash
307      */
308     for (i = start - 1, str = *source; i; i--, str++) {
309 	if ((str = strchr(str, '-')) == 0)
310 	    return 0;
311     }
312 
313     /*
314      * find the stopth dash
315      */
316     for (i = stop - start + 1, str1 = str; i; i--, str1++) {
317 	if ((str1 = strchr(str1, '-')) == 0)
318 	    return 0;
319     }
320 
321     /*
322      * put a \0 at the end of the fields
323      */
324     *(str1 - 1) = '\0';
325 
326     /*
327      * move source forward
328      */
329     *source = str1;
330 
331     return str;
332 }
333 
334 static Boolean
check_fontname(const char * name)335 check_fontname(const char *name)
336 {
337     Boolean result = True;
338 
339     if (IsEmpty(name)) {
340 	TRACE(("fontname missing\n"));
341 	result = False;
342     }
343     return result;
344 }
345 
346 /*
347  * Gets the font properties from a given font structure.  We use the FONT name
348  * to find them out, since that seems easier.
349  *
350  * Returns a pointer to a static FontNameProperties structure
351  * or NULL on error.
352  */
353 static FontNameProperties *
get_font_name_props(Display * dpy,XFontStruct * fs,char ** result)354 get_font_name_props(Display *dpy, XFontStruct *fs, char **result)
355 {
356     static FontNameProperties props;
357     static char *last_name;
358 
359     Atom fontatom;
360     char *name;
361     char *str;
362 
363     if (fs == NULL)
364 	return NULL;
365 
366     /*
367      * first get the full font name
368      */
369     name = 0;
370     fontatom = XInternAtom(dpy, "FONT", False);
371     if (fontatom != 0) {
372 	XFontProp *fp;
373 	int i;
374 
375 	for (i = 0, fp = fs->properties; i < fs->n_properties; i++, fp++) {
376 	    if (fp->name == fontatom) {
377 		name = XGetAtomName(dpy, fp->card32);
378 		break;
379 	    }
380 	}
381     }
382 
383     if (name == 0)
384 	return 0;
385 
386     /*
387      * XGetAtomName allocates memory - don't leak
388      */
389     XFree(last_name);
390     last_name = name;
391 
392     if (result != 0) {
393 	if (!check_fontname(name))
394 	    return 0;
395 	free(*result);
396 	*result = x_strdup(name);
397     }
398 
399     /*
400      * Now split it up into parts and put them in
401      * their places. Since we are using parts of
402      * the original string, we must not free the Atom Name
403      */
404 
405     /* registry, foundry, family */
406     if ((props.beginning = n_fields(&name, 1, 3)) == 0)
407 	return 0;
408 
409     /* weight is the next */
410     if ((props.weight = n_fields(&name, 1, 1)) == 0)
411 	return 0;
412 
413     /* slant */
414     if ((props.slant = n_fields(&name, 1, 1)) == 0)
415 	return 0;
416 
417     /* width */
418     if ((props.wideness = n_fields(&name, 1, 1)) == 0)
419 	return 0;
420 
421     /* add style */
422     if ((props.add_style = n_fields(&name, 1, 1)) == 0)
423 	return 0;
424 
425     /* pixel size */
426     if ((str = n_fields(&name, 1, 1)) == 0)
427 	return 0;
428     if ((props.pixel_size = atoi(str)) == 0)
429 	return 0;
430 
431     /* point size */
432     if ((props.point_size = n_fields(&name, 1, 1)) == 0)
433 	return 0;
434 
435     /* res_x */
436     if ((str = n_fields(&name, 1, 1)) == 0)
437 	return 0;
438     if ((props.res_x = atoi(str)) == 0)
439 	return 0;
440 
441     /* res_y */
442     if ((str = n_fields(&name, 1, 1)) == 0)
443 	return 0;
444     if ((props.res_y = atoi(str)) == 0)
445 	return 0;
446 
447     /* spacing */
448     if ((props.spacing = n_fields(&name, 1, 1)) == 0)
449 	return 0;
450 
451     /* average width */
452     if ((str = n_fields(&name, 1, 1)) == 0)
453 	return 0;
454     if ((props.average_width = atoi(str)) == 0)
455 	return 0;
456 
457     /* the rest: charset registry and charset encoding */
458     props.end = name;
459 
460     return &props;
461 }
462 
463 #define ALLOCHUNK(n) ((n | 127) + 1)
464 
465 static void
alloca_fontname(char ** result,size_t next)466 alloca_fontname(char **result, size_t next)
467 {
468     size_t last = (*result != 0) ? strlen(*result) : 0;
469     size_t have = (*result != 0) ? ALLOCHUNK(last) : 0;
470     size_t want = last + next + 2;
471 
472     if (want >= have) {
473 	want = ALLOCHUNK(want);
474 	if (last != 0) {
475 	    char *save = *result;
476 	    *result = TypeRealloc(char, want, *result);
477 	    if (*result == 0)
478 		free(save);
479 	} else {
480 	    if ((*result = TypeMallocN(char, want)) != 0)
481 		**result = '\0';
482 	}
483     }
484 }
485 
486 static void
append_fontname_str(char ** result,const char * value)487 append_fontname_str(char **result, const char *value)
488 {
489     if (value == 0)
490 	value = "*";
491     alloca_fontname(result, strlen(value));
492     if (*result != 0) {
493 	if (**result != '\0')
494 	    strcat(*result, "-");
495 	strcat(*result, value);
496     }
497 }
498 
499 static void
append_fontname_num(char ** result,int value)500 append_fontname_num(char **result, int value)
501 {
502     if (value < 0) {
503 	append_fontname_str(result, "*");
504     } else {
505 	char temp[100];
506 	sprintf(temp, "%d", value);
507 	append_fontname_str(result, temp);
508     }
509 }
510 
511 /*
512  * Take the given font props and try to make a well formed font name specifying
513  * the same base font and size and everything, but with different weight/width
514  * according to the parameters.  The return value is allocated, should be freed
515  * by the caller.
516  */
517 static char *
derive_font_name(FontNameProperties * props,const char * use_weight,int use_average_width,const char * use_encoding)518 derive_font_name(FontNameProperties *props,
519 		 const char *use_weight,
520 		 int use_average_width,
521 		 const char *use_encoding)
522 {
523     char *result = 0;
524 
525     append_fontname_str(&result, props->beginning);
526     append_fontname_str(&result, use_weight);
527     append_fontname_str(&result, props->slant);
528     append_fontname_str(&result, 0);
529     append_fontname_str(&result, 0);
530     append_fontname_num(&result, props->pixel_size);
531     append_fontname_str(&result, props->point_size);
532     append_fontname_num(&result, props->res_x);
533     append_fontname_num(&result, props->res_y);
534     append_fontname_str(&result, props->spacing);
535     append_fontname_num(&result, use_average_width);
536     append_fontname_str(&result, use_encoding);
537 
538     xtermDerivedFont(result);
539     return result;
540 }
541 
542 static char *
bold_font_name(FontNameProperties * props,int use_average_width)543 bold_font_name(FontNameProperties *props, int use_average_width)
544 {
545     return derive_font_name(props, "bold", use_average_width, props->end);
546 }
547 
548 #if OPT_WIDE_ATTRS
549 static char *
italic_font_name(FontNameProperties * props,const char * slant)550 italic_font_name(FontNameProperties *props, const char *slant)
551 {
552     FontNameProperties myprops = *props;
553     myprops.slant = slant;
554     return derive_font_name(&myprops, props->weight, myprops.average_width, props->end);
555 }
556 
557 static Boolean
open_italic_font(XtermWidget xw,int n,FontNameProperties * fp,XTermFonts * data)558 open_italic_font(XtermWidget xw, int n, FontNameProperties *fp, XTermFonts * data)
559 {
560     static const char *slant[] =
561     {
562 	"o",
563 	"i"
564     };
565     Cardinal pass;
566     Boolean result = False;
567 
568     NoFontWarning(data);
569     for (pass = 0; pass < XtNumber(slant); ++pass) {
570 	char *name;
571 	if ((name = italic_font_name(fp, slant[pass])) != 0) {
572 	    TRACE(("open_italic_font %s %s\n",
573 		   whichFontEnum((VTFontEnum) n), name));
574 	    if (xtermOpenFont(xw, name, data, False)) {
575 		result = (data->fs != 0);
576 #if OPT_REPORT_FONTS
577 		if (resource.reportFonts) {
578 		    printf("opened italic version of %s:\n\t%s\n",
579 			   whichFontEnum((VTFontEnum) n),
580 			   name);
581 		}
582 #endif
583 	    }
584 	    free(name);
585 	    if (result)
586 		break;
587 	}
588     }
589 #if OPT_TRACE
590     if (result) {
591 	XFontStruct *fs = data->fs;
592 	if (fs != 0) {
593 	    TRACE(("...actual size %dx%d (ascent %d, descent %d)\n",
594 		   fs->ascent +
595 		   fs->descent,
596 		   fs->max_bounds.width,
597 		   fs->ascent,
598 		   fs->descent));
599 	}
600     }
601 #endif
602     return result;
603 }
604 #endif
605 
606 #if OPT_WIDE_CHARS
607 #define derive_wide_font(props, weight) \
608 	derive_font_name(props, weight, props->average_width * 2, "ISO10646-1")
609 
610 static char *
wide_font_name(FontNameProperties * props)611 wide_font_name(FontNameProperties *props)
612 {
613     return derive_wide_font(props, "medium");
614 }
615 
616 static char *
widebold_font_name(FontNameProperties * props)617 widebold_font_name(FontNameProperties *props)
618 {
619     return derive_wide_font(props, "bold");
620 }
621 #endif /* OPT_WIDE_CHARS */
622 
623 #if OPT_DEC_CHRSET
624 /*
625  * Take the given font props and try to make a well formed font name specifying
626  * the same base font but changed depending on the given attributes and chrset.
627  *
628  * For double width fonts, we just double the X-resolution, for double height
629  * fonts we double the pixel-size and Y-resolution
630  */
631 char *
xtermSpecialFont(XTermDraw * params)632 xtermSpecialFont(XTermDraw * params)
633 {
634     TScreen *screen = TScreenOf(params->xw);
635 #if OPT_TRACE
636     static char old_spacing[80];
637     static FontNameProperties old_props;
638 #endif
639     FontNameProperties *props;
640     char *result = 0;
641     const char *weight;
642     int pixel_size;
643     int res_x;
644     int res_y;
645 
646     props = get_font_name_props(screen->display,
647 				GetNormalFont(screen, fNorm)->fs, 0);
648     if (props == 0)
649 	return result;
650 
651     pixel_size = props->pixel_size;
652     res_x = props->res_x;
653     res_y = props->res_y;
654     if (params->attr_flags & BOLD)
655 	weight = "bold";
656     else
657 	weight = props->weight;
658 
659     if (CSET_DOUBLE(params->this_chrset))
660 	res_x *= 2;
661 
662     if (params->this_chrset == CSET_DHL_TOP
663 	|| params->this_chrset == CSET_DHL_BOT) {
664 	res_y *= 2;
665 	pixel_size *= 2;
666     }
667 #if OPT_TRACE
668     if (old_props.res_x != res_x
669 	|| old_props.res_x != res_y
670 	|| old_props.pixel_size != pixel_size
671 	|| strcmp(old_props.spacing, props->spacing)) {
672 	TRACE(("xtermSpecialFont(atts = %#x, draw = %#x, chrset = %#x)\n",
673 	       params->attr_flags, params->draw_flags, params->this_chrset));
674 	TRACE(("res_x      = %d\n", res_x));
675 	TRACE(("res_y      = %d\n", res_y));
676 	TRACE(("point_size = %s\n", props->point_size));
677 	TRACE(("pixel_size = %d\n", pixel_size));
678 	TRACE(("spacing    = %s\n", props->spacing));
679 	old_props.res_x = res_x;
680 	old_props.res_y = res_y;
681 	old_props.pixel_size = pixel_size;
682 	old_props.spacing = old_spacing;
683 	sprintf(old_spacing, "%.*s", (int) sizeof(old_spacing) - 2, props->spacing);
684     }
685 #endif
686 
687     append_fontname_str(&result, props->beginning);
688     append_fontname_str(&result, weight);
689     append_fontname_str(&result, props->slant);
690     append_fontname_str(&result, props->wideness);
691     append_fontname_str(&result, props->add_style);
692     append_fontname_num(&result, pixel_size);
693     append_fontname_str(&result, props->point_size);
694     append_fontname_num(&result, (params->draw_flags & NORESOLUTION) ? -1 : res_x);
695     append_fontname_num(&result, (params->draw_flags & NORESOLUTION) ? -1 : res_y);
696     append_fontname_str(&result, props->spacing);
697     append_fontname_str(&result, 0);
698     append_fontname_str(&result, props->end);
699 
700     xtermDerivedFont(result);
701     return result;
702 }
703 #endif /* OPT_DEC_CHRSET */
704 
705 /*
706  * Case-independent comparison for font-names, including wildcards.
707  * XLFD allows '?' as a wildcard, but we do not handle that (no one seems
708  * to use it).
709  */
710 static Bool
same_font_name(const char * pattern,const char * match)711 same_font_name(const char *pattern, const char *match)
712 {
713     Bool result = False;
714 
715     if (pattern && match) {
716 	while (*pattern && *match) {
717 	    if (*pattern == *match) {
718 		pattern++;
719 		match++;
720 	    } else if (*pattern == '*' || *match == '*') {
721 		if (same_font_name(pattern + 1, match)) {
722 		    return True;
723 		} else if (same_font_name(pattern, match + 1)) {
724 		    return True;
725 		} else {
726 		    return False;
727 		}
728 	    } else {
729 		int p = x_toupper(*pattern++);
730 		int m = x_toupper(*match++);
731 		if (p != m)
732 		    return False;
733 	    }
734 	}
735 	result = (*pattern == *match);	/* both should be NUL */
736     }
737     return result;
738 }
739 
740 /*
741  * Double-check the fontname that we asked for versus what the font server
742  * actually gave us.  The larger fixed fonts do not always have a matching bold
743  * font, and the font server may try to scale another font or otherwise
744  * substitute a mismatched font.
745  *
746  * If we cannot get what we requested, we will fallback to the original
747  * behavior, which simulates bold by overstriking each character at one pixel
748  * offset.
749  */
750 static int
got_bold_font(Display * dpy,XFontStruct * fs,String requested)751 got_bold_font(Display *dpy, XFontStruct *fs, String requested)
752 {
753     char *actual = 0;
754     int got;
755 
756     if (get_font_name_props(dpy, fs, &actual) == 0)
757 	got = 0;
758     else
759 	got = same_font_name(requested, actual);
760     free(actual);
761     return got;
762 }
763 
764 /*
765  * Check normal/bold (or wide/wide-bold) font pairs to see if we will be able
766  * to check for missing glyphs in a comparable manner.
767  */
768 static int
comparable_metrics(XFontStruct * normal,XFontStruct * bold)769 comparable_metrics(XFontStruct *normal, XFontStruct *bold)
770 {
771 #define DATA "comparable_metrics: "
772     int result = 0;
773 
774     if (normal == 0 || bold == 0) {
775 	;
776     } else if (normal->all_chars_exist) {
777 	if (bold->all_chars_exist) {
778 	    result = 1;
779 	} else {
780 	    TRACE((DATA "all chars exist in normal font, but not in bold\n"));
781 	}
782     } else if (normal->per_char != 0) {
783 	if (bold->per_char != 0) {
784 	    result = 1;
785 	} else {
786 	    TRACE((DATA "normal font has per-char metrics, but not bold\n"));
787 	}
788     } else {
789 	TRACE((DATA "normal font is not very good!\n"));
790 	result = 1;		/* give in (we're not going in reverse) */
791     }
792     return result;
793 #undef DATA
794 }
795 
796 /*
797  * If the font server tries to adjust another font, it may not adjust it
798  * properly.  Check that the bounding boxes are compatible.  Otherwise we'll
799  * leave trash on the display when we mix normal and bold fonts.
800  */
801 static int
same_font_size(XtermWidget xw,XFontStruct * nfs,XFontStruct * bfs)802 same_font_size(XtermWidget xw, XFontStruct *nfs, XFontStruct *bfs)
803 {
804     TScreen *screen = TScreenOf(xw);
805     int result = 0;
806 
807     if (nfs != 0 && bfs != 0) {
808 	TRACE(("same_font_size height %d/%d, min %d/%d max %d/%d\n",
809 	       nfs->ascent + nfs->descent,
810 	       bfs->ascent + bfs->descent,
811 	       nfs->min_bounds.width, bfs->min_bounds.width,
812 	       nfs->max_bounds.width, bfs->max_bounds.width));
813 	result = screen->free_bold_box
814 	    || ((nfs->ascent + nfs->descent) == (bfs->ascent + bfs->descent)
815 		&& (nfs->min_bounds.width == bfs->min_bounds.width
816 		    || nfs->min_bounds.width == bfs->min_bounds.width + 1)
817 		&& (nfs->max_bounds.width == bfs->max_bounds.width
818 		    || nfs->max_bounds.width == bfs->max_bounds.width + 1));
819     }
820     return result;
821 }
822 
823 /*
824  * Check if the font looks like it has fixed width
825  */
826 static int
is_fixed_font(XFontStruct * fs)827 is_fixed_font(XFontStruct *fs)
828 {
829     if (fs)
830 	return (fs->min_bounds.width == fs->max_bounds.width);
831     return 1;
832 }
833 
834 static int
differing_widths(XFontStruct * a,XFontStruct * b)835 differing_widths(XFontStruct *a, XFontStruct *b)
836 {
837     int result = 0;
838     if (a != NULL && b != NULL && a->max_bounds.width != b->max_bounds.width)
839 	result = 1;
840     return result;
841 }
842 
843 /*
844  * Check if the font looks like a double width font (i.e. contains
845  * characters of width X and 2X
846  */
847 #if OPT_WIDE_CHARS
848 static int
is_double_width_font(XFontStruct * fs)849 is_double_width_font(XFontStruct *fs)
850 {
851     return (fs != NULL && ((2 * fs->min_bounds.width) == fs->max_bounds.width));
852 }
853 #else
854 #define is_double_width_font(fs) 0
855 #endif
856 
857 #if OPT_WIDE_CHARS && OPT_RENDERFONT && defined(HAVE_TYPE_FCCHAR32)
858 #define HALF_WIDTH_TEST_STRING "1234567890"
859 
860 /* '1234567890' in Chinese characters in UTF-8 */
861 #define FULL_WIDTH_TEST_STRING "\xe4\xb8\x80\xe4\xba\x8c\xe4\xb8\x89" \
862                                "\xe5\x9b\x9b\xe4\xba\x94" \
863 			       "\xef\xa7\x91\xe4\xb8\x83\xe5\x85\xab" \
864 			       "\xe4\xb9\x9d\xef\xa6\xb2"
865 
866 /* '1234567890' in Korean script in UTF-8 */
867 #define FULL_WIDTH_TEST_STRING2 "\xec\x9d\xbc\xec\x9d\xb4\xec\x82\xbc" \
868                                 "\xec\x82\xac\xec\x98\xa4" \
869 			        "\xec\x9c\xa1\xec\xb9\xa0\xed\x8c\x94" \
870 			        "\xea\xb5\xac\xec\x98\x81"
871 
872 #define HALF_WIDTH_CHAR1  0x0031	/* '1' */
873 #define HALF_WIDTH_CHAR2  0x0057	/* 'W' */
874 #define FULL_WIDTH_CHAR1  0x4E00	/* CJK Ideograph 'number one' */
875 #define FULL_WIDTH_CHAR2  0xAC00	/* Korean script syllable 'Ka' */
876 
877 static Bool
is_double_width_font_xft(Display * dpy,XftFont * font)878 is_double_width_font_xft(Display *dpy, XftFont *font)
879 {
880     XGlyphInfo gi1, gi2;
881     FcChar32 c1 = HALF_WIDTH_CHAR1, c2 = HALF_WIDTH_CHAR2;
882     String fwstr = FULL_WIDTH_TEST_STRING;
883     String hwstr = HALF_WIDTH_TEST_STRING;
884 
885     /* Some Korean fonts don't have Chinese characters at all. */
886     if (!XftCharExists(dpy, font, FULL_WIDTH_CHAR1)) {
887 	if (!XftCharExists(dpy, font, FULL_WIDTH_CHAR2))
888 	    return False;	/* Not a CJK font */
889 	else			/* a Korean font without CJK Ideographs */
890 	    fwstr = FULL_WIDTH_TEST_STRING2;
891     }
892 
893     XftTextExtents32(dpy, font, &c1, 1, &gi1);
894     XftTextExtents32(dpy, font, &c2, 1, &gi2);
895     if (gi1.xOff != gi2.xOff)	/* Not a fixed-width font */
896 	return False;
897 
898     XftTextExtentsUtf8(dpy,
899 		       font,
900 		       (_Xconst FcChar8 *) hwstr,
901 		       (int) strlen(hwstr),
902 		       &gi1);
903     XftTextExtentsUtf8(dpy,
904 		       font,
905 		       (_Xconst FcChar8 *) fwstr,
906 		       (int) strlen(fwstr),
907 		       &gi2);
908 
909     /*
910      * fontconfig and Xft prior to 2.2(?) set the width of half-width
911      * characters identical to that of full-width character in CJK double-width
912      * (bi-width / monospace) font even though the former is half as wide as
913      * the latter.  This was fixed sometime before the release of fontconfig
914      * 2.2 in early 2003.  See
915      *  http://bugzilla.mozilla.org/show_bug.cgi?id=196312
916      * In the meantime, we have to check both possibilities.
917      */
918     return ((2 * gi1.xOff == gi2.xOff) || (gi1.xOff == gi2.xOff));
919 }
920 #else
921 #define is_double_width_font_xft(dpy, xftfont) 0
922 #endif
923 
924 #define EmptyFont(fs) (fs != 0 \
925 		   && ((fs)->ascent + (fs)->descent == 0 \
926 		    || (fs)->max_bounds.width == 0))
927 
928 #define FontSize(fs) (((fs)->ascent + (fs)->descent) \
929 		    *  (fs)->max_bounds.width)
930 
931 const VTFontNames *
xtermFontName(const char * normal)932 xtermFontName(const char *normal)
933 {
934     static VTFontNames data;
935     FREE_STRING(data.f_n);
936     memset(&data, 0, sizeof(data));
937     if (normal)
938 	data.f_n = x_strdup(normal);
939     return &data;
940 }
941 
942 const VTFontNames *
defaultVTFontNames(XtermWidget xw)943 defaultVTFontNames(XtermWidget xw)
944 {
945     static VTFontNames data;
946     memset(&data, 0, sizeof(data));
947     data.f_n = DefaultFontN(xw);
948     data.f_b = DefaultFontB(xw);
949 #if OPT_WIDE_CHARS
950     data.f_w = DefaultFontW(xw);
951     data.f_wb = DefaultFontWB(xw);
952 #endif
953     return &data;
954 }
955 
956 static void
cache_menu_font_name(TScreen * screen,int fontnum,int which,const char * name)957 cache_menu_font_name(TScreen *screen, int fontnum, int which, const char *name)
958 {
959     if (name != 0) {
960 	String last = screen->menu_font_names[fontnum][which];
961 	if (last != 0) {
962 	    if (strcmp(last, name)) {
963 		FREE_STRING(last);
964 		TRACE(("caching menu fontname %d.%d %s\n", fontnum, which, name));
965 		screen->menu_font_names[fontnum][which] = x_strdup(name);
966 	    }
967 	} else {
968 	    TRACE(("caching menu fontname %d.%d %s\n", fontnum, which, name));
969 	    screen->menu_font_names[fontnum][which] = x_strdup(name);
970 	}
971     }
972 }
973 
974 static void
cannotFont(XtermWidget xw,const char * who,const char * tag,const char * name)975 cannotFont(XtermWidget xw, const char *who, const char *tag, const char *name)
976 {
977     static NameList *reported;
978     NameList *list;
979 
980     switch (xw->misc.fontWarnings) {
981     case fwNever:
982 	return;
983     case fwResource:
984 	if (is_derived_font_name(name))
985 	    return;
986 	break;
987     case fwAlways:
988 	break;
989     }
990     for (list = reported; list != 0; list = list->next) {
991 	if (!x_strcasecmp(name, list->name)) {
992 	    return;
993 	}
994     }
995     if ((list = TypeMalloc(NameList)) != 0) {
996 	list->name = x_strdup(name);
997 	list->next = reported;
998 	reported = list;
999     }
1000     xtermWarning("cannot %s%s%s %sfont \"%s\"\n",
1001 		 who, *tag ? " " : "", tag,
1002 		 is_derived_font_name(name) ? "derived " : "",
1003 		 name);
1004 }
1005 
1006 #if OPT_RENDERFONT
1007 static void
noUsableXft(XtermWidget xw,const char * name)1008 noUsableXft(XtermWidget xw, const char *name)
1009 {
1010     switch (xw->misc.fontWarnings) {
1011     case fwNever:
1012 	return;
1013     case fwResource:
1014 	/* these combinations of wide/bold/italic are all "derived" */
1015 	return;
1016     case fwAlways:
1017 	break;
1018     }
1019     xtermWarning("did not find a usable %s TrueType font\n", name);
1020 }
1021 #endif
1022 
1023 XFontStruct *
xtermLoadQueryFont(XtermWidget xw,const char * name)1024 xtermLoadQueryFont(XtermWidget xw, const char *name)
1025 {
1026     XFontStruct *result = NULL;
1027     size_t have = strlen(name);
1028     if (have == 0 || have > MAX_U_STRING) {
1029 	;			/* just ignore it */
1030     } else {
1031 	TScreen *screen = TScreenOf(xw);
1032 	result = XLoadQueryFont(screen->display, name);
1033     }
1034     return result;
1035 }
1036 
1037 /*
1038  * Open the given font and verify that it is non-empty.  Return a null on
1039  * failure.
1040  */
1041 Bool
xtermOpenFont(XtermWidget xw,const char * name,XTermFonts * result,Bool force)1042 xtermOpenFont(XtermWidget xw,
1043 	      const char *name,
1044 	      XTermFonts * result,
1045 	      Bool force)
1046 {
1047     Bool code = False;
1048 
1049     TRACE(("xtermOpenFont %d:%d '%s'\n",
1050 	   result->warn, xw->misc.fontWarnings, NonNull(name)));
1051     if (!IsEmpty(name)) {
1052 	if ((result->fs = xtermLoadQueryFont(xw, name)) != 0) {
1053 	    code = True;
1054 	    if (EmptyFont(result->fs)) {
1055 		xtermCloseFont(xw, result);
1056 		code = False;
1057 	    } else {
1058 		result->fn = x_strdup(name);
1059 	    }
1060 	} else if (XmuCompareISOLatin1(name, DEFFONT) != 0) {
1061 	    if (result->warn <= xw->misc.fontWarnings
1062 #if OPT_RENDERFONT
1063 		&& !UsingRenderFont(xw)
1064 #endif
1065 		) {
1066 		cannotFont(xw, "load", "", name);
1067 	    } else {
1068 		TRACE(("xtermOpenFont: cannot load font '%s'\n", name));
1069 	    }
1070 	    if (force) {
1071 		NoFontWarning(result);
1072 		code = xtermOpenFont(xw, DEFFONT, result, True);
1073 	    }
1074 	}
1075     }
1076     NoFontWarning(result);
1077     return code;
1078 }
1079 
1080 /*
1081  * Close the font and free the font info.
1082  */
1083 void
xtermCloseFont(XtermWidget xw,XTermFonts * fnt)1084 xtermCloseFont(XtermWidget xw, XTermFonts * fnt)
1085 {
1086     if (fnt != 0 && fnt->fs != 0) {
1087 	TScreen *screen = TScreenOf(xw);
1088 
1089 	clrCgsFonts(xw, WhichVWin(screen), fnt);
1090 	XFreeFont(screen->display, fnt->fs);
1091 	xtermFreeFontInfo(fnt);
1092     }
1093 }
1094 
1095 /*
1096  * Close and free the font (as well as any aliases).
1097  */
1098 static void
xtermCloseFont2(XtermWidget xw,XTermFonts * fnts,int which)1099 xtermCloseFont2(XtermWidget xw, XTermFonts * fnts, int which)
1100 {
1101     XFontStruct *thisFont = fnts[which].fs;
1102 
1103     if (thisFont != 0) {
1104 	int k;
1105 
1106 	xtermCloseFont(xw, &fnts[which]);
1107 	for (k = 0; k < fMAX; ++k) {
1108 	    if (k != which) {
1109 		if (thisFont == fnts[k].fs) {
1110 		    xtermFreeFontInfo(&fnts[k]);
1111 		}
1112 	    }
1113 	}
1114     }
1115 }
1116 
1117 /*
1118  * Close the listed fonts, noting that some may use copies of the pointer.
1119  */
1120 void
xtermCloseFonts(XtermWidget xw,XTermFonts * fnts)1121 xtermCloseFonts(XtermWidget xw, XTermFonts * fnts)
1122 {
1123     int j;
1124 
1125     for (j = 0; j < fMAX; ++j) {
1126 	xtermCloseFont2(xw, fnts, j);
1127     }
1128 }
1129 
1130 /*
1131  * Make a copy of the source, assuming the XFontStruct's to be unique, but
1132  * ensuring that the names are reallocated to simplify freeing.
1133  */
1134 void
xtermCopyFontInfo(XTermFonts * target,XTermFonts * source)1135 xtermCopyFontInfo(XTermFonts * target, XTermFonts * source)
1136 {
1137     xtermFreeFontInfo(target);
1138     target->chrset = source->chrset;
1139     target->flags = source->flags;
1140     target->fn = x_strdup(source->fn);
1141     target->fs = source->fs;
1142     target->warn = source->warn;
1143 }
1144 
1145 void
xtermFreeFontInfo(XTermFonts * target)1146 xtermFreeFontInfo(XTermFonts * target)
1147 {
1148     target->chrset = 0;
1149     target->flags = 0;
1150     FreeAndNull(target->fn);
1151     target->fs = 0;
1152 }
1153 
1154 #if OPT_REPORT_FONTS
1155 static void
reportXCharStruct(const char * tag,XCharStruct * cs)1156 reportXCharStruct(const char *tag, XCharStruct * cs)
1157 {
1158     printf("\t\t%s:\n", tag);
1159     printf("\t\t\tlbearing: %d\n", cs->lbearing);
1160     printf("\t\t\trbearing: %d\n", cs->rbearing);
1161     printf("\t\t\twidth:    %d\n", cs->width);
1162     printf("\t\t\tascent:   %d\n", cs->ascent);
1163     printf("\t\t\tdescent:  %d\n", cs->descent);
1164 }
1165 
1166 static void
reportOneVTFont(const char * tag,XTermFonts * fnt)1167 reportOneVTFont(const char *tag,
1168 		XTermFonts * fnt)
1169 {
1170     if (!IsEmpty(fnt->fn) && fnt->fs != 0) {
1171 	XFontStruct *fs = fnt->fs;
1172 	unsigned first_char = 0;
1173 	unsigned last_char = 0;
1174 
1175 	if (fs->max_byte1 == 0) {
1176 	    first_char = fs->min_char_or_byte2;
1177 	    last_char = fs->max_char_or_byte2;
1178 	} else {
1179 	    first_char = (fs->min_byte1 * 256) + fs->min_char_or_byte2;
1180 	    last_char = (fs->max_byte1 * 256) + fs->max_char_or_byte2;
1181 	}
1182 
1183 	printf("\t%s: %s\n", tag, NonNull(fnt->fn));
1184 	printf("\t\tall chars:     %s\n", fs->all_chars_exist ? "yes" : "no");
1185 	printf("\t\tdefault char:  %d\n", fs->default_char);
1186 	printf("\t\tdirection:     %d\n", fs->direction);
1187 	printf("\t\tascent:        %d\n", fs->ascent);
1188 	printf("\t\tdescent:       %d\n", fs->descent);
1189 	printf("\t\tfirst char:    %u\n", first_char);
1190 	printf("\t\tlast char:     %u\n", last_char);
1191 	printf("\t\tmaximum-chars: %u\n", countGlyphs(fs));
1192 	if (FontLacksMetrics(fnt)) {
1193 	    printf("\t\tmissing-chars: ?\n");
1194 	    printf("\t\tpresent-chars: ?\n");
1195 	} else {
1196 	    unsigned missing = 0;
1197 	    unsigned ch;
1198 	    for (ch = first_char; ch <= last_char; ++ch) {
1199 		if (xtermMissingChar(ch, fnt)) {
1200 		    ++missing;
1201 		}
1202 	    }
1203 	    printf("\t\tmissing-chars: %u\n", missing);
1204 	    printf("\t\tpresent-chars: %u\n", countGlyphs(fs) - missing);
1205 	}
1206 	printf("\t\tmin_byte1:     %d\n", fs->min_byte1);
1207 	printf("\t\tmax_byte1:     %d\n", fs->max_byte1);
1208 	printf("\t\tproperties:    %d\n", fs->n_properties);
1209 	reportXCharStruct("min_bounds", &(fs->min_bounds));
1210 	reportXCharStruct("max_bounds", &(fs->max_bounds));
1211 	/* TODO: report fs->properties and fs->per_char */
1212     }
1213 }
1214 
1215 static void
reportVTFontInfo(XtermWidget xw,int fontnum)1216 reportVTFontInfo(XtermWidget xw, int fontnum)
1217 {
1218     if (resource.reportFonts) {
1219 	TScreen *screen = TScreenOf(xw);
1220 
1221 	if (fontnum) {
1222 	    printf("Loaded VTFonts(font%d)\n", fontnum);
1223 	} else {
1224 	    printf("Loaded VTFonts(default)\n");
1225 	}
1226 
1227 	reportOneVTFont("fNorm", GetNormalFont(screen, fNorm));
1228 	reportOneVTFont("fBold", GetNormalFont(screen, fBold));
1229 #if OPT_WIDE_CHARS
1230 	reportOneVTFont("fWide", GetNormalFont(screen, fWide));
1231 	reportOneVTFont("fWBold", GetNormalFont(screen, fWBold));
1232 #endif
1233     }
1234 }
1235 #endif
1236 
1237 void
xtermUpdateFontGCs(XtermWidget xw,MyGetFont myfunc)1238 xtermUpdateFontGCs(XtermWidget xw, MyGetFont myfunc)
1239 {
1240     TScreen *screen = TScreenOf(xw);
1241     VTwin *win = WhichVWin(screen);
1242     Pixel new_normal = getXtermFG(xw, xw->flags, xw->cur_foreground);
1243     Pixel new_revers = getXtermBG(xw, xw->flags, xw->cur_background);
1244 
1245     setCgsFore(xw, win, gcNorm, new_normal);
1246     setCgsBack(xw, win, gcNorm, new_revers);
1247     setCgsFont(xw, win, gcNorm, myfunc(screen, fNorm));
1248 
1249     copyCgs(xw, win, gcBold, gcNorm);
1250     setCgsFont2(xw, win, gcBold, myfunc(screen, fBold), fBold);
1251 
1252     setCgsFore(xw, win, gcNormReverse, new_revers);
1253     setCgsBack(xw, win, gcNormReverse, new_normal);
1254     setCgsFont(xw, win, gcNormReverse, myfunc(screen, fNorm));
1255 
1256     copyCgs(xw, win, gcBoldReverse, gcNormReverse);
1257     setCgsFont2(xw, win, gcBoldReverse, myfunc(screen, fBold), fBold);
1258 
1259     if_OPT_WIDE_CHARS(screen, {
1260 	XTermFonts *wide_xx = myfunc(screen, fWide);
1261 	XTermFonts *bold_xx = myfunc(screen, fWBold);
1262 	if (wide_xx->fs != 0
1263 	    && bold_xx->fs != 0) {
1264 	    setCgsFore(xw, win, gcWide, new_normal);
1265 	    setCgsBack(xw, win, gcWide, new_revers);
1266 	    setCgsFont(xw, win, gcWide, wide_xx);
1267 
1268 	    copyCgs(xw, win, gcWBold, gcWide);
1269 	    setCgsFont(xw, win, gcWBold, bold_xx);
1270 
1271 	    setCgsFore(xw, win, gcWideReverse, new_revers);
1272 	    setCgsBack(xw, win, gcWideReverse, new_normal);
1273 	    setCgsFont(xw, win, gcWideReverse, wide_xx);
1274 
1275 	    copyCgs(xw, win, gcWBoldReverse, gcWideReverse);
1276 	    setCgsFont(xw, win, gcWBoldReverse, bold_xx);
1277 	}
1278     });
1279 }
1280 
1281 #if OPT_WIDE_ATTRS
1282 unsigned
xtermUpdateItalics(XtermWidget xw,unsigned new_attrs,unsigned old_attrs)1283 xtermUpdateItalics(XtermWidget xw, unsigned new_attrs, unsigned old_attrs)
1284 {
1285     TScreen *screen = TScreenOf(xw);
1286 
1287     if (UseItalicFont(screen)) {
1288 	if ((new_attrs & ATR_ITALIC) && !(old_attrs & ATR_ITALIC)) {
1289 	    xtermLoadItalics(xw);
1290 	    xtermUpdateFontGCs(xw, getItalicFont);
1291 	} else if (!(new_attrs & ATR_ITALIC) && (old_attrs & ATR_ITALIC)) {
1292 	    xtermUpdateFontGCs(xw, getNormalFont);
1293 	}
1294     }
1295     return new_attrs;
1296 }
1297 #endif
1298 
1299 #if OPT_TRACE && OPT_BOX_CHARS
1300 static void
show_font_misses(const char * name,XTermFonts * fp)1301 show_font_misses(const char *name, XTermFonts * fp)
1302 {
1303     if (fp->fs != 0) {
1304 	if (FontLacksMetrics(fp)) {
1305 	    TRACE(("%s font lacks metrics\n", name));
1306 	} else if (FontIsIncomplete(fp)) {
1307 	    TRACE(("%s font is incomplete\n", name));
1308 	} else {
1309 	    TRACE(("%s font is complete\n", name));
1310 	}
1311     } else {
1312 	TRACE(("%s font is missing\n", name));
1313     }
1314 }
1315 #endif
1316 
1317 static Bool
loadNormFP(XtermWidget xw,char ** nameOutP,XTermFonts * infoOut,int fontnum)1318 loadNormFP(XtermWidget xw,
1319 	   char **nameOutP,
1320 	   XTermFonts * infoOut,
1321 	   int fontnum)
1322 {
1323     Bool status = True;
1324 
1325     TRACE(("loadNormFP (%s)\n", NonNull(*nameOutP)));
1326 
1327     if (!xtermOpenFont(xw,
1328 		       *nameOutP,
1329 		       infoOut,
1330 		       (fontnum == fontMenu_default))) {
1331 	/*
1332 	 * If we are opening the default font, and it happens to be missing,
1333 	 * force that to the compiled-in default font, e.g., "fixed".  If we
1334 	 * cannot open the font, disable it from the menu.
1335 	 */
1336 	if (fontnum != fontMenu_fontsel) {
1337 	    SetItemSensitivity(fontMenuEntries[fontnum].widget, False);
1338 	}
1339 	status = False;
1340     }
1341     return status;
1342 }
1343 
1344 static Bool
loadBoldFP(XtermWidget xw,char ** nameOutP,XTermFonts * infoOut,const char * nameRef,XTermFonts * infoRef,int fontnum)1345 loadBoldFP(XtermWidget xw,
1346 	   char **nameOutP,
1347 	   XTermFonts * infoOut,
1348 	   const char *nameRef,
1349 	   XTermFonts * infoRef,
1350 	   int fontnum)
1351 {
1352     TScreen *screen = TScreenOf(xw);
1353     Bool status = True;
1354 
1355     TRACE(("loadBoldFP (%s)\n", NonNull(*nameOutP)));
1356 
1357     if (!check_fontname(*nameOutP)) {
1358 	FontNameProperties *fp;
1359 	char *normal = x_strdup(nameRef);
1360 
1361 	fp = get_font_name_props(screen->display, infoRef->fs, &normal);
1362 	if (fp != 0) {
1363 	    NoFontWarning(infoOut);
1364 	    *nameOutP = bold_font_name(fp, fp->average_width);
1365 	    if (!xtermOpenFont(xw, *nameOutP, infoOut, False)) {
1366 		free(*nameOutP);
1367 		*nameOutP = bold_font_name(fp, -1);
1368 		xtermOpenFont(xw, *nameOutP, infoOut, False);
1369 	    }
1370 	    TRACE(("...derived bold '%s'\n", NonNull(*nameOutP)));
1371 	}
1372 	if (fp == 0 || infoOut->fs == 0) {
1373 	    xtermCopyFontInfo(infoOut, infoRef);
1374 	    TRACE(("...cannot load a matching bold font\n"));
1375 	} else if (comparable_metrics(infoRef->fs, infoOut->fs)
1376 		   && same_font_size(xw, infoRef->fs, infoOut->fs)
1377 		   && got_bold_font(screen->display, infoOut->fs, *nameOutP)) {
1378 	    TRACE(("...got a matching bold font\n"));
1379 	    cache_menu_font_name(screen, fontnum, fBold, *nameOutP);
1380 	} else {
1381 	    xtermCloseFont2(xw, infoOut - fBold, fBold);
1382 	    *infoOut = *infoRef;
1383 	    TRACE(("...did not get a matching bold font\n"));
1384 	}
1385 	free(normal);
1386     } else if (!xtermOpenFont(xw, *nameOutP, infoOut, False)) {
1387 	xtermCopyFontInfo(infoOut, infoRef);
1388 	TRACE(("...cannot load bold font '%s'\n", NonNull(*nameOutP)));
1389     } else {
1390 	cache_menu_font_name(screen, fontnum, fBold, *nameOutP);
1391     }
1392 
1393     /*
1394      * Most of the time this call to load the font will succeed, even if
1395      * there is no wide font :  the X server doubles the width of the
1396      * normal font, or similar.
1397      *
1398      * But if it did fail for some reason, then nevermind.
1399      */
1400     if (EmptyFont(infoOut->fs))
1401 	status = False;		/* can't use a 0-sized font */
1402 
1403     if (!same_font_size(xw, infoRef->fs, infoOut->fs)
1404 	&& (is_fixed_font(infoRef->fs) && is_fixed_font(infoOut->fs))) {
1405 	TRACE(("...ignoring mismatched normal/bold fonts\n"));
1406 	xtermCloseFont2(xw, infoOut - fBold, fBold);
1407 	xtermCopyFontInfo(infoOut, infoRef);
1408     }
1409 
1410     return status;
1411 }
1412 
1413 #if OPT_WIDE_CHARS
1414 static Bool
loadWideFP(XtermWidget xw,char ** nameOutP,XTermFonts * infoOut,const char * nameRef,XTermFonts * infoRef,int fontnum)1415 loadWideFP(XtermWidget xw,
1416 	   char **nameOutP,
1417 	   XTermFonts * infoOut,
1418 	   const char *nameRef,
1419 	   XTermFonts * infoRef,
1420 	   int fontnum)
1421 {
1422     TScreen *screen = TScreenOf(xw);
1423     Bool status = True;
1424 
1425     TRACE(("loadWideFP (%s)\n", NonNull(*nameOutP)));
1426 
1427     if (!check_fontname(*nameOutP)
1428 	&& (screen->utf8_fonts && !is_double_width_font(infoRef->fs))) {
1429 	char *normal = x_strdup(nameRef);
1430 	FontNameProperties *fp = get_font_name_props(screen->display,
1431 						     infoRef->fs, &normal);
1432 	if (fp != 0) {
1433 	    *nameOutP = wide_font_name(fp);
1434 	    NoFontWarning(infoOut);
1435 	}
1436 	free(normal);
1437     }
1438 
1439     if (check_fontname(*nameOutP)) {
1440 	if (xtermOpenFont(xw, *nameOutP, infoOut, False)
1441 	    && is_derived_font_name(*nameOutP)
1442 	    && EmptyFont(infoOut->fs)) {
1443 	    xtermCloseFont2(xw, infoOut - fWide, fWide);
1444 	}
1445 	if (infoOut->fs == 0) {
1446 	    xtermCopyFontInfo(infoOut, infoRef);
1447 	} else {
1448 	    TRACE(("...%s wide %s\n",
1449 		   is_derived_font_name(*nameOutP) ? "derived" : "given",
1450 		   NonNull(*nameOutP)));
1451 	    cache_menu_font_name(screen, fontnum, fWide, *nameOutP);
1452 	}
1453     } else {
1454 	xtermCopyFontInfo(infoOut, infoRef);
1455     }
1456 #define MinWidthOf(fs) fs->min_bounds.width
1457 #define MaxWidthOf(fs) fs->max_bounds.width
1458     xw->work.force_wideFont = False;
1459     if (MaxWidthOf(infoOut->fs) != (2 * MaxWidthOf(infoRef->fs))) {
1460 	TRACE(("...reference width %d\n", MaxWidthOf(infoRef->fs)));
1461 	TRACE(("...?? double-width %d\n", 2 * MaxWidthOf(infoRef->fs)));
1462 	TRACE(("...actual width    %d\n", MaxWidthOf(infoOut->fs)));
1463 	xw->work.force_wideFont = True;
1464     }
1465     return status;
1466 }
1467 
1468 static Bool
loadWBoldFP(XtermWidget xw,char ** nameOutP,XTermFonts * infoOut,const char * wideNameRef,XTermFonts * wideInfoRef,const char * boldNameRef,XTermFonts * boldInfoRef,int fontnum)1469 loadWBoldFP(XtermWidget xw,
1470 	    char **nameOutP,
1471 	    XTermFonts * infoOut,
1472 	    const char *wideNameRef, XTermFonts * wideInfoRef,
1473 	    const char *boldNameRef, XTermFonts * boldInfoRef,
1474 	    int fontnum)
1475 {
1476     TScreen *screen = TScreenOf(xw);
1477     Bool status = True;
1478     char *bold = NULL;
1479 
1480     TRACE(("loadWBoldFP (%s)\n", NonNull(*nameOutP)));
1481 
1482     if (!check_fontname(*nameOutP)) {
1483 	FontNameProperties *fp;
1484 	fp = get_font_name_props(screen->display, boldInfoRef->fs, &bold);
1485 	if (fp != 0) {
1486 	    *nameOutP = widebold_font_name(fp);
1487 	    NoFontWarning(infoOut);
1488 	}
1489     }
1490 
1491     if (check_fontname(*nameOutP)) {
1492 
1493 	if (xtermOpenFont(xw, *nameOutP, infoOut, False)
1494 	    && is_derived_font_name(*nameOutP)
1495 	    && !compatibleWideCounts(wideInfoRef->fs, infoOut->fs)) {
1496 	    xtermCloseFont2(xw, infoOut - fWBold, fWBold);
1497 	}
1498 
1499 	if (infoOut->fs == 0) {
1500 	    if (is_derived_font_name(*nameOutP))
1501 		free(*nameOutP);
1502 	    if (IsEmpty(wideNameRef)) {
1503 		*nameOutP = x_strdup(boldNameRef);
1504 		xtermCopyFontInfo(infoOut, boldInfoRef);
1505 		TRACE(("...cannot load wide-bold, use bold %s\n",
1506 		       NonNull(boldNameRef)));
1507 	    } else {
1508 		*nameOutP = x_strdup(wideNameRef);
1509 		xtermCopyFontInfo(infoOut, wideInfoRef);
1510 		TRACE(("...cannot load wide-bold, use wide %s\n",
1511 		       NonNull(wideNameRef)));
1512 	    }
1513 	} else {
1514 	    TRACE(("...%s wide/bold %s\n",
1515 		   is_derived_font_name(*nameOutP) ? "derived" : "given",
1516 		   NonNull(*nameOutP)));
1517 	    cache_menu_font_name(screen, fontnum, fWBold, *nameOutP);
1518 	}
1519     } else if (is_double_width_font(boldInfoRef->fs)) {
1520 	xtermCopyFontInfo(infoOut, boldInfoRef);
1521 	TRACE(("...bold font is double-width, use it %s\n", NonNull(boldNameRef)));
1522     } else {
1523 	xtermCopyFontInfo(infoOut, wideInfoRef);
1524 	TRACE(("...cannot load wide bold font, use wide %s\n", NonNull(wideNameRef)));
1525     }
1526 
1527     free(bold);
1528 
1529     if (EmptyFont(infoOut->fs)) {
1530 	status = False;		/* can't use a 0-sized font */
1531     } else {
1532 	if ((!comparable_metrics(wideInfoRef->fs, infoOut->fs)
1533 	     || (!same_font_size(xw, wideInfoRef->fs, infoOut->fs)
1534 		 && is_fixed_font(wideInfoRef->fs)
1535 		 && is_fixed_font(infoOut->fs)))) {
1536 	    TRACE(("...ignoring mismatched normal/bold wide fonts\n"));
1537 	    xtermCloseFont2(xw, infoOut - fWBold, fWBold);
1538 	    xtermCopyFontInfo(infoOut, wideInfoRef);
1539 	}
1540     }
1541 
1542     return status;
1543 }
1544 #endif
1545 
1546 int
xtermLoadFont(XtermWidget xw,const VTFontNames * fonts,Bool doresize,int fontnum)1547 xtermLoadFont(XtermWidget xw,
1548 	      const VTFontNames * fonts,
1549 	      Bool doresize,
1550 	      int fontnum)
1551 {
1552     TScreen *screen = TScreenOf(xw);
1553     VTwin *win = WhichVWin(screen);
1554 
1555     VTFontNames myfonts;
1556     XTermFonts fnts[fMAX];
1557     char *tmpname = NULL;
1558     Boolean proportional = False;
1559 
1560     memset(&myfonts, 0, sizeof(myfonts));
1561     memset(fnts, 0, sizeof(fnts));
1562 
1563     if (fonts != 0)
1564 	myfonts = *fonts;
1565     if (!check_fontname(myfonts.f_n))
1566 	return 0;
1567 
1568     if (fontnum == fontMenu_fontescape
1569 	&& myfonts.f_n != screen->MenuFontName(fontnum)) {
1570 	if ((tmpname = x_strdup(myfonts.f_n)) == 0)
1571 	    return 0;
1572     }
1573 
1574     TRACE(("Begin Cgs - xtermLoadFont(%s)\n", myfonts.f_n));
1575     releaseWindowGCs(xw, win);
1576 
1577 #define DbgResource(name, field, index) \
1578     TRACE(("xtermLoadFont #%d "name" %s%s\n", \
1579     	   fontnum, \
1580 	   (fnts[index].warn == fwResource) ? "*" : " ", \
1581 	   NonNull(myfonts.field)))
1582     DbgResource("normal", f_n, fNorm);
1583     DbgResource("bold  ", f_b, fBold);
1584 #if OPT_WIDE_CHARS
1585     DbgResource("wide  ", f_w, fWide);
1586     DbgResource("w/bold", f_wb, fWBold);
1587 #endif
1588 
1589     if (!loadNormFP(xw,
1590 		    &myfonts.f_n,
1591 		    &fnts[fNorm],
1592 		    fontnum))
1593 	goto bad;
1594 
1595     if (!loadBoldFP(xw,
1596 		    &myfonts.f_b,
1597 		    &fnts[fBold],
1598 		    myfonts.f_n,
1599 		    &fnts[fNorm],
1600 		    fontnum))
1601 	goto bad;
1602 
1603     /*
1604      * If there is no widefont specified, fake it by doubling AVERAGE_WIDTH
1605      * of normal fonts XLFD, and asking for it.  This plucks out 18x18ja
1606      * and 12x13ja as the corresponding fonts for 9x18 and 6x13.
1607      */
1608     if_OPT_WIDE_CHARS(screen, {
1609 
1610 	if (!loadWideFP(xw,
1611 			&myfonts.f_w,
1612 			&fnts[fWide],
1613 			myfonts.f_n,
1614 			&fnts[fNorm],
1615 			fontnum))
1616 	    goto bad;
1617 
1618 	if (!loadWBoldFP(xw,
1619 			 &myfonts.f_wb,
1620 			 &fnts[fWBold],
1621 			 myfonts.f_w,
1622 			 &fnts[fWide],
1623 			 myfonts.f_b,
1624 			 &fnts[fBold],
1625 			 fontnum))
1626 	    goto bad;
1627 
1628     });
1629 
1630     /*
1631      * Normal/bold fonts should be the same width.  Also, the min/max
1632      * values should be the same.
1633      */
1634     if (fnts[fNorm].fs != 0
1635 	&& fnts[fBold].fs != 0
1636 	&& (!is_fixed_font(fnts[fNorm].fs)
1637 	    || !is_fixed_font(fnts[fBold].fs)
1638 	    || differing_widths(fnts[fNorm].fs, fnts[fBold].fs))) {
1639 	TRACE(("Proportional font! normal %d/%d, bold %d/%d\n",
1640 	       fnts[fNorm].fs->min_bounds.width,
1641 	       fnts[fNorm].fs->max_bounds.width,
1642 	       fnts[fBold].fs->min_bounds.width,
1643 	       fnts[fBold].fs->max_bounds.width));
1644 	proportional = True;
1645     }
1646 
1647     if_OPT_WIDE_CHARS(screen, {
1648 	if (fnts[fWide].fs != 0
1649 	    && fnts[fWBold].fs != 0
1650 	    && (!is_fixed_font(fnts[fWide].fs)
1651 		|| !is_fixed_font(fnts[fWBold].fs)
1652 		|| differing_widths(fnts[fWide].fs, fnts[fWBold].fs))) {
1653 	    TRACE(("Proportional font! wide %d/%d, wide bold %d/%d\n",
1654 		   fnts[fWide].fs->min_bounds.width,
1655 		   fnts[fWide].fs->max_bounds.width,
1656 		   fnts[fWBold].fs->min_bounds.width,
1657 		   fnts[fWBold].fs->max_bounds.width));
1658 	    proportional = True;
1659 	}
1660     });
1661 
1662     /* TODO : enforce that the width of the wide font is 2* the width
1663        of the narrow font */
1664 
1665     /*
1666      * If we're switching fonts, free the old ones.  Otherwise we'll leak
1667      * the memory that is associated with the old fonts.  The
1668      * XLoadQueryFont call allocates a new XFontStruct.
1669      */
1670     xtermCloseFonts(xw, screen->fnts);
1671 #if OPT_WIDE_ATTRS
1672     xtermCloseFonts(xw, screen->ifnts);
1673     screen->ifnts_ok = False;
1674 #endif
1675 
1676     xtermCopyFontInfo(GetNormalFont(screen, fNorm), &fnts[fNorm]);
1677     xtermCopyFontInfo(GetNormalFont(screen, fBold), &fnts[fBold]);
1678 #if OPT_WIDE_CHARS
1679     xtermCopyFontInfo(GetNormalFont(screen, fWide), &fnts[fWide]);
1680     if (fnts[fWBold].fs == NULL)
1681 	xtermCopyFontInfo(GetNormalFont(screen, fWide), &fnts[fWide]);
1682     xtermCopyFontInfo(GetNormalFont(screen, fWBold), &fnts[fWBold]);
1683 #endif
1684 
1685     xtermUpdateFontGCs(xw, getNormalFont);
1686 
1687 #if OPT_BOX_CHARS
1688     screen->allow_packing = proportional;
1689     setupPackedFonts(xw);
1690 #endif
1691     screen->fnt_prop = (Boolean) (proportional && !(screen->force_packed));
1692     screen->fnt_boxes = 1;
1693 
1694 #if OPT_BOX_CHARS
1695     /*
1696      * xterm uses character positions 1-31 of a font for the line-drawing
1697      * characters.  Check that they are all present.  The null character
1698      * (0) is special, and is not used.
1699      */
1700 #if OPT_RENDERFONT
1701     if (UsingRenderFont(xw)) {
1702 	/*
1703 	 * FIXME: we shouldn't even be here if we're using Xft.
1704 	 */
1705 	screen->fnt_boxes = 0;
1706 	TRACE(("assume Xft missing line-drawing chars\n"));
1707     } else
1708 #endif
1709     {
1710 	unsigned ch;
1711 
1712 #if OPT_TRACE
1713 #define TRACE_MISS(index) show_font_misses(#index, &fnts[index])
1714 	TRACE_MISS(fNorm);
1715 	TRACE_MISS(fBold);
1716 #if OPT_WIDE_CHARS
1717 	TRACE_MISS(fWide);
1718 	TRACE_MISS(fWBold);
1719 #endif
1720 #endif
1721 
1722 #if OPT_WIDE_CHARS
1723 	if (screen->utf8_mode || screen->unicode_font) {
1724 	    UIntSet(screen->fnt_boxes, 2);
1725 	    for (ch = 1; ch < 32; ch++) {
1726 		unsigned n = dec2ucs(screen, ch);
1727 		if ((n != UCS_REPL)
1728 		    && (n != ch)
1729 		    && (screen->fnt_boxes & 2)) {
1730 		    if (xtermMissingChar(n, &fnts[fNorm]) ||
1731 			xtermMissingChar(n, &fnts[fBold])) {
1732 			UIntClr(screen->fnt_boxes, 2);
1733 			TRACE(("missing graphics character #%d, U+%04X\n",
1734 			       ch, n));
1735 			break;
1736 		    }
1737 		}
1738 	    }
1739 	}
1740 #endif
1741 
1742 	for (ch = 1; ch < 32; ch++) {
1743 	    if (xtermMissingChar(ch, &fnts[fNorm])) {
1744 		TRACE(("missing normal char #%d\n", ch));
1745 		UIntClr(screen->fnt_boxes, 1);
1746 		break;
1747 	    }
1748 	    if (xtermMissingChar(ch, &fnts[fBold])) {
1749 		TRACE(("missing bold   char #%d\n", ch));
1750 		UIntClr(screen->fnt_boxes, 1);
1751 		break;
1752 	    }
1753 	}
1754 
1755 	TRACE(("Will %suse internal line-drawing characters (mode %d)\n",
1756 	       screen->fnt_boxes ? "not " : "",
1757 	       screen->fnt_boxes));
1758     }
1759 #endif
1760 
1761     if (screen->always_bold_mode) {
1762 	screen->enbolden = screen->bold_mode;
1763     } else {
1764 	screen->enbolden = screen->bold_mode
1765 	    && ((fnts[fNorm].fs == fnts[fBold].fs)
1766 		|| same_font_name(myfonts.f_n, myfonts.f_b));
1767     }
1768     TRACE(("Will %suse 1-pixel offset/overstrike to simulate bold\n",
1769 	   screen->enbolden ? "" : "not "));
1770 
1771     set_menu_font(False);
1772     screen->menu_font_number = fontnum;
1773     set_menu_font(True);
1774     if (tmpname) {		/* if setting escape or sel */
1775 	if (screen->MenuFontName(fontnum))
1776 	    FREE_STRING(screen->MenuFontName(fontnum));
1777 	screen->MenuFontName(fontnum) = tmpname;
1778 	if (fontnum == fontMenu_fontescape) {
1779 	    update_font_escape();
1780 	}
1781 #if OPT_SHIFT_FONTS
1782 	screen->menu_font_sizes[fontnum] = FontSize(fnts[fNorm].fs);
1783 #endif
1784     }
1785     set_cursor_gcs(xw);
1786     xtermUpdateFontInfo(xw, doresize);
1787     TRACE(("Success Cgs - xtermLoadFont\n"));
1788 #if OPT_REPORT_FONTS
1789     reportVTFontInfo(xw, fontnum);
1790 #endif
1791     FREE_FNAME(f_n);
1792     FREE_FNAME(f_b);
1793 #if OPT_WIDE_CHARS
1794     FREE_FNAME(f_w);
1795     FREE_FNAME(f_wb);
1796 #endif
1797     if (fnts[fNorm].fn == fnts[fBold].fn) {
1798 	free(fnts[fNorm].fn);
1799     } else {
1800 	free(fnts[fNorm].fn);
1801 	free(fnts[fBold].fn);
1802     }
1803 #if OPT_WIDE_CHARS
1804     free(fnts[fWide].fn);
1805     free(fnts[fWBold].fn);
1806 #endif
1807     xtermSetWinSize(xw);
1808     return 1;
1809 
1810   bad:
1811     free(tmpname);
1812 
1813 #if OPT_RENDERFONT
1814     if ((fontnum == fontMenu_fontsel) && (fontnum != screen->menu_font_number)) {
1815 	int old_fontnum = screen->menu_font_number;
1816 #if OPT_TOOLBAR
1817 	SetItemSensitivity(fontMenuEntries[fontnum].widget, True);
1818 #endif
1819 	Bell(xw, XkbBI_MinorError, 0);
1820 	myfonts.f_n = screen->MenuFontName(old_fontnum);
1821 	return xtermLoadFont(xw, &myfonts, doresize, old_fontnum);
1822     } else if (x_strcasecmp(myfonts.f_n, DEFFONT)) {
1823 	int code;
1824 
1825 	myfonts.f_n = x_strdup(DEFFONT);
1826 	TRACE(("...recovering for TrueType fonts\n"));
1827 	code = xtermLoadFont(xw, &myfonts, doresize, fontnum);
1828 	if (code) {
1829 	    if (fontnum != fontMenu_fontsel) {
1830 		SetItemSensitivity(fontMenuEntries[fontnum].widget,
1831 				   UsingRenderFont(xw));
1832 	    }
1833 	    TRACE(("...recovered size %dx%d\n",
1834 		   FontHeight(screen),
1835 		   FontWidth(screen)));
1836 	}
1837 	return code;
1838     }
1839 #endif
1840 
1841     releaseWindowGCs(xw, win);
1842 
1843     xtermCloseFonts(xw, fnts);
1844     TRACE(("Fail Cgs - xtermLoadFont\n"));
1845     return 0;
1846 }
1847 
1848 #if OPT_WIDE_ATTRS
1849 /*
1850  * (Attempt to) load matching italics for the current normal/bold/etc fonts.
1851  * If the attempt fails for a given style, use the non-italic font.
1852  */
1853 void
xtermLoadItalics(XtermWidget xw)1854 xtermLoadItalics(XtermWidget xw)
1855 {
1856     TScreen *screen = TScreenOf(xw);
1857 
1858     if (UseItalicFont(screen) && !screen->ifnts_ok) {
1859 	int n;
1860 	FontNameProperties *fp;
1861 	XTermFonts *data;
1862 
1863 	screen->ifnts_ok = True;
1864 	for (n = 0; n < fMAX; ++n) {
1865 	    switch (n) {
1866 	    case fNorm:
1867 		/* FALLTHRU */
1868 	    case fBold:
1869 		/* FALLTHRU */
1870 #if OPT_WIDE_CHARS
1871 	    case fWide:
1872 		/* FALLTHRU */
1873 	    case fWBold:
1874 #endif
1875 		/* FALLTHRU */
1876 		data = getItalicFont(screen, n);
1877 
1878 		/*
1879 		 * FIXME - need to handle font-leaks
1880 		 */
1881 		data->fs = 0;
1882 		if (getNormalFont(screen, n)->fs != 0 &&
1883 		    (fp = get_font_name_props(screen->display,
1884 					      getNormalFont(screen, n)->fs,
1885 					      0)) != 0) {
1886 		    if (!open_italic_font(xw, n, fp, data)) {
1887 			if (n > 0) {
1888 			    xtermCopyFontInfo(data,
1889 					      getItalicFont(screen, n - 1));
1890 			} else {
1891 			    xtermOpenFont(xw,
1892 					  getNormalFont(screen, n)->fn,
1893 					  data, False);
1894 			}
1895 		    }
1896 		}
1897 		break;
1898 	    }
1899 	}
1900     }
1901 }
1902 #endif
1903 
1904 #if OPT_LOAD_VTFONTS || OPT_WIDE_CHARS
1905 /*
1906  * Collect font-names that we can modify with the load-vt-fonts() action.
1907  */
1908 #define MERGE_SUBFONT(dst,src,name) \
1909 	if (IsEmpty(dst.name)) { \
1910 	    TRACE(("MERGE_SUBFONT " #dst "." #name " merge \"%s\"\n", NonNull(src.name))); \
1911 	    dst.name = x_strdup(src.name); \
1912 	} else { \
1913 	    TRACE(("MERGE_SUBFONT " #dst "." #name " found \"%s\"\n", NonNull(dst.name))); \
1914 	}
1915 #define MERGE_SUBLIST(dst,src,name) \
1916 	if (merge_sublist(&(dst.fonts.x11.name), src.fonts.x11.name)) { \
1917 	    TRACE(("MERGE_SUBLIST " #dst "." #name " merge \"%s\"\n", src.fonts.x11.name[0])); \
1918 	} else { \
1919 	    TRACE(("MERGE_SUBLIST " #dst "." #name " found \"%s\"\n", dst.fonts.x11.name[0])); \
1920 	}
1921 
1922 #define INFER_SUBFONT(dst,src,name) \
1923 	if (IsEmpty(dst.name)) { \
1924 	    TRACE(("INFER_SUBFONT " #dst "." #name " will infer\n")); \
1925 	    dst.name = x_strdup(""); \
1926 	} else { \
1927 	    TRACE(("INFER_SUBFONT " #dst "." #name " found \"%s\"\n", NonNull(dst.name))); \
1928 	}
1929 
1930 #define FREE_MENU_FONTS(dst) \
1931 	TRACE(("FREE_MENU_FONTS " #dst "\n")); \
1932 	for (n = fontMenu_default; n <= fontMenu_lastBuiltin; ++n) { \
1933 	    for (m = 0; m < fMAX; ++m) { \
1934 		FREE_STRING(dst.menu_font_names[n][m]); \
1935 		dst.menu_font_names[n][m] = 0; \
1936 	    } \
1937 	}
1938 
1939 #define COPY_MENU_FONTS(dst,src) \
1940 	TRACE(("COPY_MENU_FONTS " #src " to " #dst "\n")); \
1941 	for (n = fontMenu_default; n <= fontMenu_lastBuiltin; ++n) { \
1942 	    for (m = 0; m < fMAX; ++m) { \
1943 		FREE_STRING(dst.menu_font_names[n][m]); \
1944 		dst.menu_font_names[n][m] = x_strdup(src.menu_font_names[n][m]); \
1945 	    } \
1946 	    TRACE((".. " #dst ".menu_fonts_names[%d] = %s\n", n, NonNull(dst.menu_font_names[n][fNorm]))); \
1947 	}
1948 
1949 #define COPY_DEFAULT_FONTS(target, source) \
1950 	TRACE(("COPY_DEFAULT_FONTS " #source " to " #target "\n")); \
1951 	xtermCopyVTFontNames(&target.default_font, &source.default_font)
1952 
1953 #define COPY_X11_FONTLISTS(target, source) \
1954 	TRACE(("COPY_X11_FONTLISTS " #source " to " #target "\n")); \
1955 	xtermCopyFontLists(xw, &target.fonts.x11, &source.fonts.x11)
1956 
1957 static void
xtermCopyVTFontNames(VTFontNames * target,VTFontNames * source)1958 xtermCopyVTFontNames(VTFontNames * target, VTFontNames * source)
1959 {
1960 #define COPY_IT(name,field) \
1961     TRACE((".. "#name" = %s\n", NonNull(source->field))); \
1962     free(target->field); \
1963     target->field = x_strdup(source->field)
1964 
1965     TRACE(("xtermCopyVTFontNames\n"));
1966 
1967     COPY_IT(font, f_n);
1968     COPY_IT(boldFont, f_b);
1969 
1970 #if OPT_WIDE_CHARS
1971     COPY_IT(wideFont, f_w);
1972     COPY_IT(wideBoldFont, f_wb);
1973 #endif
1974 #undef COPY_IT
1975 }
1976 
1977 static void
xtermCopyFontLists(XtermWidget xw,VTFontList * target,VTFontList * source)1978 xtermCopyFontLists(XtermWidget xw, VTFontList * target, VTFontList * source)
1979 {
1980 #define COPY_IT(name,field) \
1981     copyFontList(&(target->field), source->field); \
1982     TRACE_ARGV(".. " #name, source->field)
1983 
1984     (void) xw;
1985     TRACE(("xtermCopyFontLists %s ->%s\n",
1986 	   whichFontList(xw, source),
1987 	   whichFontList(xw, target)));
1988 
1989     COPY_IT(font, list_n);
1990     COPY_IT(fontBold, list_b);
1991 #if OPT_WIDE_ATTRS || OPT_RENDERWIDE
1992     COPY_IT(fontItal, list_i);
1993     COPY_IT(fontBtal, list_bi);
1994 #endif
1995 #if OPT_WIDE_CHARS
1996     COPY_IT(wideFont, list_w);
1997     COPY_IT(wideBoldFont, list_wb);
1998     COPY_IT(wideItalFont, list_wi);
1999     COPY_IT(wideBtalFont, list_wbi);
2000 #endif
2001 #undef COPY_IT
2002 }
2003 
2004 void
xtermSaveVTFonts(XtermWidget xw)2005 xtermSaveVTFonts(XtermWidget xw)
2006 {
2007     TScreen *screen = TScreenOf(xw);
2008     Cardinal n, m;
2009 
2010     if (!screen->savedVTFonts) {
2011 
2012 	screen->savedVTFonts = True;
2013 	TRACE(("xtermSaveVTFonts saving original\n"));
2014 	COPY_DEFAULT_FONTS(screen->cacheVTFonts, xw->misc);
2015 	COPY_X11_FONTLISTS(screen->cacheVTFonts, xw->work);
2016 	COPY_MENU_FONTS(screen->cacheVTFonts, xw->screen);
2017     }
2018 }
2019 
2020 #define SAME_STRING(x,y) ((x) == (y) || ((x) && (y) && !strcmp(x, y)))
2021 #define SAME_MEMBER(n)   SAME_STRING(a->n, b->n)
2022 
2023 static Boolean
sameSubResources(SubResourceRec * a,SubResourceRec * b)2024 sameSubResources(SubResourceRec * a, SubResourceRec * b)
2025 {
2026     Boolean result = True;
2027 
2028     if (!SAME_MEMBER(default_font.f_n)
2029 	|| !SAME_MEMBER(default_font.f_b)
2030 #if OPT_WIDE_CHARS
2031 	|| !SAME_MEMBER(default_font.f_w)
2032 	|| !SAME_MEMBER(default_font.f_wb)
2033 #endif
2034 	) {
2035 	TRACE(("sameSubResources: default_font differs\n"));
2036 	result = False;
2037     } else {
2038 	int n;
2039 
2040 	for (n = 0; n < NMENUFONTS; ++n) {
2041 	    if (!SAME_MEMBER(menu_font_names[n][fNorm])) {
2042 		TRACE(("sameSubResources: menu_font_names[%d] differs\n", n));
2043 		result = False;
2044 		break;
2045 	    }
2046 	}
2047     }
2048 
2049     return result;
2050 }
2051 
2052 /*
2053  * Load the "VT" font names from the given subresource name/class.  These
2054  * correspond to the VT100 resources.
2055  */
2056 static Bool
xtermLoadVTFonts(XtermWidget xw,String myName,String myClass)2057 xtermLoadVTFonts(XtermWidget xw, String myName, String myClass)
2058 {
2059     SubResourceRec subresourceRec;
2060     SubResourceRec referenceRec;
2061 
2062     /*
2063      * These are duplicates of the VT100 font resources, but with a special
2064      * application/classname passed in to distinguish them.
2065      */
2066     static XtResource font_resources[] =
2067     {
2068 	Sres(XtNfont, XtCFont, default_font.f_n, DEFFONT),
2069 	Sres(XtNboldFont, XtCBoldFont, default_font.f_b, DEFBOLDFONT),
2070 #if OPT_WIDE_CHARS
2071 	Sres(XtNwideFont, XtCWideFont, default_font.f_w, DEFWIDEFONT),
2072 	Sres(XtNwideBoldFont, XtCWideBoldFont, default_font.f_wb, DEFWIDEBOLDFONT),
2073 #endif
2074 	Sres(XtNfont1, XtCFont1, MenuFontName(fontMenu_font1), NULL),
2075 	Sres(XtNfont2, XtCFont2, MenuFontName(fontMenu_font2), NULL),
2076 	Sres(XtNfont3, XtCFont3, MenuFontName(fontMenu_font3), NULL),
2077 	Sres(XtNfont4, XtCFont4, MenuFontName(fontMenu_font4), NULL),
2078 	Sres(XtNfont5, XtCFont5, MenuFontName(fontMenu_font5), NULL),
2079 	Sres(XtNfont6, XtCFont6, MenuFontName(fontMenu_font6), NULL),
2080 	Sres(XtNfont7, XtCFont7, MenuFontName(fontMenu_font7), NULL),
2081     };
2082     Cardinal n, m;
2083     Bool status = True;
2084     TScreen *screen = TScreenOf(xw);
2085 
2086     TRACE(("called xtermLoadVTFonts(name=%s, class=%s)\n",
2087 	   NonNull(myName), NonNull(myClass)));
2088 
2089     xtermSaveVTFonts(xw);
2090 
2091     if (IsEmpty(myName)) {
2092 	TRACE(("xtermLoadVTFonts restoring original\n"));
2093 	COPY_DEFAULT_FONTS(xw->misc, screen->cacheVTFonts);
2094 	COPY_X11_FONTLISTS(xw->work, screen->cacheVTFonts);
2095 	FREE_MENU_FONTS(xw->screen);
2096 	COPY_MENU_FONTS(xw->screen, screen->cacheVTFonts);
2097     } else {
2098 	TRACE(("xtermLoadVTFonts(%s, %s)\n", myName, myClass));
2099 
2100 	memset(&referenceRec, 0, sizeof(referenceRec));
2101 	memset(&subresourceRec, 0, sizeof(subresourceRec));
2102 	XtGetSubresources((Widget) xw, (XtPointer) &subresourceRec,
2103 			  myName, myClass,
2104 			  font_resources,
2105 			  (Cardinal) XtNumber(font_resources),
2106 			  NULL, (Cardinal) 0);
2107 
2108 	/*
2109 	 * XtGetSubresources returns no status, so we compare the returned
2110 	 * data against a zero'd struct to see if any data is returned.
2111 	 */
2112 	if (memcmp(&referenceRec, &subresourceRec, sizeof(referenceRec))
2113 	    && !sameSubResources(&(screen->cacheVTFonts), &subresourceRec)) {
2114 
2115 	    screen->mergedVTFonts = True;
2116 
2117 	    /*
2118 	     * To make it simple, reallocate the strings returned by
2119 	     * XtGetSubresources.  We can free our own strings, but not theirs.
2120 	     */
2121 	    ALLOC_STRING(subresourceRec.default_font.f_n);
2122 	    ALLOC_STRING(subresourceRec.default_font.f_b);
2123 #if OPT_WIDE_CHARS
2124 	    ALLOC_STRING(subresourceRec.default_font.f_w);
2125 	    ALLOC_STRING(subresourceRec.default_font.f_wb);
2126 #endif
2127 	    for (n = fontMenu_font1; n <= fontMenu_lastBuiltin; ++n) {
2128 		ALLOC_STRING(subresourceRec.MenuFontName(n));
2129 	    }
2130 
2131 	    /*
2132 	     * Now, save the string to a font-list for consistency
2133 	     */
2134 #define ALLOC_SUBLIST(which,field) \
2135 	    save2FontList(xw, "cached", \
2136 			  &(subresourceRec.fonts), \
2137 			  which, \
2138 			  subresourceRec.default_font.field, False)
2139 
2140 	    ALLOC_SUBLIST(fNorm, f_n);
2141 	    ALLOC_SUBLIST(fBold, f_b);
2142 #if OPT_WIDE_CHARS
2143 	    ALLOC_SUBLIST(fWide, f_w);
2144 	    ALLOC_SUBLIST(fWBold, f_wb);
2145 #endif
2146 
2147 	    /*
2148 	     * If a particular resource value was not found, use the original.
2149 	     */
2150 	    MERGE_SUBFONT(subresourceRec, xw->misc, default_font.f_n);
2151 	    INFER_SUBFONT(subresourceRec, xw->misc, default_font.f_b);
2152 	    MERGE_SUBLIST(subresourceRec, xw->work, list_n);
2153 	    MERGE_SUBLIST(subresourceRec, xw->work, list_b);
2154 #if OPT_WIDE_CHARS
2155 	    INFER_SUBFONT(subresourceRec, xw->misc, default_font.f_w);
2156 	    INFER_SUBFONT(subresourceRec, xw->misc, default_font.f_wb);
2157 	    MERGE_SUBLIST(subresourceRec, xw->work, list_w);
2158 	    MERGE_SUBLIST(subresourceRec, xw->work, list_wb);
2159 #endif
2160 	    for (n = fontMenu_font1; n <= fontMenu_lastBuiltin; ++n) {
2161 		MERGE_SUBFONT(subresourceRec, xw->screen, MenuFontName(n));
2162 	    }
2163 
2164 	    /*
2165 	     * Finally, copy the subresource data to the widget.
2166 	     */
2167 	    COPY_DEFAULT_FONTS(xw->misc, subresourceRec);
2168 	    COPY_X11_FONTLISTS(xw->work, subresourceRec);
2169 	    FREE_MENU_FONTS(xw->screen);
2170 	    COPY_MENU_FONTS(xw->screen, subresourceRec);
2171 
2172 	    FREE_STRING(screen->MenuFontName(fontMenu_default));
2173 	    FREE_STRING(screen->menu_font_names[0][fBold]);
2174 	    screen->MenuFontName(fontMenu_default) = x_strdup(DefaultFontN(xw));
2175 	    screen->menu_font_names[0][fBold] = x_strdup(DefaultFontB(xw));
2176 #if OPT_WIDE_CHARS
2177 	    FREE_STRING(screen->menu_font_names[0][fWide]);
2178 	    FREE_STRING(screen->menu_font_names[0][fWBold]);
2179 	    screen->menu_font_names[0][fWide] = x_strdup(DefaultFontW(xw));
2180 	    screen->menu_font_names[0][fWBold] = x_strdup(DefaultFontWB(xw));
2181 #endif
2182 	    /*
2183 	     * And remove our copies of strings.
2184 	     */
2185 	    FREE_STRING(subresourceRec.default_font.f_n);
2186 	    FREE_STRING(subresourceRec.default_font.f_b);
2187 #if OPT_WIDE_CHARS
2188 	    FREE_STRING(subresourceRec.default_font.f_w);
2189 	    FREE_STRING(subresourceRec.default_font.f_wb);
2190 #endif
2191 	    for (n = fontMenu_font1; n <= fontMenu_lastBuiltin; ++n) {
2192 		FREE_STRING(subresourceRec.MenuFontName(n));
2193 	    }
2194 	} else {
2195 	    TRACE(("...no resources found\n"));
2196 	    status = False;
2197 	}
2198     }
2199     TRACE((".. xtermLoadVTFonts: %d\n", status));
2200     return status;
2201 }
2202 
2203 #if OPT_WIDE_CHARS
2204 static Bool
isWideFont(XFontStruct * fp,const char * tag,Bool nullOk)2205 isWideFont(XFontStruct *fp, const char *tag, Bool nullOk)
2206 {
2207     Bool result = False;
2208 
2209     (void) tag;
2210     if (okFont(fp)) {
2211 	unsigned count = countGlyphs(fp);
2212 	TRACE(("isWideFont(%s) found %d cells\n", tag, count));
2213 	result = (count > 256) ? True : False;
2214     } else {
2215 	result = nullOk;
2216     }
2217     return result;
2218 }
2219 
2220 /*
2221  * If the current fonts are not wide, load the UTF8 fonts.
2222  *
2223  * Called during initialization (for wide-character mode), the fonts have not
2224  * been setup, so we pass nullOk=True to isWideFont().
2225  *
2226  * Called after initialization, e.g., in response to the UTF-8 menu entry
2227  * (starting from narrow character mode), it checks if the fonts are not wide.
2228  */
2229 Bool
xtermLoadWideFonts(XtermWidget xw,Bool nullOk)2230 xtermLoadWideFonts(XtermWidget xw, Bool nullOk)
2231 {
2232     TScreen *screen = TScreenOf(xw);
2233     Bool result;
2234 
2235     if (EmptyFont(GetNormalFont(screen, fWide)->fs)) {
2236 	result = (isWideFont(GetNormalFont(screen, fNorm)->fs, "normal", nullOk)
2237 		  && isWideFont(GetNormalFont(screen, fBold)->fs, "bold", nullOk));
2238     } else {
2239 	result = (isWideFont(GetNormalFont(screen, fWide)->fs, "wide", nullOk)
2240 		  && isWideFont(GetNormalFont(screen, fWBold)->fs,
2241 				"wide-bold", nullOk));
2242 	if (result && !screen->utf8_latin1) {
2243 	    result = (isWideFont(GetNormalFont(screen, fNorm)->fs, "normal", nullOk)
2244 		      && isWideFont(GetNormalFont(screen, fBold)->fs,
2245 				    "bold", nullOk));
2246 	}
2247     }
2248     if (!result) {
2249 	TRACE(("current fonts are not all wide%s\n", nullOk ? " nullOk" : ""));
2250 	result = xtermLoadVTFonts(xw, XtNutf8Fonts, XtCUtf8Fonts);
2251     }
2252     TRACE(("xtermLoadWideFonts:%d\n", result));
2253     return result;
2254 }
2255 #endif /* OPT_WIDE_CHARS */
2256 
2257 /*
2258  * Restore the default fonts, i.e., if we had switched to wide-fonts.
2259  */
2260 Bool
xtermLoadDefaultFonts(XtermWidget xw)2261 xtermLoadDefaultFonts(XtermWidget xw)
2262 {
2263     Bool result;
2264     result = xtermLoadVTFonts(xw, NULL, NULL);
2265     TRACE(("xtermLoadDefaultFonts:%d\n", result));
2266     return result;
2267 }
2268 #endif /* OPT_LOAD_VTFONTS || OPT_WIDE_CHARS */
2269 
2270 #if OPT_LOAD_VTFONTS
2271 void
HandleLoadVTFonts(Widget w,XEvent * event GCC_UNUSED,String * params,Cardinal * param_count)2272 HandleLoadVTFonts(Widget w,
2273 		  XEvent *event GCC_UNUSED,
2274 		  String *params,
2275 		  Cardinal *param_count)
2276 {
2277     XtermWidget xw;
2278 
2279     if ((xw = getXtermWidget(w)) != 0) {
2280 	static char empty[] = "";	/* appease strict compilers */
2281 
2282 	TScreen *screen = TScreenOf(xw);
2283 	char name_buf[80];
2284 	String name = (String) ((*param_count > 0) ? params[0] : empty);
2285 	char *myName = MyStackAlloc(strlen(name) + 1, name_buf);
2286 
2287 	TRACE(("HandleLoadVTFonts(%d)\n", *param_count));
2288 	if (myName != 0) {
2289 	    char class_buf[80];
2290 	    String convert = (String) ((*param_count > 1) ? params[1] : myName);
2291 	    char *myClass = MyStackAlloc(strlen(convert) + 1, class_buf);
2292 
2293 	    strcpy(myName, name);
2294 	    if (myClass != 0) {
2295 		strcpy(myClass, convert);
2296 		if (*param_count == 1)
2297 		    myClass[0] = x_toupper(myClass[0]);
2298 
2299 		if (xtermLoadVTFonts(xw, myName, myClass)) {
2300 		    int n;
2301 		    /*
2302 		     * When switching fonts, try to preserve the font-menu
2303 		     * selection, since it is less surprising to do that (if
2304 		     * the font-switching can be undone) than to switch to
2305 		     * "Default".
2306 		     */
2307 		    int font_number = screen->menu_font_number;
2308 		    if (font_number > fontMenu_lastBuiltin)
2309 			font_number = fontMenu_lastBuiltin;
2310 		    for (n = 0; n < NMENUFONTS; ++n) {
2311 			screen->menu_font_sizes[n] = 0;
2312 		    }
2313 		    if (font_number == fontMenu_default) {
2314 			SetVTFont(xw, font_number, True, defaultVTFontNames(xw));
2315 		    } else {
2316 			SetVTFont(xw, font_number, True, NULL);
2317 		    }
2318 		}
2319 		MyStackFree(myClass, class_buf);
2320 	    }
2321 	    MyStackFree(myName, name_buf);
2322 	}
2323     }
2324 }
2325 #endif /* OPT_LOAD_VTFONTS */
2326 
2327 /*
2328  * Set the limits for the box that outlines the cursor.
2329  */
2330 void
xtermSetCursorBox(TScreen * screen)2331 xtermSetCursorBox(TScreen *screen)
2332 {
2333     static XPoint VTbox[NBOX];
2334     XPoint *vp;
2335     int fw = FontWidth(screen) - 1;
2336     int fh = FontHeight(screen) - 1;
2337     int ww = isCursorBar(screen) ? 1 : fw;
2338     int hh = isCursorUnderline(screen) ? 1 : fh;
2339 
2340     vp = &VTbox[1];
2341     (vp++)->x = (short) ww;
2342     (vp++)->y = (short) hh;
2343     (vp++)->x = (short) -ww;
2344     vp->y = (short) -hh;
2345 
2346     screen->box = VTbox;
2347 }
2348 
2349 #if OPT_RENDERFONT
2350 
2351 #define CACHE_XFT(dst,src) if (src.font != 0) {\
2352 	    int err = checkXftWidth(xw, &(dst[fontnum]), &src);\
2353 	    TRACE(("Xft metrics %s[%d] = %d (%d,%d)%s advance %d, actual %d%s%s\n",\
2354 		#dst,\
2355 		fontnum,\
2356 		src.font->height,\
2357 		src.font->ascent,\
2358 		src.font->descent,\
2359 		((src.font->ascent + src.font->descent) > src.font->height ? "*" : ""),\
2360 		src.font->max_advance_width,\
2361 		dst[fontnum].map.min_width,\
2362 		dst[fontnum].map.mixed ? " mixed" : "",\
2363 		err ? " ERROR" : ""));\
2364 	    if (err) {\
2365 		xtermCloseXft(screen, &src);\
2366 		memset((&dst[fontnum]), 0, sizeof(dst[fontnum]));\
2367 		failed += err;\
2368 	    }\
2369 	}
2370 
2371 #if OPT_REPORT_FONTS
2372 static FcChar32
xtermXftFirstChar(XftFont * xft)2373 xtermXftFirstChar(XftFont *xft)
2374 {
2375     FcChar32 map[FC_CHARSET_MAP_SIZE];
2376     FcChar32 next;
2377     FcChar32 first;
2378     int i;
2379 
2380     first = FcCharSetFirstPage(xft->charset, map, &next);
2381     for (i = 0; i < FC_CHARSET_MAP_SIZE; i++) {
2382 	if (map[i]) {
2383 	    FcChar32 bits = map[i];
2384 	    first += (FcChar32) i *32;
2385 	    while (!(bits & 0x1)) {
2386 		bits >>= 1;
2387 		first++;
2388 	    }
2389 	    break;
2390 	}
2391     }
2392     return first;
2393 }
2394 
2395 static FcChar32
xtermXftLastChar(XftFont * xft)2396 xtermXftLastChar(XftFont *xft)
2397 {
2398     FcChar32 this, last, next;
2399     FcChar32 map[FC_CHARSET_MAP_SIZE];
2400     int i;
2401     last = FcCharSetFirstPage(xft->charset, map, &next);
2402     while ((this = FcCharSetNextPage(xft->charset, map, &next)) != FC_CHARSET_DONE)
2403 	last = this;
2404     last &= (FcChar32) ~ 0xff;
2405     for (i = FC_CHARSET_MAP_SIZE - 1; i >= 0; i--) {
2406 	if (map[i]) {
2407 	    FcChar32 bits = map[i];
2408 	    last += (FcChar32) i *32 + 31;
2409 	    while (!(bits & 0x80000000)) {
2410 		last--;
2411 		bits <<= 1;
2412 	    }
2413 	    break;
2414 	}
2415     }
2416     return (FcChar32) last;
2417 }
2418 #endif /* OPT_REPORT_FONTS */
2419 
2420 #if OPT_TRACE
2421 
2422 #if !OPT_WIDE_CHARS
2423 static Char *
convertToUTF8(Char * buffer,int c)2424 convertToUTF8(Char *buffer, int c)
2425 {
2426     buffer[0] = (Char) c;
2427     buffer[1] = 0;
2428     return buffer;
2429 }
2430 #endif
2431 
2432 static void
dumpXft(XtermWidget xw,XTermXftFonts * data)2433 dumpXft(XtermWidget xw, XTermXftFonts *data)
2434 {
2435     XftFont *xft = data->font;
2436     TScreen *screen = TScreenOf(xw);
2437     VTwin *win = WhichVWin(screen);
2438 
2439     FcChar32 c;
2440     FcChar32 first = xtermXftFirstChar(xft);
2441     FcChar32 last = xtermXftLastChar(xft);
2442     FcChar32 dump;
2443     unsigned count = 0;
2444     unsigned too_high = 0;
2445     unsigned too_wide = 0;
2446     Boolean skip = False;
2447 
2448     TRACE(("dumpXft " TRACE_L "\n"));
2449     TRACE(("\tdata range U+%04X..U+%04X\n", first, last));
2450     TRACE(("\tcode\tcells\tdimensions\n"));
2451 #if OPT_TRACE < 2
2452     dump = 255;
2453 #else
2454     dump = last;
2455 #endif
2456     for (c = first; c <= last; ++c) {
2457 	if (FcCharSetHasChar(xft->charset, c)) {
2458 	    int width = CharWidth(screen, c);
2459 	    XGlyphInfo extents;
2460 	    Boolean big_x;
2461 	    Boolean big_y;
2462 
2463 	    XftTextExtents32(XtDisplay(xw), xft, &c, 1, &extents);
2464 	    big_x = (extents.width > win->f_width);
2465 	    big_y = (extents.height > win->f_height);
2466 
2467 	    if (c <= dump) {
2468 		Char buffer[80];
2469 
2470 		*convertToUTF8(buffer, c) = '\0';
2471 		TRACE(("%s%s\tU+%04X\t%d\t%.1f x %.1f\t%s\n",
2472 		       (big_y ? "y" : ""),
2473 		       (big_x ? "x" : ""),
2474 		       c, width,
2475 		       ((double) extents.height) / win->f_height,
2476 		       ((double) extents.width) / win->f_width,
2477 		       buffer));
2478 	    } else if (!skip) {
2479 		skip = True;
2480 		TRACE(("\t...skipping\n"));
2481 	    }
2482 	    if (big_y)
2483 		++too_high;
2484 	    if (big_x)
2485 		++too_wide;
2486 	    ++count;
2487 	}
2488     }
2489     TRACE((TRACE_R " %u total, %u too-high, %u too-wide\n", count, too_high, too_wide));
2490 }
2491 #define DUMP_XFT(xw, data) dumpXft(xw, data)
2492 #else
2493 #define DUMP_XFT(xw, data)	/* nothing */
2494 #endif
2495 
2496 /*
2497  * Check if this is a FC_COLOR font, which fontconfig misrepresents to "fix" a
2498  * problem with web browsers.  As of 2018/12 (4 years later), Xft does not work
2499  * with that.  Even with this workaround, fontconfig has at least one bug which
2500  * causes it to crash (Debian #917034).
2501  */
2502 #ifdef FC_COLOR
2503 #define GetFcBool(pattern, what) \
2504     (FcPatternGetBool(pattern, what, 0, &fcbogus) == FcResultMatch)
2505 
2506 static Boolean
isBogusXft(XftFont * font)2507 isBogusXft(XftFont *font)
2508 {
2509     Boolean result = False;
2510     if (font != 0) {
2511 	FcBool fcbogus;
2512 	if (GetFcBool(font->pattern, FC_COLOR) && fcbogus) {
2513 	    TRACE(("...matched color-bitmap font\n"));
2514 	    result = True;
2515 	} else if (GetFcBool(font->pattern, FC_OUTLINE) && !fcbogus) {
2516 	    TRACE(("...matched non-outline font\n"));
2517 	    /* This is legal for regular bitmap fonts - fontconfig attempts to
2518 	     * find a match - but problematic for misencoded color-bitmap fonts.
2519 	     */
2520 	}
2521     }
2522     return result;
2523 }
2524 #endif
2525 
2526 #if OPT_BOX_CHARS
2527 static void
setBrokenBoxChars(XtermWidget xw,Bool state)2528 setBrokenBoxChars(XtermWidget xw, Bool state)
2529 {
2530     TRACE(("setBrokenBoxChars %s\n", BtoS(state)));
2531     term->work.broken_box_chars = (Boolean) state;
2532     TScreenOf(xw)->broken_box_chars = (Boolean) state;
2533     update_font_boxchars();
2534 }
2535 
2536 #else
2537 #define setBrokenBoxChars(xw, state)	/* nothing */
2538 #endif
2539 
2540 static Boolean
checkedXftWidth(Display * dpy,XTermXftFonts * source,unsigned limit,Dimension * width,FcChar32 c)2541 checkedXftWidth(Display *dpy,
2542 		XTermXftFonts *source,
2543 		unsigned limit,
2544 		Dimension *width,
2545 		FcChar32 c)
2546 {
2547     Boolean result = False;
2548 
2549     if (FcCharSetHasChar(source->font->charset, c)) {
2550 	XGlyphInfo extents;
2551 
2552 	result = True;
2553 	XftTextExtents32(dpy, source->font, &c, 1, &extents);
2554 	if (*width < extents.width && extents.width <= limit) {
2555 	    *width = extents.width;
2556 	}
2557     }
2558     return result;
2559 }
2560 
2561 static int
checkXftWidth(XtermWidget xw,XTermXftFonts * target,XTermXftFonts * source)2562 checkXftWidth(XtermWidget xw, XTermXftFonts *target, XTermXftFonts *source)
2563 {
2564     FcChar32 c;
2565     FcChar32 last = xtermXftLastChar(source->font);
2566     Dimension limit = (Dimension) source->font->max_advance_width;
2567     Dimension width = 0;
2568     Dimension width2 = 0;
2569     int failed = 0;
2570 #if OPT_WIDE_CHARS
2571     Cardinal n;
2572 #endif
2573 
2574     target->font = source->font;
2575     target->pattern = source->pattern;
2576     target->map.min_width = 0;
2577     target->map.max_width = limit;
2578 
2579 #if OPT_WIDE_CHARS
2580     /*
2581      * Check if the line-drawing characters are all provided in the font.
2582      * If so, take that into account for the cell-widths.
2583      */
2584     for (n = 0; n < XtNumber(unicode_boxes) - 1; ++n) {
2585 	if (!checkedXftWidth(XtDisplay(xw),
2586 			     source,
2587 			     limit,
2588 			     &width2, unicode_boxes[n].code)) {
2589 	    width2 = 0;
2590 	    TRACE(("font omits U+%04X line-drawing symbol\n",
2591 		   unicode_boxes[n].code));
2592 	    break;
2593 	}
2594     }
2595 #else
2596     (void) width2;
2597 #endif
2598 
2599     if (width2 > 0) {
2600 	Dimension check = (Dimension) (limit + 1) / 2;
2601 	TRACE(("font provides VT100-style line-drawing\n"));
2602 	/*
2603 	 * The "VT100 line-drawing" characters happen to be all "ambiguous
2604 	 * width" in Unicode's scheme.  That means that they could be twice as
2605 	 * wide as the Latin-1 characters.
2606 	 */
2607 #define FC_ERR(n) (1.2 * (n))
2608 	if (width2 > FC_ERR(check)) {
2609 	    TRACE(("line-drawing characters appear to be double-width (ignore)\n"));
2610 	    setBrokenBoxChars(xw, True);
2611 	} else if (width2 > width) {
2612 	    width = width2;
2613 	}
2614     } else {
2615 	TRACE(("font does NOT provide VT100-style line-drawing\n"));
2616 	setBrokenBoxChars(xw, True);
2617     }
2618 
2619     /*
2620      * For each printable code, ask what its width is.  Given the maximum width
2621      * for those, we have a reasonable estimate of the single-column width.
2622      *
2623      * Ignore control characters - their extent information is misleading.
2624      */
2625     for (c = 32; c < 256; ++c) {
2626 	if (CharWidth(TScreenOf(xw), c) <= 0)
2627 	    continue;
2628 	if (FcCharSetHasChar(source->font->charset, c)) {
2629 	    (void) checkedXftWidth(XtDisplay(xw),
2630 				   source,
2631 				   target->map.max_width,
2632 				   &width, c);
2633 	}
2634     }
2635 
2636     /*
2637      * Sometimes someone uses a symbol font which has no useful ASCII or
2638      * Latin-1 characters.  Allow that, in case they did it intentionally.
2639      */
2640     if (width == 0) {
2641 	failed = 1;
2642 	if (last >= 256) {
2643 	    width = target->map.max_width;
2644 	}
2645     }
2646     target->map.min_width = width;
2647     target->map.mixed = (target->map.max_width >= (target->map.min_width + 1));
2648     return failed;
2649 }
2650 
2651 #if OPT_REPORT_FONTS
2652 static void
reportXftFonts(XtermWidget xw,XftFont * fp,const char * name,const char * tag,XftPattern * match)2653 reportXftFonts(XtermWidget xw,
2654 	       XftFont *fp,
2655 	       const char *name,
2656 	       const char *tag,
2657 	       XftPattern *match)
2658 {
2659     if (resource.reportFonts) {
2660 	char buffer[1024];
2661 	FcChar32 first_char = xtermXftFirstChar(fp);
2662 	FcChar32 last_char = xtermXftLastChar(fp);
2663 	FcChar32 ch;
2664 	unsigned missing = 0;
2665 
2666 	printf("Loaded XftFonts(%s[%s])\n", name, tag);
2667 
2668 	for (ch = first_char; ch <= last_char; ++ch) {
2669 	    if (xtermXftMissing(xw, fp, ch)) {
2670 		++missing;
2671 	    }
2672 	}
2673 	printf("\t\tfirst char:    %u\n", first_char);
2674 	printf("\t\tlast char:     %u\n", last_char);
2675 	printf("\t\tmissing-chars: %u\n", missing);
2676 	printf("\t\tpresent-chars: %u\n", (last_char - first_char) + 1 - missing);
2677 
2678 	if (XftNameUnparse(match, buffer, (int) sizeof(buffer))) {
2679 	    char *target;
2680 	    char *source = buffer;
2681 	    while ((target = strtok(source, ":")) != 0) {
2682 		printf("\t%s\n", target);
2683 		source = 0;
2684 	    }
2685 	}
2686 	fflush(stdout);
2687     }
2688 }
2689 #else
2690 #define reportXftFonts(xw, result, name, tag, match)	/* empty */
2691 #endif /* OPT_REPORT_FONTS */
2692 
2693 /*
2694  * Xft discards the pattern-match during open-pattern if the result happens to
2695  * match a currently-open file, but provides no clue to the caller when it does
2696  * this.  That is, closing a font-file may leave the data in Xft's cache, while
2697  * opening a file may free the data used for the match.
2698  *
2699  * Because of this problem, we cannot reliably refer to the pattern-match data
2700  * if it may have been seen before.
2701  */
2702 Boolean
maybeXftCache(XtermWidget xw,XftFont * font)2703 maybeXftCache(XtermWidget xw, XftFont *font)
2704 {
2705     Boolean result = False;
2706     if (font != 0) {
2707 	TScreen *screen = TScreenOf(xw);
2708 	ListXftFonts *p;
2709 	for (p = screen->list_xft_fonts; p != 0; p = p->next) {
2710 	    if (p->font == font) {
2711 		result = True;
2712 		break;
2713 	    }
2714 	}
2715 	if (!result) {
2716 	    p = TypeXtMalloc(ListXftFonts);
2717 	    if (p != 0) {
2718 		p->font = font;
2719 		p->next = screen->list_xft_fonts;
2720 		screen->list_xft_fonts = p;
2721 	    }
2722 	}
2723     }
2724     return result;
2725 }
2726 
2727 /*
2728  * Drop an entry from the cache, and close the font.
2729  */
2730 void
closeCachedXft(TScreen * screen,XftFont * font)2731 closeCachedXft(TScreen *screen, XftFont *font)
2732 {
2733     if (font != 0) {
2734 	ListXftFonts *p, *q;
2735 
2736 	for (p = screen->list_xft_fonts, q = 0; p != 0; q = p, p = p->next) {
2737 	    if (p->font == font) {
2738 		XftFontClose(screen->display, font);
2739 		if (q != 0) {
2740 		    q->next = p->next;
2741 		} else {
2742 		    screen->list_xft_fonts = p->next;
2743 		}
2744 		free(p);
2745 		break;
2746 	    }
2747 	}
2748     }
2749 }
2750 
2751 static XftFont *
xtermOpenXft(XtermWidget xw,const char * name,XftPattern * pat,const char * tag)2752 xtermOpenXft(XtermWidget xw, const char *name, XftPattern *pat, const char *tag)
2753 {
2754     TScreen *screen = TScreenOf(xw);
2755     Display *dpy = screen->display;
2756     XftResult status;
2757     XftFont *result = 0;
2758 
2759     TRACE(("xtermOpenXft(name=%s, tag=%s)\n", name, tag));
2760     if (pat != 0) {
2761 	XftPattern *match;
2762 
2763 	FcConfigSubstitute(NULL, pat, FcMatchPattern);
2764 	XftDefaultSubstitute(dpy, DefaultScreen(dpy), pat);
2765 
2766 	match = FcFontMatch(NULL, pat, &status);
2767 	if (match != 0) {
2768 	    Boolean maybeReopened = False;
2769 	    result = XftFontOpenPattern(dpy, match);
2770 #ifdef FC_COLOR
2771 	    if (result != 0) {
2772 		if (isBogusXft(result)) {
2773 		    XftFontClose(dpy, result);
2774 		    result = 0;
2775 		    maybeReopened = True;
2776 		}
2777 	    }
2778 #endif
2779 	    if (result != 0) {
2780 		TRACE(("...matched %s font\n", tag));
2781 		if (!maybeXftCache(xw, result)) {
2782 		    reportXftFonts(xw, result, name, tag, match);
2783 		}
2784 	    } else {
2785 		TRACE(("...could not open %s font\n", tag));
2786 		if (!maybeReopened)
2787 		    XftPatternDestroy(match);
2788 		if (xw->misc.fontWarnings >= fwAlways) {
2789 		    cannotFont(xw, "open", tag, name);
2790 		}
2791 	    }
2792 	} else {
2793 	    TRACE(("...did not match %s font\n", tag));
2794 	    if (xw->misc.fontWarnings >= fwResource) {
2795 		cannotFont(xw, "match", tag, name);
2796 	    }
2797 	}
2798     }
2799     return result;
2800 }
2801 
2802 #if OPT_SHIFT_FONTS
2803 /*
2804  * Don't make a dependency on the math library for a single function.
2805  * (Newton Raphson).
2806  */
2807 static double
dimSquareRoot(double value)2808 dimSquareRoot(double value)
2809 {
2810     double result = 0.0;
2811     if (value > 0.0) {
2812 	int n;
2813 	double older = value;
2814 	for (n = 0; n < 10; ++n) {
2815 	    double delta = (older * older - value) / (2.0 * older);
2816 	    double newer = older - delta;
2817 	    older = newer;
2818 	    result = newer;
2819 	    if (delta > -0.001 && delta < 0.001)
2820 		break;
2821 	}
2822     }
2823     return result;
2824 }
2825 #endif
2826 
2827 #ifdef DEBUG_XFT
2828 static void
trace_xft_glyph(TScreen * screen,XftFont * font,FT_Face face,int code,const char * name)2829 trace_xft_glyph(TScreen *screen, XftFont *font, FT_Face face, int code, const char *name)
2830 {
2831     if (!XftGlyphExists(screen->display, font, code)) {
2832 	TRACE(("Xft glyph U+%04X missing :%s\n", code, name));
2833     } else if (FT_Load_Char(face, code, FT_LOAD_RENDER) == 0) {
2834 	FT_GlyphSlot g = face->glyph;
2835 	TRACE(("Xft glyph U+%04X size(%3d,%3d) at(%3d,%3d) :%s\n",
2836 	       code,
2837 	       g->bitmap.rows, g->bitmap.width,
2838 	       g->bitmap_top, g->bitmap_left,
2839 	       name));
2840     }
2841 }
2842 
2843 #if OPT_WIDE_CHARS
2844 static void
trace_xft_line_drawing(TScreen * screen,XftFont * font,FT_Face face)2845 trace_xft_line_drawing(TScreen *screen, XftFont *font, FT_Face face)
2846 {
2847     int n;
2848     for (n = 0; unicode_boxes[n].code != 0; ++n) {
2849 	trace_xft_glyph(screen, font, face, unicode_boxes[n].code,
2850 			unicode_boxes[n].name);
2851     }
2852 }
2853 #else
2854 #define trace_xft_line_drawing(screen, font, face)	/* nothing */
2855 #endif
2856 #endif /* DEBUG_XFT */
2857 
2858 /*
2859  * Check if the line-drawing characters do not fill the bounding box.  If so,
2860  * they're not useful.
2861  */
2862 #if OPT_BOX_CHARS
2863 static void
linedrawing_gaps(XtermWidget xw,XftFont * font)2864 linedrawing_gaps(XtermWidget xw, XftFont *font)
2865 {
2866     Boolean broken;
2867 
2868 #if OPT_WIDE_CHARS
2869     TScreen *screen = TScreenOf(xw);
2870     int n;
2871     FT_Face face;
2872     face = XftLockFace(font);
2873     broken = False;
2874     for (n = 0; unicode_boxes[n].code; ++n) {
2875 	unsigned code = unicode_boxes[n].code;
2876 
2877 	if (!XftGlyphExists(screen->display, font, code)) {
2878 	    TRACE(("Xft glyph U+%04X is missing\n", code));
2879 	    broken = True;
2880 	    break;
2881 	}
2882 
2883 	if (FT_Load_Char(face, code, FT_LOAD_RENDER) == 0) {
2884 	    FT_GlyphSlot g = face->glyph;
2885 	    TRACE(("Xft glyph U+%04X size(%3d,%3d) at(%3d,%3d) :%s\n",
2886 		   code,
2887 		   g->bitmap.rows, g->bitmap.width,
2888 		   g->bitmap_top, g->bitmap_left,
2889 		   unicode_boxes[n].name));
2890 	    /*
2891 	     * While it is possible for badly-designed fonts to have line
2892 	     * drawing characters which do not meet, FreeType aggravates the
2893 	     * situation with its rounding.  Check for an obvious case where
2894 	     * the weights at the ends of a vertical line do not add up.  That
2895 	     * shows up as two under-weight rows at the beginning/end of the
2896 	     * bitmap.
2897 	     */
2898 	    if (code == 0x2502) {
2899 		unsigned r, c;
2900 		unsigned mids = 0, ends = 0;
2901 		unsigned char *data = g->bitmap.buffer;
2902 
2903 		switch (g->bitmap.pixel_mode) {
2904 		case FT_PIXEL_MODE_MONO:
2905 		    /* FALLTHRU */
2906 		case FT_PIXEL_MODE_GRAY:
2907 		    for (r = 0; r < (unsigned) g->bitmap.rows; ++r) {
2908 			unsigned k = r * (unsigned) g->bitmap.pitch;
2909 			unsigned sum = 0;
2910 			for (c = 0; c < (unsigned) g->bitmap.width; ++c) {
2911 			    unsigned xx = 0;
2912 			    switch (g->bitmap.pixel_mode) {
2913 			    case FT_PIXEL_MODE_MONO:
2914 				xx = (unsigned) ((data[k + (c / 8)]
2915 						  >> (c % 8)) & 1);
2916 				break;
2917 			    case FT_PIXEL_MODE_GRAY:
2918 				xx = data[k + c];
2919 				break;
2920 			    }
2921 			    sum += xx;
2922 			    TRACE2((" %2x", xx));
2923 			}
2924 			TRACE2((" = %u\n", sum));
2925 			if (r > 0 && (r + 1) < (unsigned) g->bitmap.rows) {
2926 			    mids = sum;
2927 			} else {
2928 			    ends += sum;
2929 			}
2930 		    }
2931 		    TRACE(("...compare middle %u vs ends %u\n", mids, ends));
2932 		    if ((mids > ends) && (g->bitmap.rows < 16))
2933 			broken = True;
2934 		    break;
2935 		default:
2936 		    TRACE(("FIXME pixel_mode %d not handled\n",
2937 			   g->bitmap.pixel_mode));
2938 		    break;
2939 		}
2940 		if (broken)
2941 		    break;
2942 	    }
2943 	    /*
2944 	     * The factor of two accounts for line-drawing that goes through
2945 	     * the middle of a cell, possibly leaving half of the cell unused.
2946 	     * A horizontal line has to extend the full width of the cell.
2947 	     */
2948 	    switch (unicode_boxes[n].high) {
2949 	    case 1:
2950 		if ((unsigned) g->bitmap.rows < (unsigned) FontHeight(screen)) {
2951 		    TRACE(("...bitmap is shorter than full-cell (%u vs %u)\n",
2952 			   (unsigned) g->bitmap.rows,
2953 			   (unsigned) FontHeight(screen)));
2954 		    broken = True;
2955 		}
2956 		break;
2957 	    case 2:
2958 		if ((unsigned) (g->bitmap.rows * 2) < (unsigned) FontHeight(screen)) {
2959 		    TRACE(("...bitmap is too short for half-cell (%u vs %u)\n",
2960 			   (unsigned) (g->bitmap.rows * 2),
2961 			   (unsigned) FontHeight(screen)));
2962 		    broken = True;
2963 		}
2964 		break;
2965 	    }
2966 	    switch (unicode_boxes[n].wide) {
2967 	    case 1:
2968 		if ((unsigned) g->bitmap.width < (unsigned) FontWidth(screen)) {
2969 		    TRACE(("...bitmap is narrower than full-cell (%u vs %u)\n",
2970 			   (unsigned) g->bitmap.width,
2971 			   (unsigned) FontWidth(screen)));
2972 		    broken = True;
2973 		}
2974 		break;
2975 	    case 2:
2976 		if ((unsigned) (g->bitmap.width * 2) < (unsigned) FontWidth(screen)) {
2977 		    TRACE(("...bitmap is too narrow for half-cell (%u vs %u)\n",
2978 			   (unsigned) (g->bitmap.width * 2),
2979 			   (unsigned) FontWidth(screen)));
2980 		    broken = True;
2981 		}
2982 		break;
2983 	    }
2984 	    if (broken)
2985 		break;
2986 	}
2987     }
2988     XftUnlockFace(font);
2989 #else
2990     (void) font;
2991     broken = True;
2992 #endif
2993 
2994     if (broken) {
2995 	TRACE(("Xft line-drawing would not work\n"));
2996 	setBrokenBoxChars(xw, True);
2997     }
2998 }
2999 #endif /* OPT_BOX_CHARS */
3000 
3001 /*
3002  * Given the Xft font metrics, determine the actual font size.  This is used
3003  * for each font to ensure that normal, bold and italic fonts follow the same
3004  * rule.
3005  */
3006 static void
setRenderFontsize(XtermWidget xw,VTwin * win,XftFont * font,const char * tag)3007 setRenderFontsize(XtermWidget xw, VTwin *win, XftFont *font, const char *tag)
3008 {
3009     if (font != 0) {
3010 	TScreen *screen = TScreenOf(xw);
3011 	int width, height, ascent, descent;
3012 #ifdef DEBUG_XFT
3013 	int n;
3014 	FT_Face face;
3015 	FT_Size size;
3016 	FT_Size_Metrics metrics;
3017 	Boolean scalable;
3018 	Boolean is_fixed;
3019 	Boolean debug_xft = False;
3020 
3021 	face = XftLockFace(font);
3022 	size = face->size;
3023 	metrics = size->metrics;
3024 	is_fixed = FT_IS_FIXED_WIDTH(face);
3025 	scalable = FT_IS_SCALABLE(face);
3026 	trace_xft_line_drawing(screen, font, face);
3027 	for (n = 32; n < 127; ++n) {
3028 	    char name[80];
3029 	    sprintf(name, "letter \"%c\"", n);
3030 	    trace_xft_glyph(screen, font, face, n, name);
3031 	}
3032 	XftUnlockFace(font);
3033 
3034 	/* freetype's inconsistent for this sign */
3035 	metrics.descender = -metrics.descender;
3036 
3037 #define TR_XFT	   "Xft metrics: "
3038 #define D_64(name) ((double)(metrics.name)/64.0)
3039 #define M_64(a,b)  ((font->a * 64) != metrics.b)
3040 #define BOTH(a,b)  D_64(b), M_64(a,b) ? "*" : ""
3041 
3042 	debug_xft = (M_64(ascent, ascender)
3043 		     || M_64(descent, descender)
3044 		     || M_64(height, height)
3045 		     || M_64(max_advance_width, max_advance));
3046 
3047 	TRACE(("Xft font is %sscalable, %sfixed-width\n",
3048 	       is_fixed ? "" : "not ",
3049 	       scalable ? "" : "not "));
3050 
3051 	if (debug_xft) {
3052 	    TRACE(("Xft font size %d+%d vs %d by %d\n",
3053 		   font->ascent,
3054 		   font->descent,
3055 		   font->height,
3056 		   font->max_advance_width));
3057 	    TRACE((TR_XFT "ascender    %6.2f%s\n", BOTH(ascent, ascender)));
3058 	    TRACE((TR_XFT "descender   %6.2f%s\n", BOTH(descent, descender)));
3059 	    TRACE((TR_XFT "height      %6.2f%s\n", BOTH(height, height)));
3060 	    TRACE((TR_XFT "max_advance %6.2f%s\n", BOTH(max_advance_width, max_advance)));
3061 	} else {
3062 	    TRACE((TR_XFT "matches font\n"));
3063 	}
3064 #endif
3065 
3066 	width = font->max_advance_width;
3067 	height = font->height;
3068 	ascent = font->ascent;
3069 	descent = font->descent;
3070 	if (screen->force_xft_height && height < ascent + descent) {
3071 	    TRACE(("...height is less than ascent + descent (%u vs %u)\n",
3072 		   height, ascent + descent));
3073 	    if ((ascent + descent) > (height + 1)) {
3074 		/* this happens less than 10% of the time */
3075 		--ascent;
3076 		--descent;
3077 		TRACE(("...decrement both ascent and descent before retry\n"));
3078 	    } else if (ascent > descent) {
3079 		/* this is the usual case */
3080 		--ascent;
3081 		TRACE(("...decrement ascent before retry\n"));
3082 	    } else {
3083 		/* this could happen, though rare... */
3084 		--descent;
3085 		TRACE(("...decrement descent before retry\n"));
3086 	    }
3087 	    height = ascent + descent;
3088 	    font->ascent = ascent;
3089 	    font->descent = descent;
3090 	    TRACE(("...updated height %d vs %d (ascent %d, descent %d)\n",
3091 		   height, ascent + descent, ascent, descent));
3092 	}
3093 	if (is_double_width_font_xft(screen->display, font)) {
3094 	    TRACE(("...reduce width from %d to %d\n", width, width >> 1));
3095 	    width >>= 1;
3096 	}
3097 	if (tag == 0) {
3098 	    SetFontWidth(screen, win, width);
3099 	    SetFontHeight(screen, win, height);
3100 	    win->f_ascent = ascent;
3101 	    win->f_descent = descent;
3102 	    TRACE(("setRenderFontsize result %dx%d (%d+%d)\n",
3103 		   width, height, ascent, descent));
3104 	} else if (win->f_width < width ||
3105 		   win->f_height < height ||
3106 		   win->f_ascent < ascent ||
3107 		   win->f_descent < descent) {
3108 	    TRACE(("setRenderFontsize %s changed %dx%d (%d+%d) to %dx%d (%d+%d)\n",
3109 		   tag,
3110 		   win->f_width, win->f_height, win->f_ascent, win->f_descent,
3111 		   width, height, ascent, descent));
3112 
3113 	    SetFontWidth(screen, win, width);
3114 	    SetFontHeight(screen, win, height);
3115 	    win->f_ascent = ascent;
3116 	    win->f_descent = descent;
3117 	} else {
3118 	    TRACE(("setRenderFontsize %s unchanged\n", tag));
3119 	}
3120 #if OPT_BOX_CHARS
3121 	if (!screen->broken_box_chars && (tag == 0)) {
3122 	    linedrawing_gaps(xw, font);
3123 	}
3124 #endif
3125     }
3126 }
3127 #endif
3128 
3129 static void
checkFontInfo(int value,const char * tag,int failed)3130 checkFontInfo(int value, const char *tag, int failed)
3131 {
3132     if (value == 0 || failed) {
3133 	if (value == 0) {
3134 	    xtermWarning("Selected font has no non-zero %s for ISO-8859-1 encoding\n", tag);
3135 	    exit(1);
3136 	} else {
3137 	    xtermWarning("Selected font has no valid %s for ISO-8859-1 encoding\n", tag);
3138 	}
3139     }
3140 }
3141 
3142 #if OPT_RENDERFONT
3143 void
xtermCloseXft(TScreen * screen,XTermXftFonts * pub)3144 xtermCloseXft(TScreen *screen, XTermXftFonts *pub)
3145 {
3146     if (pub->font != 0) {
3147 	Cardinal n;
3148 
3149 	closeCachedXft(screen, pub->font);
3150 	pub->font = 0;
3151 
3152 	if (pub->pattern) {
3153 	    XftPatternDestroy(pub->pattern);
3154 	    pub->pattern = 0;
3155 	}
3156 	if (pub->fontset) {
3157 	    XftFontSetDestroy(pub->fontset);
3158 	    pub->fontset = 0;
3159 	}
3160 
3161 	for (n = 0; n < pub->limit; ++n) {
3162 	    if (pub->cache[n].font) {
3163 		closeCachedXft(screen, pub->cache[n].font);
3164 		pub->cache[n].font = 0;
3165 	    }
3166 	}
3167 	free(pub->cache);
3168 	pub->cache = NULL;
3169     }
3170 }
3171 
3172 /*
3173  * Get the faceName/faceNameDoublesize resource setting.
3174  */
3175 String
getFaceName(XtermWidget xw,Bool wideName)3176 getFaceName(XtermWidget xw, Bool wideName)
3177 {
3178 #if OPT_RENDERWIDE
3179     String result = (wideName
3180 		     ? FirstItemOf(xw->work.fonts.xft.list_w)
3181 		     : CurrentXftFont(xw));
3182 #else
3183     String result = CurrentXftFont(xw);
3184     (void) wideName;
3185 #endif
3186     return x_nonempty(result);
3187 }
3188 
3189 /*
3190  * If we change the faceName, we'll have to re-acquire all of the fonts that
3191  * are derived from it.
3192  */
3193 void
setFaceName(XtermWidget xw,const char * value)3194 setFaceName(XtermWidget xw, const char *value)
3195 {
3196     TScreen *screen = TScreenOf(xw);
3197     Boolean changed = (Boolean) ((CurrentXftFont(xw) == 0)
3198 				 || strcmp(CurrentXftFont(xw), value));
3199 
3200     if (changed) {
3201 	int n;
3202 
3203 	CurrentXftFont(xw) = x_strdup(value);
3204 	for (n = 0; n < NMENUFONTS; ++n) {
3205 	    int e;
3206 	    xw->misc.face_size[n] = -1.0;
3207 	    for (e = 0; e < fMAX; ++e) {
3208 		xtermCloseXft(screen, getMyXftFont(xw, e, n));
3209 	    }
3210 	}
3211     }
3212 }
3213 #endif
3214 
3215 /*
3216  * Compute useful values for the font/window sizes
3217  */
3218 void
xtermComputeFontInfo(XtermWidget xw,VTwin * win,XFontStruct * font,int sbwidth)3219 xtermComputeFontInfo(XtermWidget xw,
3220 		     VTwin *win,
3221 		     XFontStruct *font,
3222 		     int sbwidth)
3223 {
3224     TScreen *screen = TScreenOf(xw);
3225 
3226     int i, j, width, height;
3227 #if OPT_RENDERFONT
3228     int fontnum = screen->menu_font_number;
3229 #endif
3230     int failed = 0;
3231 
3232 #if OPT_RENDERFONT
3233     /*
3234      * xterm contains a lot of references to fonts, assuming they are fixed
3235      * size.  This chunk of code overrides the actual font-selection (see
3236      * drawXtermText()), if the user has selected render-font.  All of the
3237      * font-loading for fixed-fonts still goes on whether or not this chunk
3238      * overrides it.
3239      */
3240     if (UsingRenderFont(xw) && fontnum >= 0) {
3241 	String face_name = getFaceName(xw, False);
3242 	XTermXftFonts norm = screen->renderFontNorm[fontnum];
3243 	XTermXftFonts bold = screen->renderFontBold[fontnum];
3244 	XTermXftFonts ital = screen->renderFontItal[fontnum];
3245 	XTermXftFonts btal = screen->renderFontBtal[fontnum];
3246 #if OPT_RENDERWIDE
3247 	XTermXftFonts wnorm = screen->renderWideNorm[fontnum];
3248 	XTermXftFonts wbold = screen->renderWideBold[fontnum];
3249 	XTermXftFonts wital = screen->renderWideItal[fontnum];
3250 	XTermXftFonts wbtal = screen->renderWideBtal[fontnum];
3251 #endif
3252 
3253 	if (norm.font == 0 && !IsEmpty(face_name)) {
3254 	    XftPattern *pat;
3255 	    double face_size;
3256 
3257 	    TRACE(("xtermComputeFontInfo font %d: norm(face %s, size %.1f)\n",
3258 		   fontnum, face_name,
3259 		   xw->misc.face_size[fontnum]));
3260 
3261 	    TRACE(("Using Xft %d\n", XftVersion));
3262 	    TRACE(("Using FontConfig %d\n", FC_VERSION));
3263 
3264 	    fillInFaceSize(xw, fontnum);
3265 	    face_size = (double) xw->misc.face_size[fontnum];
3266 
3267 	    /*
3268 	     * By observation (there is no documentation), XftPatternBuild is
3269 	     * cumulative.  Build the bold- and italic-patterns on top of the
3270 	     * normal pattern.
3271 	     */
3272 #ifdef FC_COLOR
3273 #define NormXftPattern \
3274 	    XFT_FAMILY,     XftTypeString, "mono", \
3275 	    FC_COLOR,       XftTypeBool,   FcFalse, \
3276 	    FC_OUTLINE,     XftTypeBool,   FcTrue, \
3277 	    XFT_SIZE,       XftTypeDouble, face_size
3278 #else
3279 #define NormXftPattern \
3280 	    XFT_FAMILY,     XftTypeString, "mono", \
3281 	    XFT_SIZE,       XftTypeDouble, face_size
3282 #endif
3283 
3284 #define BoldXftPattern(norm) \
3285 	    XFT_WEIGHT,     XftTypeInteger, XFT_WEIGHT_BOLD, \
3286 	    XFT_CHAR_WIDTH, XftTypeInteger, norm.font->max_advance_width
3287 
3288 #define ItalXftPattern(norm) \
3289 	    XFT_SLANT,      XftTypeInteger, XFT_SLANT_ITALIC, \
3290 	    XFT_CHAR_WIDTH, XftTypeInteger, norm.font->max_advance_width
3291 
3292 #define BtalXftPattern(norm) \
3293 	    XFT_WEIGHT,     XftTypeInteger, XFT_WEIGHT_BOLD, \
3294 	    XFT_SLANT,      XftTypeInteger, XFT_SLANT_ITALIC, \
3295 	    XFT_CHAR_WIDTH, XftTypeInteger, norm.font->max_advance_width
3296 
3297 #if OPT_WIDE_ATTRS
3298 #define HAVE_ITALICS 1
3299 #define FIND_ITALICS ((pat = XftNameParse(face_name)) != 0)
3300 #elif OPT_ISO_COLORS
3301 #define HAVE_ITALICS 1
3302 #define FIND_ITALICS (screen->italicULMode && (pat = XftNameParse(face_name)) != 0)
3303 #else
3304 #define HAVE_ITALICS 0
3305 #endif
3306 
3307 #if OPT_DEC_CHRSET
3308 	    freeall_DoubleFT(xw);
3309 #endif
3310 	    if ((pat = XftNameParse(face_name)) != 0) {
3311 #define OPEN_XFT(name, tag) name.font = xtermOpenXft(xw, face_name, name.pattern, tag)
3312 		norm.pattern = XftPatternDuplicate(pat);
3313 		XftPatternBuild(norm.pattern,
3314 				NormXftPattern,
3315 				(void *) 0);
3316 		OPEN_XFT(norm, "normal");
3317 
3318 		if (norm.font != 0) {
3319 		    bold.pattern = XftPatternDuplicate(pat);
3320 		    XftPatternBuild(bold.pattern,
3321 				    NormXftPattern,
3322 				    BoldXftPattern(norm),
3323 				    (void *) 0);
3324 		    OPEN_XFT(bold, "bold");
3325 
3326 #if HAVE_ITALICS
3327 		    if (FIND_ITALICS) {
3328 			ital.pattern = XftPatternDuplicate(pat);
3329 			XftPatternBuild(ital.pattern,
3330 					NormXftPattern,
3331 					ItalXftPattern(norm),
3332 					(void *) 0);
3333 			OPEN_XFT(ital, "italic");
3334 			btal.pattern = XftPatternDuplicate(pat);
3335 			XftPatternBuild(btal.pattern,
3336 					NormXftPattern,
3337 					BtalXftPattern(norm),
3338 					(void *) 0);
3339 			OPEN_XFT(btal, "bold-italic");
3340 		    }
3341 #endif
3342 
3343 		    /*
3344 		     * FIXME:  just assume that the corresponding font has no
3345 		     * graphics characters.
3346 		     */
3347 		    if (screen->fnt_boxes) {
3348 			screen->fnt_boxes = 0;
3349 			TRACE(("Xft opened - will %suse internal line-drawing characters\n",
3350 			       screen->fnt_boxes ? "not " : ""));
3351 		    }
3352 		}
3353 
3354 		CACHE_XFT(screen->renderFontNorm, norm);
3355 
3356 		CACHE_XFT(screen->renderFontBold, bold);
3357 		if (norm.font != 0 && !bold.font) {
3358 		    noUsableXft(xw, "bold");
3359 		    XftPatternDestroy(bold.pattern);
3360 		    bold.pattern = XftPatternDuplicate(pat);
3361 		    XftPatternBuild(bold.pattern,
3362 				    NormXftPattern,
3363 				    (void *) 0);
3364 		    OPEN_XFT(bold, "bold");
3365 		    failed = 0;
3366 		    CACHE_XFT(screen->renderFontBold, bold);
3367 		}
3368 #if HAVE_ITALICS
3369 		CACHE_XFT(screen->renderFontItal, ital);
3370 		if (norm.font != 0 && !ital.font) {
3371 		    noUsableXft(xw, "italic");
3372 		    XftPatternDestroy(ital.pattern);
3373 		    ital.pattern = XftPatternDuplicate(pat);
3374 		    XftPatternBuild(ital.pattern,
3375 				    NormXftPattern,
3376 				    (void *) 0);
3377 		    OPEN_XFT(ital, "italics");
3378 		    failed = 0;
3379 		    CACHE_XFT(screen->renderFontItal, ital);
3380 		}
3381 		CACHE_XFT(screen->renderFontBtal, btal);
3382 		if (norm.font != 0 && !btal.font) {
3383 		    noUsableXft(xw, "bold italic");
3384 		    XftPatternDestroy(btal.pattern);
3385 		    btal.pattern = XftPatternDuplicate(pat);
3386 		    XftPatternBuild(btal.pattern,
3387 				    NormXftPattern,
3388 				    (void *) 0);
3389 		    OPEN_XFT(btal, "bold-italics");
3390 		    failed = 0;
3391 		    CACHE_XFT(screen->renderFontBtal, btal);
3392 		}
3393 #endif
3394 		XftPatternDestroy(pat);
3395 	    } else {
3396 		failed = 1;
3397 	    }
3398 #undef OPEN_XFT
3399 
3400 	    /*
3401 	     * See xtermXftDrawString().  A separate double-width font is nice
3402 	     * to have, but not essential.
3403 	     */
3404 #if OPT_RENDERWIDE
3405 	    if (norm.font != 0 && screen->wide_chars) {
3406 		int char_width = norm.font->max_advance_width * 2;
3407 		double aspect = ((FirstItemOf(xw->work.fonts.xft.list_w)
3408 				  || screen->renderFontNorm[fontnum].map.mixed)
3409 				 ? 1.0
3410 				 : 2.0);
3411 
3412 		face_name = getFaceName(xw, True);
3413 		TRACE(("xtermComputeFontInfo wide(face %s, char_width %d)\n",
3414 		       NonNull(face_name),
3415 		       char_width));
3416 
3417 #define WideXftPattern \
3418 		XFT_FAMILY,     XftTypeString,   "mono", \
3419 		XFT_SIZE,       XftTypeDouble,   face_size, \
3420 		XFT_SPACING,    XftTypeInteger,  XFT_MONO, \
3421 		XFT_CHAR_WIDTH, XftTypeInteger,  char_width, \
3422 		FC_ASPECT,      XftTypeDouble,   aspect
3423 
3424 		if (!IsEmpty(face_name) && (pat = XftNameParse(face_name))
3425 		    != 0) {
3426 #define OPEN_XFT(name, tag) name.font = xtermOpenXft(xw, face_name, name.pattern, tag)
3427 		    wnorm.pattern = XftPatternDuplicate(pat);
3428 		    XftPatternBuild(wnorm.pattern,
3429 				    WideXftPattern,
3430 				    (void *) 0);
3431 		    OPEN_XFT(wnorm, "wide");
3432 
3433 		    if (wnorm.font != 0) {
3434 			wbold.pattern = XftPatternDuplicate(pat);
3435 			XftPatternBuild(wbold.pattern,
3436 					WideXftPattern,
3437 					BoldXftPattern(wnorm),
3438 					(void *) 0);
3439 			OPEN_XFT(wbold, "wide-bold");
3440 
3441 #if HAVE_ITALICS
3442 			if (FIND_ITALICS) {
3443 			    wital.pattern = XftPatternDuplicate(pat);
3444 			    XftPatternBuild(wital.pattern,
3445 					    WideXftPattern,
3446 					    ItalXftPattern(wnorm),
3447 					    (void *) 0);
3448 			    OPEN_XFT(wital, "wide-italic");
3449 			}
3450 			CACHE_XFT(screen->renderWideBtal, wbtal);
3451 			if (!wbtal.font) {
3452 			    noUsableXft(xw, "wide bold");
3453 			    XftPatternDestroy(wbtal.pattern);
3454 			    wbtal.pattern = XftPatternDuplicate(pat);
3455 			    XftPatternBuild(wbtal.pattern,
3456 					    WideXftPattern,
3457 					    (void *) 0);
3458 			    OPEN_XFT(wbtal, "wide-bold-italics");
3459 			    failed = 0;
3460 			    CACHE_XFT(screen->renderWideBtal, wbtal);
3461 			}
3462 #endif
3463 		    }
3464 
3465 		    CACHE_XFT(screen->renderWideNorm, wnorm);
3466 
3467 		    CACHE_XFT(screen->renderWideBold, wbold);
3468 		    if (wnorm.font != 0 && !wbold.font) {
3469 			noUsableXft(xw, "wide-bold");
3470 			XftPatternDestroy(wbold.pattern);
3471 			wbold.pattern = XftPatternDuplicate(pat);
3472 			XftPatternBuild(bold.pattern,
3473 					WideXftPattern,
3474 					(void *) 0);
3475 			OPEN_XFT(wbold, "wide-bold");
3476 			failed = 0;
3477 			CACHE_XFT(screen->renderWideBold, bold);
3478 		    }
3479 
3480 		    CACHE_XFT(screen->renderWideItal, wital);
3481 		    if (wnorm.font != 0 && !wital.font) {
3482 			noUsableXft(xw, "wide-italic");
3483 			XftPatternDestroy(wital.pattern);
3484 			wital.pattern = XftPatternDuplicate(pat);
3485 			XftPatternBuild(wital.pattern,
3486 					WideXftPattern,
3487 					(void *) 0);
3488 			OPEN_XFT(wital, "wide-italic");
3489 			failed = 0;
3490 			CACHE_XFT(screen->renderWideItal, wital);
3491 		    }
3492 
3493 		    XftPatternDestroy(pat);
3494 		}
3495 #undef OPEN_XFT
3496 	    }
3497 #endif /* OPT_RENDERWIDE */
3498 	}
3499 	if (norm.font == 0) {
3500 	    TRACE(("...no TrueType font found for number %d, disable menu entry\n", fontnum));
3501 	    xw->work.render_font = False;
3502 	    update_font_renderfont();
3503 	    /* now we will fall through into the bitmap fonts */
3504 	} else {
3505 	    setBrokenBoxChars(xw, False);
3506 	    setRenderFontsize(xw, win, norm.font, NULL);
3507 	    setRenderFontsize(xw, win, bold.font, "bold");
3508 	    setRenderFontsize(xw, win, ital.font, "ital");
3509 	    setRenderFontsize(xw, win, btal.font, "btal");
3510 #if OPT_BOX_CHARS
3511 	    setupPackedFonts(xw);
3512 
3513 	    if (screen->force_packed) {
3514 		XTermXftFonts *use = &(screen->renderFontNorm[fontnum]);
3515 		SetFontHeight(screen, win, use->font->ascent + use->font->descent);
3516 		SetFontWidth(screen, win, use->map.min_width);
3517 		TRACE(("...packed TrueType font %dx%d vs %d\n",
3518 		       win->f_height,
3519 		       win->f_width,
3520 		       use->map.max_width));
3521 	    }
3522 #endif
3523 	    DUMP_XFT(xw, &(screen->renderFontNorm[fontnum]));
3524 	}
3525     }
3526     /*
3527      * Are we handling a bitmap font?
3528      */
3529     else
3530 #endif /* OPT_RENDERFONT */
3531     {
3532 	if (is_double_width_font(font) && !(screen->fnt_prop)) {
3533 	    SetFontWidth(screen, win, font->min_bounds.width);
3534 	} else {
3535 	    SetFontWidth(screen, win, font->max_bounds.width);
3536 	}
3537 	SetFontHeight(screen, win, font->ascent + font->descent);
3538 	win->f_ascent = font->ascent;
3539 	win->f_descent = font->descent;
3540     }
3541     i = 2 * screen->border + sbwidth;
3542     j = 2 * screen->border;
3543     width = MaxCols(screen) * win->f_width + i;
3544     height = MaxRows(screen) * win->f_height + j;
3545     win->fullwidth = (Dimension) width;
3546     win->fullheight = (Dimension) height;
3547     win->width = width - i;
3548     win->height = height - j;
3549 
3550     TRACE(("xtermComputeFontInfo window %dx%d (full %dx%d), fontsize %dx%d (asc %d, dsc %d)\n",
3551 	   win->height,
3552 	   win->width,
3553 	   win->fullheight,
3554 	   win->fullwidth,
3555 	   win->f_height,
3556 	   win->f_width,
3557 	   win->f_ascent,
3558 	   win->f_descent));
3559 
3560     checkFontInfo(win->f_height, "height", failed);
3561     checkFontInfo(win->f_width, "width", failed);
3562 }
3563 
3564 /* save this information as a side-effect for double-sized characters */
3565 static void
xtermSaveFontInfo(TScreen * screen,XFontStruct * font)3566 xtermSaveFontInfo(TScreen *screen, XFontStruct *font)
3567 {
3568     screen->fnt_wide = (Dimension) (font->max_bounds.width);
3569     screen->fnt_high = (Dimension) (font->ascent + font->descent);
3570     TRACE(("xtermSaveFontInfo %dx%d\n", screen->fnt_high, screen->fnt_wide));
3571 }
3572 
3573 /*
3574  * After loading a new font, update the structures that use its size.
3575  */
3576 void
xtermUpdateFontInfo(XtermWidget xw,Bool doresize)3577 xtermUpdateFontInfo(XtermWidget xw, Bool doresize)
3578 {
3579     TScreen *screen = TScreenOf(xw);
3580 
3581     int scrollbar_width;
3582     VTwin *win = &(screen->fullVwin);
3583 
3584 #if USE_DOUBLE_BUFFER
3585     discardRenderDraw(TScreenOf(xw));
3586 #endif /* USE_DOUBLE_BUFFER */
3587 
3588     scrollbar_width = (xw->misc.scrollbar
3589 		       ? (screen->scrollWidget->core.width +
3590 			  BorderWidth(screen->scrollWidget))
3591 		       : 0);
3592     xtermComputeFontInfo(xw, win, GetNormalFont(screen, fNorm)->fs, scrollbar_width);
3593     xtermSaveFontInfo(screen, GetNormalFont(screen, fNorm)->fs);
3594 
3595     if (doresize) {
3596 	if (VWindow(screen)) {
3597 	    xtermClear(xw);
3598 	}
3599 	TRACE(("xtermUpdateFontInfo " TRACE_L "\n"));
3600 	DoResizeScreen(xw);	/* set to the new natural size */
3601 	ResizeScrollBar(xw);
3602 	Redraw();
3603 	TRACE((TRACE_R " xtermUpdateFontInfo\n"));
3604 #ifdef SCROLLBAR_RIGHT
3605 	updateRightScrollbar(xw);
3606 #endif
3607     }
3608     xtermSetCursorBox(screen);
3609 }
3610 
3611 #if OPT_BOX_CHARS || OPT_REPORT_FONTS
3612 
3613 /*
3614  * Returns true if the given character is missing from the specified font.
3615  */
3616 Bool
xtermMissingChar(unsigned ch,XTermFonts * font)3617 xtermMissingChar(unsigned ch, XTermFonts * font)
3618 {
3619     Bool result = False;
3620     XFontStruct *fs = font->fs;
3621     XCharStruct *pc = 0;
3622 
3623     if (fs == NULL) {
3624 	result = True;
3625     } else if (fs->max_byte1 == 0) {
3626 #if OPT_WIDE_CHARS
3627 	if (ch < 256)
3628 #endif
3629 	{
3630 	    CI_GET_CHAR_INFO_1D(fs, E2A(ch), pc);
3631 	}
3632     }
3633 #if OPT_WIDE_CHARS
3634     else {
3635 	unsigned row = (ch >> 8);
3636 	unsigned col = (ch & 0xff);
3637 	CI_GET_CHAR_INFO_2D(fs, row, col, pc);
3638     }
3639 #endif
3640 
3641     if (pc == 0 || CI_NONEXISTCHAR(pc)) {
3642 	TRACE2(("xtermMissingChar %#04x (!exists)\n", ch));
3643 	result = True;
3644     }
3645     if (ch < KNOWN_MISSING) {
3646 	font->known_missing[ch] = (Char) (result ? 2 : 1);
3647     }
3648     return result;
3649 }
3650 #endif
3651 
3652 #if OPT_BOX_CHARS
3653 /*
3654  * The grid is arbitrary, enough resolution that nothing's lost in
3655  * initialization.
3656  */
3657 #define BOX_HIGH 60
3658 #define BOX_WIDE 60
3659 
3660 #define MID_HIGH (BOX_HIGH/2)
3661 #define MID_WIDE (BOX_WIDE/2)
3662 
3663 #define CHR_WIDE ((9*BOX_WIDE)/10)
3664 #define CHR_HIGH ((9*BOX_HIGH)/10)
3665 
3666 /*
3667  * ...since we'll scale the values anyway.
3668  */
3669 #define Scale_XY(n,d,f) ((int)(n) * ((int)(f))) / (d)
3670 #define SCALED_X(n) Scale_XY(n, BOX_WIDE, font_width)
3671 #define SCALED_Y(n) Scale_XY(n, BOX_HIGH, font_height)
3672 #define SCALE_X(n) n = SCALED_X(n)
3673 #define SCALE_Y(n) n = SCALED_Y(n)
3674 
3675 #define SEG(x0,y0,x1,y1) x0,y0, x1,y1
3676 
3677 /*
3678  * Draw the given graphic character, if it is simple enough (i.e., a
3679  * line-drawing character).
3680  */
3681 void
xtermDrawBoxChar(XTermDraw * params,unsigned ch,GC gc,int x,int y,int cells,Bool xftords)3682 xtermDrawBoxChar(XTermDraw * params,
3683 		 unsigned ch,
3684 		 GC gc,
3685 		 int x,
3686 		 int y,
3687 		 int cells,
3688 		 Bool xftords)
3689 {
3690     TScreen *screen = TScreenOf(params->xw);
3691     /* *INDENT-OFF* */
3692     static const short glyph_ht[] = {
3693 	SEG(1*BOX_WIDE/10,  0,		1*BOX_WIDE/10,5*MID_HIGH/6),	/* H */
3694 	SEG(6*BOX_WIDE/10,  0,		6*BOX_WIDE/10,5*MID_HIGH/6),
3695 	SEG(1*BOX_WIDE/10,5*MID_HIGH/12,6*BOX_WIDE/10,5*MID_HIGH/12),
3696 	SEG(2*BOX_WIDE/10,  MID_HIGH,	  CHR_WIDE,	MID_HIGH),	/* T */
3697 	SEG(6*BOX_WIDE/10,  MID_HIGH,	6*BOX_WIDE/10,	CHR_HIGH),
3698 	-1
3699     }, glyph_ff[] = {
3700 	SEG(1*BOX_WIDE/10,  0,		6*BOX_WIDE/10,	0),		/* F */
3701 	SEG(1*BOX_WIDE/10,5*MID_HIGH/12,6*CHR_WIDE/12,5*MID_HIGH/12),
3702 	SEG(1*BOX_WIDE/10,  0,		0*BOX_WIDE/3, 5*MID_HIGH/6),
3703 	SEG(1*BOX_WIDE/3,   MID_HIGH,	  CHR_WIDE,	MID_HIGH),	/* F */
3704 	SEG(1*BOX_WIDE/3, 8*MID_HIGH/6,10*CHR_WIDE/12,8*MID_HIGH/6),
3705 	SEG(1*BOX_WIDE/3,   MID_HIGH,	1*BOX_WIDE/3,	CHR_HIGH),
3706 	-1
3707     }, glyph_lf[] = {
3708 	SEG(1*BOX_WIDE/10,  0,		1*BOX_WIDE/10,9*MID_HIGH/12),	/* L */
3709 	SEG(1*BOX_WIDE/10,9*MID_HIGH/12,6*BOX_WIDE/10,9*MID_HIGH/12),
3710 	SEG(1*BOX_WIDE/3,   MID_HIGH,	  CHR_WIDE,	MID_HIGH),	/* F */
3711 	SEG(1*BOX_WIDE/3, 8*MID_HIGH/6,10*CHR_WIDE/12,8*MID_HIGH/6),
3712 	SEG(1*BOX_WIDE/3,   MID_HIGH,	1*BOX_WIDE/3,	CHR_HIGH),
3713 	-1
3714     }, glyph_nl[] = {
3715 	SEG(1*BOX_WIDE/10,5*MID_HIGH/6, 1*BOX_WIDE/10,	0),		/* N */
3716 	SEG(1*BOX_WIDE/10,  0,		5*BOX_WIDE/6, 5*MID_HIGH/6),
3717 	SEG(5*BOX_WIDE/6, 5*MID_HIGH/6, 5*BOX_WIDE/6,	0),
3718 	SEG(1*BOX_WIDE/3,   MID_HIGH,	1*BOX_WIDE/3,	CHR_HIGH),	/* L */
3719 	SEG(1*BOX_WIDE/3,   CHR_HIGH,	  CHR_WIDE,	CHR_HIGH),
3720 	-1
3721     }, glyph_vt[] = {
3722 	SEG(1*BOX_WIDE/10,   0,		5*BOX_WIDE/12,5*MID_HIGH/6),	/* V */
3723 	SEG(5*BOX_WIDE/12,5*MID_HIGH/6, 5*BOX_WIDE/6,	0),
3724 	SEG(2*BOX_WIDE/10,  MID_HIGH,	  CHR_WIDE,	MID_HIGH),	/* T */
3725 	SEG(6*BOX_WIDE/10,  MID_HIGH,	6*BOX_WIDE/10,	CHR_HIGH),
3726 	-1
3727     }, plus_or_minus[] =
3728     {
3729 	SEG(  0,	  5*BOX_HIGH/6,	  CHR_WIDE,   5*BOX_HIGH/6),
3730 	SEG(  MID_WIDE,	  2*BOX_HIGH/6,	  MID_WIDE,   4*BOX_HIGH/6),
3731 	SEG(  0,	  3*BOX_HIGH/6,	  CHR_WIDE,   3*BOX_HIGH/6),
3732 	-1
3733     }, lower_right_corner[] =
3734     {
3735 	SEG(  0,	    MID_HIGH,	  MID_WIDE,	MID_HIGH),
3736 	SEG(  MID_WIDE,	    MID_HIGH,	  MID_WIDE,	0),
3737 	-1
3738     }, upper_right_corner[] =
3739     {
3740 	SEG(  0,	    MID_HIGH,	  MID_WIDE,	MID_HIGH),
3741 	SEG( MID_WIDE,	    MID_HIGH,	  MID_WIDE,	BOX_HIGH),
3742 	-1
3743     }, upper_left_corner[] =
3744     {
3745 	SEG(  MID_WIDE,	    MID_HIGH,	  BOX_WIDE,	MID_HIGH),
3746 	SEG(  MID_WIDE,	    MID_HIGH,	  MID_WIDE,	BOX_HIGH),
3747 	-1
3748     }, lower_left_corner[] =
3749     {
3750 	SEG(  MID_WIDE,	    0,		  MID_WIDE,	MID_HIGH),
3751 	SEG(  MID_WIDE,	    MID_WIDE,	  BOX_WIDE,	MID_HIGH),
3752 	-1
3753     }, cross[] =
3754     {
3755 	SEG(  0,	    MID_HIGH,	  BOX_WIDE,	MID_HIGH),
3756 	SEG(  MID_WIDE,	    0,		  MID_WIDE,	BOX_HIGH),
3757 	-1
3758     }, scan_line_1[] =
3759     {
3760 	SEG(  0,	    0,		  BOX_WIDE,	0),
3761 	-1
3762     }, scan_line_3[] =
3763     {
3764 	SEG(  0,	    BOX_HIGH/4,	  BOX_WIDE,	BOX_HIGH/4),
3765 	-1
3766     }, scan_line_7[] =
3767     {
3768 	SEG( 0,		    MID_HIGH,	  BOX_WIDE,	MID_HIGH),
3769 	-1
3770     }, scan_line_9[] =
3771     {
3772 	SEG(  0,	  3*BOX_HIGH/4,	  BOX_WIDE,   3*BOX_HIGH/4),
3773 	-1
3774     }, horizontal_line[] =
3775     {
3776 	SEG(  0,	    BOX_HIGH,	  BOX_WIDE,	BOX_HIGH),
3777 	-1
3778     }, left_tee[] =
3779     {
3780 	SEG(  MID_WIDE,	    0,		  MID_WIDE,	BOX_HIGH),
3781 	SEG(  MID_WIDE,	    MID_HIGH,	  BOX_WIDE,	MID_HIGH),
3782 	-1
3783     }, right_tee[] =
3784     {
3785 	SEG(  MID_WIDE,	    0,		  MID_WIDE,	BOX_HIGH),
3786 	SEG(  MID_WIDE,	    MID_HIGH,	  0,		MID_HIGH),
3787 	-1
3788     }, bottom_tee[] =
3789     {
3790 	SEG(  0,	    MID_HIGH,	  BOX_WIDE,	MID_HIGH),
3791 	SEG(  MID_WIDE,	    0,		  MID_WIDE,	MID_HIGH),
3792 	-1
3793     }, top_tee[] =
3794     {
3795 	SEG(  0,	    MID_HIGH,	  BOX_WIDE,	MID_HIGH),
3796 	SEG(  MID_WIDE,	    MID_HIGH,	  MID_WIDE,	BOX_HIGH),
3797 	-1
3798     }, vertical_line[] =
3799     {
3800 	SEG(  MID_WIDE,	    0,		  MID_WIDE,	BOX_HIGH),
3801 	-1
3802     }, less_than_or_equal[] =
3803     {
3804 	SEG(  CHR_WIDE,	    BOX_HIGH/3,	  0,		MID_HIGH),
3805 	SEG(  CHR_WIDE,	  2*BOX_HIGH/3,	  0,		MID_HIGH),
3806 	SEG(  0,	  3*BOX_HIGH/4,	  CHR_WIDE,   3*BOX_HIGH/4),
3807 	-1
3808     }, greater_than_or_equal[] =
3809     {
3810 	SEG(  0,	    BOX_HIGH/3,	  CHR_WIDE,	MID_HIGH),
3811 	SEG(  0,	  2*BOX_HIGH/3,	  CHR_WIDE,	MID_HIGH),
3812 	SEG(  0,	  3*BOX_HIGH/4,	  CHR_WIDE,   3*BOX_HIGH/4),
3813 	-1
3814     }, greek_pi[] =
3815     {
3816 	SEG(  0,	    MID_HIGH,	  CHR_WIDE,	MID_HIGH),
3817 	SEG(5*CHR_WIDE/6,   MID_HIGH,	5*CHR_WIDE/6,	CHR_HIGH),
3818 	SEG(2*CHR_WIDE/6,   MID_HIGH,	2*CHR_WIDE/6,	CHR_HIGH),
3819 	-1
3820     }, not_equal_to[] =
3821     {
3822 	SEG(2*BOX_WIDE/3, 1*BOX_HIGH/3, 1*BOX_WIDE/3,	CHR_HIGH),
3823 	SEG(  0,	  2*BOX_HIGH/3,	  CHR_WIDE,   2*BOX_HIGH/3),
3824 	SEG(  0,	    MID_HIGH,	  CHR_WIDE,	MID_HIGH),
3825 	-1
3826     };
3827 
3828     static const struct {
3829 	const int mode;			/* 1=y, 2=x, 3=both */
3830 	const short *data;
3831     } lines[] =
3832     {
3833 	{ 0, 0 },			/* 00 (unused) */
3834 	{ 0, 0 },			/* 01 diamond */
3835 	{ 0, 0 },			/* 02 box */
3836 	{ 0, glyph_ht },		/* 03 HT */
3837 	{ 0, glyph_ff },		/* 04 FF */
3838 	{ 0, 0 },			/* 05 CR */
3839 	{ 0, glyph_lf },		/* 06 LF */
3840 	{ 0, 0 },			/* 07 degrees (small circle) */
3841 	{ 3, plus_or_minus },		/* 08 */
3842 	{ 0, glyph_nl },		/* 09 */
3843 	{ 0, glyph_vt },		/* 0A */
3844 	{ 3, lower_right_corner },	/* 0B */
3845 	{ 3, upper_right_corner },	/* 0C */
3846 	{ 3, upper_left_corner },	/* 0D */
3847 	{ 3, lower_left_corner },	/* 0E */
3848 	{ 3, cross },			/* 0F */
3849 	{ 2, scan_line_1 },		/* 10 */
3850 	{ 2, scan_line_3 },		/* 11 */
3851 	{ 2, scan_line_7 },		/* 12 */
3852 	{ 2, scan_line_9 },		/* 13 */
3853 	{ 2, horizontal_line },		/* 14 */
3854 	{ 3, left_tee },		/* 15 */
3855 	{ 3, right_tee },		/* 16 */
3856 	{ 3, bottom_tee },		/* 17 */
3857 	{ 3, top_tee },			/* 18 */
3858 	{ 1, vertical_line },		/* 19 */
3859 	{ 0, less_than_or_equal },	/* 1A */
3860 	{ 0, greater_than_or_equal },	/* 1B */
3861 	{ 0, greek_pi },		/* 1C */
3862 	{ 0, not_equal_to },		/* 1D */
3863 	{ 0, 0 },			/* 1E LB */
3864 	{ 0, 0 },			/* 1F bullet */
3865     };
3866     /* *INDENT-ON* */
3867 
3868     GC gc2;
3869     CgsEnum cgsId = (ch == 2) ? gcDots : gcLine;
3870     VTwin *cgsWin = WhichVWin(screen);
3871     const short *p;
3872     unsigned font_width = (((params->draw_flags & DOUBLEWFONT) ? 2U : 1U)
3873 			   * screen->fnt_wide);
3874     unsigned font_height = (((params->draw_flags & DOUBLEHFONT) ? 2U : 1U)
3875 			    * screen->fnt_high);
3876 
3877     if (cells > 1)
3878 	font_width *= (unsigned) cells;
3879 
3880 #if OPT_WIDE_CHARS
3881     /*
3882      * Try to show line-drawing characters if we happen to be in UTF-8
3883      * mode, but have gotten an old-style font.
3884      */
3885     if (screen->utf8_mode
3886 #if OPT_RENDERFONT
3887 	&& !UsingRenderFont(params->xw)
3888 #endif
3889 	&& (ch > 127)
3890 	&& (ch != UCS_REPL)) {
3891 	int which = (params->attr_flags & BOLD) ? fBold : fNorm;
3892 	unsigned n;
3893 	for (n = 1; n < 32; n++) {
3894 	    if (xtermMissingChar(n, getNormalFont(screen, which)))
3895 		continue;
3896 	    if (dec2ucs(screen, n) != ch)
3897 		continue;
3898 	    TRACE(("...use xterm-style linedrawing U+%04X ->%d\n", ch, n));
3899 	    ch = n;
3900 	    break;
3901 	}
3902     }
3903 #endif
3904 
3905 #if OPT_VT52_MODE
3906     if (!(screen->vtXX_level)) {
3907 	switch (ch) {
3908 	case 6:
3909 	    ch = 7;
3910 	    break;
3911 	default:
3912 	    ch = 256;
3913 	    break;
3914 	}
3915     }
3916 #endif
3917 
3918     /*
3919      * Line-drawing characters show use the full (scaled) cellsize, while
3920      * other characters should be shifted to center them vertically.
3921      */
3922     if (!xftords) {
3923 	if ((ch < XtNumber(lines)) && (lines[ch].mode & 3) != 0) {
3924 	    font_height = (unsigned) ((float) font_height * screen->scale_height);
3925 	} else {
3926 	    y += ScaleShift(screen);
3927 	}
3928     }
3929 
3930     TRACE(("DRAW_BOX(%02X) cell %dx%d at %d,%d%s\n",
3931 	   ch, font_height, font_width, y, x,
3932 	   ((ch >= XtNumber(lines))
3933 	    ? "-BAD"
3934 	    : "")));
3935 
3936     if (cgsId == gcDots) {
3937 	setCgsFont(params->xw, cgsWin, cgsId, getCgsFont(params->xw, cgsWin, gc));
3938 	setCgsFore(params->xw, cgsWin, cgsId, getCgsFore(params->xw, cgsWin, gc));
3939 	setCgsBack(params->xw, cgsWin, cgsId, getCgsBack(params->xw, cgsWin, gc));
3940     } else {
3941 	setCgsFont(params->xw, cgsWin, cgsId, getCgsFont(params->xw, cgsWin, gc));
3942 	setCgsFore(params->xw, cgsWin, cgsId, getCgsBack(params->xw, cgsWin, gc));
3943 	setCgsBack(params->xw, cgsWin, cgsId, getCgsBack(params->xw, cgsWin, gc));
3944     }
3945     gc2 = getCgsGC(params->xw, cgsWin, cgsId);
3946 
3947     if (!(params->draw_flags & NOBACKGROUND)) {
3948 	XFillRectangle(screen->display, VDrawable(screen), gc2, x, y,
3949 		       font_width,
3950 		       font_height);
3951     }
3952 
3953     setCgsFont(params->xw, cgsWin, cgsId, getCgsFont(params->xw, cgsWin, gc));
3954     setCgsFore(params->xw, cgsWin, cgsId, getCgsFore(params->xw, cgsWin, gc));
3955     setCgsBack(params->xw, cgsWin, cgsId, getCgsBack(params->xw, cgsWin, gc));
3956     gc2 = getCgsGC(params->xw, cgsWin, cgsId);
3957 
3958     XSetLineAttributes(screen->display, gc2,
3959 		       (params->attr_flags & BOLD)
3960 		       ? ((font_height > 12)
3961 			  ? font_height / 12
3962 			  : 1)
3963 		       : ((font_height > 16)
3964 			  ? font_height / 16
3965 			  : 1),
3966 		       LineSolid,
3967 		       CapProjecting,
3968 		       JoinMiter);
3969 
3970     if (ch == 1) {		/* diamond */
3971 	XPoint points[5];
3972 	int npoints = 5, n;
3973 
3974 	points[0].x = MID_WIDE;
3975 	points[0].y = BOX_HIGH / 4;
3976 
3977 	points[1].x = 8 * BOX_WIDE / 8;
3978 	points[1].y = MID_HIGH;
3979 
3980 	points[2].x = points[0].x;
3981 	points[2].y = 3 * BOX_HIGH / 4;
3982 
3983 	points[3].x = 0 * BOX_WIDE / 8;
3984 	points[3].y = points[1].y;
3985 
3986 	points[4].x = points[0].x;
3987 	points[4].y = points[0].y;
3988 
3989 	for (n = 0; n < npoints; ++n) {
3990 	    points[n].x = (short) (SCALED_X(points[n].x));
3991 	    points[n].y = (short) (SCALED_Y(points[n].y));
3992 	    points[n].x = (short) (points[n].x + x);
3993 	    points[n].y = (short) (points[n].y + y);
3994 	}
3995 
3996 	XFillPolygon(screen->display,
3997 		     VDrawable(screen), gc2,
3998 		     points, npoints,
3999 		     Convex, CoordModeOrigin);
4000     } else if (ch == 7) {	/* degrees */
4001 	unsigned width = (BOX_WIDE / 3);
4002 	int x_coord = MID_WIDE - (int) (width / 2);
4003 	int y_coord = MID_HIGH - (int) width;
4004 
4005 	SCALE_X(x_coord);
4006 	SCALE_Y(y_coord);
4007 	width = (unsigned) SCALED_X(width);
4008 
4009 	XDrawArc(screen->display,
4010 		 VDrawable(screen), gc2,
4011 		 x + x_coord, y + y_coord, width, width,
4012 		 0,
4013 		 360 * 64);
4014     } else if (ch == 0x1f) {	/* bullet */
4015 	unsigned width = 7 * BOX_WIDE / 10;
4016 	int x_coord = MID_WIDE - (int) (width / 3);
4017 	int y_coord = MID_HIGH - (int) (width / 3);
4018 
4019 	SCALE_X(x_coord);
4020 	SCALE_Y(y_coord);
4021 	width = (unsigned) SCALED_X(width);
4022 
4023 	XDrawArc(screen->display,
4024 		 VDrawable(screen), gc2,
4025 		 x + x_coord, y + y_coord, width, width,
4026 		 0,
4027 		 360 * 64);
4028     } else if (ch < XtNumber(lines)
4029 	       && (p = lines[ch].data) != 0) {
4030 	int coord[4];
4031 	int n = 0;
4032 	while (*p >= 0) {
4033 	    coord[n++] = *p++;
4034 	    if (n == 4) {
4035 		SCALE_X(coord[0]);
4036 		SCALE_Y(coord[1]);
4037 		SCALE_X(coord[2]);
4038 		SCALE_Y(coord[3]);
4039 		XDrawLine(screen->display,
4040 			  VDrawable(screen), gc2,
4041 			  x + coord[0], y + coord[1],
4042 			  x + coord[2], y + coord[3]);
4043 		n = 0;
4044 	    }
4045 	}
4046     } else if (screen->force_all_chars) {
4047 	/* bounding rectangle, for debugging */
4048 	XDrawRectangle(screen->display, VDrawable(screen), gc2, x, y,
4049 		       font_width - 1,
4050 		       font_height - 1);
4051     }
4052 }
4053 #endif /* OPT_BOX_CHARS */
4054 
4055 #if OPT_RENDERFONT
4056 /*
4057  * Check if the glyph is defined in the given font, and (try to) filter out
4058  * cases where double-width glyphs are stuffed into a single-width outline.
4059  */
4060 static Boolean
foundXftGlyph(XtermWidget xw,XftFont * font,unsigned wc)4061 foundXftGlyph(XtermWidget xw, XftFont *font, unsigned wc)
4062 {
4063     TScreen *screen = TScreenOf(xw);
4064     Boolean result = False;
4065 
4066     if (font != 0 && XftGlyphExists(screen->display, font, wc)) {
4067 	int expect;
4068 
4069 	if ((expect = CharWidth(screen, wc)) > 0) {
4070 	    XGlyphInfo gi;
4071 	    int actual;
4072 
4073 	    XftTextExtents32(screen->display, font, &wc, 1, &gi);
4074 	    /*
4075 	     * Some (more than a few) fonts are sloppy; allow 10% outside
4076 	     * the bounding box to accommodate them.
4077 	     */
4078 	    actual = ((gi.xOff * 10) >= (11 * FontWidth(screen))) ? 2 : 1;
4079 	    if (actual <= expect) {
4080 		/* allow double-cell if wcwidth agrees */
4081 		result = True;
4082 	    } else {
4083 		TRACE(("SKIP U+%04X %d vs %d (%d vs %d)\n",
4084 		       wc, gi.xOff, FontWidth(screen), actual, expect));
4085 	    }
4086 	} else {
4087 	    result = True;
4088 	}
4089     }
4090     return result;
4091 }
4092 
4093 static void
markXftOpened(XtermWidget xw,XTermXftFonts * which,Cardinal n,unsigned wc)4094 markXftOpened(XtermWidget xw, XTermXftFonts *which, Cardinal n, unsigned wc)
4095 {
4096     if (which->cache[n].usage != xcOpened) {
4097 	which->opened++;
4098 	which->cache[n].usage = xcOpened;
4099 	/* XFT_DEBUG=3 will show useful context for this */
4100 	if (getenv("XFT_DEBUG") != 0) {
4101 	    printf("xterm: matched U+%04X in fontset #%d [%u:%u]\n",
4102 		   wc, n + 1,
4103 		   which->opened,
4104 		   xw->work.max_fontsets);
4105 	}
4106     }
4107 }
4108 
4109 /*
4110  * Check if the given character has a glyph known to Xft.  If it is missing,
4111  * try first to replace the font with a fallback that provides the glyph.
4112  */
4113 XftFont *
findXftGlyph(XtermWidget xw,XftFont * given,unsigned wc)4114 findXftGlyph(XtermWidget xw, XftFont *given, unsigned wc)
4115 {
4116     TScreen *screen = TScreenOf(xw);
4117     XTermXftFonts *which = 0;
4118     XftFont *result = 0;
4119     /* workaround for interface changes... */
4120     int fontnum = screen->menu_font_number;
4121     static int table[] =
4122     {
4123 	offsetof(TScreen, renderFontNorm),
4124 	offsetof(TScreen, renderFontBold),
4125 	offsetof(TScreen, renderFontItal),
4126 	offsetof(TScreen, renderFontBtal),
4127 #if OPT_RENDERWIDE
4128 	offsetof(TScreen, renderWideNorm),
4129 	offsetof(TScreen, renderWideBold),
4130 	offsetof(TScreen, renderWideItal),
4131 	offsetof(TScreen, renderWideBtal),
4132 #endif
4133     };
4134     Cardinal n;
4135     FcResult status;
4136     const char *tag = 0;
4137 
4138     /* if fontsets are not wanted, just leave */
4139     if (xw->work.max_fontsets == 0) {
4140 	return 0;
4141     }
4142 
4143     /* ignore codes in private use areas */
4144     if ((wc >= 0xe000 && wc <= 0xf8ff)
4145 	|| (wc >= 0xf0000 && wc <= 0xffffd)
4146 	|| (wc >= 0x100000 && wc <= 0x10fffd)) {
4147 	return 0;
4148     }
4149     /* the end of the BMP is reserved for non-characters */
4150     if (wc >= 0xfff0 && wc <= 0xffff) {
4151 	return 0;
4152     }
4153 
4154     for (n = 0; n < XtNumber(table); ++n) {
4155 	XTermXftFonts *check = (XTermXftFonts *) ((void *) ((char *) screen
4156 							    + table[n]));
4157 	if (check[fontnum].font == given) {
4158 	    which = &check[fontnum];
4159 	    tag = whichFontEnum((VTFontEnum) n);
4160 	    break;
4161 	}
4162     }
4163     if (which != 0) {
4164 	if (which->fontset == 0) {
4165 	    FcFontSet *sortedFonts;
4166 	    FcPattern *myPattern;
4167 	    int j;
4168 
4169 	    myPattern = FcPatternDuplicate(which->pattern);
4170 
4171 	    FcPatternAddBool(myPattern, FC_SCALABLE, FcTrue);
4172 	    FcPatternAddInteger(myPattern, FC_CHAR_WIDTH, given->max_advance_width);
4173 
4174 	    FcConfigSubstitute(FcConfigGetCurrent(),
4175 			       myPattern,
4176 			       FcMatchPattern);
4177 	    FcDefaultSubstitute(myPattern);
4178 
4179 	    which->fontset = FcFontSetCreate();
4180 
4181 	    sortedFonts = FcFontSort(0, myPattern, FcTrue, 0, &status);
4182 
4183 	    if (!sortedFonts || sortedFonts->nfont <= 0) {
4184 		xtermWarning("did not find any usable TrueType font\n");
4185 		return 0;
4186 	    }
4187 	    which->limit = (unsigned) sortedFonts->nfont;
4188 	    which->cache = TypeCallocN(XTermXftCache, (which->limit + 1));
4189 	    for (j = 0; j < sortedFonts->nfont; j++) {
4190 		FcPattern *font_pattern;
4191 
4192 		font_pattern = FcFontRenderPrepare(FcConfigGetCurrent(),
4193 						   myPattern,
4194 						   sortedFonts->fonts[j]);
4195 		if (font_pattern)
4196 		    FcFontSetAdd(which->fontset, font_pattern);
4197 	    }
4198 
4199 	    FcFontSetSortDestroy(sortedFonts);
4200 	    FcPatternDestroy(myPattern);
4201 	}
4202 	if (which->fontset != 0) {
4203 	    XftFont *check;
4204 	    Cardinal empty = which->limit;
4205 
4206 	    for (n = 0; n < which->limit; ++n) {
4207 		XftCache usage = which->cache[n].usage;
4208 		if (usage == xcEmpty) {
4209 		    if (empty > n)
4210 			empty = n;
4211 		} else if (usage == xcOpened
4212 			   || (usage == xcUnused
4213 			       && (which->opened < xw->work.max_fontsets))) {
4214 		    check = which->cache[n].font;
4215 		    if (foundXftGlyph(xw, check, wc)) {
4216 			markXftOpened(xw, which, n, wc);
4217 			result = check;
4218 			TRACE_FALLBACK(xw, "old", wc, (int) n, result);
4219 			break;
4220 		    }
4221 		}
4222 	    }
4223 
4224 	    if ((result == 0)
4225 		&& (empty < which->limit)
4226 		&& (which->opened < xw->work.max_fontsets)) {
4227 		FcPattern *myPattern = 0;
4228 		FcPattern *myReport = 0;
4229 
4230 		for (n = empty; n < which->limit; ++n) {
4231 		    if (which->cache[n].usage >= xcBogus)
4232 			continue;
4233 		    if (resource.reportFonts) {
4234 			myReport = FcPatternDuplicate(which->fontset->fonts[n]);
4235 		    }
4236 		    myPattern = FcPatternDuplicate(which->fontset->fonts[n]);
4237 		    check = XftFontOpenPattern(screen->display, myPattern);
4238 		    closeCachedXft(screen, which->cache[n].font);
4239 		    (void) maybeXftCache(xw, check);
4240 		    which->cache[n].font = check;
4241 		    which->cache[n].usage = xcBogus;
4242 		    if (check == 0)
4243 			continue;	/* shouldn't happen... */
4244 #ifdef FC_COLOR
4245 		    if (isBogusXft(check)) {
4246 			continue;
4247 		    }
4248 #endif
4249 		    if (foundXftGlyph(xw, check, wc)) {
4250 			markXftOpened(xw, which, n, wc);
4251 			reportXftFonts(xw, check, "fallback", tag, myReport);
4252 			result = check;
4253 			TRACE_FALLBACK(xw, "new", wc, (int) n, result);
4254 			break;
4255 		    }
4256 		    /*
4257 		     * The slot is opened, but we are not using it.
4258 		     */
4259 		    which->cache[n].usage = xcUnused;
4260 		}
4261 	    }
4262 	}
4263     }
4264     return result;
4265 }
4266 
4267 /*
4268  * Check if the given character has a glyph known to Xft.  If it is missing,
4269  * return true.
4270  *
4271  * see xc/lib/Xft/xftglyphs.c
4272  */
4273 Bool
xtermXftMissing(XtermWidget xw,XftFont * font,unsigned wc)4274 xtermXftMissing(XtermWidget xw, XftFont *font, unsigned wc)
4275 {
4276     Bool result = False;
4277 
4278     if (font != 0) {
4279 	TScreen *screen = TScreenOf(xw);
4280 	if (!XftGlyphExists(screen->display, font, wc)) {
4281 #if OPT_WIDE_CHARS
4282 	    TRACE2(("xtermXftMissing %d (dec=%#x, ucs=%#x)\n",
4283 		    wc, ucs2dec(screen, wc), dec2ucs(screen, wc)));
4284 #else
4285 	    TRACE2(("xtermXftMissing %d\n", wc));
4286 #endif
4287 	    result = True;
4288 	}
4289     }
4290     return result;
4291 }
4292 #endif /* OPT_RENDERFONT */
4293 
4294 #if OPT_WIDE_CHARS
4295 #define MY_UCS(ucs,dec) case ucs: result = dec; break
4296 unsigned
ucs2dec(TScreen * screen,unsigned ch)4297 ucs2dec(TScreen *screen, unsigned ch)
4298 {
4299     unsigned result = ch;
4300 
4301     (void) screen;
4302     if ((ch > 127)
4303 	&& (ch != UCS_REPL)) {
4304 #if OPT_VT52_MODE
4305 	if (screen != 0 && !(screen->vtXX_level)) {
4306 	    /*
4307 	     * Intentionally empty: it would be possible to use the built-in
4308 	     * line-drawing fallback in xtermDrawBoxChar(), but for testing
4309 	     * ncurses, this is good enough.
4310 	     */
4311 	    ;
4312 	} else
4313 #endif
4314 	    switch (ch) {
4315 		MY_UCS(0x25ae, 0);	/* black vertical rectangle           */
4316 		MY_UCS(0x25c6, 1);	/* black diamond                      */
4317 		MY_UCS(0x2592, 2);	/* medium shade                       */
4318 		MY_UCS(0x2409, 3);	/* symbol for horizontal tabulation   */
4319 		MY_UCS(0x240c, 4);	/* symbol for form feed               */
4320 		MY_UCS(0x240d, 5);	/* symbol for carriage return         */
4321 		MY_UCS(0x240a, 6);	/* symbol for line feed               */
4322 		MY_UCS(0x00b0, 7);	/* degree sign                        */
4323 		MY_UCS(0x00b1, 8);	/* plus-minus sign                    */
4324 		MY_UCS(0x2424, 9);	/* symbol for newline                 */
4325 		MY_UCS(0x240b, 10);	/* symbol for vertical tabulation     */
4326 		MY_UCS(0x2518, 11);	/* box drawings light up and left     */
4327 		MY_UCS(0x2510, 12);	/* box drawings light down and left   */
4328 		MY_UCS(0x250c, 13);	/* box drawings light down and right  */
4329 		MY_UCS(0x2514, 14);	/* box drawings light up and right    */
4330 		MY_UCS(0x253c, 15);	/* box drawings light vertical and horizontal */
4331 		MY_UCS(0x23ba, 16);	/* box drawings scan 1                */
4332 		MY_UCS(0x23bb, 17);	/* box drawings scan 3                */
4333 		MY_UCS(0x2500, 18);	/* box drawings light horizontal      */
4334 		MY_UCS(0x23bc, 19);	/* box drawings scan 7                */
4335 		MY_UCS(0x23bd, 20);	/* box drawings scan 9                */
4336 		MY_UCS(0x251c, 21);	/* box drawings light vertical and right      */
4337 		MY_UCS(0x2524, 22);	/* box drawings light vertical and left       */
4338 		MY_UCS(0x2534, 23);	/* box drawings light up and horizontal       */
4339 		MY_UCS(0x252c, 24);	/* box drawings light down and horizontal     */
4340 		MY_UCS(0x2502, 25);	/* box drawings light vertical        */
4341 		MY_UCS(0x2264, 26);	/* less-than or equal to              */
4342 		MY_UCS(0x2265, 27);	/* greater-than or equal to           */
4343 		MY_UCS(0x03c0, 28);	/* greek small letter pi              */
4344 		MY_UCS(0x2260, 29);	/* not equal to                       */
4345 		MY_UCS(0x00a3, 30);	/* pound sign                         */
4346 		MY_UCS(0x00b7, 31);	/* middle dot                         */
4347 	    }
4348     }
4349     return result;
4350 }
4351 
4352 #undef  MY_UCS
4353 #define MY_UCS(ucs,dec) case dec: result = ucs; break
4354 
4355 unsigned
dec2ucs(TScreen * screen,unsigned ch)4356 dec2ucs(TScreen *screen, unsigned ch)
4357 {
4358     unsigned result = ch;
4359 
4360     (void) screen;
4361     if (xtermIsDecGraphic(ch)) {
4362 #if OPT_VT52_MODE
4363 	if (screen != 0 && !(screen->vtXX_level)) {
4364 	    switch (ch) {
4365 		MY_UCS(0x0020, 0);	/* nbsp, treat as blank           */
4366 		MY_UCS(0x0020, 1);	/* reserved, treat as blank       */
4367 		MY_UCS(0x25ae, 2);	/* black vertical rectangle       */
4368 		MY_UCS(0x215f, 3);	/* "1/"                           */
4369 		MY_UCS(0x0020, 4);	/* "3/", not in Unicode, ignore   */
4370 		MY_UCS(0x0020, 5);	/* "5/", not in Unicode, ignore   */
4371 		MY_UCS(0x0020, 6);	/* "7/", not in Unicode, ignore   */
4372 		MY_UCS(0x00b0, 7);	/* degree sign                    */
4373 		MY_UCS(0x00b1, 8);	/* plus-minus sign                */
4374 		MY_UCS(0x2192, 9);	/* right-arrow                    */
4375 		MY_UCS(0x2026, 10);	/* ellipsis                       */
4376 		MY_UCS(0x00f7, 11);	/* divide by                      */
4377 		MY_UCS(0x2193, 12);	/* down arrow                     */
4378 		MY_UCS(0x23ba, 13);	/* bar at scan 0                  */
4379 		MY_UCS(0x23ba, 14);	/* bar at scan 1                  */
4380 		MY_UCS(0x23bb, 15);	/* bar at scan 2                  */
4381 		MY_UCS(0x23bb, 16);	/* bar at scan 3                  */
4382 		MY_UCS(0x23bc, 17);	/* bar at scan 4                  */
4383 		MY_UCS(0x23bc, 18);	/* bar at scan 5                  */
4384 		MY_UCS(0x23bd, 19);	/* bar at scan 6                  */
4385 		MY_UCS(0x23bd, 20);	/* bar at scan 7                  */
4386 		MY_UCS(0x2080, 21);	/* subscript 0                    */
4387 		MY_UCS(0x2081, 22);	/* subscript 1                    */
4388 		MY_UCS(0x2082, 23);	/* subscript 2                    */
4389 		MY_UCS(0x2083, 24);	/* subscript 3                    */
4390 		MY_UCS(0x2084, 25);	/* subscript 4                    */
4391 		MY_UCS(0x2085, 26);	/* subscript 5                    */
4392 		MY_UCS(0x2086, 27);	/* subscript 6                    */
4393 		MY_UCS(0x2087, 28);	/* subscript 7                    */
4394 		MY_UCS(0x2088, 29);	/* subscript 8                    */
4395 		MY_UCS(0x2089, 30);	/* subscript 9                    */
4396 		MY_UCS(0x00b6, 31);	/* paragraph                      */
4397 	    }
4398 	} else
4399 #endif
4400 	    switch (ch) {
4401 		MY_UCS(0x25ae, 0);	/* black vertical rectangle           */
4402 		MY_UCS(0x25c6, 1);	/* black diamond                      */
4403 		MY_UCS(0x2592, 2);	/* medium shade                       */
4404 		MY_UCS(0x2409, 3);	/* symbol for horizontal tabulation   */
4405 		MY_UCS(0x240c, 4);	/* symbol for form feed               */
4406 		MY_UCS(0x240d, 5);	/* symbol for carriage return         */
4407 		MY_UCS(0x240a, 6);	/* symbol for line feed               */
4408 		MY_UCS(0x00b0, 7);	/* degree sign                        */
4409 		MY_UCS(0x00b1, 8);	/* plus-minus sign                    */
4410 		MY_UCS(0x2424, 9);	/* symbol for newline                 */
4411 		MY_UCS(0x240b, 10);	/* symbol for vertical tabulation     */
4412 		MY_UCS(0x2518, 11);	/* box drawings light up and left     */
4413 		MY_UCS(0x2510, 12);	/* box drawings light down and left   */
4414 		MY_UCS(0x250c, 13);	/* box drawings light down and right  */
4415 		MY_UCS(0x2514, 14);	/* box drawings light up and right    */
4416 		MY_UCS(0x253c, 15);	/* box drawings light vertical and horizontal */
4417 		MY_UCS(0x23ba, 16);	/* box drawings scan 1                */
4418 		MY_UCS(0x23bb, 17);	/* box drawings scan 3                */
4419 		MY_UCS(0x2500, 18);	/* box drawings light horizontal      */
4420 		MY_UCS(0x23bc, 19);	/* box drawings scan 7                */
4421 		MY_UCS(0x23bd, 20);	/* box drawings scan 9                */
4422 		MY_UCS(0x251c, 21);	/* box drawings light vertical and right      */
4423 		MY_UCS(0x2524, 22);	/* box drawings light vertical and left       */
4424 		MY_UCS(0x2534, 23);	/* box drawings light up and horizontal       */
4425 		MY_UCS(0x252c, 24);	/* box drawings light down and horizontal     */
4426 		MY_UCS(0x2502, 25);	/* box drawings light vertical        */
4427 		MY_UCS(0x2264, 26);	/* less-than or equal to              */
4428 		MY_UCS(0x2265, 27);	/* greater-than or equal to           */
4429 		MY_UCS(0x03c0, 28);	/* greek small letter pi              */
4430 		MY_UCS(0x2260, 29);	/* not equal to                       */
4431 		MY_UCS(0x00a3, 30);	/* pound sign                         */
4432 		MY_UCS(0x00b7, 31);	/* middle dot                         */
4433 	    }
4434     }
4435     return result;
4436 }
4437 
4438 #endif /* OPT_WIDE_CHARS */
4439 
4440 #if OPT_RENDERFONT || OPT_SHIFT_FONTS
4441 static int
lookupOneFontSize(XtermWidget xw,int fontnum)4442 lookupOneFontSize(XtermWidget xw, int fontnum)
4443 {
4444     TScreen *screen = TScreenOf(xw);
4445 
4446     if (screen->menu_font_sizes[fontnum] == 0) {
4447 	XTermFonts fnt;
4448 
4449 	memset(&fnt, 0, sizeof(fnt));
4450 	screen->menu_font_sizes[fontnum] = -1;
4451 	if (xtermOpenFont(xw, screen->MenuFontName(fontnum), &fnt, True)) {
4452 	    if (fontnum <= fontMenu_lastBuiltin
4453 		|| strcmp(fnt.fn, DEFFONT)) {
4454 		screen->menu_font_sizes[fontnum] = FontSize(fnt.fs);
4455 		if (screen->menu_font_sizes[fontnum] <= 0)
4456 		    screen->menu_font_sizes[fontnum] = -1;
4457 	    }
4458 	    xtermCloseFont(xw, &fnt);
4459 	}
4460     }
4461     return (screen->menu_font_sizes[fontnum] > 0);
4462 }
4463 
4464 /*
4465  * Cache the font-sizes so subsequent larger/smaller font actions will go fast.
4466  */
4467 static void
lookupFontSizes(XtermWidget xw)4468 lookupFontSizes(XtermWidget xw)
4469 {
4470     int n;
4471 
4472     for (n = 0; n < NMENUFONTS; n++) {
4473 	(void) lookupOneFontSize(xw, n);
4474     }
4475 }
4476 #endif /* OPT_RENDERFONT || OPT_SHIFT_FONTS */
4477 
4478 #if OPT_RENDERFONT
4479 static double
defaultFaceSize(void)4480 defaultFaceSize(void)
4481 {
4482     double result;
4483     float value;
4484 
4485     if (sscanf(DEFFACESIZE, "%f", &value) == 1)
4486 	result = (double) value;
4487     else
4488 	result = 14.0;
4489     return result;
4490 }
4491 
4492 static void
fillInFaceSize(XtermWidget xw,int fontnum)4493 fillInFaceSize(XtermWidget xw, int fontnum)
4494 {
4495     TScreen *screen = TScreenOf(xw);
4496     double face_size = (double) xw->misc.face_size[fontnum];
4497 
4498     if (face_size <= 0.0) {
4499 #if OPT_SHIFT_FONTS
4500 	/*
4501 	 * If the user is switching font-sizes, make it follow by
4502 	 * default the same ratios to the default as the fixed fonts
4503 	 * would, for easy comparison.  There will be some differences
4504 	 * since the fixed fonts have a variety of height/width ratios,
4505 	 * but this is simpler than adding another resource value - and
4506 	 * as noted above, the data for the fixed fonts are available.
4507 	 */
4508 	(void) lookupOneFontSize(xw, 0);
4509 	if (fontnum == fontMenu_default) {
4510 	    face_size = defaultFaceSize();
4511 	} else if (lookupOneFontSize(xw, fontnum)
4512 		   && (screen->menu_font_sizes[0]
4513 		       != screen->menu_font_sizes[fontnum])) {
4514 	    double ratio;
4515 	    long num = screen->menu_font_sizes[fontnum];
4516 	    long den = screen->menu_font_sizes[0];
4517 
4518 	    if (den <= 0)
4519 		den = 1;
4520 	    ratio = dimSquareRoot((double) num / (double) den);
4521 
4522 	    face_size = (ratio * (double) xw->misc.face_size[0]);
4523 	    TRACE(("scaled[%d] using %3ld/%ld = %.2f -> %f\n",
4524 		   fontnum, num, den, ratio, face_size));
4525 	} else
4526 #endif
4527 	{
4528 #define LikeBitmap(s) (((s) / 78.0) * (double) xw->misc.face_size[fontMenu_default])
4529 	    switch (fontnum) {
4530 	    case fontMenu_font1:
4531 		face_size = LikeBitmap(2.0);
4532 		break;
4533 	    case fontMenu_font2:
4534 		face_size = LikeBitmap(35.0);
4535 		break;
4536 	    case fontMenu_font3:
4537 		face_size = LikeBitmap(60.0);
4538 		break;
4539 	    default:
4540 		face_size = defaultFaceSize();
4541 		break;
4542 	    case fontMenu_font4:
4543 		face_size = LikeBitmap(90.0);
4544 		break;
4545 	    case fontMenu_font5:
4546 		face_size = LikeBitmap(135.0);
4547 		break;
4548 	    case fontMenu_font6:
4549 		face_size = LikeBitmap(200.0);
4550 		break;
4551 	    case fontMenu_font7:
4552 		face_size = LikeBitmap(240.0);
4553 		break;
4554 	    }
4555 	    TRACE(("builtin[%d] -> %f\n", fontnum, face_size));
4556 	}
4557 	xw->misc.face_size[fontnum] = (float) face_size;
4558     }
4559 }
4560 
4561 /* no selection or escape */
4562 #define NMENU_RENDERFONTS (fontMenu_lastBuiltin + 1)
4563 
4564 /*
4565  * Workaround for breakage in font-packages - check if all of the bitmap font
4566  * sizes are the same, and if we're using TrueType fonts.
4567  */
4568 static Boolean
useFaceSizes(XtermWidget xw)4569 useFaceSizes(XtermWidget xw)
4570 {
4571     Boolean result = False;
4572 
4573     TRACE(("useFaceSizes " TRACE_L "\n"));
4574     if (UsingRenderFont(xw)) {
4575 	Boolean nonzero = True;
4576 	int n;
4577 
4578 	for (n = 0; n < NMENU_RENDERFONTS; ++n) {
4579 	    if (xw->misc.face_size[n] <= 0.0f) {
4580 		nonzero = False;
4581 		break;
4582 	    }
4583 	}
4584 	if (!nonzero) {
4585 	    Boolean broken_fonts = True;
4586 	    TScreen *screen = TScreenOf(xw);
4587 	    long first;
4588 
4589 	    lookupFontSizes(xw);
4590 	    first = screen->menu_font_sizes[0];
4591 	    for (n = 0; n < NMENUFONTS; n++) {
4592 		if (screen->menu_font_sizes[n] > 0
4593 		    && screen->menu_font_sizes[n] != first) {
4594 		    broken_fonts = False;
4595 		    break;
4596 		}
4597 	    }
4598 
4599 	    if (broken_fonts) {
4600 
4601 		TRACE(("bitmap fonts are broken - set faceSize resources\n"));
4602 		for (n = 0; n < NMENUFONTS; n++) {
4603 		    fillInFaceSize(xw, n);
4604 		}
4605 
4606 	    }
4607 	}
4608 	result = True;
4609     }
4610     TRACE((TRACE_R " useFaceSizes %d\n", result));
4611     return result;
4612 }
4613 #endif /* OPT_RENDERFONT */
4614 
4615 #if OPT_SHIFT_FONTS
4616 /*
4617  * Find the index of a larger/smaller font (according to the sign of 'relative'
4618  * and its magnitude), starting from the 'old' index.
4619  */
4620 int
lookupRelativeFontSize(XtermWidget xw,int old,int relative)4621 lookupRelativeFontSize(XtermWidget xw, int old, int relative)
4622 {
4623     TScreen *screen = TScreenOf(xw);
4624     int m = -1;
4625 
4626     TRACE(("lookupRelativeFontSize(old=%d, relative=%d)\n", old, relative));
4627     if (!IsIcon(screen)) {
4628 #if OPT_RENDERFONT
4629 	if (useFaceSizes(xw)) {
4630 	    TRACE(("...using FaceSize\n"));
4631 	    if (relative != 0) {
4632 		int n;
4633 		for (n = 0; n < NMENU_RENDERFONTS; ++n) {
4634 		    fillInFaceSize(xw, n);
4635 		    if (xw->misc.face_size[n] > 0 &&
4636 			xw->misc.face_size[n] != xw->misc.face_size[old]) {
4637 			int cmp_0 = ((xw->misc.face_size[n] >
4638 				      xw->misc.face_size[old])
4639 				     ? relative
4640 				     : -relative);
4641 			int cmp_m = ((m < 0)
4642 				     ? 1
4643 				     : ((xw->misc.face_size[n] <
4644 					 xw->misc.face_size[m])
4645 					? relative
4646 					: -relative));
4647 			if (cmp_0 > 0 && cmp_m > 0) {
4648 			    m = n;
4649 			}
4650 		    }
4651 		}
4652 	    }
4653 	} else
4654 #endif
4655 	{
4656 	    TRACE(("...using bitmap areas\n"));
4657 	    lookupFontSizes(xw);
4658 	    if (relative != 0) {
4659 		int n;
4660 		for (n = 0; n < NMENUFONTS; ++n) {
4661 		    if (screen->menu_font_sizes[n] > 0 &&
4662 			screen->menu_font_sizes[n] !=
4663 			screen->menu_font_sizes[old]) {
4664 			int cmp_0 = ((screen->menu_font_sizes[n] >
4665 				      screen->menu_font_sizes[old])
4666 				     ? relative
4667 				     : -relative);
4668 			int cmp_m = ((m < 0)
4669 				     ? 1
4670 				     : ((screen->menu_font_sizes[n] <
4671 					 screen->menu_font_sizes[m])
4672 					? relative
4673 					: -relative));
4674 			if (cmp_0 > 0 && cmp_m > 0) {
4675 			    m = n;
4676 			}
4677 		    }
4678 		}
4679 	    }
4680 	}
4681 	TRACE(("...new index %d\n", m));
4682 	if (m >= 0) {
4683 	    if (relative > 1)
4684 		m = lookupRelativeFontSize(xw, m, relative - 1);
4685 	    else if (relative < -1)
4686 		m = lookupRelativeFontSize(xw, m, relative + 1);
4687 	}
4688     }
4689     return m;
4690 }
4691 
4692 /* ARGSUSED */
4693 void
HandleLargerFont(Widget w,XEvent * event GCC_UNUSED,String * params GCC_UNUSED,Cardinal * param_count GCC_UNUSED)4694 HandleLargerFont(Widget w,
4695 		 XEvent *event GCC_UNUSED,
4696 		 String *params GCC_UNUSED,
4697 		 Cardinal *param_count GCC_UNUSED)
4698 {
4699     XtermWidget xw;
4700 
4701     TRACE(("Handle larger-vt-font for %p\n", (void *) w));
4702     if ((xw = getXtermWidget(w)) != 0) {
4703 	if (xw->misc.shift_fonts) {
4704 	    TScreen *screen = TScreenOf(xw);
4705 	    int m;
4706 
4707 	    m = lookupRelativeFontSize(xw, screen->menu_font_number, 1);
4708 	    if (m >= 0) {
4709 		SetVTFont(xw, m, True, NULL);
4710 	    } else {
4711 		Bell(xw, XkbBI_MinorError, 0);
4712 	    }
4713 	}
4714     }
4715 }
4716 
4717 /* ARGSUSED */
4718 void
HandleSmallerFont(Widget w,XEvent * event GCC_UNUSED,String * params GCC_UNUSED,Cardinal * param_count GCC_UNUSED)4719 HandleSmallerFont(Widget w,
4720 		  XEvent *event GCC_UNUSED,
4721 		  String *params GCC_UNUSED,
4722 		  Cardinal *param_count GCC_UNUSED)
4723 {
4724     XtermWidget xw;
4725 
4726     TRACE(("Handle smaller-vt-font for %p\n", (void *) w));
4727     if ((xw = getXtermWidget(w)) != 0) {
4728 	if (xw->misc.shift_fonts) {
4729 	    TScreen *screen = TScreenOf(xw);
4730 	    int m;
4731 
4732 	    m = lookupRelativeFontSize(xw, screen->menu_font_number, -1);
4733 	    if (m >= 0) {
4734 		SetVTFont(xw, m, True, NULL);
4735 	    } else {
4736 		Bell(xw, XkbBI_MinorError, 0);
4737 	    }
4738 	}
4739     }
4740 }
4741 #endif /* OPT_SHIFT_FONTS */
4742 
4743 int
xtermGetFont(const char * param)4744 xtermGetFont(const char *param)
4745 {
4746     int fontnum;
4747 
4748     switch (param[0]) {
4749     case 'd':
4750     case 'D':
4751     case '0':
4752 	fontnum = fontMenu_default;
4753 	break;
4754     case '1':
4755 	fontnum = fontMenu_font1;
4756 	break;
4757     case '2':
4758 	fontnum = fontMenu_font2;
4759 	break;
4760     case '3':
4761 	fontnum = fontMenu_font3;
4762 	break;
4763     case '4':
4764 	fontnum = fontMenu_font4;
4765 	break;
4766     case '5':
4767 	fontnum = fontMenu_font5;
4768 	break;
4769     case '6':
4770 	fontnum = fontMenu_font6;
4771 	break;
4772     case '7':
4773 	fontnum = fontMenu_font7;
4774 	break;
4775     case 'e':
4776     case 'E':
4777 	fontnum = fontMenu_fontescape;
4778 	break;
4779     case 's':
4780     case 'S':
4781 	fontnum = fontMenu_fontsel;
4782 	break;
4783     default:
4784 	fontnum = -1;
4785 	break;
4786     }
4787     TRACE(("xtermGetFont(%s) ->%d\n", NonNull(param), fontnum));
4788     return fontnum;
4789 }
4790 
4791 /* ARGSUSED */
4792 void
HandleSetFont(Widget w,XEvent * event GCC_UNUSED,String * params,Cardinal * param_count)4793 HandleSetFont(Widget w,
4794 	      XEvent *event GCC_UNUSED,
4795 	      String *params,
4796 	      Cardinal *param_count)
4797 {
4798     XtermWidget xw;
4799 
4800     if ((xw = getXtermWidget(w)) != 0) {
4801 	int fontnum;
4802 	VTFontNames fonts;
4803 
4804 	memset(&fonts, 0, sizeof(fonts));
4805 
4806 	if (*param_count == 0) {
4807 	    fontnum = fontMenu_default;
4808 	} else {
4809 	    Cardinal maxparams = 1;	/* total number of params allowed */
4810 	    int result = xtermGetFont(params[0]);
4811 
4812 	    switch (result) {
4813 	    case fontMenu_default:	/* FALLTHRU */
4814 	    case fontMenu_font1:	/* FALLTHRU */
4815 	    case fontMenu_font2:	/* FALLTHRU */
4816 	    case fontMenu_font3:	/* FALLTHRU */
4817 	    case fontMenu_font4:	/* FALLTHRU */
4818 	    case fontMenu_font5:	/* FALLTHRU */
4819 	    case fontMenu_font6:	/* FALLTHRU */
4820 	    case fontMenu_font7:	/* FALLTHRU */
4821 		break;
4822 	    case fontMenu_fontescape:
4823 #if OPT_WIDE_CHARS
4824 		maxparams = 5;
4825 #else
4826 		maxparams = 3;
4827 #endif
4828 		break;
4829 	    case fontMenu_fontsel:
4830 		maxparams = 2;
4831 		break;
4832 	    default:
4833 		Bell(xw, XkbBI_MinorError, 0);
4834 		return;
4835 	    }
4836 	    fontnum = result;
4837 
4838 	    if (*param_count > maxparams) {	/* see if extra args given */
4839 		Bell(xw, XkbBI_MinorError, 0);
4840 		return;
4841 	    }
4842 	    switch (*param_count) {	/* assign 'em */
4843 #if OPT_WIDE_CHARS
4844 	    case 5:
4845 		fonts.f_wb = x_strdup(params[4]);
4846 		/* FALLTHRU */
4847 	    case 4:
4848 		fonts.f_w = x_strdup(params[3]);
4849 #endif
4850 		/* FALLTHRU */
4851 	    case 3:
4852 		fonts.f_b = x_strdup(params[2]);
4853 		/* FALLTHRU */
4854 	    case 2:
4855 		fonts.f_n = x_strdup(params[1]);
4856 		break;
4857 	    }
4858 	}
4859 
4860 	SetVTFont(xw, fontnum, True, &fonts);
4861     }
4862 }
4863 
4864 void
SetVTFont(XtermWidget xw,int which,Bool doresize,const VTFontNames * fonts)4865 SetVTFont(XtermWidget xw,
4866 	  int which,
4867 	  Bool doresize,
4868 	  const VTFontNames * fonts)
4869 {
4870     TScreen *screen = TScreenOf(xw);
4871 
4872     TRACE(("SetVTFont(which=%d, f_n=%s, f_b=%s)\n", which,
4873 	   (fonts && fonts->f_n) ? fonts->f_n : "<null>",
4874 	   (fonts && fonts->f_b) ? fonts->f_b : "<null>"));
4875 
4876     if (IsIcon(screen)) {
4877 	Bell(xw, XkbBI_MinorError, 0);
4878     } else if (which >= 0 && which < NMENUFONTS) {
4879 	VTFontNames myfonts;
4880 
4881 	memset(&myfonts, 0, sizeof(myfonts));
4882 	if (fonts != 0)
4883 	    myfonts = *fonts;
4884 
4885 	if (which == fontMenu_fontsel) {	/* go get the selection */
4886 	    FindFontSelection(xw, myfonts.f_n, False);
4887 	} else {
4888 	    int oldFont = screen->menu_font_number;
4889 
4890 #define USE_CACHED(field, name) \
4891 	    if (myfonts.field == 0) { \
4892 		myfonts.field = x_strdup(screen->menu_font_names[which][name]); \
4893 		TRACE(("set myfonts." #field " from menu_font_names[%d][" #name "] %s\n", \
4894 		       which, NonNull(myfonts.field))); \
4895 	    } else { \
4896 		TRACE(("set myfonts." #field " reused\n")); \
4897 	    }
4898 #define SAVE_FNAME(field, name) \
4899 	    if (myfonts.field != 0) { \
4900 		if (screen->menu_font_names[which][name] == 0 \
4901 		 || strcmp(screen->menu_font_names[which][name], myfonts.field)) { \
4902 		    TRACE(("updating menu_font_names[%d][" #name "] to \"%s\"\n", \
4903 			   which, myfonts.field)); \
4904 		    FREE_STRING(screen->menu_font_names[which][name]); \
4905 		    screen->menu_font_names[which][name] = x_strdup(myfonts.field); \
4906 		} \
4907 	    }
4908 
4909 	    USE_CACHED(f_n, fNorm);
4910 	    USE_CACHED(f_b, fBold);
4911 #if OPT_WIDE_CHARS
4912 	    USE_CACHED(f_w, fWide);
4913 	    USE_CACHED(f_wb, fWBold);
4914 #endif
4915 	    if (xtermLoadFont(xw,
4916 			      &myfonts,
4917 			      doresize, which)) {
4918 		/*
4919 		 * If successful, save the data so that a subsequent query via
4920 		 * OSC-50 will return the expected values.
4921 		 */
4922 		SAVE_FNAME(f_n, fNorm);
4923 		SAVE_FNAME(f_b, fBold);
4924 #if OPT_WIDE_CHARS
4925 		SAVE_FNAME(f_w, fWide);
4926 		SAVE_FNAME(f_wb, fWBold);
4927 #endif
4928 	    } else {
4929 		(void) xtermLoadFont(xw,
4930 				     xtermFontName(screen->MenuFontName(oldFont)),
4931 				     doresize, oldFont);
4932 		Bell(xw, XkbBI_MinorError, 0);
4933 	    }
4934 	    FREE_FNAME(f_n);
4935 	    FREE_FNAME(f_b);
4936 #if OPT_WIDE_CHARS
4937 	    FREE_FNAME(f_w);
4938 	    FREE_FNAME(f_wb);
4939 #endif
4940 	}
4941     } else {
4942 	Bell(xw, XkbBI_MinorError, 0);
4943     }
4944     return;
4945 }
4946 
4947 #if OPT_RENDERFONT
4948 static void
trimSizeFromFace(char * face_name,float * face_size)4949 trimSizeFromFace(char *face_name, float *face_size)
4950 {
4951     char *first = strstr(face_name, ":size=");
4952     if (first == 0) {
4953 	first = face_name;
4954     } else {
4955 	first++;
4956     }
4957     if (!strncmp(first, "size=", (size_t) 5)) {
4958 	char *last = strchr(first, ':');
4959 	char mark;
4960 	float value;
4961 	char extra;
4962 	TRACE(("...before trimming, font = \"%s\"\n", face_name));
4963 	if (last == 0)
4964 	    last = first + strlen(first);
4965 	mark = *last;
4966 	*last = '\0';
4967 	if (sscanf(first, "size=%g%c", &value, &extra) == 1) {
4968 	    TRACE(("...trimmed size from font: %g\n", value));
4969 	    if (face_size != 0)
4970 		*face_size = value;
4971 	}
4972 	if (mark) {
4973 	    while ((*first++ = *++last) != '\0') {
4974 		;
4975 	    }
4976 	} else {
4977 	    if (first != face_name)
4978 		--first;
4979 	    *first = '\0';
4980 	}
4981 	TRACE(("...after trimming, font = \"%s\"\n", face_name));
4982     }
4983 }
4984 #endif
4985 
4986 /*
4987  * Save a font specification to the proper list.
4988  */
4989 static void
save2FontList(XtermWidget xw,const char * name,XtermFontNames * fontnames,VTFontEnum which,const char * source,Bool ttf)4990 save2FontList(XtermWidget xw,
4991 	      const char *name,
4992 	      XtermFontNames * fontnames,
4993 	      VTFontEnum which,
4994 	      const char *source,
4995 	      Bool ttf)
4996 {
4997     char *value;
4998     size_t plen;
4999     Bool marked = False;
5000     Bool use_ttf = ttf;
5001 
5002     (void) xw;
5003 
5004     if (source == 0)
5005 	source = "";
5006     while (isspace(CharOf(*source)))
5007 	++source;
5008 
5009     /* fontconfig patterns can contain ':' separators, but we'll treat
5010      * a leading prefix specially to denote whether the pattern might be
5011      * XLFD ("x" or "xlfd") versus Xft ("xft").
5012      */
5013     for (plen = 0; source[plen] != '\0'; ++plen) {
5014 	if (source[plen] == ':') {
5015 	    marked = True;
5016 	    switch (plen) {
5017 	    case 0:
5018 		++plen;		/* trim leading ':' */
5019 		break;
5020 	    case 1:
5021 		if (!strncmp(source, "x", plen)) {
5022 		    ++plen;
5023 		    use_ttf = False;
5024 		} else {
5025 		    marked = False;
5026 		}
5027 		break;
5028 	    case 3:
5029 		if (!strncmp(source, "xft", plen)) {
5030 		    ++plen;
5031 		    use_ttf = True;
5032 		} else {
5033 		    marked = False;
5034 		}
5035 		break;
5036 	    case 4:
5037 		if (!strncmp(source, "xlfd", plen)) {
5038 		    ++plen;
5039 		    use_ttf = False;
5040 		} else {
5041 		    marked = False;
5042 		}
5043 		break;
5044 	    default:
5045 		marked = False;
5046 		plen = 0;
5047 		break;
5048 	    }
5049 	    break;
5050 	}
5051     }
5052     if (!marked)
5053 	plen = 0;
5054     value = x_strtrim(source + plen);
5055     if (value != 0) {
5056 	Bool success = False;
5057 #if OPT_RENDERFONT
5058 	VTFontList *target = (use_ttf
5059 			      ? &(fontnames->xft)
5060 			      : &(fontnames->x11));
5061 #else
5062 	VTFontList *target = &(fontnames->x11);
5063 #endif
5064 	char ***list = 0;
5065 	char **next = 0;
5066 	size_t count = 0;
5067 
5068 	(void) use_ttf;
5069 	switch (which) {
5070 	case fNorm:
5071 	    list = &(target->list_n);
5072 	    break;
5073 	case fBold:
5074 	    list = &(target->list_b);
5075 	    break;
5076 #if OPT_WIDE_ATTRS || OPT_RENDERWIDE
5077 	case fItal:
5078 	    list = &(target->list_i);
5079 	    break;
5080 	case fBtal:
5081 	    list = &(target->list_bi);
5082 	    break;
5083 #endif
5084 #if OPT_WIDE_CHARS
5085 	case fWide:
5086 	    list = &(target->list_w);
5087 	    break;
5088 	case fWBold:
5089 	    list = &(target->list_wb);
5090 	    break;
5091 	case fWItal:
5092 	    list = &(target->list_wi);
5093 	    break;
5094 	case fWBtal:
5095 	    list = &(target->list_wbi);
5096 	    break;
5097 #endif
5098 	case fMAX:
5099 	    list = 0;
5100 	    break;
5101 	}
5102 
5103 	if (list != 0) {
5104 	    success = True;
5105 	    if (*list != 0) {
5106 		while ((*list)[count] != 0) {
5107 		    if (IsEmpty((*list)[count])) {
5108 			TRACE(("... initial %s\n", value));
5109 			free((*list)[count]);
5110 			break;
5111 		    } else if (!strcmp(value, (*list)[count])) {
5112 			TRACE(("... duplicate %s\n", value));
5113 			success = False;
5114 			break;
5115 		    }
5116 		    ++count;
5117 		}
5118 	    }
5119 	    if (success) {
5120 		next = realloc(*list, sizeof(char *) * (count + 2));
5121 		if (next != 0) {
5122 #if OPT_RENDERFONT
5123 		    if (use_ttf) {
5124 			trimSizeFromFace(value,
5125 					 (count == 0 && which == fNorm)
5126 					 ? &(xw->misc.face_size[0])
5127 					 : (float *) 0);
5128 		    }
5129 #endif
5130 		    next[count++] = value;
5131 		    next[count] = 0;
5132 		    *list = next;
5133 		    TRACE(("... saved \"%s\" \"%s\" %lu:\"%s\"\n",
5134 			   whichFontList(xw, target),
5135 			   whichFontList2(xw, *list),
5136 			   (unsigned long) count,
5137 			   value));
5138 		} else {
5139 		    fprintf(stderr,
5140 			    "realloc failure in save2FontList(%s)\n",
5141 			    name);
5142 		    freeFontList(list);
5143 		    success = False;
5144 		}
5145 	    }
5146 	}
5147 	if (success) {
5148 #if (MAX_XFT_FONTS == MAX_XLFD_FONTS)
5149 	    size_t limit = MAX_XFT_FONTS;
5150 #else
5151 	    size_t limit = use_ttf ? MAX_XFT_FONTS : MAX_XLFD_FONTS;
5152 #endif
5153 	    if (count > limit && *x_skip_blanks(value)) {
5154 		fprintf(stderr, "%s: too many fonts for %s, ignoring %s\n",
5155 			ProgramName,
5156 			whichFontEnum(which),
5157 			value);
5158 		if (list && *list) {
5159 		    free((*list)[limit]);
5160 		    (*list)[limit] = 0;
5161 		}
5162 	    }
5163 	} else {
5164 	    free(value);
5165 	}
5166     }
5167 }
5168 
5169 /*
5170  * In principle, any of the font-name resources could be extended to be a list
5171  * of font-names.  That would be bad for performance, but as a basis for an
5172  * extension, parse the font-name as a comma-separated list, creating/updating
5173  * an array of font-names.
5174  */
5175 void
allocFontList(XtermWidget xw,const char * name,XtermFontNames * target,VTFontEnum which,const char * source,Bool ttf)5176 allocFontList(XtermWidget xw,
5177 	      const char *name,
5178 	      XtermFontNames * target,
5179 	      VTFontEnum which,
5180 	      const char *source,
5181 	      Bool ttf)
5182 {
5183     char *blob;
5184 
5185     blob = x_strdup(source);
5186     if (blob != 0) {
5187 	int n;
5188 	int pass;
5189 	char **list = 0;
5190 
5191 	TRACE(("allocFontList %s name=\"%s\" source=\"%s\"\n",
5192 	       whichFontEnum(which), name, blob));
5193 
5194 	for (pass = 0; pass < 2; ++pass) {
5195 	    unsigned count = 0;
5196 	    if (pass)
5197 		list[0] = blob;
5198 	    for (n = 0; blob[n] != '\0'; ++n) {
5199 		if (blob[n] == ',') {
5200 		    ++count;
5201 		    if (pass != 0) {
5202 			blob[n] = '\0';
5203 			list[count] = blob + n + 1;
5204 		    }
5205 		}
5206 	    }
5207 	    if (!pass) {
5208 		if (count == 0 && *blob == '\0')
5209 		    break;
5210 		list = TypeCallocN(char *, count + 2);
5211 		if (list == 0)
5212 		    break;
5213 	    }
5214 	}
5215 	if (list) {
5216 	    for (n = 0; list[n] != 0; ++n) {
5217 		if (*list[n]) {
5218 		    save2FontList(xw, name, target, which, list[n], ttf);
5219 		}
5220 	    }
5221 	    free(list);
5222 	}
5223     }
5224     free(blob);
5225 }
5226 
5227 static void
initFontList(XtermWidget xw,const char * name,XtermFontNames * target,Bool ttf)5228 initFontList(XtermWidget xw,
5229 	     const char *name,
5230 	     XtermFontNames * target,
5231 	     Bool ttf)
5232 {
5233     int which;
5234 
5235     TRACE(("initFontList(%s)\n", name));
5236     for (which = 0; which < fMAX; ++which) {
5237 	save2FontList(xw, name, target, (VTFontEnum) which, "", ttf);
5238     }
5239 }
5240 
5241 void
initFontLists(XtermWidget xw)5242 initFontLists(XtermWidget xw)
5243 {
5244     TRACE(("initFontLists\n"));
5245     initFontList(xw, "x11 font", &(xw->work.fonts), False);
5246 #if OPT_RENDERFONT
5247     initFontList(xw, "xft font", &(xw->work.fonts), True);
5248 #endif
5249 #if OPT_LOAD_VTFONTS || OPT_WIDE_CHARS
5250     initFontList(xw, "cached font",
5251 		 &(xw->screen.cacheVTFonts.fonts), False);
5252 #endif
5253 }
5254 
5255 void
copyFontList(char *** targetp,char ** source)5256 copyFontList(char ***targetp, char **source)
5257 {
5258     freeFontList(targetp);
5259 
5260     if (source != 0) {
5261 	int pass;
5262 	size_t count;
5263 
5264 	for (pass = 0; pass < 2; ++pass) {
5265 	    for (count = 0; source[count] != 0; ++count) {
5266 		if (pass)
5267 		    (*targetp)[count] = x_strdup(source[count]);
5268 	    }
5269 	    if (!pass) {
5270 		++count;
5271 		*targetp = TypeCallocN(char *, count);
5272 	    }
5273 	}
5274     } else {
5275 	*targetp = TypeCallocN(char *, 2);
5276 	(*targetp)[0] = x_strdup("");
5277     }
5278 }
5279 
5280 #if OPT_LOAD_VTFONTS || OPT_WIDE_CHARS
5281 static Boolean
merge_sublist(char *** targetp,char ** source)5282 merge_sublist(char ***targetp, char **source)
5283 {
5284     Boolean result = False;
5285     if ((*targetp == 0 || IsEmpty(**targetp)) && !IsEmpty(*source)) {
5286 	copyFontList(targetp, source);
5287 	result = True;
5288     }
5289     return result;
5290 }
5291 #endif
5292 
5293 void
freeFontList(char *** targetp)5294 freeFontList(char ***targetp)
5295 {
5296     if (targetp != 0) {
5297 	char **target = *targetp;
5298 	if (target != 0) {
5299 	    int n;
5300 	    for (n = 0; target[n] != 0; ++n) {
5301 		free(target[n]);
5302 	    }
5303 	    free(target);
5304 	    *targetp = 0;
5305 	}
5306     }
5307 }
5308 
5309 void
freeFontLists(VTFontList * lists)5310 freeFontLists(VTFontList * lists)
5311 {
5312     int which;
5313 
5314     TRACE(("freeFontLists\n"));
5315     for (which = 0; which < fMAX; ++which) {
5316 	char ***target = 0;
5317 	switch (which) {
5318 	case fNorm:
5319 	    target = &(lists->list_n);
5320 	    break;
5321 	case fBold:
5322 	    target = &(lists->list_b);
5323 	    break;
5324 #if OPT_WIDE_ATTRS || OPT_RENDERWIDE
5325 	case fItal:
5326 	    target = &(lists->list_i);
5327 	    break;
5328 	case fBtal:
5329 	    target = &(lists->list_bi);
5330 	    break;
5331 #endif
5332 #if OPT_WIDE_CHARS
5333 	case fWide:
5334 	    target = &(lists->list_w);
5335 	    break;
5336 	case fWBold:
5337 	    target = &(lists->list_wb);
5338 	    break;
5339 	case fWItal:
5340 	    target = &(lists->list_wi);
5341 	    break;
5342 	case fWBtal:
5343 	    target = &(lists->list_wbi);
5344 	    break;
5345 #endif
5346 	default:
5347 	    target = 0;
5348 	    break;
5349 	}
5350 	freeFontList(target);
5351     }
5352 }
5353 
5354 /*
5355  * Return a pointer to the XLFD font information for a given font class.
5356  * XXX make this allocate the font on demand.
5357  */
5358 XTermFonts *
getNormalFont(TScreen * screen,int which)5359 getNormalFont(TScreen *screen, int which)
5360 {
5361     XTermFonts *result = 0;
5362     if (which >= 0 && which < fMAX)
5363 	result = GetNormalFont(screen, which);
5364     return result;
5365 }
5366 
5367 #if OPT_DEC_CHRSET
5368 XTermFonts *
getDoubleFont(TScreen * screen,int which)5369 getDoubleFont(TScreen *screen, int which)
5370 {
5371     XTermFonts *result = 0;
5372     if ((int) which >= 0 && which < NUM_CHRSET)
5373 	result = GetDoubleFont(screen, which);
5374     return result;
5375 }
5376 
5377 #if OPT_RENDERFONT
5378 XftFont *
getDoubleXftFont(XTermDraw * params,unsigned chrset,unsigned attr_flags)5379 getDoubleXftFont(XTermDraw * params, unsigned chrset, unsigned attr_flags)
5380 {
5381     XftFont *result = 0;
5382 
5383     XtermWidget xw = params->xw;
5384     TScreen *screen = TScreenOf(xw);
5385     XftPattern *top_pattern;
5386     int fontnum = screen->menu_font_number;
5387     const char *face_name = getFaceName(xw, False);
5388 
5389     if (chrset != CSET_SWL
5390 	&& (top_pattern = XftNameParse(face_name)) != 0) {
5391 	double face_size = (double) xw->misc.face_size[fontnum];
5392 	XftPattern *sub_pattern = XftPatternDuplicate(top_pattern);
5393 
5394 	switch (chrset) {
5395 	case CSET_DHL_TOP:
5396 	    /* FALLTHRU */
5397 	case CSET_DHL_BOT:
5398 	    face_size *= 2;
5399 	    XftPatternBuild(sub_pattern,
5400 			    NormXftPattern,
5401 			    (void *) 0);
5402 	    break;
5403 	case CSET_DWL:
5404 	    XftPatternBuild(sub_pattern,
5405 			    NormXftPattern,
5406 			    FC_ASPECT, XftTypeDouble, 2.0,
5407 			    (void *) 0);
5408 	    break;
5409 	}
5410 	if (attr_flags & BOLD) {
5411 	    XftPatternBuild(sub_pattern,
5412 			    XFT_WEIGHT, XftTypeInteger, XFT_WEIGHT_BOLD,
5413 			    (void *) 0);
5414 	}
5415 	result = xtermOpenXft(xw, face_name, sub_pattern, "doublesize");
5416     }
5417     return result;
5418 }
5419 #endif
5420 #endif /* OPT_DEC_CHRSET */
5421 
5422 #if OPT_WIDE_ATTRS || OPT_RENDERWIDE
5423 XTermFonts *
getItalicFont(TScreen * screen,int which)5424 getItalicFont(TScreen *screen, int which)
5425 {
5426     XTermFonts *result = 0;
5427 #if OPT_WIDE_ATTRS
5428     if (which >= 0 && which < fMAX)
5429 	result = GetItalicFont(screen, which);
5430 #else
5431     (void) screen;
5432     (void) which;
5433 #endif
5434     return result;
5435 }
5436 #endif
5437 
5438 #if OPT_RENDERFONT
5439 /*
5440  * This returns a pointer to everything known about a given Xft font.
5441  * XXX make this allocate the font on demand.
5442  */
5443 XTermXftFonts *
getMyXftFont(XtermWidget xw,int which,int fontnum)5444 getMyXftFont(XtermWidget xw, int which, int fontnum)
5445 {
5446     TScreen *screen = TScreenOf(xw);
5447     XTermXftFonts *result = 0;
5448     if (fontnum >= 0 && fontnum < NMENUFONTS) {
5449 	switch ((VTFontEnum) which) {
5450 	case fNorm:
5451 	    result = &(screen->renderFontNorm[fontnum]);
5452 	    break;
5453 	case fBold:
5454 	    result = &(screen->renderFontBold[fontnum]);
5455 	    break;
5456 #if OPT_WIDE_ATTRS || OPT_RENDERWIDE
5457 	case fItal:
5458 	    result = &(screen->renderFontItal[fontnum]);
5459 	    break;
5460 	case fBtal:
5461 	    result = &(screen->renderFontBtal[fontnum]);
5462 	    break;
5463 #endif
5464 #if OPT_WIDE_CHARS
5465 	case fWide:
5466 	    result = &(screen->renderWideNorm[fontnum]);
5467 	    break;
5468 	case fWBold:
5469 	    result = &(screen->renderWideBold[fontnum]);
5470 	    break;
5471 	case fWItal:
5472 	    result = &(screen->renderWideItal[fontnum]);
5473 	    break;
5474 	case fWBtal:
5475 	    result = &(screen->renderWideBtal[fontnum]);
5476 	    break;
5477 #endif
5478 	case fMAX:
5479 	    break;
5480 	}
5481     }
5482     return result;
5483 }
5484 
5485 XftFont *
getXftFont(XtermWidget xw,VTFontEnum which,int fontnum)5486 getXftFont(XtermWidget xw, VTFontEnum which, int fontnum)
5487 {
5488     XTermXftFonts *data = getMyXftFont(xw, (int) which, fontnum);
5489     XftFont *result = 0;
5490     if (data != 0)
5491 	result = data->font;
5492     return result;
5493 }
5494 #endif
5495 
5496 const char *
whichFontEnum(VTFontEnum value)5497 whichFontEnum(VTFontEnum value)
5498 {
5499     const char *result = "?";
5500 #define DATA(name) case name: result = #name; break
5501     switch (value) {
5502 	DATA(fNorm);
5503 	DATA(fBold);
5504 #if OPT_WIDE_ATTRS || OPT_RENDERWIDE
5505 	DATA(fItal);
5506 	DATA(fBtal);
5507 #endif
5508 #if OPT_WIDE_CHARS
5509 	DATA(fWide);
5510 	DATA(fWBold);
5511 	DATA(fWItal);
5512 	DATA(fWBtal);
5513 #endif
5514 	DATA(fMAX);
5515     }
5516 #undef DATA
5517     return result;
5518 }
5519 
5520 const char *
whichFontList(XtermWidget xw,VTFontList * value)5521 whichFontList(XtermWidget xw, VTFontList * value)
5522 {
5523     const char *result = "?";
5524     if (value == &(xw->work.fonts.x11))
5525 	result = "x11_fontnames";
5526 #if OPT_RENDERFONT
5527     else if (value == &(xw->work.fonts.xft))
5528 	result = "xft_fontnames";
5529 #endif
5530 #if OPT_LOAD_VTFONTS || OPT_WIDE_CHARS
5531     else if (value == &(xw->screen.cacheVTFonts.fonts.x11))
5532 	result = "cached_fontnames";
5533 #endif
5534     return result;
5535 }
5536 
5537 static const char *
whichFontList2s(VTFontList * list,char ** value)5538 whichFontList2s(VTFontList * list, char **value)
5539 {
5540     const char *result = 0;
5541 #define DATA(name) if (value == (list->name)) result = #name
5542     DATA(list_n);
5543     DATA(list_b);
5544 #if OPT_WIDE_ATTRS || OPT_RENDERWIDE
5545     DATA(list_i);
5546     DATA(list_bi);
5547 #endif
5548 #if OPT_WIDE_CHARS
5549     DATA(list_w);
5550     DATA(list_wb);
5551     DATA(list_wi);
5552     DATA(list_wbi);
5553 #endif
5554 #undef DATA
5555     return result;
5556 }
5557 
5558 const char *
whichFontList2(XtermWidget xw,char ** value)5559 whichFontList2(XtermWidget xw, char **value)
5560 {
5561     const char *result = 0;
5562 #define DATA(name) (result = whichFontList2s(&(xw->name), value))
5563     if (DATA(work.fonts.x11) == 0) {
5564 #if OPT_RENDERFONT
5565 	if (DATA(work.fonts.xft) == 0)
5566 #endif
5567 #if OPT_LOAD_VTFONTS || OPT_WIDE_CHARS
5568 	    if (DATA(screen.cacheVTFonts.fonts.x11) == 0)
5569 #endif
5570 		result = "?";
5571     }
5572 #undef DATA
5573     return result;
5574 }
5575