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