Home | History | Annotate | Line # | Download | only in dist
      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
    130 static void
    131 traceScrnBuf(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 
    163 ScrnBuf
    164 scrnHeadAddr(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  */
    178 void
    179 setupLineData(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  */
    263 static void
    264 extractScrnData(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 
    284 static ScrnPtr *
    285 allocScrnHead(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  */
    305 static unsigned
    306 sizeofScrnRow(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 
    345 Char *
    346 allocScrnData(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  */
    378 ScrnBuf
    379 allocScrnBuf(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  */
    398 static void
    399 saveEditBufLines(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  */
    417 static void
    418 unsaveEditBufLines(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  */
    444 static int
    445 Reallocate(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  */
    532 static void
    533 ReallocateBufOffsets(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  */
    600 static void
    601 ReallocateFifoIndex(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  */
    635 void
    636 ChangeToWide(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  */
    692 void
    693 CopyCells(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 
    782 static void
    783 FillIAttr(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  */
    793 void
    794 ClearCells(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  */
    846 void
    847 ScrnClearCells(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  */
    870 void
    871 ScrnDisownSelection(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  */
    887 void
    888 ScrnWriteText(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  */
   1041 static void
   1042 ScrnClearLines(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 
   1121 void
   1122 ScrnAllocBuf(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 
   1148 size_t
   1149 ScrnPointers(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  */
   1169 void
   1170 ScrnInsertLine(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  */
   1217 void
   1218 ScrnDeleteLine(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  */
   1271 void
   1272 ScrnInsertChar(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  */
   1334 void
   1335 ScrnDeleteChar(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 
   1395 void
   1396 FreeMarkGCs(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 
   1411 static GC
   1412 MakeMarkGC(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  */
   1438 void
   1439 ShowWrapMarks(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  */
   1462 static void
   1463 blockSelectBounds(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  */
   1480 static int
   1481 intersectsSelection(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  */
   1506 static void
   1507 recurseForNotSelectedAndAdjust(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  */
   1552 void
   1553 ScrnRefresh(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(&params,
   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(&params,
   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(&params,
   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(&params,
   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  */
   1970 void
   1971 ScrnUpdate(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  */
   1992 void
   1993 ClearBufRows(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
   2017 static LineData *
   2018 freeLineData(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  */
   2051 static LineData *
   2052 allocLineData(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   */
   2101 void
   2102 ScreenResize(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  */
   2430 Bool
   2431 non_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 
   2460 static int
   2461 limitedParseRow(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 
   2478 static int
   2479 limitedParseCol(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  */
   2503 void
   2504 xtermParseRect(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 
   2525 static Bool
   2526 validRect(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  */
   2548 void
   2549 ScrnFillRectangle(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  */
   2653 void
   2654 ScrnCopyRectangle(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  */
   2815 void
   2816 ScrnMarkRectangle(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  */
   2948 void
   2949 ScrnWipeRectangle(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  */
   3018 void
   3019 xtermCheckRect(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 
   3147 static void
   3148 set_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 
   3190 void
   3191 ResetHiddenHint(XtermWidget xw)
   3192 {
   3193     set_ewmh_hint(xw, _NET_WM_STATE_REMOVE, "_NET_WM_STATE_HIDDEN");
   3194 }
   3195 
   3196 #if OPT_MAXIMIZE
   3197 
   3198 static _Xconst char *
   3199 ewmhProperty(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 
   3219 static void
   3220 set_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 
   3255 static void
   3256 unset_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  */
   3288 static Boolean
   3289 probe_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  */
   3359 void
   3360 FullScreen(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