util.c revision ad37e533
1/* $XTermId: util.c,v 1.877 2021/03/21 21:27:08 tom Exp $ */ 2 3/* 4 * Copyright 1999-2020,2021 by Thomas E. Dickey 5 * 6 * All Rights Reserved 7 * 8 * Permission is hereby granted, free of charge, to any person obtaining a 9 * copy of this software and associated documentation files (the 10 * "Software"), to deal in the Software without restriction, including 11 * without limitation the rights to use, copy, modify, merge, publish, 12 * distribute, sublicense, and/or sell copies of the Software, and to 13 * permit persons to whom the Software is furnished to do so, subject to 14 * the following conditions: 15 * 16 * The above copyright notice and this permission notice shall be included 17 * in all copies or substantial portions of the Software. 18 * 19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 20 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 21 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 22 * IN NO EVENT SHALL THE ABOVE LISTED COPYRIGHT HOLDER(S) BE LIABLE FOR ANY 23 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 24 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 25 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 26 * 27 * Except as contained in this notice, the name(s) of the above copyright 28 * holders shall not be used in advertising or otherwise to promote the 29 * sale, use or other dealings in this Software without prior written 30 * authorization. 31 * 32 * 33 * Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts. 34 * 35 * All Rights Reserved 36 * 37 * Permission to use, copy, modify, and distribute this software and its 38 * documentation for any purpose and without fee is hereby granted, 39 * provided that the above copyright notice appear in all copies and that 40 * both that copyright notice and this permission notice appear in 41 * supporting documentation, and that the name of Digital Equipment 42 * Corporation not be used in advertising or publicity pertaining to 43 * distribution of the software without specific, written prior permission. 44 * 45 * 46 * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING 47 * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL 48 * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR 49 * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, 50 * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, 51 * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS 52 * SOFTWARE. 53 */ 54 55/* util.c */ 56 57#include <xterm.h> 58 59#include <data.h> 60#include <error.h> 61#include <menu.h> 62#include <fontutils.h> 63#include <xstrings.h> 64 65#include <stdio.h> 66#include <string.h> 67#include <ctype.h> 68#include <assert.h> 69 70#if OPT_WIDE_CHARS 71#if defined(HAVE_WCHAR_H) && defined(HAVE_WCWIDTH) 72#include <wchar.h> 73#endif 74#include <wcwidth.h> 75#endif 76 77#ifdef HAVE_X11_EXTENSIONS_XINERAMA_H 78#include <X11/extensions/Xinerama.h> 79#endif /* HAVE_X11_EXTENSIONS_XINERAMA_H */ 80 81#include <graphics.h> 82 83static int handle_translated_exposure(XtermWidget xw, 84 int rect_x, 85 int rect_y, 86 int rect_width, 87 int rect_height); 88static void ClearLeft(XtermWidget xw); 89static void CopyWait(XtermWidget xw); 90static void horizontal_copy_area(XtermWidget xw, 91 int firstchar, 92 int nchars, 93 int amount); 94static void vertical_copy_area(XtermWidget xw, 95 int firstline, 96 int nlines, 97 int amount, 98 int left, 99 int right); 100 101#if OPT_WIDE_CHARS 102unsigned first_widechar; 103int (*my_wcwidth) (wchar_t); 104#endif 105 106#if OPT_WIDE_CHARS 107/* 108 * We will modify the 'n' cells beginning at the current position. 109 * Some of those cells may be part of multi-column characters, including 110 * carryover from the left. Find the limits of the multi-column characters 111 * that we should fill with blanks, return true if filling is needed. 112 */ 113int 114DamagedCells(TScreen *screen, unsigned n, int *klp, int *krp, int row, int col) 115{ 116 CLineData *ld = getLineData(screen, row); 117 int result = False; 118 119 assert(ld); 120 if (col < (int) ld->lineSize) { 121 int nn = (int) n; 122 int kl = col; 123 int kr = col + nn; 124 125 if (kr >= (int) ld->lineSize) { 126 nn = (ld->lineSize - col - 1); 127 kr = col + nn; 128 } 129 130 if (nn > 0) { 131 assert(kl < (int) ld->lineSize); 132 if (ld->charData[kl] == HIDDEN_CHAR) { 133 while (kl > 0) { 134 if (ld->charData[--kl] != HIDDEN_CHAR) { 135 break; 136 } 137 } 138 } else { 139 kl = col + 1; 140 } 141 142 assert(kr < (int) ld->lineSize); 143 if (ld->charData[kr] == HIDDEN_CHAR) { 144 while (kr < screen->max_col) { 145 assert((kr + 1) < (int) ld->lineSize); 146 if (ld->charData[++kr] != HIDDEN_CHAR) { 147 --kr; 148 break; 149 } 150 } 151 } else { 152 kr = col - 1; 153 } 154 155 if (klp) 156 *klp = kl; 157 if (krp) 158 *krp = kr; 159 result = (kr >= kl); 160 } 161 } 162 163 return result; 164} 165 166int 167DamagedCurCells(TScreen *screen, unsigned n, int *klp, int *krp) 168{ 169 return DamagedCells(screen, n, klp, krp, screen->cur_row, screen->cur_col); 170} 171#endif /* OPT_WIDE_CHARS */ 172 173/* 174 * These routines are used for the jump scroll feature 175 */ 176void 177FlushScroll(XtermWidget xw) 178{ 179 TScreen *screen = TScreenOf(xw); 180 int i; 181 int shift = INX2ROW(screen, 0); 182 int bot = screen->max_row - shift; 183 int refreshtop; 184 int refreshheight; 185 int scrolltop; 186 int scrollheight; 187 int left = ScrnLeftMargin(xw); 188 int right = ScrnRightMargin(xw); 189 Boolean full_lines = (Boolean) ((left == 0) && (right == screen->max_col)); 190 191 if (screen->cursor_state) 192 HideCursor(xw); 193 194 TRACE(("FlushScroll %s-lines scroll:%d refresh %d\n", 195 full_lines ? "full" : "partial", 196 screen->scroll_amt, 197 screen->refresh_amt)); 198 199 if (screen->scroll_amt > 0) { 200 /* 201 * Lines will be scrolled "up". 202 */ 203 refreshheight = screen->refresh_amt; 204 scrollheight = screen->bot_marg - screen->top_marg - refreshheight + 1; 205 refreshtop = screen->bot_marg - refreshheight + 1 + shift; 206 i = screen->max_row - screen->scroll_amt + 1; 207 if (refreshtop > i) { 208 refreshtop = i; 209 } 210 211 /* 212 * If this is the normal (not alternate) screen, and the top margin is 213 * at the top of the screen, then we will shift full lines scrolled out 214 * of the scrolling region into the saved-lines. 215 */ 216 if (screen->scrollWidget 217 && !screen->whichBuf 218 && full_lines 219 && screen->top_marg == 0) { 220 scrolltop = 0; 221 scrollheight += shift; 222 if (scrollheight > i) 223 scrollheight = i; 224 i = screen->bot_marg - bot; 225 if (i > 0) { 226 refreshheight -= i; 227 if (refreshheight < screen->scroll_amt) { 228 refreshheight = screen->scroll_amt; 229 } 230 } 231 i = screen->savedlines; 232 if (i < screen->savelines) { 233 i += screen->scroll_amt; 234 if (i > screen->savelines) { 235 i = screen->savelines; 236 } 237 screen->savedlines = i; 238 ScrollBarDrawThumb(xw, 1); 239 } 240 } else { 241 scrolltop = screen->top_marg + shift; 242 i = bot - (screen->bot_marg - screen->refresh_amt + screen->scroll_amt); 243 if (i > 0) { 244 if (bot < screen->bot_marg) { 245 refreshheight = screen->scroll_amt + i; 246 } 247 } else { 248 scrollheight += i; 249 refreshheight = screen->scroll_amt; 250 i = screen->top_marg + screen->scroll_amt - 1 - bot; 251 if (i > 0) { 252 refreshtop += i; 253 refreshheight -= i; 254 } 255 } 256 } 257 } else { 258 /* 259 * Lines will be scrolled "down". 260 */ 261 refreshheight = -screen->refresh_amt; 262 scrollheight = screen->bot_marg - screen->top_marg - refreshheight + 1; 263 refreshtop = screen->top_marg + shift; 264 scrolltop = refreshtop + refreshheight; 265 i = screen->bot_marg - bot; 266 if (i > 0) { 267 scrollheight -= i; 268 } 269 i = screen->top_marg + refreshheight - 1 - bot; 270 if (i > 0) { 271 refreshheight -= i; 272 } 273 } 274 275 vertical_copy_area(xw, 276 scrolltop + screen->scroll_amt, 277 scrollheight, 278 screen->scroll_amt, 279 left, 280 right); 281 ScrollSelection(screen, -(screen->scroll_amt), False); 282 screen->scroll_amt = 0; 283 screen->refresh_amt = 0; 284 285 if (refreshheight > 0) { 286 ClearCurBackground(xw, 287 refreshtop, 288 left, 289 (unsigned) refreshheight, 290 (unsigned) (right + 1 - left), 291 (unsigned) FontWidth(screen)); 292 ScrnRefresh(xw, 293 refreshtop, 294 0, 295 refreshheight, 296 MaxCols(screen), 297 False); 298 } 299 xtermTimedDbe(xw); 300 return; 301} 302 303/* 304 * Returns true if there are lines off-screen due to scrolling which should 305 * include the current line. If false, the line is visible and we should 306 * paint it now rather than waiting for the line to become visible. 307 */ 308static Bool 309AddToRefresh(XtermWidget xw) 310{ 311 TScreen *screen = TScreenOf(xw); 312 int amount = screen->refresh_amt; 313 int row = screen->cur_row; 314 Bool result; 315 316 if (amount == 0) { 317 result = False; 318 } else if (amount > 0) { 319 int bottom; 320 321 if (row == (bottom = screen->bot_marg) - amount) { 322 screen->refresh_amt++; 323 result = True; 324 } else { 325 result = (row >= bottom - amount + 1 && row <= bottom); 326 } 327 } else { 328 int top; 329 330 amount = -amount; 331 if (row == (top = screen->top_marg) + amount) { 332 screen->refresh_amt--; 333 result = True; 334 } else { 335 result = (row <= top + amount - 1 && row >= top); 336 } 337 } 338 339 /* 340 * If this line is visible, and there are scrolled-off lines, flush out 341 * those which are now visible. 342 */ 343 if (!result && screen->scroll_amt) 344 FlushScroll(xw); 345 346 return result; 347} 348 349/* 350 * Returns true if the current row is in the visible area (it should be for 351 * screen operations) and incidentally flush the scrolled-in lines which 352 * have newly become visible. 353 */ 354static Bool 355AddToVisible(XtermWidget xw) 356{ 357 TScreen *screen = TScreenOf(xw); 358 Bool result = False; 359 360 if (INX2ROW(screen, screen->cur_row) <= screen->max_row) { 361 if (!AddToRefresh(xw)) { 362 result = True; 363 } 364 } 365 return result; 366} 367 368/* 369 * If we're scrolling, leave the selection intact if possible. 370 * If it will bump into one of the extremes of the saved-lines, truncate that. 371 * If the selection is not entirely contained within the margins and not 372 * entirely outside the margins, clear it. 373 */ 374static void 375adjustHiliteOnFwdScroll(XtermWidget xw, int amount, Bool all_lines) 376{ 377 TScreen *screen = TScreenOf(xw); 378 int lo_row = (all_lines 379 ? (screen->bot_marg - screen->savelines) 380 : screen->top_marg); 381 int hi_row = screen->bot_marg; 382 int left = ScrnLeftMargin(xw); 383 int right = ScrnRightMargin(xw); 384 385 TRACE2(("adjustSelection FWD %s by %d (%s)\n", 386 screen->whichBuf ? "alternate" : "normal", 387 amount, 388 all_lines ? "all" : "visible")); 389 TRACE2((" before highlite %d.%d .. %d.%d\n", 390 screen->startH.row, 391 screen->startH.col, 392 screen->endH.row, 393 screen->endH.col)); 394 TRACE2((" margins %d..%d\n", screen->top_marg, screen->bot_marg)); 395 TRACE2((" limits %d..%d\n", lo_row, hi_row)); 396 397 if ((left > 0 || right < screen->max_col) && 398 ((screen->startH.row >= lo_row && 399 screen->startH.row - amount <= hi_row) || 400 (screen->endH.row >= lo_row && 401 screen->endH.row - amount <= hi_row))) { 402 /* 403 * This could be improved slightly by excluding the special case where 404 * the selection is on a single line outside left/right margins. 405 */ 406 TRACE2(("deselect because selection overlaps with scrolled partial-line\n")); 407 ScrnDisownSelection(xw); 408 } else if (screen->startH.row >= lo_row 409 && screen->startH.row - amount < lo_row) { 410 /* truncate the selection because its start would move out of region */ 411 if (lo_row + amount <= screen->endH.row) { 412 TRACE2(("truncate selection by changing start %d.%d to %d.%d\n", 413 screen->startH.row, 414 screen->startH.col, 415 lo_row + amount, 416 0)); 417 screen->startH.row = lo_row + amount; 418 screen->startH.col = 0; 419 } else { 420 TRACE2(("deselect because %d.%d .. %d.%d shifted %d is outside margins %d..%d\n", 421 screen->startH.row, 422 screen->startH.col, 423 screen->endH.row, 424 screen->endH.col, 425 -amount, 426 lo_row, 427 hi_row)); 428 ScrnDisownSelection(xw); 429 } 430 } else if (screen->startH.row <= hi_row && screen->endH.row > hi_row) { 431 TRACE2(("deselect because selection straddles top-margin\n")); 432 ScrnDisownSelection(xw); 433 } else if (screen->startH.row < lo_row && screen->endH.row > lo_row) { 434 TRACE2(("deselect because selection straddles bottom-margin\n")); 435 ScrnDisownSelection(xw); 436 } 437 438 TRACE2((" after highlite %d.%d .. %d.%d\n", 439 screen->startH.row, 440 screen->startH.col, 441 screen->endH.row, 442 screen->endH.col)); 443} 444 445/* 446 * This is the same as adjustHiliteOnFwdScroll(), but reversed. In this case, 447 * only the visible lines are affected. 448 */ 449static void 450adjustHiliteOnBakScroll(XtermWidget xw, int amount) 451{ 452 TScreen *screen = TScreenOf(xw); 453 int lo_row = screen->top_marg; 454 int hi_row = screen->bot_marg; 455 456 TRACE2(("adjustSelection BAK %s by %d (%s)\n", 457 screen->whichBuf ? "alternate" : "normal", 458 amount, 459 "visible")); 460 TRACE2((" before highlite %d.%d .. %d.%d\n", 461 screen->startH.row, 462 screen->startH.col, 463 screen->endH.row, 464 screen->endH.col)); 465 TRACE2((" margins %d..%d\n", screen->top_marg, screen->bot_marg)); 466 467 if (screen->endH.row >= hi_row 468 && screen->endH.row + amount > hi_row) { 469 /* truncate the selection because its start would move out of region */ 470 if (hi_row - amount >= screen->startH.row) { 471 TRACE2(("truncate selection by changing start %d.%d to %d.%d\n", 472 screen->startH.row, 473 screen->startH.col, 474 hi_row - amount, 475 0)); 476 screen->endH.row = hi_row - amount; 477 screen->endH.col = 0; 478 } else { 479 TRACE2(("deselect because %d.%d .. %d.%d shifted %d is outside margins %d..%d\n", 480 screen->startH.row, 481 screen->startH.col, 482 screen->endH.row, 483 screen->endH.col, 484 amount, 485 lo_row, 486 hi_row)); 487 ScrnDisownSelection(xw); 488 } 489 } else if (screen->endH.row >= lo_row && screen->startH.row < lo_row) { 490 ScrnDisownSelection(xw); 491 } else if (screen->endH.row > hi_row && screen->startH.row > hi_row) { 492 ScrnDisownSelection(xw); 493 } 494 495 TRACE2((" after highlite %d.%d .. %d.%d\n", 496 screen->startH.row, 497 screen->startH.col, 498 screen->endH.row, 499 screen->endH.col)); 500} 501 502/* 503 * Move cells in LineData's on the current screen to simulate scrolling by the 504 * given amount of lines. 505 */ 506static void 507scrollInMargins(XtermWidget xw, int amount, int top) 508{ 509 TScreen *screen = TScreenOf(xw); 510 LineData *src; 511 LineData *dst; 512 int row; 513 int left = ScrnLeftMargin(xw); 514 int right = ScrnRightMargin(xw); 515 int length = right + 1 - left; 516 517 if_OPT_WIDE_CHARS(screen, { 518 if (amount != 0) { 519 for (row = top; row <= screen->bot_marg; ++row) { 520 LineData *ld; 521 if ((ld = getLineData(screen, row + amount)) != 0) { 522 if (left > 0) { 523 if (ld->charData[left] == HIDDEN_CHAR) { 524 Clear1Cell(ld, left - 1); 525 Clear1Cell(ld, left); 526 } 527 } 528 if (right + 1 < (int) ld->lineSize) { 529 if (ld->charData[right + 1] == HIDDEN_CHAR) { 530 Clear1Cell(ld, right); 531 Clear1Cell(ld, right + 1); 532 } 533 } 534 } 535 } 536 } 537 }); 538 539 if (amount > 0) { 540 for (row = top; row <= screen->bot_marg - amount; ++row) { 541 if ((src = getLineData(screen, row + amount)) != 0 542 && (dst = getLineData(screen, row)) != 0) { 543 CopyCells(screen, src, dst, left, length, False); 544 } 545 } 546 while (row <= screen->bot_marg) { 547 ClearCells(xw, 0, (unsigned) length, row, left); 548 ++row; 549 } 550 } else if (amount < 0) { 551 for (row = screen->bot_marg; row >= top - amount; --row) { 552 if ((src = getLineData(screen, row + amount)) != 0 553 && (dst = getLineData(screen, row)) != 0) { 554 CopyCells(screen, src, dst, left, length, True); 555 } 556 } 557 while (row >= top) { 558 ClearCells(xw, 0, (unsigned) length, row, left); 559 --row; 560 } 561 } 562} 563 564#if OPT_WIDE_CHARS 565/* 566 * If we're repainting a section of wide-characters that, e.g., ClearCells has 567 * repaired when finding double-cell characters, then we should account for 568 * that in the repaint. 569 */ 570static void 571ScrnUpdate2(XtermWidget xw, 572 int toprow, 573 int leftcol, 574 int nrows, 575 int ncols, 576 Bool force) 577{ 578 if_OPT_WIDE_CHARS(TScreenOf(xw), { 579 if (leftcol + ncols <= TScreenOf(xw)->max_col) 580 ncols++; 581 if (leftcol > 0) { 582 leftcol--; 583 ncols++; 584 } 585 }); 586 ScrnUpdate(xw, toprow, leftcol, nrows, ncols, force); 587} 588#else 589#define ScrnUpdate2(xw, toprow, leftcol, nrows, ncols, force) \ 590 ScrnUpdate(xw, toprow, leftcol, nrows, ncols, force) 591#endif 592 593/* 594 * scrolls the screen by amount lines, erases bottom, doesn't alter 595 * cursor position (i.e. cursor moves down amount relative to text). 596 * All done within the scrolling region, of course. 597 * requires: amount > 0 598 */ 599void 600xtermScroll(XtermWidget xw, int amount) 601{ 602 TScreen *screen = TScreenOf(xw); 603 int i; 604 int refreshtop = 0; 605 int refreshheight; 606 Boolean save_wrap = screen->do_wrap; 607 int left = ScrnLeftMargin(xw); 608 int right = ScrnRightMargin(xw); 609 Boolean scroll_all_lines = (Boolean) (screen->scrollWidget 610 && !screen->whichBuf 611 && screen->top_marg == 0); 612 Boolean scroll_full_line = ((left == 0) && (right == screen->max_col)); 613 614 TRACE(("xtermScroll count=%d\n", amount)); 615 616 screen->cursor_busy += 1; 617 screen->cursor_moved = True; 618 619 if (screen->cursor_state) 620 HideCursor(xw); 621 622 i = screen->bot_marg - screen->top_marg + 1; 623 if (amount > i) 624 amount = i; 625 626 if (!scroll_full_line) { 627 refreshheight = 0; 628 } else 629#if OPT_SCROLL_LOCK 630 if (screen->allowScrollLock && screen->scroll_lock) { 631 refreshheight = 0; 632 screen->scroll_amt = 0; 633 screen->refresh_amt = 0; 634 if (--(screen->topline) < -screen->savelines) { 635 screen->topline = -screen->savelines; 636 screen->scroll_dirty = True; 637 } 638 if (++(screen->savedlines) > screen->savelines) { 639 screen->savedlines = screen->savelines; 640 } 641 } else 642#endif 643 { 644 if (ScrnHaveSelection(screen)) 645 adjustHiliteOnFwdScroll(xw, amount, scroll_all_lines); 646 647 if (screen->jumpscroll) { 648 if (screen->scroll_amt > 0) { 649 if (!screen->fastscroll) { 650 if (screen->refresh_amt + amount > i) 651 FlushScroll(xw); 652 } 653 screen->scroll_amt += amount; 654 screen->refresh_amt += amount; 655 } else { 656 if (!screen->fastscroll) { 657 if (screen->scroll_amt < 0) 658 FlushScroll(xw); 659 } 660 screen->scroll_amt = amount; 661 screen->refresh_amt = amount; 662 } 663 refreshheight = 0; 664 } else { 665 int scrolltop; 666 int scrollheight; 667 int shift; 668 int bot; 669 670 ScrollSelection(screen, -(amount), False); 671 if (amount == i) { 672 ClearScreen(xw); 673 goto done; 674 } 675 676 shift = INX2ROW(screen, 0); 677 bot = screen->max_row - shift; 678 scrollheight = i - amount; 679 refreshheight = amount; 680 681 if ((refreshtop = screen->bot_marg - refreshheight + 1 + shift) > 682 (i = screen->max_row - refreshheight + 1)) 683 refreshtop = i; 684 685 if (scroll_all_lines) { 686 scrolltop = 0; 687 if ((scrollheight += shift) > i) 688 scrollheight = i; 689 if ((i = screen->savedlines) < screen->savelines) { 690 if ((i += amount) > screen->savelines) 691 i = screen->savelines; 692 screen->savedlines = i; 693 ScrollBarDrawThumb(xw, 1); 694 } 695 } else { 696 scrolltop = screen->top_marg + shift; 697 if ((i = screen->bot_marg - bot) > 0) { 698 scrollheight -= i; 699 if ((i = screen->top_marg + amount - 1 - bot) >= 0) { 700 refreshtop += i; 701 refreshheight -= i; 702 } 703 } 704 } 705 706 if (screen->multiscroll && amount == 1 && 707 screen->topline == 0 && screen->top_marg == 0 && 708 screen->bot_marg == screen->max_row) { 709 if (screen->incopy < 0 && screen->scrolls == 0) 710 CopyWait(xw); 711 screen->scrolls++; 712 } 713 714 vertical_copy_area(xw, 715 scrolltop + amount, 716 scrollheight, 717 amount, 718 left, 719 right); 720 721 if (refreshheight > 0) { 722 ClearCurBackground(xw, 723 refreshtop, 724 left, 725 (unsigned) refreshheight, 726 (unsigned) (right + 1 - left), 727 (unsigned) FontWidth(screen)); 728 if (refreshheight > shift) 729 refreshheight = shift; 730 } 731 } 732 } 733 734 if (amount > 0) { 735 if (left > 0 || right < screen->max_col) { 736 scrollInMargins(xw, amount, screen->top_marg); 737 ScrnUpdate2(xw, 738 screen->top_marg, 739 left, 740 screen->bot_marg + 1 - screen->top_marg, 741 right + 1 - left, 742 True); 743 } else if (scroll_all_lines) { 744 ScrnDeleteLine(xw, 745 screen->saveBuf_index, 746 screen->bot_marg + screen->savelines, 747 0, 748 (unsigned) amount); 749 } else { 750 ScrnDeleteLine(xw, 751 screen->visbuf, 752 screen->bot_marg, 753 screen->top_marg, 754 (unsigned) amount); 755 } 756 } 757 758 scroll_displayed_graphics(xw, amount); 759 760 if (refreshheight > 0) { 761 ScrnRefresh(xw, 762 refreshtop, 763 left, 764 refreshheight, 765 right + 1 - left, 766 False); 767 } 768 769 done: 770 screen->do_wrap = save_wrap; 771 screen->cursor_busy -= 1; 772 return; 773} 774 775/* 776 * This is from ISO 6429, not found in any of DEC's terminals. 777 */ 778void 779xtermScrollLR(XtermWidget xw, int amount, Bool toLeft) 780{ 781 if (amount > 0) { 782 xtermColScroll(xw, amount, toLeft, ScrnLeftMargin(xw)); 783 } 784} 785 786/* 787 * Implement DECBI/DECFI (back/forward column index) 788 */ 789void 790xtermColIndex(XtermWidget xw, Bool toLeft) 791{ 792 TScreen *screen = TScreenOf(xw); 793 794 if (toLeft) { 795 if (ScrnIsColInMargins(screen, screen->cur_col)) { 796 if (screen->cur_col == ScrnLeftMargin(xw)) { 797 xtermColScroll(xw, 1, False, screen->cur_col); 798 } else { 799 CursorBack(xw, 1); 800 } 801 } else { 802 CursorBack(xw, 1); 803 } 804 } else { 805 if (ScrnIsColInMargins(screen, screen->cur_col)) { 806 if (screen->cur_col == ScrnRightMargin(xw)) { 807 xtermColScroll(xw, 1, True, ScrnLeftMargin(xw)); 808 } else { 809 CursorForward(xw, 1); 810 } 811 } else { 812 CursorForward(xw, 1); 813 } 814 } 815} 816 817/* 818 * Implement DECDC/DECIC (delete/insert column) 819 */ 820void 821xtermColScroll(XtermWidget xw, int amount, Bool toLeft, int at_col) 822{ 823 TScreen *screen = TScreenOf(xw); 824 825 if (amount > 0) { 826 int min_row; 827 int max_row; 828 829 if (ScrnHaveRowMargins(screen)) { 830 min_row = screen->top_marg; 831 max_row = screen->bot_marg; 832 } else { 833 min_row = 0; 834 max_row = screen->max_row; 835 } 836 837 if (screen->cur_row >= min_row 838 && screen->cur_row <= max_row 839 && screen->cur_col >= screen->lft_marg 840 && screen->cur_col <= screen->rgt_marg) { 841 int save_row = screen->cur_row; 842 int save_col = screen->cur_col; 843 int row; 844 845 screen->cur_col = at_col; 846 if (toLeft) { 847 for (row = min_row; row <= max_row; row++) { 848 screen->cur_row = row; 849 ScrnDeleteChar(xw, (unsigned) amount); 850 } 851 } else { 852 for (row = min_row; row <= max_row; row++) { 853 screen->cur_row = row; 854 ScrnInsertChar(xw, (unsigned) amount); 855 } 856 } 857 screen->cur_row = save_row; 858 screen->cur_col = save_col; 859 xtermRepaint(xw); 860 } 861 } 862} 863 864/* 865 * Reverse scrolls the screen by amount lines, erases top, doesn't alter 866 * cursor position (i.e. cursor moves up amount relative to text). 867 * All done within the scrolling region, of course. 868 * Requires: amount > 0 869 */ 870void 871RevScroll(XtermWidget xw, int amount) 872{ 873 TScreen *screen = TScreenOf(xw); 874 int i = screen->bot_marg - screen->top_marg + 1; 875 int left = ScrnLeftMargin(xw); 876 int right = ScrnRightMargin(xw); 877 Boolean scroll_full_line = ((left == 0) && (right == screen->max_col)); 878 879 TRACE(("RevScroll count=%d\n", amount)); 880 881 screen->cursor_busy += 1; 882 screen->cursor_moved = True; 883 884 if (screen->cursor_state) 885 HideCursor(xw); 886 887 if (amount > i) 888 amount = i; 889 890 if (ScrnHaveSelection(screen)) 891 adjustHiliteOnBakScroll(xw, amount); 892 893 if (!scroll_full_line) { 894 ; 895 } else if (screen->jumpscroll) { 896 if (screen->scroll_amt < 0) { 897 if (-screen->refresh_amt + amount > i) 898 FlushScroll(xw); 899 screen->scroll_amt -= amount; 900 screen->refresh_amt -= amount; 901 } else { 902 if (screen->scroll_amt > 0) 903 FlushScroll(xw); 904 screen->scroll_amt = -amount; 905 screen->refresh_amt = -amount; 906 } 907 } else { 908 int shift = INX2ROW(screen, 0); 909 int bot = screen->max_row - shift; 910 int refreshheight = amount; 911 int refreshtop = screen->top_marg + shift; 912 int scrollheight = (screen->bot_marg 913 - screen->top_marg - refreshheight + 1); 914 int scrolltop = refreshtop + refreshheight; 915 916 if ((i = screen->bot_marg - bot) > 0) 917 scrollheight -= i; 918 if ((i = screen->top_marg + refreshheight - 1 - bot) > 0) 919 refreshheight -= i; 920 921 if (screen->multiscroll && amount == 1 && 922 screen->topline == 0 && screen->top_marg == 0 && 923 screen->bot_marg == screen->max_row) { 924 if (screen->incopy < 0 && screen->scrolls == 0) 925 CopyWait(xw); 926 screen->scrolls++; 927 } 928 929 vertical_copy_area(xw, 930 scrolltop - amount, 931 scrollheight, 932 -amount, 933 left, 934 right); 935 936 if (refreshheight > 0) { 937 ClearCurBackground(xw, 938 refreshtop, 939 left, 940 (unsigned) refreshheight, 941 (unsigned) (right + 1 - left), 942 (unsigned) FontWidth(screen)); 943 } 944 } 945 if (amount > 0) { 946 if (left > 0 || right < screen->max_col) { 947 scrollInMargins(xw, -amount, screen->top_marg); 948 ScrnUpdate2(xw, 949 screen->top_marg, 950 left, 951 screen->bot_marg + 1 - screen->top_marg, 952 right + 1 - left, 953 True); 954 } else { 955 ScrnInsertLine(xw, 956 screen->visbuf, 957 screen->bot_marg, 958 screen->top_marg, 959 (unsigned) amount); 960 } 961 } 962 screen->cursor_busy -= 1; 963 return; 964} 965 966#if OPT_ZICONBEEP 967void 968initZIconBeep(void) 969{ 970 if (resource.zIconBeep > 100 || resource.zIconBeep < -100) { 971 resource.zIconBeep = 0; /* was 100, but I prefer to defaulting off. */ 972 xtermWarning("a number between -100 and 100 is required for zIconBeep. 0 used by default\n"); 973 } 974} 975 976static char * 977getIconName(void) 978{ 979 static char *icon_name; 980 static Arg args[] = 981 { 982 {XtNiconName, (XtArgVal) & icon_name} 983 }; 984 985 icon_name = NULL; 986 XtGetValues(toplevel, args, XtNumber(args)); 987 return icon_name; 988} 989 990static void 991setZIconBeep(XtermWidget xw) 992{ 993 TScreen *screen = TScreenOf(xw); 994 995 /* Flag icon name with "***" on window output when iconified. 996 */ 997 if (resource.zIconBeep && mapstate == IsUnmapped && !screen->zIconBeep_flagged) { 998 char *icon_name = getIconName(); 999 if (icon_name != NULL) { 1000 screen->zIconBeep_flagged = True; 1001 ChangeIconName(xw, icon_name); 1002 } 1003 xtermBell(xw, XkbBI_Info, 0); 1004 } 1005 mapstate = -1; 1006} 1007 1008/* 1009 * If warning should be given then give it 1010 */ 1011Boolean 1012showZIconBeep(XtermWidget xw, char *name) 1013{ 1014 Boolean code = False; 1015 1016 if (resource.zIconBeep && TScreenOf(xw)->zIconBeep_flagged) { 1017 char *format = resource.zIconFormat; 1018 char *newname = malloc(strlen(name) + strlen(format) + 2); 1019 if (!newname) { 1020 xtermWarning("malloc failed in showZIconBeep\n"); 1021 } else { 1022 char *marker = strstr(format, "%s"); 1023 char *result = newname; 1024 if (marker != 0) { 1025 size_t skip = (size_t) (marker - format); 1026 if (skip) { 1027 strncpy(result, format, skip); 1028 result += skip; 1029 } 1030 strcpy(result, name); 1031 strcat(result, marker + 2); 1032 } else { 1033 strcpy(result, format); 1034 strcat(result, name); 1035 } 1036 ChangeGroup(xw, XtNiconName, newname); 1037 free(newname); 1038 } 1039 code = True; 1040 } 1041 return code; 1042} 1043 1044/* 1045 * Restore the icon name, resetting the state for zIconBeep. 1046 */ 1047void 1048resetZIconBeep(XtermWidget xw) 1049{ 1050 TScreen *screen = TScreenOf(xw); 1051 1052 if (screen->zIconBeep_flagged) { 1053 char *icon_name = getIconName(); 1054 screen->zIconBeep_flagged = False; 1055 if (icon_name != NULL) { 1056 char *buf = malloc(strlen(icon_name) + 1); 1057 if (buf == NULL) { 1058 screen->zIconBeep_flagged = True; 1059 } else { 1060 char *format = resource.zIconFormat; 1061 char *marker = strstr(format, "%s"); 1062 Boolean found = False; 1063 1064 if (marker != 0) { 1065 if (marker == format 1066 || !strncmp(icon_name, format, (size_t) (marker - format))) { 1067 found = True; 1068 strcpy(buf, icon_name + (marker - format)); 1069 marker += 2; 1070 if (*marker != '\0') { 1071 size_t len_m = strlen(marker); 1072 size_t len_b = strlen(buf); 1073 if (len_m < len_b 1074 && !strcmp(buf + len_b - len_m, marker)) { 1075 buf[len_b - len_m] = '\0'; 1076 } 1077 } 1078 } 1079 } else if (!strncmp(icon_name, format, strlen(format))) { 1080 strcpy(buf, icon_name + strlen(format)); 1081 found = True; 1082 } 1083 if (found) 1084 ChangeIconName(xw, buf); 1085 free(buf); 1086 } 1087 } 1088 } 1089} 1090#else 1091#define setZIconBeep(xw) /* nothing */ 1092#endif /* OPT_ZICONBEEP */ 1093 1094/* 1095 * write a string str of length len onto the screen at 1096 * the current cursor position. update cursor position. 1097 */ 1098void 1099WriteText(XtermWidget xw, IChar *str, Cardinal len) 1100{ 1101 TScreen *screen = TScreenOf(xw); 1102 XTermDraw params; 1103 CLineData *ld = 0; 1104 unsigned attr_flags = xw->flags; 1105 CellColor fg_bg = xtermColorPair(xw); 1106 unsigned cells = visual_width(str, len); 1107 GC currentGC; 1108 1109 TRACE(("WriteText %d (%2d,%2d) %3d:%s\n", 1110 screen->topline, 1111 screen->cur_row, 1112 screen->cur_col, 1113 len, visibleIChars(str, len))); 1114 1115 if (cells + (unsigned) screen->cur_col > (unsigned) MaxCols(screen)) { 1116 cells = (unsigned) (MaxCols(screen) - screen->cur_col); 1117 } 1118 1119 if (ScrnHaveSelection(screen) 1120 && ScrnIsRowInSelection(screen, INX2ROW(screen, screen->cur_row))) { 1121 ScrnDisownSelection(xw); 1122 } 1123#if OPT_ISO_COLORS 1124 /* if colorBDMode is set, and enabled */ 1125 if (screen->colorBDMode && 1126 screen->boldColors && 1127 !hasDirectFG(attr_flags) && 1128 /* and bold foreground color on bold background color */ 1129 GetCellColorFG(fg_bg) > COLOR_7 && 1130 GetCellColorFG(fg_bg) < MIN_ANSI_COLORS && 1131 /* and both colors are the same */ 1132 GetCellColorFG(fg_bg) == GetCellColorBG(fg_bg)) 1133 /* clear BOLD flag, else it will be colorBD on bold background color */ 1134 UIntClr(attr_flags, BOLD); 1135#endif 1136 1137 /* if we are in insert-mode, reserve space for the new cells */ 1138 if (attr_flags & INSERT) { 1139 InsertChar(xw, cells); 1140 } 1141 1142 if (AddToVisible(xw) 1143 && ((ld = getLineData(screen, screen->cur_row))) != 0) { 1144 unsigned test; 1145 1146 if (screen->cursor_state) 1147 HideCursor(xw); 1148 1149 /* 1150 * If we overwrite part of a multi-column character, fill the rest 1151 * of it with blanks. 1152 */ 1153 if_OPT_WIDE_CHARS(screen, { 1154 int kl; 1155 int kr; 1156 if (DamagedCurCells(screen, cells, &kl, &kr)) 1157 ClearInLine(xw, screen->cur_row, kl, (unsigned) (kr - kl + 1)); 1158 }); 1159 1160 if (attr_flags & INVISIBLE) { 1161 Cardinal n; 1162 for (n = 0; n < cells; ++n) 1163 str[n] = ' '; 1164 } 1165 1166 TRACE(("WriteText calling drawXtermText (%d) (%d,%d)\n", 1167 LineCharSet(screen, ld), 1168 screen->cur_row, 1169 screen->cur_col)); 1170 1171 test = attr_flags; 1172#if OPT_ISO_COLORS 1173 { 1174 int fg; 1175 if (screen->colorAttrMode) { 1176 fg = MapToColorMode(xw->cur_foreground, screen, attr_flags); 1177 } else { 1178 fg = xw->cur_foreground; 1179 } 1180 checkVeryBoldColors(test, fg); 1181 } 1182#endif 1183 1184 /* make sure that the correct GC is current */ 1185 currentGC = updatedXtermGC(xw, attr_flags, fg_bg, False); 1186 1187 /* *INDENT-EQLS* */ 1188 params.xw = xw; 1189 params.attr_flags = (test & DRAWX_MASK); 1190 params.draw_flags = 0; 1191 params.this_chrset = LineCharSet(screen, ld); 1192 params.real_chrset = CSET_SWL; 1193 params.on_wide = 0; 1194 1195 drawXtermText(¶ms, 1196 currentGC, 1197 LineCursorX(screen, ld, screen->cur_col), 1198 CursorY(screen, screen->cur_row), 1199 str, len); 1200 1201 resetXtermGC(xw, attr_flags, False); 1202 } 1203 1204 ScrnWriteText(xw, str, attr_flags, fg_bg, len); 1205 CursorForward(xw, (int) cells); 1206 setZIconBeep(xw); 1207 return; 1208} 1209 1210/* 1211 * If cursor not in scrolling region, returns. Else, 1212 * inserts n blank lines at the cursor's position. Lines above the 1213 * bottom margin are lost. 1214 */ 1215void 1216InsertLine(XtermWidget xw, int n) 1217{ 1218 TScreen *screen = TScreenOf(xw); 1219 int i; 1220 int left = ScrnLeftMargin(xw); 1221 int right = ScrnRightMargin(xw); 1222 Boolean scroll_full_line = ((left == 0) && (right == screen->max_col)); 1223 1224 if (!ScrnIsRowInMargins(screen, screen->cur_row) 1225 || screen->cur_col < left 1226 || screen->cur_col > right) 1227 return; 1228 1229 TRACE(("InsertLine count=%d\n", n)); 1230 1231 set_cur_col(screen, ScrnLeftMargin(xw)); 1232 if (screen->cursor_state) 1233 HideCursor(xw); 1234 1235 if (ScrnHaveSelection(screen) 1236 && ScrnAreRowsInSelection(screen, 1237 INX2ROW(screen, screen->top_marg), 1238 INX2ROW(screen, screen->cur_row - 1)) 1239 && ScrnAreRowsInSelection(screen, 1240 INX2ROW(screen, screen->cur_row), 1241 INX2ROW(screen, screen->bot_marg))) { 1242 ScrnDisownSelection(xw); 1243 } 1244 1245 ResetWrap(screen); 1246 if (n > (i = screen->bot_marg - screen->cur_row + 1)) 1247 n = i; 1248 if (screen->jumpscroll && scroll_full_line) { 1249 if (screen->scroll_amt <= 0 && 1250 screen->cur_row <= -screen->refresh_amt) { 1251 if (-screen->refresh_amt + n > MaxRows(screen)) 1252 FlushScroll(xw); 1253 screen->scroll_amt -= n; 1254 screen->refresh_amt -= n; 1255 } else { 1256 if (screen->scroll_amt) 1257 FlushScroll(xw); 1258 } 1259 } 1260 if (!screen->scroll_amt && scroll_full_line) { 1261 int shift = INX2ROW(screen, 0); 1262 int bot = screen->max_row - shift; 1263 int refreshheight = n; 1264 int refreshtop = screen->cur_row + shift; 1265 int scrolltop = refreshtop + refreshheight; 1266 int scrollheight = (screen->bot_marg 1267 - screen->cur_row - refreshheight + 1); 1268 1269 if ((i = screen->bot_marg - bot) > 0) 1270 scrollheight -= i; 1271 if ((i = screen->cur_row + refreshheight - 1 - bot) > 0) 1272 refreshheight -= i; 1273 vertical_copy_area(xw, scrolltop - n, scrollheight, -n, left, right); 1274 if (refreshheight > 0) { 1275 ClearCurBackground(xw, 1276 refreshtop, 1277 left, 1278 (unsigned) refreshheight, 1279 (unsigned) (right + 1 - left), 1280 (unsigned) FontWidth(screen)); 1281 } 1282 } 1283 if (n > 0) { 1284 if (scroll_full_line) { 1285 ScrnInsertLine(xw, 1286 screen->visbuf, 1287 screen->bot_marg, 1288 screen->cur_row, 1289 (unsigned) n); 1290 } else { 1291 scrollInMargins(xw, -n, screen->cur_row); 1292 ScrnUpdate2(xw, 1293 screen->cur_row, 1294 left, 1295 screen->bot_marg + 1 - screen->cur_row, 1296 right + 1 - left, 1297 True); 1298 } 1299 } 1300} 1301 1302/* 1303 * If cursor not in scrolling region, returns. Else, deletes n lines 1304 * at the cursor's position, lines added at bottom margin are blank. 1305 */ 1306void 1307DeleteLine(XtermWidget xw, int n) 1308{ 1309 TScreen *screen = TScreenOf(xw); 1310 int i; 1311 int left = ScrnLeftMargin(xw); 1312 int right = ScrnRightMargin(xw); 1313 Boolean scroll_all_lines = (Boolean) (screen->scrollWidget 1314 && !screen->whichBuf 1315 && screen->cur_row == 0); 1316 Boolean scroll_full_line = ((left == 0) && (right == screen->max_col)); 1317 1318 if (!ScrnIsRowInMargins(screen, screen->cur_row) || 1319 !ScrnIsColInMargins(screen, screen->cur_col)) 1320 return; 1321 1322 TRACE(("DeleteLine count=%d\n", n)); 1323 1324 set_cur_col(screen, ScrnLeftMargin(xw)); 1325 if (screen->cursor_state) 1326 HideCursor(xw); 1327 1328 if (n > (i = screen->bot_marg - screen->cur_row + 1)) { 1329 n = i; 1330 } 1331 if (ScrnHaveSelection(screen) 1332 && ScrnAreRowsInSelection(screen, 1333 INX2ROW(screen, screen->cur_row), 1334 INX2ROW(screen, screen->cur_row + n - 1))) { 1335 ScrnDisownSelection(xw); 1336 } 1337 1338 ResetWrap(screen); 1339 if (screen->jumpscroll && scroll_full_line) { 1340 if (screen->scroll_amt >= 0 && screen->cur_row == screen->top_marg) { 1341 if (screen->refresh_amt + n > MaxRows(screen)) 1342 FlushScroll(xw); 1343 screen->scroll_amt += n; 1344 screen->refresh_amt += n; 1345 } else { 1346 if (screen->scroll_amt) 1347 FlushScroll(xw); 1348 } 1349 } 1350 1351 /* adjust screen->buf */ 1352 if (n > 0) { 1353 if (left > 0 || right < screen->max_col) { 1354 scrollInMargins(xw, n, screen->cur_row); 1355 } else if (scroll_all_lines) { 1356 ScrnDeleteLine(xw, 1357 screen->saveBuf_index, 1358 screen->bot_marg + screen->savelines, 1359 0, 1360 (unsigned) n); 1361 } else { 1362 ScrnDeleteLine(xw, 1363 screen->visbuf, 1364 screen->bot_marg, 1365 screen->cur_row, 1366 (unsigned) n); 1367 } 1368 } 1369 1370 /* repaint the screen, as needed */ 1371 if (!scroll_full_line) { 1372 ScrnUpdate2(xw, 1373 screen->cur_row, 1374 left, 1375 screen->bot_marg + 1 - screen->cur_row, 1376 right + 1 - left, 1377 True); 1378 } else if (!screen->scroll_amt) { 1379 int shift = INX2ROW(screen, 0); 1380 int bot = screen->max_row - shift; 1381 int refreshtop; 1382 int refreshheight = n; 1383 int scrolltop; 1384 int scrollheight = i - n; 1385 1386 if ((refreshtop = screen->bot_marg - refreshheight + 1 + shift) > 1387 (i = screen->max_row - refreshheight + 1)) 1388 refreshtop = i; 1389 if (scroll_all_lines) { 1390 scrolltop = 0; 1391 if ((scrollheight += shift) > i) 1392 scrollheight = i; 1393 if ((i = screen->savedlines) < screen->savelines) { 1394 if ((i += n) > screen->savelines) 1395 i = screen->savelines; 1396 screen->savedlines = i; 1397 ScrollBarDrawThumb(xw, 1); 1398 } 1399 } else { 1400 scrolltop = screen->cur_row + shift; 1401 if ((i = screen->bot_marg - bot) > 0) { 1402 scrollheight -= i; 1403 if ((i = screen->cur_row + n - 1 - bot) >= 0) { 1404 refreshheight -= i; 1405 } 1406 } 1407 } 1408 vertical_copy_area(xw, scrolltop + n, scrollheight, n, left, right); 1409 if (shift > 0 && refreshheight > 0) { 1410 int rows = refreshheight; 1411 if (rows > shift) 1412 rows = shift; 1413 ScrnUpdate(xw, refreshtop, 0, rows, MaxCols(screen), True); 1414 refreshtop += shift; 1415 refreshheight -= shift; 1416 } 1417 if (refreshheight > 0) { 1418 ClearCurBackground(xw, 1419 refreshtop, 1420 left, 1421 (unsigned) refreshheight, 1422 (unsigned) (right + 1 - left), 1423 (unsigned) FontWidth(screen)); 1424 } 1425 } 1426} 1427 1428/* 1429 * Insert n blanks at the cursor's position, no wraparound 1430 */ 1431void 1432InsertChar(XtermWidget xw, unsigned n) 1433{ 1434 TScreen *screen = TScreenOf(xw); 1435 CLineData *ld; 1436 unsigned limit; 1437 int row = INX2ROW(screen, screen->cur_row); 1438 int left = ScrnLeftMargin(xw); 1439 int right = ScrnRightMargin(xw); 1440 1441 if (screen->cursor_state) 1442 HideCursor(xw); 1443 1444 TRACE(("InsertChar count=%d\n", n)); 1445 1446 if (ScrnHaveSelection(screen) 1447 && ScrnIsRowInSelection(screen, row)) { 1448 ScrnDisownSelection(xw); 1449 } 1450 ResetWrap(screen); 1451 1452 limit = (unsigned) (right + 1 - screen->cur_col); 1453 1454 if (n > limit) 1455 n = limit; 1456 1457 if (screen->cur_col < left || screen->cur_col > right) { 1458 n = 0; 1459 } else if (AddToVisible(xw) 1460 && (ld = getLineData(screen, screen->cur_row)) != 0) { 1461 int col = right + 1 - (int) n; 1462 1463 /* 1464 * If we shift part of a multi-column character, fill the rest 1465 * of it with blanks. Do similar repair for the text which will 1466 * be shifted into the right-margin. 1467 */ 1468 if_OPT_WIDE_CHARS(screen, { 1469 int kl; 1470 int kr = screen->cur_col; 1471 if (DamagedCurCells(screen, n, &kl, (int *) 0) && kr > kl) { 1472 ClearInLine(xw, screen->cur_row, kl, (unsigned) (kr - kl + 1)); 1473 } 1474 kr = screen->max_col - (int) n + 1; 1475 if (DamagedCells(screen, n, &kl, (int *) 0, 1476 screen->cur_row, 1477 kr) && kr > kl) { 1478 ClearInLine(xw, screen->cur_row, kl, (unsigned) (kr - kl + 1)); 1479 } 1480 }); 1481 1482#if OPT_DEC_CHRSET 1483 if (CSET_DOUBLE(GetLineDblCS(ld))) { 1484 col = MaxCols(screen) / 2 - (int) n; 1485 } 1486#endif 1487 /* 1488 * prevent InsertChar from shifting the end of a line over 1489 * if it is being appended to 1490 */ 1491 if (non_blank_line(screen, screen->cur_row, 1492 screen->cur_col, MaxCols(screen))) { 1493 horizontal_copy_area(xw, screen->cur_col, 1494 col - screen->cur_col, 1495 (int) n); 1496 } 1497 1498 ClearCurBackground(xw, 1499 INX2ROW(screen, screen->cur_row), 1500 screen->cur_col, 1501 1U, 1502 n, 1503 (unsigned) LineFontWidth(screen, ld)); 1504 } 1505 if (n != 0) { 1506 /* adjust screen->buf */ 1507 ScrnInsertChar(xw, n); 1508 } 1509} 1510 1511/* 1512 * Deletes n chars at the cursor's position, no wraparound. 1513 */ 1514void 1515DeleteChar(XtermWidget xw, unsigned n) 1516{ 1517 TScreen *screen = TScreenOf(xw); 1518 CLineData *ld; 1519 unsigned limit; 1520 int row = INX2ROW(screen, screen->cur_row); 1521 int right = ScrnRightMargin(xw); 1522 1523 if (screen->cursor_state) 1524 HideCursor(xw); 1525 1526 if (!ScrnIsColInMargins(screen, screen->cur_col)) 1527 return; 1528 1529 TRACE(("DeleteChar count=%d\n", n)); 1530 1531 if (ScrnHaveSelection(screen) 1532 && ScrnIsRowInSelection(screen, row)) { 1533 ScrnDisownSelection(xw); 1534 } 1535 ResetWrap(screen); 1536 1537 limit = (unsigned) (right + 1 - screen->cur_col); 1538 1539 if (n > limit) 1540 n = limit; 1541 1542 if (AddToVisible(xw) 1543 && (ld = getLineData(screen, screen->cur_row)) != 0) { 1544 int col = right + 1 - (int) n; 1545 1546 /* 1547 * If we delete part of a multi-column character, fill the rest 1548 * of it with blanks. 1549 */ 1550 if_OPT_WIDE_CHARS(screen, { 1551 int kl; 1552 int kr; 1553 if (DamagedCurCells(screen, n, &kl, &kr)) 1554 ClearInLine(xw, screen->cur_row, kl, (unsigned) (kr - kl + 1)); 1555 }); 1556 1557#if OPT_DEC_CHRSET 1558 if (CSET_DOUBLE(GetLineDblCS(ld))) { 1559 col = MaxCols(screen) / 2 - (int) n; 1560 } 1561#endif 1562 horizontal_copy_area(xw, 1563 (screen->cur_col + (int) n), 1564 col - screen->cur_col, 1565 -((int) n)); 1566 1567 ClearCurBackground(xw, 1568 INX2ROW(screen, screen->cur_row), 1569 col, 1570 1U, 1571 n, 1572 (unsigned) LineFontWidth(screen, ld)); 1573 } 1574 if (n != 0) { 1575 /* adjust screen->buf */ 1576 ScrnDeleteChar(xw, n); 1577 } 1578} 1579 1580/* 1581 * Clear from cursor position to beginning of display, inclusive. 1582 */ 1583static void 1584ClearAbove(XtermWidget xw) 1585{ 1586 TScreen *screen = TScreenOf(xw); 1587 1588 if (screen->protected_mode != OFF_PROTECT) { 1589 int row; 1590 unsigned len = (unsigned) MaxCols(screen); 1591 1592 assert(screen->max_col >= 0); 1593 for (row = 0; row < screen->cur_row; row++) 1594 ClearInLine(xw, row, 0, len); 1595 ClearInLine(xw, screen->cur_row, 0, (unsigned) screen->cur_col); 1596 } else { 1597 int top; 1598 1599 if (screen->cursor_state) 1600 HideCursor(xw); 1601 if ((top = INX2ROW(screen, 0)) <= screen->max_row) { 1602 int height; 1603 1604 if (screen->scroll_amt) 1605 FlushScroll(xw); 1606 if ((height = screen->cur_row + top) > screen->max_row) 1607 height = screen->max_row + 1; 1608 if ((height -= top) > 0) { 1609 chararea_clear_displayed_graphics(screen, 1610 0, 1611 top, 1612 MaxCols(screen), 1613 height); 1614 1615 ClearCurBackground(xw, 1616 top, 1617 0, 1618 (unsigned) height, 1619 (unsigned) MaxCols(screen), 1620 (unsigned) FontWidth(screen)); 1621 } 1622 } 1623 ClearBufRows(xw, 0, screen->cur_row - 1); 1624 } 1625 1626 ClearLeft(xw); 1627} 1628 1629/* 1630 * Clear from cursor position to end of display, inclusive. 1631 */ 1632static void 1633ClearBelow(XtermWidget xw) 1634{ 1635 TScreen *screen = TScreenOf(xw); 1636 1637 ClearRight(xw, -1); 1638 1639 if (screen->protected_mode != OFF_PROTECT) { 1640 int row; 1641 unsigned len = (unsigned) MaxCols(screen); 1642 1643 assert(screen->max_col >= 0); 1644 for (row = screen->cur_row + 1; row <= screen->max_row; row++) 1645 ClearInLine(xw, row, 0, len); 1646 } else { 1647 int top; 1648 1649 if ((top = INX2ROW(screen, screen->cur_row)) <= screen->max_row) { 1650 if (screen->scroll_amt) 1651 FlushScroll(xw); 1652 if (++top <= screen->max_row) { 1653 chararea_clear_displayed_graphics(screen, 1654 0, 1655 top, 1656 MaxCols(screen), 1657 (screen->max_row - top + 1)); 1658 ClearCurBackground(xw, 1659 top, 1660 0, 1661 (unsigned) (screen->max_row - top + 1), 1662 (unsigned) MaxCols(screen), 1663 (unsigned) FontWidth(screen)); 1664 } 1665 } 1666 ClearBufRows(xw, screen->cur_row + 1, screen->max_row); 1667 } 1668} 1669 1670/* 1671 * Clear the given row, for the given range of columns, returning 1 if no 1672 * protected characters were found, 0 otherwise. 1673 */ 1674static int 1675ClearInLine2(XtermWidget xw, int flags, int row, int col, unsigned len) 1676{ 1677 TScreen *screen = TScreenOf(xw); 1678 CLineData *ld; 1679 int rc = 1; 1680 1681 TRACE(("ClearInLine(row=%d, col=%d, len=%d) vs %d..%d\n", 1682 row, col, len, 1683 screen->startH.row, 1684 screen->startH.col)); 1685 1686 if (ScrnHaveSelection(screen) 1687 && ScrnIsRowInSelection(screen, row)) { 1688 ScrnDisownSelection(xw); 1689 } 1690 1691 if (col + (int) len >= MaxCols(screen)) { 1692 len = (unsigned) (MaxCols(screen) - col); 1693 } 1694 1695 /* If we've marked protected text on the screen, we'll have to 1696 * check each time we do an erase. 1697 */ 1698 if (screen->protected_mode != OFF_PROTECT) { 1699 unsigned n; 1700 IAttr *attrs = getLineData(screen, row)->attribs + col; 1701 int saved_mode = screen->protected_mode; 1702 Bool done; 1703 1704 /* disable this branch during recursion */ 1705 screen->protected_mode = OFF_PROTECT; 1706 1707 do { 1708 done = True; 1709 for (n = 0; n < len; n++) { 1710 if (attrs[n] & PROTECTED) { 1711 rc = 0; /* found a protected segment */ 1712 if (n != 0) { 1713 ClearInLine(xw, row, col, n); 1714 } 1715 while ((n < len) 1716 && (attrs[n] & PROTECTED)) { 1717 n++; 1718 } 1719 done = False; 1720 break; 1721 } 1722 } 1723 /* setup for another segment, past the protected text */ 1724 if (!done) { 1725 attrs += n; 1726 col += (int) n; 1727 len -= n; 1728 } 1729 } while (!done); 1730 1731 screen->protected_mode = saved_mode; 1732 if ((int) len <= 0) { 1733 return 0; 1734 } 1735 } 1736 /* fall through to the final non-protected segment */ 1737 1738 if (screen->cursor_state) 1739 HideCursor(xw); 1740 ResetWrap(screen); 1741 1742 if (AddToVisible(xw) 1743 && (ld = getLineData(screen, row)) != 0) { 1744 1745 ClearCurBackground(xw, 1746 INX2ROW(screen, row), 1747 col, 1748 1U, 1749 len, 1750 (unsigned) LineFontWidth(screen, ld)); 1751 } 1752 1753 if (len != 0) { 1754 ClearCells(xw, flags, len, row, col); 1755 } 1756 1757 return rc; 1758} 1759 1760int 1761ClearInLine(XtermWidget xw, int row, int col, unsigned len) 1762{ 1763 TScreen *screen = TScreenOf(xw); 1764 int flags = 0; 1765 1766 /* 1767 * If we're clearing to the end of the line, we won't count this as 1768 * "drawn" characters. We'll only do cut/paste on "drawn" characters, 1769 * so this has the effect of suppressing trailing blanks from a 1770 * selection. 1771 */ 1772 if (col + (int) len < MaxCols(screen)) { 1773 flags |= CHARDRAWN; 1774 } 1775 return ClearInLine2(xw, flags, row, col, len); 1776} 1777 1778/* 1779 * Clear the next n characters on the cursor's line, including the cursor's 1780 * position. 1781 */ 1782void 1783ClearRight(XtermWidget xw, int n) 1784{ 1785 TScreen *screen = TScreenOf(xw); 1786 LineData *ld; 1787 unsigned len = (unsigned) (MaxCols(screen) - screen->cur_col); 1788 1789 assert(screen->max_col >= 0); 1790 assert(screen->max_col >= screen->cur_col); 1791 1792 if (n < 0) /* the remainder of the line */ 1793 n = MaxCols(screen); 1794 if (n == 0) /* default for 'ECH' */ 1795 n = 1; 1796 1797 if (len > (unsigned) n) 1798 len = (unsigned) n; 1799 1800 ld = getLineData(screen, screen->cur_row); 1801 if (AddToVisible(xw)) { 1802 if_OPT_WIDE_CHARS(screen, { 1803 int col = screen->cur_col; 1804 int row = screen->cur_row; 1805 int kl; 1806 int kr; 1807 if (DamagedCurCells(screen, len, &kl, &kr) && kr >= kl) { 1808 int xx = col; 1809 if (kl < xx) { 1810 ClearInLine2(xw, 0, row, kl, (unsigned) (xx - kl)); 1811 } 1812 xx = col + (int) len - 1; 1813 if (kr > xx) { 1814 ClearInLine2(xw, 0, row, xx + 1, (unsigned) (kr - xx)); 1815 } 1816 } 1817 }); 1818 (void) ClearInLine(xw, screen->cur_row, screen->cur_col, len); 1819 } else { 1820 ScrnClearCells(xw, screen->cur_row, screen->cur_col, len); 1821 } 1822 1823 /* with the right part cleared, we can't be wrapping */ 1824 LineClrWrapped(ld); 1825 ShowWrapMarks(xw, screen->cur_row, ld); 1826 ResetWrap(screen); 1827} 1828 1829/* 1830 * Clear first part of cursor's line, inclusive. 1831 */ 1832static void 1833ClearLeft(XtermWidget xw) 1834{ 1835 TScreen *screen = TScreenOf(xw); 1836 unsigned len = (unsigned) screen->cur_col + 1; 1837 1838 assert(screen->cur_col >= 0); 1839 if (AddToVisible(xw)) { 1840 if_OPT_WIDE_CHARS(screen, { 1841 int row = screen->cur_row; 1842 int kl; 1843 int kr; 1844 if (DamagedCurCells(screen, 1, &kl, &kr) && kr >= kl) { 1845 ClearInLine2(xw, 0, row, kl, (unsigned) (kr - kl + 1)); 1846 } 1847 }); 1848 (void) ClearInLine(xw, screen->cur_row, 0, len); 1849 } else { 1850 ScrnClearCells(xw, screen->cur_row, 0, len); 1851 } 1852} 1853 1854/* 1855 * Erase the cursor's line. 1856 */ 1857static void 1858ClearLine(XtermWidget xw) 1859{ 1860 TScreen *screen = TScreenOf(xw); 1861 unsigned len = (unsigned) MaxCols(screen); 1862 1863 assert(screen->max_col >= 0); 1864 (void) ClearInLine(xw, screen->cur_row, 0, len); 1865} 1866 1867void 1868ClearScreen(XtermWidget xw) 1869{ 1870 TScreen *screen = TScreenOf(xw); 1871 int top; 1872 1873 TRACE(("ClearScreen\n")); 1874 1875 if (screen->cursor_state) 1876 HideCursor(xw); 1877 1878 ScrnDisownSelection(xw); 1879 ResetWrap(screen); 1880 if ((top = INX2ROW(screen, 0)) <= screen->max_row) { 1881 if (screen->scroll_amt) 1882 FlushScroll(xw); 1883 chararea_clear_displayed_graphics(screen, 1884 0, 1885 top, 1886 MaxCols(screen), 1887 (screen->max_row - top + 1)); 1888 ClearCurBackground(xw, 1889 top, 1890 0, 1891 (unsigned) (screen->max_row - top + 1), 1892 (unsigned) MaxCols(screen), 1893 (unsigned) FontWidth(screen)); 1894 } 1895 ClearBufRows(xw, 0, screen->max_row); 1896} 1897 1898/* 1899 * If we've written protected text DEC-style, and are issuing a non-DEC 1900 * erase, temporarily reset the protected_mode flag so that the erase will 1901 * ignore the protected flags. 1902 */ 1903void 1904do_erase_char(XtermWidget xw, int param, int mode) 1905{ 1906 TScreen *screen = TScreenOf(xw); 1907 int saved_mode = screen->protected_mode; 1908 1909 if (saved_mode == DEC_PROTECT 1910 && saved_mode != mode) { 1911 screen->protected_mode = OFF_PROTECT; 1912 } 1913 1914 ClearRight(xw, param); 1915 screen->protected_mode = saved_mode; 1916} 1917 1918void 1919do_erase_line(XtermWidget xw, int param, int mode) 1920{ 1921 TScreen *screen = TScreenOf(xw); 1922 int saved_mode = screen->protected_mode; 1923 1924 if (saved_mode == DEC_PROTECT 1925 && saved_mode != mode) { 1926 screen->protected_mode = OFF_PROTECT; 1927 } 1928 1929 switch (param) { 1930 case -1: /* DEFAULT */ 1931 case 0: 1932 ClearRight(xw, -1); 1933 break; 1934 case 1: 1935 ClearLeft(xw); 1936 break; 1937 case 2: 1938 ClearLine(xw); 1939 break; 1940 } 1941 screen->protected_mode = saved_mode; 1942} 1943 1944/* 1945 * Just like 'do_erase_line()', except that this intercepts ED controls. If we 1946 * clear the whole screen, we'll get the return-value from ClearInLine, and 1947 * find if there were any protected characters left. If not, reset the 1948 * protected mode flag in the screen data (it's slower). 1949 */ 1950void 1951do_erase_display(XtermWidget xw, int param, int mode) 1952{ 1953 TScreen *screen = TScreenOf(xw); 1954 int saved_mode = screen->protected_mode; 1955 1956 if (saved_mode == DEC_PROTECT 1957 && saved_mode != mode) 1958 screen->protected_mode = OFF_PROTECT; 1959 1960 switch (param) { 1961 case -1: /* DEFAULT */ 1962 case 0: 1963 if (screen->cur_row == 0 1964 && screen->cur_col == 0) { 1965 screen->protected_mode = saved_mode; 1966 do_erase_display(xw, 2, mode); 1967 saved_mode = screen->protected_mode; 1968 } else 1969 ClearBelow(xw); 1970 break; 1971 1972 case 1: 1973 if (screen->cur_row == screen->max_row 1974 && screen->cur_col == screen->max_col) { 1975 screen->protected_mode = saved_mode; 1976 do_erase_display(xw, 2, mode); 1977 saved_mode = screen->protected_mode; 1978 } else 1979 ClearAbove(xw); 1980 break; 1981 1982 case 2: 1983 /* 1984 * We use 'ClearScreen()' throughout the remainder of the 1985 * program for places where we don't care if the characters are 1986 * protected or not. So we modify the logic around this call 1987 * on 'ClearScreen()' to handle protected characters. 1988 */ 1989 if (screen->protected_mode != OFF_PROTECT) { 1990 int row; 1991 int rc = 1; 1992 unsigned len = (unsigned) MaxCols(screen); 1993 1994 assert(screen->max_col >= 0); 1995 for (row = 0; row <= screen->max_row; row++) 1996 rc &= ClearInLine(xw, row, 0, len); 1997 if (rc != 0) 1998 saved_mode = OFF_PROTECT; 1999 } else { 2000 ClearScreen(xw); 2001 } 2002 break; 2003 2004 case 3: 2005 /* xterm addition - erase saved lines. */ 2006 if (screen->eraseSavedLines) { 2007 screen->savedlines = 0; 2008 ScrollBarDrawThumb(xw, 1); 2009 } 2010 break; 2011 } 2012 screen->protected_mode = saved_mode; 2013} 2014 2015static Boolean 2016screen_has_data(XtermWidget xw) 2017{ 2018 TScreen *screen = TScreenOf(xw); 2019 Boolean result = False; 2020 int row; 2021 2022 for (row = 0; row < screen->max_row; ++row) { 2023 CLineData *ld; 2024 2025 if ((ld = getLineData(screen, row)) != 0) { 2026 int col; 2027 2028 for (col = 0; col < screen->max_col; ++col) { 2029 if (ld->attribs[col] & CHARDRAWN) { 2030 result = True; 2031 break; 2032 } 2033 } 2034 } 2035 if (result) 2036 break; 2037 } 2038 return result; 2039} 2040 2041/* 2042 * Like tiXtraScroll, perform a scroll up of the page contents. In this case, 2043 * it happens for the special case when erasing the whole display starting from 2044 * the upper-left corner of the screen. 2045 */ 2046void 2047do_cd_xtra_scroll(XtermWidget xw) 2048{ 2049 TScreen *screen = TScreenOf(xw); 2050 2051 if (xw->misc.cdXtraScroll 2052 && screen->cur_col == 0 2053 && screen->cur_row == 0 2054 && screen_has_data(xw)) { 2055 xtermScroll(xw, screen->max_row); 2056 } 2057} 2058 2059/* 2060 * Scroll the page up (saving it). This is called when doing terminal 2061 * initialization (ti) or exiting from that (te). 2062 */ 2063void 2064do_ti_xtra_scroll(XtermWidget xw) 2065{ 2066 TScreen *screen = TScreenOf(xw); 2067 2068 if (xw->misc.tiXtraScroll) { 2069 xtermScroll(xw, screen->max_row); 2070 } 2071} 2072 2073static void 2074CopyWait(XtermWidget xw) 2075{ 2076 TScreen *screen = TScreenOf(xw); 2077 XEvent reply; 2078 XEvent *rep = &reply; 2079#ifndef NO_ACTIVE_ICON 2080 int retries = 0; 2081#endif 2082 2083#if USE_DOUBLE_BUFFER 2084 if (resource.buffered) 2085 return; 2086#endif 2087 2088 for (;;) { 2089#ifndef NO_ACTIVE_ICON 2090 if (xw->work.active_icon != eiFalse) { 2091 /* 2092 * The XWindowEvent call blocks until an event is available. That 2093 * can hang when using active-icon and iconifying/deiconifying 2094 * while the terminal is receiving lots of output. Checking with 2095 * this call on the other hand may lose exposure events which 2096 * arrive too late. As a compromise, try several times with a 2097 * time-delay before assuming no more events are available. 2098 */ 2099 if (XCheckWindowEvent(screen->display, 2100 VWindow(screen), 2101 ExposureMask, 2102 &reply)) { 2103 retries = 0; 2104 } else { 2105 if (++retries >= 1000) 2106 return; 2107 usleep(100U); /* wait 0.1msec */ 2108 continue; 2109 } 2110 } else 2111#endif 2112 XWindowEvent(screen->display, VWindow(screen), ExposureMask, &reply); 2113 switch (reply.type) { 2114 case Expose: 2115 HandleExposure(xw, &reply); 2116 break; 2117 case NoExpose: 2118 case GraphicsExpose: 2119 if (screen->incopy <= 0) { 2120 screen->incopy = 1; 2121 if (screen->scrolls > 0) 2122 screen->scrolls--; 2123 } 2124 if (reply.type == GraphicsExpose) 2125 HandleExposure(xw, &reply); 2126 2127 if ((reply.type == NoExpose) || 2128 ((XExposeEvent *) rep)->count == 0) { 2129 if (screen->incopy <= 0 && screen->scrolls > 0) 2130 screen->scrolls--; 2131 if (screen->scrolls == 0) { 2132 screen->incopy = 0; 2133 return; 2134 } 2135 screen->incopy = -1; 2136 } 2137 break; 2138 } 2139 } 2140} 2141 2142/* 2143 * used by vertical_copy_area and and horizontal_copy_area 2144 */ 2145static void 2146copy_area(XtermWidget xw, 2147 int src_x, 2148 int src_y, 2149 unsigned width, 2150 unsigned height, 2151 int dest_x, 2152 int dest_y) 2153{ 2154 TScreen *screen = TScreenOf(xw); 2155 2156 if (width != 0 && height != 0) { 2157 /* wait for previous CopyArea to complete unless 2158 multiscroll is enabled and active */ 2159 if (screen->incopy && screen->scrolls == 0) 2160 CopyWait(xw); 2161 screen->incopy = -1; 2162 2163 /* save for translating Expose events */ 2164 screen->copy_src_x = src_x; 2165 screen->copy_src_y = src_y; 2166 screen->copy_width = width; 2167 screen->copy_height = height; 2168 screen->copy_dest_x = dest_x; 2169 screen->copy_dest_y = dest_y; 2170 2171 XCopyArea(screen->display, 2172 VDrawable(screen), VDrawable(screen), 2173 NormalGC(xw, screen), 2174 src_x, src_y, width, height, dest_x, dest_y); 2175 } 2176} 2177 2178/* 2179 * use when inserting or deleting characters on the current line 2180 */ 2181static void 2182horizontal_copy_area(XtermWidget xw, 2183 int firstchar, /* char pos on screen to start copying at */ 2184 int nchars, 2185 int amount) /* number of characters to move right */ 2186{ 2187 TScreen *screen = TScreenOf(xw); 2188 CLineData *ld; 2189 2190 if ((ld = getLineData(screen, screen->cur_row)) != 0) { 2191 int src_x = LineCursorX(screen, ld, firstchar); 2192 int src_y = CursorY(screen, screen->cur_row); 2193 2194 copy_area(xw, src_x, src_y, 2195 (unsigned) (nchars * LineFontWidth(screen, ld)), 2196 (unsigned) FontHeight(screen), 2197 src_x + amount * LineFontWidth(screen, ld), src_y); 2198 } 2199} 2200 2201/* 2202 * use when inserting or deleting lines from the screen 2203 */ 2204static void 2205vertical_copy_area(XtermWidget xw, 2206 int firstline, /* line on screen to start copying at */ 2207 int nlines, 2208 int amount, /* number of lines to move up (neg=down) */ 2209 int left, 2210 int right) 2211{ 2212 TScreen *screen = TScreenOf(xw); 2213 2214 TRACE(("vertical_copy_area - firstline=%d nlines=%d left=%d right=%d amount=%d\n", 2215 firstline, nlines, left, right, amount)); 2216 2217 if (nlines > 0) { 2218 int src_x = CursorX(screen, left); 2219 int src_y = firstline * FontHeight(screen) + screen->border; 2220 unsigned int w = (unsigned) ((right + 1 - left) * FontWidth(screen)); 2221 unsigned int h = (unsigned) (nlines * FontHeight(screen)); 2222 int dst_x = src_x; 2223 int dst_y = src_y - amount * FontHeight(screen); 2224 2225 copy_area(xw, src_x, src_y, w, h, dst_x, dst_y); 2226 2227 if (screen->show_wrap_marks) { 2228 int row; 2229 int first = firstline - amount; 2230 int last = firstline + nlines + amount; 2231 2232 for (row = first; row < last; ++row) { 2233 CLineData *ld; 2234 int mapped = amount + row + screen->topline; 2235 2236 if ((ld = getLineData(screen, mapped)) != 0) { 2237 ShowWrapMarks(xw, row, ld); 2238 } 2239 } 2240 } 2241 } 2242} 2243 2244/* 2245 * use when scrolling the entire screen 2246 */ 2247void 2248scrolling_copy_area(XtermWidget xw, 2249 int firstline, /* line on screen to start copying at */ 2250 int nlines, 2251 int amount) /* number of lines to move up (neg=down) */ 2252{ 2253 2254 if (nlines > 0) { 2255 vertical_copy_area(xw, firstline, nlines, amount, 0, TScreenOf(xw)->max_col); 2256 } 2257} 2258 2259/* 2260 * Handler for Expose events on the VT widget. 2261 * Returns 1 iff the area where the cursor was got refreshed. 2262 */ 2263int 2264HandleExposure(XtermWidget xw, XEvent *event) 2265{ 2266 TScreen *screen = TScreenOf(xw); 2267 XExposeEvent *reply = (XExposeEvent *) event; 2268 2269#ifndef NO_ACTIVE_ICON 2270 if (reply->window == screen->iconVwin.window) { 2271 WhichVWin(screen) = &screen->iconVwin; 2272 TRACE(("HandleExposure - icon\n")); 2273 } else { 2274 WhichVWin(screen) = &screen->fullVwin; 2275 TRACE(("HandleExposure - normal\n")); 2276 } 2277 TRACE((" event %d,%d %dx%d\n", 2278 reply->y, 2279 reply->x, 2280 reply->height, 2281 reply->width)); 2282#endif /* NO_ACTIVE_ICON */ 2283 2284 /* if not doing CopyArea or if this is a GraphicsExpose, don't translate */ 2285 if (!screen->incopy || event->type != Expose) { 2286 return handle_translated_exposure(xw, reply->x, reply->y, 2287 reply->width, 2288 reply->height); 2289 } else { 2290 /* compute intersection of area being copied with 2291 area being exposed. */ 2292 int both_x1 = Max(screen->copy_src_x, reply->x); 2293 int both_y1 = Max(screen->copy_src_y, reply->y); 2294 int both_x2 = Min(screen->copy_src_x + (int) screen->copy_width, 2295 (reply->x + (int) reply->width)); 2296 int both_y2 = Min(screen->copy_src_y + (int) screen->copy_height, 2297 (reply->y + (int) reply->height)); 2298 int value = 0; 2299 2300 /* was anything copied affected? */ 2301 if (both_x2 > both_x1 && both_y2 > both_y1) { 2302 /* do the copied area */ 2303 value = handle_translated_exposure 2304 (xw, reply->x + screen->copy_dest_x - screen->copy_src_x, 2305 reply->y + screen->copy_dest_y - screen->copy_src_y, 2306 reply->width, reply->height); 2307 } 2308 /* was anything not copied affected? */ 2309 if (reply->x < both_x1 || reply->y < both_y1 2310 || reply->x + reply->width > both_x2 2311 || reply->y + reply->height > both_y2) 2312 value = handle_translated_exposure(xw, reply->x, reply->y, 2313 reply->width, reply->height); 2314 2315 return value; 2316 } 2317} 2318 2319static void 2320set_background(XtermWidget xw, int color) 2321{ 2322 TScreen *screen = TScreenOf(xw); 2323 Pixel c = getXtermBG(xw, xw->flags, color); 2324 2325#if OPT_WIDE_ATTRS 2326 TRACE(("set_background(%d) %#lx %s\n", color, c, 2327 ((xw->flags & ATR_DIRECT_BG) 2328 ? "direct" 2329 : "indexed"))); 2330#else 2331 TRACE(("set_background(%d) %#lx\n", color, c)); 2332#endif 2333 XSetWindowBackground(screen->display, VShellWindow(xw), c); 2334 XSetWindowBackground(screen->display, VWindow(screen), c); 2335 initBorderGC(xw, WhichVWin(screen)); 2336} 2337 2338void 2339xtermClear2(XtermWidget xw, int x, int y, unsigned width, unsigned height) 2340{ 2341 TScreen *screen = TScreenOf(xw); 2342 VTwin *vwin = WhichVWin(screen); 2343 Drawable draw = VDrawable(screen); 2344 GC gc; 2345 2346 if ((gc = vwin->border_gc) != 0) { 2347 int vmark1 = screen->border; 2348 int vmark2 = vwin->height + vmark1; 2349 int hmark1 = OriginX(screen); 2350 int hmark2 = vwin->width + hmark1; 2351 if (y < vmark1) { 2352 int yy = y + (int) height; 2353 int h1 = (yy <= vmark1) ? (yy - y) : (vmark1 - y); 2354 XFillRectangle(screen->display, draw, gc, 2355 x, y, width, (unsigned) h1); 2356 if (yy > vmark1) { 2357 xtermClear2(xw, x, vmark1, width, (unsigned) (yy - vmark1)); 2358 } 2359 } else if (y < vmark2) { 2360 int yy = y + (int) height; 2361 int h2 = (yy <= vmark2) ? (yy - y) : (vmark2 - y); 2362 int xb = x; 2363 int xx = x + (int) width; 2364 int ww = (int) width; 2365 if (x < hmark1) { 2366 int w1 = (xx <= hmark1) ? (xx - x) : (hmark1 - x); 2367 XFillRectangle(screen->display, draw, gc, 2368 x, y, (unsigned) w1, (unsigned) h2); 2369 x += w1; 2370 ww -= w1; 2371 } 2372 if ((ww > 0) && (x < hmark2)) { 2373 int w2 = (xx <= hmark2) ? (xx - x) : (hmark2 - x); 2374#if USE_DOUBLE_BUFFER 2375 if (resource.buffered) { 2376 XFillRectangle(screen->display, draw, 2377 FillerGC(xw, screen), 2378 x, y, (unsigned) w2, (unsigned) h2); 2379 } else 2380#endif 2381 XClearArea(screen->display, VWindow(screen), 2382 x, y, (unsigned) w2, (unsigned) h2, False); 2383 x += w2; 2384 ww -= w2; 2385 } 2386 if (ww > 0) { 2387 XFillRectangle(screen->display, draw, gc, 2388 x, y, (unsigned) ww, (unsigned) h2); 2389 } 2390 if (yy > vmark2) { 2391 xtermClear2(xw, xb, vmark2, width, (unsigned) (yy - vmark2)); 2392 } 2393 } else { 2394 XFillRectangle(screen->display, draw, gc, x, y, width, height); 2395 } 2396 } else { 2397#if USE_DOUBLE_BUFFER 2398 if (resource.buffered) { 2399 gc = FillerGC(xw, screen); 2400 XFillRectangle(screen->display, draw, gc, 2401 x, y, width, height); 2402 } else 2403#endif 2404 XClearArea(screen->display, 2405 VWindow(screen), 2406 x, y, width, height, False); 2407 } 2408} 2409 2410/* 2411 * Called by the ExposeHandler to do the actual repaint after the coordinates 2412 * have been translated to allow for any CopyArea in progress. 2413 * The rectangle passed in is pixel coordinates. 2414 */ 2415static int 2416handle_translated_exposure(XtermWidget xw, 2417 int rect_x, 2418 int rect_y, 2419 int rect_width, 2420 int rect_height) 2421{ 2422 TScreen *screen = TScreenOf(xw); 2423 int toprow, leftcol, nrows, ncols; 2424 int x0, x1; 2425 int y0, y1; 2426 int result = 0; 2427 2428 TRACE(("handle_translated_exposure at %d,%d size %dx%d\n", 2429 rect_y, rect_x, rect_height, rect_width)); 2430 2431 x0 = (rect_x - OriginX(screen)); 2432 x1 = (x0 + rect_width); 2433 2434 y0 = (rect_y - OriginY(screen)); 2435 y1 = (y0 + rect_height); 2436 2437 if ((x0 < 0 || 2438 y0 < 0 || 2439 x1 > Width(screen) || 2440 y1 > Height(screen))) { 2441 set_background(xw, -1); 2442 xtermClear2(xw, 2443 rect_x, 2444 rect_y, 2445 (unsigned) rect_width, 2446 (unsigned) rect_height); 2447 } 2448 toprow = y0 / FontHeight(screen); 2449 if (toprow < 0) 2450 toprow = 0; 2451 2452 leftcol = x0 / FontWidth(screen); 2453 if (leftcol < 0) 2454 leftcol = 0; 2455 2456 nrows = (y1 - 1) / FontHeight(screen) - toprow + 1; 2457 ncols = (x1 - 1) / FontWidth(screen) - leftcol + 1; 2458 toprow -= screen->scrolls; 2459 if (toprow < 0) { 2460 nrows += toprow; 2461 toprow = 0; 2462 } 2463 if (toprow + nrows > MaxRows(screen)) 2464 nrows = MaxRows(screen) - toprow; 2465 if (leftcol + ncols > MaxCols(screen)) 2466 ncols = MaxCols(screen) - leftcol; 2467 2468 if (nrows > 0 && ncols > 0) { 2469 ScrnRefresh(xw, toprow, leftcol, nrows, ncols, True); 2470 first_map_occurred(); 2471 if (screen->cur_row >= toprow && 2472 screen->cur_row < toprow + nrows && 2473 screen->cur_col >= leftcol && 2474 screen->cur_col < leftcol + ncols) { 2475 result = 1; 2476 } 2477 2478 } 2479 TRACE(("...handle_translated_exposure %d\n", result)); 2480 return (result); 2481} 2482 2483/***====================================================================***/ 2484 2485void 2486GetColors(XtermWidget xw, ScrnColors * pColors) 2487{ 2488 TScreen *screen = TScreenOf(xw); 2489 int n; 2490 2491 pColors->which = 0; 2492 for (n = 0; n < NCOLORS; ++n) { 2493 SET_COLOR_VALUE(pColors, n, T_COLOR(screen, n)); 2494 } 2495} 2496 2497void 2498ChangeColors(XtermWidget xw, ScrnColors * pNew) 2499{ 2500 Bool repaint = False; 2501 TScreen *screen = TScreenOf(xw); 2502 VTwin *win = WhichVWin(screen); 2503 2504 TRACE(("ChangeColors\n")); 2505 2506 if (COLOR_DEFINED(pNew, TEXT_CURSOR)) { 2507 T_COLOR(screen, TEXT_CURSOR) = COLOR_VALUE(pNew, TEXT_CURSOR); 2508 TRACE(("... TEXT_CURSOR: %#lx\n", T_COLOR(screen, TEXT_CURSOR))); 2509 FreeMarkGCs(xw); 2510 /* no repaint needed */ 2511 } else if ((T_COLOR(screen, TEXT_CURSOR) == T_COLOR(screen, TEXT_FG)) && 2512 (COLOR_DEFINED(pNew, TEXT_FG))) { 2513 if (T_COLOR(screen, TEXT_CURSOR) != COLOR_VALUE(pNew, TEXT_FG)) { 2514 T_COLOR(screen, TEXT_CURSOR) = COLOR_VALUE(pNew, TEXT_FG); 2515 TRACE(("... TEXT_CURSOR: %#lx\n", T_COLOR(screen, TEXT_CURSOR))); 2516 if (screen->Vshow) 2517 repaint = True; 2518 } 2519 FreeMarkGCs(xw); 2520 } 2521 2522 if (COLOR_DEFINED(pNew, TEXT_FG)) { 2523 Pixel fg = COLOR_VALUE(pNew, TEXT_FG); 2524 T_COLOR(screen, TEXT_FG) = fg; 2525 TRACE(("... TEXT_FG: %#lx\n", T_COLOR(screen, TEXT_FG))); 2526 if (screen->Vshow) { 2527 setCgsFore(xw, win, gcNorm, fg); 2528 setCgsBack(xw, win, gcNormReverse, fg); 2529 setCgsFore(xw, win, gcBold, fg); 2530 setCgsBack(xw, win, gcBoldReverse, fg); 2531 repaint = True; 2532 } 2533 FreeMarkGCs(xw); 2534 } 2535 2536 if (COLOR_DEFINED(pNew, TEXT_BG)) { 2537 Pixel bg = COLOR_VALUE(pNew, TEXT_BG); 2538 T_COLOR(screen, TEXT_BG) = bg; 2539 TRACE(("... TEXT_BG: %#lx\n", T_COLOR(screen, TEXT_BG))); 2540 if (screen->Vshow) { 2541 setCgsBack(xw, win, gcNorm, bg); 2542 setCgsFore(xw, win, gcNormReverse, bg); 2543 setCgsBack(xw, win, gcBold, bg); 2544 setCgsFore(xw, win, gcBoldReverse, bg); 2545 set_background(xw, -1); 2546 repaint = True; 2547 } 2548 } 2549#if OPT_HIGHLIGHT_COLOR 2550 if (COLOR_DEFINED(pNew, HIGHLIGHT_BG)) { 2551 if (T_COLOR(screen, HIGHLIGHT_BG) != COLOR_VALUE(pNew, HIGHLIGHT_BG)) { 2552 T_COLOR(screen, HIGHLIGHT_BG) = COLOR_VALUE(pNew, HIGHLIGHT_BG); 2553 TRACE(("... HIGHLIGHT_BG: %#lx\n", T_COLOR(screen, HIGHLIGHT_BG))); 2554 if (screen->Vshow) 2555 repaint = True; 2556 } 2557 } 2558 if (COLOR_DEFINED(pNew, HIGHLIGHT_FG)) { 2559 if (T_COLOR(screen, HIGHLIGHT_FG) != COLOR_VALUE(pNew, HIGHLIGHT_FG)) { 2560 T_COLOR(screen, HIGHLIGHT_FG) = COLOR_VALUE(pNew, HIGHLIGHT_FG); 2561 TRACE(("... HIGHLIGHT_FG: %#lx\n", T_COLOR(screen, HIGHLIGHT_FG))); 2562 if (screen->Vshow) 2563 repaint = True; 2564 } 2565 } 2566#endif 2567 2568 if (COLOR_DEFINED(pNew, MOUSE_FG) || (COLOR_DEFINED(pNew, MOUSE_BG))) { 2569 if (COLOR_DEFINED(pNew, MOUSE_FG)) { 2570 T_COLOR(screen, MOUSE_FG) = COLOR_VALUE(pNew, MOUSE_FG); 2571 TRACE(("... MOUSE_FG: %#lx\n", T_COLOR(screen, MOUSE_FG))); 2572 } 2573 if (COLOR_DEFINED(pNew, MOUSE_BG)) { 2574 T_COLOR(screen, MOUSE_BG) = COLOR_VALUE(pNew, MOUSE_BG); 2575 TRACE(("... MOUSE_BG: %#lx\n", T_COLOR(screen, MOUSE_BG))); 2576 } 2577 2578 if (screen->Vshow) { 2579 recolor_cursor(screen, 2580 screen->pointer_cursor, 2581 T_COLOR(screen, MOUSE_FG), 2582 T_COLOR(screen, MOUSE_BG)); 2583 XDefineCursor(screen->display, VWindow(screen), 2584 screen->pointer_cursor); 2585 } 2586#if OPT_TEK4014 2587 if (TEK4014_SHOWN(xw)) { 2588 TekScreen *tekscr = TekScreenOf(tekWidget); 2589 Window tekwin = TWindow(tekscr); 2590 if (tekwin) { 2591 recolor_cursor(screen, 2592 tekscr->arrow, 2593 T_COLOR(screen, MOUSE_FG), 2594 T_COLOR(screen, MOUSE_BG)); 2595 XDefineCursor(screen->display, tekwin, tekscr->arrow); 2596 } 2597 } 2598#endif 2599 /* no repaint needed */ 2600 } 2601 2602 if (COLOR_DEFINED(pNew, TEXT_FG) || 2603 COLOR_DEFINED(pNew, TEXT_BG) || 2604 COLOR_DEFINED(pNew, TEXT_CURSOR)) { 2605 if (set_cursor_gcs(xw) && screen->Vshow) { 2606 repaint = True; 2607 } 2608 } 2609#if OPT_TEK4014 2610 if (COLOR_DEFINED(pNew, TEK_FG) || 2611 COLOR_DEFINED(pNew, TEK_BG)) { 2612 ChangeTekColors(tekWidget, screen, pNew); 2613 if (TEK4014_SHOWN(xw)) { 2614 TekRepaint(tekWidget); 2615 } 2616 } else if (COLOR_DEFINED(pNew, TEK_CURSOR)) { 2617 ChangeTekColors(tekWidget, screen, pNew); 2618 } 2619#endif 2620 if (repaint) 2621 xtermRepaint(xw); 2622} 2623 2624void 2625xtermClear(XtermWidget xw) 2626{ 2627 TScreen *screen = TScreenOf(xw); 2628 2629 TRACE(("xtermClear\n")); 2630 xtermClear2(xw, 0, 0, FullWidth(screen), FullHeight(screen)); 2631} 2632 2633void 2634xtermRepaint(XtermWidget xw) 2635{ 2636 TScreen *screen = TScreenOf(xw); 2637 2638 TRACE(("xtermRepaint\n")); 2639 xtermClear(xw); 2640 ScrnRefresh(xw, 0, 0, MaxRows(screen), MaxCols(screen), True); 2641} 2642 2643/***====================================================================***/ 2644 2645Boolean 2646isDefaultForeground(const char *name) 2647{ 2648 return (Boolean) !x_strcasecmp(name, XtDefaultForeground); 2649} 2650 2651Boolean 2652isDefaultBackground(const char *name) 2653{ 2654 return (Boolean) !x_strcasecmp(name, XtDefaultBackground); 2655} 2656 2657#if OPT_WIDE_CHARS 2658/* 2659 * Check for Unicode BIDI control characters, which may be miscategorized via 2660 * wcwidth() and iswprint() as zero-width printable characters. 2661 */ 2662Boolean 2663isWideControl(unsigned ch) 2664{ 2665 Boolean result; 2666 2667 switch (ch) { 2668 case 0x200E: 2669 case 0x200F: 2670 case 0x202A: 2671 case 0x202B: 2672 case 0x202C: 2673 case 0x202D: 2674 case 0x202E: 2675 result = True; 2676 break; 2677 default: 2678 result = False; 2679 break; 2680 } 2681 return result; 2682} 2683#endif 2684 2685/***====================================================================***/ 2686 2687typedef struct { 2688 Pixel fg; 2689 Pixel bg; 2690} ToSwap; 2691 2692#if OPT_HIGHLIGHT_COLOR 2693#define hc_param ,Bool hilite_color 2694#define hc_value ,screen->hilite_color 2695#else 2696#define hc_param /* nothing */ 2697#define hc_value /* nothing */ 2698#endif 2699 2700/* 2701 * Use this to swap the foreground/background color values in the resource 2702 * data, and to build up a list of the pairs which must be swapped in the 2703 * GC cache. 2704 */ 2705static void 2706swapLocally(ToSwap * list, int *count, ColorRes * fg, ColorRes * bg hc_param) 2707{ 2708 ColorRes tmp; 2709 Boolean found = False; 2710 2711#if OPT_COLOR_RES 2712 Pixel fg_color = fg->value; 2713 Pixel bg_color = bg->value; 2714#else 2715 Pixel fg_color = *fg; 2716 Pixel bg_color = *bg; 2717#endif 2718 2719#if OPT_HIGHLIGHT_COLOR 2720 if ((fg_color != bg_color) || !hilite_color) 2721#endif 2722 { 2723 int n; 2724 2725 EXCHANGE(*fg, *bg, tmp); 2726 for (n = 0; n < *count; ++n) { 2727 if ((list[n].fg == fg_color && list[n].bg == bg_color) 2728 || (list[n].fg == bg_color && list[n].bg == fg_color)) { 2729 found = True; 2730 break; 2731 } 2732 } 2733 if (!found) { 2734 list[*count].fg = fg_color; 2735 list[*count].bg = bg_color; 2736 *count = *count + 1; 2737 TRACE(("swapLocally fg %#lx, bg %#lx ->%d\n", 2738 fg_color, bg_color, *count)); 2739 } 2740 } 2741} 2742 2743static void 2744reallySwapColors(XtermWidget xw, ToSwap * list, int count) 2745{ 2746 int j, k; 2747 2748 TRACE(("reallySwapColors\n")); 2749 for (j = 0; j < count; ++j) { 2750 for_each_text_gc(k) { 2751 redoCgs(xw, list[j].fg, list[j].bg, (CgsEnum) k); 2752 } 2753 } 2754 FreeMarkGCs(xw); 2755} 2756 2757static void 2758swapVTwinGCs(XtermWidget xw, VTwin *win) 2759{ 2760 swapCgs(xw, win, gcNorm, gcNormReverse); 2761 swapCgs(xw, win, gcBold, gcBoldReverse); 2762} 2763 2764void 2765ReverseVideo(XtermWidget xw) 2766{ 2767 TScreen *screen = TScreenOf(xw); 2768 ToSwap listToSwap[5]; 2769 int numToSwap = 0; 2770 2771 TRACE(("ReverseVideo now %s\n", BtoS(xw->misc.re_verse))); 2772 2773 /* 2774 * Swap SGR foreground and background colors. By convention, these are 2775 * the colors assigned to "black" (SGR #0) and "white" (SGR #7). Also, 2776 * SGR #8 and SGR #15 are the bold (or bright) versions of SGR #0 and 2777 * #7, respectively. 2778 * 2779 * We don't swap colors that happen to match the screen's foreground 2780 * and background because that tends to produce bizarre effects. 2781 */ 2782#define swapAnyColor(name,a,b) swapLocally(listToSwap, &numToSwap, &(screen->name[a]), &(screen->name[b]) hc_value) 2783#define swapAColor(a,b) swapAnyColor(Acolors, a, b) 2784 if_OPT_ISO_COLORS(screen, { 2785 swapAColor(0, 7); 2786 swapAColor(8, 15); 2787 }); 2788 2789 if (T_COLOR(screen, TEXT_CURSOR) == T_COLOR(screen, TEXT_FG)) { 2790 T_COLOR(screen, TEXT_CURSOR) = T_COLOR(screen, TEXT_BG); 2791 } 2792#define swapTColor(a,b) swapAnyColor(Tcolors, a, b) 2793 swapTColor(TEXT_FG, TEXT_BG); 2794 swapTColor(MOUSE_FG, MOUSE_BG); 2795 2796 reallySwapColors(xw, listToSwap, numToSwap); 2797 2798 swapVTwinGCs(xw, &(screen->fullVwin)); 2799#ifndef NO_ACTIVE_ICON 2800 swapVTwinGCs(xw, &(screen->iconVwin)); 2801#endif /* NO_ACTIVE_ICON */ 2802 2803 xw->misc.re_verse = (Boolean) !xw->misc.re_verse; 2804 TRACE(("...swapping done, set ReverseVideo %s\n", BtoS(xw->misc.re_verse))); 2805 2806 if (XtIsRealized((Widget) xw)) { 2807 xtermDisplayPointer(xw); 2808 } 2809#if OPT_TEK4014 2810 if (TEK4014_SHOWN(xw)) { 2811 TekScreen *tekscr = TekScreenOf(tekWidget); 2812 Window tekwin = TWindow(tekscr); 2813 recolor_cursor(screen, 2814 tekscr->arrow, 2815 T_COLOR(screen, MOUSE_FG), 2816 T_COLOR(screen, MOUSE_BG)); 2817 XDefineCursor(screen->display, tekwin, tekscr->arrow); 2818 } 2819#endif 2820 2821 if (screen->scrollWidget) 2822 ScrollBarReverseVideo(screen->scrollWidget); 2823 2824 if (XtIsRealized((Widget) xw)) { 2825 set_background(xw, -1); 2826 } 2827#if OPT_TEK4014 2828 TekReverseVideo(xw, tekWidget); 2829#endif 2830 if (XtIsRealized((Widget) xw)) { 2831 xtermRepaint(xw); 2832 } 2833#if OPT_TEK4014 2834 if (TEK4014_SHOWN(xw)) { 2835 TekRepaint(tekWidget); 2836 } 2837#endif 2838 ReverseOldColors(xw); 2839 set_cursor_gcs(xw); 2840 update_reversevideo(); 2841 TRACE(("...ReverseVideo now %s\n", BtoS(xw->misc.re_verse))); 2842} 2843 2844void 2845recolor_cursor(TScreen *screen, 2846 Cursor cursor, /* X cursor ID to set */ 2847 unsigned long fg, /* pixel indexes to look up */ 2848 unsigned long bg) /* pixel indexes to look up */ 2849{ 2850 Display *dpy = screen->display; 2851 XColor colordefs[2]; /* 0 is foreground, 1 is background */ 2852 2853 colordefs[0].pixel = fg; 2854 colordefs[1].pixel = bg; 2855 XQueryColors(dpy, DefaultColormap(dpy, DefaultScreen(dpy)), 2856 colordefs, 2); 2857 XRecolorCursor(dpy, cursor, colordefs, colordefs + 1); 2858 cleanup_colored_cursor(); 2859 return; 2860} 2861 2862#if OPT_RENDERFONT 2863#define XFT_CACHE_LIMIT ((unsigned)(~0) >> 1) 2864#define XFT_CACHE_SIZE 16 2865typedef struct { 2866 XftColor color; 2867 unsigned use; 2868} XftColorCache; 2869 2870static int 2871compare_xft_color_cache(const void *a, const void *b) 2872{ 2873 return (int) (((const XftColorCache *) a)->use - 2874 ((const XftColorCache *) b)->use); 2875} 2876 2877static XftColor * 2878getXftColor(XtermWidget xw, Pixel pixel) 2879{ 2880 static XftColorCache cache[XFT_CACHE_SIZE + 1]; 2881 static unsigned latest_use; 2882 int i; 2883 int oldest; 2884 unsigned oldest_use; 2885 XColor color; 2886 Boolean found = False; 2887 2888 oldest_use = XFT_CACHE_LIMIT; 2889 oldest = 0; 2890 if (latest_use == XFT_CACHE_LIMIT) { 2891 latest_use = 0; 2892 qsort(cache, (size_t) XFT_CACHE_SIZE, sizeof(XftColorCache), compare_xft_color_cache); 2893 for (i = 0; i < XFT_CACHE_SIZE; i++) { 2894 if (cache[i].use) { 2895 cache[i].use = ++latest_use; 2896 } 2897 } 2898 } 2899 for (i = 0; i < XFT_CACHE_SIZE; i++) { 2900 if (cache[i].use) { 2901 if (cache[i].color.pixel == pixel) { 2902 found = True; 2903 break; 2904 } 2905 } 2906 if (cache[i].use < oldest_use) { 2907 oldest_use = cache[i].use; 2908 oldest = i; 2909 } 2910 } 2911 if (!found) { 2912 i = oldest; 2913 color.pixel = pixel; 2914 XQueryColor(TScreenOf(xw)->display, xw->core.colormap, &color); 2915 cache[i].color.color.red = color.red; 2916 cache[i].color.color.green = color.green; 2917 cache[i].color.color.blue = color.blue; 2918 cache[i].color.color.alpha = 0xffff; 2919 cache[i].color.pixel = pixel; 2920 } 2921 cache[i].use = ++latest_use; 2922 return &cache[i].color; 2923} 2924 2925/* 2926 * The cell-width is related to, but not the same as the wide-character width. 2927 * We will only get useful values from wcwidth() for codes above 255. 2928 * Otherwise, interpret according to internal data. 2929 */ 2930#if OPT_RENDERWIDE 2931 2932#if OPT_C1_PRINT 2933#define XtermCellWidth(xw, ch) \ 2934 (((ch) == 0 || (ch) == 127) \ 2935 ? 0 \ 2936 : (((ch) < 256) \ 2937 ? (((ch) >= 128 && (ch) < 160) \ 2938 ? (TScreenOf(xw)->c1_printable ? 1 : 0) \ 2939 : 1) \ 2940 : CharWidth(ch))) 2941#else 2942#define XtermCellWidth(xw, ch) \ 2943 (((ch) == 0 || (ch) == 127) \ 2944 ? 0 \ 2945 : (((ch) < 256) \ 2946 ? 1 \ 2947 : CharWidth(ch))) 2948#endif 2949 2950#endif /* OPT_RENDERWIDE */ 2951 2952#define XFT_FONT(which) getXftFont(params->xw, which, fontnum) 2953 2954#if OPT_ISO_COLORS 2955#define UseBoldFont(screen) (!(screen)->colorBDMode || ((screen)->veryBoldColors & BOLD)) 2956#else 2957#define UseBoldFont(screen) 1 2958#endif 2959 2960#if OPT_RENDERWIDE 2961/* 2962 * Find Xft (truetype) double-width font for the given normal/bold attributes. 2963 */ 2964static XftFont * 2965getWideXftFont(XTermDraw * params, 2966 unsigned attr_flags) 2967{ 2968 TScreen *screen = TScreenOf(params->xw); 2969 int fontnum = screen->menu_font_number; 2970 XftFont *wfont = 0; 2971 2972#if OPT_WIDE_ATTRS 2973 if ((attr_flags & ATR_ITALIC) 2974#if OPT_ISO_COLORS 2975 && !screen->colorITMode 2976#endif 2977 ) { 2978 if ((attr_flags & BOLDATTR(screen)) 2979 && UseBoldFont(screen) 2980 && XFT_FONT(fWBtal)) { 2981 wfont = XFT_FONT(fWBtal); 2982 } else if (XFT_FONT(fWItal)) { 2983 wfont = XFT_FONT(fWItal); 2984 } 2985 } 2986 if (wfont != 0) { 2987 ; /* skip the other tests */ 2988 } else 2989#endif 2990#if OPT_ISO_COLORS 2991 if ((attr_flags & UNDERLINE) 2992 && !screen->colorULMode 2993 && screen->italicULMode 2994 && XFT_FONT(fWItal)) { 2995 wfont = XFT_FONT(fWItal); 2996 } else 2997#endif 2998 if ((attr_flags & BOLDATTR(screen)) 2999 && UseBoldFont(screen) 3000 && XFT_FONT(fWBold)) { 3001 wfont = XFT_FONT(fWBold); 3002 } else { 3003 wfont = XFT_FONT(fWide); 3004 } 3005 return wfont; 3006} 3007#endif /* OPT_RENDERWIDE */ 3008 3009/* 3010 * Find Xft (truetype) single-width font for the given normal/bold attributes. 3011 */ 3012static XftFont * 3013getNormXftFont(XTermDraw * params, 3014 unsigned attr_flags, 3015 Bool *did_ul) 3016{ 3017 TScreen *screen = TScreenOf(params->xw); 3018 int fontnum = screen->menu_font_number; 3019 XftFont *font = 0; 3020 3021 (void) did_ul; 3022#if OPT_DEC_CHRSET 3023 if (CSET_DOUBLE(params->real_chrset)) { 3024 font = xterm_DoubleFT(params, params->real_chrset, attr_flags); 3025 } 3026 if (font != 0) { 3027 ; /* found a usable double-sized font */ 3028 } else 3029#endif 3030#if OPT_WIDE_ATTRS 3031 if ((attr_flags & ATR_ITALIC) 3032#if OPT_ISO_COLORS 3033 && !screen->colorITMode 3034#endif 3035 ) { 3036 if ((attr_flags & BOLDATTR(screen)) 3037 && UseBoldFont(screen) 3038 && XFT_FONT(fBtal)) { 3039 font = XFT_FONT(fBtal); 3040 } else if (XFT_FONT(fItal)) { 3041 font = XFT_FONT(fItal); 3042 } 3043 } 3044 if (font != 0) { 3045 ; /* skip the other tests */ 3046 } else 3047#endif 3048#if OPT_ISO_COLORS 3049 if ((attr_flags & UNDERLINE) 3050 && !screen->colorULMode 3051 && screen->italicULMode 3052 && XFT_FONT(fItal)) { 3053 font = XFT_FONT(fItal); 3054 *did_ul = True; 3055 } else 3056#endif 3057 if ((attr_flags & BOLDATTR(screen)) 3058 && UseBoldFont(screen) 3059 && XFT_FONT(fBold)) { 3060 font = XFT_FONT(fBold); 3061 } else { 3062 font = XFT_FONT(fNorm); 3063 } 3064 return font; 3065} 3066 3067#if OPT_RENDERWIDE 3068#define pickXftFont(width, nf, wf) ((width == 2 && wf != 0) ? wf : nf) 3069#else 3070#define pickXftFont(width, nf, wf) (nf) 3071#endif 3072 3073/* 3074 * fontconfig/Xft combination prior to 2.2 has a problem with 3075 * CJK truetype 'double-width' (bi-width/monospace) fonts leading 3076 * to the 's p a c e d o u t' rendering. Consequently, we can't 3077 * rely on XftDrawString8/16 when one of those fonts is used. 3078 * Instead, we need to roll out our own using XftDrawCharSpec. 3079 * A patch in the same spirit (but in a rather different form) 3080 * was applied to gnome vte and gtk2 port of vim. 3081 * See http://bugzilla.mozilla.org/show_bug.cgi?id=196312 3082 */ 3083static int 3084xtermXftDrawString(XTermDraw * params, 3085 unsigned attr_flags, 3086 XftColor *color, 3087 XftFont *font, 3088 int x, 3089 int y, 3090 const IChar *text, 3091 Cardinal len, 3092 Bool really) 3093{ 3094 TScreen *screen = TScreenOf(params->xw); 3095 int ncells = 0; 3096 3097 (void) attr_flags; 3098 if (len != 0) { 3099#if OPT_RENDERWIDE 3100 XftCharSpec *sbuf; 3101 XftFont *wfont = getWideXftFont(params, attr_flags); 3102 Cardinal src, dst; 3103 XftFont *lastFont = 0; 3104 XftFont *currFont = 0; 3105 Cardinal start = 0; 3106 int charWidth; 3107 int fwidth = FontWidth(screen); 3108#if OPT_DEC_CHRSET 3109 Boolean forceDbl = CSET_DOUBLE(params->real_chrset); 3110#else 3111 Boolean forceDbl = False; 3112#endif 3113 3114 BumpTypedBuffer(XftCharSpec, 2 * len); 3115 sbuf = BfBuf(XftCharSpec); 3116 3117 for (src = dst = 0; src < len; src++) { 3118 FcChar32 wc = *text++; 3119 3120 charWidth = XtermCellWidth(params->xw, (wchar_t) wc); 3121 if (charWidth < 0) 3122 continue; 3123 3124 sbuf[dst].ucs4 = wc; 3125 sbuf[dst].x = (short) (x + fwidth * ncells); 3126 sbuf[dst].y = (short) (y); 3127 3128 currFont = pickXftFont(charWidth, font, wfont); 3129 ncells += charWidth; 3130 3131 if (lastFont != currFont) { 3132 if ((lastFont != 0) && really) { 3133 XftDrawCharSpec(screen->renderDraw, 3134 color, 3135 lastFont, 3136 sbuf + start, 3137 (int) (dst - start)); 3138 } 3139 start = dst; 3140 lastFont = currFont; 3141 } 3142 ++dst; 3143 3144 if (forceDbl && charWidth < 2) { 3145 sbuf[dst].ucs4 = ' '; 3146 sbuf[dst].x = (short) (x + fwidth * ncells); 3147 sbuf[dst].y = (short) (y); 3148 ++dst; 3149 ncells += charWidth; 3150 } 3151 } 3152 if ((dst != start) && really) { 3153 XftDrawCharSpec(screen->renderDraw, 3154 color, 3155 lastFont, 3156 sbuf + start, 3157 (int) (dst - start)); 3158 } 3159#else /* !OPT_RENDERWIDE */ 3160 if (really) { 3161 XftChar8 *buffer; 3162 int dst; 3163 3164 BumpTypedBuffer(XftChar8, len); 3165 buffer = BfBuf(XftChar8); 3166 3167 for (dst = 0; dst < (int) len; ++dst) 3168 buffer[dst] = CharOf(text[dst]); 3169 3170 XftDrawString8(screen->renderDraw, 3171 color, 3172 font, 3173 x, y, buffer, (int) len); 3174 } 3175 ncells = (int) len; 3176#endif 3177 xtermNeedSwap(params->xw, 1); 3178 } 3179 return ncells; 3180} 3181#define xtermXftWidth(params, attr_flags, color, font, x, y, chars, len) \ 3182 xtermXftDrawString(params, attr_flags, color, font, x, y, chars, len, False) 3183#endif /* OPT_RENDERFONT */ 3184 3185#if OPT_WIDE_CHARS 3186/* 3187 * Map characters commonly "fixed" by groff back to their ASCII equivalents. 3188 * Also map other useful equivalents. 3189 */ 3190unsigned 3191AsciiEquivs(unsigned ch) 3192{ 3193 switch (ch) { 3194 case 0x2010: /* groff "-" */ 3195 case 0x2011: 3196 case 0x2012: 3197 case 0x2013: 3198 case 0x2014: 3199 case 0x2015: 3200 case 0x2212: /* groff "\-" */ 3201 ch = '-'; 3202 break; 3203 case 0x2018: /* groff "`" */ 3204 ch = '`'; 3205 break; 3206 case 0x2019: /* groff ' */ 3207 ch = '\''; 3208 break; 3209 case 0x201C: /* groff lq */ 3210 case 0x201D: /* groff rq */ 3211 ch = '"'; 3212 break; 3213 case 0x2329: /* groff ".URL" */ 3214 ch = '<'; 3215 break; 3216 case 0x232a: /* groff ".URL" */ 3217 ch = '>'; 3218 break; 3219 default: 3220 if (ch >= 0xff01 && ch <= 0xff5e) { 3221 /* "Fullwidth" codes (actually double-width) */ 3222 ch -= 0xff00; 3223 ch += ANSI_SPA; 3224 break; 3225 } 3226 } 3227 return ch; 3228} 3229 3230/* 3231 * Actually this should be called "groff_workaround()" - for the places where 3232 * groff stomps on compatibility. Still, if enough people get used to it, 3233 * this might someday become a quasi-standard. 3234 */ 3235#if OPT_BOX_CHARS 3236static int 3237ucs_workaround(XTermDraw * params, 3238 unsigned ch, 3239 GC gc, 3240 int x, 3241 int y) 3242{ 3243 TScreen *screen = TScreenOf(params->xw); 3244 int fixed = False; 3245 3246 if (screen->wide_chars && screen->utf8_mode && ch > 256) { 3247 IChar eqv = (IChar) AsciiEquivs(ch); 3248 3249 if (eqv != (IChar) ch) { 3250 int width = CharWidth(ch); 3251 3252 do { 3253 drawXtermText(params, 3254 gc, 3255 x, 3256 y, 3257 &eqv, 3258 1); 3259 x += FontWidth(screen); 3260 eqv = BAD_ASCII; 3261 } while (width-- > 1); 3262 3263 fixed = True; 3264 } else if (ch == HIDDEN_CHAR) { 3265 fixed = True; 3266 } 3267 } 3268 return fixed; 3269} 3270#endif /* OPT_BOX_CHARS */ 3271#endif /* OPT_WIDE_CHARS */ 3272 3273/* 3274 * Use this when the characters will not fill the cell area properly. Fill the 3275 * area where we'll write the characters, otherwise we'll get gaps between 3276 * them, e.g., in the original background color. 3277 * 3278 * The cursor is a special case, because the XFillRectangle call only uses the 3279 * foreground, while we've set the cursor color in the background. So we need 3280 * a special GC for that. 3281 */ 3282static void 3283xtermFillCells(XTermDraw * params, 3284 GC gc, 3285 int x, 3286 int y, 3287 Cardinal len) 3288{ 3289 TScreen *screen = TScreenOf(params->xw); 3290 VTwin *currentWin = WhichVWin(screen); 3291 3292 if (!(params->draw_flags & NOBACKGROUND)) { 3293 CgsEnum srcId = getCgsId(params->xw, currentWin, gc); 3294 CgsEnum dstId = gcMAX; 3295 Pixel fg = getCgsFore(params->xw, currentWin, gc); 3296 Pixel bg = getCgsBack(params->xw, currentWin, gc); 3297 3298 switch (srcId) { 3299 case gcVTcursNormal: 3300 case gcVTcursReverse: 3301 dstId = gcVTcursOutline; 3302 break; 3303 case gcVTcursFilled: 3304 case gcVTcursOutline: 3305 /* FIXME */ 3306 break; 3307 case gcNorm: 3308 dstId = gcNormReverse; 3309 break; 3310 case gcNormReverse: 3311 dstId = gcNorm; 3312 break; 3313 case gcBold: 3314 dstId = gcBoldReverse; 3315 break; 3316 case gcBoldReverse: 3317 dstId = gcBold; 3318 break; 3319 case gcBorder: 3320 case gcFiller: 3321 dstId = srcId; 3322 break; 3323#if OPT_BOX_CHARS 3324 case gcLine: 3325 case gcDots: 3326 /* FIXME */ 3327 break; 3328#endif 3329#if OPT_DEC_CHRSET 3330 case gcCNorm: 3331 case gcCBold: 3332 /* FIXME */ 3333 break; 3334#endif 3335#if OPT_WIDE_CHARS 3336 case gcWide: 3337 dstId = gcWideReverse; 3338 break; 3339 case gcWBold: 3340 dstId = gcBoldReverse; 3341 break; 3342 case gcWideReverse: 3343 case gcWBoldReverse: 3344 /* FIXME */ 3345 break; 3346#endif 3347#if OPT_TEK4014 3348 case gcTKcurs: 3349 /* FIXME */ 3350 break; 3351#endif 3352 case gcMAX: 3353 break; 3354 } 3355 3356 if (dstId != gcMAX) { 3357 setCgsFore(params->xw, currentWin, dstId, bg); 3358 setCgsBack(params->xw, currentWin, dstId, fg); 3359 3360 XFillRectangle(screen->display, VDrawable(screen), 3361 getCgsGC(params->xw, currentWin, dstId), 3362 x, y, 3363 len * (Cardinal) FontWidth(screen), 3364 (unsigned) FontHeight(screen)); 3365 } 3366 } 3367} 3368 3369#if OPT_TRACE 3370static void 3371xtermSetClipRectangles(Display *dpy, 3372 GC gc, 3373 int x, 3374 int y, 3375 XRectangle * rp, 3376 Cardinal nr, 3377 int order) 3378{ 3379#if 0 3380 TScreen *screen = TScreenOf(term); 3381 Drawable draw = VDrawable(screen); 3382 3383 XSetClipMask(dpy, gc, None); 3384 XDrawRectangle(screen->display, draw, gc, 3385 x + rp->x - 1, 3386 y + rp->y - 1, 3387 rp->width, 3388 rp->height); 3389#endif 3390 3391 XSetClipRectangles(dpy, gc, 3392 x, y, rp, (int) nr, order); 3393 TRACE(("clipping @(%3d,%3d) (%3d,%3d)..(%3d,%3d)\n", 3394 y, x, 3395 rp->y, rp->x, rp->height, rp->width)); 3396} 3397 3398#else 3399#define xtermSetClipRectangles(dpy, gc, x, y, rp, nr, order) \ 3400 XSetClipRectangles(dpy, gc, x, y, rp, (int) nr, order) 3401#endif 3402 3403#if OPT_CLIP_BOLD 3404/* 3405 * This special case is a couple of percent slower, but avoids a lot of pixel 3406 * trash in rxcurses' hanoi.cmd demo (e.g., 10x20 font). 3407 */ 3408#define beginClipping(screen,gc,pwidth,plength) \ 3409 if (pwidth > 2) { \ 3410 if (screen->use_clipping) { \ 3411 XRectangle clip; \ 3412 int clip_x = x; \ 3413 int clip_y = y - FontHeight(screen) + FontDescent(screen); \ 3414 clip.x = 0; \ 3415 clip.y = 0; \ 3416 clip.height = (unsigned short) FontHeight(screen); \ 3417 clip.width = (unsigned short) ((pwidth) * (plength)); \ 3418 xtermSetClipRectangles(screen->display, gc, \ 3419 clip_x, clip_y, \ 3420 &clip, 1, Unsorted); \ 3421 } else if (screen->use_border_clipping) { \ 3422 XRectangle clip; \ 3423 clip.x = 0; \ 3424 clip.y = 0; \ 3425 clip.height = (unsigned short) Height(screen); \ 3426 clip.width = (unsigned short) Width(screen); \ 3427 xtermSetClipRectangles(screen->display, gc, \ 3428 0, 0, \ 3429 &clip, 1, Unsorted); \ 3430 } \ 3431 } 3432#define endClipping(screen,gc) \ 3433 XSetClipMask(screen->display, gc, None) 3434#else 3435#define beginClipping(screen,gc,pwidth,plength) /* nothing */ 3436#define endClipping(screen,gc) /* nothing */ 3437#endif /* OPT_CLIP_BOLD */ 3438 3439#if OPT_RENDERFONT 3440static int 3441drawClippedXftString(XTermDraw * params, 3442 unsigned attr_flags, 3443 XftFont *font, 3444 XftColor *fg_color, 3445 int x, 3446 int y, 3447 const IChar *text, 3448 Cardinal len) 3449{ 3450 int ncells = xtermXftWidth(params, attr_flags, 3451 fg_color, 3452 font, x, y, 3453 text, 3454 len); 3455 TScreen *screen = TScreenOf(params->xw); 3456 int fontHigh = FontHeight(screen); 3457 int fontWide = FontWidth(screen); 3458 3459 if (fontWide > 2) { 3460 int plength = (ncells ? ncells : 1); 3461 Boolean halfHigh = False; 3462#if OPT_DEC_CHRSET 3463 Boolean halfWide = False; 3464 3465 switch (params->real_chrset) { 3466 case CSET_SWL: 3467 break; 3468 case CSET_DWL: 3469 halfWide = True; 3470 break; 3471 case CSET_DHL_TOP: 3472 halfHigh = True; 3473 halfWide = True; 3474 break; 3475 case CSET_DHL_BOT: 3476 halfHigh = True; 3477 halfWide = True; 3478 break; 3479 } 3480 if (CSET_DOUBLE(params->real_chrset)) { 3481 fontHigh = font->height; 3482 fontWide = font->max_advance_width; 3483 /* check if this is really a double-height font */ 3484 if (halfHigh) { 3485 int wantHigh = (int) (FontHeight(screen) * 1.8); 3486 halfHigh = (fontHigh >= wantHigh); 3487 TRACE(("comparing fontHigh %d/%d vs %d:" 3488 " double-height %s for %s\n", 3489 fontHigh, FontHeight(screen), 3490 wantHigh, BtoS(halfHigh), 3491 visibleDblChrset(params->real_chrset))); 3492 } 3493 fontHigh = (halfHigh 3494 ? (2 * FontHeight(screen)) 3495 : FontHeight(screen)); 3496 /* check if this is really a double-width font */ 3497 if (halfWide) { 3498 int wantWide = (int) (FontWidth(screen) * 1.8); 3499 halfWide = (fontWide >= wantWide); 3500 TRACE(("comparing fontWide %d/%d vs %d:" 3501 " double-width %s for %s\n", 3502 fontWide, FontWidth(screen), 3503 wantWide, BtoS(halfWide), 3504 visibleDblChrset(params->real_chrset))); 3505 } 3506 fontWide = (halfWide 3507 ? (2 * FontWidth(screen)) 3508 : FontWidth(screen)); 3509 } 3510#endif 3511 if (screen->use_clipping || halfHigh) { 3512 XRectangle clip; 3513 double adds = ((double) screen->scale_height - 1.0f) * fontHigh; 3514 int height = dimRound(adds + fontHigh); 3515 int descnt = dimRound(adds / 2.0) + FontDescent(screen); 3516 int clip_x = (x); 3517 int clip_y = (y) - height + descnt; 3518 3519 clip.x = 0; 3520 clip.y = 0; 3521 clip.height = (Dimension) height; 3522 clip.width = (Dimension) (fontWide * (Dimension) (plength)); 3523 3524#if OPT_DEC_CHRSET 3525 if (halfHigh) { 3526 int adjust; 3527 3528 clip.height = (unsigned short) FontHeight(screen); 3529 clip_y += descnt; 3530 if (params->real_chrset == CSET_DHL_BOT) { 3531 y -= clip.height; 3532 } 3533 adjust = ((clip_y - OriginY(screen)) % FontHeight(screen)); 3534 if (adjust) { 3535 if (adjust > FontHeight(screen) / 2) 3536 adjust -= FontHeight(screen); 3537 clip_y -= adjust; 3538 } 3539 } 3540#endif 3541 XftDrawSetClipRectangles(screen->renderDraw, 3542 clip_x, clip_y, 3543 &clip, 1); 3544 } else if (screen->use_border_clipping) { 3545 XRectangle clip; 3546 3547 clip.x = (Position) OriginX(screen); 3548 clip.y = (Position) OriginY(screen); 3549 clip.height = (Dimension) Height(screen); 3550 clip.width = (Dimension) Width(screen); 3551 3552 XftDrawSetClipRectangles(screen->renderDraw, 3553 0, 0, 3554 &clip, 1); 3555 } 3556 } 3557 3558 xtermXftDrawString(params, attr_flags, 3559 fg_color, 3560 font, x, y + ScaleShift(screen), 3561 text, 3562 len, 3563 True); 3564 XftDrawSetClip(screen->renderDraw, 0); 3565 return ncells; 3566} 3567#endif 3568 3569#ifndef NO_ACTIVE_ICON 3570#define WhichVFontData(screen,name) \ 3571 (IsIcon(screen) ? getIconicFont(screen) \ 3572 : GetNormalFont(screen, name)) 3573#else 3574#define WhichVFontData(screen,name) \ 3575 GetNormalFont(screen, name) 3576#endif 3577 3578static void 3579drawUnderline(XtermWidget xw, 3580 GC gc, 3581 unsigned attr_flags, 3582 unsigned underline_len, 3583 int font_width, 3584 int x, 3585 int y, 3586 Bool did_ul) 3587{ 3588 TScreen *screen = TScreenOf(xw); 3589 3590 if (screen->underline && !did_ul) { 3591 int repeat = 0; 3592 int descent = FontDescent(screen); 3593 int length = x + (int) underline_len * font_width - 1; 3594 3595#if OPT_WIDE_ATTRS 3596 if ((attr_flags & ATR_STRIKEOUT)) { 3597 int where = y - ((3 * FontAscent(screen)) / 8); 3598 XDrawLine(screen->display, VDrawable(screen), gc, 3599 x, where, 3600 length, 3601 where); 3602 } 3603 if ((attr_flags & ATR_DBL_UNDER)) { 3604 repeat = 2; 3605 } else 3606#endif 3607 if ((attr_flags & UNDERLINE)) { 3608 repeat = 1; 3609 } 3610 while (repeat-- > 0) { 3611 if (descent-- > 1) 3612 y++; 3613 XDrawLine(screen->display, VDrawable(screen), gc, 3614 x, y, 3615 length, 3616 y); 3617 } 3618 } 3619} 3620 3621#if OPT_WIDE_ATTRS 3622/* 3623 * As a special case, we are currently allowing italic fonts to be inexact 3624 * matches for the normal font's size. That introduces a problem: either the 3625 * ascent or descent may be shorter, leaving a gap that has to be filled in. 3626 * Or they may be larger, requiring clipping. Check for both cases. 3627 */ 3628static int 3629fixupItalics(XTermDraw * params, 3630 GC gc, 3631 XTermFonts * curFont, 3632 int y, int x, 3633 int font_width, 3634 Cardinal len) 3635{ 3636 TScreen *screen = TScreenOf(params->xw); 3637 VTwin *cgsWin = WhichVWin(screen); 3638 XFontStruct *realFp = curFont->fs; 3639 XFontStruct *thisFp = getCgsFont(params->xw, cgsWin, gc)->fs; 3640 int need_clipping = 0; 3641 int need_filling = 0; 3642 3643 if (thisFp->ascent > realFp->ascent) 3644 need_clipping = 1; 3645 else if (thisFp->ascent < realFp->ascent) 3646 need_filling = 1; 3647 3648 if (thisFp->descent > realFp->descent) 3649 need_clipping = 1; 3650 else if (thisFp->descent < realFp->descent) 3651 need_filling = 1; 3652 3653 if (need_clipping) { 3654 beginClipping(screen, gc, font_width, (int) len); 3655 } 3656 if (need_filling) { 3657 xtermFillCells(params, 3658 gc, 3659 x, 3660 y - realFp->ascent, 3661 len); 3662 } 3663 return need_clipping; 3664} 3665#endif 3666 3667#if OPT_DEC_CHRSET 3668static int 3669fakeDoubleChars(XTermDraw * params, 3670 GC gc, 3671 int y, 3672 int x, 3673 const IChar *text, 3674 Cardinal len) 3675{ 3676 unsigned need = 2 * len; 3677 IChar *temp = TypeMallocN(IChar, need); 3678 3679 if (temp != 0) { 3680 unsigned n = 0; 3681 XTermDraw recur = *params; 3682 3683 recur.this_chrset = CSET_SWL; 3684 3685 while (len--) { 3686 temp[n++] = *text++; 3687 temp[n++] = ' '; 3688 } 3689 x = drawXtermText(&recur, 3690 gc, 3691 x, y, 3692 temp, 3693 n); 3694 free(temp); 3695 } 3696 return x; 3697} 3698#endif /* OPT_DEC_CHRSET */ 3699 3700#define SetMissing(tag) \ 3701 TRACE(("%s %s: missing %d %04X\n", __FILE__, tag, missing, ch)); \ 3702 missing = 1 3703 3704#define MaxImageString 255 3705 3706/* 3707 * Draws text with the specified combination of bold/underline. The return 3708 * value is the updated x position. 3709 */ 3710int 3711drawXtermText(XTermDraw * params, 3712 GC gc, 3713 int start_x, 3714 int start_y, 3715 const IChar *text, 3716 Cardinal len) 3717{ 3718 XTermDraw recur = *params; 3719 TScreen *screen = TScreenOf(recur.xw); 3720 int x = start_x; 3721 int y = start_y; 3722 int y_shift = ScaleShift(screen); 3723 Cardinal real_length = len; 3724 Cardinal underline_len = 0; 3725 /* Intended width of the font to draw (as opposed to the actual width of 3726 the X font, and the width of the default font) */ 3727 int font_width = (((recur.draw_flags & DOUBLEWFONT) ? 2 : 1) 3728 * screen->fnt_wide); 3729 Bool did_ul = False; 3730 XTermFonts *curFont; 3731#if OPT_WIDE_ATTRS 3732 int need_clipping = 0; 3733#endif 3734 3735#if OPT_WIDE_CHARS 3736 if (text == 0) 3737 return 0; 3738#endif 3739 TRACE(("DRAWTEXT%c[%4d,%4d] (%d)%3d:%s\n", 3740 screen->cursor_state == OFF ? ' ' : '*', 3741 y, x, recur.this_chrset, len, 3742 visibleIChars(text, len))); 3743 3744#if OPT_DEC_CHRSET 3745 if (CSET_DOUBLE(recur.this_chrset)) { 3746 /* We could try drawing double-size characters in the icon, but 3747 * given that the icon font is usually nil or nil2, there 3748 * doesn't seem to be much point. 3749 */ 3750 int inx = 0; 3751 GC gc2; 3752 3753#if OPT_RENDERFONT 3754 if (UsingRenderFont(recur.xw)) { 3755 if (!IsIcon(screen) && screen->font_doublesize) { 3756 TRACE(("drawing %s\n", visibleDblChrset((unsigned) recur.this_chrset))); 3757 recur.real_chrset = recur.this_chrset; 3758 recur.this_chrset = CSET_SWL; 3759 x = drawXtermText(&recur, 3760 gc, 3761 x, y, 3762 text, 3763 len); 3764 x += ((int) len) * FontWidth(screen); 3765 } else { 3766 x = fakeDoubleChars(&recur, 3767 gc, y, x, 3768 text, len); 3769 } 3770 } else 3771#endif 3772 if ((!IsIcon(screen) && screen->font_doublesize) 3773 && (gc2 = xterm_DoubleGC(&recur, gc, &inx)) != 0) { 3774 /* draw actual double-sized characters */ 3775 XFontStruct *fs = getDoubleFont(screen, inx)->fs; 3776 XRectangle rect, *rp = ▭ 3777 Cardinal nr = 1; 3778 3779 font_width *= 2; 3780 recur.draw_flags |= DOUBLEWFONT; 3781 3782 rect.x = 0; 3783 rect.y = 0; 3784 rect.width = (unsigned short) ((int) len * font_width); 3785 rect.height = (unsigned short) (FontHeight(screen)); 3786 3787 TRACE(("drawing %s\n", visibleDblChrset((unsigned) recur.this_chrset))); 3788 switch (recur.this_chrset) { 3789 case CSET_DHL_TOP: 3790 rect.y = (short) -(fs->ascent / 2); 3791 y -= rect.y; 3792 recur.draw_flags |= DOUBLEHFONT; 3793 break; 3794 case CSET_DHL_BOT: 3795 rect.y = (short) (rect.height - (fs->ascent / 2)); 3796 y -= rect.y; 3797 recur.draw_flags |= DOUBLEHFONT; 3798 break; 3799 case CSET_DWL: 3800 break; 3801 default: 3802 nr = 0; 3803 break; 3804 } 3805 3806 if (nr) { 3807 xtermSetClipRectangles(screen->display, gc2, 3808 x, y, rp, nr, YXBanded); 3809 xtermFillCells(&recur, gc, x, y + rect.y, len * 2); 3810 } else { 3811 XSetClipMask(screen->display, gc2, None); 3812 } 3813 3814 /* Call ourselves recursively with the new gc */ 3815 3816 /* 3817 * If we're trying to use proportional font, or if the 3818 * font server didn't give us what we asked for wrt 3819 * width, position each character independently. 3820 */ 3821 if (screen->fnt_prop 3822 || (fs->min_bounds.width != fs->max_bounds.width) 3823 || (fs->min_bounds.width != 2 * FontWidth(screen))) { 3824 /* It is hard to fall-through to the main 3825 branch: in a lot of places the check 3826 for the cached font info is for 3827 normal/bold fonts only. */ 3828 XTermDraw param2 = recur; 3829 param2.this_chrset = CSET_SWL; 3830 while (len--) { 3831 x = drawXtermText(¶m2, 3832 gc2, 3833 x, y, 3834 text++, 3835 1); 3836 x += FontWidth(screen); 3837 } 3838 } else { 3839 XTermDraw param2 = recur; 3840 param2.this_chrset = CSET_SWL; 3841 x = drawXtermText(¶m2, 3842 gc2, 3843 x, y, 3844 text, 3845 len); 3846 x += (int) len *FontWidth(screen); 3847 } 3848 3849 } else { /* simulate double-sized characters */ 3850 x = fakeDoubleChars(&recur, 3851 gc, y, x, 3852 text, len); 3853 } 3854 TRACE(("DrewText [%4d,%4d]\n", y, x)); 3855 return x; 3856 } 3857#endif 3858#if OPT_RENDERFONT 3859 if (UsingRenderFont(recur.xw)) { 3860 VTwin *currentWin = WhichVWin(screen); 3861 Display *dpy = screen->display; 3862 XftFont *font; 3863 XftFont *font0; 3864 XGCValues values; 3865#if OPT_RENDERWIDE 3866 XftFont *wfont; 3867 XftFont *wfont0; 3868#endif 3869 3870#if OPT_DEC_CHRSET 3871 /* 3872 * If this is a VT100-style double-width font, ensure that everything 3873 * is printed using two-columns per character. 3874 */ 3875 Boolean forceDbl = CSET_DOUBLE(recur.real_chrset); 3876#else 3877 Boolean forceDbl = False; 3878#endif 3879 3880 (void) forceDbl; 3881 /* 3882 * Defer creating the drawable until we need it. 3883 */ 3884 if (!screen->renderDraw) { 3885 int scr; 3886 Drawable draw = VDrawable(screen); 3887 Visual *visual; 3888 3889 scr = DefaultScreen(dpy); 3890 visual = DefaultVisual(dpy, scr); 3891 screen->renderDraw = XftDrawCreate(dpy, draw, visual, 3892 DefaultColormap(dpy, scr)); 3893 } 3894 3895 /* 3896 * font0/wfont0 provide fallback to non-bolded Xft font if a glyph is 3897 * not found in the bold version. 3898 */ 3899#define IS_BOLD (recur.attr_flags & BOLDATTR(screen)) 3900#define NOT_BOLD (recur.attr_flags & ~BOLDATTR(screen)) 3901 font = getNormXftFont(&recur, recur.attr_flags, &did_ul); 3902 font0 = IS_BOLD ? getNormXftFont(&recur, NOT_BOLD, &did_ul) : font; 3903 (void) font0; 3904#if OPT_RENDERWIDE 3905 wfont = getWideXftFont(&recur, recur.attr_flags); 3906 wfont0 = IS_BOLD ? getWideXftFont(&recur, NOT_BOLD) : wfont; 3907#endif 3908 3909#define GET_XFT_FG() getXftColor(recur.xw, values.foreground) 3910#define GET_XFT_BG() getXftColor(recur.xw, values.background) 3911 3912 values.foreground = getCgsFore(recur.xw, currentWin, gc); 3913 values.background = getCgsBack(recur.xw, currentWin, gc); 3914 3915 if (!(recur.draw_flags & NOBACKGROUND)) { 3916 XftColor *bg_color = GET_XFT_BG(); 3917 int ncells = xtermXftWidth(&recur, recur.attr_flags, 3918 bg_color, 3919 font, x, y, 3920 text, 3921 len); 3922 XftDrawRect(screen->renderDraw, 3923 bg_color, 3924 x, y, 3925 (unsigned) (ncells * FontWidth(screen)), 3926 (unsigned) FontHeight(screen)); 3927 } 3928 3929 y += font->ascent; 3930#if OPT_BOX_CHARS 3931 { 3932 /* adding code to substitute simulated line-drawing characters */ 3933 int last, first = 0; 3934 int curX = x; 3935 3936 for (last = 0; last < (int) len; last++) { 3937 Boolean replace = False; 3938 Boolean missing = False; 3939 unsigned ch = (unsigned) text[last]; 3940 int filler = 0; 3941#if OPT_WIDE_CHARS 3942 int needed = forceDbl ? 2 : CharWidth(ch); 3943 XftFont *currFont = pickXftFont(needed, font, wfont); 3944 XftFont *tempFont = 0; 3945#define CURR_TEMP (tempFont ? tempFont : currFont) 3946 3947 if (xtermIsDecGraphic(ch) || ch == 0) { 3948 /* 3949 * Xft generally does not have the line-drawing characters 3950 * in cells 0-31. Assume this (we cannot inspect the 3951 * picture easily...), and attempt to fill in from real 3952 * line-drawing character in the font at the Unicode 3953 * position. Failing that, use our own box-characters. 3954 */ 3955 if (screen->force_box_chars 3956 || screen->broken_box_chars 3957 || xtermXftMissing(recur.xw, 3958 currFont, 3959 dec2ucs(screen, ch))) { 3960 SetMissing("case 1"); 3961 } else { 3962 ch = dec2ucs(screen, ch); 3963 replace = True; 3964 } 3965 } else if (ch >= 256) { 3966 /* 3967 * If we're reading UTF-8 from the client, we may have a 3968 * line-drawing character. Translate it back to our 3969 * box-code if Xft tells us that the glyph is missing. 3970 */ 3971 if_OPT_WIDE_CHARS(screen, { 3972 unsigned part = ucs2dec(screen, ch); 3973 if (xtermIsDecGraphic(part)) { 3974 if (screen->force_box_chars 3975 || screen->broken_box_chars) { 3976 SetMissing("case 2"); 3977 ch = part; 3978 } 3979 } 3980 if (!missing && xtermXftMissing(recur.xw, currFont, ch)) { 3981 XftFont *test = findXftGlyph(recur.xw, currFont, ch); 3982 if (test == 0) 3983 test = pickXftFont(needed, font0, wfont0); 3984 if (!xtermXftMissing(recur.xw, test, ch)) { 3985 tempFont = test; 3986 replace = True; 3987 filler = 0; 3988 } else if ((part = AsciiEquivs(ch)) != ch) { 3989 filler = needed - 1; 3990 ch = part; 3991 replace = True; 3992 } else if (ch != HIDDEN_CHAR) { 3993 SetMissing("case 3"); 3994 } 3995 } 3996 }); 3997 } 3998#else 3999 XftFont *currFont = font; 4000#define CURR_TEMP (currFont) 4001 if (xtermIsDecGraphic(ch)) { 4002 /* 4003 * Xft generally does not have the line-drawing characters 4004 * in cells 1-31. Check for this, and attempt to fill in 4005 * from real line-drawing character in the font at the 4006 * Unicode position. Failing that, use our own 4007 * box-characters. 4008 */ 4009 if (xtermXftMissing(recur.xw, currFont, ch)) { 4010 SetMissing("case 4"); 4011 } 4012 } 4013#endif 4014 4015 /* 4016 * If we now have one of our box-codes, draw it directly. 4017 */ 4018 if (missing || replace) { 4019 /* line drawing character time */ 4020 if (last > first) { 4021 int nc = drawClippedXftString(&recur, 4022 recur.attr_flags, 4023 currFont, 4024 GET_XFT_FG(), 4025 curX, 4026 y, 4027 text + first, 4028 (Cardinal) (last - first)); 4029 curX += nc * FontWidth(screen); 4030 underline_len += (Cardinal) nc; 4031 } 4032 if (missing) { 4033 Dimension old_wide = screen->fnt_wide; 4034 Dimension old_high = screen->fnt_high; 4035 screen->fnt_wide = (Dimension) FontWidth(screen); 4036 screen->fnt_high = (Dimension) FontHeight(screen); 4037 4038 xtermDrawBoxChar(&recur, ch, 4039 gc, 4040 curX, 4041 y - FontAscent(screen), 4042 1, 4043 True); 4044 curX += FontWidth(screen); 4045 underline_len += 1; 4046 screen->fnt_wide = old_wide; 4047 screen->fnt_high = old_high; 4048 } else { 4049 IChar ch2 = (IChar) ch; 4050 int nc = drawClippedXftString(&recur, 4051 recur.attr_flags, 4052 CURR_TEMP, 4053 GET_XFT_FG(), 4054 curX, 4055 y, 4056 &ch2, 4057 1); 4058 curX += nc * FontWidth(screen); 4059 underline_len += (Cardinal) nc; 4060 if (filler) { 4061 ch2 = ' '; 4062 nc = drawClippedXftString(&recur, 4063 recur.attr_flags, 4064 CURR_TEMP, 4065 GET_XFT_FG(), 4066 curX, 4067 y, 4068 &ch2, 4069 1); 4070 curX += nc * FontWidth(screen); 4071 underline_len += (Cardinal) nc; 4072 } 4073 } 4074 first = last + 1; 4075 } 4076 } 4077 if (last > first) { 4078 underline_len += (Cardinal) 4079 drawClippedXftString(&recur, 4080 recur.attr_flags, 4081 font, 4082 GET_XFT_FG(), 4083 curX, 4084 y, 4085 text + first, 4086 (Cardinal) (last - first)); 4087 } 4088 } 4089#else 4090 { 4091 underline_len += (Cardinal) 4092 drawClippedXftString(&recur, 4093 recur.attr_flags, 4094 font, 4095 GET_XFT_FG(), 4096 x, 4097 y, 4098 text, 4099 len); 4100 } 4101#endif /* OPT_BOX_CHARS */ 4102 4103 drawUnderline(recur.xw, 4104 gc, 4105 recur.attr_flags, 4106 underline_len, 4107 FontWidth(screen), 4108 x, 4109 y + y_shift, 4110 did_ul); 4111 4112 x += (int) len *FontWidth(screen); 4113 4114 return x; 4115 } 4116#endif /* OPT_RENDERFONT */ 4117 curFont = ((recur.attr_flags & BOLDATTR(screen)) 4118 ? WhichVFontData(screen, fBold) 4119 : WhichVFontData(screen, fNorm)); 4120 /* 4121 * If we're asked to display a proportional font, do this with a fixed 4122 * pitch. Yes, it's ugly. But we cannot distinguish the use of xterm 4123 * as a dumb terminal vs its use as in fullscreen programs such as vi. 4124 * Hint: do not try to use a proportional font in the icon. 4125 */ 4126 if (!IsIcon(screen) && !(recur.draw_flags & CHARBYCHAR) && screen->fnt_prop) { 4127 int adj, width; 4128 4129 while (len--) { 4130 int cells = WideCells(*text); 4131#if OPT_BOX_CHARS 4132#if OPT_WIDE_CHARS 4133 if (*text == HIDDEN_CHAR) { 4134 ++text; 4135 continue; 4136 } else 4137#endif 4138 if (IsXtermMissingChar(screen, *text, curFont)) { 4139 adj = 0; 4140 } else 4141#endif 4142 { 4143 if_WIDE_OR_NARROW(screen, { 4144 XChar2b temp[1]; 4145 temp[0].byte2 = LO_BYTE(*text); 4146 temp[0].byte1 = HI_BYTE(*text); 4147 width = XTextWidth16(curFont->fs, temp, 1); 4148 } 4149 , { 4150 char temp[1]; 4151 temp[0] = (char) LO_BYTE(*text); 4152 width = XTextWidth(curFont->fs, temp, 1); 4153 }); 4154 adj = (FontWidth(screen) - width) / 2; 4155 if (adj < 0) 4156 adj = 0; 4157 } 4158 xtermFillCells(&recur, gc, x, y, (Cardinal) cells); 4159 recur.draw_flags |= (NOBACKGROUND | CHARBYCHAR); 4160 x = drawXtermText(&recur, 4161 gc, x + adj, y, 4162 text++, 1) - adj; 4163 } 4164 4165 return x; 4166 } 4167#if OPT_BOX_CHARS 4168 /* 4169 * Draw some substitutions, if needed. The font may not include the 4170 * line-drawing set, or it may be incomplete (in which case we'll draw an 4171 * empty space via xtermDrawBoxChar), or we may be told to force our 4172 * line-drawing. 4173 * 4174 * The empty space is a special case which can be overridden with the 4175 * showMissingGlyphs resource to produce an outline. Not all fonts in 4176 * "modern" (sic) X provide an empty space; some use a thick outline or 4177 * something like the replacement character. If you would rather not see 4178 * that, you can set assumeAllChars. 4179 */ 4180 if (!IsIcon(screen) 4181 && !(recur.draw_flags & NOTRANSLATION) 4182 && (!screen->fnt_boxes 4183 || (FontIsIncomplete(curFont) && !screen->assume_all_chars) 4184 || screen->force_box_chars)) { 4185 /* 4186 * Fill in missing box-characters. Find regions without missing 4187 * characters, and draw them calling ourselves recursively. Draw 4188 * missing characters via xtermDrawBoxChar(). 4189 */ 4190 int last, first = 0; 4191 Bool drewBoxes = False; 4192 4193 for (last = 0; last < (int) len; last++) { 4194 unsigned ch = (unsigned) text[last]; 4195 Bool isMissing; 4196 int ch_width; 4197#if OPT_WIDE_CHARS 4198 4199 if (ch == HIDDEN_CHAR) { 4200 if (last > first) { 4201 XTermDraw param2 = recur; 4202 param2.draw_flags |= NOTRANSLATION; 4203 x = drawXtermText(¶m2, 4204 gc, 4205 x, y, 4206 text + first, 4207 (unsigned) (last - first)); 4208 } 4209 first = last + 1; 4210 drewBoxes = True; 4211 continue; 4212 } 4213 ch_width = CharWidth(ch); 4214 isMissing = 4215 IsXtermMissingChar(screen, ch, 4216 ((recur.on_wide || ch_width > 1) 4217 && okFont(NormalWFont(screen))) 4218 ? WhichVFontData(screen, fWide) 4219 : curFont); 4220#else 4221 isMissing = IsXtermMissingChar(screen, ch, curFont); 4222 ch_width = 1; 4223#endif 4224 /* 4225 * If the character is not missing, but we're in wide-character 4226 * mode and the character happens to be a wide-character that 4227 * corresponds to the line-drawing set, allow the forceBoxChars 4228 * resource (or menu entry) to force it to display using our 4229 * tables. 4230 */ 4231 if_OPT_WIDE_CHARS(screen, { 4232 if (!isMissing 4233 && TScreenOf(recur.xw)->force_box_chars) { 4234 if (ch > 255 4235 && ucs2dec(screen, ch) < 32) { 4236 ch = ucs2dec(screen, ch); 4237 isMissing = True; 4238 } else if (ch < 32) { 4239 isMissing = True; 4240 } 4241 } 4242 }); 4243 4244 if (isMissing) { 4245 if (last > first) { 4246 XTermDraw param2 = recur; 4247 param2.draw_flags |= NOTRANSLATION; 4248 x = drawXtermText(&recur, 4249 gc, 4250 x, y, 4251 text + first, 4252 (unsigned) (last - first)); 4253 } 4254#if OPT_WIDE_CHARS 4255 if (ch_width <= 0 && ch < 32) 4256 ch_width = 1; /* special case for line-drawing */ 4257 else if (ch_width < 0) 4258 ch_width = 1; /* special case for combining char */ 4259 if (!ucs_workaround(&recur, ch, gc, x, y)) { 4260 xtermDrawBoxChar(&recur, ch, gc, x, y, ch_width, False); 4261 } 4262#else 4263 xtermDrawBoxChar(&recur, ch, gc, x, y, ch_width, False); 4264#endif 4265 x += (ch_width * FontWidth(screen)); 4266 first = last + 1; 4267 drewBoxes = True; 4268 } 4269 } 4270 if (last <= first) { 4271 return x; 4272 } 4273 text += first; 4274 len = (Cardinal) (last - first); 4275 recur.draw_flags |= NOTRANSLATION; 4276 if (drewBoxes) { 4277 return drawXtermText(&recur, 4278 gc, 4279 x, 4280 y, 4281 text, 4282 len); 4283 } 4284 } 4285#endif /* OPT_BOX_CHARS */ 4286 /* 4287 * Behave as if the font has (maybe Unicode-replacements for) drawing 4288 * characters in the range 1-31 (either we were not asked to ignore them, 4289 * or the caller made sure that there is none). 4290 */ 4291#if OPT_WIDE_ATTRS 4292#define AttrFlags() recur.attr_flags 4293#define DrawFlags() recur.draw_flags 4294#else 4295#define AttrFlags() (recur.attr_flags & DRAWX_MASK) 4296#define DrawFlags() (recur.draw_flags & (unsigned)~DRAWX_MASK) 4297#endif 4298 TRACE(("drawtext%c[%4d,%4d] {%#x,%#x} (%d) %d:%s\n", 4299 screen->cursor_state == OFF ? ' ' : '*', 4300 y, x, 4301 AttrFlags(), 4302 DrawFlags(), 4303 recur.this_chrset, len, 4304 visibleIChars(text, len))); 4305 if (screen->scale_height != 1.0f) { 4306 xtermFillCells(&recur, gc, x, y, (Cardinal) len); 4307 } 4308 y += FontAscent(screen); 4309 4310#if OPT_WIDE_CHARS 4311 4312 if (screen->wide_chars || screen->unicode_font) { 4313 XChar2b *buffer; 4314 Bool needWide = False; 4315 int src, dst; 4316 Bool useBoldFont; 4317 int ascent_adjust = 0; 4318 4319 BumpTypedBuffer(XChar2b, len); 4320 buffer = BfBuf(XChar2b); 4321 4322 for (src = dst = 0; src < (int) len; src++) { 4323 IChar ch = text[src]; 4324 4325 if (ch == HIDDEN_CHAR) 4326 continue; 4327 4328#if OPT_BOX_CHARS 4329 if ((screen->fnt_boxes == 1) && (ch >= 256)) { 4330 unsigned part = ucs2dec(screen, ch); 4331 if (part < 32) 4332 ch = (IChar) part; 4333 } 4334#endif 4335 4336 if (!needWide 4337 && !IsIcon(screen) 4338 && ((recur.on_wide || CharWidth(ch) > 1) 4339 && okFont(NormalWFont(screen)))) { 4340 needWide = True; 4341 } 4342#if OPT_WIDER_ICHAR 4343 /* 4344 * bitmap-fonts are limited to 16-bits. 4345 */ 4346 if (ch > NARROW_ICHAR) { 4347 ch = 0; 4348 } 4349#endif 4350 buffer[dst].byte2 = LO_BYTE(ch); 4351 buffer[dst].byte1 = HI_BYTE(ch); 4352#if OPT_MINI_LUIT 4353#define UCS2SBUF(value) buffer[dst].byte2 = LO_BYTE(value);\ 4354 buffer[dst].byte1 = HI_BYTE(value) 4355 4356#define Map2Sbuf(from,to) (text[src] == from) { UCS2SBUF(to); } 4357 4358 if (screen->latin9_mode && !screen->utf8_mode && text[src] < 256) { 4359 4360 /* see http://www.cs.tut.fi/~jkorpela/latin9.html */ 4361 /* *INDENT-OFF* */ 4362 if Map2Sbuf(0xa4, 0x20ac) 4363 else if Map2Sbuf(0xa6, 0x0160) 4364 else if Map2Sbuf(0xa8, 0x0161) 4365 else if Map2Sbuf(0xb4, 0x017d) 4366 else if Map2Sbuf(0xb8, 0x017e) 4367 else if Map2Sbuf(0xbc, 0x0152) 4368 else if Map2Sbuf(0xbd, 0x0153) 4369 else if Map2Sbuf(0xbe, 0x0178) 4370 /* *INDENT-ON* */ 4371 4372 } 4373 if (screen->unicode_font 4374 && (text[src] == ANSI_DEL || 4375 text[src] < ANSI_SPA)) { 4376 unsigned ni = dec2ucs(screen, 4377 (unsigned) ((text[src] == ANSI_DEL) 4378 ? 0 4379 : text[src])); 4380 UCS2SBUF(ni); 4381 } 4382#endif /* OPT_MINI_LUIT */ 4383 ++dst; 4384 } 4385 4386 /* 4387 * Check for special case where the bold font lacks glyphs found in the 4388 * normal font, and drop down to normal fonts with overstriking to help 4389 * show the actual characters. 4390 */ 4391 useBoldFont = ((recur.attr_flags & BOLDATTR(screen)) != 0); 4392 if (useBoldFont) { 4393 XTermFonts *norm = 0; 4394 XTermFonts *bold = 0; 4395 Bool noBold, noNorm; 4396 4397 if (needWide && okFont(BoldWFont(screen))) { 4398 norm = WhichVFontData(screen, fWide); 4399 bold = WhichVFontData(screen, fWBold); 4400 } else if (okFont(BoldFont(screen))) { 4401 norm = WhichVFontData(screen, fNorm); 4402 bold = WhichVFontData(screen, fBold); 4403 } else { 4404 useBoldFont = False; 4405 } 4406 4407 if (useBoldFont && FontIsIncomplete(bold)) { 4408 for (src = 0; src < (int) len; src++) { 4409 IChar ch = text[src]; 4410 4411 if (ch == HIDDEN_CHAR) 4412 continue; 4413 4414 noBold = IsXtermMissingChar(screen, ch, bold); 4415 if (noBold) { 4416 noNorm = IsXtermMissingChar(screen, ch, norm); 4417 if (!noNorm) { 4418 useBoldFont = False; 4419 break; 4420 } 4421 } 4422 } 4423 } 4424 } 4425 4426 /* FIXME This is probably wrong. But it works. */ 4427 underline_len = len; 4428 4429 /* Set the drawing font */ 4430 if (!(recur.draw_flags & (DOUBLEHFONT | DOUBLEWFONT))) { 4431 VTwin *currentWin = WhichVWin(screen); 4432 VTFontEnum fntId; 4433 CgsEnum cgsId; 4434 Pixel fg = getCgsFore(recur.xw, currentWin, gc); 4435 Pixel bg = getCgsBack(recur.xw, currentWin, gc); 4436 4437 if (needWide 4438 && useBoldFont 4439 && okFont(BoldWFont(screen))) { 4440 fntId = fWBold; 4441 cgsId = gcWBold; 4442 } else if (needWide) { 4443 fntId = fWide; 4444 cgsId = gcWide; 4445 } else if (useBoldFont) { 4446 fntId = fBold; 4447 cgsId = gcBold; 4448 } else { 4449 fntId = fNorm; 4450 cgsId = gcNorm; 4451 } 4452 4453 setCgsFore(recur.xw, currentWin, cgsId, fg); 4454 setCgsBack(recur.xw, currentWin, cgsId, bg); 4455 gc = getCgsGC(recur.xw, currentWin, cgsId); 4456 4457#if OPT_WIDE_ATTRS 4458#if OPT_DEC_CHRSET 4459 if (!(CSET_DOUBLE(recur.this_chrset) || (recur.draw_flags & DOUBLEWFONT))) 4460#endif 4461 need_clipping = fixupItalics(&recur, 4462 gc, 4463 getCgsFont(recur.xw, 4464 currentWin, gc), 4465 y, x, font_width, len); 4466#endif 4467 if (fntId != fNorm) { 4468 XFontStruct *thisFp = WhichVFont(screen, fntId); 4469 ascent_adjust = (thisFp->ascent 4470 - NormalFont(screen)->ascent); 4471 if (thisFp->max_bounds.width == 4472 NormalFont(screen)->max_bounds.width * 2) { 4473 underline_len = real_length = (Cardinal) (dst * 2); 4474 } else if (cgsId == gcWide || cgsId == gcWBold) { 4475 underline_len = real_length = (Cardinal) (dst * 2); 4476 xtermFillCells(&recur, 4477 gc, 4478 x, 4479 y - thisFp->ascent, 4480 real_length); 4481 } 4482 } 4483 } 4484 4485 if (recur.draw_flags & NOBACKGROUND) { 4486 XDrawString16(screen->display, 4487 VDrawable(screen), gc, 4488 x, y + y_shift + ascent_adjust, 4489 buffer, dst); 4490 } else if (dst <= MaxImageString) { 4491 XDrawImageString16(screen->display, 4492 VDrawable(screen), gc, 4493 x, y + y_shift + ascent_adjust, 4494 buffer, dst); 4495 } else { 4496 int b_pos; 4497 int b_max = MaxImageString; 4498 for (b_pos = 0; b_pos < dst; b_pos += b_max) { 4499 if (b_pos + b_max > dst) 4500 b_max = (dst - b_pos); 4501 XDrawImageString16(screen->display, 4502 VDrawable(screen), gc, 4503 x + (b_pos * FontWidth(screen)), 4504 y + y_shift + ascent_adjust, 4505 buffer + b_pos, 4506 b_max); 4507 } 4508 } 4509#if OPT_WIDE_ATTRS 4510 if (need_clipping) { 4511 endClipping(screen, gc); 4512 } 4513#endif 4514 4515 if ((recur.attr_flags & BOLDATTR(screen)) && (screen->enbolden || !useBoldFont)) { 4516 if (!(recur.draw_flags & (DOUBLEWFONT | DOUBLEHFONT))) { 4517 beginClipping(screen, gc, (Cardinal) font_width, len); 4518 } 4519 XDrawString16(screen->display, VDrawable(screen), gc, 4520 x + 1, 4521 y + y_shift + ascent_adjust, 4522 buffer, dst); 4523 if (!(recur.draw_flags & (DOUBLEWFONT | DOUBLEHFONT))) { 4524 endClipping(screen, gc); 4525 } 4526 } 4527 4528 } else 4529#endif /* OPT_WIDE_CHARS */ 4530 { 4531 int length = (int) len; /* X should have used unsigned */ 4532#if OPT_WIDE_CHARS 4533 char *buffer; 4534 int dst; 4535 4536 BumpTypedBuffer(char, len); 4537 buffer = BfBuf(char); 4538 4539 for (dst = 0; dst < length; ++dst) 4540 buffer[dst] = (char) LO_BYTE(text[dst]); 4541#else 4542 char *buffer = (char *) text; 4543#endif 4544 4545#if OPT_WIDE_ATTRS 4546#if OPT_DEC_CHRSET 4547 if (!(CSET_DOUBLE(recur.this_chrset) || (recur.draw_flags & DOUBLEWFONT))) 4548#endif 4549 need_clipping = fixupItalics(&recur, gc, curFont, 4550 y, x, font_width, len); 4551#endif 4552 4553 if (recur.draw_flags & NOBACKGROUND) { 4554 XDrawString(screen->display, VDrawable(screen), gc, 4555 x, y + y_shift, buffer, length); 4556 } else if (length <= MaxImageString) { 4557 XDrawImageString(screen->display, VDrawable(screen), gc, 4558 x, y + y_shift, buffer, length); 4559 } else { 4560 int b_pos; 4561 int b_max = MaxImageString; 4562 for (b_pos = 0; b_pos < length; b_pos += b_max) { 4563 if (b_pos + b_max > length) 4564 b_max = (length - b_pos); 4565 XDrawImageString(screen->display, 4566 VDrawable(screen), gc, 4567 x + (b_pos * FontWidth(screen)), 4568 y + y_shift, 4569 buffer + b_pos, 4570 b_max); 4571 } 4572 } 4573 4574#if OPT_WIDE_ATTRS 4575 if (need_clipping) { 4576 endClipping(screen, gc); 4577 } 4578#endif 4579 underline_len = (Cardinal) length; 4580 if ((recur.attr_flags & BOLDATTR(screen)) && screen->enbolden) { 4581 if (!(recur.draw_flags & (DOUBLEWFONT | DOUBLEHFONT))) { 4582 beginClipping(screen, gc, font_width, length); 4583 } 4584 XDrawString(screen->display, VDrawable(screen), gc, 4585 x + 1, y + y_shift, buffer, length); 4586 if (!(recur.draw_flags & (DOUBLEWFONT | DOUBLEHFONT))) { 4587 endClipping(screen, gc); 4588 } 4589 } 4590 } 4591 4592 drawUnderline(recur.xw, 4593 gc, 4594 recur.attr_flags, 4595 underline_len, 4596 font_width, 4597 x, 4598 y + y_shift, 4599 did_ul); 4600 4601 x += ((int) real_length) * FontWidth(screen); 4602 return x; 4603} 4604 4605#if OPT_WIDE_CHARS 4606/* 4607 * Allocate buffer - workaround for wide-character interfaces. 4608 */ 4609void 4610allocXtermChars(ScrnPtr *buffer, Cardinal length) 4611{ 4612 if (*buffer == 0) { 4613 *buffer = (ScrnPtr) XtMalloc(length); 4614 } else { 4615 *buffer = (ScrnPtr) XtRealloc((char *) *buffer, length); 4616 } 4617} 4618#endif 4619 4620/* set up size hints for window manager; min 1 char by 1 char */ 4621void 4622xtermSizeHints(XtermWidget xw, int scrollbarWidth) 4623{ 4624 TScreen *screen = TScreenOf(xw); 4625 4626 TRACE(("xtermSizeHints\n")); 4627 TRACE((" border %d\n", xw->core.border_width)); 4628 TRACE((" scrollbar %d\n", scrollbarWidth)); 4629 4630 xw->hints.base_width = 2 * screen->border + scrollbarWidth; 4631 xw->hints.base_height = 2 * screen->border; 4632 4633#if OPT_TOOLBAR 4634 TRACE((" toolbar %d\n", ToolbarHeight(xw))); 4635 4636 xw->hints.base_height += ToolbarHeight(xw); 4637 xw->hints.base_height += BorderWidth(xw) * 2; 4638 xw->hints.base_width += BorderWidth(xw) * 2; 4639#endif 4640 4641 if (xw->misc.resizeByPixel) { 4642 xw->hints.width_inc = 1; 4643 xw->hints.height_inc = 1; 4644 } else { 4645 xw->hints.width_inc = FontWidth(screen); 4646 xw->hints.height_inc = FontHeight(screen); 4647 } 4648 xw->hints.min_width = xw->hints.base_width + xw->hints.width_inc; 4649 xw->hints.min_height = xw->hints.base_height + xw->hints.height_inc; 4650 4651 xw->hints.width = MaxCols(screen) * FontWidth(screen) + xw->hints.min_width; 4652 xw->hints.height = MaxRows(screen) * FontHeight(screen) + xw->hints.min_height; 4653 4654 xw->hints.flags |= (PSize | PBaseSize | PMinSize | PResizeInc); 4655 4656 TRACE_HINTS(&(xw->hints)); 4657} 4658 4659void 4660getXtermSizeHints(XtermWidget xw) 4661{ 4662 TScreen *screen = TScreenOf(xw); 4663 long supp; 4664 4665 if (!XGetWMNormalHints(screen->display, VShellWindow(xw), 4666 &xw->hints, &supp)) 4667 memset(&xw->hints, 0, sizeof(xw->hints)); 4668 TRACE_HINTS(&(xw->hints)); 4669} 4670 4671CgsEnum 4672whichXtermCgs(XtermWidget xw, unsigned attr_flags, Bool hilite) 4673{ 4674 TScreen *screen = TScreenOf(xw); 4675 CgsEnum cgsId = gcMAX; 4676 4677 if (ReverseOrHilite(screen, attr_flags, hilite)) { 4678 if (attr_flags & BOLDATTR(screen)) { 4679 cgsId = gcBoldReverse; 4680 } else { 4681 cgsId = gcNormReverse; 4682 } 4683 } else { 4684 if (attr_flags & BOLDATTR(screen)) { 4685 cgsId = gcBold; 4686 } else { 4687 cgsId = gcNorm; 4688 } 4689 } 4690 return cgsId; 4691} 4692 4693/* 4694 * Returns a GC, selected according to the font (reverse/bold/normal) that is 4695 * required for the current position (implied). The GC is updated with the 4696 * current screen foreground and background colors. 4697 */ 4698GC 4699updatedXtermGC(XtermWidget xw, unsigned attr_flags, CellColor fg_bg, 4700 Bool hilite) 4701{ 4702 TScreen *screen = TScreenOf(xw); 4703 VTwin *win = WhichVWin(screen); 4704 CgsEnum cgsId = whichXtermCgs(xw, attr_flags, hilite); 4705 Pixel my_fg = extract_fg(xw, fg_bg, attr_flags); 4706 Pixel my_bg = extract_bg(xw, fg_bg, attr_flags); 4707 Pixel fg_pix = getXtermFG(xw, attr_flags, (int) my_fg); 4708 Pixel bg_pix = getXtermBG(xw, attr_flags, (int) my_bg); 4709 Pixel xx_pix; 4710#if OPT_HIGHLIGHT_COLOR 4711 Boolean reverse2 = ((attr_flags & INVERSE) && hilite); 4712 Pixel selbg_pix = T_COLOR(screen, HIGHLIGHT_BG); 4713 Pixel selfg_pix = T_COLOR(screen, HIGHLIGHT_FG); 4714 Boolean always = screen->hilite_color; 4715 Boolean use_selbg = (Boolean) (always && 4716 isNotForeground(xw, fg_pix, bg_pix, selbg_pix)); 4717 Boolean use_selfg = (Boolean) (always && 4718 isNotBackground(xw, fg_pix, bg_pix, selfg_pix)); 4719#endif 4720 4721 (void) fg_bg; 4722 (void) my_bg; 4723 (void) my_fg; 4724 4725 /* 4726 * Discard video attributes overridden by colorXXXMode's. 4727 */ 4728 checkVeryBoldColors(attr_flags, my_fg); 4729 4730 if (ReverseOrHilite(screen, attr_flags, hilite)) { 4731#if OPT_HIGHLIGHT_COLOR 4732 if (!screen->hilite_color) { 4733 if (selbg_pix != T_COLOR(screen, TEXT_FG) 4734 && selbg_pix != fg_pix 4735 && selbg_pix != bg_pix 4736 && selbg_pix != xw->dft_foreground) { 4737 bg_pix = fg_pix; 4738 fg_pix = selbg_pix; 4739 } 4740 } 4741#endif 4742 EXCHANGE(fg_pix, bg_pix, xx_pix); 4743#if OPT_HIGHLIGHT_COLOR 4744 if (screen->hilite_color) { 4745 if (screen->hilite_reverse) { 4746 if (use_selbg) { 4747 if (use_selfg) { 4748 bg_pix = fg_pix; 4749 } else { 4750 fg_pix = bg_pix; 4751 bg_pix = selbg_pix; 4752 } 4753 } 4754 if (use_selfg) 4755 fg_pix = selfg_pix; 4756 } 4757 } 4758#endif 4759 } else if ((attr_flags & INVERSE) && hilite) { 4760#if OPT_HIGHLIGHT_COLOR 4761 if (!screen->hilite_color) { 4762 if (selbg_pix != T_COLOR(screen, TEXT_FG) 4763 && selbg_pix != fg_pix 4764 && selbg_pix != bg_pix 4765 && selbg_pix != xw->dft_foreground) { 4766 bg_pix = fg_pix; 4767 fg_pix = selbg_pix; 4768 } 4769 } 4770#endif 4771 /* double-reverse... EXCHANGE(fg_pix, bg_pix, xx_pix); */ 4772#if OPT_HIGHLIGHT_COLOR 4773 if (screen->hilite_color) { 4774 if (screen->hilite_reverse) { 4775 if (use_selbg) { 4776 if (use_selfg ^ reverse2) { 4777 bg_pix = fg_pix; 4778 } else { 4779 fg_pix = bg_pix; 4780 } 4781 if (reverse2) { 4782 fg_pix = selbg_pix; 4783 } else { 4784 bg_pix = selbg_pix; 4785 } 4786 } 4787 if (use_selfg) { 4788 if (reverse2) { 4789 bg_pix = selfg_pix; 4790 } else { 4791 fg_pix = selfg_pix; 4792 } 4793 } 4794 } 4795 } 4796#endif 4797 } 4798#if OPT_HIGHLIGHT_COLOR 4799 if (!screen->hilite_color || !screen->hilite_reverse) { 4800 if (hilite && !screen->hilite_reverse) { 4801 if (use_selbg) { 4802 if (reverse2) 4803 fg_pix = selbg_pix; 4804 else 4805 bg_pix = selbg_pix; 4806 } 4807 if (use_selfg) { 4808 if (reverse2) 4809 bg_pix = selfg_pix; 4810 else 4811 fg_pix = selfg_pix; 4812 } 4813 } 4814 } 4815#endif 4816 4817#if OPT_BLINK_TEXT 4818 if ((screen->blink_state == ON) && 4819 (!screen->blink_as_bold) && 4820 (attr_flags & BLINK)) { 4821 fg_pix = bg_pix; 4822 } 4823#endif 4824 4825 setCgsFore(xw, win, cgsId, fg_pix); 4826 setCgsBack(xw, win, cgsId, bg_pix); 4827 return getCgsGC(xw, win, cgsId); 4828} 4829 4830/* 4831 * Resets the foreground/background of the GC returned by 'updatedXtermGC()' 4832 * to the values that would be set in SGR_Foreground and SGR_Background. This 4833 * duplicates some logic, but only modifies 1/4 as many GC's. 4834 */ 4835void 4836resetXtermGC(XtermWidget xw, unsigned attr_flags, Bool hilite) 4837{ 4838 TScreen *screen = TScreenOf(xw); 4839 VTwin *win = WhichVWin(screen); 4840 CgsEnum cgsId = whichXtermCgs(xw, attr_flags, hilite); 4841 Pixel fg_pix = getXtermFG(xw, attr_flags, xw->cur_foreground); 4842 Pixel bg_pix = getXtermBG(xw, attr_flags, xw->cur_background); 4843 4844 checkVeryBoldColors(attr_flags, xw->cur_foreground); 4845 4846 if (ReverseOrHilite(screen, attr_flags, hilite)) { 4847 setCgsFore(xw, win, cgsId, bg_pix); 4848 setCgsBack(xw, win, cgsId, fg_pix); 4849 } else { 4850 setCgsFore(xw, win, cgsId, fg_pix); 4851 setCgsBack(xw, win, cgsId, bg_pix); 4852 } 4853} 4854 4855#if OPT_ISO_COLORS 4856/* 4857 * Extract the foreground-color index from a color pair. 4858 * If we've got BOLD or UNDERLINE color-mode active, those will be used. 4859 */ 4860Pixel 4861extract_fg(XtermWidget xw, CellColor color, unsigned attr_flags) 4862{ 4863 unsigned fg = ExtractForeground(color); 4864 4865 if (TScreenOf(xw)->colorAttrMode 4866 || (fg == ExtractBackground(color))) { 4867 fg = MapToColorMode(fg, TScreenOf(xw), attr_flags); 4868 } 4869 return fg; 4870} 4871 4872/* 4873 * Extract the background-color index from a color pair. 4874 * If we've got INVERSE color-mode active, that will be used. 4875 */ 4876Pixel 4877extract_bg(XtermWidget xw, CellColor color, unsigned attr_flags) 4878{ 4879 unsigned bg = ExtractBackground(color); 4880 4881 if (TScreenOf(xw)->colorAttrMode 4882 || (bg == ExtractForeground(color))) { 4883 if (TScreenOf(xw)->colorRVMode && (attr_flags & INVERSE)) 4884 bg = COLOR_RV; 4885 } 4886 return bg; 4887} 4888 4889/* 4890 * Combine the current foreground and background into a single 8-bit number. 4891 * Note that we're storing the SGR foreground, since cur_foreground may be set 4892 * to COLOR_UL, COLOR_BD or COLOR_BL, which would make the code larger than 8 4893 * bits. 4894 * 4895 * This assumes that fg/bg are equal when we override with one of the special 4896 * attribute colors. 4897 */ 4898CellColor 4899makeColorPair(XtermWidget xw) 4900{ 4901 CellColor result; 4902 4903#if OPT_DIRECT_COLOR 4904 result.fg = xw->cur_foreground; 4905 result.bg = xw->cur_background; 4906#else 4907 int fg = xw->cur_foreground; 4908 int bg = xw->cur_background; 4909 unsigned my_bg = okIndexedColor(bg) ? (unsigned) bg : 0; 4910 unsigned my_fg = okIndexedColor(fg) ? (unsigned) fg : my_bg; 4911 4912 result = (CellColor) (my_fg | (my_bg << COLOR_BITS)); 4913#endif 4914 4915 return result; 4916} 4917 4918/* 4919 * Using the "current" SGR background, clear a rectangle. 4920 */ 4921void 4922ClearCurBackground(XtermWidget xw, 4923 int top, 4924 int left, 4925 unsigned height, 4926 unsigned width, 4927 unsigned fw) 4928{ 4929 TScreen *screen = TScreenOf(xw); 4930 4931 TRACE(("ClearCurBackground %d,%d %dx%d with %d\n", 4932 top, left, height, width, xw->cur_background)); 4933 4934 assert((int) width > 0); 4935 assert((left + (int) width) <= screen->max_col + 1); 4936 assert((int) height <= screen->max_row + 1); 4937 4938 if (VWindow(screen)) { 4939 set_background(xw, xw->cur_background); 4940 4941 xtermClear2(xw, 4942 CursorX2(screen, left, fw), 4943 CursorY2(screen, top), 4944 (width * fw), 4945 (height * (unsigned) FontHeight(screen))); 4946 4947 set_background(xw, -1); 4948 } 4949} 4950#endif /* OPT_ISO_COLORS */ 4951 4952Pixel 4953getXtermBackground(XtermWidget xw, unsigned attr_flags, int color) 4954{ 4955 Pixel result = T_COLOR(TScreenOf(xw), TEXT_BG); 4956 4957#if OPT_ISO_COLORS 4958 if (color >= 0) { 4959 if_OPT_DIRECT_COLOR2_else(TScreenOf(xw), (attr_flags & ATR_DIRECT_BG), { 4960 result = (Pixel) color; 4961 }) if ((attr_flags & BG_COLOR) && (color < MAXCOLORS)) { 4962 result = GET_COLOR_RES(xw, TScreenOf(xw)->Acolors[color]); 4963 } 4964 } 4965#else 4966 (void) attr_flags; 4967 (void) color; 4968#endif 4969 return result; 4970} 4971 4972Pixel 4973getXtermForeground(XtermWidget xw, unsigned attr_flags, int color) 4974{ 4975 Pixel result = T_COLOR(TScreenOf(xw), TEXT_FG); 4976 4977#if OPT_ISO_COLORS 4978 if_OPT_DIRECT_COLOR2_else(TScreenOf(xw), (attr_flags & ATR_DIRECT_FG), { 4979 result = (Pixel) color; 4980 }) 4981 if ((attr_flags & FG_COLOR) && 4982 (color >= 0 && color < MAXCOLORS)) { 4983 result = GET_COLOR_RES(xw, TScreenOf(xw)->Acolors[color]); 4984 } 4985#else 4986 (void) attr_flags; 4987 (void) color; 4988#endif 4989 4990#if OPT_WIDE_ATTRS 4991#define DIM_IT(n) work.n = (unsigned short) ((2 * (unsigned)work.n) / 3) 4992 if ((attr_flags & ATR_FAINT)) { 4993 static Pixel last_in; 4994 static Pixel last_out; 4995 if ((result != last_in) 4996 && ((color >= 0) 4997 || (result != (Pixel) color))) { 4998 XColor work; 4999 work.pixel = result; 5000 last_in = result; 5001 if (XQueryColor(TScreenOf(xw)->display, xw->core.colormap, &work)) { 5002 DIM_IT(red); 5003 DIM_IT(green); 5004 DIM_IT(blue); 5005 if (allocateBestRGB(xw, &work)) { 5006 result = work.pixel; 5007 } 5008 } 5009 last_out = result; 5010 } else { 5011 result = last_out; 5012 } 5013 } 5014#endif 5015 return result; 5016} 5017 5018/* 5019 * Returns a single base character for the given cell. 5020 */ 5021unsigned 5022getXtermCell(TScreen *screen, int row, int col) 5023{ 5024 CLineData *ld = getLineData(screen, row); 5025 5026 return ((ld && (col < (int) ld->lineSize)) 5027 ? ld->charData[col] 5028 : (unsigned) ' '); 5029} 5030 5031/* 5032 * Sets a single base character for the given cell. 5033 */ 5034void 5035putXtermCell(TScreen *screen, int row, int col, int ch) 5036{ 5037 LineData *ld = getLineData(screen, row); 5038 5039 if (ld && (col < (int) ld->lineSize)) { 5040 ld->charData[col] = (CharData) ch; 5041 if_OPT_WIDE_CHARS(screen, { 5042 size_t off; 5043 for_each_combData(off, ld) { 5044 ld->combData[off][col] = 0; 5045 } 5046 }); 5047 } 5048} 5049 5050#if OPT_WIDE_CHARS 5051/* 5052 * Add a combining character for the given cell 5053 */ 5054void 5055addXtermCombining(TScreen *screen, int row, int col, unsigned ch) 5056{ 5057 if (ch != 0) { 5058 LineData *ld = getLineData(screen, row); 5059 size_t off; 5060 5061 TRACE(("addXtermCombining %d,%d U+%04X (%d)\n", 5062 row, col, ch, CharWidth(ch))); 5063 5064 for_each_combData(off, ld) { 5065 if (!ld->combData[off][col]) { 5066 ld->combData[off][col] = (CharData) ch; 5067 break; 5068 } 5069 } 5070 } 5071} 5072 5073unsigned 5074getXtermCombining(TScreen *screen, int row, int col, int off) 5075{ 5076 CLineData *ld = getLineData(screen, row); 5077 return (ld->combSize ? ld->combData[off][col] : 0U); 5078} 5079#endif 5080 5081void 5082update_keyboard_type(void) 5083{ 5084 update_delete_del(); 5085 update_tcap_fkeys(); 5086 update_old_fkeys(); 5087 update_hp_fkeys(); 5088 update_sco_fkeys(); 5089 update_sun_fkeys(); 5090 update_sun_kbd(); 5091} 5092 5093void 5094set_keyboard_type(XtermWidget xw, xtermKeyboardType type, Bool set) 5095{ 5096 xtermKeyboardType save = xw->keyboard.type; 5097 5098 TRACE(("set_keyboard_type(%s, %s) currently %s\n", 5099 visibleKeyboardType(type), 5100 BtoS(set), 5101 visibleKeyboardType(xw->keyboard.type))); 5102 if (set) { 5103 xw->keyboard.type = type; 5104 } else { 5105 xw->keyboard.type = keyboardIsDefault; 5106 } 5107 5108 if (save != xw->keyboard.type) { 5109 update_keyboard_type(); 5110 } 5111} 5112 5113void 5114toggle_keyboard_type(XtermWidget xw, xtermKeyboardType type) 5115{ 5116 xtermKeyboardType save = xw->keyboard.type; 5117 5118 TRACE(("toggle_keyboard_type(%s) currently %s\n", 5119 visibleKeyboardType(type), 5120 visibleKeyboardType(xw->keyboard.type))); 5121 if (xw->keyboard.type == type) { 5122 xw->keyboard.type = keyboardIsDefault; 5123 } else { 5124 xw->keyboard.type = type; 5125 } 5126 5127 if (save != xw->keyboard.type) { 5128 update_keyboard_type(); 5129 } 5130} 5131 5132const char * 5133visibleKeyboardType(xtermKeyboardType type) 5134{ 5135 const char *result = "?"; 5136 switch (type) { 5137 CASETYPE(keyboardIsLegacy); /* bogus vt220 codes for F1-F4, etc. */ 5138 CASETYPE(keyboardIsDefault); 5139 CASETYPE(keyboardIsHP); 5140 CASETYPE(keyboardIsSCO); 5141 CASETYPE(keyboardIsSun); 5142 CASETYPE(keyboardIsTermcap); 5143 CASETYPE(keyboardIsVT220); 5144 } 5145 return result; 5146} 5147 5148static void 5149init_keyboard_type(XtermWidget xw, xtermKeyboardType type, Bool set) 5150{ 5151 TRACE(("init_keyboard_type(%s, %s) currently %s\n", 5152 visibleKeyboardType(type), 5153 BtoS(set), 5154 visibleKeyboardType(xw->keyboard.type))); 5155 if (set) { 5156 /* 5157 * Check for conflicts, e.g., if someone asked for both Sun and HP 5158 * function keys. 5159 */ 5160 if (guard_keyboard_type) { 5161 xtermWarning("Conflicting keyboard type option (%s/%s)\n", 5162 visibleKeyboardType(xw->keyboard.type), 5163 visibleKeyboardType(type)); 5164 } 5165 xw->keyboard.type = type; 5166 guard_keyboard_type = True; 5167 update_keyboard_type(); 5168 } 5169} 5170 5171/* 5172 * If the keyboardType resource is set, use that, overriding the individual 5173 * boolean resources for different keyboard types. 5174 */ 5175void 5176decode_keyboard_type(XtermWidget xw, XTERM_RESOURCE * rp) 5177{ 5178#define DATA(n, t, f) { n, t, XtOffsetOf(XTERM_RESOURCE, f) } 5179#define FLAG(n) *(Boolean *)(((char *)rp) + table[n].offset) 5180 static struct { 5181 const char *name; 5182 xtermKeyboardType type; 5183 unsigned offset; 5184 } table[] = { 5185 DATA(NAME_OLD_KT, keyboardIsLegacy, oldKeyboard), 5186#if OPT_HP_FUNC_KEYS 5187 DATA(NAME_HP_KT, keyboardIsHP, hpFunctionKeys), 5188#endif 5189#if OPT_SCO_FUNC_KEYS 5190 DATA(NAME_SCO_KT, keyboardIsSCO, scoFunctionKeys), 5191#endif 5192#if OPT_SUN_FUNC_KEYS 5193 DATA(NAME_SUN_KT, keyboardIsSun, sunFunctionKeys), 5194#endif 5195#if OPT_SUNPC_KBD 5196 DATA(NAME_VT220_KT, keyboardIsVT220, sunKeyboard), 5197#endif 5198#if OPT_TCAP_FKEYS 5199 DATA(NAME_TCAP_KT, keyboardIsTermcap, termcapKeys), 5200#endif 5201 }; 5202 Cardinal n; 5203 TScreen *screen = TScreenOf(xw); 5204 5205 TRACE(("decode_keyboard_type(%s)\n", rp->keyboardType)); 5206 if (!x_strcasecmp(rp->keyboardType, "unknown")) { 5207 /* 5208 * Let the individual resources comprise the keyboard-type. 5209 */ 5210 for (n = 0; n < XtNumber(table); ++n) 5211 init_keyboard_type(xw, table[n].type, FLAG(n)); 5212 } else if (!x_strcasecmp(rp->keyboardType, "default")) { 5213 /* 5214 * Set the keyboard-type to the Sun/PC type, allowing modified 5215 * function keys, etc. 5216 */ 5217 for (n = 0; n < XtNumber(table); ++n) 5218 init_keyboard_type(xw, table[n].type, False); 5219 } else { 5220 Bool found = False; 5221 5222 /* 5223 * Special case: oldXtermFKeys should have been like the others. 5224 */ 5225 if (!x_strcasecmp(rp->keyboardType, NAME_OLD_KT)) { 5226 TRACE(("special case, setting oldXtermFKeys\n")); 5227 screen->old_fkeys = True; 5228 screen->old_fkeys0 = True; 5229 } 5230 5231 /* 5232 * Choose an individual keyboard type. 5233 */ 5234 for (n = 0; n < XtNumber(table); ++n) { 5235 if (!x_strcasecmp(rp->keyboardType, table[n].name + 1)) { 5236 FLAG(n) = True; 5237 found = True; 5238 } else { 5239 FLAG(n) = False; 5240 } 5241 init_keyboard_type(xw, table[n].type, FLAG(n)); 5242 } 5243 if (!found) { 5244 xtermWarning("KeyboardType resource \"%s\" not found\n", 5245 rp->keyboardType); 5246 } 5247 } 5248#undef DATA 5249#undef FLAG 5250} 5251 5252#if OPT_WIDE_CHARS 5253#if defined(HAVE_WCHAR_H) && defined(HAVE_WCWIDTH) 5254/* 5255 * If xterm is running in a UTF-8 locale, it is still possible to encounter 5256 * old runtime configurations which yield incomplete or inaccurate data. 5257 */ 5258static Bool 5259systemWcwidthOk(int samplesize, int samplepass) 5260{ 5261 wchar_t n; 5262 int oops = 0; 5263 5264 for (n = 21; n <= 25; ++n) { 5265 wchar_t code = (wchar_t) dec2ucs(NULL, (unsigned) n); 5266 int system_code = wcwidth(code); 5267 int intern_code = mk_wcwidth(code); 5268 5269 /* 5270 * Solaris 10 wcwidth() returns "2" for all of the line-drawing (page 5271 * 0x2500) and most of the geometric shapes (a few are excluded, just 5272 * to make it more difficult to use). Do a sanity check to avoid using 5273 * it. 5274 */ 5275 if ((system_code < 0 && intern_code >= 1) 5276 || (system_code >= 0 && intern_code != system_code)) { 5277 TRACE(("systemWcwidthOk: broken system line-drawing wcwidth\n")); 5278 oops += (samplepass + 1); 5279 break; 5280 } 5281 } 5282 5283 for (n = 0; n < (wchar_t) samplesize; ++n) { 5284 int system_code = wcwidth(n); 5285 int intern_code = mk_wcwidth(n); 5286 5287 /* 5288 * When this check was originally implemented, there were few if any 5289 * libraries with full Unicode coverage. Time passes, and it is 5290 * possible to make a full comparison of the BMP. There are some 5291 * differences: mk_wcwidth() marks some codes as combining and some 5292 * as single-width, differing from GNU libc. 5293 */ 5294 if ((system_code < 0 && intern_code >= 1) 5295 || (system_code >= 0 && intern_code != system_code)) { 5296 TRACE((".. width(U+%04X) = %d, expected %d\n", 5297 (unsigned) n, system_code, intern_code)); 5298 if (++oops > samplepass) 5299 break; 5300 } 5301 } 5302 TRACE(("systemWcwidthOk: %d/%d mismatches, allowed %d\n", 5303 oops, (int) n, samplepass)); 5304 return (oops <= samplepass); 5305} 5306#endif /* HAVE_WCWIDTH */ 5307 5308void 5309decode_wcwidth(XtermWidget xw) 5310{ 5311 int mode = ((xw->misc.cjk_width ? 2 : 0) 5312 + (xw->misc.mk_width ? 1 : 0) 5313 + 1); 5314 5315 switch (mode) { 5316 default: 5317#if defined(HAVE_WCHAR_H) && defined(HAVE_WCWIDTH) 5318 if (xtermEnvUTF8() && 5319 systemWcwidthOk(xw->misc.mk_samplesize, xw->misc.mk_samplepass)) { 5320 my_wcwidth = wcwidth; 5321 TRACE(("using system wcwidth() function\n")); 5322 break; 5323 } 5324#endif 5325 /* FALLTHRU */ 5326 case 2: 5327 my_wcwidth = &mk_wcwidth; 5328 TRACE(("using MK wcwidth() function\n")); 5329 break; 5330 case 3: 5331 /* FALLTHRU */ 5332 case 4: 5333 my_wcwidth = &mk_wcwidth_cjk; 5334 TRACE(("using MK-CJK wcwidth() function\n")); 5335 break; 5336 } 5337 5338 for (first_widechar = 128; first_widechar < 4500; ++first_widechar) { 5339 if (my_wcwidth((wchar_t) first_widechar) > 1) { 5340 TRACE(("first_widechar %#x\n", first_widechar)); 5341 break; 5342 } 5343 } 5344} 5345#endif 5346 5347/* 5348 * Extend a (normally) boolean resource value by checking for additional values 5349 * which will be mapped into true/false. 5350 */ 5351int 5352extendedBoolean(const char *value, const FlagList * table, Cardinal limit) 5353{ 5354 int result = -1; 5355 long check; 5356 char *next; 5357 Cardinal n; 5358 5359 if ((x_strcasecmp(value, "true") == 0) 5360 || (x_strcasecmp(value, "yes") == 0) 5361 || (x_strcasecmp(value, "on") == 0)) { 5362 result = True; 5363 } else if ((x_strcasecmp(value, "false") == 0) 5364 || (x_strcasecmp(value, "no") == 0) 5365 || (x_strcasecmp(value, "off") == 0)) { 5366 result = False; 5367 } else if ((check = strtol(value, &next, 0)) >= 0 && FullS2L(value, next)) { 5368 if (check >= (long) limit) /* i.e., past False=0, True=1 */ 5369 check = True; 5370 result = (int) check; 5371 } else { 5372 for (n = 0; n < limit - 2; ++n) { 5373 if (table[n].name == NULL) { 5374 break; 5375 } else if (x_strcasecmp(value, table[n].name) == 0) { 5376 result = table[n].code; 5377 break; 5378 } 5379 } 5380 } 5381 5382 if (result < 0) { 5383 xtermWarning("Unrecognized keyword: %s\n", value); 5384 result = False; 5385 } 5386 5387 TRACE(("extendedBoolean(%s) = %d\n", value, result)); 5388 return result; 5389} 5390 5391/* 5392 * Something like round() from math library, but round() is less widely-used 5393 * than xterm. Also, there are no negative numbers to complicate this. 5394 */ 5395int 5396dimRound(double value) 5397{ 5398 int result = (int) value; 5399 if (result < value) 5400 ++result; 5401 return result; 5402} 5403 5404/* 5405 * Find the geometry of the specified Xinerama screen 5406 */ 5407static void 5408find_xinerama_screen(Display *display, int screen, struct Xinerama_geometry *ret) 5409{ 5410#ifdef HAVE_X11_EXTENSIONS_XINERAMA_H 5411 XineramaScreenInfo *screens; 5412 int nb_screens; 5413 5414 if (screen == -1) /* already inited */ 5415 return; 5416 screens = XineramaQueryScreens(display, &nb_screens); 5417 if (screen >= nb_screens) { 5418 xtermWarning("Xinerama screen %d does not exist\n", screen); 5419 return; 5420 } 5421 if (screen == -2) { 5422 int ptr_x, ptr_y; 5423 int dummy_int, i; 5424 unsigned dummy_uint; 5425 Window dummy_win; 5426 if (nb_screens == 0) 5427 return; 5428 XQueryPointer(display, DefaultRootWindow(display), 5429 &dummy_win, &dummy_win, 5430 &ptr_x, &ptr_y, 5431 &dummy_int, &dummy_int, &dummy_uint); 5432 for (i = 0; i < nb_screens; i++) { 5433 if ((ptr_x - screens[i].x_org) < screens[i].width && 5434 (ptr_y - screens[i].y_org) < screens[i].height) { 5435 screen = i; 5436 break; 5437 } 5438 } 5439 if (screen < 0) { 5440 xtermWarning("Mouse not in any Xinerama screen, using 0\n"); 5441 screen = 0; 5442 } 5443 } 5444 ret->scr_x = screens[screen].x_org; 5445 ret->scr_y = screens[screen].y_org; 5446 ret->scr_w = screens[screen].width; 5447 ret->scr_h = screens[screen].height; 5448#else /* HAVE_X11_EXTENSIONS_XINERAMA_H */ 5449 (void) display; 5450 (void) ret; 5451 if (screen > 0) 5452 xtermWarning("Xinerama support not enabled\n"); 5453#endif /* HAVE_X11_EXTENSIONS_XINERAMA_H */ 5454} 5455 5456/* 5457 * Parse the screen code after the @ in a geometry string. 5458 */ 5459static void 5460parse_xinerama_screen(Display *display, const char *str, struct Xinerama_geometry *ret) 5461{ 5462 int screen = -1; 5463 char *end; 5464 5465 if (*str == 'g') { 5466 screen = -1; 5467 str++; 5468 } else if (*str == 'c') { 5469 screen = -2; 5470 str++; 5471 } else { 5472 long s = strtol(str, &end, 0); 5473 if (FullS2L(str, end) && ((int) s >= 0)) { 5474 screen = (int) s; 5475 str = end; 5476 } 5477 } 5478 if (*str) { 5479 xtermWarning("invalid Xinerama specification '%s'\n", str); 5480 return; 5481 } 5482 if (screen == -1) /* already done */ 5483 return; 5484 find_xinerama_screen(display, screen, ret); 5485} 5486 5487/* 5488 * Parse a geometry string with extra Xinerama specification: 5489 * <w>x<h>+<x>+<y>@<screen>. 5490 */ 5491int 5492XParseXineramaGeometry(Display *display, char *parsestring, struct Xinerama_geometry *ret) 5493{ 5494 char *at, buf[128]; 5495 5496 ret->scr_x = 0; 5497 ret->scr_y = 0; 5498 ret->scr_w = DisplayWidth(display, DefaultScreen(display)); 5499 ret->scr_h = DisplayHeight(display, DefaultScreen(display)); 5500 at = strchr(parsestring, '@'); 5501 if (at != NULL && (size_t) (at - parsestring) < sizeof(buf) - 1) { 5502 memcpy(buf, parsestring, (size_t) (at - parsestring)); 5503 buf[at - parsestring] = 0; 5504 parsestring = buf; 5505 parse_xinerama_screen(display, at + 1, ret); 5506 } 5507 return ((strlen(parsestring) <= MAX_U_STRING) 5508 ? XParseGeometry(parsestring, &ret->x, &ret->y, &ret->w, &ret->h) 5509 : 0); 5510} 5511 5512#if USE_DOUBLE_BUFFER 5513Window 5514VDrawable(TScreen *screen) 5515{ 5516 screen->needSwap = 1; 5517 return WhichVWin(screen)->drawable; 5518} 5519#endif 5520 5521#if OPT_RENDERFONT 5522#ifndef discardRenderDraw 5523void 5524discardRenderDraw(TScreen *screen) 5525{ 5526 if ( 5527#if USE_DOUBLE_BUFFER 5528 resource.buffered && 5529#endif 5530 screen->renderDraw) { 5531 XftDrawDestroy(screen->renderDraw); 5532 screen->renderDraw = NULL; 5533 } 5534} 5535#endif 5536#endif /* OPT_RENDERFONT */ 5537 5538char * 5539xtermSetLocale(int category, String after) 5540{ 5541 char *before = x_strdup(setlocale(category, 0)); 5542 5543 (void) setlocale(category, after); 5544 TRACE(("before setlocale :%s\n", NonNull(before))); 5545 TRACE(("updated locale :%s\n", NonNull(setlocale(category, 0)))); 5546 return before; 5547} 5548 5549void 5550xtermResetLocale(int category, char *before) 5551{ 5552 (void) setlocale(category, before); 5553 free(before); 5554 TRACE(("restored locale :%s\n", NonNull(setlocale(category, 0)))); 5555} 5556