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