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