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