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