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