screen.c revision 492d43a5
1/* $XTermId: screen.c,v 1.426 2010/10/11 00:46:05 tom Exp $ */
2
3/*
4 * Copyright 1999-2009,2010 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 <xcharmouse.h>
62#include <xterm_io.h>
63
64#if OPT_WIDE_CHARS
65#include <fontutils.h>
66#include <menu.h>
67#endif
68
69#include <assert.h>
70#include <signal.h>
71
72#define inSaveBuf(screen, buf, inx) \
73	((buf) == (screen)->saveBuf_index && \
74	 ((inx) < (screen)->savelines || (screen)->savelines == 0))
75
76#define getMinRow(screen) ((xw->flags & ORIGIN) ? (screen)->top_marg : 0)
77#define getMaxRow(screen) ((xw->flags & ORIGIN) ? (screen)->bot_marg : (screen)->max_row)
78#define getMinCol(screen) 0
79#define getMaxCol(screen) ((screen)->max_col)
80
81#define MoveLineData(base, dst, src, len) \
82	memmove(scrnHeadAddr(screen, base, (unsigned) (dst)), \
83		scrnHeadAddr(screen, base, (unsigned) (src)), \
84		(size_t) scrnHeadSize(screen, (unsigned) (len)))
85
86#define SaveLineData(base, src, len) \
87	(void) ScrnPointers(screen, len); \
88	memcpy (screen->save_ptr, \
89		scrnHeadAddr(screen, base, src), \
90		(size_t) scrnHeadSize(screen, (unsigned) (len)))
91
92#define RestoreLineData(base, dst, len) \
93	memcpy (scrnHeadAddr(screen, base, dst), \
94		screen->save_ptr, \
95		(size_t) scrnHeadSize(screen, (unsigned) (len)))
96
97#if OPT_SAVE_LINES
98#define VisBuf(screen) screen->editBuf_index[screen->whichBuf]
99#else
100#define VisBuf(screen) scrnHeadAddr(screen, screen->saveBuf_index, (unsigned) savelines)
101#endif
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 *) src; \
123		assert(IsAligned(dst)); \
124		src += skipNcol##type
125
126#define ScrnBufAddr(ptrs, offset)  (ScrnBuf)    ((char *) (ptrs) + (offset))
127#define LineDataAddr(ptrs, offset) (LineData *) ((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
150static unsigned
151scrnHeadSize(TScreen * screen, unsigned count)
152{
153    unsigned result = SizeOfLineData;
154
155    (void) screen;
156
157#if OPT_WIDE_CHARS
158    if (screen->wide_chars) {
159	result += (unsigned) screen->lineExtra;
160    }
161#endif
162    result *= count;
163
164    return result;
165}
166
167ScrnBuf
168scrnHeadAddr(TScreen * screen, ScrnBuf base, unsigned offset)
169{
170    unsigned size = scrnHeadSize(screen, offset);
171    ScrnBuf result = ScrnBufAddr(base, size);
172
173    assert((int) offset >= 0);
174
175    return result;
176}
177
178/*
179 * Given a block of data, build index to it in the 'base' parameter.
180 */
181void
182setupLineData(TScreen * screen, ScrnBuf base, Char * data, unsigned nrow, unsigned ncol)
183{
184    unsigned i;
185    unsigned offset = 0;
186    unsigned jump = scrnHeadSize(screen, 1);
187    LineData *ptr;
188#if OPT_WIDE_CHARS
189    unsigned j;
190#endif
191    /* these names are based on types */
192    unsigned skipNcolChar = (ncol * SizeofScrnPtr(attribs));
193    unsigned skipNcolCharData = (ncol * SizeofScrnPtr(charData));
194#if OPT_ISO_COLORS
195    unsigned skipNcolCellColor = (ncol * SizeofScrnPtr(color));
196#endif
197
198    AlignValue(skipNcolChar);
199#if OPT_ISO_COLORS
200    AlignValue(skipNcolCellColor);
201#endif
202    AlignValue(skipNcolCharData);
203
204    for (i = 0; i < nrow; i++, offset += jump) {
205	ptr = LineDataAddr(base, offset);
206
207	ptr->lineSize = (Dimension) ncol;
208	ptr->bufHead = 0;
209#if OPT_DEC_CHRSET
210	SetLineDblCS(ptr, 0);
211#endif
212	SetupScrnPtr(ptr->attribs, data, Char);
213#if OPT_ISO_COLORS
214	SetupScrnPtr(ptr->color, data, CellColor);
215#endif
216	SetupScrnPtr(ptr->charData, data, CharData);
217#if OPT_WIDE_CHARS
218	if (screen->wide_chars) {
219	    unsigned extra = (unsigned) screen->max_combining;
220
221	    ptr->combSize = (Char) extra;
222	    for (j = 0; j < extra; ++j) {
223		SetupScrnPtr(ptr->combData[j], data, CharData);
224	    }
225	}
226#endif
227    }
228}
229
230#define ExtractScrnData(name) \
231		memcpy(dstPtrs->name, \
232		       ((LineData *) srcPtrs)->name,\
233		       dstCols * sizeof(dstPtrs->name[0])); \
234		nextPtr += (srcCols * sizeof(dstPtrs->name[0]))
235
236/*
237 * As part of reallocating the screen buffer when resizing, extract from
238 * the old copy of the screen buffer the data which will be used in the
239 * new copy of the screen buffer.
240 */
241static void
242extractScrnData(TScreen * screen,
243		ScrnBuf dstPtrs,
244		ScrnBuf srcPtrs,
245		unsigned nrows,
246		unsigned move_down)
247{
248    unsigned j;
249
250    TRACE(("extractScrnData(nrows %d)\n", nrows));
251
252    TRACE_SCRNBUF("extract from", screen, srcPtrs, nrows);
253    for (j = 0; j < nrows; j++) {
254	LineData *dst = (LineData *) scrnHeadAddr(screen,
255						  dstPtrs, j + move_down);
256	LineData *src = (LineData *) scrnHeadAddr(screen,
257						  srcPtrs, j);
258	copyLineData(dst, src);
259    }
260}
261
262static ScrnPtr *
263allocScrnHead(TScreen * screen, unsigned nrow)
264{
265    ScrnPtr *result;
266    unsigned size = scrnHeadSize(screen, 1);
267
268    result = (ScrnPtr *) calloc((size_t) nrow, (size_t) size);
269    if (result == 0)
270	SysError(ERROR_SCALLOC);
271
272    TRACE(("allocScrnHead %d -> %d -> %p..%p\n", nrow, nrow * size,
273	   (void *) result,
274	   (char *) result + (nrow * size) - 1));
275    return result;
276}
277
278/*
279 * Return the size of a line's data.
280 */
281static unsigned
282sizeofScrnRow(TScreen * screen, unsigned ncol)
283{
284    unsigned result;
285    unsigned sizeAttribs;
286#if OPT_ISO_COLORS
287    unsigned sizeColors;
288#endif
289
290    (void) screen;
291
292    result = (ncol * (unsigned) sizeof(CharData));
293    AlignValue(result);
294
295#if OPT_WIDE_CHARS
296    if (screen->wide_chars) {
297	result *= (unsigned) (1 + screen->max_combining);
298    }
299#endif
300
301    sizeAttribs = (ncol * SizeofScrnPtr(attribs));
302    AlignValue(sizeAttribs);
303    result += sizeAttribs;
304
305#if OPT_ISO_COLORS
306    sizeColors = (ncol * SizeofScrnPtr(color));
307    AlignValue(sizeColors);
308    result += sizeColors;
309#endif
310
311    return result;
312}
313
314Char *
315allocScrnData(TScreen * screen, unsigned nrow, unsigned ncol)
316{
317    Char *result;
318    size_t length = (nrow * sizeofScrnRow(screen, ncol));
319
320    if ((result = (Char *) calloc(length, sizeof(Char))) == 0)
321	SysError(ERROR_SCALLOC2);
322
323    TRACE(("allocScrnData %ux%u -> %lu -> %p..%p\n",
324	   nrow, ncol, (unsigned long) length, result, result + length - 1));
325    return result;
326}
327
328/*
329 * Allocates memory for a 2-dimensional array of chars and returns a pointer
330 * thereto.  Each line is formed from a set of char arrays, with an index
331 * (i.e., the ScrnBuf type).  The first pointer in the index is reserved for
332 * per-line flags, and does not point to data.
333 *
334 * After the per-line flags, we have a series of pointers to char arrays:  The
335 * first one is the actual character array, the second one is the attributes,
336 * the third is the foreground and background colors, and the fourth denotes
337 * the character set.
338 *
339 * We store it all as pointers, because of alignment considerations.
340 */
341ScrnBuf
342allocScrnBuf(XtermWidget xw, unsigned nrow, unsigned ncol, Char ** addr)
343{
344    TScreen *screen = TScreenOf(xw);
345    ScrnBuf base = 0;
346
347    if (nrow != 0) {
348	base = allocScrnHead(screen, nrow);
349	*addr = allocScrnData(screen, nrow, ncol);
350
351	setupLineData(screen, base, *addr, nrow, ncol);
352    }
353
354    TRACE(("allocScrnBuf %dx%d ->%p\n", nrow, ncol, (void *) base));
355    return (base);
356}
357
358#if OPT_SAVE_LINES
359/*
360 * Copy line-data from the visible (edit) buffer to the save-lines buffer.
361 */
362static void
363saveEditBufLines(TScreen * screen, ScrnBuf sb, unsigned n)
364{
365    unsigned j;
366
367    TRACE(("...copying %d lines from editBuf to saveBuf\n", n));
368#if OPT_FIFO_LINES
369    (void) sb;
370#endif
371    for (j = 0; j < n; ++j) {
372#if OPT_FIFO_LINES
373	LineData *dst = addScrollback(screen);
374#else
375	unsigned k = (screen->savelines + j - n);
376	LineData *dst = (LineData *) scrnHeadAddr(screen, sb, k);
377#endif
378	LineData *src = getLineData(screen, (int) j);
379	copyLineData(dst, src);
380    }
381}
382
383/*
384 * Copy line-data from the save-lines buffer to the visible (edit) buffer.
385 */
386static void
387unsaveEditBufLines(TScreen * screen, ScrnBuf sb, unsigned n)
388{
389    unsigned j;
390
391    TRACE(("...copying %d lines from saveBuf to editBuf\n", n));
392    for (j = 0; j < n; ++j) {
393	int extra = (int) (n - j);
394	LineData *dst = (LineData *) scrnHeadAddr(screen, sb, j);
395#if OPT_FIFO_LINES
396	LineData *src;
397
398	if ((screen->saved_fifo - extra) <= 0) {
399	    TRACE(("...FIXME: must clear text!\n"));
400	    continue;
401	}
402	src = getScrollback(screen, -extra);
403#else
404	unsigned k = (screen->savelines - extra);
405	LineData *src = (LineData *) scrnHeadAddr(screen,
406						  screen->saveBuf_index, k);
407#endif
408	copyLineData(dst, src);
409    }
410}
411#endif
412
413/*
414 *  This is called when the screen is resized.
415 *  Returns the number of lines the text was moved down (neg for up).
416 *  (Return value only necessary with SouthWestGravity.)
417 */
418static int
419Reallocate(XtermWidget xw,
420	   ScrnBuf * sbuf,
421	   Char ** sbufaddr,
422	   unsigned nrow,
423	   unsigned ncol,
424	   unsigned oldrow)
425{
426    TScreen *screen = TScreenOf(xw);
427    ScrnBuf oldBufHead;
428    ScrnBuf newBufHead;
429    Char *newBufData;
430    unsigned minrows;
431    Char *oldBufData;
432    int move_down = 0, move_up = 0;
433
434    if (sbuf == NULL || *sbuf == NULL) {
435	return 0;
436    }
437
438    oldBufData = *sbufaddr;
439
440    TRACE(("Reallocate %dx%d -> %dx%d\n", oldrow, MaxCols(screen), nrow, ncol));
441
442    /*
443     * realloc sbuf, the pointers to all the lines.
444     * If the screen shrinks, remove lines off the top of the buffer
445     * if resizeGravity resource says to do so.
446     */
447    TRACE(("Check move_up, nrow %d vs oldrow %d (resizeGravity %s)\n",
448	   nrow, oldrow,
449	   BtoS(GravityIsSouthWest(xw))));
450    if (GravityIsSouthWest(xw)) {
451	if (nrow < oldrow) {
452	    /* Remove lines off the top of the buffer if necessary. */
453	    move_up = (int) (oldrow - nrow)
454		- (TScreenOf(xw)->max_row - TScreenOf(xw)->cur_row);
455	    if (move_up < 0)
456		move_up = 0;
457	    /* Overlapping move here! */
458	    TRACE(("move_up %d\n", move_up));
459	    if (move_up) {
460		ScrnBuf dst = *sbuf;
461		unsigned len = (unsigned) ((int) oldrow - move_up);
462
463		TRACE_SCRNBUF("before move_up", screen, dst, oldrow);
464		SaveLineData(dst, 0, (size_t) move_up);
465		MoveLineData(dst, 0, (size_t) move_up, len);
466		RestoreLineData(dst, len, (size_t) move_up);
467		TRACE_SCRNBUF("after move_up", screen, dst, oldrow);
468	    }
469	}
470    }
471    oldBufHead = *sbuf;
472    *sbuf = allocScrnHead(screen, (unsigned) nrow);
473    newBufHead = *sbuf;
474
475    /*
476     * Create the new buffer space and copy old buffer contents there, line by
477     * line.
478     */
479    newBufData = allocScrnData(screen, nrow, ncol);
480    *sbufaddr = newBufData;
481
482    minrows = (oldrow < nrow) ? oldrow : nrow;
483    if (GravityIsSouthWest(xw)) {
484	if (nrow > oldrow) {
485	    /* move data down to bottom of expanded screen */
486	    move_down = Min((int) (nrow - oldrow), TScreenOf(xw)->savedlines);
487	}
488    }
489
490    setupLineData(screen, newBufHead, *sbufaddr, nrow, ncol);
491    extractScrnData(screen, newBufHead, oldBufHead, minrows,
492#if OPT_SAVE_LINES
493		    0
494#else
495		    (unsigned) move_down
496#endif
497	);
498    free(oldBufHead);
499
500    /* Now free the old data */
501    free(oldBufData);
502
503    TRACE(("...Reallocate %dx%d ->%p\n", nrow, ncol, (void *) newBufHead));
504    return move_down ? move_down : -move_up;	/* convert to rows */
505}
506
507#if OPT_WIDE_CHARS
508/*
509 * This function reallocates memory if changing the number of Buf offsets.
510 * The code is based on Reallocate().
511 */
512static void
513ReallocateBufOffsets(XtermWidget xw,
514		     ScrnBuf * sbuf,
515		     Char ** sbufaddr,
516		     unsigned nrow,
517		     unsigned ncol)
518{
519    TScreen *screen = TScreenOf(xw);
520    unsigned i;
521    ScrnBuf newBufHead;
522    Char *oldBufData;
523    ScrnBuf oldBufHead;
524
525    unsigned old_jump = scrnHeadSize(screen, 1);
526    unsigned new_jump;
527    unsigned new_ptrs = 1 + (unsigned) (screen->max_combining);
528    unsigned dstCols = ncol;
529    unsigned srcCols = ncol;
530    LineData *dstPtrs;
531    LineData *srcPtrs;
532    Char *nextPtr;
533
534    assert(nrow != 0);
535    assert(ncol != 0);
536
537    oldBufData = *sbufaddr;
538    oldBufHead = *sbuf;
539
540    /*
541     * Allocate a new LineData array, retain the old one until we've copied
542     * the data that it points to, as well as non-pointer data, e.g., bufHead.
543     *
544     * Turn on wide-chars temporarily when constructing pointers, since that is
545     * used to decide whether to address the combData[] array, which affects
546     * the length of the LineData structure.
547     */
548    screen->wide_chars = True;
549
550    new_jump = scrnHeadSize(screen, 1);
551    newBufHead = allocScrnHead(screen, nrow);
552    *sbufaddr = allocScrnData(screen, nrow, ncol);
553    setupLineData(screen, newBufHead, *sbufaddr, nrow, ncol);
554
555    screen->wide_chars = False;
556
557    nextPtr = *sbufaddr;
558
559    srcPtrs = (LineData *) oldBufHead;
560    dstPtrs = (LineData *) newBufHead;
561    for (i = 0; i < nrow; i++) {
562	dstPtrs->bufHead = srcPtrs->bufHead;
563	ExtractScrnData(attribs);
564#if OPT_ISO_COLORS
565	ExtractScrnData(color);
566#endif
567	ExtractScrnData(charData);
568
569	nextPtr += ncol * new_ptrs;
570	srcPtrs = LineDataAddr(srcPtrs, old_jump);
571	dstPtrs = LineDataAddr(dstPtrs, new_jump);
572    }
573
574    /* Now free the old data */
575    free(oldBufData);
576    free(oldBufHead);
577
578    *sbuf = newBufHead;
579
580    TRACE(("ReallocateBufOffsets %dx%d ->%p\n", nrow, ncol, *sbufaddr));
581}
582
583/*
584 * This function dynamically adds support for wide-characters.
585 */
586void
587ChangeToWide(XtermWidget xw)
588{
589    TScreen *screen = TScreenOf(xw);
590
591    if (screen->wide_chars)
592	return;
593
594    TRACE(("ChangeToWide\n"));
595    if (xtermLoadWideFonts(xw, True)) {
596	int whichBuf = screen->whichBuf;
597
598#if !OPT_FIFO_LINES || !OPT_SAVE_LINES
599	int savelines = screen->scrollWidget ? screen->savelines : 0;
600
601	if (savelines < 0)
602	    savelines = 0;
603#endif
604
605	/*
606	 * If we're displaying the alternate screen, switch the pointers back
607	 * temporarily so ReallocateBufOffsets() will operate on the proper
608	 * data in the alternate buffer.
609	 */
610	if (screen->whichBuf)
611	    SwitchBufPtrs(screen, 0);
612
613#if OPT_SAVE_LINES
614#if !OPT_FIFO_LINES
615	ReallocateBufOffsets(xw,
616			     &screen->saveBuf_index,
617			     &screen->saveBuf_data,
618			     (unsigned) savelines,
619			     (unsigned) MaxCols(screen));
620#endif
621	if (screen->editBuf_index[0]) {
622	    ReallocateBufOffsets(xw,
623				 &screen->editBuf_index[0],
624				 &screen->editBuf_data[0],
625				 (unsigned) MaxRows(screen),
626				 (unsigned) MaxCols(screen));
627	}
628#else
629	ReallocateBufOffsets(xw,
630			     &screen->saveBuf_index,
631			     &screen->saveBuf_data,
632			     (unsigned) (MaxRows(screen) + savelines),
633			     (unsigned) MaxCols(screen));
634#endif
635	if (screen->editBuf_index[1]) {
636	    ReallocateBufOffsets(xw,
637				 &screen->editBuf_index[1],
638				 &screen->editBuf_data[1],
639				 (unsigned) MaxRows(screen),
640				 (unsigned) MaxCols(screen));
641	}
642
643	screen->wide_chars = True;
644	screen->visbuf = VisBuf(screen);
645
646	/*
647	 * Switch the pointers back before we start painting on the screen.
648	 */
649	if (whichBuf)
650	    SwitchBufPtrs(screen, whichBuf);
651
652	update_font_utf8_mode();
653	SetVTFont(xw, screen->menu_font_number, True, NULL);
654    }
655    TRACE(("...ChangeToWide\n"));
656}
657#endif
658
659/*
660 * Clear cells, no side-effects.
661 */
662void
663ClearCells(XtermWidget xw, int flags, unsigned len, int row, int col)
664{
665    if (len != 0) {
666	TScreen *screen = TScreenOf(xw);
667	LineData *ld;
668	unsigned n;
669
670	ld = getLineData(screen, row);
671
672	flags = (int) ((unsigned) flags | TERM_COLOR_FLAGS(xw));
673
674	for (n = 0; n < len; ++n)
675	    ld->charData[(unsigned) col + n] = (CharData) ' ';
676
677	memset(ld->attribs + col, flags, (size_t) len);
678
679	if_OPT_ISO_COLORS(screen, {
680	    CellColor p = xtermColorPair(xw);
681	    for (n = 0; n < len; ++n) {
682		ld->color[(unsigned) col + n] = p;
683	    }
684	});
685	if_OPT_WIDE_CHARS(screen, {
686	    size_t off;
687	    for_each_combData(off, ld) {
688		memset(ld->combData[off] + col, 0, (size_t) len * sizeof(CharData));
689	    }
690	});
691    }
692}
693
694/*
695 * Clear data in the screen-structure (no I/O).
696 * Check for wide-character damage as well, clearing the damaged cells.
697 */
698void
699ScrnClearCells(XtermWidget xw, int row, int col, unsigned len)
700{
701#if OPT_WIDE_CHARS
702    TScreen *screen = TScreenOf(xw);
703#endif
704    int flags = 0;
705
706    if_OPT_WIDE_CHARS(screen, {
707	int kl;
708	int kr;
709
710	if (DamagedCells(screen, len, &kl, &kr, row, col)
711	    && kr >= kl) {
712	    ClearCells(xw, flags, (unsigned) (kr - kl + 1), row, kl);
713	}
714    });
715    ClearCells(xw, flags, len, row, col);
716}
717
718/*
719 * Disown the selection and repaint the area that is highlighted so it is no
720 * longer highlighted.
721 */
722void
723ScrnDisownSelection(XtermWidget xw)
724{
725    if (ScrnHaveSelection(TScreenOf(xw))) {
726	if (TScreenOf(xw)->keepSelection) {
727	    UnhiliteSelection(xw);
728	} else {
729	    DisownSelection(xw);
730	}
731    }
732}
733
734/*
735 * Writes str into buf at screen's current row and column.  Characters are set
736 * to match flags.
737 */
738void
739ScrnWriteText(XtermWidget xw,
740	      IChar * str,
741	      unsigned flags,
742	      unsigned cur_fg_bg,
743	      unsigned length)
744{
745    TScreen *screen = TScreenOf(xw);
746    LineData *ld;
747    Char *attrs;
748    int avail = MaxCols(screen) - screen->cur_col;
749    IChar *chars;
750#if OPT_WIDE_CHARS
751    IChar starcol1;
752#endif
753    unsigned n;
754    unsigned real_width = visual_width(str, length);
755
756    (void) cur_fg_bg;
757
758    if (real_width + (unsigned) screen->cur_col > (unsigned) MaxCols(screen)) {
759	real_width = (unsigned) (MaxCols(screen) - screen->cur_col);
760    }
761
762    if (avail <= 0)
763	return;
764    if (length > (unsigned) avail)
765	length = (unsigned) avail;
766    if (length == 0 || real_width == 0)
767	return;
768
769    ld = getLineData(screen, screen->cur_row);
770
771    chars = ld->charData + screen->cur_col;
772    attrs = ld->attribs + screen->cur_col;
773
774#if OPT_WIDE_CHARS
775    starcol1 = *chars;
776#endif
777
778    /* write blanks if we're writing invisible text */
779    for (n = 0; n < length; ++n) {
780	if ((flags & INVISIBLE))
781	    chars[n] = ' ';
782	else
783	    chars[n] = str[n];
784    }
785
786#if OPT_BLINK_TEXT
787    if ((flags & BLINK) && !(screen->blink_as_bold)) {
788	LineSetBlinked(ld);
789    }
790#endif
791
792    if_OPT_WIDE_CHARS(screen, {
793
794	if (real_width != length) {
795	    IChar *char1 = chars;
796	    if (screen->cur_col
797		&& starcol1 == HIDDEN_CHAR
798		&& isWide((int) char1[-1])) {
799		char1[-1] = (CharData) ' ';
800	    }
801	    /* if we are overwriting the right hand half of a
802	       wide character, make the other half vanish */
803	    while (length) {
804		int ch = (int) str[0];
805
806		*char1++ = *str++;
807		length--;
808
809		if (isWide(ch)) {
810		    *char1++ = (CharData) HIDDEN_CHAR;
811		}
812	    }
813
814	    if (*char1 == HIDDEN_CHAR
815		&& char1[-1] == HIDDEN_CHAR) {
816		*char1 = (CharData) ' ';
817	    }
818	    /* if we are overwriting the left hand half of a
819	       wide character, make the other half vanish */
820	} else {
821	    if (screen->cur_col
822		&& starcol1 == HIDDEN_CHAR
823		&& isWide((int) chars[-1])) {
824		chars[-1] = (CharData) ' ';
825	    }
826	    /* if we are overwriting the right hand half of a
827	       wide character, make the other half vanish */
828	    if (chars[length] == HIDDEN_CHAR
829		&& isWide((int) chars[length - 1])) {
830		chars[length] = (CharData) ' ';
831	    }
832	}
833    });
834
835    flags &= ATTRIBUTES;
836    flags |= CHARDRAWN;
837    memset(attrs, (Char) flags, (size_t) real_width);
838
839    if_OPT_WIDE_CHARS(screen, {
840	size_t off;
841	for_each_combData(off, ld) {
842	    memset(ld->combData[off] + screen->cur_col,
843		   0,
844		   real_width * sizeof(CharData));
845	}
846    });
847    if_OPT_ISO_COLORS(screen, {
848	unsigned j;
849	for (j = 0; j < real_width; ++j)
850	    ld->color[screen->cur_col + (int) j] = (CellColor) cur_fg_bg;
851    });
852
853    if_OPT_WIDE_CHARS(screen, {
854	screen->last_written_col = screen->cur_col + (int) real_width - 1;
855	screen->last_written_row = screen->cur_row;
856    });
857
858    if_OPT_XMC_GLITCH(screen, {
859	Resolve_XMC(xw);
860    });
861
862    return;
863}
864
865/*
866 * Saves pointers to the n lines beginning at sb + where, and clears the lines
867 */
868static void
869ScrnClearLines(XtermWidget xw, ScrnBuf sb, int where, unsigned n, unsigned size)
870{
871    TScreen *screen = TScreenOf(xw);
872    ScrnPtr *base;
873    unsigned jump = scrnHeadSize(screen, 1);
874    unsigned i;
875    LineData *work;
876    unsigned flags = TERM_COLOR_FLAGS(xw);
877#if OPT_ISO_COLORS
878    unsigned j;
879#endif
880
881    TRACE(("ScrnClearLines(%s:where %d, n %d, size %d)\n",
882	   (sb == screen->saveBuf_index) ? "save" : "edit",
883	   where, n, size));
884
885    assert(n != 0);
886    assert(size != 0);
887
888    /* save n lines at where */
889    SaveLineData(sb, (unsigned) where, (size_t) n);
890
891    /* clear contents of old rows */
892    base = screen->save_ptr;
893    for (i = 0; i < n; ++i) {
894	work = (LineData *) base;
895	work->bufHead = 0;
896#if OPT_DEC_CHRSET
897	SetLineDblCS(work, 0);
898#endif
899
900	memset(work->charData, 0, size * sizeof(CharData));
901	if (TERM_COLOR_FLAGS(xw)) {
902	    memset(work->attribs, (int) flags, (size_t) size);
903#if OPT_ISO_COLORS
904	    {
905		CellColor p = xtermColorPair(xw);
906		for (j = 0; j < size; ++j) {
907		    work->color[j] = p;
908		}
909	    }
910#endif
911	} else {
912	    memset(work->attribs, 0, (size_t) size);
913#if OPT_ISO_COLORS
914	    memset(work->color, 0, size * sizeof(work->color[0]));
915#endif
916	}
917#if OPT_WIDE_CHARS
918	if (screen->wide_chars) {
919	    size_t off;
920
921	    for (off = 0; off < work->combSize; ++off) {
922		memset(work->combData[off], 0, size * sizeof(CharData));
923	    }
924	}
925#endif
926	base = ScrnBufAddr(base, jump);
927    }
928}
929
930/*
931 * We're always ensured of having a visible buffer, but may not have saved
932 * lines.  Check the pointer that's sure to work.
933 */
934#if OPT_SAVE_LINES
935#define OkAllocBuf(screen) (screen->editBuf_index[0] != 0)
936#else
937#define OkAllocBuf(screen) (screen->saveBuf_index != 0)
938#endif
939
940void
941ScrnAllocBuf(XtermWidget xw)
942{
943    TScreen *screen = TScreenOf(xw);
944
945    if (!OkAllocBuf(screen)) {
946	int nrows = MaxRows(screen);
947#if !OPT_SAVE_LINES
948	int savelines = screen->scrollWidget ? screen->savelines : 0;
949#endif
950
951	TRACE(("ScrnAllocBuf %dx%d (%d)\n",
952	       nrows, MaxCols(screen), screen->savelines));
953
954#if OPT_SAVE_LINES
955	if (screen->savelines != 0) {
956#if OPT_FIFO_LINES
957	    /* for FIFO, we only need space for the index - addScrollback inits */
958	    screen->saveBuf_index = allocScrnHead(screen,
959						  (unsigned) (screen->savelines));
960#else
961	    screen->saveBuf_index = allocScrnBuf(xw,
962						 (unsigned) screen->savelines,
963						 (unsigned) MaxCols(screen),
964						 &screen->saveBuf_data);
965#endif
966	} else {
967	    screen->saveBuf_index = 0;
968	}
969	screen->editBuf_index[0] = allocScrnBuf(xw,
970						(unsigned) nrows,
971						(unsigned) MaxCols(screen),
972						&screen->editBuf_data[0]);
973#else /* !OPT_SAVE_LINES */
974	screen->saveBuf_index = allocScrnBuf(xw,
975					     (unsigned) (nrows + screen->savelines),
976					     (unsigned) (MaxCols(screen)),
977					     &screen->saveBuf_data);
978#endif /* OPT_SAVE_LINES */
979	screen->visbuf = VisBuf(screen);
980    }
981    return;
982}
983
984size_t
985ScrnPointers(TScreen * screen, size_t len)
986{
987    size_t result = scrnHeadSize(screen, (unsigned) len);
988
989    if (result > screen->save_len) {
990	if (screen->save_len)
991	    screen->save_ptr = (ScrnPtr *) realloc(screen->save_ptr, result);
992	else
993	    screen->save_ptr = (ScrnPtr *) malloc(result);
994	screen->save_len = len;
995	if (screen->save_ptr == 0)
996	    SysError(ERROR_SAVE_PTR);
997    }
998    TRACE2(("ScrnPointers %ld ->%p\n", (long) len, screen->save_ptr));
999    return result;
1000}
1001
1002/*
1003 * Inserts n blank lines at sb + where, treating last as a bottom margin.
1004 */
1005void
1006ScrnInsertLine(XtermWidget xw, ScrnBuf sb, int last, int where, unsigned n)
1007{
1008    TScreen *screen = TScreenOf(xw);
1009    unsigned size = (unsigned) MaxCols(screen);
1010
1011    TRACE(("ScrnInsertLine(last %d, where %d, n %d, size %d)\n",
1012	   last, where, n, size));
1013
1014    assert(where >= 0);
1015    assert(last >= (int) n);
1016    assert(last >= where);
1017
1018    assert(n != 0);
1019    assert(size != 0);
1020
1021    /* save n lines at bottom */
1022    ScrnClearLines(xw, sb, (last -= (int) n - 1), n, size);
1023
1024    /*
1025     * WARNING, overlapping copy operation.  Move down lines (pointers).
1026     *
1027     *   +----|---------|--------+
1028     *
1029     * is copied in the array to:
1030     *
1031     *   +--------|---------|----+
1032     */
1033    assert(last >= where);
1034    /*
1035     * This will never shift from the saveBuf to editBuf, so there is no need
1036     * to handle that case.
1037     */
1038    MoveLineData(sb,
1039		 (unsigned) (where + (int) n),
1040		 (unsigned) where,
1041		 (unsigned) (last - where));
1042
1043    /* reuse storage for new lines at where */
1044    RestoreLineData(sb, (unsigned) where, n);
1045}
1046
1047/*
1048 * Deletes n lines at sb + where, treating last as a bottom margin.
1049 */
1050void
1051ScrnDeleteLine(XtermWidget xw, ScrnBuf sb, int last, int where, unsigned n)
1052{
1053    TScreen *screen = TScreenOf(xw);
1054    unsigned size = (unsigned) MaxCols(screen);
1055
1056    TRACE(("ScrnDeleteLine(%s:last %d, where %d, n %d, size %d)\n",
1057	   (sb == screen->saveBuf_index) ? "save" : "edit",
1058	   last, where, n, size));
1059
1060    assert(where >= 0);
1061    assert(last >= where + (int) n - 1);
1062
1063    assert(n != 0);
1064    assert(size != 0);
1065
1066    /* move up lines */
1067    last -= ((int) n - 1);
1068#if OPT_SAVE_LINES
1069    if (inSaveBuf(screen, sb, where)) {
1070#if !OPT_FIFO_LINES
1071	int from = where + n;
1072#endif
1073
1074	/* we shouldn't be editing the saveBuf, only scroll into it */
1075	assert(last >= screen->savelines);
1076
1077	if (sb != 0) {
1078#if OPT_FIFO_LINES
1079	    /* copy lines from editBuf to saveBuf (allocating as we go...) */
1080	    saveEditBufLines(screen, sb, n);
1081#else
1082	    ScrnClearLines(xw, sb, where, n, size);
1083
1084	    /* move the pointers within saveBuf */
1085	    TRACE(("...%smoving pointers in saveBuf (compare %d %d)\n",
1086		   ((screen->savelines > from)
1087		    ? ""
1088		    : "SKIP "),
1089		   screen->savelines,
1090		   from));
1091	    if (screen->savelines > from) {
1092		MoveLineData(sb,
1093			     (unsigned) where,
1094			     (unsigned) from,
1095			     (unsigned) (screen->savelines - from));
1096	    }
1097
1098	    /* reuse storage in saveBuf */
1099	    TRACE(("...reuse %d lines storage in saveBuf\n", n));
1100	    RestoreLineData(sb, (unsigned) screen->savelines - n, n);
1101
1102	    /* copy lines from editBuf to saveBuf (into the reused storage) */
1103	    saveEditBufLines(screen, sb, n);
1104#endif
1105	}
1106
1107	/* adjust variables to fall-thru into changes only to editBuf */
1108	TRACE(("...adjusting variables, to work on editBuf alone\n"));
1109	last -= screen->savelines;
1110	where = 0;
1111	sb = screen->visbuf;
1112    }
1113#endif
1114    /*
1115     * Scroll the visible buffer (editBuf).
1116     */
1117    ScrnClearLines(xw, sb, where, n, size);
1118
1119    MoveLineData(sb,
1120		 (unsigned) where,
1121		 (unsigned) (where + (int) n),
1122		 (size_t) (last - where));
1123
1124    /* reuse storage for new bottom lines */
1125    RestoreLineData(sb, (unsigned) last, n);
1126}
1127
1128/*
1129 * Inserts n blanks in screen at current row, col.  Size is the size of each
1130 * row.
1131 */
1132void
1133ScrnInsertChar(XtermWidget xw, unsigned n)
1134{
1135#define MemMove(data) \
1136    	for (j = last - 1; j >= (col + (int) n); --j) \
1137	    data[j] = data[j - (int) n]
1138
1139    TScreen *screen = TScreenOf(xw);
1140    int last = MaxCols(screen);
1141    int row = screen->cur_row;
1142    int col = screen->cur_col;
1143    int j;
1144    LineData *ld;
1145
1146    if (last <= (col + (int) n)) {
1147	if (last <= col)
1148	    return;
1149	n = (unsigned) (last - col);
1150    }
1151
1152    assert(screen->cur_col >= 0);
1153    assert(screen->cur_row >= 0);
1154    assert(n > 0);
1155    assert(last > (int) n);
1156
1157    if_OPT_WIDE_CHARS(screen, {
1158	int xx = screen->cur_row;
1159	int kl;
1160	int kr = screen->cur_col;
1161	if (DamagedCells(screen, n, &kl, (int *) 0, xx, kr) && kr > kl) {
1162	    ClearCells(xw, 0, (unsigned) (kr - kl + 1), row, kl);
1163	}
1164	kr = screen->max_col - (int) n + 1;
1165	if (DamagedCells(screen, n, &kl, (int *) 0, xx, kr) && kr > kl) {
1166	    ClearCells(xw, 0, (unsigned) (kr - kl + 1), row, kl);
1167	}
1168    });
1169
1170    if ((ld = getLineData(screen, row)) != 0) {
1171	MemMove(ld->charData);
1172	MemMove(ld->attribs);
1173
1174	if_OPT_ISO_COLORS(screen, {
1175	    MemMove(ld->color);
1176	});
1177	if_OPT_WIDE_CHARS(screen, {
1178	    size_t off;
1179	    for_each_combData(off, ld) {
1180		MemMove(ld->combData[off]);
1181	    }
1182	});
1183    }
1184    ClearCells(xw, CHARDRAWN, n, row, col);
1185
1186#undef MemMove
1187}
1188
1189/*
1190 * Deletes n characters at current row, col.
1191 */
1192void
1193ScrnDeleteChar(XtermWidget xw, unsigned n)
1194{
1195#define MemMove(data) \
1196    	for (j = col; j < last - (int) n; ++j) \
1197	    data[j] = data[j + (int) n]
1198
1199    TScreen *screen = TScreenOf(xw);
1200    int last = MaxCols(screen);
1201    int row = screen->cur_row;
1202    int col = screen->cur_col;
1203    int j;
1204    LineData *ld;
1205
1206    if (last <= (col + (int) n)) {
1207	if (last <= col)
1208	    return;
1209	n = (unsigned) (last - col);
1210    }
1211
1212    assert(screen->cur_col >= 0);
1213    assert(screen->cur_row >= 0);
1214    assert(n > 0);
1215    assert(last > (int) n);
1216
1217    if_OPT_WIDE_CHARS(screen, {
1218	int kl;
1219	int kr;
1220	if (DamagedCells(screen, n, &kl, &kr,
1221			 screen->cur_row,
1222			 screen->cur_col))
1223	    ClearCells(xw, 0, (unsigned) (kr - kl + 1), row, kl);
1224    });
1225
1226    if ((ld = getLineData(screen, row)) != 0) {
1227	MemMove(ld->charData);
1228	MemMove(ld->attribs);
1229
1230	if_OPT_ISO_COLORS(screen, {
1231	    MemMove(ld->color);
1232	});
1233	if_OPT_WIDE_CHARS(screen, {
1234	    size_t off;
1235	    for_each_combData(off, ld) {
1236		MemMove(ld->combData[off]);
1237	    }
1238	});
1239	LineClrWrapped(ld);
1240	if (screen->show_wrap_marks) {
1241	    ShowWrapMarks(xw, row, ld);
1242	}
1243    }
1244    ClearCells(xw, 0, n, row, (last - (int) n));
1245
1246#undef MemMove
1247}
1248
1249/*
1250 * This is useful for debugging both xterm and applications that may manipulate
1251 * its line-wrapping state.
1252 */
1253void
1254ShowWrapMarks(XtermWidget xw, int row, LineData * ld)
1255{
1256    TScreen *screen = TScreenOf(xw);
1257    Boolean set = LineTstWrapped(ld);
1258    CgsEnum cgsId = set ? gcVTcursFilled : gcVTcursReverse;
1259    VTwin *currentWin = WhichVWin(screen);
1260    int y = row * FontHeight(screen) + screen->border;
1261    int x = LineCursorX(screen, ld, screen->max_col + 1);
1262
1263    TRACE2(("ShowWrapMarks %d:%s\n", row, BtoS(set)));
1264
1265    XFillRectangle(screen->display, VWindow(screen),
1266		   getCgsGC(xw, currentWin, cgsId),
1267		   x, y,
1268		   (unsigned) screen->border,
1269		   (unsigned) FontHeight(screen));
1270}
1271
1272/*
1273 * Repaints the area enclosed by the parameters.
1274 * Requires: (toprow, leftcol), (toprow + nrows, leftcol + ncols) are
1275 * 	     coordinates of characters in screen;
1276 *	     nrows and ncols positive.
1277 *	     all dimensions are based on single-characters.
1278 */
1279void
1280ScrnRefresh(XtermWidget xw,
1281	    int toprow,
1282	    int leftcol,
1283	    int nrows,
1284	    int ncols,
1285	    Bool force)		/* ... leading/trailing spaces */
1286{
1287    TScreen *screen = TScreenOf(xw);
1288    LineData *ld;
1289    int y = toprow * FontHeight(screen) + screen->border;
1290    int row;
1291    int maxrow = toprow + nrows - 1;
1292    int scrollamt = screen->scroll_amt;
1293    unsigned gc_changes = 0;
1294#ifdef __CYGWIN__
1295    static char first_time = 1;
1296#endif
1297    static int recurse = 0;
1298
1299    TRACE(("ScrnRefresh top %d (%d,%d) - (%d,%d)%s {{\n",
1300	   screen->topline, toprow, leftcol,
1301	   nrows, ncols,
1302	   force ? " force" : ""));
1303
1304    if (screen->cursorp.col >= leftcol
1305	&& screen->cursorp.col <= (leftcol + ncols - 1)
1306	&& screen->cursorp.row >= ROW2INX(screen, toprow)
1307	&& screen->cursorp.row <= ROW2INX(screen, maxrow))
1308	screen->cursor_state = OFF;
1309
1310    for (row = toprow; row <= maxrow; y += FontHeight(screen), row++) {
1311#if OPT_ISO_COLORS
1312	CellColor *fb = 0;
1313#define ColorOf(col) (CellColor) (fb ? fb[col] : 0)
1314#endif
1315#if OPT_WIDE_CHARS
1316	int wideness = 0;
1317#endif
1318#define BLANK_CEL(cell) (chars[cell] == ' ')
1319	IChar *chars;
1320	Char *attrs;
1321	int col = leftcol;
1322	int maxcol = leftcol + ncols - 1;
1323	int hi_col = maxcol;
1324	int lastind;
1325	unsigned flags;
1326	unsigned test;
1327	CellColor fg_bg = 0;
1328	unsigned fg = 0, bg = 0;
1329	int x;
1330	GC gc;
1331	Bool hilite;
1332
1333	(void) fg;
1334	(void) bg;
1335#if !OPT_ISO_COLORS
1336	fg_bg = 0;
1337#endif
1338
1339	if (row < screen->top_marg || row > screen->bot_marg)
1340	    lastind = row;
1341	else
1342	    lastind = row - scrollamt;
1343
1344	if (lastind < 0 || lastind > screen->max_row)
1345	    continue;
1346
1347	TRACE2(("ScrnRefresh row=%d lastind=%d ->%d\n",
1348		row, lastind, ROW2INX(screen, lastind)));
1349
1350	if ((ld = getLineData(screen, ROW2INX(screen, lastind))) == 0
1351	    || ld->charData == 0
1352	    || ld->attribs == 0) {
1353	    break;
1354	}
1355
1356	if (screen->show_wrap_marks) {
1357	    ShowWrapMarks(xw, lastind, ld);
1358	}
1359
1360	if (maxcol >= (int) ld->lineSize) {
1361	    maxcol = ld->lineSize - 1;
1362	    hi_col = maxcol;
1363	}
1364
1365	chars = ld->charData;
1366	attrs = ld->attribs;
1367
1368	if_OPT_WIDE_CHARS(screen, {
1369	    /* This fixes an infinite recursion bug, that leads
1370	       to display anomalies. It seems to be related to
1371	       problems with the selection. */
1372	    if (recurse < 3) {
1373		/* adjust to redraw all of a widechar if we just wanted
1374		   to draw the right hand half */
1375		if (leftcol > 0 &&
1376		    chars[leftcol] == HIDDEN_CHAR &&
1377		    isWide((int) chars[leftcol - 1])) {
1378		    leftcol--;
1379		    ncols++;
1380		    col = leftcol;
1381		}
1382	    } else {
1383		fprintf(stderr, "This should not happen. Why is it so?\n");
1384	    }
1385	});
1386
1387	if (row < screen->startH.row || row > screen->endH.row ||
1388	    (row == screen->startH.row && maxcol < screen->startH.col) ||
1389	    (row == screen->endH.row && col >= screen->endH.col)) {
1390#if OPT_DEC_CHRSET
1391	    /*
1392	     * Temporarily change dimensions to double-sized characters so
1393	     * we can reuse the recursion on this function.
1394	     */
1395	    if (CSET_DOUBLE(GetLineDblCS(ld))) {
1396		col /= 2;
1397		maxcol /= 2;
1398	    }
1399#endif
1400	    /*
1401	     * If row does not intersect selection; don't hilite blanks.
1402	     */
1403	    if (!force) {
1404		while (col <= maxcol && (attrs[col] & ~BOLD) == 0 &&
1405		       BLANK_CEL(col))
1406		    col++;
1407
1408		while (col <= maxcol && (attrs[maxcol] & ~BOLD) == 0 &&
1409		       BLANK_CEL(maxcol))
1410		    maxcol--;
1411	    }
1412#if OPT_DEC_CHRSET
1413	    if (CSET_DOUBLE(GetLineDblCS(ld))) {
1414		col *= 2;
1415		maxcol *= 2;
1416	    }
1417#endif
1418	    hilite = False;
1419	} else {
1420	    /* row intersects selection; split into pieces of single type */
1421	    if (row == screen->startH.row && col < screen->startH.col) {
1422		recurse++;
1423		ScrnRefresh(xw, row, col, 1, screen->startH.col - col,
1424			    force);
1425		col = screen->startH.col;
1426	    }
1427	    if (row == screen->endH.row && maxcol >= screen->endH.col) {
1428		recurse++;
1429		ScrnRefresh(xw, row, screen->endH.col, 1,
1430			    maxcol - screen->endH.col + 1, force);
1431		maxcol = screen->endH.col - 1;
1432	    }
1433
1434	    /*
1435	     * If we're highlighting because the user is doing cut/paste,
1436	     * trim the trailing blanks from the highlighted region so we're
1437	     * showing the actual extent of the text that'll be cut.  If
1438	     * we're selecting a blank line, we'll highlight one column
1439	     * anyway.
1440	     *
1441	     * We don't do this if the mouse-hilite mode is set because that
1442	     * would be too confusing.
1443	     *
1444	     * The default if the highlightSelection resource isn't set will
1445	     * highlight the whole width of the terminal, which is easy to
1446	     * see, but harder to use (because trailing blanks aren't as
1447	     * apparent).
1448	     */
1449	    if (screen->highlight_selection
1450		&& screen->send_mouse_pos != VT200_HIGHLIGHT_MOUSE) {
1451		hi_col = screen->max_col;
1452		while (hi_col > 0 && !(attrs[hi_col] & CHARDRAWN))
1453		    hi_col--;
1454	    }
1455
1456	    /* remaining piece should be hilited */
1457	    hilite = True;
1458	}
1459
1460	if (col > maxcol)
1461	    continue;
1462
1463	/*
1464	 * Go back to double-sized character dimensions if the line has
1465	 * double-width characters.  Note that 'hi_col' is already in the
1466	 * right units.
1467	 */
1468	if_OPT_DEC_CHRSET({
1469	    if (CSET_DOUBLE(GetLineDblCS(ld))) {
1470		col /= 2;
1471		maxcol /= 2;
1472	    }
1473	});
1474
1475	flags = attrs[col];
1476
1477	if_OPT_WIDE_CHARS(screen, {
1478	    wideness = isWide((int) chars[col]);
1479	});
1480
1481	if_OPT_ISO_COLORS(screen, {
1482	    fb = ld->color;
1483	    fg_bg = ColorOf(col);
1484	    fg = extract_fg(xw, fg_bg, flags);
1485	    bg = extract_bg(xw, fg_bg, flags);
1486	});
1487
1488	gc = updatedXtermGC(xw, flags, fg_bg, hilite);
1489	gc_changes |= (flags & (FG_COLOR | BG_COLOR));
1490
1491	x = LineCursorX(screen, ld, col);
1492	lastind = col;
1493
1494	for (; col <= maxcol; col++) {
1495	    if ((attrs[col] != flags)
1496		|| (hilite && (col > hi_col))
1497#if OPT_ISO_COLORS
1498		|| ((flags & FG_COLOR)
1499		    && (extract_fg(xw, ColorOf(col), attrs[col]) != fg))
1500		|| ((flags & BG_COLOR)
1501		    && (extract_bg(xw, ColorOf(col), attrs[col]) != bg))
1502#endif
1503#if OPT_WIDE_CHARS
1504		|| (isWide((int) chars[col]) != wideness
1505		    && chars[col] != HIDDEN_CHAR)
1506#endif
1507		) {
1508		assert(col >= lastind);
1509		TRACE(("ScrnRefresh looping drawXtermText %d..%d:%s\n",
1510		       lastind, col,
1511		       visibleIChars((&chars[lastind]),
1512				     (unsigned) (col - lastind))));
1513
1514		test = flags;
1515		checkVeryBoldColors(test, fg);
1516
1517		x = drawXtermText(xw, test & DRAWX_MASK, gc, x, y,
1518				  GetLineDblCS(ld),
1519				  &chars[lastind],
1520				  (unsigned) (col - lastind), 0);
1521
1522		if_OPT_WIDE_CHARS(screen, {
1523		    int i;
1524		    size_t off;
1525
1526		    for_each_combData(off, ld) {
1527			IChar *com_off = ld->combData[off];
1528
1529			for (i = lastind; i < col; i++) {
1530			    int my_x = LineCursorX(screen, ld, i);
1531			    IChar base = chars[i];
1532
1533			    if (isWide((int) base))
1534				my_x = LineCursorX(screen, ld, i - 1);
1535
1536			    if (com_off[i] != 0)
1537				drawXtermText(xw,
1538					      (test & DRAWX_MASK)
1539					      | NOBACKGROUND,
1540					      gc, my_x, y,
1541					      GetLineDblCS(ld),
1542					      com_off + i,
1543					      1, isWide((int) base));
1544			}
1545		    }
1546		});
1547
1548		resetXtermGC(xw, flags, hilite);
1549
1550		lastind = col;
1551
1552		if (hilite && (col > hi_col))
1553		    hilite = False;
1554
1555		flags = attrs[col];
1556		if_OPT_ISO_COLORS(screen, {
1557		    fg_bg = ColorOf(col);
1558		    fg = extract_fg(xw, fg_bg, flags);
1559		    bg = extract_bg(xw, fg_bg, flags);
1560		});
1561		if_OPT_WIDE_CHARS(screen, {
1562		    wideness = isWide((int) chars[col]);
1563		});
1564
1565		gc = updatedXtermGC(xw, flags, fg_bg, hilite);
1566		gc_changes |= (flags & (FG_COLOR | BG_COLOR));
1567	    }
1568
1569	    if (chars[col] == 0) {
1570		chars[col] = ' ';
1571	    }
1572	}
1573
1574	assert(col >= lastind);
1575	TRACE(("ScrnRefresh calling drawXtermText %d..%d:%s\n",
1576	       lastind, col,
1577	       visibleIChars(&chars[lastind], (unsigned) (col - lastind))));
1578
1579	test = flags;
1580	checkVeryBoldColors(test, fg);
1581
1582	drawXtermText(xw, test & DRAWX_MASK, gc, x, y,
1583		      GetLineDblCS(ld),
1584		      &chars[lastind],
1585		      (unsigned) (col - lastind), 0);
1586
1587	if_OPT_WIDE_CHARS(screen, {
1588	    int i;
1589	    size_t off;
1590
1591	    for_each_combData(off, ld) {
1592		IChar *com_off = ld->combData[off];
1593
1594		for (i = lastind; i < col; i++) {
1595		    int my_x = LineCursorX(screen, ld, i);
1596		    int base = (int) chars[i];
1597
1598		    if (isWide(base))
1599			my_x = LineCursorX(screen, ld, i - 1);
1600
1601		    if (com_off[i] != 0)
1602			drawXtermText(xw,
1603				      (test & DRAWX_MASK)
1604				      | NOBACKGROUND,
1605				      gc, my_x, y,
1606				      GetLineDblCS(ld),
1607				      com_off + i,
1608				      1, isWide(base));
1609		}
1610	    }
1611	});
1612
1613	resetXtermGC(xw, flags, hilite);
1614    }
1615
1616    /*
1617     * If we're in color mode, reset the various GC's to the current
1618     * screen foreground and background so that other functions (e.g.,
1619     * ClearRight) will get the correct colors.
1620     */
1621    if_OPT_ISO_COLORS(screen, {
1622	if (gc_changes & FG_COLOR)
1623	    SGR_Foreground(xw, xw->cur_foreground);
1624	if (gc_changes & BG_COLOR)
1625	    SGR_Background(xw, xw->cur_background);
1626    });
1627
1628#if defined(__CYGWIN__) && defined(TIOCSWINSZ)
1629    if (first_time == 1) {
1630	TTYSIZE_STRUCT ts;
1631
1632	first_time = 0;
1633	TTYSIZE_ROWS(ts) = nrows;
1634	TTYSIZE_COLS(ts) = ncols;
1635	ts.ws_xpixel = xw->core.width;
1636	ts.ws_ypixel = xw->core.height;
1637	SET_TTYSIZE(screen->respond, ts);
1638    }
1639#endif
1640    recurse--;
1641
1642    TRACE(("...}} ScrnRefresh\n"));
1643    return;
1644}
1645
1646/*
1647 * Call this wrapper to ScrnRefresh() when the data has changed.  If the
1648 * refresh region overlaps the selection, we will release the primary selection.
1649 */
1650void
1651ScrnUpdate(XtermWidget xw,
1652	   int toprow,
1653	   int leftcol,
1654	   int nrows,
1655	   int ncols,
1656	   Bool force)		/* ... leading/trailing spaces */
1657{
1658    TScreen *screen = TScreenOf(xw);
1659
1660    if (ScrnHaveSelection(screen)
1661	&& (toprow <= screen->endH.row)
1662	&& (toprow + nrows - 1 >= screen->startH.row)) {
1663	ScrnDisownSelection(xw);
1664    }
1665    ScrnRefresh(xw, toprow, leftcol, nrows, ncols, force);
1666}
1667
1668/*
1669 * Sets the rows first though last of the buffer of screen to spaces.
1670 * Requires first <= last; first, last are rows of screen->buf.
1671 */
1672void
1673ClearBufRows(XtermWidget xw,
1674	     int first,
1675	     int last)
1676{
1677    TScreen *screen = TScreenOf(xw);
1678    unsigned len = (unsigned) MaxCols(screen);
1679    int row;
1680
1681    TRACE(("ClearBufRows %d..%d\n", first, last));
1682    for (row = first; row <= last; row++) {
1683	LineData *ld = getLineData(screen, row);
1684	if (ld != 0) {
1685	    if_OPT_DEC_CHRSET({
1686		/* clearing the whole row resets the doublesize characters */
1687		SetLineDblCS(ld, CSET_SWL);
1688	    });
1689	    LineClrWrapped(ld);
1690	    if (screen->show_wrap_marks) {
1691		ShowWrapMarks(xw, row, ld);
1692	    }
1693	    ClearCells(xw, 0, len, row, 0);
1694	}
1695    }
1696}
1697
1698/*
1699  Resizes screen:
1700  1. If new window would have fractional characters, sets window size so as to
1701  discard fractional characters and returns -1.
1702  Minimum screen size is 1 X 1.
1703  Note that this causes another ExposeWindow event.
1704  2. Enlarges screen->buf if necessary.  New space is appended to the bottom
1705  and to the right
1706  3. Reduces  screen->buf if necessary.  Old space is removed from the bottom
1707  and from the right
1708  4. Cursor is positioned as closely to its former position as possible
1709  5. Sets screen->max_row and screen->max_col to reflect new size
1710  6. Maintains the inner border (and clears the border on the screen).
1711  7. Clears origin mode and sets scrolling region to be entire screen.
1712  8. Returns 0
1713  */
1714int
1715ScreenResize(XtermWidget xw,
1716	     int width,
1717	     int height,
1718	     unsigned *flags)
1719{
1720    TScreen *screen = TScreenOf(xw);
1721    int code, rows, cols;
1722    int border = 2 * screen->border;
1723    int move_down_by = 0;
1724#ifdef TTYSIZE_STRUCT
1725    TTYSIZE_STRUCT ts;
1726#endif
1727    Window tw = VWindow(screen);
1728
1729    TRACE(("ScreenResize %dx%d border %d font %dx%d\n",
1730	   height, width, border,
1731	   FontHeight(screen), FontWidth(screen)));
1732
1733    assert(width > 0);
1734    assert(height > 0);
1735
1736    if (screen->is_running) {
1737	/* clear the right and bottom internal border because of NorthWest
1738	   gravity might have left junk on the right and bottom edges */
1739	if (width >= (int) FullWidth(screen)) {
1740	    XClearArea(screen->display, tw,
1741		       FullWidth(screen), 0,	/* right edge */
1742		       0, (unsigned) height,	/* from top to bottom */
1743		       False);
1744	}
1745	if (height >= (int) FullHeight(screen)) {
1746	    XClearArea(screen->display, tw,
1747		       0, FullHeight(screen),	/* bottom */
1748		       (unsigned) width, 0,	/* all across the bottom */
1749		       False);
1750	}
1751    }
1752
1753    TRACE(("...computing rows/cols: %.2f %.2f\n",
1754	   (double) (height - border) / FontHeight(screen),
1755	   (double) (width - border - ScrollbarWidth(screen)) / FontWidth(screen)));
1756
1757    rows = (height - border) / FontHeight(screen);
1758    cols = (width - border - ScrollbarWidth(screen)) / FontWidth(screen);
1759    if (rows < 1)
1760	rows = 1;
1761    if (cols < 1)
1762	cols = 1;
1763
1764    /* update buffers if the screen has changed size */
1765    if (MaxRows(screen) != rows || MaxCols(screen) != cols) {
1766	int whichBuf = 0;
1767	int delta_rows = rows - MaxRows(screen);
1768#if OPT_TRACE
1769	int delta_cols = cols - MaxCols(screen);
1770#endif
1771
1772	TRACE(("...ScreenResize chars %dx%d delta %dx%d\n",
1773	       rows, cols, delta_rows, delta_cols));
1774
1775	if (screen->is_running) {
1776#if !OPT_FIFO_LINES
1777	    int savelines = (screen->scrollWidget
1778			     ? screen->savelines
1779			     : 0);
1780#endif
1781	    if (screen->cursor_state)
1782		HideCursor();
1783#if OPT_SAVE_LINES
1784	    /*
1785	     * The non-visible buffer is simple, since we will not copy data
1786	     * to/from the saved-lines.  Do that first.
1787	     */
1788	    if (screen->editBuf_index[!screen->whichBuf]) {
1789		(void) Reallocate(xw,
1790				  &screen->editBuf_index[!screen->whichBuf],
1791				  &screen->editBuf_data[!screen->whichBuf],
1792				  (unsigned) rows,
1793				  (unsigned) cols,
1794				  (unsigned) MaxRows(screen));
1795	    }
1796
1797	    /*
1798	     * The save-lines buffer may change width, but will not change its
1799	     * height.  Deal with the cases where we copy data to/from the
1800	     * saved-lines buffer.
1801	     */
1802	    if (GravityIsSouthWest(xw)
1803		&& delta_rows
1804		&& screen->saveBuf_index != 0) {
1805
1806		if (delta_rows < 0) {
1807		    unsigned move_up = (unsigned) (-delta_rows);
1808		    ScrnBuf dst = screen->saveBuf_index;
1809
1810#if OPT_FIFO_LINES
1811		    int amount = ((MaxRows(screen) - (int) move_up - 1)
1812				  - screen->cur_row);
1813
1814		    if (amount < 0) {
1815			/* move line-data from visible-buffer to save-buffer */
1816			saveEditBufLines(screen, dst, (unsigned) -amount);
1817			move_down_by = amount;
1818		    } else {
1819			move_down_by = 0;
1820		    }
1821#else /* !OPT_FIFO_LINES */
1822		    int amount = screen->savelines - (int) move_up;
1823
1824		    TRACE_SCRNBUF("before save", screen, dst, screen->savelines);
1825
1826		    /* shift lines in save-buffer to make room */
1827		    TRACE(("...%smoving pointers in saveBuf (compare %d %d)\n",
1828			   (amount > 0
1829			    ? ""
1830			    : "SKIP "),
1831			   screen->savelines,
1832			   move_up));
1833		    if (amount > 0) {
1834			SaveLineData(dst, 0, move_up);
1835
1836			MoveLineData(dst,
1837				     0,
1838				     move_up,
1839				     (unsigned) amount);
1840
1841			TRACE(("...reuse %d lines storage in saveBuf\n", move_up));
1842			RestoreLineData(dst,
1843					(unsigned) amount,
1844					move_up);
1845			TRACE_SCRNBUF("restoresave", screen, dst, screen->savelines);
1846		    }
1847
1848		    /* copy line-data from visible-buffer to save-buffer */
1849		    saveEditBufLines(screen, dst, move_up);
1850
1851		    /* after data is copied, reallocate saved-lines */
1852		    (void) Reallocate(xw,
1853				      &screen->saveBuf_index,
1854				      &screen->saveBuf_data,
1855				      (unsigned) savelines,
1856				      (unsigned) cols,
1857				      (unsigned) savelines);
1858		    TRACE_SCRNBUF("reallocSAVE",
1859				  screen,
1860				  screen->saveBuf_index,
1861				  savelines);
1862#endif /* OPT_FIFO_LINES */
1863
1864		    /* decrease size of visible-buffer */
1865		    (void) Reallocate(xw,
1866				      &screen->editBuf_index[screen->whichBuf],
1867				      &screen->editBuf_data[screen->whichBuf],
1868				      (unsigned) rows,
1869				      (unsigned) cols,
1870				      (unsigned) MaxRows(screen));
1871		    TRACE_SCRNBUF("reallocEDIT",
1872				  screen,
1873				  screen->editBuf_index[screen->whichBuf],
1874				  rows);
1875		} else {
1876		    unsigned move_down = (unsigned) delta_rows;
1877#if OPT_FIFO_LINES
1878		    long unsave_fifo;
1879#else
1880		    ScrnBuf src = screen->saveBuf_index;
1881#endif
1882		    ScrnBuf dst;
1883		    int amount;
1884
1885		    if ((int) move_down > screen->savedlines) {
1886			move_down = (unsigned) screen->savedlines;
1887		    }
1888		    move_down_by = (int) move_down;
1889		    amount = rows - (int) move_down;
1890
1891		    /* increase size of visible-buffer */
1892		    (void) Reallocate(xw,
1893				      &screen->editBuf_index[screen->whichBuf],
1894				      &screen->editBuf_data[screen->whichBuf],
1895				      (unsigned) rows,
1896				      (unsigned) cols,
1897				      (unsigned) MaxRows(screen));
1898
1899		    dst = screen->editBuf_index[screen->whichBuf];
1900		    TRACE_SCRNBUF("reallocEDIT", screen, dst, rows);
1901
1902		    TRACE(("...%smoving pointers in editBuf (compare %d %d)\n",
1903			   (amount > 0
1904			    ? ""
1905			    : "SKIP "),
1906			   rows,
1907			   move_down));
1908		    if (amount > 0) {
1909			/* shift lines in visible-buffer to make room */
1910			SaveLineData(dst, (unsigned) amount, (size_t) move_down);
1911
1912			MoveLineData(dst,
1913				     move_down,
1914				     0,
1915				     (unsigned) amount);
1916
1917			TRACE(("...reuse %d lines storage in editBuf\n", move_down));
1918			RestoreLineData(dst,
1919					0,
1920					move_down);
1921
1922			TRACE_SCRNBUF("shifted", screen, dst, rows);
1923		    }
1924
1925		    /* copy line-data from save-buffer to visible-buffer */
1926		    unsaveEditBufLines(screen, dst, move_down);
1927		    TRACE_SCRNBUF("copied", screen, dst, rows);
1928
1929#if OPT_FIFO_LINES
1930		    unsave_fifo = (long) move_down;
1931		    if (screen->saved_fifo < (int) unsave_fifo)
1932			unsave_fifo = screen->saved_fifo;
1933
1934		    /* free up storage in fifo from the copied lines */
1935		    while (unsave_fifo-- > 0) {
1936			deleteScrollback(screen, -1);
1937			screen->saved_fifo--;
1938		    }
1939#else
1940		    amount = (screen->savelines - (int) move_down);
1941		    TRACE(("...%smoving pointers in saveBuf (compare %d %d)\n",
1942			   (amount > 0
1943			    ? ""
1944			    : "SKIP "),
1945			   rows,
1946			   move_down));
1947		    if (amount > 0) {
1948			/* shift lines in save-buffer to account for copy */
1949			src = screen->saveBuf_index;
1950			SaveLineData(src, amount, move_down);
1951
1952			MoveLineData(src,
1953				     move_down,
1954				     0,
1955				     (unsigned) amount);
1956
1957			TRACE(("...reuse %d lines storage in saveBuf\n", move_down));
1958			RestoreLineData(src,
1959					0,
1960					move_down);
1961		    }
1962#endif
1963
1964		    /* recover storage in save-buffer */
1965		}
1966	    } else {
1967#if !OPT_FIFO_LINES
1968		(void) Reallocate(xw,
1969				  &screen->saveBuf_index,
1970				  &screen->saveBuf_data,
1971				  (unsigned) savelines,
1972				  (unsigned) cols,
1973				  (unsigned) savelines);
1974#endif
1975		(void) Reallocate(xw,
1976				  &screen->editBuf_index[screen->whichBuf],
1977				  &screen->editBuf_data[screen->whichBuf],
1978				  (unsigned) rows,
1979				  (unsigned) cols,
1980				  (unsigned) MaxRows(screen));
1981	    }
1982#else /* !OPT_SAVE_LINES */
1983	    if (screen->whichBuf
1984		&& GravityIsSouthWest(xw)) {
1985		/* swap buffer pointers back to make this work */
1986		whichBuf = screen->whichBuf;
1987		SwitchBufPtrs(screen, 0);
1988	    } else {
1989		whichBuf = 0;
1990	    }
1991	    if (screen->editBuf_index[1])
1992		(void) Reallocate(xw,
1993				  &screen->editBuf_index[1],
1994				  &screen->editBuf_data[1],
1995				  (unsigned) rows,
1996				  (unsigned) cols,
1997				  (unsigned) MaxRows(screen));
1998	    move_down_by = Reallocate(xw,
1999				      &screen->saveBuf_index,
2000				      &screen->saveBuf_data,
2001				      (unsigned) (rows + savelines),
2002				      (unsigned) cols,
2003				      (unsigned) (MaxRows(screen) + savelines));
2004#endif /* OPT_SAVE_LINES */
2005	    screen->visbuf = VisBuf(screen);
2006	}
2007
2008	AdjustSavedCursor(xw, move_down_by);
2009	set_max_row(screen, screen->max_row + delta_rows);
2010	set_max_col(screen, cols - 1);
2011
2012	if (screen->is_running) {
2013	    if (GravityIsSouthWest(xw)) {
2014		screen->savedlines -= move_down_by;
2015		if (screen->savedlines < 0)
2016		    screen->savedlines = 0;
2017		if (screen->savedlines > screen->savelines)
2018		    screen->savedlines = screen->savelines;
2019		if (screen->topline < -screen->savedlines)
2020		    screen->topline = -screen->savedlines;
2021		set_cur_row(screen, screen->cur_row + move_down_by);
2022		screen->cursorp.row += move_down_by;
2023		ScrollSelection(screen, move_down_by, True);
2024
2025		if (whichBuf)
2026		    SwitchBufPtrs(screen, whichBuf);	/* put the pointers back */
2027	    }
2028	}
2029
2030	/* adjust scrolling region */
2031	set_tb_margins(screen, 0, screen->max_row);
2032	UIntClr(*flags, ORIGIN);
2033
2034	if (screen->cur_row > screen->max_row)
2035	    set_cur_row(screen, screen->max_row);
2036	if (screen->cur_col > screen->max_col)
2037	    set_cur_col(screen, screen->max_col);
2038
2039	screen->fullVwin.height = height - border;
2040	screen->fullVwin.width = width - border - screen->fullVwin.sb_info.width;
2041
2042    } else if (FullHeight(screen) == height && FullWidth(screen) == width)
2043	return (0);		/* nothing has changed at all */
2044
2045    screen->fullVwin.fullheight = (Dimension) height;
2046    screen->fullVwin.fullwidth = (Dimension) width;
2047
2048    ResizeScrollBar(xw);
2049    ResizeSelection(screen, rows, cols);
2050
2051#ifndef NO_ACTIVE_ICON
2052    if (screen->iconVwin.window) {
2053	XWindowChanges changes;
2054	screen->iconVwin.width =
2055	    MaxCols(screen) * screen->iconVwin.f_width;
2056
2057	screen->iconVwin.height =
2058	    MaxRows(screen) * screen->iconVwin.f_height;
2059
2060	changes.width = screen->iconVwin.fullwidth =
2061	    (Dimension) ((unsigned) screen->iconVwin.width
2062			 + 2 * xw->misc.icon_border_width);
2063
2064	changes.height = screen->iconVwin.fullheight =
2065	    (Dimension) ((unsigned) screen->iconVwin.height
2066			 + 2 * xw->misc.icon_border_width);
2067
2068	changes.border_width = (int) xw->misc.icon_border_width;
2069
2070	TRACE(("resizing icon window %dx%d\n", changes.height, changes.width));
2071	XConfigureWindow(XtDisplay(xw), screen->iconVwin.window,
2072			 CWWidth | CWHeight | CWBorderWidth, &changes);
2073    }
2074#endif /* NO_ACTIVE_ICON */
2075
2076#ifdef TTYSIZE_STRUCT
2077    /* Set tty's idea of window size */
2078    TTYSIZE_ROWS(ts) = (ttySize_t) rows;
2079    TTYSIZE_COLS(ts) = (ttySize_t) cols;
2080#ifdef USE_STRUCT_WINSIZE
2081    ts.ws_xpixel = (ttySize_t) width;
2082    ts.ws_ypixel = (ttySize_t) height;
2083#endif
2084    code = SET_TTYSIZE(screen->respond, ts);
2085    TRACE(("return %d from SET_TTYSIZE %dx%d\n", code, rows, cols));
2086    (void) code;
2087
2088#if defined(SIGWINCH) && defined(USE_STRUCT_TTYSIZE)
2089    if (screen->pid > 1) {
2090	int pgrp;
2091
2092	TRACE(("getting process-group\n"));
2093	if (ioctl(screen->respond, TIOCGPGRP, &pgrp) != -1) {
2094	    TRACE(("sending SIGWINCH to process group %d\n", pgrp));
2095	    kill_process_group(pgrp, SIGWINCH);
2096	}
2097    }
2098#endif /* SIGWINCH */
2099
2100#else
2101    TRACE(("ScreenResize cannot do anything to pty\n"));
2102#endif /* TTYSIZE_STRUCT */
2103    return (0);
2104}
2105
2106/*
2107 * Return true if any character cell starting at [row,col], for len-cells is
2108 * nonnull.
2109 */
2110Bool
2111non_blank_line(TScreen * screen,
2112	       int row,
2113	       int col,
2114	       int len)
2115{
2116    int i;
2117    Bool found = False;
2118    LineData *ld = getLineData(screen, row);
2119
2120    if (ld != 0) {
2121	for (i = col; i < len; i++) {
2122	    if (ld->charData[i]) {
2123		found = True;
2124		break;
2125	    }
2126	}
2127    }
2128    return found;
2129}
2130
2131/*
2132 * Rectangle parameters start from one.
2133 */
2134#define minRectRow(screen) (getMinRow(screen) + 1)
2135#define minRectCol(screen) (getMinCol(screen) + 1)
2136#define maxRectRow(screen) (getMaxRow(screen) + 1)
2137#define maxRectCol(screen) (getMaxCol(screen) + 1)
2138
2139static int
2140limitedParseRow(XtermWidget xw, TScreen * screen, int row)
2141{
2142    int min_row = minRectRow(screen);
2143    int max_row = maxRectRow(screen);
2144
2145    if (row < min_row)
2146	row = min_row;
2147    else if (row > max_row)
2148	row = max_row;
2149    return row;
2150}
2151
2152static int
2153limitedParseCol(XtermWidget xw, TScreen * screen, int col)
2154{
2155    int min_col = minRectCol(screen);
2156    int max_col = maxRectCol(screen);
2157
2158    (void) xw;
2159    if (col < min_col)
2160	col = min_col;
2161    else if (col > max_col)
2162	col = max_col;
2163    return col;
2164}
2165
2166#define LimitedParse(num, func, dft) \
2167	func(xw, screen, (nparams > num) ? params[num] : dft)
2168
2169/*
2170 * Copy the rectangle boundaries into a struct, providing default values as
2171 * needed.
2172 */
2173void
2174xtermParseRect(XtermWidget xw, int nparams, int *params, XTermRect * target)
2175{
2176    TScreen *screen = TScreenOf(xw);
2177
2178    memset(target, 0, sizeof(*target));
2179    target->top = LimitedParse(0, limitedParseRow, minRectRow(screen));
2180    target->left = LimitedParse(1, limitedParseCol, minRectCol(screen));
2181    target->bottom = LimitedParse(2, limitedParseRow, maxRectRow(screen));
2182    target->right = LimitedParse(3, limitedParseCol, maxRectCol(screen));
2183    TRACE(("parsed rectangle %d,%d %d,%d\n",
2184	   target->top,
2185	   target->left,
2186	   target->bottom,
2187	   target->right));
2188}
2189
2190static Bool
2191validRect(XtermWidget xw, XTermRect * target)
2192{
2193    TScreen *screen = TScreenOf(xw);
2194
2195    TRACE(("comparing against screensize %dx%d\n",
2196	   maxRectRow(screen),
2197	   maxRectCol(screen)));
2198    return (target != 0
2199	    && target->top >= minRectRow(screen)
2200	    && target->left >= minRectCol(screen)
2201	    && target->top <= target->bottom
2202	    && target->left <= target->right
2203	    && target->top <= maxRectRow(screen)
2204	    && target->right <= maxRectCol(screen));
2205}
2206
2207/*
2208 * Fills a rectangle with the given 8-bit character and video-attributes.
2209 * Colors and double-size attribute are unmodified.
2210 */
2211void
2212ScrnFillRectangle(XtermWidget xw,
2213		  XTermRect * target,
2214		  int value,
2215		  unsigned flags,
2216		  Bool keepColors)
2217{
2218    TScreen *screen = TScreenOf(xw);
2219
2220    TRACE(("filling rectangle with '%c' flags %#x\n", value, flags));
2221    if (validRect(xw, target)) {
2222	LineData *ld;
2223	unsigned left = (unsigned) (target->left - 1);
2224	unsigned size = (unsigned) (target->right - (int) left);
2225	unsigned attrs = flags;
2226	int row, col;
2227
2228	(void) size;
2229
2230	attrs &= ATTRIBUTES;
2231	attrs |= CHARDRAWN;
2232	for (row = target->bottom - 1; row >= (target->top - 1); row--) {
2233	    ld = getLineData(screen, row);
2234
2235	    TRACE(("filling %d [%d..%d]\n", row, left, left + size));
2236
2237	    /*
2238	     * Fill attributes, preserving "protected" flag, as well as
2239	     * colors if asked.
2240	     */
2241	    for (col = (int) left; col < target->right; ++col) {
2242		unsigned temp = ld->attribs[col];
2243
2244		if (!keepColors) {
2245		    UIntClr(temp, (FG_COLOR | BG_COLOR));
2246		}
2247		temp = attrs | (temp & (FG_COLOR | BG_COLOR | PROTECTED));
2248		temp |= CHARDRAWN;
2249		ld->attribs[col] = (Char) temp;
2250#if OPT_ISO_COLORS
2251		if (attrs & (FG_COLOR | BG_COLOR)) {
2252		    if_OPT_ISO_COLORS(screen, {
2253			ld->color[col] = xtermColorPair(xw);
2254		    });
2255		}
2256#endif
2257	    }
2258
2259	    for (col = (int) left; col < target->right; ++col)
2260		ld->charData[col] = (CharData) value;
2261
2262	    if_OPT_WIDE_CHARS(screen, {
2263		size_t off;
2264		for_each_combData(off, ld) {
2265		    memset(ld->combData[off] + left, 0, size * sizeof(CharData));
2266		}
2267	    })
2268	}
2269	ScrnUpdate(xw,
2270		   target->top - 1,
2271		   target->left - 1,
2272		   (target->bottom - target->top) + 1,
2273		   (target->right - target->left) + 1,
2274		   False);
2275    }
2276}
2277
2278#if OPT_DEC_RECTOPS
2279/*
2280 * Copies the source rectangle to the target location, including video
2281 * attributes.
2282 *
2283 * This implementation ignores page numbers.
2284 *
2285 * The reference manual does not indicate if it handles overlapping copy
2286 * properly - so we make a local copy of the source rectangle first, then apply
2287 * the target from that.
2288 */
2289void
2290ScrnCopyRectangle(XtermWidget xw, XTermRect * source, int nparam, int *params)
2291{
2292    TScreen *screen = TScreenOf(xw);
2293
2294    TRACE(("copying rectangle\n"));
2295
2296    if (validRect(xw, source)) {
2297	XTermRect target;
2298	xtermParseRect(xw,
2299		       ((nparam > 3) ? 2 : (nparam - 1)),
2300		       params,
2301		       &target);
2302	if (validRect(xw, &target)) {
2303	    Cardinal high = (Cardinal) (source->bottom - source->top) + 1;
2304	    Cardinal wide = (Cardinal) (source->right - source->left) + 1;
2305	    Cardinal size = (high * wide);
2306	    int row, col;
2307	    Cardinal j, k;
2308	    LineData *ld;
2309
2310	    CellData *cells = newCellData(xw, size);
2311
2312	    if (cells != 0) {
2313
2314		TRACE(("OK - make copy %dx%d\n", high, wide));
2315		target.bottom = target.top + (int) (high - 1);
2316		target.right = target.left + (int) (wide - 1);
2317
2318		for (row = source->top - 1; row < source->bottom; ++row) {
2319		    ld = getLineData(screen, row);
2320		    j = (Cardinal) (row - (source->top - 1));
2321		    for (col = source->left - 1; col < source->right; ++col) {
2322			k = (Cardinal) (col - (source->left - 1));
2323			saveCellData(screen, cells,
2324				     (j * wide) + k,
2325				     ld, col);
2326		    }
2327		}
2328		for (row = target.top - 1; row < target.bottom; ++row) {
2329		    ld = getLineData(screen, row);
2330		    j = (Cardinal) (row - (target.top - 1));
2331		    for (col = target.left - 1; col < target.right; ++col) {
2332			k = (Cardinal) (col - (target.left - 1));
2333			if (row >= getMinRow(screen)
2334			    && row <= getMaxRow(screen)
2335			    && col >= getMinCol(screen)
2336			    && col <= getMaxCol(screen)) {
2337			    if (j < high && k < wide) {
2338				restoreCellData(screen, cells,
2339						(j * wide) + k,
2340						ld, col);
2341			    } else {
2342				/* FIXME - clear the target cell? */
2343			    }
2344			    ld->attribs[col] |= CHARDRAWN;
2345			}
2346		    }
2347		}
2348		free(cells);
2349
2350		ScrnUpdate(xw,
2351			   (target.top - 1),
2352			   (target.left - 1),
2353			   (target.bottom - target.top) + 1,
2354			   ((target.right - target.left) + 1),
2355			   False);
2356	    }
2357	}
2358    }
2359}
2360
2361/*
2362 * Modifies the video-attributes only - so selection (not a video attribute) is
2363 * unaffected.  Colors and double-size flags are unaffected as well.
2364 *
2365 * FIXME: our representation for "invisible" does not work with this operation,
2366 * since the attribute byte is fully-allocated for other flags.  The logic
2367 * is shown for INVISIBLE because it's harmless, and useful in case the
2368 * CHARDRAWN or PROTECTED flags are reassigned.
2369 */
2370void
2371ScrnMarkRectangle(XtermWidget xw,
2372		  XTermRect * target,
2373		  Bool reverse,
2374		  int nparam,
2375		  int *params)
2376{
2377    TScreen *screen = TScreenOf(xw);
2378    Bool exact = (screen->cur_decsace == 2);
2379
2380    TRACE(("%s %s\n",
2381	   reverse ? "reversing" : "marking",
2382	   (exact
2383	    ? "rectangle"
2384	    : "region")));
2385
2386    if (validRect(xw, target)) {
2387	LineData *ld;
2388	int top = target->top - 1;
2389	int bottom = target->bottom - 1;
2390	int row, col;
2391	int n;
2392
2393	for (row = top; row <= bottom; ++row) {
2394	    int left = ((exact || (row == top))
2395			? (target->left - 1)
2396			: getMinCol(screen));
2397	    int right = ((exact || (row == bottom))
2398			 ? (target->right - 1)
2399			 : getMaxCol(screen));
2400
2401	    ld = getLineData(screen, row);
2402
2403	    TRACE(("marking %d [%d..%d]\n", row, left, right));
2404	    for (col = left; col <= right; ++col) {
2405		unsigned flags = ld->attribs[col];
2406
2407		for (n = 0; n < nparam; ++n) {
2408#if OPT_TRACE
2409		    if (row == top && col == left)
2410			TRACE(("attr param[%d] %d\n", n + 1, params[n]));
2411#endif
2412		    if (reverse) {
2413			switch (params[n]) {
2414			case 1:
2415			    flags ^= BOLD;
2416			    break;
2417			case 4:
2418			    flags ^= UNDERLINE;
2419			    break;
2420			case 5:
2421			    flags ^= BLINK;
2422			    break;
2423			case 7:
2424			    flags ^= INVERSE;
2425			    break;
2426			case 8:
2427			    flags ^= INVISIBLE;
2428			    break;
2429			}
2430		    } else {
2431			switch (params[n]) {
2432			case 0:
2433			    UIntClr(flags, SGR_MASK);
2434			    break;
2435			case 1:
2436			    flags |= BOLD;
2437			    break;
2438			case 4:
2439			    flags |= UNDERLINE;
2440			    break;
2441			case 5:
2442			    flags |= BLINK;
2443			    break;
2444			case 7:
2445			    flags |= INVERSE;
2446			    break;
2447			case 8:
2448			    flags |= INVISIBLE;
2449			    break;
2450			case 22:
2451			    UIntClr(flags, BOLD);
2452			    break;
2453			case 24:
2454			    UIntClr(flags, UNDERLINE);
2455			    break;
2456			case 25:
2457			    UIntClr(flags, BLINK);
2458			    break;
2459			case 27:
2460			    UIntClr(flags, INVERSE);
2461			    break;
2462			case 28:
2463			    UIntClr(flags, INVISIBLE);
2464			    break;
2465			}
2466		    }
2467		}
2468#if OPT_TRACE
2469		if (row == top && col == left)
2470		    TRACE(("first mask-change is %#x\n",
2471			   ld->attribs[col] ^ flags));
2472#endif
2473		ld->attribs[col] = (Char) flags;
2474	    }
2475	}
2476	ScrnRefresh(xw,
2477		    (target->top - 1),
2478		    (exact ? (target->left - 1) : getMinCol(screen)),
2479		    (target->bottom - target->top) + 1,
2480		    (exact
2481		     ? ((target->right - target->left) + 1)
2482		     : (getMaxCol(screen) - getMinCol(screen) + 1)),
2483		    False);
2484    }
2485}
2486
2487/*
2488 * Resets characters to space, except where prohibited by DECSCA.  Video
2489 * attributes (including color) are untouched.
2490 */
2491void
2492ScrnWipeRectangle(XtermWidget xw,
2493		  XTermRect * target)
2494{
2495    TScreen *screen = TScreenOf(xw);
2496
2497    TRACE(("wiping rectangle\n"));
2498
2499    if (validRect(xw, target)) {
2500	LineData *ld;
2501	int top = target->top - 1;
2502	int bottom = target->bottom - 1;
2503	int row, col;
2504
2505	for (row = top; row <= bottom; ++row) {
2506	    int left = (target->left - 1);
2507	    int right = (target->right - 1);
2508
2509	    TRACE(("wiping %d [%d..%d]\n", row, left, right));
2510
2511	    ld = getLineData(screen, row);
2512	    for (col = left; col <= right; ++col) {
2513		if (!((screen->protected_mode == DEC_PROTECT)
2514		      && (ld->attribs[col] & PROTECTED))) {
2515		    ld->attribs[col] |= CHARDRAWN;
2516		    ld->charData[col] = ' ';
2517		    if_OPT_WIDE_CHARS(screen, {
2518			size_t off;
2519			for_each_combData(off, ld) {
2520			    ld->combData[off][col] = '\0';
2521			}
2522		    })
2523		}
2524	    }
2525	}
2526	ScrnUpdate(xw,
2527		   (target->top - 1),
2528		   (target->left - 1),
2529		   (target->bottom - target->top) + 1,
2530		   ((target->right - target->left) + 1),
2531		   False);
2532    }
2533}
2534#endif /* OPT_DEC_RECTOPS */
2535