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