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