fontutils.c revision 94644356
1/* $XTermId: fontutils.c,v 1.448 2015/03/02 13:19:36 tom Exp $ */ 2 3/* 4 * Copyright 1998-2014,2015 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 SetFontWidth(screen,dst,src) (dst)->f_width = (src) 55#define SetFontHeight(screen,dst,src) (dst)->f_height = dimRound((screen)->scale_height * (float) (src)) 56 57/* from X11/Xlibint.h - not all vendors install this file */ 58#define CI_NONEXISTCHAR(cs) (((cs)->width == 0) && \ 59 (((cs)->rbearing|(cs)->lbearing| \ 60 (cs)->ascent|(cs)->descent) == 0)) 61 62#define CI_GET_CHAR_INFO_1D(fs,col,cs) \ 63{ \ 64 cs = 0; \ 65 if (col >= fs->min_char_or_byte2 && col <= fs->max_char_or_byte2) { \ 66 if (fs->per_char == NULL) { \ 67 cs = &fs->min_bounds; \ 68 } else { \ 69 cs = &fs->per_char[(col - fs->min_char_or_byte2)]; \ 70 } \ 71 if (CI_NONEXISTCHAR(cs)) cs = 0; \ 72 } \ 73} 74 75#define CI_GET_CHAR_INFO_2D(fs,row,col,cs) \ 76{ \ 77 cs = 0; \ 78 if (row >= fs->min_byte1 && row <= fs->max_byte1 && \ 79 col >= fs->min_char_or_byte2 && col <= fs->max_char_or_byte2) { \ 80 if (fs->per_char == NULL) { \ 81 cs = &fs->min_bounds; \ 82 } else { \ 83 cs = &fs->per_char[((row - fs->min_byte1) * \ 84 (fs->max_char_or_byte2 - \ 85 fs->min_char_or_byte2 + 1)) + \ 86 (col - fs->min_char_or_byte2)]; \ 87 } \ 88 if (CI_NONEXISTCHAR(cs)) cs = 0; \ 89 } \ 90} 91 92#define FREE_FNAME(field) \ 93 if (fonts == 0 || myfonts.field != fonts->field) { \ 94 FREE_STRING(myfonts.field); \ 95 myfonts.field = 0; \ 96 } 97 98/* 99 * A structure to hold the relevant properties from a font 100 * we need to make a well formed font name for it. 101 */ 102typedef struct { 103 /* registry, foundry, family */ 104 const char *beginning; 105 /* weight */ 106 const char *weight; 107 /* slant */ 108 const char *slant; 109 /* wideness */ 110 const char *wideness; 111 /* add style */ 112 const char *add_style; 113 int pixel_size; 114 const char *point_size; 115 int res_x; 116 int res_y; 117 const char *spacing; 118 int average_width; 119 /* charset registry, charset encoding */ 120 char *end; 121} FontNameProperties; 122 123#if OPT_RENDERFONT 124static void fillInFaceSize(XtermWidget, int); 125#endif 126 127#if OPT_SHIFT_FONTS 128static int lookupOneFontSize(XtermWidget, int); 129#endif 130 131#if OPT_REPORT_FONTS || OPT_WIDE_CHARS 132static unsigned 133countGlyphs(XFontStruct *fp) 134{ 135 unsigned count = 0; 136 137 if (fp != 0) { 138 if (fp->min_byte1 == 0 && fp->max_byte1 == 0) { 139 count = fp->max_char_or_byte2 - fp->min_char_or_byte2 + 1; 140 } else if (fp->min_char_or_byte2 < 256 141 && fp->max_char_or_byte2 < 256) { 142 unsigned first = (fp->min_byte1 << 8) + fp->min_char_or_byte2; 143 unsigned last = (fp->max_byte1 << 8) + fp->max_char_or_byte2; 144 count = last + 1 - first; 145 } 146 } 147 return count; 148} 149#endif 150 151#if OPT_WIDE_CHARS 152/* 153 * Verify that the wide-bold font is at least a bold font with roughly as many 154 * glyphs as the wide font. The counts should be the same, but settle for 155 * filtering out the worst of the font mismatches. 156 */ 157static Bool 158compatibleWideCounts(XFontStruct *wfs, XFontStruct *wbfs) 159{ 160 unsigned count_w = countGlyphs(wfs); 161 unsigned count_wb = countGlyphs(wbfs); 162 if (count_w <= 256 || 163 count_wb <= 256 || 164 ((count_w / 4) * 3) > count_wb) { 165 TRACE(("...font server lied (count wide %u vs wide-bold %u)\n", 166 count_w, count_wb)); 167 return False; 168 } 169 return True; 170} 171#endif /* OPT_WIDE_CHARS */ 172 173#if OPT_BOX_CHARS 174static void 175setupPackedFonts(XtermWidget xw) 176{ 177 TScreen *screen = TScreenOf(xw); 178 Bool value = False; 179 180#if OPT_RENDERFONT 181#define MIXED(name) screen->name[fontnum].map.mixed 182 if (xw->work.render_font == True) { 183 int fontnum = screen->menu_font_number; 184 185 screen->allow_packing = (Boolean) (MIXED(renderFontNorm) 186 || MIXED(renderFontBold) 187 || MIXED(renderFontItal) 188#if OPT_RENDERWIDE 189 || MIXED(renderWideNorm) 190 || MIXED(renderWideBold) 191 || MIXED(renderWideItal) 192#endif 193 ); 194#undef MIXED 195 } 196#endif /* OPT_RENDERFONT */ 197 198 value = screen->allow_packing; 199 200 SetItemSensitivity(fontMenuEntries[fontMenu_font_packedfont].widget, value); 201} 202#endif 203 204/* 205 * Returns the fields from start to stop in a dash- separated string. This 206 * function will modify the source, putting '\0's in the appropriate place and 207 * moving the beginning forward to after the '\0' 208 * 209 * This will NOT work for the last field (but we won't need it). 210 */ 211static char * 212n_fields(char **source, int start, int stop) 213{ 214 int i; 215 char *str, *str1; 216 217 /* 218 * find the start-1th dash 219 */ 220 for (i = start - 1, str = *source; i; i--, str++) { 221 if ((str = strchr(str, '-')) == 0) 222 return 0; 223 } 224 225 /* 226 * find the stopth dash 227 */ 228 for (i = stop - start + 1, str1 = str; i; i--, str1++) { 229 if ((str1 = strchr(str1, '-')) == 0) 230 return 0; 231 } 232 233 /* 234 * put a \0 at the end of the fields 235 */ 236 *(str1 - 1) = '\0'; 237 238 /* 239 * move source forward 240 */ 241 *source = str1; 242 243 return str; 244} 245 246static Boolean 247check_fontname(const char *name) 248{ 249 Boolean result = True; 250 251 if (IsEmpty(name)) { 252 TRACE(("fontname missing\n")); 253 result = False; 254 } 255 return result; 256} 257 258/* 259 * Gets the font properties from a given font structure. We use the FONT name 260 * to find them out, since that seems easier. 261 * 262 * Returns a pointer to a static FontNameProperties structure 263 * or NULL on error. 264 */ 265static FontNameProperties * 266get_font_name_props(Display *dpy, XFontStruct *fs, char **result) 267{ 268 static FontNameProperties props; 269 static char *last_name; 270 271 XFontProp *fp; 272 int i; 273 Atom fontatom = XInternAtom(dpy, "FONT", False); 274 char *name = 0; 275 char *str; 276 277 /* 278 * first get the full font name 279 */ 280 if (fontatom != 0) { 281 for (i = 0, fp = fs->properties; i < fs->n_properties; i++, fp++) { 282 if (fp->name == fontatom) { 283 name = XGetAtomName(dpy, fp->card32); 284 break; 285 } 286 } 287 } 288 289 if (name == 0) 290 return 0; 291 292 /* 293 * XGetAtomName allocates memory - don't leak 294 */ 295 if (last_name != 0) 296 XFree(last_name); 297 last_name = name; 298 299 if (result != 0) { 300 if (!check_fontname(name)) 301 return 0; 302 if (*result != 0) 303 free(*result); 304 *result = x_strdup(name); 305 } 306 307 /* 308 * Now split it up into parts and put them in 309 * their places. Since we are using parts of 310 * the original string, we must not free the Atom Name 311 */ 312 313 /* registry, foundry, family */ 314 if ((props.beginning = n_fields(&name, 1, 3)) == 0) 315 return 0; 316 317 /* weight is the next */ 318 if ((props.weight = n_fields(&name, 1, 1)) == 0) 319 return 0; 320 321 /* slant */ 322 if ((props.slant = n_fields(&name, 1, 1)) == 0) 323 return 0; 324 325 /* width */ 326 if ((props.wideness = n_fields(&name, 1, 1)) == 0) 327 return 0; 328 329 /* add style */ 330 if ((props.add_style = n_fields(&name, 1, 1)) == 0) 331 return 0; 332 333 /* pixel size */ 334 if ((str = n_fields(&name, 1, 1)) == 0) 335 return 0; 336 if ((props.pixel_size = atoi(str)) == 0) 337 return 0; 338 339 /* point size */ 340 if ((props.point_size = n_fields(&name, 1, 1)) == 0) 341 return 0; 342 343 /* res_x */ 344 if ((str = n_fields(&name, 1, 1)) == 0) 345 return 0; 346 if ((props.res_x = atoi(str)) == 0) 347 return 0; 348 349 /* res_y */ 350 if ((str = n_fields(&name, 1, 1)) == 0) 351 return 0; 352 if ((props.res_y = atoi(str)) == 0) 353 return 0; 354 355 /* spacing */ 356 if ((props.spacing = n_fields(&name, 1, 1)) == 0) 357 return 0; 358 359 /* average width */ 360 if ((str = n_fields(&name, 1, 1)) == 0) 361 return 0; 362 if ((props.average_width = atoi(str)) == 0) 363 return 0; 364 365 /* the rest: charset registry and charset encoding */ 366 props.end = name; 367 368 return &props; 369} 370 371#define ALLOCHUNK(n) ((n | 127) + 1) 372 373static void 374alloca_fontname(char **result, size_t next) 375{ 376 size_t last = (*result != 0) ? strlen(*result) : 0; 377 size_t have = (*result != 0) ? ALLOCHUNK(last) : 0; 378 size_t want = last + next + 2; 379 380 if (want >= have) { 381 want = ALLOCHUNK(want); 382 if (last != 0) { 383 char *save = *result; 384 *result = TypeRealloc(char, want, *result); 385 if (*result == 0) 386 free(save); 387 } else { 388 if ((*result = TypeMallocN(char, want)) != 0) 389 **result = '\0'; 390 } 391 } 392} 393 394static void 395append_fontname_str(char **result, const char *value) 396{ 397 if (value == 0) 398 value = "*"; 399 alloca_fontname(result, strlen(value)); 400 if (*result != 0) { 401 if (**result != '\0') 402 strcat(*result, "-"); 403 strcat(*result, value); 404 } 405} 406 407static void 408append_fontname_num(char **result, int value) 409{ 410 if (value < 0) { 411 append_fontname_str(result, "*"); 412 } else { 413 char temp[100]; 414 sprintf(temp, "%d", value); 415 append_fontname_str(result, temp); 416 } 417} 418 419/* 420 * Take the given font props and try to make a well formed font name specifying 421 * the same base font and size and everything, but with different weight/width 422 * according to the parameters. The return value is allocated, should be freed 423 * by the caller. 424 */ 425static char * 426derive_font_name(FontNameProperties *props, 427 const char *use_weight, 428 int use_average_width, 429 const char *use_encoding) 430{ 431 char *result = 0; 432 433 append_fontname_str(&result, props->beginning); 434 append_fontname_str(&result, use_weight); 435 append_fontname_str(&result, props->slant); 436 append_fontname_str(&result, 0); 437 append_fontname_str(&result, 0); 438 append_fontname_num(&result, props->pixel_size); 439 append_fontname_str(&result, props->point_size); 440 append_fontname_num(&result, props->res_x); 441 append_fontname_num(&result, props->res_y); 442 append_fontname_str(&result, props->spacing); 443 append_fontname_num(&result, use_average_width); 444 append_fontname_str(&result, use_encoding); 445 446 return result; 447} 448 449static char * 450bold_font_name(FontNameProperties *props, int use_average_width) 451{ 452 return derive_font_name(props, "bold", use_average_width, props->end); 453} 454 455#if OPT_WIDE_ATTRS 456static char * 457italic_font_name(FontNameProperties *props, int use_average_width) 458{ 459 FontNameProperties myprops = *props; 460 myprops.slant = "o"; 461 return derive_font_name(&myprops, props->weight, use_average_width, props->end); 462} 463#endif 464 465#if OPT_WIDE_CHARS 466#define derive_wide_font(props, weight) \ 467 derive_font_name(props, weight, props->average_width * 2, "ISO10646-1") 468 469static char * 470wide_font_name(FontNameProperties *props) 471{ 472 return derive_wide_font(props, "medium"); 473} 474 475static char * 476widebold_font_name(FontNameProperties *props) 477{ 478 return derive_wide_font(props, "bold"); 479} 480#endif /* OPT_WIDE_CHARS */ 481 482#if OPT_DEC_CHRSET 483/* 484 * Take the given font props and try to make a well formed font name specifying 485 * the same base font but changed depending on the given attributes and chrset. 486 * 487 * For double width fonts, we just double the X-resolution, for double height 488 * fonts we double the pixel-size and Y-resolution 489 */ 490char * 491xtermSpecialFont(TScreen *screen, unsigned attr_flags, unsigned draw_flags, unsigned chrset) 492{ 493#if OPT_TRACE 494 static char old_spacing[80]; 495 static FontNameProperties old_props; 496#endif 497 FontNameProperties *props; 498 char *result = 0; 499 const char *weight; 500 int pixel_size; 501 int res_x; 502 int res_y; 503 504 props = get_font_name_props(screen->display, screen->fnts[fNorm].fs, 0); 505 if (props == 0) 506 return result; 507 508 pixel_size = props->pixel_size; 509 res_x = props->res_x; 510 res_y = props->res_y; 511 if (attr_flags & BOLD) 512 weight = "bold"; 513 else 514 weight = props->weight; 515 516 if (CSET_DOUBLE(chrset)) 517 res_x *= 2; 518 519 if (chrset == CSET_DHL_TOP 520 || chrset == CSET_DHL_BOT) { 521 res_y *= 2; 522 pixel_size *= 2; 523 } 524#if OPT_TRACE 525 if (old_props.res_x != res_x 526 || old_props.res_x != res_y 527 || old_props.pixel_size != pixel_size 528 || strcmp(old_props.spacing, props->spacing)) { 529 TRACE(("xtermSpecialFont(atts = %#x, draw = %#x, chrset = %#x)\n", 530 attr_flags, draw_flags, chrset)); 531 TRACE(("res_x = %d\n", res_x)); 532 TRACE(("res_y = %d\n", res_y)); 533 TRACE(("point_size = %s\n", props->point_size)); 534 TRACE(("pixel_size = %d\n", pixel_size)); 535 TRACE(("spacing = %s\n", props->spacing)); 536 old_props.res_x = res_x; 537 old_props.res_x = res_y; 538 old_props.pixel_size = pixel_size; 539 old_props.spacing = old_spacing; 540 sprintf(old_spacing, "%.*s", (int) sizeof(old_spacing) - 2, props->spacing); 541 } 542#endif 543 544 append_fontname_str(&result, props->beginning); 545 append_fontname_str(&result, weight); 546 append_fontname_str(&result, props->slant); 547 append_fontname_str(&result, props->wideness); 548 append_fontname_str(&result, props->add_style); 549 append_fontname_num(&result, pixel_size); 550 append_fontname_str(&result, props->point_size); 551 append_fontname_num(&result, (draw_flags & NORESOLUTION) ? -1 : res_x); 552 append_fontname_num(&result, (draw_flags & NORESOLUTION) ? -1 : res_y); 553 append_fontname_str(&result, props->spacing); 554 append_fontname_str(&result, 0); 555 append_fontname_str(&result, props->end); 556 557 return result; 558} 559#endif /* OPT_DEC_CHRSET */ 560 561/* 562 * Case-independent comparison for font-names, including wildcards. 563 * XLFD allows '?' as a wildcard, but we do not handle that (no one seems 564 * to use it). 565 */ 566static Bool 567same_font_name(const char *pattern, const char *match) 568{ 569 Bool result = False; 570 571 if (pattern && match) { 572 while (*pattern && *match) { 573 if (*pattern == *match) { 574 pattern++; 575 match++; 576 } else if (*pattern == '*' || *match == '*') { 577 if (same_font_name(pattern + 1, match)) { 578 return True; 579 } else if (same_font_name(pattern, match + 1)) { 580 return True; 581 } else { 582 return False; 583 } 584 } else { 585 int p = x_toupper(*pattern++); 586 int m = x_toupper(*match++); 587 if (p != m) 588 return False; 589 } 590 } 591 result = (*pattern == *match); /* both should be NUL */ 592 } 593 return result; 594} 595 596/* 597 * Double-check the fontname that we asked for versus what the font server 598 * actually gave us. The larger fixed fonts do not always have a matching bold 599 * font, and the font server may try to scale another font or otherwise 600 * substitute a mismatched font. 601 * 602 * If we cannot get what we requested, we will fallback to the original 603 * behavior, which simulates bold by overstriking each character at one pixel 604 * offset. 605 */ 606static int 607got_bold_font(Display *dpy, XFontStruct *fs, String requested) 608{ 609 char *actual = 0; 610 int got; 611 612 if (get_font_name_props(dpy, fs, &actual) == 0) 613 got = 0; 614 else 615 got = same_font_name(requested, actual); 616 free(actual); 617 return got; 618} 619 620/* 621 * Check normal/bold (or wide/wide-bold) font pairs to see if we will be able 622 * to check for missing glyphs in a comparable manner. 623 */ 624static int 625comparable_metrics(XFontStruct *normal, XFontStruct *bold) 626{ 627#define DATA "comparable_metrics: " 628 int result = 0; 629 630 if (normal->all_chars_exist) { 631 if (bold->all_chars_exist) { 632 result = 1; 633 } else { 634 TRACE((DATA "all chars exist in normal font, but not in bold\n")); 635 } 636 } else if (normal->per_char != 0) { 637 if (bold->per_char != 0) { 638 result = 1; 639 } else { 640 TRACE((DATA "normal font has per-char metrics, but not bold\n")); 641 } 642 } else { 643 TRACE((DATA "normal font is not very good!\n")); 644 result = 1; /* give in (we're not going in reverse) */ 645 } 646 return result; 647#undef DATA 648} 649 650/* 651 * If the font server tries to adjust another font, it may not adjust it 652 * properly. Check that the bounding boxes are compatible. Otherwise we'll 653 * leave trash on the display when we mix normal and bold fonts. 654 */ 655static int 656same_font_size(XtermWidget xw, XFontStruct *nfs, XFontStruct *bfs) 657{ 658 TScreen *screen = TScreenOf(xw); 659 TRACE(("same_font_size height %d/%d, min %d/%d max %d/%d\n", 660 nfs->ascent + nfs->descent, 661 bfs->ascent + bfs->descent, 662 nfs->min_bounds.width, bfs->min_bounds.width, 663 nfs->max_bounds.width, bfs->max_bounds.width)); 664 return screen->free_bold_box 665 || ((nfs->ascent + nfs->descent) == (bfs->ascent + bfs->descent) 666 && (nfs->min_bounds.width == bfs->min_bounds.width 667 || nfs->min_bounds.width == bfs->min_bounds.width + 1) 668 && (nfs->max_bounds.width == bfs->max_bounds.width 669 || nfs->max_bounds.width == bfs->max_bounds.width + 1)); 670} 671 672/* 673 * Check if the font looks like it has fixed width 674 */ 675static int 676is_fixed_font(XFontStruct *fs) 677{ 678 if (fs) 679 return (fs->min_bounds.width == fs->max_bounds.width); 680 return 1; 681} 682 683/* 684 * Check if the font looks like a double width font (i.e. contains 685 * characters of width X and 2X 686 */ 687#if OPT_WIDE_CHARS 688static int 689is_double_width_font(XFontStruct *fs) 690{ 691 return ((2 * fs->min_bounds.width) == fs->max_bounds.width); 692} 693#else 694#define is_double_width_font(fs) 0 695#endif 696 697#if OPT_WIDE_CHARS && OPT_RENDERFONT && defined(HAVE_TYPE_FCCHAR32) 698#define HALF_WIDTH_TEST_STRING "1234567890" 699 700/* '1234567890' in Chinese characters in UTF-8 */ 701#define FULL_WIDTH_TEST_STRING "\xe4\xb8\x80\xe4\xba\x8c\xe4\xb8\x89" \ 702 "\xe5\x9b\x9b\xe4\xba\x94" \ 703 "\xef\xa7\x91\xe4\xb8\x83\xe5\x85\xab" \ 704 "\xe4\xb9\x9d\xef\xa6\xb2" 705 706/* '1234567890' in Korean script in UTF-8 */ 707#define FULL_WIDTH_TEST_STRING2 "\xec\x9d\xbc\xec\x9d\xb4\xec\x82\xbc" \ 708 "\xec\x82\xac\xec\x98\xa4" \ 709 "\xec\x9c\xa1\xec\xb9\xa0\xed\x8c\x94" \ 710 "\xea\xb5\xac\xec\x98\x81" 711 712#define HALF_WIDTH_CHAR1 0x0031 /* '1' */ 713#define HALF_WIDTH_CHAR2 0x0057 /* 'W' */ 714#define FULL_WIDTH_CHAR1 0x4E00 /* CJK Ideograph 'number one' */ 715#define FULL_WIDTH_CHAR2 0xAC00 /* Korean script syllable 'Ka' */ 716 717static Bool 718is_double_width_font_xft(Display *dpy, XftFont *font) 719{ 720 XGlyphInfo gi1, gi2; 721 FcChar32 c1 = HALF_WIDTH_CHAR1, c2 = HALF_WIDTH_CHAR2; 722 String fwstr = FULL_WIDTH_TEST_STRING; 723 String hwstr = HALF_WIDTH_TEST_STRING; 724 725 /* Some Korean fonts don't have Chinese characters at all. */ 726 if (!XftCharExists(dpy, font, FULL_WIDTH_CHAR1)) { 727 if (!XftCharExists(dpy, font, FULL_WIDTH_CHAR2)) 728 return False; /* Not a CJK font */ 729 else /* a Korean font without CJK Ideographs */ 730 fwstr = FULL_WIDTH_TEST_STRING2; 731 } 732 733 XftTextExtents32(dpy, font, &c1, 1, &gi1); 734 XftTextExtents32(dpy, font, &c2, 1, &gi2); 735 if (gi1.xOff != gi2.xOff) /* Not a fixed-width font */ 736 return False; 737 738 XftTextExtentsUtf8(dpy, 739 font, 740 (_Xconst FcChar8 *) hwstr, 741 (int) strlen(hwstr), 742 &gi1); 743 XftTextExtentsUtf8(dpy, 744 font, 745 (_Xconst FcChar8 *) fwstr, 746 (int) strlen(fwstr), 747 &gi2); 748 749 /* 750 * fontconfig and Xft prior to 2.2(?) set the width of half-width 751 * characters identical to that of full-width character in CJK double-width 752 * (bi-width / monospace) font even though the former is half as wide as 753 * the latter. This was fixed sometime before the release of fontconfig 754 * 2.2 in early 2003. See 755 * http://bugzilla.mozilla.org/show_bug.cgi?id=196312 756 * In the meantime, we have to check both possibilities. 757 */ 758 return ((2 * gi1.xOff == gi2.xOff) || (gi1.xOff == gi2.xOff)); 759} 760#else 761#define is_double_width_font_xft(dpy, xftfont) 0 762#endif 763 764#define EmptyFont(fs) (fs != 0 \ 765 && ((fs)->ascent + (fs)->descent == 0 \ 766 || (fs)->max_bounds.width == 0)) 767 768#define FontSize(fs) (((fs)->ascent + (fs)->descent) \ 769 * (fs)->max_bounds.width) 770 771const VTFontNames * 772xtermFontName(const char *normal) 773{ 774 static VTFontNames data; 775 FREE_STRING(data.f_n); 776 memset(&data, 0, sizeof(data)); 777 data.f_n = x_strdup(normal); 778 return &data; 779} 780 781static void 782cache_menu_font_name(TScreen *screen, int fontnum, int which, const char *name) 783{ 784 if (name != 0) { 785 String last = screen->menu_font_names[fontnum][which]; 786 if (last != 0) { 787 if (strcmp(last, name)) { 788 FREE_STRING(last); 789 TRACE(("caching menu fontname %d.%d %s\n", fontnum, which, name)); 790 screen->menu_font_names[fontnum][which] = x_strdup(name); 791 } 792 } else { 793 TRACE(("caching menu fontname %d.%d %s\n", fontnum, which, name)); 794 screen->menu_font_names[fontnum][which] = x_strdup(name); 795 } 796 } 797} 798 799/* 800 * Open the given font and verify that it is non-empty. Return a null on 801 * failure. 802 */ 803Bool 804xtermOpenFont(XtermWidget xw, 805 const char *name, 806 XTermFonts * result, 807 fontWarningTypes warn, 808 Bool force) 809{ 810 Bool code = False; 811 TScreen *screen = TScreenOf(xw); 812 813 if (!IsEmpty(name)) { 814 if ((result->fs = XLoadQueryFont(screen->display, name)) != 0) { 815 code = True; 816 if (EmptyFont(result->fs)) { 817 (void) xtermCloseFont(xw, result); 818 code = False; 819 } else { 820 result->fn = x_strdup(name); 821 } 822 } else if (XmuCompareISOLatin1(name, DEFFONT) != 0) { 823 if (warn <= xw->misc.fontWarnings 824#if OPT_RENDERFONT 825 && !UsingRenderFont(xw) 826#endif 827 ) { 828 TRACE(("OOPS: cannot load font %s\n", name)); 829 xtermWarning("cannot load font '%s'\n", name); 830#if OPT_RENDERFONT 831 /* 832 * Do a sanity check in case someone's mixed up xterm with 833 * one of those programs that read their resource data from 834 * xterm's namespace. 835 */ 836 if (strchr(name, ':') != 0 || strchr(name, '=') != 0) { 837 xtermWarning("Use the \"-fa\" option for the Xft fonts\n"); 838 } 839#endif 840 } else { 841 TRACE(("xtermOpenFont: cannot load font '%s'\n", name)); 842 } 843 if (force) { 844 code = xtermOpenFont(xw, DEFFONT, result, fwAlways, True); 845 } 846 } 847 } 848 return code; 849} 850 851/* 852 * Close the font and free the font info. 853 */ 854XTermFonts * 855xtermCloseFont(XtermWidget xw, XTermFonts * fnt) 856{ 857 if (fnt != 0 && fnt->fs != 0) { 858 TScreen *screen = TScreenOf(xw); 859 860 clrCgsFonts(xw, WhichVWin(screen), fnt); 861 XFreeFont(screen->display, fnt->fs); 862 xtermFreeFontInfo(fnt); 863 } 864 return 0; 865} 866 867/* 868 * Close the listed fonts, noting that some may use copies of the pointer. 869 */ 870void 871xtermCloseFonts(XtermWidget xw, XTermFonts * fnts) 872{ 873 int j, k; 874 875 for (j = 0; j < fMAX; ++j) { 876 /* 877 * Need to save the pointer since xtermCloseFont zeroes it 878 */ 879 XFontStruct *thisFont = fnts[j].fs; 880 if (thisFont != 0) { 881 xtermCloseFont(xw, &fnts[j]); 882 for (k = j + 1; k < fMAX; ++k) { 883 if (thisFont == fnts[k].fs) 884 xtermFreeFontInfo(&fnts[k]); 885 } 886 } 887 } 888} 889 890/* 891 * Make a copy of the source, assuming the XFontStruct's to be unique, but 892 * ensuring that the names are reallocated to simplify freeing. 893 */ 894void 895xtermCopyFontInfo(XTermFonts * target, XTermFonts * source) 896{ 897 xtermFreeFontInfo(target); 898 target->chrset = source->chrset; 899 target->flags = source->flags; 900 target->fn = x_strdup(source->fn); 901 target->fs = source->fs; 902} 903 904void 905xtermFreeFontInfo(XTermFonts * target) 906{ 907 target->chrset = 0; 908 target->flags = 0; 909 if (target->fn != 0) { 910 free(target->fn); 911 target->fn = 0; 912 } 913 target->fs = 0; 914} 915 916#if OPT_REPORT_FONTS 917static void 918reportXCharStruct(const char *tag, XCharStruct * cs) 919{ 920 printf("\t\t%s:\n", tag); 921 printf("\t\t\tlbearing: %d\n", cs->lbearing); 922 printf("\t\t\trbearing: %d\n", cs->rbearing); 923 printf("\t\t\twidth: %d\n", cs->width); 924 printf("\t\t\tascent: %d\n", cs->ascent); 925 printf("\t\t\tdescent: %d\n", cs->descent); 926} 927 928static void 929reportOneVTFont(const char *tag, 930 XTermFonts * fnt) 931{ 932 if (!IsEmpty(fnt->fn)) { 933 XFontStruct *fs = fnt->fs; 934 unsigned first_char = 0; 935 unsigned last_char = 0; 936 unsigned ch; 937 938 if (fs->max_byte1 == 0) { 939 first_char = fs->min_char_or_byte2; 940 last_char = fs->max_char_or_byte2; 941 } else { 942 first_char = (fs->min_byte1 * 256) + fs->min_char_or_byte2; 943 last_char = (fs->max_byte1 * 256) + fs->max_char_or_byte2; 944 } 945 946 printf("\t%s: %s\n", tag, NonNull(fnt->fn)); 947 printf("\t\tall chars: %s\n", fs->all_chars_exist ? "yes" : "no"); 948 printf("\t\tdefault char: %d\n", fs->default_char); 949 printf("\t\tdirection: %d\n", fs->direction); 950 printf("\t\tascent: %d\n", fs->ascent); 951 printf("\t\tdescent: %d\n", fs->descent); 952 printf("\t\tfirst char: %u\n", first_char); 953 printf("\t\tlast char: %u\n", last_char); 954 printf("\t\tmaximum-chars: %u\n", countGlyphs(fs)); 955 if (FontLacksMetrics(fnt)) { 956 printf("\t\tmissing-chars: ?\n"); 957 printf("\t\tpresent-chars: ?\n"); 958 } else { 959 unsigned missing = 0; 960 for (ch = first_char; ch <= last_char; ++ch) { 961 if (xtermMissingChar(ch, fnt)) { 962 ++missing; 963 } 964 } 965 printf("\t\tmissing-chars: %u\n", missing); 966 printf("\t\tpresent-chars: %u\n", countGlyphs(fs) - missing); 967 } 968 printf("\t\tmin_byte1: %d\n", fs->min_byte1); 969 printf("\t\tmax_byte1: %d\n", fs->max_byte1); 970 printf("\t\tproperties: %d\n", fs->n_properties); 971 reportXCharStruct("min_bounds", &(fs->min_bounds)); 972 reportXCharStruct("max_bounds", &(fs->max_bounds)); 973 /* TODO: report fs->properties and fs->per_char */ 974 } 975} 976 977static void 978reportVTFontInfo(XtermWidget xw, int fontnum) 979{ 980 if (resource.reportFonts) { 981 TScreen *screen = TScreenOf(xw); 982 983 if (fontnum) { 984 printf("Loaded VTFonts(font%d)\n", fontnum); 985 } else { 986 printf("Loaded VTFonts(default)\n"); 987 } 988 reportOneVTFont("fNorm", &screen->fnts[fNorm]); 989 reportOneVTFont("fBold", &screen->fnts[fBold]); 990#if OPT_WIDE_CHARS 991 reportOneVTFont("fWide", &screen->fnts[fWide]); 992 reportOneVTFont("fWBold", &screen->fnts[fWBold]); 993#endif 994 } 995} 996#endif 997 998void 999xtermUpdateFontGCs(XtermWidget xw, XTermFonts * fnts) 1000{ 1001 TScreen *screen = TScreenOf(xw); 1002 VTwin *win = WhichVWin(screen); 1003 Pixel new_normal = getXtermForeground(xw, xw->flags, xw->cur_foreground); 1004 Pixel new_revers = getXtermBackground(xw, xw->flags, xw->cur_background); 1005 1006 setCgsFore(xw, win, gcNorm, new_normal); 1007 setCgsBack(xw, win, gcNorm, new_revers); 1008 setCgsFont(xw, win, gcNorm, &(fnts[fNorm])); 1009 1010 copyCgs(xw, win, gcBold, gcNorm); 1011 setCgsFont(xw, win, gcBold, &(fnts[fBold])); 1012 1013 setCgsFore(xw, win, gcNormReverse, new_revers); 1014 setCgsBack(xw, win, gcNormReverse, new_normal); 1015 setCgsFont(xw, win, gcNormReverse, &(fnts[fNorm])); 1016 1017 copyCgs(xw, win, gcBoldReverse, gcNormReverse); 1018 setCgsFont(xw, win, gcBoldReverse, &(fnts[fBold])); 1019 1020 if_OPT_WIDE_CHARS(screen, { 1021 if (fnts[fWide].fs != 0 1022 && fnts[fWBold].fs != 0) { 1023 setCgsFore(xw, win, gcWide, new_normal); 1024 setCgsBack(xw, win, gcWide, new_revers); 1025 setCgsFont(xw, win, gcWide, &(fnts[fWide])); 1026 1027 copyCgs(xw, win, gcWBold, gcWide); 1028 setCgsFont(xw, win, gcWBold, &(fnts[fWBold])); 1029 1030 setCgsFore(xw, win, gcWideReverse, new_revers); 1031 setCgsBack(xw, win, gcWideReverse, new_normal); 1032 setCgsFont(xw, win, gcWideReverse, &(fnts[fWide])); 1033 1034 copyCgs(xw, win, gcWBoldReverse, gcWideReverse); 1035 setCgsFont(xw, win, gcWBoldReverse, &(fnts[fWBold])); 1036 } 1037 }); 1038} 1039 1040#if OPT_TRACE 1041static void 1042show_font_misses(const char *name, XTermFonts * fp) 1043{ 1044 if (fp->fs != 0) { 1045 if (FontLacksMetrics(fp)) { 1046 TRACE(("%s font lacks metrics\n", name)); 1047 } else if (FontIsIncomplete(fp)) { 1048 TRACE(("%s font is incomplete\n", name)); 1049 } else { 1050 TRACE(("%s font is complete\n", name)); 1051 } 1052 } else { 1053 TRACE(("%s font is missing\n", name)); 1054 } 1055} 1056#endif 1057 1058int 1059xtermLoadFont(XtermWidget xw, 1060 const VTFontNames * fonts, 1061 Bool doresize, 1062 int fontnum) 1063{ 1064 TScreen *screen = TScreenOf(xw); 1065 VTwin *win = WhichVWin(screen); 1066 1067 VTFontNames myfonts; 1068 FontNameProperties *fp; 1069 XTermFonts fnts[fMAX]; 1070 char *tmpname = NULL; 1071 char *normal = NULL; 1072 Boolean proportional = False; 1073 fontWarningTypes warn[fMAX]; 1074 int j; 1075 1076 memset(&myfonts, 0, sizeof(myfonts)); 1077 memset(fnts, 0, sizeof(fnts)); 1078 1079 if (fonts != 0) 1080 myfonts = *fonts; 1081 if (!check_fontname(myfonts.f_n)) 1082 return 0; 1083 1084 /* 1085 * Check the font names against the resource values, to see which were 1086 * derived in a previous call. If so, we'll only warn about those if 1087 * the warning level is set to "always". 1088 */ 1089 for (j = 0; j < fMAX; ++j) { 1090 warn[j] = fwAlways; 1091 } 1092#define CmpResource(field, index) \ 1093 if (same_font_name(screen->menu_font_names[fontnum][index], myfonts.field)) \ 1094 warn[index] = fwResource 1095 1096 CmpResource(f_n, fNorm); 1097 if (fontnum == fontMenu_default) { 1098 CmpResource(f_b, fBold); 1099#if OPT_WIDE_CHARS 1100 CmpResource(f_b, fWide); 1101 CmpResource(f_b, fWBold); 1102#endif 1103 } 1104 1105 if (fontnum == fontMenu_fontescape 1106 && myfonts.f_n != screen->MenuFontName(fontnum)) { 1107 if ((tmpname = x_strdup(myfonts.f_n)) == 0) 1108 return 0; 1109 } 1110 1111 TRACE(("Begin Cgs - xtermLoadFont(%s)\n", myfonts.f_n)); 1112 releaseWindowGCs(xw, win); 1113 1114#define DbgResource(name, field, index) \ 1115 TRACE(("xtermLoadFont #%d "name" %s%s\n", \ 1116 fontnum, \ 1117 (warn[index] == fwResource) ? "*" : " ", \ 1118 NonNull(myfonts.field))) 1119 DbgResource("normal", f_n, fNorm); 1120 DbgResource("bold ", f_b, fBold); 1121#if OPT_WIDE_CHARS 1122 DbgResource("wide ", f_w, fWide); 1123 DbgResource("w/bold", f_wb, fWBold); 1124#endif 1125 1126 /* 1127 * If we are opening the default font, and it happens to be missing, force 1128 * that to the compiled-in default font, e.g., "fixed". If we cannot open 1129 * the font, disable it from the menu. 1130 */ 1131 if (!xtermOpenFont(xw, 1132 myfonts.f_n, 1133 &fnts[fNorm], 1134 warn[fNorm], 1135 (fontnum == fontMenu_default))) { 1136 if (fontnum != fontMenu_fontsel) { 1137 SetItemSensitivity(fontMenuEntries[fontnum].widget, False); 1138 } 1139 goto bad; 1140 } 1141 1142 normal = x_strdup(myfonts.f_n); 1143 if (!check_fontname(myfonts.f_b)) { 1144 warn[fBold] = fwAlways; 1145 fp = get_font_name_props(screen->display, fnts[fNorm].fs, &normal); 1146 if (fp != 0) { 1147 FREE_FNAME(f_b); 1148 myfonts.f_b = bold_font_name(fp, fp->average_width); 1149 if (!xtermOpenFont(xw, myfonts.f_b, &fnts[fBold], fwAlways, False)) { 1150 FREE_FNAME(f_b); 1151 myfonts.f_b = bold_font_name(fp, -1); 1152 xtermOpenFont(xw, myfonts.f_b, &fnts[fBold], fwAlways, False); 1153 } 1154 TRACE(("...derived bold '%s'\n", NonNull(myfonts.f_b))); 1155 } 1156 if (fp == 0 || fnts[fBold].fs == 0) { 1157 xtermCopyFontInfo(&fnts[fBold], &fnts[fNorm]); 1158 TRACE(("...cannot load a matching bold font\n")); 1159 } else if (comparable_metrics(fnts[fNorm].fs, fnts[fBold].fs) 1160 && same_font_size(xw, fnts[fNorm].fs, fnts[fBold].fs) 1161 && got_bold_font(screen->display, fnts[fBold].fs, myfonts.f_b)) { 1162 TRACE(("...got a matching bold font\n")); 1163 cache_menu_font_name(screen, fontnum, fBold, myfonts.f_b); 1164 } else { 1165 xtermCloseFont(xw, &fnts[fBold]); 1166 fnts[fBold] = fnts[fNorm]; 1167 TRACE(("...did not get a matching bold font\n")); 1168 } 1169 } else if (!xtermOpenFont(xw, myfonts.f_b, &fnts[fBold], warn[fBold], False)) { 1170 xtermCopyFontInfo(&fnts[fBold], &fnts[fNorm]); 1171 warn[fBold] = fwAlways; 1172 TRACE(("...cannot load bold font '%s'\n", NonNull(myfonts.f_b))); 1173 } else { 1174 cache_menu_font_name(screen, fontnum, fBold, myfonts.f_b); 1175 } 1176 1177 /* 1178 * If there is no widefont specified, fake it by doubling AVERAGE_WIDTH 1179 * of normal fonts XLFD, and asking for it. This plucks out 18x18ja 1180 * and 12x13ja as the corresponding fonts for 9x18 and 6x13. 1181 */ 1182 if_OPT_WIDE_CHARS(screen, { 1183 Boolean derived; 1184 char *bold = NULL; 1185 1186 if (check_fontname(myfonts.f_w)) { 1187 cache_menu_font_name(screen, fontnum, fWide, myfonts.f_w); 1188 } else if (screen->utf8_fonts && !is_double_width_font(fnts[fNorm].fs)) { 1189 FREE_FNAME(f_w); 1190 fp = get_font_name_props(screen->display, fnts[fNorm].fs, &normal); 1191 if (fp != 0) { 1192 myfonts.f_w = wide_font_name(fp); 1193 warn[fWide] = fwAlways; 1194 TRACE(("...derived wide %s\n", NonNull(myfonts.f_w))); 1195 cache_menu_font_name(screen, fontnum, fWide, myfonts.f_w); 1196 } 1197 } 1198 1199 if (check_fontname(myfonts.f_w)) { 1200 (void) xtermOpenFont(xw, myfonts.f_w, &fnts[fWide], warn[fWide], False); 1201 } else { 1202 xtermCopyFontInfo(&fnts[fWide], &fnts[fNorm]); 1203 warn[fWide] = fwAlways; 1204 } 1205 1206 derived = False; 1207 if (!check_fontname(myfonts.f_wb)) { 1208 fp = get_font_name_props(screen->display, fnts[fBold].fs, &bold); 1209 if (fp != 0) { 1210 myfonts.f_wb = widebold_font_name(fp); 1211 warn[fWBold] = fwAlways; 1212 derived = True; 1213 } 1214 } 1215 1216 if (check_fontname(myfonts.f_wb)) { 1217 1218 xtermOpenFont(xw, 1219 myfonts.f_wb, 1220 &fnts[fWBold], 1221 (screen->utf8_fonts 1222 ? warn[fWBold] 1223 : (fontWarningTypes) (xw->misc.fontWarnings + 1)), 1224 False); 1225 1226 if (derived 1227 && !compatibleWideCounts(fnts[fWide].fs, fnts[fWBold].fs)) { 1228 xtermCloseFont(xw, &fnts[fWBold]); 1229 } 1230 if (fnts[fWBold].fs == 0) { 1231 FREE_FNAME(f_wb); 1232 if (IsEmpty(myfonts.f_w)) { 1233 myfonts.f_wb = x_strdup(myfonts.f_b); 1234 warn[fWBold] = fwAlways; 1235 xtermCopyFontInfo(&fnts[fWBold], &fnts[fBold]); 1236 TRACE(("...cannot load wide-bold, use bold %s\n", 1237 NonNull(myfonts.f_b))); 1238 } else { 1239 myfonts.f_wb = x_strdup(myfonts.f_w); 1240 warn[fWBold] = fwAlways; 1241 xtermCopyFontInfo(&fnts[fWBold], &fnts[fWide]); 1242 TRACE(("...cannot load wide-bold, use wide %s\n", 1243 NonNull(myfonts.f_w))); 1244 } 1245 } else { 1246 TRACE(("...%s wide/bold %s\n", 1247 derived ? "derived" : "given", 1248 NonNull(myfonts.f_wb))); 1249 cache_menu_font_name(screen, fontnum, fWBold, myfonts.f_wb); 1250 } 1251 } else if (is_double_width_font(fnts[fBold].fs)) { 1252 xtermCopyFontInfo(&fnts[fWBold], &fnts[fBold]); 1253 warn[fWBold] = fwAlways; 1254 TRACE(("...bold font is double-width, use it %s\n", NonNull(myfonts.f_b))); 1255 } else { 1256 xtermCopyFontInfo(&fnts[fWBold], &fnts[fWide]); 1257 warn[fWBold] = fwAlways; 1258 TRACE(("...cannot load wide bold font, use wide %s\n", NonNull(myfonts.f_w))); 1259 } 1260 1261 free(bold); 1262 1263 if (EmptyFont(fnts[fWBold].fs)) 1264 goto bad; /* can't use a 0-sized font */ 1265 }); 1266 1267 /* 1268 * Most of the time this call to load the font will succeed, even if 1269 * there is no wide font : the X server doubles the width of the 1270 * normal font, or similar. 1271 * 1272 * But if it did fail for some reason, then nevermind. 1273 */ 1274 if (EmptyFont(fnts[fBold].fs)) 1275 goto bad; /* can't use a 0-sized font */ 1276 1277 if (!same_font_size(xw, fnts[fNorm].fs, fnts[fBold].fs) 1278 && (is_fixed_font(fnts[fNorm].fs) && is_fixed_font(fnts[fBold].fs))) { 1279 TRACE(("...ignoring mismatched normal/bold fonts\n")); 1280 xtermCloseFont(xw, &fnts[fBold]); 1281 xtermCopyFontInfo(&fnts[fBold], &fnts[fNorm]); 1282 } 1283 1284 if_OPT_WIDE_CHARS(screen, { 1285 if (fnts[fWide].fs != 0 1286 && fnts[fWBold].fs != 0 1287 && (!comparable_metrics(fnts[fWide].fs, fnts[fWBold].fs) 1288 || (!same_font_size(xw, fnts[fWide].fs, fnts[fWBold].fs) 1289 && is_fixed_font(fnts[fWide].fs) 1290 && is_fixed_font(fnts[fWBold].fs)))) { 1291 TRACE(("...ignoring mismatched normal/bold wide fonts\n")); 1292 xtermCloseFont(xw, &fnts[fWBold]); 1293 xtermCopyFontInfo(&fnts[fWBold], &fnts[fWide]); 1294 } 1295 }); 1296 1297 /* 1298 * Normal/bold fonts should be the same width. Also, the min/max 1299 * values should be the same. 1300 */ 1301 if (!is_fixed_font(fnts[fNorm].fs) 1302 || !is_fixed_font(fnts[fBold].fs) 1303 || fnts[fNorm].fs->max_bounds.width != fnts[fBold].fs->max_bounds.width) { 1304 TRACE(("Proportional font! normal %d/%d, bold %d/%d\n", 1305 fnts[fNorm].fs->min_bounds.width, 1306 fnts[fNorm].fs->max_bounds.width, 1307 fnts[fBold].fs->min_bounds.width, 1308 fnts[fBold].fs->max_bounds.width)); 1309 proportional = True; 1310 } 1311 1312 if_OPT_WIDE_CHARS(screen, { 1313 if (fnts[fWide].fs != 0 1314 && fnts[fWBold].fs != 0 1315 && (!is_fixed_font(fnts[fWide].fs) 1316 || !is_fixed_font(fnts[fWBold].fs) 1317 || fnts[fWide].fs->max_bounds.width != fnts[fWBold].fs->max_bounds.width)) { 1318 TRACE(("Proportional font! wide %d/%d, wide bold %d/%d\n", 1319 fnts[fWide].fs->min_bounds.width, 1320 fnts[fWide].fs->max_bounds.width, 1321 fnts[fWBold].fs->min_bounds.width, 1322 fnts[fWBold].fs->max_bounds.width)); 1323 proportional = True; 1324 } 1325 }); 1326 1327 /* TODO : enforce that the width of the wide font is 2* the width 1328 of the narrow font */ 1329 1330 /* 1331 * If we're switching fonts, free the old ones. Otherwise we'll leak 1332 * the memory that is associated with the old fonts. The 1333 * XLoadQueryFont call allocates a new XFontStruct. 1334 */ 1335 xtermCloseFonts(xw, screen->fnts); 1336#if OPT_WIDE_ATTRS 1337 xtermCloseFonts(xw, screen->ifnts); 1338 screen->ifnts_ok = False; 1339#endif 1340 1341 xtermCopyFontInfo(&(screen->fnts[fNorm]), &fnts[fNorm]); 1342 xtermCopyFontInfo(&(screen->fnts[fBold]), &fnts[fBold]); 1343#if OPT_WIDE_CHARS 1344 xtermCopyFontInfo(&(screen->fnts[fWide]), &fnts[fWide]); 1345 if (fnts[fWBold].fs == NULL) 1346 xtermCopyFontInfo(&fnts[fWBold], &fnts[fWide]); 1347 xtermCopyFontInfo(&(screen->fnts[fWBold]), &fnts[fWBold]); 1348#endif 1349 1350 xtermUpdateFontGCs(xw, screen->fnts); 1351 1352#if OPT_BOX_CHARS 1353 screen->allow_packing = proportional; 1354 setupPackedFonts(xw); 1355#endif 1356 screen->fnt_prop = (Boolean) (proportional && !(screen->force_packed)); 1357 screen->fnt_boxes = True; 1358 1359#if OPT_BOX_CHARS 1360 /* 1361 * xterm uses character positions 1-31 of a font for the line-drawing 1362 * characters. Check that they are all present. The null character 1363 * (0) is special, and is not used. 1364 */ 1365#if OPT_RENDERFONT 1366 if (UsingRenderFont(xw)) { 1367 /* 1368 * FIXME: we shouldn't even be here if we're using Xft. 1369 */ 1370 screen->fnt_boxes = False; 1371 TRACE(("assume Xft missing line-drawing chars\n")); 1372 } else 1373#endif 1374 { 1375 unsigned ch; 1376 1377#if OPT_TRACE 1378#define TRACE_MISS(index) show_font_misses(#index, &fnts[index]) 1379 TRACE_MISS(fNorm); 1380 TRACE_MISS(fBold); 1381#if OPT_WIDE_CHARS 1382 TRACE_MISS(fWide); 1383 TRACE_MISS(fWBold); 1384#endif 1385#endif 1386 1387 for (ch = 1; ch < 32; ch++) { 1388 unsigned n = ch; 1389#if OPT_WIDE_CHARS 1390 if (screen->utf8_mode || screen->unicode_font) { 1391 n = dec2ucs(ch); 1392 if (n == UCS_REPL) 1393 continue; 1394 } 1395#endif 1396 if (IsXtermMissingChar(screen, n, &fnts[fNorm])) { 1397 TRACE(("missing normal char #%d\n", n)); 1398 screen->fnt_boxes = False; 1399 break; 1400 } 1401 if (IsXtermMissingChar(screen, n, &fnts[fBold])) { 1402 TRACE(("missing bold char #%d\n", n)); 1403 screen->fnt_boxes = False; 1404 break; 1405 } 1406 } 1407 } 1408 TRACE(("Will %suse internal line-drawing characters\n", 1409 screen->fnt_boxes ? "not " : "")); 1410#endif 1411 1412 if (screen->always_bold_mode) { 1413 screen->enbolden = screen->bold_mode; 1414 } else { 1415 screen->enbolden = screen->bold_mode 1416 && ((fnts[fNorm].fs == fnts[fBold].fs) 1417 || same_font_name(normal, myfonts.f_b)); 1418 } 1419 TRACE(("Will %suse 1-pixel offset/overstrike to simulate bold\n", 1420 screen->enbolden ? "" : "not ")); 1421 1422 set_menu_font(False); 1423 screen->menu_font_number = fontnum; 1424 set_menu_font(True); 1425 if (tmpname) { /* if setting escape or sel */ 1426 if (screen->MenuFontName(fontnum)) 1427 FREE_STRING(screen->MenuFontName(fontnum)); 1428 screen->MenuFontName(fontnum) = tmpname; 1429 if (fontnum == fontMenu_fontescape) { 1430 update_font_escape(); 1431 } 1432#if OPT_SHIFT_FONTS 1433 screen->menu_font_sizes[fontnum] = FontSize(fnts[fNorm].fs); 1434#endif 1435 } 1436 if (normal) 1437 free(normal); 1438 set_cursor_gcs(xw); 1439 xtermUpdateFontInfo(xw, doresize); 1440 TRACE(("Success Cgs - xtermLoadFont\n")); 1441#if OPT_REPORT_FONTS 1442 reportVTFontInfo(xw, fontnum); 1443#endif 1444 FREE_FNAME(f_n); 1445 FREE_FNAME(f_b); 1446#if OPT_WIDE_CHARS 1447 FREE_FNAME(f_w); 1448 FREE_FNAME(f_wb); 1449#endif 1450 if (fnts[fNorm].fn == fnts[fBold].fn) { 1451 free(fnts[fNorm].fn); 1452 } else { 1453 free(fnts[fNorm].fn); 1454 free(fnts[fBold].fn); 1455 } 1456#if OPT_WIDE_CHARS 1457 free(fnts[fWide].fn); 1458 free(fnts[fWBold].fn); 1459#endif 1460 return 1; 1461 1462 bad: 1463 if (normal) 1464 free(normal); 1465 if (tmpname) 1466 free(tmpname); 1467 1468#if OPT_RENDERFONT 1469 if ((fontnum == fontMenu_fontsel) && (fontnum != screen->menu_font_number)) { 1470 int old_fontnum = screen->menu_font_number; 1471#if OPT_TOOLBAR 1472 SetItemSensitivity(fontMenuEntries[fontnum].widget, True); 1473#endif 1474 Bell(xw, XkbBI_MinorError, 0); 1475 myfonts.f_n = screen->MenuFontName(old_fontnum); 1476 return xtermLoadFont(xw, &myfonts, doresize, old_fontnum); 1477 } else if (x_strcasecmp(myfonts.f_n, DEFFONT)) { 1478 int code; 1479 1480 myfonts.f_n = DEFFONT; 1481 TRACE(("...recovering for TrueType fonts\n")); 1482 code = xtermLoadFont(xw, &myfonts, doresize, fontnum); 1483 if (code) { 1484 if (fontnum != fontMenu_fontsel) { 1485 SetItemSensitivity(fontMenuEntries[fontnum].widget, 1486 UsingRenderFont(xw)); 1487 } 1488 TRACE(("...recovered size %dx%d\n", 1489 FontHeight(screen), 1490 FontWidth(screen))); 1491 } 1492 return code; 1493 } 1494#endif 1495 1496 releaseWindowGCs(xw, win); 1497 1498 xtermCloseFonts(xw, fnts); 1499 TRACE(("Fail Cgs - xtermLoadFont\n")); 1500 return 0; 1501} 1502 1503#if OPT_WIDE_ATTRS 1504/* 1505 * (Attempt to) load matching italics for the current normal/bold/etc fonts. 1506 * If the attempt fails for a given style, use the non-italic font. 1507 */ 1508void 1509xtermLoadItalics(XtermWidget xw) 1510{ 1511 TScreen *screen = TScreenOf(xw); 1512 FontNameProperties *fp; 1513 char *name; 1514 int n; 1515 1516 if (!screen->ifnts_ok) { 1517 screen->ifnts_ok = True; 1518 for (n = 0; n < fMAX; ++n) { 1519 /* 1520 * FIXME - need to handle font-leaks 1521 */ 1522 screen->ifnts[n].fs = 0; 1523 if (screen->fnts[n].fs != 0 && 1524 (fp = get_font_name_props(screen->display, 1525 screen->fnts[n].fs, 1526 0)) != 0) { 1527 if ((name = italic_font_name(fp, fp->average_width)) != 0) { 1528 TRACE(("xtermLoadItalics #%d %s\n", n, name)); 1529 (void) xtermOpenFont(xw, 1530 name, 1531 &(screen->ifnts[n]), 1532 fwResource, 1533 False); 1534#if OPT_TRACE 1535 { 1536 XFontStruct *fs = 1537 screen->ifnts[n].fs; 1538 if (fs != 0) { 1539 TRACE(("...actual size %dx%d (ascent %d, descent %d)\n", 1540 fs->ascent + 1541 fs->descent, 1542 fs->max_bounds.width, 1543 fs->ascent, 1544 fs->descent)); 1545 } 1546 } 1547#endif 1548 free(name); 1549 } 1550 } 1551 } 1552 } 1553} 1554#endif 1555 1556#if OPT_LOAD_VTFONTS || OPT_WIDE_CHARS 1557/* 1558 * Collect font-names that we can modify with the load-vt-fonts() action. 1559 */ 1560#define MERGE_SUBFONT(src,dst,name) \ 1561 if (IsEmpty(dst.name)) { \ 1562 TRACE(("MERGE_SUBFONT " #dst "." #name " merge %s\n", NonNull(src.name))); \ 1563 dst.name = x_strdup(src.name); \ 1564 } else { \ 1565 TRACE(("MERGE_SUBFONT " #dst "." #name " found %s\n", NonNull(dst.name))); \ 1566 } 1567 1568#define INFER_SUBFONT(src,dst,name) \ 1569 if (IsEmpty(dst.name)) { \ 1570 TRACE(("INFER_SUBFONT " #dst "." #name " will infer\n")); \ 1571 dst.name = x_strdup(""); \ 1572 } else { \ 1573 TRACE(("INFER_SUBFONT " #dst "." #name " found %s\n", NonNull(dst.name))); \ 1574 } 1575 1576#define FREE_MENU_FONTS(dst) \ 1577 TRACE(("FREE_MENU_FONTS " #dst "\n")); \ 1578 for (n = fontMenu_default; n <= fontMenu_lastBuiltin; ++n) { \ 1579 for (m = 0; m < fMAX; ++m) { \ 1580 FREE_STRING(dst.menu_font_names[n][m]); \ 1581 dst.menu_font_names[n][m] = 0; \ 1582 } \ 1583 } 1584 1585#define COPY_MENU_FONTS(src,dst) \ 1586 TRACE(("COPY_MENU_FONTS " #src " to " #dst "\n")); \ 1587 for (n = fontMenu_default; n <= fontMenu_lastBuiltin; ++n) { \ 1588 for (m = 0; m < fMAX; ++m) { \ 1589 FREE_STRING(dst.menu_font_names[n][m]); \ 1590 dst.menu_font_names[n][m] = x_strdup(src.menu_font_names[n][m]); \ 1591 } \ 1592 TRACE((".. " #dst ".menu_fonts_names[%d] = %s\n", n, NonNull(dst.menu_font_names[n][fNorm]))); \ 1593 } 1594 1595#define COPY_DEFAULT_FONTS(target, source) \ 1596 xtermCopyVTFontNames(&target.default_font, &source.default_font) 1597 1598static void 1599xtermCopyVTFontNames(VTFontNames * target, VTFontNames * source) 1600{ 1601 target->f_n = x_strdup(source->f_n); 1602 target->f_b = x_strdup(source->f_b); 1603#if OPT_WIDE_CHARS 1604 target->f_w = x_strdup(source->f_w); 1605 target->f_wb = x_strdup(source->f_wb); 1606#endif 1607} 1608 1609void 1610xtermSaveVTFonts(XtermWidget xw) 1611{ 1612 TScreen *screen = TScreenOf(xw); 1613 Cardinal n, m; 1614 1615 if (!screen->savedVTFonts) { 1616 1617 screen->savedVTFonts = True; 1618 TRACE(("xtermSaveVTFonts saving original\n")); 1619 COPY_DEFAULT_FONTS(screen->cacheVTFonts, xw->misc); 1620 COPY_MENU_FONTS(xw->screen, screen->cacheVTFonts); 1621 } 1622} 1623 1624#define SAME_STRING(x,y) ((x) == (y) || ((x) && (y) && !strcmp(x, y))) 1625#define SAME_MEMBER(n) SAME_STRING(a->n, b->n) 1626 1627static Boolean 1628sameSubResources(SubResourceRec * a, SubResourceRec * b) 1629{ 1630 Boolean result = True; 1631 int n; 1632 1633 if (!SAME_MEMBER(default_font.f_n) 1634 || !SAME_MEMBER(default_font.f_b) 1635#if OPT_WIDE_CHARS 1636 || !SAME_MEMBER(default_font.f_w) 1637 || !SAME_MEMBER(default_font.f_wb) 1638#endif 1639 ) { 1640 TRACE(("sameSubResources: default_font differs\n")); 1641 result = False; 1642 } else { 1643 for (n = 0; n < NMENUFONTS; ++n) { 1644 if (!SAME_MEMBER(menu_font_names[n][fNorm])) { 1645 TRACE(("sameSubResources: menu_font_names[%d] differs\n", n)); 1646 result = False; 1647 break; 1648 } 1649 } 1650 } 1651 1652 return result; 1653} 1654 1655/* 1656 * Load the "VT" font names from the given subresource name/class. These 1657 * correspond to the VT100 resources. 1658 */ 1659static Bool 1660xtermLoadVTFonts(XtermWidget xw, String myName, String myClass) 1661{ 1662 SubResourceRec subresourceRec; 1663 SubResourceRec referenceRec; 1664 1665 /* 1666 * These are duplicates of the VT100 font resources, but with a special 1667 * application/classname passed in to distinguish them. 1668 */ 1669 static XtResource font_resources[] = 1670 { 1671 Sres(XtNfont, XtCFont, default_font.f_n, DEFFONT), 1672 Sres(XtNboldFont, XtCBoldFont, default_font.f_b, DEFBOLDFONT), 1673#if OPT_WIDE_CHARS 1674 Sres(XtNwideFont, XtCWideFont, default_font.f_w, DEFWIDEFONT), 1675 Sres(XtNwideBoldFont, XtCWideBoldFont, default_font.f_wb, DEFWIDEBOLDFONT), 1676#endif 1677 Sres(XtNfont1, XtCFont1, MenuFontName(fontMenu_font1), NULL), 1678 Sres(XtNfont2, XtCFont2, MenuFontName(fontMenu_font2), NULL), 1679 Sres(XtNfont3, XtCFont3, MenuFontName(fontMenu_font3), NULL), 1680 Sres(XtNfont4, XtCFont4, MenuFontName(fontMenu_font4), NULL), 1681 Sres(XtNfont5, XtCFont5, MenuFontName(fontMenu_font5), NULL), 1682 Sres(XtNfont6, XtCFont6, MenuFontName(fontMenu_font6), NULL), 1683 }; 1684 Cardinal n, m; 1685 Bool status = True; 1686 TScreen *screen = TScreenOf(xw); 1687 1688 TRACE(("called xtermLoadVTFonts(name=%s, class=%s)\n", 1689 NonNull(myName), NonNull(myClass))); 1690 1691 xtermSaveVTFonts(xw); 1692 1693 if (IsEmpty(myName)) { 1694 TRACE(("xtermLoadVTFonts restoring original\n")); 1695 COPY_DEFAULT_FONTS(xw->misc, screen->cacheVTFonts); 1696 FREE_MENU_FONTS(xw->screen); 1697 COPY_MENU_FONTS(screen->cacheVTFonts, xw->screen); 1698 } else { 1699 TRACE(("xtermLoadVTFonts(%s, %s)\n", myName, myClass)); 1700 1701 memset(&referenceRec, 0, sizeof(referenceRec)); 1702 memset(&subresourceRec, 0, sizeof(subresourceRec)); 1703 XtGetSubresources((Widget) xw, (XtPointer) &subresourceRec, 1704 myName, myClass, 1705 font_resources, 1706 (Cardinal) XtNumber(font_resources), 1707 NULL, (Cardinal) 0); 1708 1709 /* 1710 * XtGetSubresources returns no status, so we compare the returned 1711 * data against a zero'd struct to see if any data is returned. 1712 */ 1713 if (memcmp(&referenceRec, &subresourceRec, sizeof(referenceRec)) 1714 && !sameSubResources(&(screen->cacheVTFonts), &subresourceRec)) { 1715 1716 screen->mergedVTFonts = True; 1717 1718 /* 1719 * To make it simple, reallocate the strings returned by 1720 * XtGetSubresources. We can free our own strings, but not theirs. 1721 */ 1722 ALLOC_STRING(subresourceRec.default_font.f_n); 1723 ALLOC_STRING(subresourceRec.default_font.f_b); 1724#if OPT_WIDE_CHARS 1725 ALLOC_STRING(subresourceRec.default_font.f_w); 1726 ALLOC_STRING(subresourceRec.default_font.f_wb); 1727#endif 1728 for (n = fontMenu_font1; n <= fontMenu_lastBuiltin; ++n) { 1729 ALLOC_STRING(subresourceRec.MenuFontName(n)); 1730 } 1731 1732 /* 1733 * If a particular resource value was not found, use the original. 1734 */ 1735 MERGE_SUBFONT(xw->misc, subresourceRec, default_font.f_n); 1736 INFER_SUBFONT(xw->misc, subresourceRec, default_font.f_b); 1737#if OPT_WIDE_CHARS 1738 INFER_SUBFONT(xw->misc, subresourceRec, default_font.f_w); 1739 INFER_SUBFONT(xw->misc, subresourceRec, default_font.f_wb); 1740#endif 1741 for (n = fontMenu_font1; n <= fontMenu_lastBuiltin; ++n) { 1742 MERGE_SUBFONT(xw->screen, subresourceRec, MenuFontName(n)); 1743 } 1744 1745 /* 1746 * Finally, copy the subresource data to the widget. 1747 */ 1748 COPY_DEFAULT_FONTS(xw->misc, subresourceRec); 1749 FREE_MENU_FONTS(xw->screen); 1750 COPY_MENU_FONTS(subresourceRec, xw->screen); 1751 1752 FREE_STRING(screen->MenuFontName(fontMenu_default)); 1753 FREE_STRING(screen->menu_font_names[0][fBold]); 1754 screen->MenuFontName(fontMenu_default) = x_strdup(xw->misc.default_font.f_n); 1755 screen->menu_font_names[0][fBold] = x_strdup(xw->misc.default_font.f_b); 1756#if OPT_WIDE_CHARS 1757 FREE_STRING(screen->menu_font_names[0][fWide]); 1758 FREE_STRING(screen->menu_font_names[0][fWBold]); 1759 screen->menu_font_names[0][fWide] = x_strdup(xw->misc.default_font.f_w); 1760 screen->menu_font_names[0][fWBold] = x_strdup(xw->misc.default_font.f_wb); 1761#endif 1762 /* 1763 * And remove our copies of strings. 1764 */ 1765 FREE_STRING(subresourceRec.default_font.f_n); 1766 FREE_STRING(subresourceRec.default_font.f_b); 1767#if OPT_WIDE_CHARS 1768 FREE_STRING(subresourceRec.default_font.f_w); 1769 FREE_STRING(subresourceRec.default_font.f_wb); 1770#endif 1771 for (n = fontMenu_font1; n <= fontMenu_lastBuiltin; ++n) { 1772 FREE_STRING(subresourceRec.MenuFontName(n)); 1773 } 1774 } else { 1775 TRACE(("...no resources found\n")); 1776 status = False; 1777 } 1778 } 1779 return status; 1780} 1781 1782#if OPT_WIDE_CHARS 1783static Bool 1784isWideFont(XFontStruct *fp, const char *tag, Bool nullOk) 1785{ 1786 Bool result = False; 1787 1788 (void) tag; 1789 if (okFont(fp)) { 1790 unsigned count = countGlyphs(fp); 1791 TRACE(("isWideFont(%s) found %d cells\n", tag, count)); 1792 result = (count > 256) ? True : False; 1793 } else { 1794 result = nullOk; 1795 } 1796 return result; 1797} 1798 1799/* 1800 * If the current fonts are not wide, load the UTF8 fonts. 1801 * 1802 * Called during initialization (for wide-character mode), the fonts have not 1803 * been setup, so we pass nullOk=True to isWideFont(). 1804 * 1805 * Called after initialization, e.g., in response to the UTF-8 menu entry 1806 * (starting from narrow character mode), it checks if the fonts are not wide. 1807 */ 1808Bool 1809xtermLoadWideFonts(XtermWidget xw, Bool nullOk) 1810{ 1811 TScreen *screen = TScreenOf(xw); 1812 Bool result; 1813 1814 if (EmptyFont(screen->fnts[fWide].fs)) { 1815 result = (isWideFont(screen->fnts[fNorm].fs, "normal", nullOk) 1816 && isWideFont(screen->fnts[fBold].fs, "bold", nullOk)); 1817 } else { 1818 result = (isWideFont(screen->fnts[fWide].fs, "wide", nullOk) 1819 && isWideFont(screen->fnts[fWBold].fs, "wide-bold", nullOk)); 1820 if (result && !screen->utf8_latin1) { 1821 result = (isWideFont(screen->fnts[fNorm].fs, "normal", nullOk) 1822 && isWideFont(screen->fnts[fBold].fs, "bold", nullOk)); 1823 } 1824 } 1825 if (!result) { 1826 TRACE(("current fonts are not all wide%s\n", nullOk ? " nullOk" : "")); 1827 result = xtermLoadVTFonts(xw, XtNutf8Fonts, XtCUtf8Fonts); 1828 } 1829 TRACE(("xtermLoadWideFonts:%d\n", result)); 1830 return result; 1831} 1832#endif /* OPT_WIDE_CHARS */ 1833 1834/* 1835 * Restore the default fonts, i.e., if we had switched to wide-fonts. 1836 */ 1837Bool 1838xtermLoadDefaultFonts(XtermWidget xw) 1839{ 1840 Bool result; 1841 result = xtermLoadVTFonts(xw, NULL, NULL); 1842 TRACE(("xtermLoadDefaultFonts:%d\n", result)); 1843 return result; 1844} 1845#endif /* OPT_LOAD_VTFONTS || OPT_WIDE_CHARS */ 1846 1847#if OPT_LOAD_VTFONTS 1848void 1849HandleLoadVTFonts(Widget w, 1850 XEvent *event GCC_UNUSED, 1851 String *params GCC_UNUSED, 1852 Cardinal *param_count GCC_UNUSED) 1853{ 1854 static char empty[] = ""; /* appease strict compilers */ 1855 1856 XtermWidget xw; 1857 1858 if ((xw = getXtermWidget(w)) != 0) { 1859 TScreen *screen = TScreenOf(xw); 1860 char name_buf[80]; 1861 char class_buf[80]; 1862 String name = (String) ((*param_count > 0) ? params[0] : empty); 1863 char *myName = MyStackAlloc(strlen(name) + 1, name_buf); 1864 1865 TRACE(("HandleLoadVTFonts(%d)\n", *param_count)); 1866 if (myName != 0) { 1867 String convert = (String) ((*param_count > 1) ? params[1] : myName); 1868 char *myClass = MyStackAlloc(strlen(convert) + 1, class_buf); 1869 int n; 1870 1871 strcpy(myName, name); 1872 if (myClass != 0) { 1873 strcpy(myClass, convert); 1874 if (*param_count == 1) 1875 myClass[0] = x_toupper(myClass[0]); 1876 1877 if (xtermLoadVTFonts(xw, myName, myClass)) { 1878 /* 1879 * When switching fonts, try to preserve the font-menu 1880 * selection, since it is less surprising to do that (if 1881 * the font-switching can be undone) than to switch to 1882 * "Default". 1883 */ 1884 int font_number = screen->menu_font_number; 1885 if (font_number > fontMenu_lastBuiltin) 1886 font_number = fontMenu_lastBuiltin; 1887 for (n = 0; n < NMENUFONTS; ++n) { 1888 screen->menu_font_sizes[n] = 0; 1889 } 1890 SetVTFont(xw, font_number, True, 1891 ((font_number == fontMenu_default) 1892 ? &(xw->misc.default_font) 1893 : NULL)); 1894 } 1895 MyStackFree(myClass, class_buf); 1896 } 1897 MyStackFree(myName, name_buf); 1898 } 1899 } 1900} 1901#endif /* OPT_LOAD_VTFONTS */ 1902 1903/* 1904 * Set the limits for the box that outlines the cursor. 1905 */ 1906void 1907xtermSetCursorBox(TScreen *screen) 1908{ 1909 static XPoint VTbox[NBOX]; 1910 XPoint *vp; 1911 int fw = FontWidth(screen) - 1; 1912 int fh = FontHeight(screen) - 1; 1913 int ww = isCursorBar(screen) ? 1 : fw; 1914 int hh = isCursorUnderline(screen) ? 1 : fh; 1915 1916 vp = &VTbox[1]; 1917 (vp++)->x = (short) ww; 1918 (vp++)->y = (short) hh; 1919 (vp++)->x = (short) -ww; 1920 vp->y = (short) -hh; 1921 1922 screen->box = VTbox; 1923} 1924 1925#define CACHE_XFT(dst,src) if (src != 0) {\ 1926 checkXft(xw, &(dst[fontnum]), src);\ 1927 TRACE(("Xft metrics %s[%d] = %d (%d,%d)%s advance %d, actual %d%s\n",\ 1928 #dst,\ 1929 fontnum,\ 1930 src->height,\ 1931 src->ascent,\ 1932 src->descent,\ 1933 ((src->ascent + src->descent) > src->height ? "*" : ""),\ 1934 src->max_advance_width,\ 1935 dst[fontnum].map.min_width,\ 1936 dst[fontnum].map.mixed ? " mixed" : ""));\ 1937 } 1938 1939#if OPT_RENDERFONT 1940 1941static FcChar32 1942xtermXftFirstChar(XftFont *xft) 1943{ 1944 FcChar32 map[FC_CHARSET_MAP_SIZE]; 1945 FcChar32 next; 1946 FcChar32 first; 1947 int i; 1948 1949 first = FcCharSetFirstPage(xft->charset, map, &next); 1950 for (i = 0; i < FC_CHARSET_MAP_SIZE; i++) { 1951 if (map[i]) { 1952 FcChar32 bits = map[i]; 1953 first += (FcChar32) i *32; 1954 while (!(bits & 0x1)) { 1955 bits >>= 1; 1956 first++; 1957 } 1958 break; 1959 } 1960 } 1961 return first; 1962} 1963 1964static FcChar32 1965xtermXftLastChar(XftFont *xft) 1966{ 1967 FcChar32 this, last, next; 1968 FcChar32 map[FC_CHARSET_MAP_SIZE]; 1969 int i; 1970 last = FcCharSetFirstPage(xft->charset, map, &next); 1971 while ((this = FcCharSetNextPage(xft->charset, map, &next)) != FC_CHARSET_DONE) 1972 last = this; 1973 last &= (FcChar32) ~ 0xff; 1974 for (i = FC_CHARSET_MAP_SIZE - 1; i >= 0; i--) { 1975 if (map[i]) { 1976 FcChar32 bits = map[i]; 1977 last += (FcChar32) i *32 + 31; 1978 while (!(bits & 0x80000000)) { 1979 last--; 1980 bits <<= 1; 1981 } 1982 break; 1983 } 1984 } 1985 return (long) last; 1986} 1987 1988#if OPT_TRACE > 1 1989static void 1990dumpXft(XtermWidget xw, XTermXftFonts *data) 1991{ 1992 XftFont *xft = data->font; 1993 TScreen *screen = TScreenOf(xw); 1994 VTwin *win = WhichVWin(screen); 1995 1996 FcChar32 c; 1997 FcChar32 first = xtermXftFirstChar(xft); 1998 FcChar32 last = xtermXftLastChar(xft); 1999 unsigned count = 0; 2000 unsigned outside = 0; 2001 2002 TRACE(("dumpXft {{\n")); 2003 TRACE((" data range %#6x..%#6x\n", first, last)); 2004 for (c = first; c <= last; ++c) { 2005 if (FcCharSetHasChar(xft->charset, c)) { 2006 int width = my_wcwidth((int) c); 2007 XGlyphInfo extents; 2008 2009 XftTextExtents32(XtDisplay(xw), xft, &c, 1, &extents); 2010 TRACE(("%#6x %2d %.1f\n", c, width, 2011 ((double) extents.width) / win->f_width)); 2012 if (extents.width > win->f_width) 2013 ++outside; 2014 ++count; 2015 } 2016 } 2017 TRACE(("}} %u total, %u outside\n", count, outside)); 2018} 2019#define DUMP_XFT(xw, data) dumpXft(xw, data) 2020#else 2021#define DUMP_XFT(xw, data) /* nothing */ 2022#endif 2023 2024static void 2025checkXft(XtermWidget xw, XTermXftFonts *data, XftFont *xft) 2026{ 2027 FcChar32 c; 2028 Dimension width = 0; 2029 2030 data->font = xft; 2031 data->map.min_width = 0; 2032 data->map.max_width = (Dimension) xft->max_advance_width; 2033 2034 /* 2035 * For each ASCII or ISO-8859-1 printable code, ask what its width is. 2036 * Given the maximum width for those, we have a reasonable estimate of 2037 * the single-column width. 2038 * 2039 * Ignore control characters - their extent information is misleading. 2040 */ 2041 for (c = 32; c < 256; ++c) { 2042 if (c >= 127 && c <= 159) 2043 continue; 2044 if (FcCharSetHasChar(xft->charset, c)) { 2045 XGlyphInfo extents; 2046 2047 XftTextExtents32(XtDisplay(xw), xft, &c, 1, &extents); 2048 if (width < extents.width && extents.width <= data->map.max_width) { 2049 width = extents.width; 2050 } 2051 } 2052 } 2053 data->map.min_width = width; 2054 data->map.mixed = (data->map.max_width >= (data->map.min_width + 1)); 2055} 2056 2057static void 2058reportXftFonts(XtermWidget xw, 2059 XftFont *fp, 2060 const char *name, 2061 const char *tag, 2062 XftPattern *match) 2063{ 2064 if (resource.reportFonts) { 2065 char buffer[1024]; 2066 FcChar32 first_char = xtermXftFirstChar(fp); 2067 FcChar32 last_char = xtermXftLastChar(fp); 2068 FcChar32 ch; 2069 unsigned missing = 0; 2070 2071 printf("Loaded XftFonts(%s[%s])\n", name, tag); 2072 2073 for (ch = first_char; ch <= last_char; ++ch) { 2074 if (xtermXftMissing(xw, fp, ch)) { 2075 ++missing; 2076 } 2077 } 2078 printf("\t\tfirst char: %u\n", first_char); 2079 printf("\t\tlast char: %u\n", last_char); 2080 printf("\t\tmissing-chars: %u\n", missing); 2081 printf("\t\tpresent-chars: %u\n", (last_char - first_char) + 1 - missing); 2082 2083 if (XftNameUnparse(match, buffer, (int) sizeof(buffer))) { 2084 char *target; 2085 char *source = buffer; 2086 while ((target = strtok(source, ":")) != 0) { 2087 printf("\t%s\n", target); 2088 source = 0; 2089 } 2090 } 2091 } 2092} 2093 2094static XftFont * 2095xtermOpenXft(XtermWidget xw, const char *name, XftPattern *pat, const char *tag) 2096{ 2097 TScreen *screen = TScreenOf(xw); 2098 Display *dpy = screen->display; 2099 XftPattern *match; 2100 XftResult status; 2101 XftFont *result = 0; 2102 2103 if (pat != 0) { 2104 match = XftFontMatch(dpy, DefaultScreen(dpy), pat, &status); 2105 if (match != 0) { 2106 result = XftFontOpenPattern(dpy, match); 2107 if (result != 0) { 2108 TRACE(("...matched %s font\n", tag)); 2109 reportXftFonts(xw, result, name, tag, match); 2110 } else { 2111 TRACE(("...could did not open %s font\n", tag)); 2112 XftPatternDestroy(match); 2113 if (xw->misc.fontWarnings >= fwAlways) { 2114 TRACE(("OOPS cannot open %s font \"%s\"\n", tag, name)); 2115 xtermWarning("cannot open %s font \"%s\"\n", tag, name); 2116 } 2117 } 2118 } else { 2119 TRACE(("...did not match %s font\n", tag)); 2120 if (xw->misc.fontWarnings >= fwResource) { 2121 TRACE(("OOPS: cannot match %s font \"%s\"\n", tag, name)); 2122 xtermWarning("cannot match %s font \"%s\"\n", tag, name); 2123 } 2124 } 2125 } 2126 return result; 2127} 2128#endif 2129 2130#if OPT_RENDERFONT 2131#if OPT_SHIFT_FONTS 2132/* 2133 * Don't make a dependency on the math library for a single function. 2134 * (Newton Raphson). 2135 */ 2136static double 2137dimSquareRoot(double value) 2138{ 2139 double result = 0.0; 2140 if (value > 0.0) { 2141 int n; 2142 double older = value; 2143 for (n = 0; n < 10; ++n) { 2144 double delta = (older * older - value) / (2.0 * older); 2145 double newer = older - delta; 2146 older = newer; 2147 result = newer; 2148 if (delta > -0.001 && delta < 0.001) 2149 break; 2150 } 2151 } 2152 return result; 2153} 2154#endif 2155 2156/* 2157 * Given the Xft font metrics, determine the actual font size. This is used 2158 * for each font to ensure that normal, bold and italic fonts follow the same 2159 * rule. 2160 */ 2161static void 2162setRenderFontsize(TScreen *screen, VTwin *win, XftFont *font, const char *tag) 2163{ 2164 if (font != 0) { 2165 int width, height, ascent, descent; 2166 2167 (void) screen; 2168 2169 width = font->max_advance_width; 2170 height = font->height; 2171 ascent = font->ascent; 2172 descent = font->descent; 2173 if (height < ascent + descent) { 2174 TRACE(("...increase height from %d\n", height)); 2175 height = ascent + descent; 2176 } 2177 if (is_double_width_font_xft(screen->display, font)) { 2178 TRACE(("...reduced width from %d\n", width)); 2179 width >>= 1; 2180 } 2181 if (tag == 0) { 2182 SetFontWidth(screen, win, width); 2183 SetFontHeight(screen, win, height); 2184 win->f_ascent = ascent; 2185 win->f_descent = descent; 2186 TRACE(("setRenderFontsize result %dx%d (%d+%d)\n", 2187 width, height, ascent, descent)); 2188 } else if (win->f_width < width || 2189 win->f_height < height || 2190 win->f_ascent < ascent || 2191 win->f_descent < descent) { 2192 TRACE(("setRenderFontsize %s changed %dx%d (%d+%d) to %dx%d (%d+%d)\n", 2193 tag, 2194 win->f_width, win->f_height, win->f_ascent, win->f_descent, 2195 width, height, ascent, descent)); 2196 2197 SetFontWidth(screen, win, width); 2198 SetFontHeight(screen, win, height); 2199 win->f_ascent = ascent; 2200 win->f_descent = descent; 2201 } else { 2202 TRACE(("setRenderFontsize %s unchanged\n", tag)); 2203 } 2204 } 2205} 2206#endif 2207 2208static void 2209checkFontInfo(int value, const char *tag) 2210{ 2211 if (value == 0) { 2212 xtermWarning("Selected font has no non-zero %s for ISO-8859-1 encoding\n", tag); 2213 exit(1); 2214 } 2215} 2216 2217#if OPT_RENDERFONT 2218void 2219xtermCloseXft(TScreen *screen, XTermXftFonts *pub) 2220{ 2221 if (pub->font != 0) { 2222 XftFontClose(screen->display, pub->font); 2223 pub->font = 0; 2224 } 2225} 2226 2227/* 2228 * Get the faceName/faceDoublesize resource setting. Strip off "xft:", which 2229 * is not recognized by XftNameParse(). 2230 */ 2231String 2232getFaceName(XtermWidget xw, Bool wideName GCC_UNUSED) 2233{ 2234#if OPT_RENDERWIDE 2235 String result = (wideName 2236 ? xw->misc.face_wide_name 2237 : xw->misc.face_name); 2238#else 2239 String result = xw->misc.face_name; 2240#endif 2241 if (!IsEmpty(result) && !strncmp(result, "xft:", (size_t) 4)) 2242 result += 4; 2243 return x_nonempty(result); 2244} 2245 2246/* 2247 * If we change the faceName, we'll have to re-acquire all of the fonts that 2248 * are derived from it. 2249 */ 2250void 2251setFaceName(XtermWidget xw, const char *value) 2252{ 2253 TScreen *screen = TScreenOf(xw); 2254 int n; 2255 Boolean changed = (Boolean) ((xw->misc.face_name == 0) 2256 || strcmp(xw->misc.face_name, value)); 2257 2258 if (changed) { 2259 xw->misc.face_name = x_strdup(value); 2260 for (n = 0; n < NMENUFONTS; ++n) { 2261 xw->misc.face_size[n] = -1.0; 2262 xtermCloseXft(screen, &(screen->renderFontNorm[n])); 2263 xtermCloseXft(screen, &(screen->renderFontBold[n])); 2264 xtermCloseXft(screen, &(screen->renderFontBold[n])); 2265#if OPT_RENDERWIDE 2266 xtermCloseXft(screen, &(screen->renderWideNorm[n])); 2267 xtermCloseXft(screen, &(screen->renderWideBold[n])); 2268 xtermCloseXft(screen, &(screen->renderWideItal[n])); 2269#endif 2270 } 2271 } 2272} 2273#endif 2274 2275/* 2276 * Compute useful values for the font/window sizes 2277 */ 2278void 2279xtermComputeFontInfo(XtermWidget xw, 2280 VTwin *win, 2281 XFontStruct *font, 2282 int sbwidth) 2283{ 2284 TScreen *screen = TScreenOf(xw); 2285 2286 int i, j, width, height; 2287#if OPT_RENDERFONT 2288 int fontnum = screen->menu_font_number; 2289#endif 2290 2291#if OPT_RENDERFONT 2292 /* 2293 * xterm contains a lot of references to fonts, assuming they are fixed 2294 * size. This chunk of code overrides the actual font-selection (see 2295 * drawXtermText()), if the user has selected render-font. All of the 2296 * font-loading for fixed-fonts still goes on whether or not this chunk 2297 * overrides it. 2298 */ 2299 if (UsingRenderFont(xw) && fontnum >= 0) { 2300 String face_name = getFaceName(xw, False); 2301 XftFont *norm = screen->renderFontNorm[fontnum].font; 2302 XftFont *bold = screen->renderFontBold[fontnum].font; 2303 XftFont *ital = screen->renderFontItal[fontnum].font; 2304#if OPT_RENDERWIDE 2305 XftFont *wnorm = screen->renderWideNorm[fontnum].font; 2306 XftFont *wbold = screen->renderWideBold[fontnum].font; 2307 XftFont *wital = screen->renderWideItal[fontnum].font; 2308#endif 2309 2310 if (norm == 0 && face_name) { 2311 XftPattern *pat; 2312 double face_size; 2313 2314 TRACE(("xtermComputeFontInfo font %d: norm(face %s, size %.1f)\n", 2315 fontnum, face_name, 2316 xw->misc.face_size[fontnum])); 2317 2318 fillInFaceSize(xw, fontnum); 2319 face_size = xw->misc.face_size[fontnum]; 2320 2321 /* 2322 * By observation (there is no documentation), XftPatternBuild is 2323 * cumulative. Build the bold- and italic-patterns on top of the 2324 * normal pattern. 2325 */ 2326#define NormXftPattern \ 2327 XFT_FAMILY, XftTypeString, "mono", \ 2328 XFT_SIZE, XftTypeDouble, face_size, \ 2329 XFT_SPACING, XftTypeInteger, XFT_MONO 2330 2331#define BoldXftPattern(norm) \ 2332 XFT_WEIGHT, XftTypeInteger, XFT_WEIGHT_BOLD, \ 2333 XFT_CHAR_WIDTH, XftTypeInteger, norm->max_advance_width 2334 2335#define ItalXftPattern(norm) \ 2336 XFT_SLANT, XftTypeInteger, XFT_SLANT_ITALIC, \ 2337 XFT_CHAR_WIDTH, XftTypeInteger, norm->max_advance_width 2338 2339#if OPT_WIDE_ATTRS 2340#define HAVE_ITALICS 1 2341#define FIND_ITALICS ((pat = XftNameParse(face_name)) != 0) 2342#elif OPT_ISO_COLORS 2343#define HAVE_ITALICS 1 2344#define FIND_ITALICS (screen->italicULMode && (pat = XftNameParse(face_name)) != 0) 2345#else 2346#define HAVE_ITALICS 0 2347#endif 2348 2349 if ((pat = XftNameParse(face_name)) != 0) { 2350#define OPEN_XFT(tag) xtermOpenXft(xw, face_name, pat, tag) 2351 XftPatternBuild(pat, 2352 NormXftPattern, 2353 (void *) 0); 2354 norm = OPEN_XFT("normal"); 2355 2356 if (norm != 0) { 2357 XftPatternBuild(pat, 2358 BoldXftPattern(norm), 2359 (void *) 0); 2360 bold = OPEN_XFT("bold"); 2361 2362#if HAVE_ITALICS 2363 if (FIND_ITALICS) { 2364 XftPatternBuild(pat, 2365 NormXftPattern, 2366 ItalXftPattern(norm), 2367 (void *) 0); 2368 ital = OPEN_XFT("italic"); 2369 } 2370#endif 2371#undef OPEN_XFT 2372 2373 /* 2374 * FIXME: just assume that the corresponding font has no 2375 * graphics characters. 2376 */ 2377 if (screen->fnt_boxes) { 2378 screen->fnt_boxes = False; 2379 TRACE(("Xft opened - will %suse internal line-drawing characters\n", 2380 screen->fnt_boxes ? "not " : "")); 2381 } 2382 } 2383 2384 XftPatternDestroy(pat); 2385 } 2386 2387 CACHE_XFT(screen->renderFontNorm, norm); 2388 CACHE_XFT(screen->renderFontBold, bold); 2389 CACHE_XFT(screen->renderFontItal, ital); 2390 2391 /* 2392 * See xtermXftDrawString(). 2393 */ 2394#if OPT_RENDERWIDE 2395 if (norm != 0 && screen->wide_chars) { 2396 int char_width = norm->max_advance_width * 2; 2397#ifdef FC_ASPECT 2398 double aspect = ((xw->misc.face_wide_name 2399 || screen->renderFontNorm[fontnum].map.mixed) 2400 ? 1.0 2401 : 2.0); 2402#endif 2403 2404 face_name = getFaceName(xw, True); 2405 TRACE(("xtermComputeFontInfo wide(face %s, char_width %d)\n", 2406 NonNull(face_name), 2407 char_width)); 2408 2409#define WideXftPattern \ 2410 XFT_FAMILY, XftTypeString, "mono", \ 2411 XFT_SIZE, XftTypeDouble, face_size, \ 2412 XFT_SPACING, XftTypeInteger, XFT_MONO 2413 2414 if (face_name && (pat = XftNameParse(face_name)) != 0) { 2415#define OPEN_XFT(tag) xtermOpenXft(xw, face_name, pat, tag) 2416 XftPatternBuild(pat, 2417 WideXftPattern, 2418 XFT_CHAR_WIDTH, XftTypeInteger, char_width, 2419#ifdef FC_ASPECT 2420 FC_ASPECT, XftTypeDouble, aspect, 2421#endif 2422 (void *) 0); 2423 wnorm = OPEN_XFT("wide"); 2424 2425 if (wnorm != 0) { 2426 XftPatternBuild(pat, 2427 WideXftPattern, 2428 BoldXftPattern(wnorm), 2429 (void *) 0); 2430 wbold = OPEN_XFT("wide-bold"); 2431 2432#if HAVE_ITALICS 2433 if (FIND_ITALICS) { 2434 XftPatternBuild(pat, 2435 WideXftPattern, 2436 ItalXftPattern(wnorm), 2437 (void *) 0); 2438 wital = OPEN_XFT("wide-italic"); 2439 } 2440#endif 2441#undef OPEN_XFT 2442 } 2443 XftPatternDestroy(pat); 2444 } 2445 2446 CACHE_XFT(screen->renderWideNorm, wnorm); 2447 CACHE_XFT(screen->renderWideBold, wbold); 2448 CACHE_XFT(screen->renderWideItal, wital); 2449 } 2450#endif /* OPT_RENDERWIDE */ 2451 } 2452 if (norm == 0) { 2453 TRACE(("...no TrueType font found for number %d, disable menu entry\n", fontnum)); 2454 xw->work.render_font = False; 2455 update_font_renderfont(); 2456 /* now we will fall through into the bitmap fonts */ 2457 } else { 2458 setRenderFontsize(screen, win, norm, NULL); 2459 setRenderFontsize(screen, win, bold, "bold"); 2460 setRenderFontsize(screen, win, ital, "ital"); 2461#if OPT_BOX_CHARS 2462 setupPackedFonts(xw); 2463 2464 if (screen->force_packed) { 2465 XTermXftFonts *use = &(screen->renderFontNorm[fontnum]); 2466 SetFontHeight(screen, win, use->font->ascent + use->font->descent); 2467 SetFontWidth(screen, win, use->map.min_width); 2468 TRACE(("...packed TrueType font %dx%d vs %d\n", 2469 win->f_height, 2470 win->f_width, 2471 use->map.max_width)); 2472 } 2473#endif 2474 DUMP_XFT(xw, &(screen->renderFontNorm[fontnum])); 2475 } 2476 } 2477 /* 2478 * Are we handling a bitmap font? 2479 */ 2480 else 2481#endif /* OPT_RENDERFONT */ 2482 { 2483 if (is_double_width_font(font) && !(screen->fnt_prop)) { 2484 SetFontWidth(screen, win, font->min_bounds.width); 2485 } else { 2486 SetFontWidth(screen, win, font->max_bounds.width); 2487 } 2488 SetFontHeight(screen, win, font->ascent + font->descent); 2489 win->f_ascent = font->ascent; 2490 win->f_descent = font->descent; 2491 } 2492 i = 2 * screen->border + sbwidth; 2493 j = 2 * screen->border; 2494 width = MaxCols(screen) * win->f_width + i; 2495 height = MaxRows(screen) * win->f_height + j; 2496 win->fullwidth = (Dimension) width; 2497 win->fullheight = (Dimension) height; 2498 win->width = width - i; 2499 win->height = height - j; 2500 2501 TRACE(("xtermComputeFontInfo window %dx%d (full %dx%d), fontsize %dx%d (asc %d, dsc %d)\n", 2502 win->height, 2503 win->width, 2504 win->fullheight, 2505 win->fullwidth, 2506 win->f_height, 2507 win->f_width, 2508 win->f_ascent, 2509 win->f_descent)); 2510 2511 checkFontInfo(win->f_height, "height"); 2512 checkFontInfo(win->f_width, "width"); 2513} 2514 2515/* save this information as a side-effect for double-sized characters */ 2516void 2517xtermSaveFontInfo(TScreen *screen, XFontStruct *font) 2518{ 2519 screen->fnt_wide = (Dimension) (font->max_bounds.width); 2520 screen->fnt_high = (Dimension) (font->ascent + font->descent); 2521 TRACE(("xtermSaveFontInfo %dx%d\n", screen->fnt_high, screen->fnt_wide)); 2522} 2523 2524/* 2525 * After loading a new font, update the structures that use its size. 2526 */ 2527void 2528xtermUpdateFontInfo(XtermWidget xw, Bool doresize) 2529{ 2530 TScreen *screen = TScreenOf(xw); 2531 2532 int scrollbar_width; 2533 VTwin *win = &(screen->fullVwin); 2534 2535 scrollbar_width = (xw->misc.scrollbar 2536 ? (screen->scrollWidget->core.width + 2537 BorderWidth(screen->scrollWidget)) 2538 : 0); 2539 xtermComputeFontInfo(xw, win, screen->fnts[fNorm].fs, scrollbar_width); 2540 xtermSaveFontInfo(screen, screen->fnts[fNorm].fs); 2541 2542 if (doresize) { 2543 if (VWindow(screen)) { 2544 xtermClear(xw); 2545 } 2546 TRACE(("xtermUpdateFontInfo {{\n")); 2547 DoResizeScreen(xw); /* set to the new natural size */ 2548 ResizeScrollBar(xw); 2549 Redraw(); 2550 TRACE(("... }} xtermUpdateFontInfo\n")); 2551#ifdef SCROLLBAR_RIGHT 2552 updateRightScrollbar(xw); 2553#endif 2554 } 2555 xtermSetCursorBox(screen); 2556} 2557 2558#if OPT_BOX_CHARS || OPT_REPORT_FONTS 2559 2560/* 2561 * Returns true if the given character is missing from the specified font. 2562 */ 2563Bool 2564xtermMissingChar(unsigned ch, XTermFonts * font) 2565{ 2566 Bool result = False; 2567 XFontStruct *fs = font->fs; 2568 XCharStruct *pc = 0; 2569 2570 if (fs->max_byte1 == 0) { 2571#if OPT_WIDE_CHARS 2572 if (ch < 256) 2573#endif 2574 { 2575 CI_GET_CHAR_INFO_1D(fs, E2A(ch), pc); 2576 } 2577 } 2578#if OPT_WIDE_CHARS 2579 else { 2580 unsigned row = (ch >> 8); 2581 unsigned col = (ch & 0xff); 2582 CI_GET_CHAR_INFO_2D(fs, row, col, pc); 2583 } 2584#endif 2585 2586 if (pc == 0 || CI_NONEXISTCHAR(pc)) { 2587 TRACE(("xtermMissingChar %#04x (!exists)\n", ch)); 2588 result = True; 2589 } 2590 if (ch < KNOWN_MISSING) { 2591 font->known_missing[ch] = (Char) (result ? 2 : 1); 2592 } 2593 return result; 2594} 2595#endif 2596 2597#if OPT_BOX_CHARS 2598/* 2599 * The grid is arbitrary, enough resolution that nothing's lost in 2600 * initialization. 2601 */ 2602#define BOX_HIGH 60 2603#define BOX_WIDE 60 2604 2605#define MID_HIGH (BOX_HIGH/2) 2606#define MID_WIDE (BOX_WIDE/2) 2607 2608#define CHR_WIDE ((9*BOX_WIDE)/10) 2609#define CHR_HIGH ((9*BOX_HIGH)/10) 2610 2611/* 2612 * ...since we'll scale the values anyway. 2613 */ 2614#define SCALED_X(n) ((int)(n) * (((int) font_width) - 1)) / (BOX_WIDE-1) 2615#define SCALED_Y(n) ((int)(n) * (((int) font_height) - 1)) / (BOX_HIGH-1) 2616#define SCALE_X(n) n = SCALED_X(n) 2617#define SCALE_Y(n) n = SCALED_Y(n) 2618 2619#define SEG(x0,y0,x1,y1) x0,y0, x1,y1 2620 2621/* 2622 * Draw the given graphic character, if it is simple enough (i.e., a 2623 * line-drawing character). 2624 */ 2625void 2626xtermDrawBoxChar(XtermWidget xw, 2627 unsigned ch, 2628 unsigned attr_flags, 2629 unsigned draw_flags, 2630 GC gc, 2631 int x, 2632 int y, 2633 int cells) 2634{ 2635 TScreen *screen = TScreenOf(xw); 2636 /* *INDENT-OFF* */ 2637 static const short glyph_ht[] = { 2638 SEG(1*BOX_WIDE/10, 0, 1*BOX_WIDE/10,5*MID_HIGH/6), /* H */ 2639 SEG(6*BOX_WIDE/10, 0, 6*BOX_WIDE/10,5*MID_HIGH/6), 2640 SEG(1*BOX_WIDE/10,5*MID_HIGH/12,6*BOX_WIDE/10,5*MID_HIGH/12), 2641 SEG(2*BOX_WIDE/10, MID_HIGH, CHR_WIDE, MID_HIGH), /* T */ 2642 SEG(6*BOX_WIDE/10, MID_HIGH, 6*BOX_WIDE/10, CHR_HIGH), 2643 -1 2644 }, glyph_ff[] = { 2645 SEG(1*BOX_WIDE/10, 0, 6*BOX_WIDE/10, 0), /* F */ 2646 SEG(1*BOX_WIDE/10,5*MID_HIGH/12,6*CHR_WIDE/12,5*MID_HIGH/12), 2647 SEG(1*BOX_WIDE/10, 0, 0*BOX_WIDE/3, 5*MID_HIGH/6), 2648 SEG(1*BOX_WIDE/3, MID_HIGH, CHR_WIDE, MID_HIGH), /* F */ 2649 SEG(1*BOX_WIDE/3, 8*MID_HIGH/6,10*CHR_WIDE/12,8*MID_HIGH/6), 2650 SEG(1*BOX_WIDE/3, MID_HIGH, 1*BOX_WIDE/3, CHR_HIGH), 2651 -1 2652 }, glyph_lf[] = { 2653 SEG(1*BOX_WIDE/10, 0, 1*BOX_WIDE/10,9*MID_HIGH/12), /* L */ 2654 SEG(1*BOX_WIDE/10,9*MID_HIGH/12,6*BOX_WIDE/10,9*MID_HIGH/12), 2655 SEG(1*BOX_WIDE/3, MID_HIGH, CHR_WIDE, MID_HIGH), /* F */ 2656 SEG(1*BOX_WIDE/3, 8*MID_HIGH/6,10*CHR_WIDE/12,8*MID_HIGH/6), 2657 SEG(1*BOX_WIDE/3, MID_HIGH, 1*BOX_WIDE/3, CHR_HIGH), 2658 -1 2659 }, glyph_nl[] = { 2660 SEG(1*BOX_WIDE/10,5*MID_HIGH/6, 1*BOX_WIDE/10, 0), /* N */ 2661 SEG(1*BOX_WIDE/10, 0, 5*BOX_WIDE/6, 5*MID_HIGH/6), 2662 SEG(5*BOX_WIDE/6, 5*MID_HIGH/6, 5*BOX_WIDE/6, 0), 2663 SEG(1*BOX_WIDE/3, MID_HIGH, 1*BOX_WIDE/3, CHR_HIGH), /* L */ 2664 SEG(1*BOX_WIDE/3, CHR_HIGH, CHR_WIDE, CHR_HIGH), 2665 -1 2666 }, glyph_vt[] = { 2667 SEG(1*BOX_WIDE/10, 0, 5*BOX_WIDE/12,5*MID_HIGH/6), /* V */ 2668 SEG(5*BOX_WIDE/12,5*MID_HIGH/6, 5*BOX_WIDE/6, 0), 2669 SEG(2*BOX_WIDE/10, MID_HIGH, CHR_WIDE, MID_HIGH), /* T */ 2670 SEG(6*BOX_WIDE/10, MID_HIGH, 6*BOX_WIDE/10, CHR_HIGH), 2671 -1 2672 }, plus_or_minus[] = 2673 { 2674 SEG( 0, 5*BOX_HIGH/6, CHR_WIDE, 5*BOX_HIGH/6), 2675 SEG( MID_WIDE, 2*BOX_HIGH/6, MID_WIDE, 4*BOX_HIGH/6), 2676 SEG( 0, 3*BOX_HIGH/6, CHR_WIDE, 3*BOX_HIGH/6), 2677 -1 2678 }, lower_right_corner[] = 2679 { 2680 SEG( 0, MID_HIGH, MID_WIDE, MID_HIGH), 2681 SEG( MID_WIDE, MID_HIGH, MID_WIDE, 0), 2682 -1 2683 }, upper_right_corner[] = 2684 { 2685 SEG( 0, MID_HIGH, MID_WIDE, MID_HIGH), 2686 SEG( MID_WIDE, MID_HIGH, MID_WIDE, BOX_HIGH), 2687 -1 2688 }, upper_left_corner[] = 2689 { 2690 SEG( MID_WIDE, MID_HIGH, BOX_WIDE, MID_HIGH), 2691 SEG( MID_WIDE, MID_HIGH, MID_WIDE, BOX_HIGH), 2692 -1 2693 }, lower_left_corner[] = 2694 { 2695 SEG( MID_WIDE, 0, MID_WIDE, MID_HIGH), 2696 SEG( MID_WIDE, MID_WIDE, BOX_WIDE, MID_HIGH), 2697 -1 2698 }, cross[] = 2699 { 2700 SEG( 0, MID_HIGH, BOX_WIDE, MID_HIGH), 2701 SEG( MID_WIDE, 0, MID_WIDE, BOX_HIGH), 2702 -1 2703 }, scan_line_1[] = 2704 { 2705 SEG( 0, 0, BOX_WIDE, 0), 2706 -1 2707 }, scan_line_3[] = 2708 { 2709 SEG( 0, BOX_HIGH/4, BOX_WIDE, BOX_HIGH/4), 2710 -1 2711 }, scan_line_7[] = 2712 { 2713 SEG( 0, MID_HIGH, BOX_WIDE, MID_HIGH), 2714 -1 2715 }, scan_line_9[] = 2716 { 2717 SEG( 0, 3*BOX_HIGH/4, BOX_WIDE, 3*BOX_HIGH/4), 2718 -1 2719 }, horizontal_line[] = 2720 { 2721 SEG( 0, BOX_HIGH, BOX_WIDE, BOX_HIGH), 2722 -1 2723 }, left_tee[] = 2724 { 2725 SEG( MID_WIDE, 0, MID_WIDE, BOX_HIGH), 2726 SEG( MID_WIDE, MID_HIGH, BOX_WIDE, MID_HIGH), 2727 -1 2728 }, right_tee[] = 2729 { 2730 SEG( MID_WIDE, 0, MID_WIDE, BOX_HIGH), 2731 SEG( MID_WIDE, MID_HIGH, 0, MID_HIGH), 2732 -1 2733 }, bottom_tee[] = 2734 { 2735 SEG( 0, MID_HIGH, BOX_WIDE, MID_HIGH), 2736 SEG( MID_WIDE, 0, MID_WIDE, MID_HIGH), 2737 -1 2738 }, top_tee[] = 2739 { 2740 SEG( 0, MID_HIGH, BOX_WIDE, MID_HIGH), 2741 SEG( MID_WIDE, MID_HIGH, MID_WIDE, BOX_HIGH), 2742 -1 2743 }, vertical_line[] = 2744 { 2745 SEG( MID_WIDE, 0, MID_WIDE, BOX_HIGH), 2746 -1 2747 }, less_than_or_equal[] = 2748 { 2749 SEG( CHR_WIDE, BOX_HIGH/3, 0, MID_HIGH), 2750 SEG( CHR_WIDE, 2*BOX_HIGH/3, 0, MID_HIGH), 2751 SEG( 0, 3*BOX_HIGH/4, CHR_WIDE, 3*BOX_HIGH/4), 2752 -1 2753 }, greater_than_or_equal[] = 2754 { 2755 SEG( 0, BOX_HIGH/3, CHR_WIDE, MID_HIGH), 2756 SEG( 0, 2*BOX_HIGH/3, CHR_WIDE, MID_HIGH), 2757 SEG( 0, 3*BOX_HIGH/4, CHR_WIDE, 3*BOX_HIGH/4), 2758 -1 2759 }, greek_pi[] = 2760 { 2761 SEG( 0, MID_HIGH, CHR_WIDE, MID_HIGH), 2762 SEG(5*CHR_WIDE/6, MID_HIGH, 5*CHR_WIDE/6, CHR_HIGH), 2763 SEG(2*CHR_WIDE/6, MID_HIGH, 2*CHR_WIDE/6, CHR_HIGH), 2764 -1 2765 }, not_equal_to[] = 2766 { 2767 SEG(2*BOX_WIDE/3, 1*BOX_HIGH/3, 1*BOX_WIDE/3, CHR_HIGH), 2768 SEG( 0, 2*BOX_HIGH/3, CHR_WIDE, 2*BOX_HIGH/3), 2769 SEG( 0, MID_HIGH, CHR_WIDE, MID_HIGH), 2770 -1 2771 }; 2772 /* *INDENT-ON* */ 2773 2774 static const short *lines[] = 2775 { 2776 0, /* 00 (unused) */ 2777 0, /* 01 diamond */ 2778 0, /* 02 box */ 2779 glyph_ht, /* 03 HT */ 2780 glyph_ff, /* 04 FF */ 2781 0, /* 05 CR */ 2782 glyph_lf, /* 06 LF */ 2783 0, /* 07 degrees (small circle) */ 2784 plus_or_minus, /* 08 */ 2785 glyph_nl, /* 09 */ 2786 glyph_vt, /* 0A */ 2787 lower_right_corner, /* 0B */ 2788 upper_right_corner, /* 0C */ 2789 upper_left_corner, /* 0D */ 2790 lower_left_corner, /* 0E */ 2791 cross, /* 0F */ 2792 scan_line_1, /* 10 */ 2793 scan_line_3, /* 11 */ 2794 scan_line_7, /* 12 */ 2795 scan_line_9, /* 13 */ 2796 horizontal_line, /* 14 */ 2797 left_tee, /* 15 */ 2798 right_tee, /* 16 */ 2799 bottom_tee, /* 17 */ 2800 top_tee, /* 18 */ 2801 vertical_line, /* 19 */ 2802 less_than_or_equal, /* 1A */ 2803 greater_than_or_equal, /* 1B */ 2804 greek_pi, /* 1C */ 2805 not_equal_to, /* 1D */ 2806 0, /* 1E LB */ 2807 0, /* 1F bullet */ 2808 }; 2809 2810 GC gc2; 2811 CgsEnum cgsId = (ch == 2) ? gcDots : gcLine; 2812 VTwin *cgsWin = WhichVWin(screen); 2813 const short *p; 2814 unsigned font_width = (unsigned) (((draw_flags & DOUBLEWFONT) ? 2 : 1) 2815 * screen->fnt_wide); 2816 unsigned font_height = (unsigned) (((draw_flags & DOUBLEHFONT) ? 2 : 1) 2817 * screen->fnt_high); 2818 2819 if (cells > 1) 2820 font_width *= (unsigned) cells; 2821 2822#if OPT_WIDE_CHARS 2823 /* 2824 * Try to show line-drawing characters if we happen to be in UTF-8 2825 * mode, but have gotten an old-style font. 2826 */ 2827 if (screen->utf8_mode 2828#if OPT_RENDERFONT 2829 && !UsingRenderFont(xw) 2830#endif 2831 && (ch > 127) 2832 && (ch != UCS_REPL)) { 2833 unsigned n; 2834 for (n = 1; n < 32; n++) { 2835 if (dec2ucs(n) == ch 2836 && !((attr_flags & BOLD) 2837 ? IsXtermMissingChar(screen, n, &screen->fnts[fBold]) 2838 : IsXtermMissingChar(screen, n, &screen->fnts[fNorm]))) { 2839 TRACE(("...use xterm-style linedrawing\n")); 2840 ch = n; 2841 break; 2842 } 2843 } 2844 } 2845#endif 2846 2847 TRACE(("DRAW_BOX(%d) cell %dx%d at %d,%d%s\n", 2848 ch, font_height, font_width, y, x, 2849 (ch >= (sizeof(lines) / sizeof(lines[0])) 2850 ? "-BAD" 2851 : ""))); 2852 2853 if (cgsId == gcDots) { 2854 setCgsFont(xw, cgsWin, cgsId, getCgsFont(xw, cgsWin, gc)); 2855 setCgsFore(xw, cgsWin, cgsId, getCgsFore(xw, cgsWin, gc)); 2856 setCgsBack(xw, cgsWin, cgsId, getCgsBack(xw, cgsWin, gc)); 2857 } else { 2858 setCgsFont(xw, cgsWin, cgsId, getCgsFont(xw, cgsWin, gc)); 2859 setCgsFore(xw, cgsWin, cgsId, getCgsBack(xw, cgsWin, gc)); 2860 setCgsBack(xw, cgsWin, cgsId, getCgsBack(xw, cgsWin, gc)); 2861 } 2862 gc2 = getCgsGC(xw, cgsWin, cgsId); 2863 2864 if (!(draw_flags & NOBACKGROUND)) { 2865 XFillRectangle(screen->display, VDrawable(screen), gc2, x, y, 2866 font_width, 2867 font_height); 2868 } 2869 2870 setCgsFont(xw, cgsWin, cgsId, getCgsFont(xw, cgsWin, gc)); 2871 setCgsFore(xw, cgsWin, cgsId, getCgsFore(xw, cgsWin, gc)); 2872 setCgsBack(xw, cgsWin, cgsId, getCgsBack(xw, cgsWin, gc)); 2873 gc2 = getCgsGC(xw, cgsWin, cgsId); 2874 2875 XSetLineAttributes(screen->display, gc2, 2876 (attr_flags & BOLD) 2877 ? ((font_height > 12) 2878 ? font_height / 12 2879 : 1) 2880 : ((font_height > 16) 2881 ? font_height / 16 2882 : 1), 2883 LineSolid, 2884 CapProjecting, 2885 JoinMiter); 2886 2887 if (ch == 1) { /* diamond */ 2888 XPoint points[5]; 2889 int npoints = 5, n; 2890 2891 points[0].x = MID_WIDE; 2892 points[0].y = BOX_HIGH / 4; 2893 2894 points[1].x = 8 * BOX_WIDE / 8; 2895 points[1].y = MID_HIGH; 2896 2897 points[2].x = points[0].x; 2898 points[2].y = 3 * BOX_HIGH / 4; 2899 2900 points[3].x = 0 * BOX_WIDE / 8; 2901 points[3].y = points[1].y; 2902 2903 points[4].x = points[0].x; 2904 points[4].y = points[0].y; 2905 2906 for (n = 0; n < npoints; ++n) { 2907 points[n].x = (short) SCALED_X(points[n].x); 2908 points[n].y = (short) SCALED_Y(points[n].y); 2909 points[n].x = (short) (points[n].x + x); 2910 points[n].y = (short) (points[n].y + y); 2911 } 2912 2913 XFillPolygon(screen->display, 2914 VDrawable(screen), gc2, 2915 points, npoints, 2916 Convex, CoordModeOrigin); 2917 } else if (ch == 7) { /* degrees */ 2918 unsigned width = (BOX_WIDE / 3); 2919 int x_coord = MID_WIDE - (int) (width / 2); 2920 int y_coord = MID_HIGH - (int) width; 2921 2922 SCALE_X(x_coord); 2923 SCALE_Y(y_coord); 2924 width = (unsigned) SCALED_X(width); 2925 2926 XDrawArc(screen->display, 2927 VDrawable(screen), gc2, 2928 x + x_coord, y + y_coord, width, width, 2929 0, 2930 360 * 64); 2931 } else if (ch == 0x1f) { /* bullet */ 2932 unsigned width = 7 * BOX_WIDE / 10; 2933 int x_coord = MID_WIDE - (int) (width / 3); 2934 int y_coord = MID_HIGH - (int) (width / 3); 2935 2936 SCALE_X(x_coord); 2937 SCALE_Y(y_coord); 2938 width = (unsigned) SCALED_X(width); 2939 2940 XDrawArc(screen->display, 2941 VDrawable(screen), gc2, 2942 x + x_coord, y + y_coord, width, width, 2943 0, 2944 360 * 64); 2945 } else if (ch < (sizeof(lines) / sizeof(lines[0])) 2946 && (p = lines[ch]) != 0) { 2947 int coord[4]; 2948 int n = 0; 2949 while (*p >= 0) { 2950 coord[n++] = *p++; 2951 if (n == 4) { 2952 SCALE_X(coord[0]); 2953 SCALE_Y(coord[1]); 2954 SCALE_X(coord[2]); 2955 SCALE_Y(coord[3]); 2956 XDrawLine(screen->display, 2957 VDrawable(screen), gc2, 2958 x + coord[0], y + coord[1], 2959 x + coord[2], y + coord[3]); 2960 n = 0; 2961 } 2962 } 2963 } else if (screen->force_all_chars) { 2964 /* bounding rectangle, for debugging */ 2965 XDrawRectangle(screen->display, VDrawable(screen), gc2, x, y, 2966 font_width - 1, 2967 font_height - 1); 2968 } 2969} 2970#endif /* OPT_BOX_CHARS */ 2971 2972#if OPT_RENDERFONT 2973 2974/* 2975 * Check if the given character has a glyph known to Xft. 2976 * 2977 * see xc/lib/Xft/xftglyphs.c 2978 */ 2979Bool 2980xtermXftMissing(XtermWidget xw, XftFont *font, unsigned wc) 2981{ 2982 Bool result = False; 2983 2984 if (font != 0) { 2985 TScreen *screen = TScreenOf(xw); 2986 if (!XftGlyphExists(screen->display, font, wc)) { 2987#if OPT_WIDE_CHARS 2988 TRACE(("xtermXftMissing %d (dec=%#x, ucs=%#x)\n", 2989 wc, ucs2dec(wc), dec2ucs(wc))); 2990#else 2991 TRACE(("xtermXftMissing %d\n", wc)); 2992#endif 2993 result = True; 2994 } 2995 } 2996 return result; 2997} 2998#endif /* OPT_RENDERFONT */ 2999 3000#if OPT_WIDE_CHARS 3001#define MY_UCS(ucs,dec) case ucs: result = dec; break 3002unsigned 3003ucs2dec(unsigned ch) 3004{ 3005 unsigned result = ch; 3006 if ((ch > 127) 3007 && (ch != UCS_REPL)) { 3008 switch (ch) { 3009 MY_UCS(0x25ae, 0); /* black vertical rectangle */ 3010 MY_UCS(0x25c6, 1); /* black diamond */ 3011 MY_UCS(0x2592, 2); /* medium shade */ 3012 MY_UCS(0x2409, 3); /* symbol for horizontal tabulation */ 3013 MY_UCS(0x240c, 4); /* symbol for form feed */ 3014 MY_UCS(0x240d, 5); /* symbol for carriage return */ 3015 MY_UCS(0x240a, 6); /* symbol for line feed */ 3016 MY_UCS(0x00b0, 7); /* degree sign */ 3017 MY_UCS(0x00b1, 8); /* plus-minus sign */ 3018 MY_UCS(0x2424, 9); /* symbol for newline */ 3019 MY_UCS(0x240b, 10); /* symbol for vertical tabulation */ 3020 MY_UCS(0x2518, 11); /* box drawings light up and left */ 3021 MY_UCS(0x2510, 12); /* box drawings light down and left */ 3022 MY_UCS(0x250c, 13); /* box drawings light down and right */ 3023 MY_UCS(0x2514, 14); /* box drawings light up and right */ 3024 MY_UCS(0x253c, 15); /* box drawings light vertical and horizontal */ 3025 MY_UCS(0x23ba, 16); /* box drawings scan 1 */ 3026 MY_UCS(0x23bb, 17); /* box drawings scan 3 */ 3027 MY_UCS(0x2500, 18); /* box drawings light horizontal */ 3028 MY_UCS(0x23bc, 19); /* box drawings scan 7 */ 3029 MY_UCS(0x23bd, 20); /* box drawings scan 9 */ 3030 MY_UCS(0x251c, 21); /* box drawings light vertical and right */ 3031 MY_UCS(0x2524, 22); /* box drawings light vertical and left */ 3032 MY_UCS(0x2534, 23); /* box drawings light up and horizontal */ 3033 MY_UCS(0x252c, 24); /* box drawings light down and horizontal */ 3034 MY_UCS(0x2502, 25); /* box drawings light vertical */ 3035 MY_UCS(0x2264, 26); /* less-than or equal to */ 3036 MY_UCS(0x2265, 27); /* greater-than or equal to */ 3037 MY_UCS(0x03c0, 28); /* greek small letter pi */ 3038 MY_UCS(0x2260, 29); /* not equal to */ 3039 MY_UCS(0x00a3, 30); /* pound sign */ 3040 MY_UCS(0x00b7, 31); /* middle dot */ 3041 } 3042 } 3043 return result; 3044} 3045 3046#undef MY_UCS 3047#define MY_UCS(ucs,dec) case dec: result = ucs; break 3048 3049unsigned 3050dec2ucs(unsigned ch) 3051{ 3052 unsigned result = ch; 3053 if (xtermIsDecGraphic(ch)) { 3054 switch (ch) { 3055 MY_UCS(0x25ae, 0); /* black vertical rectangle */ 3056 MY_UCS(0x25c6, 1); /* black diamond */ 3057 MY_UCS(0x2592, 2); /* medium shade */ 3058 MY_UCS(0x2409, 3); /* symbol for horizontal tabulation */ 3059 MY_UCS(0x240c, 4); /* symbol for form feed */ 3060 MY_UCS(0x240d, 5); /* symbol for carriage return */ 3061 MY_UCS(0x240a, 6); /* symbol for line feed */ 3062 MY_UCS(0x00b0, 7); /* degree sign */ 3063 MY_UCS(0x00b1, 8); /* plus-minus sign */ 3064 MY_UCS(0x2424, 9); /* symbol for newline */ 3065 MY_UCS(0x240b, 10); /* symbol for vertical tabulation */ 3066 MY_UCS(0x2518, 11); /* box drawings light up and left */ 3067 MY_UCS(0x2510, 12); /* box drawings light down and left */ 3068 MY_UCS(0x250c, 13); /* box drawings light down and right */ 3069 MY_UCS(0x2514, 14); /* box drawings light up and right */ 3070 MY_UCS(0x253c, 15); /* box drawings light vertical and horizontal */ 3071 MY_UCS(0x23ba, 16); /* box drawings scan 1 */ 3072 MY_UCS(0x23bb, 17); /* box drawings scan 3 */ 3073 MY_UCS(0x2500, 18); /* box drawings light horizontal */ 3074 MY_UCS(0x23bc, 19); /* box drawings scan 7 */ 3075 MY_UCS(0x23bd, 20); /* box drawings scan 9 */ 3076 MY_UCS(0x251c, 21); /* box drawings light vertical and right */ 3077 MY_UCS(0x2524, 22); /* box drawings light vertical and left */ 3078 MY_UCS(0x2534, 23); /* box drawings light up and horizontal */ 3079 MY_UCS(0x252c, 24); /* box drawings light down and horizontal */ 3080 MY_UCS(0x2502, 25); /* box drawings light vertical */ 3081 MY_UCS(0x2264, 26); /* less-than or equal to */ 3082 MY_UCS(0x2265, 27); /* greater-than or equal to */ 3083 MY_UCS(0x03c0, 28); /* greek small letter pi */ 3084 MY_UCS(0x2260, 29); /* not equal to */ 3085 MY_UCS(0x00a3, 30); /* pound sign */ 3086 MY_UCS(0x00b7, 31); /* middle dot */ 3087 } 3088 } 3089 return result; 3090} 3091 3092#endif /* OPT_WIDE_CHARS */ 3093 3094#if OPT_RENDERFONT || OPT_SHIFT_FONTS 3095static int 3096lookupOneFontSize(XtermWidget xw, int fontnum) 3097{ 3098 TScreen *screen = TScreenOf(xw); 3099 3100 if (screen->menu_font_sizes[fontnum] == 0) { 3101 XTermFonts fnt; 3102 3103 memset(&fnt, 0, sizeof(fnt)); 3104 screen->menu_font_sizes[fontnum] = -1; 3105 if (xtermOpenFont(xw, 3106 screen->MenuFontName(fontnum), 3107 &fnt, 3108 ((fontnum <= fontMenu_lastBuiltin) 3109 ? fwAlways 3110 : fwResource), 3111 True)) { 3112 if (fontnum <= fontMenu_lastBuiltin 3113 || strcmp(fnt.fn, DEFFONT)) { 3114 screen->menu_font_sizes[fontnum] = FontSize(fnt.fs); 3115 if (screen->menu_font_sizes[fontnum] <= 0) 3116 screen->menu_font_sizes[fontnum] = -1; 3117 } 3118 xtermCloseFont(xw, &fnt); 3119 } 3120 } 3121 return (screen->menu_font_sizes[fontnum] > 0); 3122} 3123 3124/* 3125 * Cache the font-sizes so subsequent larger/smaller font actions will go fast. 3126 */ 3127static void 3128lookupFontSizes(XtermWidget xw) 3129{ 3130 int n; 3131 3132 for (n = 0; n < NMENUFONTS; n++) { 3133 (void) lookupOneFontSize(xw, n); 3134 } 3135} 3136#endif /* OPT_RENDERFONT || OPT_SHIFT_FONTS */ 3137 3138#if OPT_RENDERFONT 3139static double 3140defaultFaceSize(void) 3141{ 3142 double result; 3143 float value; 3144 3145 if (sscanf(DEFFACESIZE, "%f", &value) == 1) 3146 result = value; 3147 else 3148 result = 14.0; 3149 return result; 3150} 3151 3152static void 3153fillInFaceSize(XtermWidget xw, int fontnum) 3154{ 3155 TScreen *screen = TScreenOf(xw); 3156 double face_size = xw->misc.face_size[fontnum]; 3157 3158 if (face_size <= 0.0) { 3159#if OPT_SHIFT_FONTS 3160 /* 3161 * If the user is switching font-sizes, make it follow by 3162 * default the same ratios to the default as the fixed fonts 3163 * would, for easy comparison. There will be some differences 3164 * since the fixed fonts have a variety of height/width ratios, 3165 * but this is simpler than adding another resource value - and 3166 * as noted above, the data for the fixed fonts are available. 3167 */ 3168 (void) lookupOneFontSize(xw, 0); 3169 if (fontnum == fontMenu_default) { 3170 face_size = defaultFaceSize(); 3171 } else if (lookupOneFontSize(xw, fontnum) 3172 && (screen->menu_font_sizes[0] 3173 != screen->menu_font_sizes[fontnum])) { 3174 double ratio; 3175 long num = screen->menu_font_sizes[fontnum]; 3176 long den = screen->menu_font_sizes[0]; 3177 3178 if (den <= 0) 3179 den = 1; 3180 ratio = dimSquareRoot((double) num / (double) den); 3181 3182 face_size = (ratio * xw->misc.face_size[0]); 3183 TRACE(("scaled[%d] using %3ld/%ld = %.2f -> %f\n", 3184 fontnum, num, den, ratio, face_size)); 3185 } else 3186#endif 3187 { 3188#define LikeBitmap(s) (((s) / 78.0) * xw->misc.face_size[fontMenu_default]) 3189 switch (fontnum) { 3190 case fontMenu_font1: 3191 face_size = LikeBitmap(2.0); 3192 break; 3193 case fontMenu_font2: 3194 face_size = LikeBitmap(35.0); 3195 break; 3196 case fontMenu_font3: 3197 face_size = LikeBitmap(60.0); 3198 break; 3199 default: 3200 face_size = defaultFaceSize(); 3201 break; 3202 case fontMenu_font4: 3203 face_size = LikeBitmap(90.0); 3204 break; 3205 case fontMenu_font5: 3206 face_size = LikeBitmap(135.0); 3207 break; 3208 case fontMenu_font6: 3209 face_size = LikeBitmap(200.0); 3210 break; 3211 } 3212 TRACE(("builtin[%d] -> %f\n", fontnum, face_size)); 3213 } 3214 xw->misc.face_size[fontnum] = (float) face_size; 3215 } 3216} 3217 3218/* no selection or escape */ 3219#define NMENU_RENDERFONTS (fontMenu_lastBuiltin + 1) 3220 3221/* 3222 * Workaround for breakage in font-packages - check if all of the bitmap font 3223 * sizes are the same, and if we're using TrueType fonts. 3224 */ 3225static Boolean 3226useFaceSizes(XtermWidget xw) 3227{ 3228 Boolean result = False; 3229 int n; 3230 3231 TRACE(("useFaceSizes {{\n")); 3232 if (UsingRenderFont(xw)) { 3233 Boolean nonzero = True; 3234 3235 for (n = 0; n < NMENU_RENDERFONTS; ++n) { 3236 if (xw->misc.face_size[n] <= 0.0) { 3237 nonzero = False; 3238 break; 3239 } 3240 } 3241 if (!nonzero) { 3242 Boolean broken_fonts = True; 3243 TScreen *screen = TScreenOf(xw); 3244 long first; 3245 3246 lookupFontSizes(xw); 3247 first = screen->menu_font_sizes[0]; 3248 for (n = 0; n < NMENUFONTS; n++) { 3249 if (screen->menu_font_sizes[n] > 0 3250 && screen->menu_font_sizes[n] != first) { 3251 broken_fonts = False; 3252 break; 3253 } 3254 } 3255 3256 if (broken_fonts) { 3257 3258 TRACE(("bitmap fonts are broken - set faceSize resources\n")); 3259 for (n = 0; n < NMENUFONTS; n++) { 3260 fillInFaceSize(xw, n); 3261 } 3262 3263 } 3264 } 3265 result = True; 3266 } 3267 TRACE(("...}}useFaceSizes %d\n", result)); 3268 return result; 3269} 3270#endif /* OPT_RENDERFONT */ 3271 3272#if OPT_SHIFT_FONTS 3273/* 3274 * Find the index of a larger/smaller font (according to the sign of 'relative' 3275 * and its magnitude), starting from the 'old' index. 3276 */ 3277int 3278lookupRelativeFontSize(XtermWidget xw, int old, int relative) 3279{ 3280 TScreen *screen = TScreenOf(xw); 3281 int n, m = -1; 3282 3283 TRACE(("lookupRelativeFontSize(old=%d, relative=%d)\n", old, relative)); 3284 if (!IsIcon(screen)) { 3285#if OPT_RENDERFONT 3286 if (useFaceSizes(xw)) { 3287 TRACE(("...using FaceSize\n")); 3288 if (relative != 0) { 3289 for (n = 0; n < NMENU_RENDERFONTS; ++n) { 3290 fillInFaceSize(xw, n); 3291 if (xw->misc.face_size[n] > 0 && 3292 xw->misc.face_size[n] != xw->misc.face_size[old]) { 3293 int cmp_0 = ((xw->misc.face_size[n] > 3294 xw->misc.face_size[old]) 3295 ? relative 3296 : -relative); 3297 int cmp_m = ((m < 0) 3298 ? 1 3299 : ((xw->misc.face_size[n] < 3300 xw->misc.face_size[m]) 3301 ? relative 3302 : -relative)); 3303 if (cmp_0 > 0 && cmp_m > 0) { 3304 m = n; 3305 } 3306 } 3307 } 3308 } 3309 } else 3310#endif 3311 { 3312 TRACE(("...using bitmap areas\n")); 3313 lookupFontSizes(xw); 3314 if (relative != 0) { 3315 for (n = 0; n < NMENUFONTS; ++n) { 3316 if (screen->menu_font_sizes[n] > 0 && 3317 screen->menu_font_sizes[n] != 3318 screen->menu_font_sizes[old]) { 3319 int cmp_0 = ((screen->menu_font_sizes[n] > 3320 screen->menu_font_sizes[old]) 3321 ? relative 3322 : -relative); 3323 int cmp_m = ((m < 0) 3324 ? 1 3325 : ((screen->menu_font_sizes[n] < 3326 screen->menu_font_sizes[m]) 3327 ? relative 3328 : -relative)); 3329 if (cmp_0 > 0 && cmp_m > 0) { 3330 m = n; 3331 } 3332 } 3333 } 3334 } 3335 } 3336 TRACE(("...new index %d\n", m)); 3337 if (m >= 0) { 3338 if (relative > 1) 3339 m = lookupRelativeFontSize(xw, m, relative - 1); 3340 else if (relative < -1) 3341 m = lookupRelativeFontSize(xw, m, relative + 1); 3342 } 3343 } 3344 return m; 3345} 3346 3347/* ARGSUSED */ 3348void 3349HandleLargerFont(Widget w GCC_UNUSED, 3350 XEvent *event GCC_UNUSED, 3351 String *params GCC_UNUSED, 3352 Cardinal *param_count GCC_UNUSED) 3353{ 3354 XtermWidget xw; 3355 3356 TRACE(("Handle larger-vt-font for %p\n", (void *) w)); 3357 if ((xw = getXtermWidget(w)) != 0) { 3358 if (xw->misc.shift_fonts) { 3359 TScreen *screen = TScreenOf(xw); 3360 int m; 3361 3362 m = lookupRelativeFontSize(xw, screen->menu_font_number, 1); 3363 if (m >= 0) { 3364 SetVTFont(xw, m, True, NULL); 3365 } else { 3366 Bell(xw, XkbBI_MinorError, 0); 3367 } 3368 } 3369 } 3370} 3371 3372/* ARGSUSED */ 3373void 3374HandleSmallerFont(Widget w GCC_UNUSED, 3375 XEvent *event GCC_UNUSED, 3376 String *params GCC_UNUSED, 3377 Cardinal *param_count GCC_UNUSED) 3378{ 3379 XtermWidget xw; 3380 3381 TRACE(("Handle smaller-vt-font for %p\n", (void *) w)); 3382 if ((xw = getXtermWidget(w)) != 0) { 3383 if (xw->misc.shift_fonts) { 3384 TScreen *screen = TScreenOf(xw); 3385 int m; 3386 3387 m = lookupRelativeFontSize(xw, screen->menu_font_number, -1); 3388 if (m >= 0) { 3389 SetVTFont(xw, m, True, NULL); 3390 } else { 3391 Bell(xw, XkbBI_MinorError, 0); 3392 } 3393 } 3394 } 3395} 3396#endif /* OPT_SHIFT_FONTS */ 3397 3398int 3399xtermGetFont(const char *param) 3400{ 3401 int fontnum; 3402 3403 switch (param[0]) { 3404 case 'd': 3405 case 'D': 3406 case '0': 3407 fontnum = fontMenu_default; 3408 break; 3409 case '1': 3410 fontnum = fontMenu_font1; 3411 break; 3412 case '2': 3413 fontnum = fontMenu_font2; 3414 break; 3415 case '3': 3416 fontnum = fontMenu_font3; 3417 break; 3418 case '4': 3419 fontnum = fontMenu_font4; 3420 break; 3421 case '5': 3422 fontnum = fontMenu_font5; 3423 break; 3424 case '6': 3425 fontnum = fontMenu_font6; 3426 break; 3427 case 'e': 3428 case 'E': 3429 fontnum = fontMenu_fontescape; 3430 break; 3431 case 's': 3432 case 'S': 3433 fontnum = fontMenu_fontsel; 3434 break; 3435 default: 3436 fontnum = -1; 3437 break; 3438 } 3439 return fontnum; 3440} 3441 3442/* ARGSUSED */ 3443void 3444HandleSetFont(Widget w GCC_UNUSED, 3445 XEvent *event GCC_UNUSED, 3446 String *params, 3447 Cardinal *param_count) 3448{ 3449 XtermWidget xw; 3450 3451 if ((xw = getXtermWidget(w)) != 0) { 3452 int fontnum; 3453 VTFontNames fonts; 3454 3455 memset(&fonts, 0, sizeof(fonts)); 3456 3457 if (*param_count == 0) { 3458 fontnum = fontMenu_default; 3459 } else { 3460 Cardinal maxparams = 1; /* total number of params allowed */ 3461 int result = xtermGetFont(params[0]); 3462 3463 switch (result) { 3464 case fontMenu_default: /* FALLTHRU */ 3465 case fontMenu_font1: /* FALLTHRU */ 3466 case fontMenu_font2: /* FALLTHRU */ 3467 case fontMenu_font3: /* FALLTHRU */ 3468 case fontMenu_font4: /* FALLTHRU */ 3469 case fontMenu_font5: /* FALLTHRU */ 3470 case fontMenu_font6: /* FALLTHRU */ 3471 break; 3472 case fontMenu_fontescape: 3473#if OPT_WIDE_CHARS 3474 maxparams = 5; 3475#else 3476 maxparams = 3; 3477#endif 3478 break; 3479 case fontMenu_fontsel: 3480 maxparams = 2; 3481 break; 3482 default: 3483 Bell(xw, XkbBI_MinorError, 0); 3484 return; 3485 } 3486 fontnum = result; 3487 3488 if (*param_count > maxparams) { /* see if extra args given */ 3489 Bell(xw, XkbBI_MinorError, 0); 3490 return; 3491 } 3492 switch (*param_count) { /* assign 'em */ 3493#if OPT_WIDE_CHARS 3494 case 5: 3495 fonts.f_wb = params[4]; 3496 /* FALLTHRU */ 3497 case 4: 3498 fonts.f_w = params[3]; 3499 /* FALLTHRU */ 3500#endif 3501 case 3: 3502 fonts.f_b = params[2]; 3503 /* FALLTHRU */ 3504 case 2: 3505 fonts.f_n = params[1]; 3506 break; 3507 } 3508 } 3509 3510 SetVTFont(xw, fontnum, True, &fonts); 3511 } 3512} 3513 3514void 3515SetVTFont(XtermWidget xw, 3516 int which, 3517 Bool doresize, 3518 const VTFontNames * fonts) 3519{ 3520 TScreen *screen = TScreenOf(xw); 3521 3522 TRACE(("SetVTFont(which=%d, f_n=%s, f_b=%s)\n", which, 3523 (fonts && fonts->f_n) ? fonts->f_n : "<null>", 3524 (fonts && fonts->f_b) ? fonts->f_b : "<null>")); 3525 3526 if (IsIcon(screen)) { 3527 Bell(xw, XkbBI_MinorError, 0); 3528 } else if (which >= 0 && which < NMENUFONTS) { 3529 VTFontNames myfonts; 3530 3531 memset(&myfonts, 0, sizeof(myfonts)); 3532 if (fonts != 0) 3533 myfonts = *fonts; 3534 3535 if (which == fontMenu_fontsel) { /* go get the selection */ 3536 FindFontSelection(xw, myfonts.f_n, False); 3537 } else { 3538 int oldFont = screen->menu_font_number; 3539 3540#define USE_CACHED(field, name) \ 3541 if (myfonts.field == 0) { \ 3542 myfonts.field = x_strdup(screen->menu_font_names[which][name]); \ 3543 TRACE(("set myfonts." #field " from menu_font_names[%d][" #name "] %s\n", \ 3544 which, NonNull(myfonts.field))); \ 3545 } else { \ 3546 TRACE(("set myfonts." #field " reused\n")); \ 3547 } 3548#define SAVE_FNAME(field, name) \ 3549 if (myfonts.field != 0) { \ 3550 if (screen->menu_font_names[which][name] == 0 \ 3551 || strcmp(screen->menu_font_names[which][name], myfonts.field)) { \ 3552 TRACE(("updating menu_font_names[%d][" #name "] to %s\n", \ 3553 which, myfonts.field)); \ 3554 FREE_STRING(screen->menu_font_names[which][name]); \ 3555 screen->menu_font_names[which][name] = x_strdup(myfonts.field); \ 3556 } \ 3557 } 3558 3559 USE_CACHED(f_n, fNorm); 3560 USE_CACHED(f_b, fBold); 3561#if OPT_WIDE_CHARS 3562 USE_CACHED(f_w, fWide); 3563 USE_CACHED(f_wb, fWBold); 3564#endif 3565 if (xtermLoadFont(xw, 3566 &myfonts, 3567 doresize, which)) { 3568 /* 3569 * If successful, save the data so that a subsequent query via 3570 * OSC-50 will return the expected values. 3571 */ 3572 SAVE_FNAME(f_n, fNorm); 3573 SAVE_FNAME(f_b, fBold); 3574#if OPT_WIDE_CHARS 3575 SAVE_FNAME(f_w, fWide); 3576 SAVE_FNAME(f_wb, fWBold); 3577#endif 3578 } else { 3579 (void) xtermLoadFont(xw, 3580 xtermFontName(screen->MenuFontName(oldFont)), 3581 doresize, oldFont); 3582 Bell(xw, XkbBI_MinorError, 0); 3583 } 3584 FREE_FNAME(f_n); 3585 FREE_FNAME(f_b); 3586#if OPT_WIDE_CHARS 3587 FREE_FNAME(f_w); 3588 FREE_FNAME(f_wb); 3589#endif 3590 } 3591 } else { 3592 Bell(xw, XkbBI_MinorError, 0); 3593 } 3594 return; 3595} 3596