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