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