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