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