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