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