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