1/* $XTermId: screen.c,v 1.657 2025/01/03 01:31:13 tom Exp $ */ 2 3/* 4 * Copyright 1999-2024,2025 by Thomas E. Dickey 5 * 6 * All Rights Reserved 7 * 8 * Permission is hereby granted, free of charge, to any person obtaining a 9 * copy of this software and associated documentation files (the 10 * "Software"), to deal in the Software without restriction, including 11 * without limitation the rights to use, copy, modify, merge, publish, 12 * distribute, sublicense, and/or sell copies of the Software, and to 13 * permit persons to whom the Software is furnished to do so, subject to 14 * the following conditions: 15 * 16 * The above copyright notice and this permission notice shall be included 17 * in all copies or substantial portions of the Software. 18 * 19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 20 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 21 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 22 * IN NO EVENT SHALL THE ABOVE LISTED COPYRIGHT HOLDER(S) BE LIABLE FOR ANY 23 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 24 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 25 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 26 * 27 * Except as contained in this notice, the name(s) of the above copyright 28 * holders shall not be used in advertising or otherwise to promote the 29 * sale, use or other dealings in this Software without prior written 30 * authorization. 31 * 32 * 33 * Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts. 34 * 35 * All Rights Reserved 36 * 37 * Permission to use, copy, modify, and distribute this software and its 38 * documentation for any purpose and without fee is hereby granted, 39 * provided that the above copyright notice appear in all copies and that 40 * both that copyright notice and this permission notice appear in 41 * supporting documentation, and that the name of Digital Equipment 42 * Corporation not be used in advertising or publicity pertaining to 43 * distribution of the software without specific, written prior permission. 44 * 45 * 46 * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING 47 * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL 48 * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR 49 * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, 50 * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, 51 * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS 52 * SOFTWARE. 53 */ 54 55/* screen.c */ 56 57#include <stdio.h> 58#include <xterm.h> 59#include <error.h> 60#include <data.h> 61#include <xterm_io.h> 62 63#include <X11/Xatom.h> 64 65#if OPT_WIDE_ATTRS || OPT_WIDE_CHARS 66#include <fontutils.h> 67#endif 68 69#include <menu.h> 70 71#include <assert.h> 72#include <signal.h> 73 74#include <graphics.h> 75 76#define inSaveBuf(screen, buf, inx) \ 77 ((buf) == (screen)->saveBuf_index && \ 78 ((inx) < (screen)->savelines || (screen)->savelines == 0)) 79 80#define getMinRow(screen) ((xw->flags & ORIGIN) ? (screen)->top_marg : 0) 81#define getMaxRow(screen) ((xw->flags & ORIGIN) ? (screen)->bot_marg : (screen)->max_row) 82#define getMinCol(screen) ((xw->flags & ORIGIN) ? (screen)->lft_marg : 0) 83#define getMaxCol(screen) ((xw->flags & ORIGIN) ? (screen)->rgt_marg : (screen)->max_col) 84 85#define MoveLineData(base, dst, src, len) \ 86 memmove(scrnHeadAddr(screen, base, (unsigned) (dst)), \ 87 scrnHeadAddr(screen, base, (unsigned) (src)), \ 88 (size_t) scrnHeadSize(screen, (unsigned) (len))) 89 90#define SaveLineData(base, src, len) \ 91 (void) ScrnPointers(screen, len); \ 92 memcpy (screen->save_ptr, \ 93 scrnHeadAddr(screen, base, src), \ 94 (size_t) scrnHeadSize(screen, (unsigned) (len))) 95 96#define RestoreLineData(base, dst, len) \ 97 memcpy (scrnHeadAddr(screen, base, dst), \ 98 screen->save_ptr, \ 99 (size_t) scrnHeadSize(screen, (unsigned) (len))) 100 101#define VisBuf(screen) screen->editBuf_index[screen->whichBuf] 102 103/* 104 * ScrnPtr's can point to different types of data. 105 */ 106#define SizeofScrnPtr(name) \ 107 (unsigned) sizeof(*((LineData *)0)->name) 108 109/* 110 * The pointers in LineData point into a block of text allocated as a single 111 * chunk for the given number of rows. Ensure that these pointers are aligned 112 * at least to int-boundaries. 113 */ 114#define AlignMask() (sizeof(int) - 1) 115#define IsAligned(value) (((unsigned long) (value) & AlignMask()) == 0) 116 117#define AlignValue(value) \ 118 if (!IsAligned(value)) \ 119 value = (value | (unsigned) AlignMask()) + 1 120 121#define SetupScrnPtr(dst,src,type) \ 122 dst = (type *) (void *) src; \ 123 assert(IsAligned(dst)); \ 124 src += skipNcol##type 125 126#define ScrnBufAddr(ptrs, offset) (ScrnBuf) ((void *) ((char *) (ptrs) + (offset))) 127#define LineDataAddr(ptrs, offset) (LineData *) ((void *) ((char *) (ptrs) + (offset))) 128 129#if OPT_TRACE > 1 130static void 131traceScrnBuf(const char *tag, TScreen *screen, ScrnBuf sb, unsigned len) 132{ 133 unsigned j; 134 135 TRACE(("traceScrnBuf %s\n", tag)); 136 for (j = 0; j < len; ++j) { 137 LineData *src = (LineData *) scrnHeadAddr(screen, sb, j); 138 TRACE(("%p %s%3d:%s\n", 139 src, ((int) j >= screen->savelines) ? "*" : " ", 140 j, visibleIChars(src->charData, src->lineSize))); 141 } 142 TRACE(("...traceScrnBuf %s\n", tag)); 143} 144 145#define TRACE_SCRNBUF(tag, screen, sb, len) traceScrnBuf(tag, screen, sb, len) 146#else 147#define TRACE_SCRNBUF(tag, screen, sb, len) /*nothing */ 148#endif 149 150#if OPT_WIDE_CHARS 151#define scrnHeadSize(screen, count) \ 152 (unsigned) ((count) * \ 153 (SizeOfLineData + \ 154 ((screen)->wide_chars \ 155 ? (unsigned) (screen)->lineExtra \ 156 : 0))) 157#else 158#define scrnHeadSize(screen, count) \ 159 (unsigned) ((count) * \ 160 SizeOfLineData) 161#endif 162 163ScrnBuf 164scrnHeadAddr(TScreen *screen, ScrnBuf base, unsigned offset) 165{ 166 unsigned size = scrnHeadSize(screen, offset); 167 ScrnBuf result = ScrnBufAddr(base, size); 168 169 (void) screen; 170 assert((int) offset >= 0); 171 172 return result; 173} 174 175/* 176 * Given a block of data, build index to it in the 'base' parameter. 177 */ 178void 179setupLineData(TScreen *screen, 180 ScrnBuf base, 181 Char *data, 182 unsigned nrow, 183 unsigned ncol, 184 Bool bottom) 185{ 186 unsigned i; 187 unsigned offset = 0; 188 unsigned jump = scrnHeadSize(screen, 1); 189 LineData *ptr; 190#if OPT_WIDE_CHARS 191 unsigned j; 192#endif 193 /* these names are based on types */ 194 unsigned skipNcolIAttr; 195 unsigned skipNcolCharData; 196#if OPT_DEC_RECTOPS 197 unsigned skipNcolChar; 198#endif 199#if OPT_ISO_COLORS 200 unsigned skipNcolCellColor; 201#endif 202 203 (void) screen; 204 AlignValue(ncol); 205 206 (void) bottom; 207#if OPT_STATUS_LINE 208 if (bottom) { 209 AddStatusLineRows(nrow); 210 } 211#endif 212 213 /* *INDENT-EQLS* */ 214 skipNcolIAttr = (ncol * SizeofScrnPtr(attribs)); 215 skipNcolCharData = (ncol * SizeofScrnPtr(charData)); 216#if OPT_DEC_RECTOPS 217 skipNcolChar = (ncol * SizeofScrnPtr(charSeen)); /* = charSets */ 218#endif 219#if OPT_ISO_COLORS 220 skipNcolCellColor = (ncol * SizeofScrnPtr(color)); 221#endif 222 223 for (i = 0; i < nrow; i++, offset += jump) { 224 ptr = LineDataAddr(base, offset); 225 226 ptr->lineSize = (Dimension) ncol; 227 ptr->bufHead = 0; 228#if OPT_DEC_CHRSET 229 SetLineDblCS(ptr, 0); 230#endif 231 SetupScrnPtr(ptr->attribs, data, IAttr); 232#if OPT_ISO_COLORS 233 SetupScrnPtr(ptr->color, data, CellColor); 234#endif 235 SetupScrnPtr(ptr->charData, data, CharData); 236#if OPT_DEC_RECTOPS 237 SetupScrnPtr(ptr->charSeen, data, Char); 238 SetupScrnPtr(ptr->charSets, data, Char); 239#endif 240#if OPT_WIDE_CHARS 241 if (screen->wide_chars) { 242 unsigned extra = (unsigned) screen->max_combining; 243 244 ptr->combSize = (Char) extra; 245 for (j = 0; j < extra; ++j) { 246 SetupScrnPtr(ptr->combData[j], data, CharData); 247 } 248 } 249#endif 250 } 251} 252 253#define ExtractScrnData(name) \ 254 memcpy(dstPtrs->name, \ 255 ((LineData *) srcPtrs)->name,\ 256 dstCols * sizeof(dstPtrs->name[0])) 257 258/* 259 * As part of reallocating the screen buffer when resizing, extract from 260 * the old copy of the screen buffer the data which will be used in the 261 * new copy of the screen buffer. 262 */ 263static void 264extractScrnData(TScreen *screen, 265 ScrnBuf dstPtrs, 266 ScrnBuf srcPtrs, 267 unsigned nrows, 268 unsigned move_down) 269{ 270 unsigned j; 271 272 TRACE(("extractScrnData(nrows %d)\n", nrows)); 273 274 TRACE_SCRNBUF("extract from", screen, srcPtrs, nrows); 275 for (j = 0; j < nrows; j++) { 276 LineData *dst = (LineData *) scrnHeadAddr(screen, 277 dstPtrs, j + move_down); 278 LineData *src = (LineData *) scrnHeadAddr(screen, 279 srcPtrs, j); 280 copyLineData(dst, src); 281 } 282} 283 284static ScrnPtr * 285allocScrnHead(TScreen *screen, unsigned nrow) 286{ 287 ScrnPtr *result; 288 unsigned size = scrnHeadSize(screen, 1); 289 290 (void) screen; 291 AddStatusLineRows(nrow); 292 result = (ScrnPtr *) calloc((size_t) nrow, (size_t) size); 293 if (result == NULL) 294 SysError(ERROR_SCALLOC); 295 296 TRACE(("allocScrnHead %d -> %d -> %p..%p\n", nrow, nrow * size, 297 (void *) result, 298 (char *) result + (nrow * size) - 1)); 299 return result; 300} 301 302/* 303 * Return the size of a line's data. 304 */ 305static unsigned 306sizeofScrnRow(TScreen *screen, unsigned ncol) 307{ 308 unsigned result; 309 unsigned sizeAttribs; 310#if OPT_ISO_COLORS 311 unsigned sizeColors; 312#endif 313 314 (void) screen; 315 316 result = (ncol * (unsigned) sizeof(CharData)); /* ->charData */ 317 AlignValue(result); 318 319#if OPT_DEC_RECTOPS 320 result += (ncol * (unsigned) sizeof(Char)); /* ->charSeen */ 321 AlignValue(result); 322 result += (ncol * (unsigned) sizeof(Char)); /* ->charSets */ 323 AlignValue(result); 324#endif 325 326#if OPT_WIDE_CHARS 327 if (screen->wide_chars) { 328 result *= (unsigned) (1 + screen->max_combining); 329 } 330#endif 331 332 sizeAttribs = (ncol * SizeofScrnPtr(attribs)); 333 AlignValue(sizeAttribs); 334 result += sizeAttribs; 335 336#if OPT_ISO_COLORS 337 sizeColors = (ncol * SizeofScrnPtr(color)); 338 AlignValue(sizeColors); 339 result += sizeColors; 340#endif 341 342 return result; 343} 344 345Char * 346allocScrnData(TScreen *screen, unsigned nrow, unsigned ncol, Bool bottom) 347{ 348 Char *result = NULL; 349 size_t length; 350 351 AlignValue(ncol); 352 if (bottom) { 353 AddStatusLineRows(nrow); 354 } 355 length = (nrow * sizeofScrnRow(screen, ncol)); 356 if (length == 0 357 || (result = (Char *) calloc(length, sizeof(Char))) == NULL) 358 SysError(ERROR_SCALLOC2); 359 360 TRACE(("allocScrnData %ux%u -> %lu -> %p..%p\n", 361 nrow, ncol, (unsigned long) length, result, result + length - 1)); 362 return result; 363} 364 365/* 366 * Allocates memory for a 2-dimensional array of chars and returns a pointer 367 * thereto. Each line is formed from a set of char arrays, with an index 368 * (i.e., the ScrnBuf type). The first pointer in the index is reserved for 369 * per-line flags, and does not point to data. 370 * 371 * After the per-line flags, we have a series of pointers to char arrays: The 372 * first one is the actual character array, the second one is the attributes, 373 * the third is the foreground and background colors, and the fourth denotes 374 * the character set. 375 * 376 * We store it all as pointers, because of alignment considerations. 377 */ 378ScrnBuf 379allocScrnBuf(XtermWidget xw, unsigned nrow, unsigned ncol, Char **addr) 380{ 381 TScreen *screen = TScreenOf(xw); 382 ScrnBuf base = NULL; 383 384 if (nrow != 0) { 385 base = allocScrnHead(screen, nrow); 386 *addr = allocScrnData(screen, nrow, ncol, True); 387 388 setupLineData(screen, base, *addr, nrow, ncol, True); 389 } 390 391 TRACE(("allocScrnBuf %dx%d ->%p\n", nrow, ncol, (void *) base)); 392 return (base); 393} 394 395/* 396 * Copy line-data from the visible (edit) buffer to the save-lines buffer. 397 */ 398static void 399saveEditBufLines(TScreen *screen, unsigned n) 400{ 401 unsigned j; 402 403 TRACE(("...copying %d lines from editBuf to saveBuf\n", n)); 404 405 for (j = 0; j < n; ++j) { 406 407 LineData *dst = addScrollback(screen); 408 409 LineData *src = getLineData(screen, (int) j); 410 copyLineData(dst, src); 411 } 412} 413 414/* 415 * Copy line-data from the save-lines buffer to the visible (edit) buffer. 416 */ 417static void 418unsaveEditBufLines(TScreen *screen, ScrnBuf sb, unsigned n) 419{ 420 unsigned j; 421 422 TRACE(("...copying %d lines from saveBuf to editBuf\n", n)); 423 for (j = 0; j < n; ++j) { 424 int extra = (int) (n - j); 425 LineData *dst = (LineData *) scrnHeadAddr(screen, sb, j); 426 427 CLineData *src; 428 429 if (extra > screen->saved_fifo || extra > screen->savelines) { 430 TRACE(("...FIXME: must clear text!\n")); 431 continue; 432 } 433 src = getScrollback(screen, -extra); 434 435 copyLineData(dst, src); 436 } 437} 438 439/* 440 * This is called when the screen is resized. 441 * Returns the number of lines the text was moved down (neg for up). 442 * (Return value only necessary with SouthWestGravity.) 443 */ 444static int 445Reallocate(XtermWidget xw, 446 ScrnBuf *sbuf, 447 Char **sbufaddr, 448 unsigned nrow, 449 unsigned ncol, 450 unsigned oldrow) 451{ 452 TScreen *screen = TScreenOf(xw); 453 ScrnBuf oldBufHead; 454 ScrnBuf newBufHead; 455 Char *newBufData; 456 unsigned minrows; 457 Char *oldBufData; 458 int move_down = 0, move_up = 0; 459 460 if (sbuf == NULL || *sbuf == NULL) { 461 return 0; 462 } 463 464 oldBufData = *sbufaddr; 465 466 TRACE(("Reallocate %dx%d -> %dx%d\n", oldrow, MaxCols(screen), nrow, ncol)); 467 468 /* 469 * realloc sbuf, the pointers to all the lines. 470 * If the screen shrinks, remove lines off the top of the buffer 471 * if resizeGravity resource says to do so. 472 */ 473 TRACE(("Check move_up, nrow %d vs oldrow %d (resizeGravity %s)\n", 474 nrow, oldrow, 475 BtoS(GravityIsSouthWest(xw)))); 476 if (GravityIsSouthWest(xw)) { 477 if (nrow < oldrow) { 478 /* Remove lines off the top of the buffer if necessary. */ 479 move_up = (int) (oldrow - nrow) 480 - (TScreenOf(xw)->max_row - TScreenOf(xw)->cur_row); 481 if (move_up < 0) 482 move_up = 0; 483 /* Overlapping move here! */ 484 TRACE(("move_up %d\n", move_up)); 485 if (move_up) { 486 ScrnBuf dst = *sbuf; 487 unsigned len = (unsigned) ((int) oldrow - move_up); 488 489 TRACE_SCRNBUF("before move_up", screen, dst, oldrow); 490 SaveLineData(dst, 0, (size_t) move_up); 491 MoveLineData(dst, 0, (size_t) move_up, len); 492 RestoreLineData(dst, len, (size_t) move_up); 493 TRACE_SCRNBUF("after move_up", screen, dst, oldrow); 494 } 495 } 496 } 497 oldBufHead = *sbuf; 498 *sbuf = allocScrnHead(screen, (unsigned) nrow); 499 newBufHead = *sbuf; 500 501 /* 502 * Create the new buffer space and copy old buffer contents there, line by 503 * line. 504 */ 505 newBufData = allocScrnData(screen, nrow, ncol, True); 506 *sbufaddr = newBufData; 507 508 minrows = (oldrow < nrow) ? oldrow : nrow; 509 if (GravityIsSouthWest(xw)) { 510 if (nrow > oldrow) { 511 /* move data down to bottom of expanded screen */ 512 move_down = Min((int) (nrow - oldrow), TScreenOf(xw)->savedlines); 513 } 514 } 515 516 setupLineData(screen, newBufHead, *sbufaddr, nrow, ncol, True); 517 extractScrnData(screen, newBufHead, oldBufHead, minrows, 0); 518 519 /* Now free the old data */ 520 free(oldBufData); 521 free(oldBufHead); 522 523 TRACE(("...Reallocate %dx%d ->%p\n", nrow, ncol, (void *) newBufHead)); 524 return move_down ? move_down : -move_up; /* convert to rows */ 525} 526 527#if OPT_WIDE_CHARS 528/* 529 * This function reallocates memory if changing the number of Buf offsets. 530 * The code is based on Reallocate(). 531 */ 532static void 533ReallocateBufOffsets(XtermWidget xw, 534 ScrnBuf *sbuf, 535 Char **sbufaddr, 536 unsigned nrow, 537 unsigned ncol) 538{ 539 TScreen *screen = TScreenOf(xw); 540 unsigned i; 541 ScrnBuf newBufHead; 542 Char *oldBufData; 543 ScrnBuf oldBufHead; 544 545 unsigned old_jump = scrnHeadSize(screen, 1); 546 unsigned new_jump; 547 unsigned dstCols = ncol; 548 LineData *dstPtrs; 549 LineData *srcPtrs; 550 551 assert(nrow != 0); 552 assert(ncol != 0); 553 554 oldBufData = *sbufaddr; 555 oldBufHead = *sbuf; 556 557 /* 558 * Allocate a new LineData array, retain the old one until we've copied 559 * the data that it points to, as well as non-pointer data, e.g., bufHead. 560 * 561 * Turn on wide-chars temporarily when constructing pointers, since that is 562 * used to decide whether to address the combData[] array, which affects 563 * the length of the LineData structure. 564 */ 565 screen->wide_chars = True; 566 567 new_jump = scrnHeadSize(screen, 1); 568 newBufHead = allocScrnHead(screen, nrow); 569 *sbufaddr = allocScrnData(screen, nrow, ncol, True); 570 setupLineData(screen, newBufHead, *sbufaddr, nrow, ncol, True); 571 572 screen->wide_chars = False; 573 574 srcPtrs = (LineData *) oldBufHead; 575 dstPtrs = (LineData *) newBufHead; 576 for (i = 0; i < nrow; i++) { 577 dstPtrs->bufHead = srcPtrs->bufHead; 578 ExtractScrnData(attribs); 579#if OPT_ISO_COLORS 580 ExtractScrnData(color); 581#endif 582 ExtractScrnData(charData); 583 584 srcPtrs = LineDataAddr(srcPtrs, old_jump); 585 dstPtrs = LineDataAddr(dstPtrs, new_jump); 586 } 587 588 /* Now free the old data */ 589 free(oldBufData); 590 free(oldBufHead); 591 592 *sbuf = newBufHead; 593 594 TRACE(("ReallocateBufOffsets %dx%d ->%p\n", nrow, ncol, *sbufaddr)); 595} 596 597/* 598 * Allocate a new FIFO index. 599 */ 600static void 601ReallocateFifoIndex(XtermWidget xw) 602{ 603 TScreen *screen = TScreenOf(xw); 604 605 if (screen->savelines > 0 && screen->saveBuf_index != NULL) { 606 ScrnBuf newBufHead; 607 LineData *dstPtrs; 608 LineData *srcPtrs; 609 unsigned i; 610 unsigned old_jump = scrnHeadSize(screen, 1); 611 unsigned new_jump; 612 613 screen->wide_chars = True; 614 newBufHead = allocScrnHead(screen, (unsigned) screen->savelines); 615 new_jump = scrnHeadSize(screen, 1); 616 617 srcPtrs = (LineData *) screen->saveBuf_index; 618 dstPtrs = (LineData *) newBufHead; 619 620 for (i = 0; i < (unsigned) screen->savelines; ++i) { 621 memcpy(dstPtrs, srcPtrs, SizeOfLineData); 622 srcPtrs = LineDataAddr(srcPtrs, old_jump); 623 dstPtrs = LineDataAddr(dstPtrs, new_jump); 624 } 625 626 screen->wide_chars = False; 627 free(screen->saveBuf_index); 628 screen->saveBuf_index = newBufHead; 629 } 630} 631 632/* 633 * This function dynamically adds support for wide-characters. 634 */ 635void 636ChangeToWide(XtermWidget xw) 637{ 638 TScreen *screen = TScreenOf(xw); 639 640 if (screen->wide_chars) 641 return; 642 643 TRACE(("ChangeToWide\n")); 644 if (xtermLoadWideFonts(xw, True)) { 645 int whichBuf = screen->whichBuf; 646 647 /* 648 * If we're displaying the alternate screen, switch the pointers back 649 * temporarily so ReallocateBufOffsets() will operate on the proper 650 * data in the alternate buffer. 651 */ 652 if (screen->whichBuf) 653 SwitchBufPtrs(xw, 0); 654 655 ReallocateFifoIndex(xw); 656 657 if (screen->editBuf_index[0]) { 658 ReallocateBufOffsets(xw, 659 &screen->editBuf_index[0], 660 &screen->editBuf_data[0], 661 (unsigned) MaxRows(screen), 662 (unsigned) MaxCols(screen)); 663 } 664 665 if (screen->editBuf_index[1]) { 666 ReallocateBufOffsets(xw, 667 &screen->editBuf_index[1], 668 &screen->editBuf_data[1], 669 (unsigned) MaxRows(screen), 670 (unsigned) MaxCols(screen)); 671 } 672 673 screen->wide_chars = True; 674 screen->visbuf = VisBuf(screen); 675 676 /* 677 * Switch the pointers back before we start painting on the screen. 678 */ 679 if (whichBuf) 680 SwitchBufPtrs(xw, whichBuf); 681 682 update_font_utf8_mode(); 683 SetVTFont(xw, screen->menu_font_number, True, NULL); 684 } 685 TRACE(("...ChangeToWide\n")); 686} 687#endif 688 689/* 690 * Copy cells, no side-effects. 691 */ 692void 693CopyCells(TScreen *screen, LineData *src, LineData *dst, int col, int len, Bool down) 694{ 695 (void) screen; 696 (void) down; 697 698 if (len > 0) { 699 int n; 700 int last = col + len; 701#if OPT_WIDE_CHARS 702 int fix_l = -1; 703 int fix_r = -1; 704#endif 705 706 /* 707 * If the copy overwrites a double-width character which has one half 708 * outside the margin, then we will replace both cells with blanks. 709 */ 710 if_OPT_WIDE_CHARS(screen, { 711 if (col > 0) { 712 if (dst->charData[col] == HIDDEN_CHAR) { 713 if (down) { 714 Clear2Cell(dst, src, col - 1); 715 Clear2Cell(dst, src, col); 716 } else { 717 if (src->charData[col] != HIDDEN_CHAR) { 718 Clear2Cell(dst, src, col - 1); 719 Clear2Cell(dst, src, col); 720 } else { 721 fix_l = col - 1; 722 } 723 } 724 } else if (src->charData[col] == HIDDEN_CHAR) { 725 Clear2Cell(dst, src, col - 1); 726 Clear2Cell(dst, src, col); 727 ++col; 728 } 729 } 730 if (last < (int) src->lineSize) { 731 if (dst->charData[last] == HIDDEN_CHAR) { 732 if (down) { 733 Clear2Cell(dst, src, last - 1); 734 Clear2Cell(dst, src, last); 735 } else { 736 if (src->charData[last] != HIDDEN_CHAR) { 737 Clear2Cell(dst, src, last); 738 } else { 739 fix_r = last - 1; 740 } 741 } 742 } else if (src->charData[last] == HIDDEN_CHAR) { 743 last--; 744 Clear2Cell(dst, src, last); 745 } 746 } 747 }); 748 749 for (n = col; n < last; ++n) { 750 dst->charData[n] = src->charData[n]; 751 dst->attribs[n] = src->attribs[n]; 752 } 753 754 if_OPT_ISO_COLORS(screen, { 755 for (n = col; n < last; ++n) { 756 dst->color[n] = src->color[n]; 757 } 758 }); 759 760 if_OPT_WIDE_CHARS(screen, { 761 size_t off; 762 for (n = col; n < last; ++n) { 763 for_each_combData(off, src) { 764 dst->combData[off][n] = src->combData[off][n]; 765 } 766 } 767 }); 768 769 if_OPT_WIDE_CHARS(screen, { 770 if (fix_l >= 0) { 771 Clear2Cell(dst, src, fix_l); 772 Clear2Cell(dst, src, fix_l + 1); 773 } 774 if (fix_r >= 0) { 775 Clear2Cell(dst, src, fix_r); 776 Clear2Cell(dst, src, fix_r + 1); 777 } 778 }); 779 } 780} 781 782static void 783FillIAttr(IAttr * target, unsigned source, size_t count) 784{ 785 while (count-- != 0) { 786 *target++ = (IAttr) source; 787 } 788} 789 790/* 791 * Clear cells, no side-effects. 792 */ 793void 794ClearCells(XtermWidget xw, int flags, unsigned len, int row, int col) 795{ 796 if (len != 0) { 797 TScreen *screen = TScreenOf(xw); 798 LineData *ld; 799 unsigned n; 800 801 ld = getLineData(screen, row); 802 803 if (((unsigned) col + len) > ld->lineSize) 804 len = (unsigned) (ld->lineSize - col); 805 806 if_OPT_WIDE_CHARS(screen, { 807 if (((unsigned) col + len) < ld->lineSize && 808 ld->charData[col + (int) len] == HIDDEN_CHAR) { 809 len++; 810 } 811 if (col > 0 && 812 ld->charData[col] == HIDDEN_CHAR) { 813 len++; 814 col--; 815 } 816 }); 817 818 flags = (int) ((unsigned) flags | TERM_COLOR_FLAGS(xw)); 819 820 for (n = 0; n < len; ++n) { 821 if_OPT_DEC_RECTOPS(ld->charSeen[(unsigned) col + n] = ' '); 822 ld->charData[(unsigned) col + n] = (CharData) ' '; 823 } 824 825 FillIAttr(ld->attribs + col, (unsigned) flags, (size_t) len); 826 827 if_OPT_ISO_COLORS(screen, { 828 CellColor p = xtermColorPair(xw); 829 for (n = 0; n < len; ++n) { 830 ld->color[(unsigned) col + n] = p; 831 } 832 }); 833 if_OPT_WIDE_CHARS(screen, { 834 size_t off; 835 for_each_combData(off, ld) { 836 memset(ld->combData[off] + col, 0, (size_t) len * sizeof(CharData)); 837 } 838 }); 839 } 840} 841 842/* 843 * Clear data in the screen-structure (no I/O). 844 * Check for wide-character damage as well, clearing the damaged cells. 845 */ 846void 847ScrnClearCells(XtermWidget xw, int row, int col, unsigned len) 848{ 849#if OPT_WIDE_CHARS 850 TScreen *screen = TScreenOf(xw); 851#endif 852 int flags = 0; 853 854 if_OPT_WIDE_CHARS(screen, { 855 int kl; 856 int kr; 857 858 if (DamagedCells(screen, len, &kl, &kr, row, col) 859 && kr >= kl) { 860 ClearCells(xw, flags, (unsigned) (kr - kl + 1), row, kl); 861 } 862 }); 863 ClearCells(xw, flags, len, row, col); 864} 865 866/* 867 * Disown the selection and repaint the area that is highlighted so it is no 868 * longer highlighted. 869 */ 870void 871ScrnDisownSelection(XtermWidget xw) 872{ 873 if (ScrnHaveSelection(TScreenOf(xw))) { 874 TRACE(("ScrnDisownSelection\n")); 875 if (TScreenOf(xw)->keepSelection) { 876 UnhiliteSelection(xw); 877 } else { 878 DisownSelection(xw); 879 } 880 } 881} 882 883/* 884 * Writes str into buf at screen's current row and column. Characters are set 885 * to match flags. 886 */ 887void 888ScrnWriteText(XtermWidget xw, 889 Cardinal offset, 890 Cardinal length, 891 unsigned flags, 892 CellColor cur_fg_bg) 893{ 894 IChar *str = xw->work.write_text + offset; 895 TScreen *screen = TScreenOf(xw); 896 LineData *ld; 897 IAttr *attrs; 898 int avail = MaxCols(screen) - screen->cur_col; 899 IChar *chars; 900#if OPT_DEC_RECTOPS 901 Char *seens; 902#endif 903#if OPT_WIDE_CHARS 904 IChar starcol1; 905#endif 906 unsigned n; 907 unsigned real_width = visual_width(str, length); 908 909 (void) cur_fg_bg; /* quiet compiler warnings when unused */ 910 911 if (real_width + (unsigned) screen->cur_col > (unsigned) MaxCols(screen)) { 912 real_width = (unsigned) (MaxCols(screen) - screen->cur_col); 913 } 914 915 if (avail <= 0) 916 return; 917 if (length > (unsigned) avail) 918 length = (unsigned) avail; 919 if (length == 0 || real_width == 0) 920 return; 921 922 ld = getLineData(screen, screen->cur_row); 923 924 if_OPT_DEC_RECTOPS(seens = ld->charSeen + screen->cur_col); 925 chars = ld->charData + screen->cur_col; 926 attrs = ld->attribs + screen->cur_col; 927 928#if OPT_WIDE_CHARS 929 starcol1 = *chars; 930#endif 931 932 /* 933 * Copy the string onto the line, 934 * writing blanks if we're writing invisible text. 935 */ 936 for (n = 0; n < length; ++n) { 937#if OPT_DEC_RECTOPS 938 if (xw->work.write_sums != NULL) { 939 ld->charSeen[screen->cur_col + (int) n] = xw->work.buffer_sums[n]; 940 ld->charSets[screen->cur_col + (int) n] = xw->work.buffer_sets[n]; 941 } else { 942 seens[n] = (str[n] < 32 943#if OPT_WIDE_CHARS 944 || str[n] > 255 945#endif 946 ) 947 ? ANSI_ESC 948 : (Char) str[n]; 949 } 950#endif /* OPT_DEC_RECTOPS */ 951 chars[n] = str[n]; 952 } 953 954#if OPT_BLINK_TEXT 955 if ((flags & BLINK) && !(screen->blink_as_bold)) { 956 LineSetBlinked(ld); 957 } 958#endif 959 960 if_OPT_WIDE_CHARS(screen, { 961 962 if (real_width != length) { 963 IChar *char1 = chars; 964 if (screen->cur_col 965 && starcol1 == HIDDEN_CHAR 966 && isWide((int) char1[-1])) { 967 char1[-1] = (CharData) ' '; 968 } 969 /* if we are overwriting the right hand half of a 970 wide character, make the other half vanish */ 971 while (length) { 972 int ch = (int) str[0]; 973 974 *char1++ = *str++; 975 length--; 976 977 if (isWide(ch)) { 978 *char1++ = (CharData) HIDDEN_CHAR; 979 } 980 } 981 982 if (*char1 == HIDDEN_CHAR 983 && char1[-1] == HIDDEN_CHAR) { 984 *char1 = (CharData) ' '; 985 } 986 /* if we are overwriting the left hand half of a 987 wide character, make the other half vanish */ 988 } else { 989 if (screen->cur_col 990 && starcol1 == HIDDEN_CHAR 991 && isWide((int) chars[-1])) { 992 chars[-1] = (CharData) ' '; 993 } 994 /* if we are overwriting the right hand half of a 995 wide character, make the other half vanish */ 996 if (chars[length] == HIDDEN_CHAR 997 && isWide((int) chars[length - 1])) { 998 chars[length] = (CharData) ' '; 999 } 1000 } 1001 }); 1002 1003 flags &= ATTRIBUTES; 1004 flags |= CHARDRAWN; 1005 FillIAttr(attrs, flags, (size_t) real_width); 1006 1007 if_OPT_WIDE_CHARS(screen, { 1008 size_t off; 1009 for_each_combData(off, ld) { 1010 memset(ld->combData[off] + screen->cur_col, 1011 0, 1012 real_width * sizeof(CharData)); 1013 } 1014 }); 1015 if_OPT_ISO_COLORS(screen, { 1016 unsigned j; 1017 for (j = 0; j < real_width; ++j) 1018 ld->color[screen->cur_col + (int) j] = cur_fg_bg; 1019 }); 1020 1021#if OPT_WIDE_CHARS 1022 screen->last_written_col = screen->cur_col + (int) real_width - 1; 1023 screen->last_written_row = screen->cur_row; 1024#endif 1025 1026 chararea_clear_displayed_graphics(screen, 1027 screen->cur_col, 1028 screen->cur_row, 1029 (int) real_width, 1); 1030 1031 if_OPT_XMC_GLITCH(screen, { 1032 Resolve_XMC(xw); 1033 }); 1034 1035 return; 1036} 1037 1038/* 1039 * Saves pointers to the n lines beginning at sb + where, and clears the lines 1040 */ 1041static void 1042ScrnClearLines(XtermWidget xw, ScrnBuf sb, int where, unsigned n, unsigned size) 1043{ 1044 TScreen *screen = TScreenOf(xw); 1045 ScrnPtr *base; 1046 unsigned jump = scrnHeadSize(screen, 1); 1047 unsigned i; 1048 LineData *work; 1049 unsigned flags = TERM_COLOR_FLAGS(xw); 1050#if OPT_ISO_COLORS 1051 unsigned j; 1052#endif 1053 1054 TRACE(("ScrnClearLines(%s:where %d, n %d, size %d)\n", 1055 (sb == screen->saveBuf_index) ? "save" : "edit", 1056 where, n, size)); 1057 1058 assert((int) n > 0); 1059 assert(size != 0); 1060 1061 /* save n lines at where */ 1062 SaveLineData(sb, (unsigned) where, (size_t) n); 1063 1064 /* clear contents of old rows */ 1065 base = screen->save_ptr; 1066 for (i = 0; i < n; ++i) { 1067 work = (LineData *) base; 1068 work->bufHead = 0; 1069#if OPT_DEC_CHRSET 1070 SetLineDblCS(work, 0); 1071#endif 1072 1073 memset(work->charData, 0, size * sizeof(CharData)); 1074 if (TERM_COLOR_FLAGS(xw)) { 1075 FillIAttr(work->attribs, flags, (size_t) size); 1076#if OPT_ISO_COLORS 1077 { 1078 CellColor p = xtermColorPair(xw); 1079 for (j = 0; j < size; ++j) { 1080 work->color[j] = p; 1081 } 1082 } 1083#endif 1084 } else { 1085 FillIAttr(work->attribs, 0, (size_t) size); 1086#if OPT_ISO_COLORS 1087 memset(work->color, 0, size * sizeof(work->color[0])); 1088#endif 1089 } 1090 if_OPT_DEC_RECTOPS({ 1091 memset(work->charSeen, 0, size * sizeof(Char)); 1092 memset(work->charSets, 0, size * sizeof(work->charSets[0])); 1093 }); 1094#if OPT_WIDE_CHARS 1095 if (screen->wide_chars) { 1096 size_t off; 1097 1098 for (off = 0; off < work->combSize; ++off) { 1099 memset(work->combData[off], 0, size * sizeof(CharData)); 1100 } 1101 } 1102#endif 1103 base = ScrnBufAddr(base, jump); 1104 } 1105 1106 /* FIXME: this looks wrong -- rcombs */ 1107 chararea_clear_displayed_graphics(screen, 1108 where + screen->savelines, 1109 0, 1110 screen->max_col + 1, 1111 (int) n); 1112} 1113 1114/* 1115 * We're always ensured of having a visible buffer, but may not have saved 1116 * lines. Check the pointer that's sure to work. 1117 */ 1118 1119#define OkAllocBuf(screen) (screen->editBuf_index[0] != NULL) 1120 1121void 1122ScrnAllocBuf(XtermWidget xw) 1123{ 1124 TScreen *screen = TScreenOf(xw); 1125 1126 if (!OkAllocBuf(screen)) { 1127 int nrows = MaxRows(screen); 1128 1129 TRACE(("ScrnAllocBuf %dx%d (%d)\n", 1130 nrows, MaxCols(screen), screen->savelines)); 1131 1132 if (screen->savelines != 0) { 1133 /* for FIFO, we only need space for the index - addScrollback inits */ 1134 screen->saveBuf_index = allocScrnHead(screen, 1135 (unsigned) (screen->savelines)); 1136 } else { 1137 screen->saveBuf_index = NULL; 1138 } 1139 screen->editBuf_index[0] = allocScrnBuf(xw, 1140 (unsigned) nrows, 1141 (unsigned) MaxCols(screen), 1142 &screen->editBuf_data[0]); 1143 screen->visbuf = VisBuf(screen); 1144 } 1145 return; 1146} 1147 1148size_t 1149ScrnPointers(TScreen *screen, size_t len) 1150{ 1151 size_t result = scrnHeadSize(screen, (unsigned) len); 1152 1153 if (result > screen->save_len) { 1154 if (screen->save_len) 1155 screen->save_ptr = (ScrnPtr *) realloc(screen->save_ptr, result); 1156 else 1157 screen->save_ptr = (ScrnPtr *) malloc(result); 1158 screen->save_len = len; 1159 if (screen->save_ptr == NULL) 1160 SysError(ERROR_SAVE_PTR); 1161 } 1162 TRACE2(("ScrnPointers %ld ->%p\n", (long) len, screen->save_ptr)); 1163 return result; 1164} 1165 1166/* 1167 * Inserts n blank lines at sb + where, treating last as a bottom margin. 1168 */ 1169void 1170ScrnInsertLine(XtermWidget xw, ScrnBuf sb, int last, int where, unsigned n) 1171{ 1172 TScreen *screen = TScreenOf(xw); 1173 unsigned size = (unsigned) MaxCols(screen); 1174 1175 TRACE(("ScrnInsertLine(last %d, where %d, n %d, size %d)\n", 1176 last, where, n, size)); 1177 1178 assert(where >= 0); 1179 assert(last >= where); 1180 1181 assert((int) n > 0); 1182 assert(size != 0); 1183 1184 /* save n lines at bottom */ 1185 ScrnClearLines(xw, sb, (last -= (int) n - 1), n, size); 1186 if (last < 0) { 1187 TRACE(("...remainder of screen is blank\n")); 1188 return; 1189 } 1190 1191 /* 1192 * WARNING, overlapping copy operation. Move down lines (pointers). 1193 * 1194 * +----|---------|--------+ 1195 * 1196 * is copied in the array to: 1197 * 1198 * +--------|---------|----+ 1199 */ 1200 assert(last >= where); 1201 /* 1202 * This will never shift from the saveBuf to editBuf, so there is no need 1203 * to handle that case. 1204 */ 1205 MoveLineData(sb, 1206 (unsigned) (where + (int) n), 1207 (unsigned) where, 1208 (unsigned) (last - where)); 1209 1210 /* reuse storage for new lines at where */ 1211 RestoreLineData(sb, (unsigned) where, n); 1212} 1213 1214/* 1215 * Deletes n lines at sb + where, treating last as a bottom margin. 1216 */ 1217void 1218ScrnDeleteLine(XtermWidget xw, ScrnBuf sb, int last, int where, unsigned n) 1219{ 1220 TScreen *screen = TScreenOf(xw); 1221 unsigned size = (unsigned) MaxCols(screen); 1222 1223 TRACE(("ScrnDeleteLine(%s:last %d, where %d, n %d, size %d)\n", 1224 (sb == screen->saveBuf_index) ? "save" : "edit", 1225 last, where, n, size)); 1226 1227 assert(where >= 0); 1228 assert(last >= where + (int) n - 1); 1229 1230 assert((int) n > 0); 1231 assert(size != 0); 1232 1233 /* move up lines */ 1234 last -= ((int) n - 1); 1235 1236 if (inSaveBuf(screen, sb, where)) { 1237 1238 /* we shouldn't be editing the saveBuf, only scroll into it */ 1239 assert(last >= screen->savelines); 1240 1241 if (sb != NULL) { 1242 /* copy lines from editBuf to saveBuf (allocating as we go...) */ 1243 saveEditBufLines(screen, n); 1244 } 1245 1246 /* adjust variables to fall-thru into changes only to editBuf */ 1247 TRACE(("...adjusting variables, to work on editBuf alone\n")); 1248 last -= screen->savelines; 1249 where = 0; 1250 sb = screen->visbuf; 1251 } 1252 1253 /* 1254 * Scroll the visible buffer (editBuf). 1255 */ 1256 ScrnClearLines(xw, sb, where, n, size); 1257 1258 MoveLineData(sb, 1259 (unsigned) where, 1260 (unsigned) (where + (int) n), 1261 (size_t) (last - where)); 1262 1263 /* reuse storage for new bottom lines */ 1264 RestoreLineData(sb, (unsigned) last, n); 1265} 1266 1267/* 1268 * Inserts n blanks in screen at current row, col. Size is the size of each 1269 * row. 1270 */ 1271void 1272ScrnInsertChar(XtermWidget xw, unsigned n) 1273{ 1274#define MemMove(data) \ 1275 for (j = last; j >= (col + (int) n); --j) \ 1276 data[j] = data[j - (int) n] 1277 1278 TScreen *screen = TScreenOf(xw); 1279 int first = ScrnLeftMargin(xw); 1280 int last = ScrnRightMargin(xw); 1281 int row = screen->cur_row; 1282 int col = screen->cur_col; 1283 LineData *ld; 1284 1285 if (col < first || col > last) { 1286 TRACE(("ScrnInsertChar - col %d outside [%d..%d]\n", col, first, last)); 1287 return; 1288 } else if (last < (col + (int) n)) { 1289 n = (unsigned) (last + 1 - col); 1290 } 1291 1292 assert(screen->cur_col >= 0); 1293 assert(screen->cur_row >= 0); 1294 assert((int) n >= 0); 1295 assert((last + 1) >= (int) n); 1296 1297 if_OPT_WIDE_CHARS(screen, { 1298 int xx = screen->cur_row; 1299 int kl; 1300 int kr = screen->cur_col; 1301 if (DamagedCells(screen, n, &kl, (int *) 0, xx, kr) && kr > kl) { 1302 ClearCells(xw, 0, (unsigned) (kr - kl + 1), row, kl); 1303 } 1304 kr = last - (int) n + 1; 1305 if (DamagedCells(screen, n, &kl, (int *) 0, xx, kr) && kr > kl) { 1306 ClearCells(xw, 0, (unsigned) (kr - kl + 1), row, kl); 1307 } 1308 }); 1309 1310 if ((ld = getLineData(screen, row)) != NULL) { 1311 int j; 1312 1313 MemMove(ld->charData); 1314 MemMove(ld->attribs); 1315 1316 if_OPT_ISO_COLORS(screen, { 1317 MemMove(ld->color); 1318 }); 1319 if_OPT_WIDE_CHARS(screen, { 1320 size_t off; 1321 for_each_combData(off, ld) { 1322 MemMove(ld->combData[off]); 1323 } 1324 }); 1325 } 1326 ClearCells(xw, CHARDRAWN, n, row, col); 1327 1328#undef MemMove 1329} 1330 1331/* 1332 * Deletes n characters at current row, col. 1333 */ 1334void 1335ScrnDeleteChar(XtermWidget xw, unsigned n) 1336{ 1337#define MemMove(data) \ 1338 for (j = col; j < last - (int) n; ++j) \ 1339 data[j] = data[j + (int) n] 1340 1341 TScreen *screen = TScreenOf(xw); 1342 int first = ScrnLeftMargin(xw); 1343 int last = ScrnRightMargin(xw) + 1; 1344 int row = screen->cur_row; 1345 int col = screen->cur_col; 1346 LineData *ld; 1347 1348 if (col < first || col > last) { 1349 TRACE(("ScrnDeleteChar - col %d outside [%d..%d]\n", col, first, last)); 1350 return; 1351 } else if (last <= (col + (int) n)) { 1352 n = (unsigned) (last - col); 1353 } 1354 1355 assert(screen->cur_col >= 0); 1356 assert(screen->cur_row >= 0); 1357 assert((int) n >= 0); 1358 assert(last >= (int) n); 1359 1360 if_OPT_WIDE_CHARS(screen, { 1361 int kl; 1362 int kr; 1363 if (DamagedCells(screen, n, &kl, &kr, 1364 screen->cur_row, 1365 screen->cur_col)) 1366 ClearCells(xw, 0, (unsigned) (kr - kl + 1), row, kl); 1367 }); 1368 1369 if ((ld = getLineData(screen, row)) != NULL) { 1370 int j; 1371 1372 MemMove(ld->charData); 1373 MemMove(ld->attribs); 1374 1375 if_OPT_ISO_COLORS(screen, { 1376 MemMove(ld->color); 1377 }); 1378 if_OPT_WIDE_CHARS(screen, { 1379 size_t off; 1380 for_each_combData(off, ld) { 1381 MemMove(ld->combData[off]); 1382 } 1383 }); 1384 LineClrWrapped(ld); 1385 ShowWrapMarks(xw, row, ld); 1386 } 1387 ClearCells(xw, 0, n, row, (last - (int) n)); 1388 1389#undef MemMove 1390} 1391 1392#define WhichMarkGC(set) (set ? 1 : 0) 1393#define WhichMarkColor(set) T_COLOR(screen, (set ? TEXT_CURSOR : TEXT_BG)) 1394 1395void 1396FreeMarkGCs(XtermWidget xw) 1397{ 1398 TScreen *const screen = TScreenOf(xw); 1399 Display *const display = screen->display; 1400 VTwin *vwin = WhichVWin(screen); 1401 int which; 1402 1403 for (which = 0; which < 2; ++which) { 1404 if (vwin->marker_gc[which] != NULL) { 1405 XFreeGC(display, vwin->marker_gc[which]); 1406 vwin->marker_gc[which] = NULL; 1407 } 1408 } 1409} 1410 1411static GC 1412MakeMarkGC(XtermWidget xw, Bool set) 1413{ 1414 TScreen *const screen = TScreenOf(xw); 1415 VTwin *vwin = WhichVWin(screen); 1416 int which = WhichMarkGC(set); 1417 1418 if (vwin->marker_gc[which] == NULL) { 1419 Display *const display = screen->display; 1420 Window const drawable = VDrawable(screen); 1421 XGCValues xgcv; 1422 XtGCMask mask = GCForeground; 1423 1424 memset(&xgcv, 0, sizeof(xgcv)); 1425 xgcv.foreground = WhichMarkColor(set); 1426 vwin->marker_gc[which] = XCreateGC(display, 1427 drawable, 1428 mask, 1429 &xgcv); 1430 } 1431 return vwin->marker_gc[which]; 1432} 1433 1434/* 1435 * This is useful for debugging both xterm and applications that may manipulate 1436 * its line-wrapping state. 1437 */ 1438void 1439ShowWrapMarks(XtermWidget xw, int row, CLineData *ld) 1440{ 1441 TScreen *screen = TScreenOf(xw); 1442 if (screen->show_wrap_marks && row >= 0 && row <= screen->max_row) { 1443 Bool set = (Bool) LineTstWrapped(ld); 1444 int y = row * FontHeight(screen) + screen->border; 1445 int x = LineCursorX(screen, ld, screen->max_col + 1); 1446 1447 TRACE2(("ShowWrapMarks %d:%s\n", row, BtoS(set))); 1448 1449 XFillRectangle(screen->display, 1450 VDrawable(screen), 1451 MakeMarkGC(xw, set), 1452 x, y, 1453 (unsigned) screen->border, 1454 (unsigned) FontHeight(screen)); 1455 } 1456} 1457 1458#if OPT_BLOCK_SELECT 1459/* 1460 * Return the start and end cols of a block selection 1461 */ 1462static void 1463blockSelectBounds(TScreen *screen, 1464 int *start, 1465 int *end) 1466{ 1467 assert(screen->blockSelecting); 1468 if (screen->startH.col < screen->endH.col) { 1469 *start = screen->startH.col; 1470 *end = screen->endH.col; 1471 } else { 1472 *start = screen->endH.col; 1473 *end = screen->startH.col; 1474 } 1475} 1476 1477/* 1478 * Return 1 if any part of [col, maxcol] intersects with the selection. 1479 */ 1480static int 1481intersectsSelection(TScreen *screen, 1482 int row, 1483 int col, 1484 int maxcol) 1485{ 1486 if (screen->blockSelecting) { 1487 int start, end; 1488 blockSelectBounds(screen, &start, &end); 1489 return start != end 1490 && (row >= screen->startH.row && row <= screen->endH.row) 1491 && ((start >= col && start <= maxcol) 1492 || (end > col && end < maxcol)) ? 1 : 0; 1493 } 1494 return !(row < screen->startH.row || row > screen->endH.row 1495 || (row == screen->startH.row && maxcol < screen->startH.col) 1496 || (row == screen->endH.row && col >= screen->endH.col)) ? 1 : 0; 1497} 1498 1499/* 1500 * If there are any parts of [col, maxcol] not in the selection, 1501 * invoke ScrnRefresh on them, then adjust [col, maxcol] to be fully 1502 * inside the selection. The intent is to optimize the loop at the 1503 * end of ScrnRefresh, so that we are painting either all highlighted 1504 * or all unhighlighted cells. 1505 */ 1506static void 1507recurseForNotSelectedAndAdjust(XtermWidget xw, 1508 int row, 1509 int *col, 1510 int *maxcol, 1511 int force) 1512{ 1513 TScreen *screen = TScreenOf(xw); 1514 if (screen->blockSelecting) { 1515 int start, end; 1516 blockSelectBounds(screen, &start, &end); 1517 if (*col < start) { 1518 ScrnRefresh(xw, row, *col, 1, start - *col, force); 1519 *col = start; 1520 } 1521 if (*maxcol >= end) { 1522 ScrnRefresh(xw, row, end, 1, *maxcol - end + 1, force); 1523 *maxcol = end - 1; 1524 } 1525 } else { 1526 if (row == screen->startH.row && *col < screen->startH.col) { 1527 ScrnRefresh(xw, row, *col, 1, screen->startH.col - *col, 1528 force); 1529 *col = screen->startH.col; 1530 } 1531 if (row == screen->endH.row && *maxcol >= screen->endH.col) { 1532 ScrnRefresh(xw, row, screen->endH.col, 1, 1533 *maxcol - screen->endH.col + 1, force); 1534 *maxcol = screen->endH.col - 1; 1535 } 1536 } 1537} 1538#else 1539#define intersectsSelection(screen, row, col, maxcol) \ 1540 ((row >= screen->startH.row && row <= screen->endH.row) \ 1541 && (row != screen->startH.row || maxcol >= screen->startH.col) \ 1542 && (row != screen->endH.row || col < screen->endH.col)) 1543#endif /* OPT_BLOCK_SELECT */ 1544 1545/* 1546 * Repaints the area enclosed by the parameters. 1547 * Requires: (toprow, leftcol), (toprow + nrows, leftcol + ncols) are 1548 * coordinates of characters in screen; 1549 * nrows and ncols positive. 1550 * all dimensions are based on single-characters. 1551 */ 1552void 1553ScrnRefresh(XtermWidget xw, 1554 int toprow, 1555 int leftcol, 1556 int nrows, 1557 int ncols, 1558 Bool force) /* ... leading/trailing spaces */ 1559{ 1560 TScreen *screen = TScreenOf(xw); 1561 XTermDraw params; 1562 CLineData *ld; 1563 int y = toprow * FontHeight(screen) + screen->border; 1564 int row; 1565 int maxrow = toprow + nrows - 1; 1566 int scrollamt = screen->scroll_amt; 1567 unsigned gc_changes = 0; 1568#ifdef __CYGWIN__ 1569 static char first_time = 1; 1570#endif 1571 static int recurse = 0; 1572#if OPT_WIDE_ATTRS 1573 unsigned old_attrs = xw->flags; 1574#endif 1575 1576 TRACE(("ScrnRefresh top %d (%d,%d) - (%d,%d)%s " TRACE_L "\n", 1577 screen->topline, toprow, leftcol, 1578 nrows, ncols, 1579 force ? " force" : "")); 1580 1581#if OPT_STATUS_LINE 1582 if (!recurse && (maxrow == screen->max_row) && IsStatusShown(screen)) { 1583 TRACE(("...allow a row for status-line\n")); 1584 nrows += StatusLineRows; 1585 maxrow += StatusLineRows; 1586 } 1587#endif 1588 (void) recurse; 1589 ++recurse; 1590 1591 if (screen->cursorp.col >= leftcol 1592 && screen->cursorp.col <= (leftcol + ncols - 1) 1593 && screen->cursorp.row >= ROW2INX(screen, toprow) 1594 && screen->cursorp.row <= ROW2INX(screen, maxrow)) 1595 screen->cursor_state = OFF; 1596 1597 for (row = toprow; row <= maxrow; y += FontHeight(screen), row++) { 1598#if OPT_ISO_COLORS 1599 CellColor *fb = NULL; 1600#define ColorOf(col) (fb ? fb[col] : initCColor) 1601#endif 1602#if OPT_WIDE_CHARS 1603 int wideness = 0; 1604#endif 1605#define BLANK_CEL(cell) (chars[cell] == ' ') 1606 IChar *chars; 1607 const IAttr *attrs; 1608 int col = leftcol; 1609 int maxcol = leftcol + ncols - 1; 1610 int hi_col = maxcol; 1611 int lastind; 1612 unsigned flags; 1613 unsigned test; 1614 CellColor fg_bg = initCColor; 1615 Pixel fg = 0, bg = 0; 1616 int x; 1617 GC gc; 1618 Bool hilite; 1619 1620 (void) fg; 1621 (void) bg; 1622#if !OPT_ISO_COLORS 1623 fg_bg = 0; 1624#endif 1625 1626 if (row < screen->top_marg || row > screen->bot_marg) 1627 lastind = row; 1628 else 1629 lastind = row - scrollamt; 1630 1631 if (lastind < 0 || lastind > LastRowNumber(screen)) 1632 continue; 1633 1634 TRACE2(("ScrnRefresh row=%d lastind=%d ->%d\n", 1635 row, lastind, ROW2INX(screen, lastind))); 1636 1637 if ((ld = getLineData(screen, ROW2INX(screen, lastind))) == NULL 1638 || ld->charData == NULL 1639 || ld->attribs == NULL) { 1640 break; 1641 } 1642 1643 ShowWrapMarks(xw, lastind, ld); 1644 1645 if (maxcol >= (int) ld->lineSize) { 1646 maxcol = ld->lineSize - 1; 1647 hi_col = maxcol; 1648 } 1649 1650 chars = ld->charData; 1651 attrs = ld->attribs; 1652 1653 if_OPT_WIDE_CHARS(screen, { 1654 /* This fixes an infinite recursion bug, that leads 1655 to display anomalies. It seems to be related to 1656 problems with the selection. */ 1657 if (recurse < 3) { 1658 /* adjust to redraw all of a widechar if we just wanted 1659 to draw the right hand half */ 1660 if (leftcol > 0 && 1661 chars[leftcol] == HIDDEN_CHAR && 1662 isWide((int) chars[leftcol - 1])) { 1663 leftcol--; 1664 ncols++; 1665 col = leftcol; 1666 } 1667 } else { 1668 xtermWarning("Unexpected recursion drawing hidden characters.\n"); 1669 } 1670 }); 1671 1672 if (!intersectsSelection(screen, row, col, maxcol)) { 1673#if OPT_DEC_CHRSET 1674 /* 1675 * Temporarily change dimensions to double-sized characters so 1676 * we can reuse the recursion on this function. 1677 */ 1678 if (CSET_DOUBLE(GetLineDblCS(ld))) { 1679 col /= 2; 1680 maxcol /= 2; 1681 } 1682#endif 1683 /* 1684 * If row does not intersect selection; don't hilite blanks 1685 * unless block selecting. 1686 */ 1687 if (!force 1688#if OPT_BLOCK_SELECT 1689 && !screen->blockSelecting 1690#endif 1691 ) { 1692 while (col <= maxcol && (attrs[col] & ~BOLD) == 0 && 1693 BLANK_CEL(col)) 1694 col++; 1695 1696 while (col <= maxcol && (attrs[maxcol] & ~BOLD) == 0 && 1697 BLANK_CEL(maxcol)) 1698 maxcol--; 1699 } 1700#if OPT_DEC_CHRSET 1701 if (CSET_DOUBLE(GetLineDblCS(ld))) { 1702 col *= 2; 1703 maxcol *= 2; 1704 } 1705#endif 1706 hilite = False; 1707 } else { 1708#if OPT_BLOCK_SELECT 1709 /* row intersects selection; recurse for the unselected pieces 1710 * of col to maxcol, then adjust col and maxcol so that they are 1711 * strictly inside the selection. 1712 */ 1713 recurseForNotSelectedAndAdjust(xw, row, &col, &maxcol, force); 1714#else 1715 /* row intersects selection; split into pieces of single type */ 1716 if (row == screen->startH.row && col < screen->startH.col) { 1717 ScrnRefresh(xw, row, col, 1, screen->startH.col - col, 1718 force); 1719 col = screen->startH.col; 1720 } 1721 if (row == screen->endH.row && maxcol >= screen->endH.col) { 1722 ScrnRefresh(xw, row, screen->endH.col, 1, 1723 maxcol - screen->endH.col + 1, force); 1724 maxcol = screen->endH.col - 1; 1725 } 1726#endif 1727 1728 /* 1729 * If we're highlighting because the user is doing cut/paste, 1730 * trim the trailing blanks from the highlighted region so we're 1731 * showing the actual extent of the text that'll be cut. If 1732 * we're selecting a blank line, we'll highlight one column 1733 * anyway. 1734 * 1735 * We don't do this if the mouse-hilite mode is set because that 1736 * would be too confusing. The same applies to block select mode. 1737 * 1738 * The default if the highlightSelection resource isn't set will 1739 * highlight the whole width of the terminal, which is easy to 1740 * see, but harder to use (because trailing blanks aren't as 1741 * apparent). 1742 */ 1743 if (screen->highlight_selection 1744#if OPT_BLOCK_SELECT 1745 && !screen->blockSelecting 1746#endif 1747 && screen->send_mouse_pos != VT200_HIGHLIGHT_MOUSE) { 1748 hi_col = screen->max_col; 1749 while (hi_col > 0 && !(attrs[hi_col] & CHARDRAWN)) 1750 hi_col--; 1751 } 1752 1753 /* remaining piece should be hilited */ 1754 hilite = True; 1755 } 1756 1757 if (col > maxcol) 1758 continue; 1759 1760 /* 1761 * Go back to double-sized character dimensions if the line has 1762 * double-width characters. Note that 'hi_col' is already in the 1763 * right units. 1764 */ 1765 if_OPT_DEC_CHRSET({ 1766 if (CSET_DOUBLE(GetLineDblCS(ld))) { 1767 col /= 2; 1768 maxcol /= 2; 1769 } 1770 }); 1771 1772 flags = attrs[col]; 1773 1774 if_OPT_WIDE_CHARS(screen, { 1775 wideness = isWide((int) chars[col]); 1776 }); 1777 1778 if_OPT_ISO_COLORS(screen, { 1779 fb = ld->color; 1780 fg_bg = ColorOf(col); 1781 fg = extract_fg(xw, fg_bg, flags); 1782 bg = extract_bg(xw, fg_bg, flags); 1783 }); 1784#if OPT_WIDE_ATTRS 1785 old_attrs = xtermUpdateItalics(xw, flags, old_attrs); 1786#endif 1787 gc = updatedXtermGC(xw, flags, fg_bg, hilite); 1788 gc_changes |= (flags & (FG_COLOR | BG_COLOR)); 1789 1790 x = LineCursorX(screen, ld, col); 1791 lastind = col; 1792 1793 for (; col <= maxcol; col++) { 1794 if ( 1795#if OPT_WIDE_CHARS 1796 (chars[col] != HIDDEN_CHAR) && 1797#endif 1798 ((attrs[col] != flags) 1799 || (hilite && (col > hi_col)) 1800#if OPT_ISO_COLORS 1801 || ((flags & FG_COLOR) 1802 && (extract_fg(xw, ColorOf(col), attrs[col]) != fg)) 1803 || ((flags & BG_COLOR) 1804 && (extract_bg(xw, ColorOf(col), attrs[col]) != bg)) 1805#endif 1806#if OPT_WIDE_CHARS 1807 || (isWide((int) chars[col]) != wideness) 1808#endif 1809 ) 1810 ) { 1811 assert(col >= lastind); 1812 TRACE(("ScrnRefresh looping drawXtermText %d..%d:%s\n", 1813 lastind, col, 1814 visibleIChars((&chars[lastind]), 1815 (unsigned) (col - lastind)))); 1816 1817 test = flags; 1818 checkVeryBoldColors(test, fg); 1819 1820 /* *INDENT-EQLS* */ 1821 params.xw = xw; 1822 params.attr_flags = (test & DRAWX_MASK); 1823 params.draw_flags = 0; 1824 params.this_chrset = GetLineDblCS(ld); 1825 params.real_chrset = CSET_SWL; 1826 params.on_wide = 0; 1827 1828 x = drawXtermText(¶ms, 1829 gc, x, y, 1830 &chars[lastind], 1831 (unsigned) (col - lastind)); 1832 1833 if_OPT_WIDE_CHARS(screen, { 1834 int i; 1835 size_t off; 1836 1837 params.draw_flags = NOBACKGROUND; 1838 1839 for_each_combData(off, ld) { 1840 IChar *com_off = ld->combData[off]; 1841 1842 for (i = lastind; i < col; i++) { 1843 int my_x = LineCursorX(screen, ld, i); 1844 IChar base = chars[i]; 1845 1846 if ((params.on_wide = isWide((int) base)) != 0) 1847 my_x = LineCursorX(screen, ld, i - 1); 1848 1849 if (com_off[i] != 0) 1850 drawXtermText(¶ms, 1851 gc, my_x, y, 1852 com_off + i, 1853 1); 1854 } 1855 } 1856 }); 1857 1858 resetXtermGC(xw, flags, hilite); 1859 1860 lastind = col; 1861 1862 if (hilite && (col > hi_col)) 1863 hilite = False; 1864 1865 flags = attrs[col]; 1866 if_OPT_ISO_COLORS(screen, { 1867 fg_bg = ColorOf(col); 1868 fg = extract_fg(xw, fg_bg, flags); 1869 bg = extract_bg(xw, fg_bg, flags); 1870 }); 1871 if_OPT_WIDE_CHARS(screen, { 1872 wideness = isWide((int) chars[col]); 1873 }); 1874 1875#if OPT_WIDE_ATTRS 1876 old_attrs = xtermUpdateItalics(xw, flags, old_attrs); 1877#endif 1878 gc = updatedXtermGC(xw, flags, fg_bg, hilite); 1879 gc_changes |= (flags & (FG_COLOR | BG_COLOR)); 1880 } 1881 1882 if (chars[col] == 0) { 1883 chars[col] = ' '; 1884 } 1885 } 1886 1887 assert(col >= lastind); 1888 TRACE(("ScrnRefresh calling drawXtermText %d..%d:%s\n", 1889 lastind, col, 1890 visibleIChars(&chars[lastind], (unsigned) (col - lastind)))); 1891 1892 test = flags; 1893 checkVeryBoldColors(test, fg); 1894 1895 /* *INDENT-EQLS* */ 1896 params.xw = xw; 1897 params.attr_flags = (test & DRAWX_MASK); 1898 params.draw_flags = 0; 1899 params.this_chrset = GetLineDblCS(ld); 1900 params.real_chrset = CSET_SWL; 1901 params.on_wide = 0; 1902 1903 drawXtermText(¶ms, 1904 gc, x, y, 1905 &chars[lastind], 1906 (unsigned) (col - lastind)); 1907 1908 if_OPT_WIDE_CHARS(screen, { 1909 int i; 1910 size_t off; 1911 1912 params.draw_flags = NOBACKGROUND; 1913 1914 for_each_combData(off, ld) { 1915 IChar *com_off = ld->combData[off]; 1916 1917 for (i = lastind; i < col; i++) { 1918 int my_x = LineCursorX(screen, ld, i); 1919 int base = (int) chars[i]; 1920 1921 if ((params.on_wide = isWide(base)) != 0) 1922 my_x = LineCursorX(screen, ld, i - 1); 1923 1924 if (com_off[i] != 0) 1925 drawXtermText(¶ms, 1926 gc, my_x, y, 1927 com_off + i, 1928 1); 1929 } 1930 } 1931 }); 1932 1933 resetXtermGC(xw, flags, hilite); 1934 } 1935 1936 refresh_displayed_graphics(xw, leftcol, toprow, ncols, nrows); 1937 1938 /* 1939 * If we're in color mode, reset the various GC's to the current 1940 * screen foreground and background so that other functions (e.g., 1941 * ClearRight) will get the correct colors. 1942 */ 1943#if OPT_WIDE_ATTRS 1944 (void) xtermUpdateItalics(xw, xw->flags, old_attrs); 1945#endif 1946 if_OPT_ISO_COLORS(screen, { 1947 if (gc_changes & FG_COLOR) 1948 SGR_Foreground(xw, xw->cur_foreground); 1949 if (gc_changes & BG_COLOR) 1950 SGR_Background(xw, xw->cur_background); 1951 }); 1952 (void) gc_changes; 1953 1954#if defined(__CYGWIN__) && defined(TIOCSWINSZ) 1955 if (first_time == 1) { 1956 first_time = 0; 1957 update_winsize(screen, nrows, ncols, xw->core.height, xw->core.width); 1958 } 1959#endif 1960 recurse--; 1961 1962 TRACE((TRACE_R " ScrnRefresh\n")); 1963 return; 1964} 1965 1966/* 1967 * Call this wrapper to ScrnRefresh() when the data has changed. If the 1968 * refresh region overlaps the selection, we will release the primary selection. 1969 */ 1970void 1971ScrnUpdate(XtermWidget xw, 1972 int toprow, 1973 int leftcol, 1974 int nrows, 1975 int ncols, 1976 Bool force) /* ... leading/trailing spaces */ 1977{ 1978 TScreen *screen = TScreenOf(xw); 1979 1980 if (ScrnHaveSelection(screen) 1981 && (toprow <= screen->endH.row) 1982 && (toprow + nrows - 1 >= screen->startH.row)) { 1983 ScrnDisownSelection(xw); 1984 } 1985 ScrnRefresh(xw, toprow, leftcol, nrows, ncols, force); 1986} 1987 1988/* 1989 * Sets the rows first though last of the buffer of screen to spaces. 1990 * Requires first <= last; first, last are rows of screen->buf. 1991 */ 1992void 1993ClearBufRows(XtermWidget xw, 1994 int first, 1995 int last) 1996{ 1997 TScreen *screen = TScreenOf(xw); 1998 unsigned len = (unsigned) MaxCols(screen); 1999 int row; 2000 2001 TRACE(("ClearBufRows %d..%d\n", first, last)); 2002 for (row = first; row <= last; row++) { 2003 LineData *ld = getLineData(screen, row); 2004 if (ld != NULL) { 2005 if_OPT_DEC_CHRSET({ 2006 /* clearing the whole row resets the doublesize characters */ 2007 SetLineDblCS(ld, CSET_SWL); 2008 }); 2009 LineClrWrapped(ld); 2010 ShowWrapMarks(xw, row, ld); 2011 ClearCells(xw, 0, len, row, 0); 2012 } 2013 } 2014} 2015 2016#if OPT_STATUS_LINE 2017static LineData * 2018freeLineData(TScreen *screen, LineData *source) 2019{ 2020 (void) screen; 2021 if (source != NULL) { 2022 free(source->attribs); 2023 free(source->charData); 2024#if OPT_ISO_COLORS 2025 free(source->color); 2026#endif 2027#if OPT_WIDE_CHARS 2028 if_OPT_WIDE_CHARS(screen, { 2029 size_t off; 2030 for_each_combData(off, source) { 2031 free(source->combData[off]); 2032 } 2033 }); 2034#endif 2035 free(source); 2036 source = NULL; 2037 } 2038 return source; 2039} 2040 2041#define ALLOC_IT(field) \ 2042 if (result != NULL) { \ 2043 if ((result->field = calloc((size_t) ncol, sizeof(*result->field))) == NULL) { \ 2044 result = freeLineData(screen, result); \ 2045 } \ 2046 } 2047 2048/* 2049 * Allocate a temporary LineData structure, which is not part of the index. 2050 */ 2051static LineData * 2052allocLineData(TScreen *screen, LineData *source) 2053{ 2054 LineData *result = NULL; 2055 Dimension ncol = (Dimension) (source->lineSize + 1); 2056 size_t size = sizeof(*result); 2057#if OPT_WIDE_CHARS 2058 size += source->combSize * sizeof(result->combData[0]); 2059#endif 2060 if ((result = calloc((size_t) 1, size)) != NULL) { 2061 result->lineSize = ncol; 2062 ALLOC_IT(attribs); 2063#if OPT_ISO_COLORS 2064 ALLOC_IT(color); 2065#endif 2066#if OPT_DEC_RECTOPS 2067 ALLOC_IT(charSeen); 2068 ALLOC_IT(charData); 2069#endif 2070#if OPT_WIDE_CHARS 2071 ALLOC_IT(charSets); 2072 if_OPT_WIDE_CHARS(screen, { 2073 size_t off; 2074 for_each_combData(off, source) { 2075 ALLOC_IT(combData[off]); 2076 } 2077 }); 2078#endif 2079 } 2080 return result; 2081} 2082 2083#undef ALLOC_IT 2084#endif /* OPT_STATUS_LINE */ 2085 2086/* 2087 Resizes screen: 2088 1. If new window would have fractional characters, sets window size so as to 2089 discard fractional characters and returns -1. 2090 Minimum screen size is 1 X 1. 2091 Note that this causes another ExposeWindow event. 2092 2. Enlarges screen->buf if necessary. New space is appended to the bottom 2093 and to the right 2094 3. Reduces screen->buf if necessary. Old space is removed from the bottom 2095 and from the right 2096 4. Cursor is positioned as closely to its former position as possible 2097 5. Sets screen->max_row and screen->max_col to reflect new size 2098 6. Maintains the inner border (and clears the border on the screen). 2099 7. Clears origin mode and sets scrolling region to be entire screen. 2100 */ 2101void 2102ScreenResize(XtermWidget xw, 2103 int width, 2104 int height, 2105 unsigned *flags) 2106{ 2107 TScreen *screen = TScreenOf(xw); 2108 int rows, cols; 2109 const int border = 2 * screen->border; 2110 int move_down_by = 0; 2111 Boolean forced = False; 2112 2113#if OPT_STATUS_LINE 2114 LineData *savedStatus = NULL; 2115#endif 2116 2117 TRACE(("ScreenResize %dx%d border 2*%d font %dx%d\n", 2118 height, width, screen->border, 2119 FontHeight(screen), FontWidth(screen))); 2120 2121 assert(width > 0); 2122 assert(height > 0); 2123 2124 TRACE(("...computing rows/cols: %.2f %.2f\n", 2125 (double) (height - border) / FontHeight(screen), 2126 (double) (width - border - ScrollbarWidth(screen)) / FontWidth(screen))); 2127 2128 rows = (height - border) / FontHeight(screen); 2129 cols = (width - border - ScrollbarWidth(screen)) / FontWidth(screen); 2130 if (rows < 1) 2131 rows = 1; 2132 if (cols < 1) 2133 cols = 1; 2134 2135#if OPT_STATUS_LINE 2136 /* 2137 * The dimensions passed to this function include the status-line. 2138 * Discount that here (to obtain the actual rows/columns), and save 2139 * the contents of the status-line, to repaint it after resizing. 2140 */ 2141 TRACE(("...StatusShown %d/%d\n", IsStatusShown(screen), screen->status_shown)); 2142 if (IsStatusShown(screen)) { 2143 int oldRow = MaxRows(screen); 2144 int newRow = rows - StatusLineRows; 2145 LineData *oldLD; 2146 TRACE(("...status line is currently on row %d(%d-%d) vs %d\n", 2147 oldRow, 2148 MaxRows(screen), 2149 (screen->status_shown ? 0 : StatusLineRows), 2150 rows)); 2151 oldLD = getLineData(screen, oldRow); 2152 TRACE(("...copying:%s\n", 2153 visibleIChars(oldLD->charData, 2154 oldLD->lineSize))); 2155 TRACE(("...will move status-line from row %d to %d\n", 2156 oldRow, 2157 newRow)); 2158 savedStatus = allocLineData(screen, oldLD); 2159 copyLineData(savedStatus, oldLD); 2160 TRACE(("...copied::%s\n", 2161 visibleIChars(savedStatus->charData, 2162 savedStatus->lineSize))); 2163 TRACE(("...discount a row for status-line\n")); 2164 rows = newRow; 2165 height -= FontHeight(screen) * StatusLineRows; 2166 } 2167#endif 2168 2169 if (screen->is_running) { 2170 /* clear the right and bottom internal border because of NorthWest 2171 gravity might have left junk on the right and bottom edges */ 2172 if (width >= (int) FullWidth(screen)) { 2173 xtermClear2(xw, 2174 FullWidth(screen), 0, /* right edge */ 2175 0, (unsigned) height); /* from top to bottom */ 2176 } 2177 if (height >= (int) FullHeight(screen)) { 2178 xtermClear2(xw, 2179 0, FullHeight(screen), /* bottom */ 2180 (unsigned) width, 0); /* all across the bottom */ 2181 } 2182 } 2183 2184 /* update buffers if the screen has changed size */ 2185 if (forced) { 2186 ; 2187 } else if (MaxRows(screen) != rows || MaxCols(screen) != cols) { 2188 int delta_rows = rows - MaxRows(screen); 2189#if OPT_TRACE 2190 int delta_cols = cols - MaxCols(screen); 2191#endif 2192 2193 TRACE(("...ScreenResize chars %dx%d delta %dx%d\n", 2194 rows, cols, delta_rows, delta_cols)); 2195 2196 if (screen->is_running) { 2197 if (screen->cursor_state) 2198 HideCursor(xw); 2199 2200 /* 2201 * The non-visible buffer is simple, since we will not copy data 2202 * to/from the saved-lines. Do that first. 2203 */ 2204 if (screen->editBuf_index[!screen->whichBuf]) { 2205 (void) Reallocate(xw, 2206 &screen->editBuf_index[!screen->whichBuf], 2207 &screen->editBuf_data[!screen->whichBuf], 2208 (unsigned) rows, 2209 (unsigned) cols, 2210 (unsigned) MaxRows(screen)); 2211 } 2212 2213 /* 2214 * The save-lines buffer may change width, but will not change its 2215 * height. Deal with the cases where we copy data to/from the 2216 * saved-lines buffer. 2217 */ 2218 if (GravityIsSouthWest(xw) 2219 && delta_rows 2220 && screen->saveBuf_index != NULL) { 2221 2222 if (delta_rows < 0) { 2223 unsigned move_up = (unsigned) (-delta_rows); 2224 int amount = ((MaxRows(screen) - (int) move_up - 1) 2225 - screen->cur_row); 2226 2227 if (amount < 0) { 2228 /* move line-data from visible-buffer to save-buffer */ 2229 saveEditBufLines(screen, (unsigned) -amount); 2230 move_down_by = amount; 2231 } else { 2232 move_down_by = 0; 2233 } 2234 2235 /* decrease size of visible-buffer */ 2236 (void) Reallocate(xw, 2237 &screen->editBuf_index[screen->whichBuf], 2238 &screen->editBuf_data[screen->whichBuf], 2239 (unsigned) rows, 2240 (unsigned) cols, 2241 (unsigned) MaxRows(screen)); 2242 TRACE_SCRNBUF("reallocEDIT", 2243 screen, 2244 screen->editBuf_index[screen->whichBuf], 2245 rows); 2246 } else { 2247 unsigned move_down = (unsigned) delta_rows; 2248 long unsave_fifo; 2249 ScrnBuf dst; 2250 int amount; 2251 2252 if ((int) move_down > screen->savedlines) { 2253 move_down = (unsigned) screen->savedlines; 2254 } 2255 move_down_by = (int) move_down; 2256 amount = rows - (int) move_down; 2257 2258 /* increase size of visible-buffer */ 2259 (void) Reallocate(xw, 2260 &screen->editBuf_index[screen->whichBuf], 2261 &screen->editBuf_data[screen->whichBuf], 2262 (unsigned) rows, 2263 (unsigned) cols, 2264 (unsigned) MaxRows(screen)); 2265 2266 dst = screen->editBuf_index[screen->whichBuf]; 2267 TRACE_SCRNBUF("reallocEDIT", screen, dst, rows); 2268 2269 TRACE(("...%smoving pointers in editBuf (compare %d %d)\n", 2270 (amount > 0 2271 ? "" 2272 : "SKIP "), 2273 rows, 2274 move_down)); 2275 if (amount > 0) { 2276 /* shift lines in visible-buffer to make room */ 2277 SaveLineData(dst, (unsigned) amount, (size_t) move_down); 2278 2279 MoveLineData(dst, 2280 move_down, 2281 0, 2282 (unsigned) amount); 2283 2284 TRACE(("...reuse %d lines storage in editBuf\n", move_down)); 2285 RestoreLineData(dst, 2286 0, 2287 move_down); 2288 2289 TRACE_SCRNBUF("shifted", screen, dst, rows); 2290 } 2291 2292 /* copy line-data from save-buffer to visible-buffer */ 2293 unsaveEditBufLines(screen, dst, move_down); 2294 TRACE_SCRNBUF("copied", screen, dst, rows); 2295 2296 unsave_fifo = (long) move_down; 2297 if (screen->saved_fifo < (int) unsave_fifo) 2298 unsave_fifo = screen->saved_fifo; 2299 2300 /* free up storage in fifo from the copied lines */ 2301 while (unsave_fifo-- > 0) { 2302 deleteScrollback(screen); 2303 } 2304 2305 /* recover storage in save-buffer */ 2306 } 2307 } else { 2308 (void) Reallocate(xw, 2309 &screen->editBuf_index[screen->whichBuf], 2310 &screen->editBuf_data[screen->whichBuf], 2311 (unsigned) rows, 2312 (unsigned) cols, 2313 (unsigned) MaxRows(screen)); 2314 } 2315 2316 screen->visbuf = VisBuf(screen); 2317 } 2318 2319 AdjustSavedCursor(xw, move_down_by); 2320 set_max_row(screen, screen->max_row + delta_rows); 2321 set_max_col(screen, cols - 1); 2322 2323 if (screen->is_running) { 2324 if (GravityIsSouthWest(xw)) { 2325 screen->savedlines -= move_down_by; 2326 if (screen->savedlines < 0) 2327 screen->savedlines = 0; 2328 if (screen->savedlines > screen->savelines) 2329 screen->savedlines = screen->savelines; 2330 if (screen->topline < -screen->savedlines) 2331 screen->topline = -screen->savedlines; 2332 set_cur_row(screen, screen->cur_row + move_down_by); 2333 screen->cursorp.row += move_down_by; 2334 ScrollSelection(screen, move_down_by, True); 2335 } 2336 } 2337 2338 /* adjust scrolling region */ 2339 resetMargins(xw); 2340 UIntClr(*flags, ORIGIN); 2341 2342 if (screen->cur_row > screen->max_row) 2343 set_cur_row(screen, screen->max_row); 2344 if (screen->cur_col > screen->max_col) 2345 set_cur_col(screen, screen->max_col); 2346 2347 screen->fullVwin.height = height - border; 2348 screen->fullVwin.width = width - border - screen->fullVwin.sb_info.width; 2349 2350 scroll_displayed_graphics(xw, -move_down_by); 2351 } else if (FullHeight(screen) == height && FullWidth(screen) == width) { 2352#if OPT_STATUS_LINE 2353 if (savedStatus != NULL) { 2354 TRACE(("...status line is currently saved!\n")); 2355 freeLineData(screen, savedStatus); 2356 } 2357#endif 2358 return; /* nothing has changed at all */ 2359 } 2360 2361 screen->fullVwin.fullheight = (Dimension) height; 2362 screen->fullVwin.fullwidth = (Dimension) width; 2363 2364 ResizeScrollBar(xw); 2365 ResizeSelection(screen, rows, cols); 2366 2367#ifndef NO_ACTIVE_ICON 2368 if (screen->iconVwin.window) { 2369 XWindowChanges changes; 2370 screen->iconVwin.width = 2371 MaxCols(screen) * screen->iconVwin.f_width; 2372 2373 screen->iconVwin.height = 2374 MaxRows(screen) * screen->iconVwin.f_height; 2375 2376 changes.width = screen->iconVwin.fullwidth = 2377 (Dimension) ((unsigned) screen->iconVwin.width 2378 + 2 * xw->misc.icon_border_width); 2379 2380 changes.height = screen->iconVwin.fullheight = 2381 (Dimension) ((unsigned) screen->iconVwin.height 2382 + 2 * xw->misc.icon_border_width); 2383 2384 changes.border_width = (int) xw->misc.icon_border_width; 2385 2386 TRACE(("resizing icon window %dx%d\n", changes.height, changes.width)); 2387 XConfigureWindow(XtDisplay(xw), screen->iconVwin.window, 2388 CWWidth | CWHeight | CWBorderWidth, &changes); 2389 } 2390#endif /* NO_ACTIVE_ICON */ 2391 2392#if OPT_STATUS_LINE 2393 if (savedStatus != NULL) { 2394 int newRow = LastRowNumber(screen); 2395 LineData *newLD = getLineData(screen, newRow); 2396 TRACE(("...status line is currently on row %d\n", 2397 LastRowNumber(screen))); 2398 copyLineData(newLD, savedStatus); 2399 TRACE(("...copied::%s\n", 2400 visibleIChars(newLD->charData, 2401 newLD->lineSize))); 2402 freeLineData(screen, savedStatus); 2403 } 2404#endif 2405 2406#ifdef TTYSIZE_STRUCT 2407 if (update_winsize(screen, rows, cols, height, width) == 0) { 2408#if defined(SIGWINCH) && defined(TIOCGPGRP) 2409 if (screen->pid > 1) { 2410 int pgrp; 2411 2412 TRACE(("getting process-group\n")); 2413 if (ioctl(screen->respond, TIOCGPGRP, &pgrp) != -1) { 2414 TRACE(("sending SIGWINCH to process group %d\n", pgrp)); 2415 kill_process_group(pgrp, SIGWINCH); 2416 } 2417 } 2418#endif /* SIGWINCH */ 2419 } 2420#else 2421 TRACE(("ScreenResize cannot do anything to pty\n")); 2422#endif /* TTYSIZE_STRUCT */ 2423 return; 2424} 2425 2426/* 2427 * Return true if any character cell starting at [row,col], for len-cells is 2428 * nonnull. 2429 */ 2430Bool 2431non_blank_line(TScreen *screen, 2432 int row, 2433 int col, 2434 int len) 2435{ 2436 Bool found = False; 2437 LineData *ld = getLineData(screen, row); 2438 2439 if (ld != NULL) { 2440 int i; 2441 2442 for (i = col; i < len; i++) { 2443 if (ld->charData[i]) { 2444 found = True; 2445 break; 2446 } 2447 } 2448 } 2449 return found; 2450} 2451 2452/* 2453 * Limit/map rectangle parameters. 2454 */ 2455#define minRectRow(screen) (getMinRow(screen) + 1) 2456#define minRectCol(screen) (getMinCol(screen) + 1) 2457#define maxRectRow(screen) (getMaxRow(screen) + 1) 2458#define maxRectCol(screen) (getMaxCol(screen) + 1) 2459 2460static int 2461limitedParseRow(XtermWidget xw, int row) 2462{ 2463 TScreen *screen = TScreenOf(xw); 2464 int min_row = minRectRow(screen); 2465 int max_row = maxRectRow(screen); 2466 2467 if (xw->flags & ORIGIN) 2468 row += screen->top_marg; 2469 2470 if (row < min_row) 2471 row = min_row; 2472 else if (row > max_row) 2473 row = max_row; 2474 2475 return row; 2476} 2477 2478static int 2479limitedParseCol(XtermWidget xw, int col) 2480{ 2481 TScreen *screen = TScreenOf(xw); 2482 int min_col = minRectCol(screen); 2483 int max_col = maxRectCol(screen); 2484 2485 if (xw->flags & ORIGIN) 2486 col += screen->lft_marg; 2487 2488 if (col < min_col) 2489 col = min_col; 2490 else if (col > max_col) 2491 col = max_col; 2492 2493 return col; 2494} 2495 2496#define LimitedParse(num, func, dft) \ 2497 func(xw, (nparams > num && params[num] > 0) ? params[num] : dft) 2498 2499/* 2500 * Copy the rectangle boundaries into a struct, providing default values as 2501 * needed. 2502 */ 2503void 2504xtermParseRect(XtermWidget xw, int nparams, int *params, XTermRect *target) 2505{ 2506 TScreen *screen = TScreenOf(xw); 2507 2508 memset(target, 0, sizeof(*target)); 2509 target->top = LimitedParse(0, limitedParseRow, minRectRow(screen)); 2510 target->left = LimitedParse(1, limitedParseCol, minRectCol(screen)); 2511 target->bottom = LimitedParse(2, limitedParseRow, maxRectRow(screen)); 2512 target->right = LimitedParse(3, limitedParseCol, maxRectCol(screen)); 2513 TRACE(("parsed %d params for rectangle %d,%d %d,%d default %d,%d %d,%d\n", 2514 nparams, 2515 target->top, 2516 target->left, 2517 target->bottom, 2518 target->right, 2519 minRectRow(screen), 2520 minRectCol(screen), 2521 maxRectRow(screen), 2522 maxRectCol(screen))); 2523} 2524 2525static Bool 2526validRect(XtermWidget xw, XTermRect *target) 2527{ 2528 TScreen *screen = TScreenOf(xw); 2529 Bool result = (target != NULL 2530 && target->top >= minRectRow(screen) 2531 && target->left >= minRectCol(screen) 2532 && target->top <= target->bottom 2533 && target->left <= target->right 2534 && target->top <= maxRectRow(screen) 2535 && target->right <= maxRectCol(screen)); 2536 2537 TRACE(("comparing against screensize %dx%d, is%s valid\n", 2538 maxRectRow(screen), 2539 maxRectCol(screen), 2540 result ? "" : " NOT")); 2541 return result; 2542} 2543 2544/* 2545 * Fills a rectangle with the given 8-bit character and video-attributes. 2546 * Colors and double-size attribute are unmodified. 2547 */ 2548void 2549ScrnFillRectangle(XtermWidget xw, 2550 XTermRect *target, 2551 int value, 2552 unsigned flags, 2553 Bool keepColors) 2554{ 2555 IChar actual = (IChar) value; 2556 TScreen *screen = TScreenOf(xw); 2557 2558 TRACE(("filling rectangle with '%s' flags %#x\n", 2559 visibleIChars(&actual, 1), flags)); 2560 if (validRect(xw, target)) { 2561 LineData *ld; 2562 int top = (target->top - 1); 2563 int left = (target->left - 1); 2564 int right = (target->right - 1); 2565 int bottom = (target->bottom - 1); 2566 int numcols = (right - left) + 1; 2567 int numrows = (bottom - top) + 1; 2568 unsigned attrs = flags; 2569 int row, col; 2570 int b_left = 0; 2571 int b_right = 0; 2572 2573 (void) numcols; 2574 2575 attrs &= ATTRIBUTES; 2576 attrs |= CHARDRAWN; 2577 for (row = bottom; row >= top; row--) { 2578 ld = getLineData(screen, row); 2579 2580 TRACE(("filling %d [%d..%d]\n", row, left, left + numcols)); 2581 2582 if_OPT_WIDE_CHARS(screen, { 2583 if (left > 0) { 2584 if (ld->charData[left] == HIDDEN_CHAR) { 2585 b_left = 1; 2586 Clear1Cell(ld, left - 1); 2587 Clear1Cell(ld, left); 2588 } 2589 } 2590 if (right + 1 < (int) ld->lineSize) { 2591 if (ld->charData[right + 1] == HIDDEN_CHAR) { 2592 b_right = 1; 2593 Clear1Cell(ld, right); 2594 Clear1Cell(ld, right + 1); 2595 } 2596 } 2597 }); 2598 2599 /* 2600 * Fill attributes, preserving colors. 2601 */ 2602 for (col = left; col <= right; ++col) { 2603 unsigned temp = ld->attribs[col]; 2604 2605 if (!keepColors) { 2606 UIntClr(temp, (FG_COLOR | BG_COLOR)); 2607 } 2608 temp = attrs | (temp & (FG_COLOR | BG_COLOR)) | CHARDRAWN; 2609 ld->attribs[col] = (IAttr) temp; 2610 if_OPT_ISO_COLORS(screen, { 2611 if (attrs & (FG_COLOR | BG_COLOR)) { 2612 ld->color[col] = xtermColorPair(xw); 2613 } 2614 }); 2615 } 2616 2617 for (col = left; col <= right; ++col) 2618 ld->charData[col] = actual; 2619 2620 if_OPT_WIDE_CHARS(screen, { 2621 size_t off; 2622 for_each_combData(off, ld) { 2623 memset(ld->combData[off] + left, 2624 0, 2625 (size_t) numcols * sizeof(CharData)); 2626 } 2627 }) 2628 } 2629 chararea_clear_displayed_graphics(screen, 2630 left, 2631 top, 2632 numcols, numrows); 2633 ScrnUpdate(xw, 2634 top, 2635 left - b_left, 2636 numrows, 2637 numcols + b_left + b_right, 2638 False); 2639 } 2640} 2641 2642#if OPT_DEC_RECTOPS 2643/* 2644 * Copies the source rectangle to the target location, including video 2645 * attributes. 2646 * 2647 * This implementation ignores page numbers. 2648 * 2649 * The reference manual does not indicate if it handles overlapping copy 2650 * properly - so we make a local copy of the source rectangle first, then apply 2651 * the target from that. 2652 */ 2653void 2654ScrnCopyRectangle(XtermWidget xw, XTermRect *source, int nparam, int *params) 2655{ 2656 TScreen *screen = TScreenOf(xw); 2657 2658 TRACE(("copying rectangle\n")); 2659 2660 if (nparam > 4) 2661 nparam = 4; 2662 2663 if (validRect(xw, source)) { 2664 XTermRect target; 2665 xtermParseRect(xw, 2666 ((nparam > 2) ? 2 : nparam), 2667 params, 2668 &target); 2669 if (validRect(xw, &target)) { 2670 Cardinal high = (Cardinal) (source->bottom - source->top) + 1; 2671 Cardinal wide = (Cardinal) (source->right - source->left) + 1; 2672 Cardinal size = (high * wide); 2673 int row, col; 2674 Cardinal j, k; 2675 LineData *ld; 2676 int b_left = 0; 2677 int b_right = 0; 2678 2679 CellData *cells = newCellData(xw, size); 2680 2681 if (cells != NULL) { 2682 2683 TRACE(("OK - make copy %dx%d\n", high, wide)); 2684 target.bottom = target.top + (int) (high - 1); 2685 target.right = target.left + (int) (wide - 1); 2686 2687 for (row = source->top - 1; row < source->bottom; ++row) { 2688 ld = getLineData(screen, row); 2689 if (ld == NULL) 2690 continue; 2691 j = (Cardinal) (row - (source->top - 1)); 2692 TRACE2(("ROW %d\n", row + 1)); 2693 for (col = source->left - 1; col < source->right; ++col) { 2694 k = (Cardinal) (col - (source->left - 1)); 2695 saveCellData(screen, cells, 2696 (j * wide) + k, 2697 ld, source, col); 2698 } 2699 } 2700 for (row = target.top - 1; row < target.bottom; ++row) { 2701 ld = getLineData(screen, row); 2702 if (ld == NULL) 2703 continue; 2704 j = (Cardinal) (row - (target.top - 1)); 2705 TRACE2(("ROW %d\n", row + 1)); 2706 for (col = target.left - 1; col < target.right; ++col) { 2707 k = (Cardinal) (col - (target.left - 1)); 2708 if (row >= getMinRow(screen) 2709 && row <= getMaxRow(screen) 2710 && col >= getMinCol(screen) 2711 && col <= getMaxCol(screen) 2712 && (j < high) 2713 && (k < wide)) { 2714 if_OPT_WIDE_CHARS(screen, { 2715 if (ld->charData[col] == HIDDEN_CHAR 2716 && (col + 1) == target.left) { 2717 b_left = 1; 2718 Clear1Cell(ld, col - 1); 2719 } 2720 if ((col + 1) == target.right 2721 && ld->charData[col] == HIDDEN_CHAR) { 2722 b_right = 1; 2723 } 2724 }); 2725 restoreCellData(screen, cells, 2726 (j * wide) + k, 2727 ld, &target, col); 2728 } 2729 ld->attribs[col] |= CHARDRAWN; 2730 } 2731#if OPT_BLINK_TEXT 2732 if (LineHasBlinking(screen, ld)) { 2733 LineSetBlinked(ld); 2734 } else { 2735 LineClrBlinked(ld); 2736 } 2737#endif 2738 } 2739 free(cells); 2740 2741 ScrnUpdate(xw, 2742 (target.top - 1), 2743 (target.left - (1 + b_left)), 2744 (target.bottom - target.top) + 1, 2745 ((target.right - target.left) + (1 + b_left + b_right)), 2746 False); 2747 } 2748 } 2749 } 2750} 2751 2752/* 2753 * Modifies the video-attributes only - so selection (not a video attribute) is 2754 * unaffected. Colors and double-size flags are unaffected as well. 2755 * 2756 * Reference: VSRM - Character Cell Display EL-00070-05 2757 * 2758 * Section: 2759 * ------- 2760 * CHANGE ATTRIBUTES RECTANGULAR AREA -- DECCARA 2761 * Page 5-173 2762 * 2763 * Quote: 2764 * The character positions affected depend on the current setting of DECSACE 2765 * (STREAM or RECTANGLE). See DECSACE for details. 2766 * 2767 * Notes: 2768 * xterm allows 8 (hidden) to be reversed, as an extension. 2769 * 2770 * Section: 2771 * ------- 2772 * REVERSE ATTRIBUTES RECTANGULAR AREA -- DECRARA 2773 * Page 5-175 2774 * 2775 * Quote: 2776 * The video attribute(s) to be reversed are in the affected area are indicated 2777 * by one or more subsequent parameters. These parameters are similar to the 2778 * parameters of the Set Graphic Rendition control function (SGR): 2779 * 2780 * Parameter Parameter Meaning 2781 * 0 Reverse all attributes 2782 * 1 Reverse bold attribute 2783 * 4 Reverse underscore attribute 2784 * 5 Reverse blinking attribute 2785 * 7 Reverse negative (reverse) image attribute 2786 * 2787 * All other parameter values shall be ignored unless they are part of a well 2788 * defined extension to the architecture. Note if the Color Text Extension is 2789 * present, the color text SGR values are ignored since the "reverse" of a 2790 * color is not defined by the extension. 2791 * 2792 * Notes: 2793 * xterm allows 8 (hidden) to be reversed, as an extension. 2794 * 2795 * Section: 2796 * ------- 2797 * SELECT ATTRIBUTE CHANGE EXTENT -- DECSACE 2798 * Page 5-177 2799 * 2800 * Quote: 2801 * When Ps = 0 or 1, DECCARA and DECRARA affects the stream of character 2802 * positions beginning with the first character position specified in the 2803 * command, and ending with the second character position specified. 2804 * 2805 * Notes: 2806 * The description of DECSACE goes on to state that "unoccupied" cells are 2807 * not affected in STREAM mode, while in RECTANGLE mode they are converted 2808 * to blanks. 2809 * 2810 * While STREAM uses the upper-left and lower-right cell coordinates for a 2811 * RECTANGLE (which may take into account ORIGIN mode), the characters wrap, 2812 * in STREAM mode, and DEC 070 does not appear to state that ORIGIN mode 2813 * affects the wrap-margins. 2814 */ 2815void 2816ScrnMarkRectangle(XtermWidget xw, 2817 XTermRect *target, 2818 Bool reverse, 2819 int nparam, 2820 int *params) 2821{ 2822 TScreen *screen = TScreenOf(xw); 2823 Bool exact = (screen->cur_decsace == 2); 2824 2825 TRACE(("%s %s\n", 2826 reverse ? "reversing" : "marking", 2827 (exact 2828 ? "rectangle" 2829 : "region"))); 2830 2831 if (validRect(xw, target)) { 2832 LineData *ld; 2833 int top = target->top - 1; 2834 int bottom = target->bottom - 1; 2835 int row, col; 2836 int n; 2837 2838 for (row = top; row <= bottom; ++row) { 2839 int left = ((exact || (row == top)) 2840 ? (target->left - 1) 2841 : 0); 2842 int right = ((exact || (row == bottom)) 2843 ? (target->right - 1) 2844 : screen->max_col); 2845 2846 ld = getLineData(screen, row); 2847 2848 TRACE(("marking %d [%d..%d]\n", row, left, right)); 2849 for (col = left; col <= right; ++col) { 2850 unsigned flags = ld->attribs[col]; 2851 2852 if (!(flags & CHARDRAWN)) { 2853 if (exact) { 2854 flags |= CHARDRAWN; 2855 Clear1Cell(ld, col); 2856 } else { 2857 continue; 2858 } 2859 } 2860 2861 for (n = 0; n < nparam; ++n) { 2862#if OPT_TRACE 2863 if (row == top && col == left) 2864 TRACE(("attr param[%d] %d\n", n + 1, params[n])); 2865#endif 2866 if (reverse) { 2867 switch (params[n]) { 2868 case 0: 2869 flags ^= SGR_MASK; 2870 break; 2871 case 1: 2872 flags ^= BOLD; 2873 break; 2874 case 4: 2875 flags ^= UNDERLINE; 2876 break; 2877 case 5: 2878 flags ^= BLINK; 2879 break; 2880 case 7: 2881 flags ^= INVERSE; 2882 break; 2883 case 8: 2884 flags ^= INVISIBLE; 2885 break; 2886 } 2887 } else { 2888 switch (params[n]) { 2889 case 0: 2890 UIntClr(flags, SGR_MASK); 2891 break; 2892 case 1: 2893 flags |= BOLD; 2894 break; 2895 case 4: 2896 flags |= UNDERLINE; 2897 break; 2898 case 5: 2899 flags |= BLINK; 2900 break; 2901 case 7: 2902 flags |= INVERSE; 2903 break; 2904 case 8: 2905 flags |= INVISIBLE; 2906 break; 2907 case 22: 2908 UIntClr(flags, BOLD); 2909 break; 2910 case 24: 2911 UIntClr(flags, UNDERLINE); 2912 break; 2913 case 25: 2914 UIntClr(flags, BLINK); 2915 break; 2916 case 27: 2917 UIntClr(flags, INVERSE); 2918 break; 2919 case 28: 2920 UIntClr(flags, INVISIBLE); 2921 break; 2922 } 2923 } 2924 } 2925#if OPT_TRACE 2926 if (row == top && col == left) 2927 TRACE(("first mask-change is %#x\n", 2928 ld->attribs[col] ^ flags)); 2929#endif 2930 ld->attribs[col] = (IAttr) flags; 2931 } 2932 } 2933 ScrnRefresh(xw, 2934 (target->top - 1), 2935 (exact ? (target->left - 1) : getMinCol(screen)), 2936 (target->bottom - target->top) + 1, 2937 (exact 2938 ? ((target->right - target->left) + 1) 2939 : (getMaxCol(screen) - getMinCol(screen) + 1)), 2940 True); 2941 } 2942} 2943 2944/* 2945 * Resets characters to space, except where prohibited by DECSCA. Video 2946 * attributes (including color) are untouched. 2947 */ 2948void 2949ScrnWipeRectangle(XtermWidget xw, 2950 XTermRect *target) 2951{ 2952 TScreen *screen = TScreenOf(xw); 2953 2954 TRACE(("wiping rectangle\n")); 2955 2956#define IsProtected(ld, col) \ 2957 ((screen->protected_mode == DEC_PROTECT) \ 2958 && (ld->attribs[col] & PROTECTED)) 2959 2960 if (validRect(xw, target)) { 2961 int top = target->top - 1; 2962 int left = target->left - 1; 2963 int right = target->right - 1; 2964 int bottom = target->bottom - 1; 2965 int numcols = (right - left) + 1; 2966 int numrows = (bottom - top) + 1; 2967 int row, col; 2968 int b_left = 0; 2969 int b_right = 0; 2970 2971 for (row = top; row <= bottom; ++row) { 2972 LineData *ld; 2973 2974 TRACE(("wiping %d [%d..%d]\n", row, left, right)); 2975 2976 ld = getLineData(screen, row); 2977 2978 if_OPT_WIDE_CHARS(screen, { 2979 if (left > 0 && !IsProtected(ld, left)) { 2980 if (ld->charData[left] == HIDDEN_CHAR) { 2981 b_left = 1; 2982 Clear1Cell(ld, left - 1); 2983 Clear1Cell(ld, left); 2984 } 2985 } 2986 if (right + 1 < (int) ld->lineSize && !IsProtected(ld, right)) { 2987 if (ld->charData[right + 1] == HIDDEN_CHAR) { 2988 b_right = 1; 2989 Clear1Cell(ld, right); 2990 Clear1Cell(ld, right + 1); 2991 } 2992 } 2993 }); 2994 2995 for (col = left; col <= right; ++col) { 2996 if (!IsProtected(ld, col)) { 2997 ld->attribs[col] |= CHARDRAWN; 2998 Clear1Cell(ld, col); 2999 } 3000 } 3001 } 3002 chararea_clear_displayed_graphics(screen, 3003 left, 3004 top, 3005 numcols, numrows); 3006 ScrnUpdate(xw, 3007 top, 3008 left - b_left, 3009 numrows, 3010 numcols + b_left + b_right, 3011 False); 3012 } 3013} 3014 3015/* 3016 * Compute a checksum, ignoring the page number (since we have only one page). 3017 */ 3018void 3019xtermCheckRect(XtermWidget xw, 3020 int nparam, 3021 int *params, 3022 int *result) 3023{ 3024 TScreen *screen = TScreenOf(xw); 3025 XTermRect target; 3026 LineData *ld; 3027 int total = 0; 3028 int trimmed = 0; 3029 int mode = screen->checksum_ext; 3030 3031 TRACE(("xtermCheckRect: %s%s%s%s%s%s\n", 3032 (mode == csDEC) ? "DEC" : "checksumExtension", 3033 (mode & csPOSITIVE) ? " !negative" : "", 3034 (mode & csATTRIBS) ? " !attribs" : "", 3035 (mode & csNOTRIM) ? " !trimmed" : "", 3036 (mode & csDRAWN) ? " !drawn" : "", 3037 (mode & csBYTE) ? " !byte" : "")); 3038 3039 if (nparam > 2) { 3040 nparam -= 2; 3041 params += 2; 3042 } 3043 xtermParseRect(xw, nparam, params, &target); 3044 if (validRect(xw, &target)) { 3045 int top = target.top - 1; 3046 int bottom = target.bottom - 1; 3047 int row, col; 3048 Boolean first = True; 3049 int embedded = 0; 3050 3051 for (row = top; row <= bottom; ++row) { 3052 int left = (target.left - 1); 3053 int right = (target.right - 1); 3054 int ch; 3055 3056 ld = getLineData(screen, row); 3057 if (ld == NULL) 3058 continue; 3059 for (col = left; col <= right && col < (int) ld->lineSize; ++col) { 3060 if (!(ld->attribs[col] & CHARDRAWN)) { 3061 if (!(mode & (csNOTRIM | csDRAWN))) 3062 continue; 3063 ch = ' '; 3064 } else if (!(mode & csBYTE)) { 3065 ch = xtermCharSetDec(xw, 3066 ld->charSeen[col], 3067 ld->charSets[col]); 3068 } else { 3069 ch = (int) ld->charData[col]; 3070 if_OPT_WIDE_CHARS(screen, { 3071 if (is_UCS_SPECIAL(ch)) 3072 continue; 3073 }); 3074 } 3075 if (!(mode & csATTRIBS)) { 3076#if OPT_ISO_COLORS && OPT_VT525_COLORS 3077 if (screen->terminal_id == 525) { 3078 IAttr flags = ld->attribs[col]; 3079 CellColor fg_bg = ld->color[col]; 3080 int fg = (int) extract_fg(xw, fg_bg, flags); 3081 int bg = (int) extract_bg(xw, fg_bg, flags); 3082 Boolean dft_bg = (bg < 0); 3083 Boolean dft_fg = (fg < 0); 3084 3085 if (dft_bg) 3086 bg = screen->assigned_bg; 3087 if (bg >= 0 && bg < 16) 3088 ch += bg; 3089 3090 if (dft_fg) 3091 fg = screen->assigned_fg; 3092 if (fg >= 0 && fg < 16) 3093 ch += (fg << 4); 3094 3095 /* special case to match VT525 behavior */ 3096 if (dft_bg && !dft_fg && (ld->attribs[col] & BOLD)) 3097 ch -= 0x80; 3098 } 3099#endif 3100 if (ld->attribs[col] & PROTECTED) 3101 ch += 0x4; 3102#if OPT_WIDE_ATTRS 3103 if (ld->attribs[col] & INVISIBLE) 3104 ch += 0x8; 3105#endif 3106 if (ld->attribs[col] & UNDERLINE) 3107 ch += 0x10; 3108 if (ld->attribs[col] & INVERSE) 3109 ch += 0x20; 3110 if (ld->attribs[col] & BLINK) 3111 ch += 0x40; 3112 if (ld->attribs[col] & BOLD) 3113 ch += 0x80; 3114 } 3115 if (first || (ch != ' ') || (ld->attribs[col] & DRAWX_MASK)) { 3116 trimmed += ch + embedded; 3117 embedded = 0; 3118 } else if ((mode & csNOTRIM)) { 3119 embedded += ch; 3120 } 3121 total += ch; 3122 if_OPT_WIDE_CHARS(screen, { 3123 /* FIXME - not counted if trimming blanks */ 3124 if (!(mode & csBYTE)) { 3125 size_t off; 3126 for_each_combData(off, ld) { 3127 total += (int) ld->combData[off][col]; 3128 } 3129 } 3130 }); 3131 first = ((mode & csNOTRIM) != 0) ? True : False; 3132 } 3133 if (!(mode & csNOTRIM)) { 3134 embedded = 0; 3135 first = False; 3136 } 3137 } 3138 } 3139 if (!(mode & csNOTRIM)) 3140 total = trimmed; 3141 if (!(mode & csPOSITIVE)) 3142 total = -total; 3143 *result = total; 3144} 3145#endif /* OPT_DEC_RECTOPS */ 3146 3147static void 3148set_ewmh_hint(XtermWidget xw, int operation, _Xconst char *prop) 3149{ 3150 TScreen *screen = TScreenOf(xw); 3151 Display *dpy = screen->display; 3152 Window window; 3153 XEvent e; 3154 Atom atom_fullscreen = CachedInternAtom(dpy, prop); 3155 Atom atom_state = CachedInternAtom(dpy, "_NET_WM_STATE"); 3156 3157#if OPT_TRACE 3158 const char *what = "?"; 3159 switch (operation) { 3160 case _NET_WM_STATE_ADD: 3161 what = "adding"; 3162 break; 3163 case _NET_WM_STATE_REMOVE: 3164 what = "removing"; 3165 break; 3166 } 3167 TRACE(("set_ewmh_hint %s %s\n", what, prop)); 3168#endif 3169 3170#if OPT_TEK4014 3171 if (TEK4014_ACTIVE(xw)) { 3172 window = TShellWindow; 3173 } else 3174#endif 3175 window = VShellWindow(xw); 3176 3177 memset(&e, 0, sizeof(e)); 3178 e.xclient.type = ClientMessage; 3179 e.xclient.message_type = atom_state; 3180 e.xclient.display = dpy; 3181 e.xclient.window = window; 3182 e.xclient.format = 32; 3183 e.xclient.data.l[0] = operation; 3184 e.xclient.data.l[1] = (long) atom_fullscreen; 3185 3186 XSendEvent(dpy, DefaultRootWindow(dpy), False, 3187 SubstructureRedirectMask, &e); 3188} 3189 3190void 3191ResetHiddenHint(XtermWidget xw) 3192{ 3193 set_ewmh_hint(xw, _NET_WM_STATE_REMOVE, "_NET_WM_STATE_HIDDEN"); 3194} 3195 3196#if OPT_MAXIMIZE 3197 3198static _Xconst char * 3199ewmhProperty(int mode) 3200{ 3201 _Xconst char *result; 3202 switch (mode) { 3203 default: 3204 result = NULL; 3205 break; 3206 case 1: 3207 result = "_NET_WM_STATE_FULLSCREEN"; 3208 break; 3209 case 2: 3210 result = "_NET_WM_STATE_MAXIMIZED_VERT"; 3211 break; 3212 case 3: 3213 result = "_NET_WM_STATE_MAXIMIZED_HORZ"; 3214 break; 3215 } 3216 return result; 3217} 3218 3219static void 3220set_resize_increments(XtermWidget xw) 3221{ 3222 TScreen *screen = TScreenOf(xw); 3223 int min_width = (2 * screen->border) + screen->fullVwin.sb_info.width; 3224 int min_height = (2 * screen->border); 3225 XSizeHints sizehints; 3226 3227 TRACE(("set_resize_increments\n")); 3228 memset(&sizehints, 0, sizeof(XSizeHints)); 3229 sizehints.width_inc = FontWidth(screen); 3230 sizehints.height_inc = FontHeight(screen); 3231 sizehints.flags = PResizeInc; 3232 TRACE_HINTS(&sizehints); 3233 XSetWMNormalHints(screen->display, VShellWindow(xw), &sizehints); 3234 3235 TRACE(("setting values for widget %p:\n", (void *) SHELL_OF(xw))); 3236 TRACE((" base width %d\n", min_width)); 3237 TRACE((" base height %d\n", min_width)); 3238 TRACE((" min width %d\n", min_width + FontWidth(screen))); 3239 TRACE((" min height %d\n", min_width + FontHeight(screen))); 3240 TRACE((" width inc %d\n", FontWidth(screen))); 3241 TRACE((" height inc %d\n", FontHeight(screen))); 3242 3243 XtVaSetValues(SHELL_OF(xw), 3244 XtNbaseWidth, min_width, 3245 XtNbaseHeight, min_height, 3246 XtNminWidth, min_width + FontWidth(screen), 3247 XtNminHeight, min_height + FontHeight(screen), 3248 XtNwidthInc, FontWidth(screen), 3249 XtNheightInc, FontHeight(screen), 3250 (XtPointer) 0); 3251 3252 XFlush(XtDisplay(xw)); 3253} 3254 3255static void 3256unset_resize_increments(XtermWidget xw) 3257{ 3258 TScreen *screen = TScreenOf(xw); 3259 XSizeHints sizehints; 3260 3261 TRACE(("unset_resize_increments\n")); 3262 memset(&sizehints, 0, sizeof(XSizeHints)); 3263 sizehints.width_inc = 1; 3264 sizehints.height_inc = 1; 3265 sizehints.flags = PResizeInc; 3266 TRACE_HINTS(&sizehints); 3267 XSetWMNormalHints(screen->display, VShellWindow(xw), &sizehints); 3268 3269 XtVaSetValues(SHELL_OF(xw), 3270 XtNwidthInc, 1, 3271 XtNheightInc, 1, 3272 (XtPointer) 0); 3273 3274 XFlush(XtDisplay(xw)); 3275} 3276 3277/* 3278 * Check if the given property is supported on the root window. 3279 * 3280 * The XGetWindowProperty function returns a list of Atom's which corresponds 3281 * to the output of xprop. The actual list (ignore the manpage, which refers 3282 * to an array of 32-bit values) is constructed by _XRead32, which uses long 3283 * as a datatype. 3284 * 3285 * Alternatively, we could check _NET_WM_ALLOWED_ACTIONS on the application's 3286 * window. 3287 */ 3288static Boolean 3289probe_netwm(Display *dpy, _Xconst char *propname) 3290{ 3291 Atom atom_fullscreen = CachedInternAtom(dpy, propname); 3292 Atom atom_supported = CachedInternAtom(dpy, "_NET_SUPPORTED"); 3293 Atom actual_type; 3294 int actual_format; 3295 long long_offset = 0; 3296 long long_length = 128; /* number of items to ask for at a time */ 3297 unsigned int i; 3298 unsigned long nitems, bytes_after; 3299 unsigned char *args; 3300 long *ldata; 3301 Boolean has_capability = False; 3302 Boolean rc; 3303 3304 while (!has_capability) { 3305 rc = xtermGetWinProp(dpy, 3306 DefaultRootWindow(dpy), 3307 atom_supported, 3308 long_offset, 3309 long_length, 3310 AnyPropertyType, /* req_type */ 3311 &actual_type, /* actual_type_return */ 3312 &actual_format, /* actual_format_return */ 3313 &nitems, /* nitems_return */ 3314 &bytes_after, /* bytes_after_return */ 3315 &args /* prop_return */ 3316 ); 3317 if (!rc 3318 || actual_type != XA_ATOM) { 3319 break; 3320 } 3321 ldata = (long *) (void *) args; 3322 for (i = 0; i < nitems; i++) { 3323#if OPT_TRACE > 1 3324 char *name; 3325 if ((name = XGetAtomName(dpy, ldata[i])) != 0) { 3326 TRACE(("atom[%d] = %s\n", i, name)); 3327 XFree(name); 3328 } else { 3329 TRACE(("atom[%d] = ?\n", i)); 3330 } 3331#endif 3332 if ((Atom) ldata[i] == atom_fullscreen) { 3333 has_capability = True; 3334 break; 3335 } 3336 } 3337 XFree(ldata); 3338 3339 if (!has_capability) { 3340 if (bytes_after != 0) { 3341 long remaining = (long) (bytes_after / sizeof(long)); 3342 if (long_length > remaining) 3343 long_length = remaining; 3344 long_offset += (long) nitems; 3345 } else { 3346 break; 3347 } 3348 } 3349 } 3350 3351 TRACE(("probe_netwm(%s) ->%d\n", propname, has_capability)); 3352 return has_capability; 3353} 3354 3355/* 3356 * Alter fullscreen mode for the xterm widget, if the window manager supports 3357 * that feature. 3358 */ 3359void 3360FullScreen(XtermWidget xw, int new_ewmh_mode) 3361{ 3362 TScreen *screen = TScreenOf(xw); 3363 Display *dpy = screen->display; 3364 int old_ewmh_mode; 3365 _Xconst char *oldprop; 3366 _Xconst char *newprop; 3367 3368 int which = 0; 3369#if OPT_TEK4014 3370 if (TEK4014_ACTIVE(xw)) 3371 which = 1; 3372#endif 3373 3374 old_ewmh_mode = xw->work.ewmh[which].mode; 3375 oldprop = ewmhProperty(old_ewmh_mode); 3376 newprop = ewmhProperty(new_ewmh_mode); 3377 3378 TRACE(("FullScreen %d:%s -> %d:%s\n", 3379 old_ewmh_mode, NonNull(oldprop), 3380 new_ewmh_mode, NonNull(newprop))); 3381 3382 if (new_ewmh_mode == old_ewmh_mode) { 3383 TRACE(("...unchanged\n")); 3384 return; 3385 } else if (new_ewmh_mode < 0 || new_ewmh_mode > MAX_EWMH_MODE) { 3386 TRACE(("BUG: FullScreen %d\n", new_ewmh_mode)); 3387 return; 3388 } else if (new_ewmh_mode == 0) { 3389 xw->work.ewmh[which].checked[new_ewmh_mode] = True; 3390 xw->work.ewmh[which].allowed[new_ewmh_mode] = True; 3391 } else if (resource.fullscreen == esNever) { 3392 xw->work.ewmh[which].checked[new_ewmh_mode] = True; 3393 xw->work.ewmh[which].allowed[new_ewmh_mode] = False; 3394 } else if (!xw->work.ewmh[which].checked[new_ewmh_mode]) { 3395 xw->work.ewmh[which].checked[new_ewmh_mode] = True; 3396 xw->work.ewmh[which].allowed[new_ewmh_mode] = probe_netwm(dpy, newprop); 3397 } 3398 3399 if (xw->work.ewmh[which].allowed[new_ewmh_mode]) { 3400 TRACE(("...new EWMH mode is allowed\n")); 3401 if (new_ewmh_mode && !xw->work.ewmh[which].mode) { 3402 unset_resize_increments(xw); 3403 set_ewmh_hint(xw, _NET_WM_STATE_ADD, newprop); 3404 } else if (xw->work.ewmh[which].mode && !new_ewmh_mode) { 3405 if (!xw->misc.resizeByPixel) { 3406 set_resize_increments(xw); 3407 } 3408 set_ewmh_hint(xw, _NET_WM_STATE_REMOVE, oldprop); 3409 } else { 3410 set_ewmh_hint(xw, _NET_WM_STATE_REMOVE, oldprop); 3411 set_ewmh_hint(xw, _NET_WM_STATE_ADD, newprop); 3412 } 3413 xw->work.ewmh[which].mode = new_ewmh_mode; 3414 update_fullscreen(); 3415 } else { 3416 Bell(xw, XkbBI_MinorError, 100); 3417 } 3418} 3419#endif /* OPT_MAXIMIZE */ 3420