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