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