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