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