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