screen.c revision 956cc18d
1/* $XTermId: screen.c,v 1.396 2009/08/30 00:06:07 tom Exp $ */
2
3/*
4 * Copyright 1999-2008,2009 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, dst), \
83		scrnHeadAddr(screen, base, src), \
84		scrnHeadSize(screen, len))
85
86#define SaveLineData(base, src, len) \
87	(void) ScrnPointers(screen, len); \
88	memcpy (screen->save_ptr, \
89		scrnHeadAddr(screen, base, src), \
90		scrnHeadSize(screen, len))
91
92#define RestoreLineData(base, dst, len) \
93	memcpy (scrnHeadAddr(screen, base, dst), \
94		screen->save_ptr, \
95		scrnHeadSize(screen, 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	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 | 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 += 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(nrow, size);
269    if (result == 0)
270	SysError(ERROR_SCALLOC);
271
272    TRACE(("allocScrnHead %d -> %d -> %p..%p\n", nrow, nrow * size,
273	   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 * 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 %dx%d -> %d -> %p..%p\n",
324	   nrow, ncol, 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, 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	   unsigned oldcol)
426{
427    TScreen *screen = TScreenOf(xw);
428    ScrnBuf oldBufHead;
429    ScrnBuf newBufHead;
430    Char *newBufData;
431    unsigned minrows;
432    unsigned mincols;
433    Char *oldBufData;
434    int move_down = 0, move_up = 0;
435
436    if (sbuf == NULL || *sbuf == NULL) {
437	return 0;
438    }
439
440    oldBufData = *sbufaddr;
441
442    TRACE(("Reallocate %dx%d -> %dx%d\n", oldrow, oldcol, nrow, ncol));
443
444    /*
445     * realloc sbuf, the pointers to all the lines.
446     * If the screen shrinks, remove lines off the top of the buffer
447     * if resizeGravity resource says to do so.
448     */
449    TRACE(("Check move_up, nrow %d vs oldrow %d (resizeGravity %s)\n",
450	   nrow, oldrow,
451	   BtoS(GravityIsSouthWest(xw))));
452    if (GravityIsSouthWest(xw)) {
453	if (nrow < oldrow) {
454	    /* Remove lines off the top of the buffer if necessary. */
455	    move_up = (int) (oldrow - nrow)
456		- (xw->screen.max_row - xw->screen.cur_row);
457	    if (move_up < 0)
458		move_up = 0;
459	    /* Overlapping move here! */
460	    TRACE(("move_up %d\n", move_up));
461	    if (move_up) {
462		ScrnBuf dst = *sbuf;
463		unsigned len = (unsigned) ((int) oldrow - move_up);
464
465		TRACE_SCRNBUF("before move_up", screen, dst, oldrow);
466		SaveLineData(dst, 0, (size_t) move_up);
467		MoveLineData(dst, 0, (size_t) move_up, len);
468		RestoreLineData(dst, len, (size_t) move_up);
469		TRACE_SCRNBUF("after move_up", screen, dst, oldrow);
470	    }
471	}
472    }
473    oldBufHead = *sbuf;
474    *sbuf = allocScrnHead(screen, (unsigned) nrow);
475    newBufHead = *sbuf;
476
477    /*
478     * Create the new buffer space and copy old buffer contents there, line by
479     * line.
480     */
481    newBufData = allocScrnData(screen, nrow, ncol);
482    *sbufaddr = newBufData;
483
484    minrows = (oldrow < nrow) ? oldrow : nrow;
485    mincols = (oldcol < ncol) ? oldcol : ncol;
486    if (GravityIsSouthWest(xw)) {
487	if (nrow > oldrow) {
488	    /* move data down to bottom of expanded screen */
489	    move_down = Min((int) (nrow - oldrow), xw->screen.savedlines);
490	}
491    }
492
493    setupLineData(screen, newBufHead, *sbufaddr, nrow, ncol);
494    extractScrnData(screen, newBufHead, oldBufHead, minrows,
495#if OPT_SAVE_LINES
496		    0
497#else
498		    (unsigned) move_down
499#endif
500	);
501    free(oldBufHead);
502
503    /* Now free the old data */
504    free(oldBufData);
505
506    TRACE(("...Reallocate %dx%d ->%p\n", nrow, ncol, newBufHead));
507    return move_down ? move_down : -move_up;	/* convert to rows */
508}
509
510#if OPT_WIDE_CHARS
511/*
512 * This function reallocates memory if changing the number of Buf offsets.
513 * The code is based on Reallocate().
514 */
515static void
516ReallocateBufOffsets(XtermWidget xw,
517		     ScrnBuf * sbuf,
518		     Char ** sbufaddr,
519		     unsigned nrow,
520		     unsigned ncol)
521{
522    TScreen *screen = TScreenOf(xw);
523    unsigned i;
524    ScrnBuf newBufHead;
525    Char *oldBufData;
526    ScrnBuf oldBufHead;
527
528    unsigned old_jump = scrnHeadSize(screen, 1);
529    unsigned new_jump;
530    unsigned new_ptrs = 1 + (unsigned) (screen->max_combining);
531    unsigned dstCols = ncol;
532    unsigned srcCols = ncol;
533    LineData *dstPtrs;
534    LineData *srcPtrs;
535    Char *nextPtr;
536
537    assert(nrow != 0);
538    assert(ncol != 0);
539
540    oldBufData = *sbufaddr;
541    oldBufHead = *sbuf;
542
543    /*
544     * Allocate a new LineData array, retain the old one until we've copied
545     * the data that it points to, as well as non-pointer data, e.g., bufHead.
546     *
547     * Turn on wide-chars temporarily when constructing pointers, since that is
548     * used to decide whether to address the combData[] array, which affects
549     * the length of the LineData structure.
550     */
551    screen->wide_chars = True;
552
553    new_jump = scrnHeadSize(screen, 1);
554    newBufHead = allocScrnHead(screen, nrow);
555    *sbufaddr = allocScrnData(screen, nrow, ncol);
556    setupLineData(screen, newBufHead, *sbufaddr, nrow, ncol);
557
558    screen->wide_chars = False;
559
560    nextPtr = *sbufaddr;
561
562    srcPtrs = (LineData *) oldBufHead;
563    dstPtrs = (LineData *) newBufHead;
564    for (i = 0; i < nrow; i++) {
565	dstPtrs->bufHead = srcPtrs->bufHead;
566	ExtractScrnData(attribs);
567#if OPT_ISO_COLORS
568	ExtractScrnData(color);
569#endif
570	ExtractScrnData(charData);
571
572	nextPtr += ncol * new_ptrs;
573	srcPtrs = LineDataAddr(srcPtrs, old_jump);
574	dstPtrs = LineDataAddr(dstPtrs, new_jump);
575    }
576
577    /* Now free the old data */
578    free(oldBufData);
579    free(oldBufHead);
580
581    *sbuf = newBufHead;
582
583    TRACE(("ReallocateBufOffsets %dx%d ->%p\n", nrow, ncol, *sbufaddr));
584}
585
586/*
587 * This function dynamically adds support for wide-characters.
588 */
589void
590ChangeToWide(XtermWidget xw)
591{
592    TScreen *screen = &(xw->screen);
593    int savelines = screen->scrollWidget ? screen->savelines : 0;
594
595    if (screen->wide_chars)
596	return;
597
598    TRACE(("ChangeToWide\n"));
599    if (xtermLoadWideFonts(xw, True)) {
600	if (savelines < 0)
601	    savelines = 0;
602
603	/*
604	 * If we're displaying the alternate screen, switch the pointers back
605	 * temporarily so ReallocateBufOffsets() will operate on the proper
606	 * data in the alternate buffer.
607	 */
608	if (screen->whichBuf)
609	    SwitchBufPtrs(screen);
610
611#if OPT_SAVE_LINES
612#if !OPT_FIFO_LINES
613	ReallocateBufOffsets(xw,
614			     &screen->saveBuf_index,
615			     &screen->saveBuf_data,
616			     (unsigned) savelines,
617			     (unsigned) MaxCols(screen));
618#endif
619	if (screen->editBuf_index[0]) {
620	    ReallocateBufOffsets(xw,
621				 &screen->editBuf_index[0],
622				 &screen->editBuf_data[0],
623				 (unsigned) MaxRows(screen),
624				 (unsigned) MaxCols(screen));
625	}
626#else
627	ReallocateBufOffsets(xw,
628			     &screen->saveBuf_index,
629			     &screen->saveBuf_data,
630			     (unsigned) (MaxRows(screen) + savelines),
631			     (unsigned) MaxCols(screen));
632#endif
633	if (screen->editBuf_index[1]) {
634	    ReallocateBufOffsets(xw,
635				 &screen->editBuf_index[1],
636				 &screen->editBuf_data[1],
637				 (unsigned) MaxRows(screen),
638				 (unsigned) MaxCols(screen));
639	}
640
641	screen->wide_chars = True;
642	screen->visbuf = VisBuf(screen);
643
644	/*
645	 * Switch the pointers back before we start painting on the screen.
646	 */
647	if (screen->whichBuf)
648	    SwitchBufPtrs(screen);
649
650	update_font_utf8_mode();
651	SetVTFont(xw, screen->menu_font_number, True, NULL);
652    }
653    TRACE(("...ChangeToWide\n"));
654}
655#endif
656
657/*
658 * Clear cells, no side-effects.
659 */
660void
661ClearCells(XtermWidget xw, int flags, unsigned len, int row, int col)
662{
663    if (len != 0) {
664	TScreen *screen = &(xw->screen);
665	LineData *ld;
666	unsigned n;
667
668	ld = getLineData(screen, row);
669
670	flags |= TERM_COLOR_FLAGS(xw);
671
672	for (n = 0; n < len; ++n)
673	    ld->charData[(unsigned) col + n] = (CharData) ' ';
674
675	memset(ld->attribs + col, flags, len);
676
677	if_OPT_ISO_COLORS(screen, {
678	    CellColor p = xtermColorPair(xw);
679	    for (n = 0; n < len; ++n) {
680		ld->color[(unsigned) col + n] = p;
681	    }
682	});
683	if_OPT_WIDE_CHARS(screen, {
684	    size_t off;
685	    for_each_combData(off, ld) {
686		memset(ld->combData[off] + col, 0, len * sizeof(IChar));
687	    }
688	});
689    }
690}
691
692/*
693 * Clear data in the screen-structure (no I/O).
694 * Check for wide-character damage as well, clearing the damaged cells.
695 */
696void
697ScrnClearCells(XtermWidget xw, int row, int col, unsigned len)
698{
699#if OPT_WIDE_CHARS
700    TScreen *screen = &(xw->screen);
701#endif
702    int flags = 0;
703
704    if_OPT_WIDE_CHARS(screen, {
705	int kl;
706	int kr;
707	if (DamagedCells(screen, len, &kl, &kr, INX2ROW(screen, row), col)
708	    && kr >= kl) {
709	    ClearCells(xw, flags, (unsigned) (kr - kl + 1), row, kl);
710	}
711    });
712    ClearCells(xw, flags, len, row, col);
713}
714
715/*
716 * Disown the selection and repaint the area that is highlighted so it is no
717 * longer highlighted.
718 */
719void
720ScrnDisownSelection(XtermWidget xw)
721{
722    if (ScrnHaveSelection(&(xw->screen))) {
723	if (xw->screen.keepSelection) {
724	    UnhiliteSelection(xw);
725	} else {
726	    DisownSelection(xw);
727	}
728    }
729}
730
731/*
732 * Writes str into buf at screen's current row and column.  Characters are set
733 * to match flags.
734 */
735void
736ScrnWriteText(XtermWidget xw,
737	      IChar * str,
738	      unsigned flags,
739	      CellColor cur_fg_bg,
740	      unsigned length)
741{
742    TScreen *screen = &(xw->screen);
743    LineData *ld;
744#if OPT_ISO_COLORS
745    CellColor *fb = 0;
746#endif
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 (avail <= 0)
759	return;
760    if (length > (unsigned) avail)
761	length = (unsigned) avail;
762    if (length == 0 || real_width == 0)
763	return;
764
765    ld = getLineData(screen, screen->cur_row);
766
767    chars = ld->charData + screen->cur_col;
768    attrs = ld->attribs + screen->cur_col;
769
770    if_OPT_ISO_COLORS(screen, {
771	fb = ld->color + screen->cur_col;
772    });
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, 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(IChar));
845	}
846    });
847    if_OPT_ISO_COLORS(screen, {
848	unsigned j;
849	for (j = 0; j < real_width; ++j)
850	    fb[j] = 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 = &(xw->screen);
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(IChar));
901	if (TERM_COLOR_FLAGS(xw)) {
902	    memset(work->attribs, (int) flags, 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);
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(IChar));
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, 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 = &(xw->screen);
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 = &(xw->screen);
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 = &(xw->screen);
1140    int last = MaxCols(screen);
1141    int row = screen->cur_row;
1142    int col = screen->cur_col;
1143    int j, nbytes;
1144    LineData *ld;
1145
1146    if (last <= (col + (int) n)) {
1147	if (last <= col)
1148	    return;
1149	n = (unsigned) (last - col);
1150    }
1151    nbytes = (last - (col + (int) n));
1152
1153    assert(screen->cur_col >= 0);
1154    assert(screen->cur_row >= 0);
1155    assert(n > 0);
1156    assert(last > (int) n);
1157
1158    if_OPT_WIDE_CHARS(screen, {
1159	int xx = INX2ROW(screen, screen->cur_row);
1160	int kl;
1161	int kr = screen->cur_col;
1162	if (DamagedCells(screen, n, &kl, (int *) 0, xx, kr) && kr > kl) {
1163	    ClearCells(xw, 0, (unsigned) (kr - kl + 1), row, kl);
1164	}
1165	kr = screen->max_col - (int) n + 1;
1166	if (DamagedCells(screen, n, &kl, (int *) 0, xx, kr) && kr > kl) {
1167	    ClearCells(xw, 0, (unsigned) (kr - kl + 1), row, kl);
1168	}
1169    });
1170
1171    if ((ld = getLineData(screen, row)) != 0) {
1172	MemMove(ld->charData);
1173	MemMove(ld->attribs);
1174
1175	if_OPT_ISO_COLORS(screen, {
1176	    MemMove(ld->color);
1177	});
1178	if_OPT_WIDE_CHARS(screen, {
1179	    size_t off;
1180	    for_each_combData(off, ld) {
1181		MemMove(ld->combData[off]);
1182	    }
1183	});
1184    }
1185    ClearCells(xw, CHARDRAWN, n, row, col);
1186
1187#undef MemMove
1188}
1189
1190/*
1191 * Deletes n characters at current row, col.
1192 */
1193void
1194ScrnDeleteChar(XtermWidget xw, unsigned n)
1195{
1196#define MemMove(data) \
1197    	for (j = col; j < last - (int) n; ++j) \
1198	    data[j] = data[j + (int) n]
1199
1200    TScreen *screen = &(xw->screen);
1201    int last = MaxCols(screen);
1202    int row = screen->cur_row;
1203    int col = screen->cur_col;
1204    int j, nbytes;
1205    LineData *ld;
1206
1207    if (last <= (col + (int) n)) {
1208	if (last <= col)
1209	    return;
1210	n = (unsigned) (last - col);
1211    }
1212    nbytes = (last - (col + (int) n));
1213
1214    assert(screen->cur_col >= 0);
1215    assert(screen->cur_row >= 0);
1216    assert(n > 0);
1217    assert(last > (int) n);
1218
1219    if_OPT_WIDE_CHARS(screen, {
1220	int kl;
1221	int kr;
1222	if (DamagedCells(screen, n, &kl, &kr,
1223			 INX2ROW(screen, screen->cur_row),
1224			 screen->cur_col))
1225	    ClearCells(xw, 0, (unsigned) (kr - kl + 1), row, kl);
1226    });
1227
1228    if ((ld = getLineData(screen, row)) != 0) {
1229	MemMove(ld->charData);
1230	MemMove(ld->attribs);
1231
1232	if_OPT_ISO_COLORS(screen, {
1233	    MemMove(ld->color);
1234	});
1235	if_OPT_WIDE_CHARS(screen, {
1236	    size_t off;
1237	    for_each_combData(off, ld) {
1238		MemMove(ld->combData[off]);
1239	    }
1240	});
1241	LineClrWrapped(ld);
1242    }
1243    ClearCells(xw, 0, n, row, (last - (int) n));
1244
1245#undef MemMove
1246}
1247
1248/*
1249 * Repaints the area enclosed by the parameters.
1250 * Requires: (toprow, leftcol), (toprow + nrows, leftcol + ncols) are
1251 * 	     coordinates of characters in screen;
1252 *	     nrows and ncols positive.
1253 *	     all dimensions are based on single-characters.
1254 */
1255void
1256ScrnRefresh(XtermWidget xw,
1257	    int toprow,
1258	    int leftcol,
1259	    int nrows,
1260	    int ncols,
1261	    Bool force)		/* ... leading/trailing spaces */
1262{
1263    TScreen *screen = &(xw->screen);
1264    LineData *ld;
1265    int y = toprow * FontHeight(screen) + screen->border;
1266    int row;
1267    int maxrow = toprow + nrows - 1;
1268    int scrollamt = screen->scroll_amt;
1269    int max = screen->max_row;
1270    unsigned gc_changes = 0;
1271#ifdef __CYGWIN__
1272    static char first_time = 1;
1273#endif
1274    static int recurse = 0;
1275
1276    TRACE(("ScrnRefresh (%d,%d) - (%d,%d)%s {{\n",
1277	   toprow, leftcol,
1278	   nrows, ncols,
1279	   force ? " force" : ""));
1280
1281    if (screen->cursorp.col >= leftcol
1282	&& screen->cursorp.col <= (leftcol + ncols - 1)
1283	&& screen->cursorp.row >= ROW2INX(screen, toprow)
1284	&& screen->cursorp.row <= ROW2INX(screen, maxrow))
1285	screen->cursor_state = OFF;
1286
1287    for (row = toprow; row <= maxrow; y += FontHeight(screen), row++) {
1288#if OPT_ISO_COLORS
1289	CellColor *fb = 0;
1290#define ColorOf(col) fb[col]
1291#endif
1292#if OPT_WIDE_CHARS
1293	int wideness = 0;
1294#endif
1295#define BLANK_CEL(cell) (chars[cell] == ' ')
1296	IChar *chars;
1297	Char *attrs;
1298	int col = leftcol;
1299	int maxcol = leftcol + ncols - 1;
1300	int hi_col = maxcol;
1301	int lastind;
1302	unsigned flags;
1303	unsigned test;
1304	CellColor fg_bg = 0;
1305	unsigned fg = 0, bg = 0;
1306	int x;
1307	GC gc;
1308	Bool hilite;
1309
1310	(void) fg;
1311	(void) bg;
1312#if !OPT_ISO_COLORS
1313	fg_bg = 0;
1314#endif
1315
1316	if (row < screen->top_marg || row > screen->bot_marg)
1317	    lastind = row;
1318	else
1319	    lastind = row - scrollamt;
1320
1321	TRACE2(("ScrnRefresh row=%d lastind=%d/%d\n", row, lastind, max));
1322	if (lastind < 0 || lastind > max)
1323	    continue;
1324
1325	if ((ld = getLineData(screen, ROW2INX(screen, lastind))) == 0)
1326	    break;
1327	if (maxcol >= ld->lineSize) {
1328	    maxcol = ld->lineSize - 1;
1329	    hi_col = maxcol;
1330	}
1331
1332	chars = ld->charData;
1333	attrs = ld->attribs;
1334
1335	if_OPT_WIDE_CHARS(screen, {
1336	    /* This fixes an infinite recursion bug, that leads
1337	       to display anomalies. It seems to be related to
1338	       problems with the selection. */
1339	    if (recurse < 3) {
1340		/* adjust to redraw all of a widechar if we just wanted
1341		   to draw the right hand half */
1342		if (leftcol > 0 &&
1343		    chars[leftcol] == HIDDEN_CHAR &&
1344		    isWide((int) chars[leftcol - 1])) {
1345		    leftcol--;
1346		    ncols++;
1347		    col = leftcol;
1348		}
1349	    } else {
1350		fprintf(stderr, "This should not happen. Why is it so?\n");
1351	    }
1352	});
1353
1354	if (row < screen->startH.row || row > screen->endH.row ||
1355	    (row == screen->startH.row && maxcol < screen->startH.col) ||
1356	    (row == screen->endH.row && col >= screen->endH.col)) {
1357#if OPT_DEC_CHRSET
1358	    /*
1359	     * Temporarily change dimensions to double-sized characters so
1360	     * we can reuse the recursion on this function.
1361	     */
1362	    if (CSET_DOUBLE(GetLineDblCS(ld))) {
1363		col /= 2;
1364		maxcol /= 2;
1365	    }
1366#endif
1367	    /*
1368	     * If row does not intersect selection; don't hilite blanks.
1369	     */
1370	    if (!force) {
1371		while (col <= maxcol && (attrs[col] & ~BOLD) == 0 &&
1372		       BLANK_CEL(col))
1373		    col++;
1374
1375		while (col <= maxcol && (attrs[maxcol] & ~BOLD) == 0 &&
1376		       BLANK_CEL(maxcol))
1377		    maxcol--;
1378	    }
1379#if OPT_DEC_CHRSET
1380	    if (CSET_DOUBLE(GetLineDblCS(ld))) {
1381		col *= 2;
1382		maxcol *= 2;
1383	    }
1384#endif
1385	    hilite = False;
1386	} else {
1387	    /* row intersects selection; split into pieces of single type */
1388	    if (row == screen->startH.row && col < screen->startH.col) {
1389		recurse++;
1390		ScrnRefresh(xw, row, col, 1, screen->startH.col - col,
1391			    force);
1392		col = screen->startH.col;
1393	    }
1394	    if (row == screen->endH.row && maxcol >= screen->endH.col) {
1395		recurse++;
1396		ScrnRefresh(xw, row, screen->endH.col, 1,
1397			    maxcol - screen->endH.col + 1, force);
1398		maxcol = screen->endH.col - 1;
1399	    }
1400
1401	    /*
1402	     * If we're highlighting because the user is doing cut/paste,
1403	     * trim the trailing blanks from the highlighted region so we're
1404	     * showing the actual extent of the text that'll be cut.  If
1405	     * we're selecting a blank line, we'll highlight one column
1406	     * anyway.
1407	     *
1408	     * We don't do this if the mouse-hilite mode is set because that
1409	     * would be too confusing.
1410	     *
1411	     * The default if the highlightSelection resource isn't set will
1412	     * highlight the whole width of the terminal, which is easy to
1413	     * see, but harder to use (because trailing blanks aren't as
1414	     * apparent).
1415	     */
1416	    if (screen->highlight_selection
1417		&& screen->send_mouse_pos != VT200_HIGHLIGHT_MOUSE) {
1418		hi_col = screen->max_col;
1419		while (hi_col > 0 && !(attrs[hi_col] & CHARDRAWN))
1420		    hi_col--;
1421	    }
1422
1423	    /* remaining piece should be hilited */
1424	    hilite = True;
1425	}
1426
1427	if (col > maxcol)
1428	    continue;
1429
1430	/*
1431	 * Go back to double-sized character dimensions if the line has
1432	 * double-width characters.  Note that 'hi_col' is already in the
1433	 * right units.
1434	 */
1435	if_OPT_DEC_CHRSET({
1436	    if (CSET_DOUBLE(GetLineDblCS(ld))) {
1437		col /= 2;
1438		maxcol /= 2;
1439	    }
1440	});
1441
1442	flags = attrs[col];
1443
1444	if_OPT_WIDE_CHARS(screen, {
1445	    wideness = isWide((int) chars[col]);
1446	});
1447
1448	if_OPT_ISO_COLORS(screen, {
1449	    fb = ld->color;
1450	    fg_bg = ColorOf(col);
1451	    fg = extract_fg(xw, fg_bg, flags);
1452	    bg = extract_bg(xw, fg_bg, flags);
1453	});
1454
1455	gc = updatedXtermGC(xw, flags, fg_bg, hilite);
1456	gc_changes |= (flags & (FG_COLOR | BG_COLOR));
1457
1458	x = LineCursorX(screen, ld, col);
1459	lastind = col;
1460
1461	for (; col <= maxcol; col++) {
1462	    if ((attrs[col] != flags)
1463		|| (hilite && (col > hi_col))
1464#if OPT_ISO_COLORS
1465		|| ((flags & FG_COLOR)
1466		    && (extract_fg(xw, ColorOf(col), attrs[col]) != fg))
1467		|| ((flags & BG_COLOR)
1468		    && (extract_bg(xw, ColorOf(col), attrs[col]) != bg))
1469#endif
1470#if OPT_WIDE_CHARS
1471		|| (isWide((int) chars[col]) != wideness
1472		    && chars[col] != HIDDEN_CHAR)
1473#endif
1474		) {
1475		assert(col >= lastind);
1476		TRACE(("ScrnRefresh looping drawXtermText %d..%d:%s\n",
1477		       lastind, col,
1478		       visibleIChars((&chars[lastind]),
1479				     (unsigned) (col - lastind))));
1480
1481		test = flags;
1482		checkVeryBoldColors(test, fg);
1483
1484		x = drawXtermText(xw, test & DRAWX_MASK, gc, x, y,
1485				  GetLineDblCS(ld),
1486				  &chars[lastind],
1487				  (unsigned) (col - lastind), 0);
1488
1489		if_OPT_WIDE_CHARS(screen, {
1490		    int i;
1491		    size_t off;
1492
1493		    for_each_combData(off, ld) {
1494			IChar *com_off = ld->combData[off];
1495
1496			for (i = lastind; i < col; i++) {
1497			    int my_x = LineCursorX(screen, ld, i);
1498			    IChar base = chars[i];
1499
1500			    if (isWide((int) base))
1501				my_x = LineCursorX(screen, ld, i - 1);
1502
1503			    if (com_off[i] != 0)
1504				drawXtermText(xw,
1505					      (test & DRAWX_MASK)
1506					      | NOBACKGROUND,
1507					      gc, my_x, y,
1508					      GetLineDblCS(ld),
1509					      com_off + i,
1510					      1, isWide((int) base));
1511			}
1512		    }
1513		});
1514
1515		resetXtermGC(xw, flags, hilite);
1516
1517		lastind = col;
1518
1519		if (hilite && (col > hi_col))
1520		    hilite = False;
1521
1522		flags = attrs[col];
1523		if_OPT_ISO_COLORS(screen, {
1524		    fg_bg = ColorOf(col);
1525		    fg = extract_fg(xw, fg_bg, flags);
1526		    bg = extract_bg(xw, fg_bg, flags);
1527		});
1528		if_OPT_WIDE_CHARS(screen, {
1529		    wideness = isWide((int) chars[col]);
1530		});
1531
1532		gc = updatedXtermGC(xw, flags, fg_bg, hilite);
1533		gc_changes |= (flags & (FG_COLOR | BG_COLOR));
1534	    }
1535
1536	    if (chars[col] == 0) {
1537		chars[col] = ' ';
1538	    }
1539	}
1540
1541	assert(col >= lastind);
1542	TRACE(("ScrnRefresh calling drawXtermText %d..%d:%s\n",
1543	       lastind, col,
1544	       visibleIChars(&chars[lastind], (unsigned) (col - lastind))));
1545
1546	test = flags;
1547	checkVeryBoldColors(test, fg);
1548
1549	drawXtermText(xw, test & DRAWX_MASK, gc, x, y,
1550		      GetLineDblCS(ld),
1551		      &chars[lastind],
1552		      (unsigned) (col - lastind), 0);
1553
1554	if_OPT_WIDE_CHARS(screen, {
1555	    int i;
1556	    size_t off;
1557
1558	    for_each_combData(off, ld) {
1559		IChar *com_off = ld->combData[off];
1560
1561		for (i = lastind; i < col; i++) {
1562		    int my_x = LineCursorX(screen, ld, i);
1563		    int base = (int) chars[i];
1564
1565		    if (isWide(base))
1566			my_x = LineCursorX(screen, ld, i - 1);
1567
1568		    if (com_off[i] != 0)
1569			drawXtermText(xw,
1570				      (test & DRAWX_MASK)
1571				      | NOBACKGROUND,
1572				      gc, my_x, y,
1573				      GetLineDblCS(ld),
1574				      com_off + i,
1575				      1, isWide(base));
1576		}
1577	    }
1578	});
1579
1580	resetXtermGC(xw, flags, hilite);
1581    }
1582
1583    /*
1584     * If we're in color mode, reset the various GC's to the current
1585     * screen foreground and background so that other functions (e.g.,
1586     * ClearRight) will get the correct colors.
1587     */
1588    if_OPT_ISO_COLORS(screen, {
1589	if (gc_changes & FG_COLOR)
1590	    SGR_Foreground(xw, xw->cur_foreground);
1591	if (gc_changes & BG_COLOR)
1592	    SGR_Background(xw, xw->cur_background);
1593    });
1594
1595#if defined(__CYGWIN__) && defined(TIOCSWINSZ)
1596    if (first_time == 1) {
1597	TTYSIZE_STRUCT ts;
1598
1599	first_time = 0;
1600	TTYSIZE_ROWS(ts) = nrows;
1601	TTYSIZE_COLS(ts) = ncols;
1602	ts.ws_xpixel = xw->core.width;
1603	ts.ws_ypixel = xw->core.height;
1604	SET_TTYSIZE(screen->respond, ts);
1605    }
1606#endif
1607    recurse--;
1608
1609    TRACE(("...}} ScrnRefresh\n"));
1610    return;
1611}
1612
1613/*
1614 * Call this wrapper to ScrnRefresh() when the data has changed.  If the
1615 * refresh region overlaps the selection, we will release the primary selection.
1616 */
1617void
1618ScrnUpdate(XtermWidget xw,
1619	   int toprow,
1620	   int leftcol,
1621	   int nrows,
1622	   int ncols,
1623	   Bool force)		/* ... leading/trailing spaces */
1624{
1625    TScreen *screen = &(xw->screen);
1626
1627    if (ScrnHaveSelection(screen)
1628	&& (toprow <= screen->endH.row)
1629	&& (toprow + nrows - 1 >= screen->startH.row)) {
1630	ScrnDisownSelection(xw);
1631    }
1632    ScrnRefresh(xw, toprow, leftcol, nrows, ncols, force);
1633}
1634
1635/*
1636 * Sets the rows first though last of the buffer of screen to spaces.
1637 * Requires first <= last; first, last are rows of screen->buf.
1638 */
1639void
1640ClearBufRows(XtermWidget xw,
1641	     int first,
1642	     int last)
1643{
1644    TScreen *screen = &(xw->screen);
1645    unsigned len = (unsigned) MaxCols(screen);
1646    int row;
1647
1648    TRACE(("ClearBufRows %d..%d\n", first, last));
1649    for (row = first; row <= last; row++) {
1650	LineData *ld = getLineData(screen, ROW2INX(screen, row));
1651	if_OPT_DEC_CHRSET({
1652	    /* clearing the whole row resets the doublesize characters */
1653	    SetLineDblCS(ld, CSET_SWL);
1654	});
1655	LineClrWrapped(ld);
1656	ClearCells(xw, 0, len, row, 0);
1657    }
1658}
1659
1660/*
1661  Resizes screen:
1662  1. If new window would have fractional characters, sets window size so as to
1663  discard fractional characters and returns -1.
1664  Minimum screen size is 1 X 1.
1665  Note that this causes another ExposeWindow event.
1666  2. Enlarges screen->buf if necessary.  New space is appended to the bottom
1667  and to the right
1668  3. Reduces  screen->buf if necessary.  Old space is removed from the bottom
1669  and from the right
1670  4. Cursor is positioned as closely to its former position as possible
1671  5. Sets screen->max_row and screen->max_col to reflect new size
1672  6. Maintains the inner border (and clears the border on the screen).
1673  7. Clears origin mode and sets scrolling region to be entire screen.
1674  8. Returns 0
1675  */
1676int
1677ScreenResize(XtermWidget xw,
1678	     int width,
1679	     int height,
1680	     unsigned *flags)
1681{
1682    TScreen *screen = &(xw->screen);
1683    int code, rows, cols;
1684    int border = 2 * screen->border;
1685    int move_down_by = 0;
1686#ifdef TTYSIZE_STRUCT
1687    TTYSIZE_STRUCT ts;
1688#endif
1689    Window tw = VWindow(screen);
1690
1691    TRACE(("ScreenResize %dx%d border %d font %dx%d\n",
1692	   height, width, border,
1693	   FontHeight(screen), FontWidth(screen)));
1694
1695    assert(width > 0);
1696    assert(height > 0);
1697
1698    if (screen->is_running) {
1699	/* clear the right and bottom internal border because of NorthWest
1700	   gravity might have left junk on the right and bottom edges */
1701	if (width >= FullWidth(screen)) {
1702	    XClearArea(screen->display, tw,
1703		       FullWidth(screen), 0,	/* right edge */
1704		       0, (unsigned) height,	/* from top to bottom */
1705		       False);
1706	}
1707	if (height >= FullHeight(screen)) {
1708	    XClearArea(screen->display, tw,
1709		       0, FullHeight(screen),	/* bottom */
1710		       (unsigned) width, 0,	/* all across the bottom */
1711		       False);
1712	}
1713    }
1714
1715    TRACE(("...computing rows/cols: %.2f %.2f\n",
1716	   (double) (height - border) / FontHeight(screen),
1717	   (double) (width - border - ScrollbarWidth(screen)) / FontWidth(screen)));
1718
1719    rows = (height - border) / FontHeight(screen);
1720    cols = (width - border - ScrollbarWidth(screen)) / FontWidth(screen);
1721    if (rows < 1)
1722	rows = 1;
1723    if (cols < 1)
1724	cols = 1;
1725
1726    /* update buffers if the screen has changed size */
1727    if (MaxRows(screen) != rows || MaxCols(screen) != cols) {
1728	int delta_rows = rows - MaxRows(screen);
1729#if OPT_TRACE
1730	int delta_cols = cols - MaxCols(screen);
1731#endif
1732
1733	TRACE(("...ScreenResize chars %dx%d delta %dx%d\n",
1734	       rows, cols, delta_rows, delta_cols));
1735
1736	if (screen->is_running) {
1737#if !OPT_FIFO_LINES
1738	    int savelines = (screen->scrollWidget
1739			     ? screen->savelines
1740			     : 0);
1741#endif
1742	    if (screen->cursor_state)
1743		HideCursor();
1744#if OPT_SAVE_LINES
1745	    /*
1746	     * The non-visible buffer is simple, since we will not copy data
1747	     * to/from the saved-lines.  Do that first.
1748	     */
1749	    if (screen->editBuf_index[!screen->whichBuf]) {
1750		(void) Reallocate(xw,
1751				  &screen->editBuf_index[!screen->whichBuf],
1752				  &screen->editBuf_data[!screen->whichBuf],
1753				  (unsigned) rows,
1754				  (unsigned) cols,
1755				  (unsigned) MaxRows(screen),
1756				  (unsigned) MaxCols(screen));
1757	    }
1758
1759	    /*
1760	     * The save-lines buffer may change width, but will not change its
1761	     * height.  Deal with the cases where we copy data to/from the
1762	     * saved-lines buffer.
1763	     */
1764	    if (GravityIsSouthWest(xw)
1765		&& delta_rows
1766		&& screen->saveBuf_index != 0) {
1767
1768		move_down_by = delta_rows;
1769
1770		if (delta_rows < 0) {
1771		    unsigned move_up = (unsigned) (-delta_rows);
1772		    ScrnBuf dst = screen->saveBuf_index;
1773
1774#if OPT_FIFO_LINES
1775		    int amount = ((MaxRows(screen) - (int) move_up - 1)
1776				  - screen->cur_row);
1777
1778		    if (amount < 0) {
1779			/* move line-data from visible-buffer to save-buffer */
1780			saveEditBufLines(screen, dst, -amount);
1781			move_up = -amount;
1782			move_down_by = amount;
1783		    } else {
1784			move_down_by = 0;
1785		    }
1786#else /* !OPT_FIFO_LINES */
1787		    int amount = screen->savelines - (int) move_up;
1788
1789		    TRACE_SCRNBUF("before save", screen, dst, screen->savelines);
1790
1791		    /* shift lines in save-buffer to make room */
1792		    TRACE(("...%smoving pointers in saveBuf (compare %d %d)\n",
1793			   (amount > 0
1794			    ? ""
1795			    : "SKIP "),
1796			   screen->savelines,
1797			   move_up));
1798		    if (amount > 0) {
1799			SaveLineData(dst, 0, move_up);
1800
1801			MoveLineData(dst,
1802				     0,
1803				     move_up,
1804				     (unsigned) amount);
1805
1806			TRACE(("...reuse %d lines storage in saveBuf\n", move_up));
1807			RestoreLineData(dst,
1808					(unsigned) amount,
1809					move_up);
1810			TRACE_SCRNBUF("restoresave", screen, dst, screen->savelines);
1811		    }
1812
1813		    /* copy line-data from visible-buffer to save-buffer */
1814		    saveEditBufLines(screen, dst, move_up);
1815
1816		    /* after data is copied, reallocate saved-lines */
1817		    (void) Reallocate(xw,
1818				      &screen->saveBuf_index,
1819				      &screen->saveBuf_data,
1820				      (unsigned) savelines,
1821				      (unsigned) cols,
1822				      (unsigned) savelines,
1823				      (unsigned) MaxCols(screen));
1824		    TRACE_SCRNBUF("reallocSAVE",
1825				  screen,
1826				  screen->saveBuf_index,
1827				  savelines);
1828#endif /* OPT_FIFO_LINES */
1829
1830		    /* decrease size of visible-buffer */
1831		    (void) Reallocate(xw,
1832				      &screen->editBuf_index[screen->whichBuf],
1833				      &screen->editBuf_data[screen->whichBuf],
1834				      (unsigned) rows,
1835				      (unsigned) cols,
1836				      (unsigned) MaxRows(screen),
1837				      (unsigned) MaxCols(screen));
1838		    TRACE_SCRNBUF("reallocEDIT",
1839				  screen,
1840				  screen->editBuf_index[screen->whichBuf],
1841				  rows);
1842		} else {
1843		    unsigned move_down = (unsigned) delta_rows;
1844#if OPT_FIFO_LINES
1845		    long unsave_fifo;
1846#else
1847		    ScrnBuf src = screen->saveBuf_index;
1848#endif
1849		    ScrnBuf dst;
1850		    int amount;
1851
1852		    if ((int) move_down > screen->savedlines) {
1853			move_down = (unsigned) screen->savedlines;
1854		    }
1855		    move_down_by = (int) move_down;
1856		    amount = rows - (int) move_down;
1857
1858		    /* increase size of visible-buffer */
1859		    (void) Reallocate(xw,
1860				      &screen->editBuf_index[screen->whichBuf],
1861				      &screen->editBuf_data[screen->whichBuf],
1862				      (unsigned) rows,
1863				      (unsigned) cols,
1864				      (unsigned) MaxRows(screen),
1865				      (unsigned) MaxCols(screen));
1866
1867		    dst = screen->editBuf_index[screen->whichBuf];
1868		    TRACE_SCRNBUF("reallocEDIT", screen, dst, rows);
1869
1870		    TRACE(("...%smoving pointers in editBuf (compare %d %d)\n",
1871			   (amount > 0
1872			    ? ""
1873			    : "SKIP "),
1874			   rows,
1875			   move_down));
1876		    if (amount > 0) {
1877			/* shift lines in visible-buffer to make room */
1878			SaveLineData(dst, (unsigned) amount, (size_t) move_down);
1879
1880			MoveLineData(dst,
1881				     move_down,
1882				     0,
1883				     (unsigned) amount);
1884
1885			TRACE(("...reuse %d lines storage in editBuf\n", move_down));
1886			RestoreLineData(dst,
1887					0,
1888					move_down);
1889
1890			TRACE_SCRNBUF("shifted", screen, dst, rows);
1891		    }
1892
1893		    /* copy line-data from save-buffer to visible-buffer */
1894		    unsaveEditBufLines(screen, dst, move_down);
1895		    TRACE_SCRNBUF("copied", screen, dst, rows);
1896
1897#if OPT_FIFO_LINES
1898		    unsave_fifo = (long) move_down;
1899		    if (screen->saved_fifo < (int) unsave_fifo)
1900			unsave_fifo = screen->saved_fifo;
1901
1902		    /* free up storage in fifo from the copied lines */
1903		    while (unsave_fifo-- > 0) {
1904			deleteScrollback(screen, -1);
1905			screen->saved_fifo--;
1906		    }
1907#else
1908		    amount = (screen->savelines - (int) move_down);
1909		    TRACE(("...%smoving pointers in saveBuf (compare %d %d)\n",
1910			   (amount > 0
1911			    ? ""
1912			    : "SKIP "),
1913			   rows,
1914			   move_down));
1915		    if (amount > 0) {
1916			/* shift lines in save-buffer to account for copy */
1917			src = screen->saveBuf_index;
1918			SaveLineData(src, amount, move_down);
1919
1920			MoveLineData(src,
1921				     move_down,
1922				     0,
1923				     (unsigned) amount);
1924
1925			TRACE(("...reuse %d lines storage in saveBuf\n", move_down));
1926			RestoreLineData(src,
1927					0,
1928					move_down);
1929		    }
1930#endif
1931
1932		    /* recover storage in save-buffer */
1933		}
1934	    } else {
1935#if !OPT_FIFO_LINES
1936		(void) Reallocate(xw,
1937				  &screen->saveBuf_index,
1938				  &screen->saveBuf_data,
1939				  (unsigned) savelines,
1940				  (unsigned) cols,
1941				  (unsigned) savelines,
1942				  (unsigned) MaxCols(screen));
1943#endif
1944		(void) Reallocate(xw,
1945				  &screen->editBuf_index[screen->whichBuf],
1946				  &screen->editBuf_data[screen->whichBuf],
1947				  (unsigned) rows,
1948				  (unsigned) cols,
1949				  (unsigned) MaxRows(screen),
1950				  (unsigned) MaxCols(screen));
1951	    }
1952#else /* !OPT_SAVE_LINES */
1953	    if (screen->whichBuf
1954		&& GravityIsSouthWest(xw))
1955		/* swap buffer pointers back to make this work */
1956		SwitchBufPtrs(screen);
1957	    if (screen->editBuf_index[1])
1958		(void) Reallocate(xw,
1959				  &screen->editBuf_index[1],
1960				  &screen->editBuf_data[1],
1961				  (unsigned) rows,
1962				  (unsigned) cols,
1963				  (unsigned) MaxRows(screen),
1964				  (unsigned) MaxCols(screen));
1965	    move_down_by = Reallocate(xw,
1966				      &screen->saveBuf_index,
1967				      &screen->saveBuf_data,
1968				      (unsigned) (rows + savelines),
1969				      (unsigned) cols,
1970				      (unsigned) (MaxRows(screen) + savelines),
1971				      (unsigned) MaxCols(screen));
1972#endif /* OPT_SAVE_LINES */
1973	    screen->visbuf = VisBuf(screen);
1974	}
1975
1976	AdjustSavedCursor(xw, move_down_by);
1977	set_max_row(screen, screen->max_row + delta_rows);
1978	set_max_col(screen, cols - 1);
1979
1980	if (screen->is_running) {
1981	    if (GravityIsSouthWest(xw)) {
1982		screen->savedlines -= move_down_by;
1983		if (screen->savedlines < 0)
1984		    screen->savedlines = 0;
1985		if (screen->savedlines > screen->savelines)
1986		    screen->savedlines = screen->savelines;
1987		if (screen->topline < -screen->savedlines)
1988		    screen->topline = -screen->savedlines;
1989		set_cur_row(screen, screen->cur_row + move_down_by);
1990		screen->cursorp.row += move_down_by;
1991		ScrollSelection(screen, move_down_by, True);
1992
1993		if (screen->whichBuf)
1994		    SwitchBufPtrs(screen);	/* put the pointers back */
1995	    }
1996	}
1997
1998	/* adjust scrolling region */
1999	set_tb_margins(screen, 0, screen->max_row);
2000	*flags &= ~ORIGIN;
2001
2002	if (screen->cur_row > screen->max_row)
2003	    set_cur_row(screen, screen->max_row);
2004	if (screen->cur_col > screen->max_col)
2005	    set_cur_col(screen, screen->max_col);
2006
2007	screen->fullVwin.height = height - border;
2008	screen->fullVwin.width = width - border - screen->fullVwin.sb_info.width;
2009
2010    } else if (FullHeight(screen) == height && FullWidth(screen) == width)
2011	return (0);		/* nothing has changed at all */
2012
2013    screen->fullVwin.fullheight = (Dimension) height;
2014    screen->fullVwin.fullwidth = (Dimension) width;
2015
2016    ResizeScrollBar(xw);
2017    ResizeSelection(screen, rows, cols);
2018
2019#ifndef NO_ACTIVE_ICON
2020    if (screen->iconVwin.window) {
2021	XWindowChanges changes;
2022	screen->iconVwin.width =
2023	    MaxCols(screen) * screen->iconVwin.f_width;
2024
2025	screen->iconVwin.height =
2026	    MaxRows(screen) * screen->iconVwin.f_height;
2027
2028	changes.width = screen->iconVwin.fullwidth =
2029	    (Dimension) (screen->iconVwin.width + 2 * xw->misc.icon_border_width);
2030	changes.height = screen->iconVwin.fullheight =
2031	    (Dimension) (screen->iconVwin.height + 2 * xw->misc.icon_border_width);
2032	changes.border_width = (int) xw->misc.icon_border_width;
2033
2034	TRACE(("resizing icon window %dx%d\n", changes.height, changes.width));
2035	XConfigureWindow(XtDisplay(xw), screen->iconVwin.window,
2036			 CWWidth | CWHeight | CWBorderWidth, &changes);
2037    }
2038#endif /* NO_ACTIVE_ICON */
2039
2040#ifdef TTYSIZE_STRUCT
2041    /* Set tty's idea of window size */
2042    TTYSIZE_ROWS(ts) = rows;
2043    TTYSIZE_COLS(ts) = cols;
2044#ifdef USE_STRUCT_WINSIZE
2045    ts.ws_xpixel = width;
2046    ts.ws_ypixel = height;
2047#endif
2048    code = SET_TTYSIZE(screen->respond, ts);
2049    TRACE(("return %d from SET_TTYSIZE %dx%d\n", code, rows, cols));
2050    (void) code;
2051
2052#if defined(SIGWINCH) && defined(USE_STRUCT_TTYSIZE)
2053    if (screen->pid > 1) {
2054	int pgrp;
2055
2056	TRACE(("getting process-group\n"));
2057	if (ioctl(screen->respond, TIOCGPGRP, &pgrp) != -1) {
2058	    TRACE(("sending SIGWINCH to process group %d\n", pgrp));
2059	    kill_process_group(pgrp, SIGWINCH);
2060	}
2061    }
2062#endif /* SIGWINCH */
2063
2064#else
2065    TRACE(("ScreenResize cannot do anything to pty\n"));
2066#endif /* TTYSIZE_STRUCT */
2067    return (0);
2068}
2069
2070/*
2071 * Return true if any character cell starting at [row,col], for len-cells is
2072 * nonnull.
2073 */
2074Bool
2075non_blank_line(TScreen * screen,
2076	       int row,
2077	       int col,
2078	       int len)
2079{
2080    int i;
2081    Bool found = False;
2082    LineData *ld = getLineData(screen, row);
2083
2084    if (ld != 0) {
2085	for (i = col; i < len; i++) {
2086	    if (ld->charData[i]) {
2087		found = True;
2088		break;
2089	    }
2090	}
2091    }
2092    return found;
2093}
2094
2095/*
2096 * Rectangle parameters start from one.
2097 */
2098#define minRectRow(screen) (getMinRow(screen) + 1)
2099#define minRectCol(screen) (getMinCol(screen) + 1)
2100#define maxRectRow(screen) (getMaxRow(screen) + 1)
2101#define maxRectCol(screen) (getMaxCol(screen) + 1)
2102
2103static int
2104limitedParseRow(XtermWidget xw, TScreen * screen, int row)
2105{
2106    int min_row = minRectRow(screen);
2107    int max_row = maxRectRow(screen);
2108
2109    if (row < min_row)
2110	row = min_row;
2111    else if (row > max_row)
2112	row = max_row;
2113    return row;
2114}
2115
2116static int
2117limitedParseCol(XtermWidget xw, TScreen * screen, int col)
2118{
2119    int min_col = minRectCol(screen);
2120    int max_col = maxRectCol(screen);
2121
2122    (void) xw;
2123    if (col < min_col)
2124	col = min_col;
2125    else if (col > max_col)
2126	col = max_col;
2127    return col;
2128}
2129
2130#define LimitedParse(num, func, dft) \
2131	func(xw, screen, (nparams > num) ? params[num] : dft)
2132
2133/*
2134 * Copy the rectangle boundaries into a struct, providing default values as
2135 * needed.
2136 */
2137void
2138xtermParseRect(XtermWidget xw, int nparams, int *params, XTermRect * target)
2139{
2140    TScreen *screen = &(xw->screen);
2141
2142    memset(target, 0, sizeof(*target));
2143    target->top = LimitedParse(0, limitedParseRow, minRectRow(screen));
2144    target->left = LimitedParse(1, limitedParseCol, minRectCol(screen));
2145    target->bottom = LimitedParse(2, limitedParseRow, maxRectRow(screen));
2146    target->right = LimitedParse(3, limitedParseCol, maxRectCol(screen));
2147    TRACE(("parsed rectangle %d,%d %d,%d\n",
2148	   target->top,
2149	   target->left,
2150	   target->bottom,
2151	   target->right));
2152}
2153
2154static Bool
2155validRect(XtermWidget xw, XTermRect * target)
2156{
2157    TScreen *screen = &(xw->screen);
2158
2159    TRACE(("comparing against screensize %dx%d\n",
2160	   maxRectRow(screen),
2161	   maxRectCol(screen)));
2162    return (target != 0
2163	    && target->top >= minRectRow(screen)
2164	    && target->left >= minRectCol(screen)
2165	    && target->top <= target->bottom
2166	    && target->left <= target->right
2167	    && target->top <= maxRectRow(screen)
2168	    && target->right <= maxRectCol(screen));
2169}
2170
2171/*
2172 * Fills a rectangle with the given 8-bit character and video-attributes.
2173 * Colors and double-size attribute are unmodified.
2174 */
2175void
2176ScrnFillRectangle(XtermWidget xw,
2177		  XTermRect * target,
2178		  int value,
2179		  unsigned flags,
2180		  Bool keepColors)
2181{
2182    TScreen *screen = &(xw->screen);
2183
2184    TRACE(("filling rectangle with '%c' flags %#x\n", value, flags));
2185    if (validRect(xw, target)) {
2186	LineData *ld;
2187	unsigned left = (unsigned) (target->left - 1);
2188	unsigned size = (unsigned) (target->right - (int) left);
2189	unsigned attrs = flags;
2190	int row, col;
2191
2192	(void) size;
2193
2194	attrs &= ATTRIBUTES;
2195	attrs |= CHARDRAWN;
2196	for (row = target->bottom - 1; row >= (target->top - 1); row--) {
2197	    ld = getLineData(screen, row);
2198
2199	    TRACE(("filling %d [%d..%d]\n", row, left, left + size));
2200
2201	    /*
2202	     * Fill attributes, preserving "protected" flag, as well as
2203	     * colors if asked.
2204	     */
2205	    for (col = (int) left; col < target->right; ++col) {
2206		unsigned temp = ld->attribs[col];
2207
2208		if (!keepColors) {
2209		    temp &= ~(FG_COLOR | BG_COLOR);
2210		}
2211		temp = attrs | (temp & (FG_COLOR | BG_COLOR | PROTECTED));
2212		temp |= CHARDRAWN;
2213		ld->attribs[col] = (Char) temp;
2214#if OPT_ISO_COLORS
2215		if (attrs & (FG_COLOR | BG_COLOR)) {
2216		    if_OPT_ISO_COLORS(screen, {
2217			ld->color[col] = xtermColorPair(xw);
2218		    });
2219		}
2220#endif
2221	    }
2222
2223	    for (col = (int) left; col < target->right; ++col)
2224		ld->charData[col] = (CharData) value;
2225
2226	    if_OPT_WIDE_CHARS(screen, {
2227		size_t off;
2228		for_each_combData(off, ld) {
2229		    memset(ld->combData[off] + left, 0, size * sizeof(IChar));
2230		}
2231	    })
2232	}
2233	ScrnUpdate(xw,
2234		   target->top - 1,
2235		   target->left - 1,
2236		   (target->bottom - target->top) + 1,
2237		   (target->right - target->left) + 1,
2238		   False);
2239    }
2240}
2241
2242#if OPT_DEC_RECTOPS
2243/*
2244 * Copies the source rectangle to the target location, including video
2245 * attributes.
2246 *
2247 * This implementation ignores page numbers.
2248 *
2249 * The reference manual does not indicate if it handles overlapping copy
2250 * properly - so we make a local copy of the source rectangle first, then apply
2251 * the target from that.
2252 */
2253void
2254ScrnCopyRectangle(XtermWidget xw, XTermRect * source, int nparam, int *params)
2255{
2256    TScreen *screen = &(xw->screen);
2257
2258    TRACE(("copying rectangle\n"));
2259
2260    if (validRect(xw, source)) {
2261	XTermRect target;
2262	xtermParseRect(xw,
2263		       ((nparam > 3) ? 2 : (nparam - 1)),
2264		       params,
2265		       &target);
2266	if (validRect(xw, &target)) {
2267	    Cardinal high = (Cardinal) (source->bottom - source->top) + 1;
2268	    Cardinal wide = (Cardinal) (source->right - source->left) + 1;
2269	    Cardinal size = (high * wide);
2270	    int row, col;
2271	    Cardinal j, k;
2272	    LineData *ld;
2273
2274	    CellData *cells = newCellData(xw, size);
2275
2276	    if (cells != 0) {
2277
2278		TRACE(("OK - make copy %dx%d\n", high, wide));
2279		target.bottom = target.top + (int) (high - 1);
2280		target.right = target.left + (int) (wide - 1);
2281
2282		for (row = source->top - 1; row < source->bottom; ++row) {
2283		    ld = getLineData(screen, row);
2284		    j = (Cardinal) (row - (source->top - 1));
2285		    for (col = source->left - 1; col < source->right; ++col) {
2286			k = (Cardinal) (col - (source->left - 1));
2287			saveCellData(screen, cells,
2288				     (j * wide) + k,
2289				     ld, col);
2290		    }
2291		}
2292		for (row = target.top - 1; row < target.bottom; ++row) {
2293		    ld = getLineData(screen, row);
2294		    j = (Cardinal) (row - (target.top - 1));
2295		    for (col = target.left - 1; col < target.right; ++col) {
2296			k = (Cardinal) (col - (target.left - 1));
2297			if (row >= getMinRow(screen)
2298			    && row <= getMaxRow(screen)
2299			    && col >= getMinCol(screen)
2300			    && col <= getMaxCol(screen)) {
2301			    if (j < high && k < wide) {
2302				restoreCellData(screen, cells,
2303						(j * wide) + k,
2304						ld, col);
2305			    } else {
2306				/* FIXME - clear the target cell? */
2307			    }
2308			    ld->attribs[col] |= CHARDRAWN;
2309			}
2310		    }
2311		}
2312		free(cells);
2313
2314		ScrnUpdate(xw,
2315			   (target.top - 1),
2316			   (target.left - 1),
2317			   (target.bottom - target.top) + 1,
2318			   ((target.right - target.left) + 1),
2319			   False);
2320	    }
2321	}
2322    }
2323}
2324
2325/*
2326 * Modifies the video-attributes only - so selection (not a video attribute) is
2327 * unaffected.  Colors and double-size flags are unaffected as well.
2328 *
2329 * FIXME: our representation for "invisible" does not work with this operation,
2330 * since the attribute byte is fully-allocated for other flags.  The logic
2331 * is shown for INVISIBLE because it's harmless, and useful in case the
2332 * CHARDRAWN or PROTECTED flags are reassigned.
2333 */
2334void
2335ScrnMarkRectangle(XtermWidget xw,
2336		  XTermRect * target,
2337		  Bool reverse,
2338		  int nparam,
2339		  int *params)
2340{
2341    TScreen *screen = &(xw->screen);
2342    Bool exact = (screen->cur_decsace == 2);
2343
2344    TRACE(("%s %s\n",
2345	   reverse ? "reversing" : "marking",
2346	   (exact
2347	    ? "rectangle"
2348	    : "region")));
2349
2350    if (validRect(xw, target)) {
2351	LineData *ld;
2352	int top = target->top - 1;
2353	int bottom = target->bottom - 1;
2354	int row, col;
2355	int n;
2356
2357	for (row = top; row <= bottom; ++row) {
2358	    int left = ((exact || (row == top))
2359			? (target->left - 1)
2360			: getMinCol(screen));
2361	    int right = ((exact || (row == bottom))
2362			 ? (target->right - 1)
2363			 : getMaxCol(screen));
2364
2365	    ld = getLineData(screen, row);
2366
2367	    TRACE(("marking %d [%d..%d]\n", row, left, right));
2368	    for (col = left; col <= right; ++col) {
2369		unsigned flags = ld->attribs[col];
2370
2371		for (n = 0; n < nparam; ++n) {
2372#if OPT_TRACE
2373		    if (row == top && col == left)
2374			TRACE(("attr param[%d] %d\n", n + 1, params[n]));
2375#endif
2376		    if (reverse) {
2377			switch (params[n]) {
2378			case 1:
2379			    flags ^= BOLD;
2380			    break;
2381			case 4:
2382			    flags ^= UNDERLINE;
2383			    break;
2384			case 5:
2385			    flags ^= BLINK;
2386			    break;
2387			case 7:
2388			    flags ^= INVERSE;
2389			    break;
2390			case 8:
2391			    flags ^= INVISIBLE;
2392			    break;
2393			}
2394		    } else {
2395			switch (params[n]) {
2396			case 0:
2397			    flags &= ~SGR_MASK;
2398			    break;
2399			case 1:
2400			    flags |= BOLD;
2401			    break;
2402			case 4:
2403			    flags |= UNDERLINE;
2404			    break;
2405			case 5:
2406			    flags |= BLINK;
2407			    break;
2408			case 7:
2409			    flags |= INVERSE;
2410			    break;
2411			case 8:
2412			    flags |= INVISIBLE;
2413			    break;
2414			case 22:
2415			    flags &= ~BOLD;
2416			    break;
2417			case 24:
2418			    flags &= ~UNDERLINE;
2419			    break;
2420			case 25:
2421			    flags &= ~BLINK;
2422			    break;
2423			case 27:
2424			    flags &= ~INVERSE;
2425			    break;
2426			case 28:
2427			    flags &= ~INVISIBLE;
2428			    break;
2429			}
2430		    }
2431		}
2432#if OPT_TRACE
2433		if (row == top && col == left)
2434		    TRACE(("first mask-change is %#x\n",
2435			   ld->attribs[col] ^ flags));
2436#endif
2437		ld->attribs[col] = (Char) flags;
2438	    }
2439	}
2440	ScrnRefresh(xw,
2441		    (target->top - 1),
2442		    (exact ? (target->left - 1) : getMinCol(screen)),
2443		    (target->bottom - target->top) + 1,
2444		    (exact
2445		     ? ((target->right - target->left) + 1)
2446		     : (getMaxCol(screen) - getMinCol(screen) + 1)),
2447		    False);
2448    }
2449}
2450
2451/*
2452 * Resets characters to space, except where prohibited by DECSCA.  Video
2453 * attributes (including color) are untouched.
2454 */
2455void
2456ScrnWipeRectangle(XtermWidget xw,
2457		  XTermRect * target)
2458{
2459    TScreen *screen = &(xw->screen);
2460
2461    TRACE(("wiping rectangle\n"));
2462
2463    if (validRect(xw, target)) {
2464	LineData *ld;
2465	int top = target->top - 1;
2466	int bottom = target->bottom - 1;
2467	int row, col;
2468
2469	for (row = top; row <= bottom; ++row) {
2470	    int left = (target->left - 1);
2471	    int right = (target->right - 1);
2472
2473	    TRACE(("wiping %d [%d..%d]\n", row, left, right));
2474
2475	    ld = getLineData(screen, row);
2476	    for (col = left; col <= right; ++col) {
2477		if (!((screen->protected_mode == DEC_PROTECT)
2478		      && (ld->attribs[col] & PROTECTED))) {
2479		    ld->attribs[col] |= CHARDRAWN;
2480		    ld->charData[col] = ' ';
2481		    if_OPT_WIDE_CHARS(screen, {
2482			size_t off;
2483			for_each_combData(off, ld) {
2484			    ld->combData[off][col] = '\0';
2485			}
2486		    })
2487		}
2488	    }
2489	}
2490	ScrnUpdate(xw,
2491		   (target->top - 1),
2492		   (target->left - 1),
2493		   (target->bottom - target->top) + 1,
2494		   ((target->right - target->left) + 1),
2495		   False);
2496    }
2497}
2498#endif /* OPT_DEC_RECTOPS */
2499