screen.c revision 4419d26b
1/* $XTermId: screen.c,v 1.623 2022/03/09 01:20:09 tom Exp $ */
2
3/*
4 * Copyright 1999-2021,2022 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(screen, 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(screen, 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
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(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(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    TRACE(("...StatusShown %d/%d\n", IsStatusShown(screen), screen->status_shown));
1989    if (IsStatusShown(screen)) {
1990	int oldRow = MaxRows(screen);
1991	TRACE(("...status line is currently on row %d(%d-%d) vs %d\n",
1992	       oldRow,
1993	       MaxRows(screen),
1994	       (screen->status_shown ? 0 : StatusLineRows),
1995	       rows));
1996	if (1 || rows != oldRow) {
1997	    LineData *oldLD = getLineData(screen, oldRow);
1998	    TRACE(("...will move status-line from row %d to %d\n",
1999		   oldRow,
2000		   rows));
2001	    savedStatus = allocLineData(screen, oldLD);
2002	    copyLineData(savedStatus, oldLD);
2003	    TRACE(("...copied::%s\n",
2004		   visibleIChars(savedStatus->charData,
2005				 savedStatus->lineSize)));
2006	}
2007	TRACE(("...discount a row for status-line\n"));
2008	rows -= StatusLineRows;
2009	height -= FontHeight(screen) * StatusLineRows;
2010    }
2011#endif
2012
2013    if (screen->is_running) {
2014	/* clear the right and bottom internal border because of NorthWest
2015	   gravity might have left junk on the right and bottom edges */
2016	if (width >= (int) FullWidth(screen)) {
2017	    xtermClear2(xw,
2018			FullWidth(screen), 0,	/* right edge */
2019			0, (unsigned) height);	/* from top to bottom */
2020	}
2021	if (height >= (int) FullHeight(screen)) {
2022	    xtermClear2(xw,
2023			0, FullHeight(screen),	/* bottom */
2024			(unsigned) width, 0);	/* all across the bottom */
2025	}
2026    }
2027
2028    /* update buffers if the screen has changed size */
2029    if (forced) {
2030	;
2031    } else if (MaxRows(screen) != rows || MaxCols(screen) != cols) {
2032	int delta_rows = rows - MaxRows(screen);
2033#if OPT_TRACE
2034	int delta_cols = cols - MaxCols(screen);
2035#endif
2036
2037	TRACE(("...ScreenResize chars %dx%d delta %dx%d\n",
2038	       rows, cols, delta_rows, delta_cols));
2039
2040	if (screen->is_running) {
2041	    if (screen->cursor_state)
2042		HideCursor(xw);
2043
2044	    /*
2045	     * The non-visible buffer is simple, since we will not copy data
2046	     * to/from the saved-lines.  Do that first.
2047	     */
2048	    if (screen->editBuf_index[!screen->whichBuf]) {
2049		(void) Reallocate(xw,
2050				  &screen->editBuf_index[!screen->whichBuf],
2051				  &screen->editBuf_data[!screen->whichBuf],
2052				  (unsigned) rows,
2053				  (unsigned) cols,
2054				  (unsigned) MaxRows(screen));
2055	    }
2056
2057	    /*
2058	     * The save-lines buffer may change width, but will not change its
2059	     * height.  Deal with the cases where we copy data to/from the
2060	     * saved-lines buffer.
2061	     */
2062	    if (GravityIsSouthWest(xw)
2063		&& delta_rows
2064		&& screen->saveBuf_index != 0) {
2065
2066		if (delta_rows < 0) {
2067		    unsigned move_up = (unsigned) (-delta_rows);
2068		    int amount = ((MaxRows(screen) - (int) move_up - 1)
2069				  - screen->cur_row);
2070
2071		    if (amount < 0) {
2072			/* move line-data from visible-buffer to save-buffer */
2073			saveEditBufLines(screen, (unsigned) -amount);
2074			move_down_by = amount;
2075		    } else {
2076			move_down_by = 0;
2077		    }
2078
2079		    /* decrease size of visible-buffer */
2080		    (void) Reallocate(xw,
2081				      &screen->editBuf_index[screen->whichBuf],
2082				      &screen->editBuf_data[screen->whichBuf],
2083				      (unsigned) rows,
2084				      (unsigned) cols,
2085				      (unsigned) MaxRows(screen));
2086		    TRACE_SCRNBUF("reallocEDIT",
2087				  screen,
2088				  screen->editBuf_index[screen->whichBuf],
2089				  rows);
2090		} else {
2091		    unsigned move_down = (unsigned) delta_rows;
2092		    long unsave_fifo;
2093		    ScrnBuf dst;
2094		    int amount;
2095
2096		    if ((int) move_down > screen->savedlines) {
2097			move_down = (unsigned) screen->savedlines;
2098		    }
2099		    move_down_by = (int) move_down;
2100		    amount = rows - (int) move_down;
2101
2102		    /* increase size of visible-buffer */
2103		    (void) Reallocate(xw,
2104				      &screen->editBuf_index[screen->whichBuf],
2105				      &screen->editBuf_data[screen->whichBuf],
2106				      (unsigned) rows,
2107				      (unsigned) cols,
2108				      (unsigned) MaxRows(screen));
2109
2110		    dst = screen->editBuf_index[screen->whichBuf];
2111		    TRACE_SCRNBUF("reallocEDIT", screen, dst, rows);
2112
2113		    TRACE(("...%smoving pointers in editBuf (compare %d %d)\n",
2114			   (amount > 0
2115			    ? ""
2116			    : "SKIP "),
2117			   rows,
2118			   move_down));
2119		    if (amount > 0) {
2120			/* shift lines in visible-buffer to make room */
2121			SaveLineData(dst, (unsigned) amount, (size_t) move_down);
2122
2123			MoveLineData(dst,
2124				     move_down,
2125				     0,
2126				     (unsigned) amount);
2127
2128			TRACE(("...reuse %d lines storage in editBuf\n", move_down));
2129			RestoreLineData(dst,
2130					0,
2131					move_down);
2132
2133			TRACE_SCRNBUF("shifted", screen, dst, rows);
2134		    }
2135
2136		    /* copy line-data from save-buffer to visible-buffer */
2137		    unsaveEditBufLines(screen, dst, move_down);
2138		    TRACE_SCRNBUF("copied", screen, dst, rows);
2139
2140		    unsave_fifo = (long) move_down;
2141		    if (screen->saved_fifo < (int) unsave_fifo)
2142			unsave_fifo = screen->saved_fifo;
2143
2144		    /* free up storage in fifo from the copied lines */
2145		    while (unsave_fifo-- > 0) {
2146			deleteScrollback(screen);
2147		    }
2148
2149		    /* recover storage in save-buffer */
2150		}
2151	    } else {
2152		(void) Reallocate(xw,
2153				  &screen->editBuf_index[screen->whichBuf],
2154				  &screen->editBuf_data[screen->whichBuf],
2155				  (unsigned) rows,
2156				  (unsigned) cols,
2157				  (unsigned) MaxRows(screen));
2158	    }
2159
2160	    screen->visbuf = VisBuf(screen);
2161	}
2162
2163	AdjustSavedCursor(xw, move_down_by);
2164	set_max_row(screen, screen->max_row + delta_rows);
2165	set_max_col(screen, cols - 1);
2166
2167	if (screen->is_running) {
2168	    if (GravityIsSouthWest(xw)) {
2169		screen->savedlines -= move_down_by;
2170		if (screen->savedlines < 0)
2171		    screen->savedlines = 0;
2172		if (screen->savedlines > screen->savelines)
2173		    screen->savedlines = screen->savelines;
2174		if (screen->topline < -screen->savedlines)
2175		    screen->topline = -screen->savedlines;
2176		set_cur_row(screen, screen->cur_row + move_down_by);
2177		screen->cursorp.row += move_down_by;
2178		ScrollSelection(screen, move_down_by, True);
2179	    }
2180	}
2181
2182	/* adjust scrolling region */
2183	resetMargins(xw);
2184	UIntClr(*flags, ORIGIN);
2185
2186	if (screen->cur_row > screen->max_row)
2187	    set_cur_row(screen, screen->max_row);
2188	if (screen->cur_col > screen->max_col)
2189	    set_cur_col(screen, screen->max_col);
2190
2191	screen->fullVwin.height = height - border;
2192	screen->fullVwin.width = width - border - screen->fullVwin.sb_info.width;
2193
2194	scroll_displayed_graphics(xw, -move_down_by);
2195    } else if (FullHeight(screen) == height && FullWidth(screen) == width) {
2196#if OPT_STATUS_LINE
2197	if (savedStatus != NULL) {
2198	    TRACE(("...status line is currently saved!\n"));
2199	    freeLineData(screen, savedStatus);
2200	}
2201#endif
2202	return;			/* nothing has changed at all */
2203    }
2204
2205    screen->fullVwin.fullheight = (Dimension) height;
2206    screen->fullVwin.fullwidth = (Dimension) width;
2207
2208    ResizeScrollBar(xw);
2209    ResizeSelection(screen, rows, cols);
2210
2211#ifndef NO_ACTIVE_ICON
2212    if (screen->iconVwin.window) {
2213	XWindowChanges changes;
2214	screen->iconVwin.width =
2215	    MaxCols(screen) * screen->iconVwin.f_width;
2216
2217	screen->iconVwin.height =
2218	    MaxRows(screen) * screen->iconVwin.f_height;
2219
2220	changes.width = screen->iconVwin.fullwidth =
2221	    (Dimension) ((unsigned) screen->iconVwin.width
2222			 + 2 * xw->misc.icon_border_width);
2223
2224	changes.height = screen->iconVwin.fullheight =
2225	    (Dimension) ((unsigned) screen->iconVwin.height
2226			 + 2 * xw->misc.icon_border_width);
2227
2228	changes.border_width = (int) xw->misc.icon_border_width;
2229
2230	TRACE(("resizing icon window %dx%d\n", changes.height, changes.width));
2231	XConfigureWindow(XtDisplay(xw), screen->iconVwin.window,
2232			 CWWidth | CWHeight | CWBorderWidth, &changes);
2233    }
2234#endif /* NO_ACTIVE_ICON */
2235
2236#if OPT_STATUS_LINE
2237    if (savedStatus != NULL) {
2238	int newRow = LastRowNumber(screen);
2239	LineData *newLD = getLineData(screen, newRow);
2240	TRACE(("...status line is currently on row %d\n",
2241	       LastRowNumber(screen)));
2242	copyLineData(newLD, savedStatus);
2243	freeLineData(screen, savedStatus);
2244    }
2245#endif
2246
2247#ifdef TTYSIZE_STRUCT
2248    if (update_winsize(screen, rows, cols, height, width) == 0) {
2249#if defined(SIGWINCH) && defined(TIOCGPGRP)
2250	if (screen->pid > 1) {
2251	    int pgrp;
2252
2253	    TRACE(("getting process-group\n"));
2254	    if (ioctl(screen->respond, TIOCGPGRP, &pgrp) != -1) {
2255		TRACE(("sending SIGWINCH to process group %d\n", pgrp));
2256		kill_process_group(pgrp, SIGWINCH);
2257	    }
2258	}
2259#endif /* SIGWINCH */
2260    }
2261#else
2262    TRACE(("ScreenResize cannot do anything to pty\n"));
2263#endif /* TTYSIZE_STRUCT */
2264    return;
2265}
2266
2267/*
2268 * Return true if any character cell starting at [row,col], for len-cells is
2269 * nonnull.
2270 */
2271Bool
2272non_blank_line(TScreen *screen,
2273	       int row,
2274	       int col,
2275	       int len)
2276{
2277    int i;
2278    Bool found = False;
2279    LineData *ld = getLineData(screen, row);
2280
2281    if (ld != 0) {
2282	for (i = col; i < len; i++) {
2283	    if (ld->charData[i]) {
2284		found = True;
2285		break;
2286	    }
2287	}
2288    }
2289    return found;
2290}
2291
2292/*
2293 * Limit/map rectangle parameters.
2294 */
2295#define minRectRow(screen) (getMinRow(screen) + 1)
2296#define minRectCol(screen) (getMinCol(screen) + 1)
2297#define maxRectRow(screen) (getMaxRow(screen) + 1)
2298#define maxRectCol(screen) (getMaxCol(screen) + 1)
2299
2300static int
2301limitedParseRow(XtermWidget xw, int row, int err)
2302{
2303    TScreen *screen = TScreenOf(xw);
2304    int min_row = minRectRow(screen);
2305    int max_row = maxRectRow(screen) + err;
2306
2307    if (xw->flags & ORIGIN)
2308	row += screen->top_marg;
2309
2310    if (row < min_row)
2311	row = min_row;
2312    else if (row > max_row)
2313	row = max_row;
2314
2315    return row;
2316}
2317
2318static int
2319limitedParseCol(XtermWidget xw, int col, int err)
2320{
2321    TScreen *screen = TScreenOf(xw);
2322    int min_col = minRectCol(screen);
2323    int max_col = maxRectCol(screen) + err;
2324
2325    if (xw->flags & ORIGIN)
2326	col += screen->lft_marg;
2327
2328    if (col < min_col)
2329	col = min_col;
2330    else if (col > max_col)
2331	col = max_col;
2332
2333    return col;
2334}
2335
2336#define LimitedParse(num, func, dft, err) \
2337	func(xw, (nparams > num && params[num] > 0) ? params[num] : dft, err)
2338
2339/*
2340 * Copy the rectangle boundaries into a struct, providing default values as
2341 * needed.
2342 */
2343void
2344xtermParseRect(XtermWidget xw, int nparams, int *params, XTermRect *target)
2345{
2346    TScreen *screen = TScreenOf(xw);
2347
2348    memset(target, 0, sizeof(*target));
2349    target->top = LimitedParse(0, limitedParseRow, minRectRow(screen), 1);
2350    target->left = LimitedParse(1, limitedParseCol, minRectCol(screen), 1);
2351    target->bottom = LimitedParse(2, limitedParseRow, maxRectRow(screen), 0);
2352    target->right = LimitedParse(3, limitedParseCol, maxRectCol(screen), 0);
2353    TRACE(("parsed %d params for rectangle %d,%d %d,%d default %d,%d %d,%d\n",
2354	   nparams,
2355	   target->top,
2356	   target->left,
2357	   target->bottom,
2358	   target->right,
2359	   minRectRow(screen),
2360	   minRectCol(screen),
2361	   maxRectRow(screen),
2362	   maxRectCol(screen)));
2363}
2364
2365static Bool
2366validRect(XtermWidget xw, XTermRect *target)
2367{
2368    TScreen *screen = TScreenOf(xw);
2369    Bool result = (target != 0
2370		   && target->top >= minRectRow(screen)
2371		   && target->left >= minRectCol(screen)
2372		   && target->top <= target->bottom
2373		   && target->left <= target->right
2374		   && target->top <= maxRectRow(screen)
2375		   && target->right <= maxRectCol(screen));
2376
2377    TRACE(("comparing against screensize %dx%d, is%s valid\n",
2378	   maxRectRow(screen),
2379	   maxRectCol(screen),
2380	   result ? "" : " NOT"));
2381    return result;
2382}
2383
2384/*
2385 * Fills a rectangle with the given 8-bit character and video-attributes.
2386 * Colors and double-size attribute are unmodified.
2387 */
2388void
2389ScrnFillRectangle(XtermWidget xw,
2390		  XTermRect *target,
2391		  int value,
2392		  unsigned flags,
2393		  Bool keepColors)
2394{
2395    IChar actual = (IChar) value;
2396    TScreen *screen = TScreenOf(xw);
2397
2398    TRACE(("filling rectangle with '%s' flags %#x\n",
2399	   visibleIChars(&actual, 1), flags));
2400    if (validRect(xw, target)) {
2401	LineData *ld;
2402	int top = (target->top - 1);
2403	int left = (target->left - 1);
2404	int right = (target->right - 1);
2405	int bottom = (target->bottom - 1);
2406	int numcols = (right - left) + 1;
2407	int numrows = (bottom - top) + 1;
2408	unsigned attrs = flags;
2409	int row, col;
2410	int b_left = 0;
2411	int b_right = 0;
2412
2413	(void) numcols;
2414
2415	attrs &= ATTRIBUTES;
2416	attrs |= CHARDRAWN;
2417	for (row = bottom; row >= top; row--) {
2418	    ld = getLineData(screen, row);
2419
2420	    TRACE(("filling %d [%d..%d]\n", row, left, left + numcols));
2421
2422	    if_OPT_WIDE_CHARS(screen, {
2423		if (left > 0) {
2424		    if (ld->charData[left] == HIDDEN_CHAR) {
2425			b_left = 1;
2426			Clear1Cell(ld, left - 1);
2427			Clear1Cell(ld, left);
2428		    }
2429		}
2430		if (right + 1 < (int) ld->lineSize) {
2431		    if (ld->charData[right + 1] == HIDDEN_CHAR) {
2432			b_right = 1;
2433			Clear1Cell(ld, right);
2434			Clear1Cell(ld, right + 1);
2435		    }
2436		}
2437	    });
2438
2439	    /*
2440	     * Fill attributes, preserving colors.
2441	     */
2442	    for (col = left; col <= right; ++col) {
2443		unsigned temp = ld->attribs[col];
2444
2445		if (!keepColors) {
2446		    UIntClr(temp, (FG_COLOR | BG_COLOR));
2447		}
2448		temp = attrs | (temp & (FG_COLOR | BG_COLOR)) | CHARDRAWN;
2449		ld->attribs[col] = (IAttr) temp;
2450		if_OPT_ISO_COLORS(screen, {
2451		    if (attrs & (FG_COLOR | BG_COLOR)) {
2452			ld->color[col] = xtermColorPair(xw);
2453		    }
2454		});
2455	    }
2456
2457	    for (col = left; col <= right; ++col)
2458		ld->charData[col] = actual;
2459
2460	    if_OPT_WIDE_CHARS(screen, {
2461		size_t off;
2462		for_each_combData(off, ld) {
2463		    memset(ld->combData[off] + left,
2464			   0,
2465			   (size_t) numcols * sizeof(CharData));
2466		}
2467	    })
2468	}
2469	chararea_clear_displayed_graphics(screen,
2470					  left,
2471					  top,
2472					  numcols, numrows);
2473	ScrnUpdate(xw,
2474		   top,
2475		   left - b_left,
2476		   numrows,
2477		   numcols + b_left + b_right,
2478		   False);
2479    }
2480}
2481
2482#if OPT_DEC_RECTOPS
2483/*
2484 * Copies the source rectangle to the target location, including video
2485 * attributes.
2486 *
2487 * This implementation ignores page numbers.
2488 *
2489 * The reference manual does not indicate if it handles overlapping copy
2490 * properly - so we make a local copy of the source rectangle first, then apply
2491 * the target from that.
2492 */
2493void
2494ScrnCopyRectangle(XtermWidget xw, XTermRect *source, int nparam, int *params)
2495{
2496    TScreen *screen = TScreenOf(xw);
2497
2498    TRACE(("copying rectangle\n"));
2499
2500    if (nparam > 4)
2501	nparam = 4;
2502
2503    if (validRect(xw, source)) {
2504	XTermRect target;
2505	xtermParseRect(xw,
2506		       ((nparam > 2) ? 2 : nparam),
2507		       params,
2508		       &target);
2509	if (validRect(xw, &target)) {
2510	    Cardinal high = (Cardinal) (source->bottom - source->top) + 1;
2511	    Cardinal wide = (Cardinal) (source->right - source->left) + 1;
2512	    Cardinal size = (high * wide);
2513	    int row, col;
2514	    Cardinal j, k;
2515	    LineData *ld;
2516	    int b_left = 0;
2517	    int b_right = 0;
2518
2519	    CellData *cells = newCellData(xw, size);
2520
2521	    if (cells != 0) {
2522
2523		TRACE(("OK - make copy %dx%d\n", high, wide));
2524		target.bottom = target.top + (int) (high - 1);
2525		target.right = target.left + (int) (wide - 1);
2526
2527		for (row = source->top - 1; row < source->bottom; ++row) {
2528		    ld = getLineData(screen, row);
2529		    if (ld == 0)
2530			continue;
2531		    j = (Cardinal) (row - (source->top - 1));
2532		    TRACE2(("ROW %d\n", row + 1));
2533		    for (col = source->left - 1; col < source->right; ++col) {
2534			k = (Cardinal) (col - (source->left - 1));
2535			saveCellData(screen, cells,
2536				     (j * wide) + k,
2537				     ld, source, col);
2538		    }
2539		}
2540		for (row = target.top - 1; row < target.bottom; ++row) {
2541		    ld = getLineData(screen, row);
2542		    if (ld == 0)
2543			continue;
2544		    j = (Cardinal) (row - (target.top - 1));
2545		    TRACE2(("ROW %d\n", row + 1));
2546		    for (col = target.left - 1; col < target.right; ++col) {
2547			k = (Cardinal) (col - (target.left - 1));
2548			if (row >= getMinRow(screen)
2549			    && row <= getMaxRow(screen)
2550			    && col >= getMinCol(screen)
2551			    && col <= getMaxCol(screen)
2552			    && (j < high)
2553			    && (k < wide)) {
2554			    if_OPT_WIDE_CHARS(screen, {
2555				if (ld->charData[col] == HIDDEN_CHAR
2556				    && (col + 1) == target.left) {
2557				    b_left = 1;
2558				    Clear1Cell(ld, col - 1);
2559				}
2560				if ((col + 1) == target.right
2561				    && ld->charData[col] == HIDDEN_CHAR) {
2562				    b_right = 1;
2563				}
2564			    });
2565			    restoreCellData(screen, cells,
2566					    (j * wide) + k,
2567					    ld, &target, col);
2568			}
2569			ld->attribs[col] |= CHARDRAWN;
2570		    }
2571#if OPT_BLINK_TEXT
2572		    if (LineHasBlinking(screen, ld)) {
2573			LineSetBlinked(ld);
2574		    } else {
2575			LineClrBlinked(ld);
2576		    }
2577#endif
2578		}
2579		free(cells);
2580
2581		ScrnUpdate(xw,
2582			   (target.top - 1),
2583			   (target.left - (1 + b_left)),
2584			   (target.bottom - target.top) + 1,
2585			   ((target.right - target.left) + (1 + b_left + b_right)),
2586			   False);
2587	    }
2588	}
2589    }
2590}
2591
2592/*
2593 * Modifies the video-attributes only - so selection (not a video attribute) is
2594 * unaffected.  Colors and double-size flags are unaffected as well.
2595 *
2596 * FIXME: our representation for "invisible" does not work with this operation,
2597 * since the attribute byte is fully-allocated for other flags.  The logic
2598 * is shown for INVISIBLE because it's harmless, and useful in case the
2599 * CHARDRAWN or PROTECTED flags are reassigned.
2600 */
2601void
2602ScrnMarkRectangle(XtermWidget xw,
2603		  XTermRect *target,
2604		  Bool reverse,
2605		  int nparam,
2606		  int *params)
2607{
2608    TScreen *screen = TScreenOf(xw);
2609    Bool exact = (screen->cur_decsace == 2);
2610
2611    TRACE(("%s %s\n",
2612	   reverse ? "reversing" : "marking",
2613	   (exact
2614	    ? "rectangle"
2615	    : "region")));
2616
2617    if (validRect(xw, target)) {
2618	LineData *ld;
2619	int top = target->top - 1;
2620	int bottom = target->bottom - 1;
2621	int row, col;
2622	int n;
2623
2624	for (row = top; row <= bottom; ++row) {
2625	    int left = ((exact || (row == top))
2626			? (target->left - 1)
2627			: getMinCol(screen));
2628	    int right = ((exact || (row == bottom))
2629			 ? (target->right - 1)
2630			 : getMaxCol(screen));
2631
2632	    ld = getLineData(screen, row);
2633
2634	    TRACE(("marking %d [%d..%d]\n", row, left, right));
2635	    for (col = left; col <= right; ++col) {
2636		unsigned flags = ld->attribs[col];
2637
2638		for (n = 0; n < nparam; ++n) {
2639#if OPT_TRACE
2640		    if (row == top && col == left)
2641			TRACE(("attr param[%d] %d\n", n + 1, params[n]));
2642#endif
2643		    if (reverse) {
2644			switch (params[n]) {
2645			case 1:
2646			    flags ^= BOLD;
2647			    break;
2648			case 4:
2649			    flags ^= UNDERLINE;
2650			    break;
2651			case 5:
2652			    flags ^= BLINK;
2653			    break;
2654			case 7:
2655			    flags ^= INVERSE;
2656			    break;
2657			case 8:
2658			    flags ^= INVISIBLE;
2659			    break;
2660			}
2661		    } else {
2662			switch (params[n]) {
2663			case 0:
2664			    UIntClr(flags, SGR_MASK);
2665			    break;
2666			case 1:
2667			    flags |= BOLD;
2668			    break;
2669			case 4:
2670			    flags |= UNDERLINE;
2671			    break;
2672			case 5:
2673			    flags |= BLINK;
2674			    break;
2675			case 7:
2676			    flags |= INVERSE;
2677			    break;
2678			case 8:
2679			    flags |= INVISIBLE;
2680			    break;
2681			case 22:
2682			    UIntClr(flags, BOLD);
2683			    break;
2684			case 24:
2685			    UIntClr(flags, UNDERLINE);
2686			    break;
2687			case 25:
2688			    UIntClr(flags, BLINK);
2689			    break;
2690			case 27:
2691			    UIntClr(flags, INVERSE);
2692			    break;
2693			case 28:
2694			    UIntClr(flags, INVISIBLE);
2695			    break;
2696			}
2697		    }
2698		}
2699#if OPT_TRACE
2700		if (row == top && col == left)
2701		    TRACE(("first mask-change is %#x\n",
2702			   ld->attribs[col] ^ flags));
2703#endif
2704		ld->attribs[col] = (IAttr) flags;
2705	    }
2706	}
2707	ScrnRefresh(xw,
2708		    (target->top - 1),
2709		    (exact ? (target->left - 1) : getMinCol(screen)),
2710		    (target->bottom - target->top) + 1,
2711		    (exact
2712		     ? ((target->right - target->left) + 1)
2713		     : (getMaxCol(screen) - getMinCol(screen) + 1)),
2714		    True);
2715    }
2716}
2717
2718/*
2719 * Resets characters to space, except where prohibited by DECSCA.  Video
2720 * attributes (including color) are untouched.
2721 */
2722void
2723ScrnWipeRectangle(XtermWidget xw,
2724		  XTermRect *target)
2725{
2726    TScreen *screen = TScreenOf(xw);
2727
2728    TRACE(("wiping rectangle\n"));
2729
2730#define IsProtected(ld, col) \
2731		((screen->protected_mode == DEC_PROTECT) \
2732		 && (ld->attribs[col] & PROTECTED))
2733
2734    if (validRect(xw, target)) {
2735	LineData *ld;
2736	int top = target->top - 1;
2737	int left = target->left - 1;
2738	int right = target->right - 1;
2739	int bottom = target->bottom - 1;
2740	int numcols = (right - left) + 1;
2741	int numrows = (bottom - top) + 1;
2742	int row, col;
2743	int b_left = 0;
2744	int b_right = 0;
2745
2746	for (row = top; row <= bottom; ++row) {
2747	    TRACE(("wiping %d [%d..%d]\n", row, left, right));
2748
2749	    ld = getLineData(screen, row);
2750
2751	    if_OPT_WIDE_CHARS(screen, {
2752		if (left > 0 && !IsProtected(ld, left)) {
2753		    if (ld->charData[left] == HIDDEN_CHAR) {
2754			b_left = 1;
2755			Clear1Cell(ld, left - 1);
2756			Clear1Cell(ld, left);
2757		    }
2758		}
2759		if (right + 1 < (int) ld->lineSize && !IsProtected(ld, right)) {
2760		    if (ld->charData[right + 1] == HIDDEN_CHAR) {
2761			b_right = 1;
2762			Clear1Cell(ld, right);
2763			Clear1Cell(ld, right + 1);
2764		    }
2765		}
2766	    });
2767
2768	    for (col = left; col <= right; ++col) {
2769		if (!IsProtected(ld, col)) {
2770		    ld->attribs[col] |= CHARDRAWN;
2771		    Clear1Cell(ld, col);
2772		}
2773	    }
2774	}
2775	chararea_clear_displayed_graphics(screen,
2776					  left,
2777					  top,
2778					  numcols, numrows);
2779	ScrnUpdate(xw,
2780		   top,
2781		   left - b_left,
2782		   numrows,
2783		   numcols + b_left + b_right,
2784		   False);
2785    }
2786}
2787
2788/*
2789 * Compute a checksum, ignoring the page number (since we have only one page).
2790 */
2791void
2792xtermCheckRect(XtermWidget xw,
2793	       int nparam,
2794	       int *params,
2795	       int *result)
2796{
2797    TScreen *screen = TScreenOf(xw);
2798    XTermRect target;
2799    LineData *ld;
2800    int total = 0;
2801    int trimmed = 0;
2802    int mode = screen->checksum_ext;
2803
2804    TRACE(("xtermCheckRect: %s%s%s%s%s%s%s\n",
2805	   (mode == csDEC) ? "DEC" : "checksumExtension",
2806	   (mode & csPOSITIVE) ? " !negative" : "",
2807	   (mode & csATTRIBS) ? " !attribs" : "",
2808	   (mode & csNOTRIM) ? " !trimmed" : "",
2809	   (mode & csDRAWN) ? " !drawn" : "",
2810	   (mode & csBYTE) ? " !byte" : "",
2811	   (mode & cs8TH) ? " !7bit" : ""));
2812
2813    if (nparam > 2) {
2814	nparam -= 2;
2815	params += 2;
2816    }
2817    xtermParseRect(xw, nparam, params, &target);
2818    if (validRect(xw, &target)) {
2819	int top = target.top - 1;
2820	int bottom = target.bottom - 1;
2821	int row, col;
2822	Boolean first = True;
2823	int embedded = 0;
2824	DECNRCM_codes my_GR = screen->gsets[(int) screen->curgr];
2825
2826	for (row = top; row <= bottom; ++row) {
2827	    int left = (target.left - 1);
2828	    int right = (target.right - 1);
2829
2830	    ld = getLineData(screen, row);
2831	    if (ld == 0)
2832		continue;
2833	    for (col = left; col <= right && col < (int) ld->lineSize; ++col) {
2834		int ch = ((ld->attribs[col] & CHARDRAWN)
2835			  ? (int) ld->charData[col]
2836			  : ' ');
2837		if (!(mode & csBYTE)) {
2838		    unsigned c2 = (unsigned) ch;
2839		    if (c2 > 0x7f && my_GR != nrc_ASCII) {
2840			c2 = xtermCharSetIn(xw, c2, my_GR);
2841			if (!(mode & cs8TH) && (c2 < 0x80))
2842			    c2 |= 0x80;
2843		    }
2844		    ch = (c2 & 0xff);
2845		}
2846		if (!(mode & csATTRIBS)) {
2847		    if (ld->attribs[col] & UNDERLINE)
2848			ch += 0x10;
2849		    if (ld->attribs[col] & INVERSE)
2850			ch += 0x20;
2851		    if (ld->attribs[col] & BLINK)
2852			ch += 0x40;
2853		    if (ld->attribs[col] & BOLD)
2854			ch += 0x80;
2855		}
2856		if (first || (ch != ' ') || (ld->attribs[col] & DRAWX_MASK)) {
2857		    trimmed += ch + embedded;
2858		    embedded = 0;
2859		} else if (ch == ' ') {
2860		    if ((mode & csNOTRIM))
2861			embedded += ch;
2862		}
2863		if ((ld->attribs[col] & CHARDRAWN)) {
2864		    total += ch;
2865		    if_OPT_WIDE_CHARS(screen, {
2866			/* FIXME - not counted if trimming blanks */
2867			if (!(mode & csBYTE)) {
2868			    size_t off;
2869			    for_each_combData(off, ld) {
2870				total += (int) ld->combData[off][col];
2871			    }
2872			}
2873		    })
2874		} else if (!(mode & csDRAWN)) {
2875		    total += ch;
2876		}
2877		first = ((mode & csNOTRIM) != 0) ? True : False;
2878	    }
2879	    if (!(mode & csNOTRIM)) {
2880		embedded = 0;
2881		first = False;
2882	    }
2883	}
2884    }
2885    if (!(mode & csNOTRIM))
2886	total = trimmed;
2887    if (!(mode & csPOSITIVE))
2888	total = -total;
2889    *result = total;
2890}
2891#endif /* OPT_DEC_RECTOPS */
2892
2893#if OPT_MAXIMIZE
2894
2895static _Xconst char *
2896ewmhProperty(int mode)
2897{
2898    _Xconst char *result;
2899    switch (mode) {
2900    default:
2901	result = 0;
2902	break;
2903    case 1:
2904	result = "_NET_WM_STATE_FULLSCREEN";
2905	break;
2906    case 2:
2907	result = "_NET_WM_STATE_MAXIMIZED_VERT";
2908	break;
2909    case 3:
2910	result = "_NET_WM_STATE_MAXIMIZED_HORZ";
2911	break;
2912    }
2913    return result;
2914}
2915
2916static void
2917set_resize_increments(XtermWidget xw)
2918{
2919    TScreen *screen = TScreenOf(xw);
2920    int min_width = (2 * screen->border) + screen->fullVwin.sb_info.width;
2921    int min_height = (2 * screen->border);
2922    XSizeHints sizehints;
2923
2924    TRACE(("set_resize_increments\n"));
2925    memset(&sizehints, 0, sizeof(XSizeHints));
2926    sizehints.width_inc = FontWidth(screen);
2927    sizehints.height_inc = FontHeight(screen);
2928    sizehints.flags = PResizeInc;
2929    TRACE_HINTS(&sizehints);
2930    XSetWMNormalHints(screen->display, VShellWindow(xw), &sizehints);
2931
2932    TRACE(("setting values for widget %p:\n", (void *) SHELL_OF(xw)));
2933    TRACE(("   base width  %d\n", min_width));
2934    TRACE(("   base height %d\n", min_width));
2935    TRACE(("   min width   %d\n", min_width + FontWidth(screen)));
2936    TRACE(("   min height  %d\n", min_width + FontHeight(screen)));
2937    TRACE(("   width inc   %d\n", FontWidth(screen)));
2938    TRACE(("   height inc  %d\n", FontHeight(screen)));
2939
2940    XtVaSetValues(SHELL_OF(xw),
2941		  XtNbaseWidth, min_width,
2942		  XtNbaseHeight, min_height,
2943		  XtNminWidth, min_width + FontWidth(screen),
2944		  XtNminHeight, min_height + FontHeight(screen),
2945		  XtNwidthInc, FontWidth(screen),
2946		  XtNheightInc, FontHeight(screen),
2947		  (XtPointer) 0);
2948
2949    XFlush(XtDisplay(xw));
2950}
2951
2952static void
2953unset_resize_increments(XtermWidget xw)
2954{
2955    TScreen *screen = TScreenOf(xw);
2956    XSizeHints sizehints;
2957
2958    TRACE(("unset_resize_increments\n"));
2959    memset(&sizehints, 0, sizeof(XSizeHints));
2960    sizehints.width_inc = 1;
2961    sizehints.height_inc = 1;
2962    sizehints.flags = PResizeInc;
2963    TRACE_HINTS(&sizehints);
2964    XSetWMNormalHints(screen->display, VShellWindow(xw), &sizehints);
2965
2966    XtVaSetValues(SHELL_OF(xw),
2967		  XtNwidthInc, 1,
2968		  XtNheightInc, 1,
2969		  (XtPointer) 0);
2970
2971    XFlush(XtDisplay(xw));
2972}
2973
2974static void
2975set_ewmh_hint(Display *dpy, Window window, int operation, _Xconst char *prop)
2976{
2977    XEvent e;
2978    Atom atom_fullscreen = XInternAtom(dpy, prop, False);
2979    Atom atom_state = XInternAtom(dpy, "_NET_WM_STATE", False);
2980
2981#if OPT_TRACE
2982    const char *what = "?";
2983    switch (operation) {
2984    case _NET_WM_STATE_ADD:
2985	what = "adding";
2986	break;
2987    case _NET_WM_STATE_REMOVE:
2988	what = "removing";
2989	break;
2990    }
2991    TRACE(("set_ewmh_hint %s %s\n", what, prop));
2992#endif
2993
2994    memset(&e, 0, sizeof(e));
2995    e.xclient.type = ClientMessage;
2996    e.xclient.message_type = atom_state;
2997    e.xclient.display = dpy;
2998    e.xclient.window = window;
2999    e.xclient.format = 32;
3000    e.xclient.data.l[0] = operation;
3001    e.xclient.data.l[1] = (long) atom_fullscreen;
3002
3003    XSendEvent(dpy, DefaultRootWindow(dpy), False,
3004	       SubstructureRedirectMask, &e);
3005}
3006
3007/*
3008 * Check if the given property is supported on the root window.
3009 *
3010 * The XGetWindowProperty function returns a list of Atom's which corresponds
3011 * to the output of xprop.  The actual list (ignore the manpage, which refers
3012 * to an array of 32-bit values) is constructed by _XRead32, which uses long
3013 * as a datatype.
3014 *
3015 * Alternatively, we could check _NET_WM_ALLOWED_ACTIONS on the application's
3016 * window.
3017 */
3018static Boolean
3019probe_netwm(Display *dpy, _Xconst char *propname)
3020{
3021    Atom atom_fullscreen = XInternAtom(dpy, propname, False);
3022    Atom atom_supported = XInternAtom(dpy, "_NET_SUPPORTED", False);
3023    Atom actual_type;
3024    int actual_format;
3025    long long_offset = 0;
3026    long long_length = 128;	/* number of items to ask for at a time */
3027    unsigned int i;
3028    unsigned long nitems, bytes_after;
3029    unsigned char *args;
3030    long *ldata;
3031    Boolean has_capability = False;
3032    Boolean rc;
3033
3034    while (!has_capability) {
3035	rc = xtermGetWinProp(dpy,
3036			     DefaultRootWindow(dpy),
3037			     atom_supported,
3038			     long_offset,
3039			     long_length,
3040			     AnyPropertyType,	/* req_type */
3041			     &actual_type,	/* actual_type_return */
3042			     &actual_format,	/* actual_format_return */
3043			     &nitems,	/* nitems_return */
3044			     &bytes_after,	/* bytes_after_return */
3045			     &args	/* prop_return */
3046	    );
3047	if (!rc
3048	    || actual_type != XA_ATOM) {
3049	    break;
3050	}
3051	ldata = (long *) (void *) args;
3052	for (i = 0; i < nitems; i++) {
3053#if OPT_TRACE > 1
3054	    char *name;
3055	    if ((name = XGetAtomName(dpy, ldata[i])) != 0) {
3056		TRACE(("atom[%d] = %s\n", i, name));
3057		XFree(name);
3058	    } else {
3059		TRACE(("atom[%d] = ?\n", i));
3060	    }
3061#endif
3062	    if ((Atom) ldata[i] == atom_fullscreen) {
3063		has_capability = True;
3064		break;
3065	    }
3066	}
3067	XFree(ldata);
3068
3069	if (!has_capability) {
3070	    if (bytes_after != 0) {
3071		long remaining = (long) (bytes_after / sizeof(long));
3072		if (long_length > remaining)
3073		    long_length = remaining;
3074		long_offset += (long) nitems;
3075	    } else {
3076		break;
3077	    }
3078	}
3079    }
3080
3081    TRACE(("probe_netwm(%s) ->%d\n", propname, has_capability));
3082    return has_capability;
3083}
3084
3085/*
3086 * Alter fullscreen mode for the xterm widget, if the window manager supports
3087 * that feature.
3088 */
3089void
3090FullScreen(XtermWidget xw, int new_ewmh_mode)
3091{
3092    TScreen *screen = TScreenOf(xw);
3093    Display *dpy = screen->display;
3094    int old_ewmh_mode;
3095    _Xconst char *oldprop;
3096    _Xconst char *newprop;
3097
3098    int which = 0;
3099    Window window;
3100
3101#if OPT_TEK4014
3102    if (TEK4014_ACTIVE(xw)) {
3103	which = 1;
3104	window = TShellWindow;
3105    } else
3106#endif
3107	window = VShellWindow(xw);
3108
3109    old_ewmh_mode = xw->work.ewmh[which].mode;
3110    oldprop = ewmhProperty(old_ewmh_mode);
3111    newprop = ewmhProperty(new_ewmh_mode);
3112
3113    TRACE(("FullScreen %d:%s -> %d:%s\n",
3114	   old_ewmh_mode, NonNull(oldprop),
3115	   new_ewmh_mode, NonNull(newprop)));
3116
3117    if (new_ewmh_mode == old_ewmh_mode) {
3118	TRACE(("...unchanged\n"));
3119	return;
3120    } else if (new_ewmh_mode < 0 || new_ewmh_mode > MAX_EWMH_MODE) {
3121	TRACE(("BUG: FullScreen %d\n", new_ewmh_mode));
3122	return;
3123    } else if (new_ewmh_mode == 0) {
3124	xw->work.ewmh[which].checked[new_ewmh_mode] = True;
3125	xw->work.ewmh[which].allowed[new_ewmh_mode] = True;
3126    } else if (resource.fullscreen == esNever) {
3127	xw->work.ewmh[which].checked[new_ewmh_mode] = True;
3128	xw->work.ewmh[which].allowed[new_ewmh_mode] = False;
3129    } else if (!xw->work.ewmh[which].checked[new_ewmh_mode]) {
3130	xw->work.ewmh[which].checked[new_ewmh_mode] = True;
3131	xw->work.ewmh[which].allowed[new_ewmh_mode] = probe_netwm(dpy, newprop);
3132    }
3133
3134    if (xw->work.ewmh[which].allowed[new_ewmh_mode]) {
3135	TRACE(("...new EWMH mode is allowed\n"));
3136	if (new_ewmh_mode && !xw->work.ewmh[which].mode) {
3137	    unset_resize_increments(xw);
3138	    set_ewmh_hint(dpy, window, _NET_WM_STATE_ADD, newprop);
3139	} else if (xw->work.ewmh[which].mode && !new_ewmh_mode) {
3140	    if (!xw->misc.resizeByPixel) {
3141		set_resize_increments(xw);
3142	    }
3143	    set_ewmh_hint(dpy, window, _NET_WM_STATE_REMOVE, oldprop);
3144	} else {
3145	    set_ewmh_hint(dpy, window, _NET_WM_STATE_REMOVE, oldprop);
3146	    set_ewmh_hint(dpy, window, _NET_WM_STATE_ADD, newprop);
3147	}
3148	xw->work.ewmh[which].mode = new_ewmh_mode;
3149	update_fullscreen();
3150    } else {
3151	Bell(xw, XkbBI_MinorError, 100);
3152    }
3153}
3154#endif /* OPT_MAXIMIZE */
3155