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