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