screen.c revision d522f475
1/* $XTermId: screen.c,v 1.241 2008/04/20 21:07:10 tom Exp $ */
2
3/*
4 * Copyright 1999-2007,2008 by Thomas E. Dickey
5 *
6 *                         All Rights Reserved
7 *
8 * Permission is hereby granted, free of charge, to any person obtaining a
9 * copy of this software and associated documentation files (the
10 * "Software"), to deal in the Software without restriction, including
11 * without limitation the rights to use, copy, modify, merge, publish,
12 * distribute, sublicense, and/or sell copies of the Software, and to
13 * permit persons to whom the Software is furnished to do so, subject to
14 * the following conditions:
15 *
16 * The above copyright notice and this permission notice shall be included
17 * in all copies or substantial portions of the Software.
18 *
19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
20 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
21 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
22 * IN NO EVENT SHALL THE ABOVE LISTED COPYRIGHT HOLDER(S) BE LIABLE FOR ANY
23 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
24 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
25 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
26 *
27 * Except as contained in this notice, the name(s) of the above copyright
28 * holders shall not be used in advertising or otherwise to promote the
29 * sale, use or other dealings in this Software without prior written
30 * authorization.
31 *
32 *
33 * Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts.
34 *
35 *                         All Rights Reserved
36 *
37 * Permission to use, copy, modify, and distribute this software and its
38 * documentation for any purpose and without fee is hereby granted,
39 * provided that the above copyright notice appear in all copies and that
40 * both that copyright notice and this permission notice appear in
41 * supporting documentation, and that the name of Digital Equipment
42 * Corporation not be used in advertising or publicity pertaining to
43 * distribution of the software without specific, written prior permission.
44 *
45 *
46 * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
47 * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
48 * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
49 * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
50 * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
51 * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
52 * SOFTWARE.
53 */
54
55/* screen.c */
56
57#include <stdio.h>
58#include <xterm.h>
59#include <error.h>
60#include <data.h>
61#include <xcharmouse.h>
62#include <xterm_io.h>
63
64#if OPT_WIDE_CHARS
65#include <fontutils.h>
66#include <menu.h>
67#endif
68
69#include <assert.h>
70#include <signal.h>
71
72#define getMinRow(screen) ((xw->flags & ORIGIN) ? (screen)->top_marg : 0)
73#define getMaxRow(screen) ((xw->flags & ORIGIN) ? (screen)->bot_marg : (screen)->max_row)
74#define getMinCol(screen) 0
75#define getMaxCol(screen) ((screen)->max_col)
76
77/*
78 * Allocates memory for a 2-dimensional array of chars and returns a pointer
79 * thereto.  Each line is formed from a set of char arrays, with an index
80 * (i.e., the ScrnBuf type).  The first pointer in the index is reserved for
81 * per-line flags, and does not point to data.
82 *
83 * After the per-line flags, we have a series of pointers to char arrays:  The
84 * first one is the actual character array, the second one is the attributes,
85 * the third is the foreground and background colors, and the fourth denotes
86 * the character set.
87 *
88 * We store it all as pointers, because of alignment considerations, together
89 * with the intention of being able to change the total number of pointers per
90 * row according to whether the user wants color or not.
91 */
92ScrnBuf
93Allocate(int nrow, int ncol, Char ** addr)
94{
95    ScrnBuf base;
96    Char *tmp;
97    int i, j, k;
98    size_t entries = MAX_PTRS * nrow;
99    size_t length = BUF_PTRS * nrow * ncol;
100
101    if ((base = TypeCallocN(ScrnPtr, entries)) == 0)
102	SysError(ERROR_SCALLOC);
103
104    if ((tmp = TypeCallocN(Char, length)) == 0)
105	SysError(ERROR_SCALLOC2);
106
107    *addr = tmp;
108    for (i = k = 0; i < nrow; i++) {
109	base[k] = 0;		/* per-line flags */
110	k += BUF_HEAD;
111	for (j = BUF_HEAD; j < MAX_PTRS; j++) {
112	    base[k++] = tmp;
113	    tmp += ncol;
114	}
115    }
116
117    return (base);
118}
119
120/*
121 *  This is called when the screen is resized.
122 *  Returns the number of lines the text was moved down (neg for up).
123 *  (Return value only necessary with SouthWestGravity.)
124 */
125static int
126Reallocate(XtermWidget xw,
127	   ScrnBuf * sbuf,
128	   Char ** sbufaddr,
129	   int nrow,
130	   int ncol,
131	   int oldrow,
132	   int oldcol)
133{
134    ScrnBuf base;
135    Char *tmp;
136    int i, j, k, minrows;
137    size_t mincols;
138    Char *oldbuf;
139    int move_down = 0, move_up = 0;
140    size_t entries = MAX_PTRS * nrow;
141    size_t length = BUF_PTRS * nrow * ncol;
142
143    if (sbuf == NULL || *sbuf == NULL) {
144	return 0;
145    }
146
147    oldbuf = *sbufaddr;
148
149    /*
150     * Special case if oldcol == ncol - straight forward realloc and
151     * update of the additional lines in sbuf
152     *
153     * FIXME: this is a good idea, but doesn't seem to be implemented.
154     * -gildea
155     */
156
157    /*
158     * realloc sbuf, the pointers to all the lines.
159     * If the screen shrinks, remove lines off the top of the buffer
160     * if resizeGravity resource says to do so.
161     */
162    if (nrow < oldrow
163	&& xw->misc.resizeGravity == SouthWestGravity) {
164	/* Remove lines off the top of the buffer if necessary. */
165	move_up = (oldrow - nrow)
166	    - (xw->screen.max_row - xw->screen.cur_row);
167	if (move_up < 0)
168	    move_up = 0;
169	/* Overlapping memmove here! */
170	memmove(*sbuf, *sbuf + (move_up * MAX_PTRS),
171		MAX_PTRS * (oldrow - move_up) * sizeof((*sbuf)[0]));
172    }
173    *sbuf = TypeRealloc(ScrnPtr, entries, *sbuf);
174    if (*sbuf == 0)
175	SysError(ERROR_RESIZE);
176    base = *sbuf;
177
178    /*
179     *  create the new buffer space and copy old buffer contents there
180     *  line by line.
181     */
182    if ((tmp = TypeCallocN(Char, length)) == 0)
183	SysError(ERROR_SREALLOC);
184    *sbufaddr = tmp;
185    minrows = (oldrow < nrow) ? oldrow : nrow;
186    mincols = (oldcol < ncol) ? oldcol : ncol;
187    if (nrow > oldrow
188	&& xw->misc.resizeGravity == SouthWestGravity) {
189	/* move data down to bottom of expanded screen */
190	move_down = Min(nrow - oldrow, xw->screen.savedlines);
191	tmp += (ncol * move_down * BUF_PTRS);
192    }
193
194    for (i = k = 0; i < minrows; i++) {
195	k += BUF_HEAD;
196	for (j = BUF_HEAD; j < MAX_PTRS; j++) {
197	    memcpy(tmp, base[k++], mincols);
198	    tmp += ncol;
199	}
200    }
201
202    /*
203     * update the pointers in sbuf
204     */
205    for (i = k = 0, tmp = *sbufaddr; i < nrow; i++) {
206	for (j = 0; j < BUF_HEAD; j++)
207	    base[k++] = 0;
208	for (j = BUF_HEAD; j < MAX_PTRS; j++) {
209	    base[k++] = tmp;
210	    tmp += ncol;
211	}
212    }
213
214    /* Now free the old buffer */
215    free(oldbuf);
216
217    return move_down ? move_down : -move_up;	/* convert to rows */
218}
219
220#if OPT_WIDE_CHARS
221#if 0
222static void
223dump_screen(const char *tag,
224	    XtermWidget xw,
225	    ScrnBuf sbuf,
226	    Char * sbufaddr,
227	    unsigned nrow,
228	    unsigned ncol)
229{
230    unsigned y, x;
231
232    TRACE(("DUMP %s, ptrs %d\n", tag, xw->num_ptrs));
233    TRACE(("  sbuf      %p\n", sbuf));
234    TRACE(("  sbufaddr  %p\n", sbufaddr));
235    TRACE(("  nrow      %d\n", nrow));
236    TRACE(("  ncol      %d\n", ncol));
237
238    for (y = 0; y < nrow; ++y) {
239	ScrnPtr ptr = BUF_CHARS(sbuf, y);
240	TRACE(("%3d:%p:", y, ptr));
241	for (x = 0; x < ncol; ++x) {
242	    Char c = ptr[x];
243	    if (c == 0)
244		c = '~';
245	    TRACE(("%c", c));
246	}
247	TRACE(("\n"));
248    }
249}
250#else
251#define dump_screen(tag, xw, sbuf, sbufaddr, nrow, ncol)	/* nothing */
252#endif
253
254/*
255 * This function reallocates memory if changing the number of Buf offsets.
256 * The code is based on Reallocate().
257 */
258static void
259ReallocateBufOffsets(XtermWidget xw,
260		     ScrnBuf * sbuf,
261		     Char ** sbufaddr,
262		     unsigned nrow,
263		     unsigned ncol,
264		     size_t new_max_offsets)
265{
266    unsigned i;
267    int j, k;
268    ScrnBuf base;
269    Char *oldbuf, *tmp;
270    size_t entries, length;
271    /*
272     * As there are 2 buffers (allbuf, altbuf), we cannot change num_ptrs in
273     * this function.  However MAX_PTRS and BUF_PTRS depend on num_ptrs so
274     * change it now and restore the value when done.
275     */
276    int old_max_ptrs = MAX_PTRS;
277
278    assert(nrow != 0);
279    assert(ncol != 0);
280    assert(new_max_offsets != 0);
281
282    dump_screen("before", xw, *sbuf, *sbufaddr, nrow, ncol);
283
284    xw->num_ptrs = new_max_offsets;
285
286    entries = MAX_PTRS * nrow;
287    length = BUF_PTRS * nrow * ncol;
288    oldbuf = *sbufaddr;
289
290    *sbuf = TypeRealloc(ScrnPtr, entries, *sbuf);
291    if (*sbuf == 0)
292	SysError(ERROR_RESIZE);
293    base = *sbuf;
294
295    if ((tmp = TypeCallocN(Char, length)) == 0)
296	SysError(ERROR_SREALLOC);
297    *sbufaddr = tmp;
298
299    for (i = k = 0; i < nrow; i++) {
300	k += BUF_HEAD;
301	for (j = BUF_HEAD; j < old_max_ptrs; j++) {
302	    memcpy(tmp, base[k++], ncol);
303	    tmp += ncol;
304	}
305	tmp += ncol * (new_max_offsets - old_max_ptrs);
306    }
307
308    /*
309     * update the pointers in sbuf
310     */
311    for (i = k = 0, tmp = *sbufaddr; i < nrow; i++) {
312	for (j = 0; j < BUF_HEAD; j++)
313	    base[k++] = 0;
314	for (j = BUF_HEAD; j < MAX_PTRS; j++) {
315	    base[k++] = tmp;
316	    tmp += ncol;
317	}
318    }
319
320    /* Now free the old buffer and restore num_ptrs */
321    free(oldbuf);
322    dump_screen("after", xw, *sbuf, *sbufaddr, nrow, ncol);
323
324    xw->num_ptrs = old_max_ptrs;
325}
326
327/*
328 * This function dynamically adds support for wide-characters.
329 */
330void
331ChangeToWide(XtermWidget xw)
332{
333    TScreen *screen = &(xw->screen);
334    unsigned new_bufoffset = OFF_FINAL + (screen->max_combining * 2);
335    int savelines = screen->scrollWidget ? screen->savelines : 0;
336
337    if (screen->wide_chars)
338	return;
339
340    TRACE(("ChangeToWide\n"));
341    if (xtermLoadWideFonts(xw, True)) {
342	if (savelines < 0)
343	    savelines = 0;
344
345	/*
346	 * If we're displaying the alternate screen, switch the pointers back
347	 * temporarily so ReallocateBufOffsets() will operate on the proper
348	 * data in altbuf.
349	 */
350	if (screen->alternate)
351	    SwitchBufPtrs(screen);
352
353	ReallocateBufOffsets(xw,
354			     &screen->allbuf, &screen->sbuf_address,
355			     (unsigned) (MaxRows(screen) + savelines),
356			     (unsigned) MaxCols(screen),
357			     new_bufoffset);
358	if (screen->altbuf) {
359	    ReallocateBufOffsets(xw,
360				 &screen->altbuf, &screen->abuf_address,
361				 (unsigned) MaxRows(screen),
362				 (unsigned) MaxCols(screen),
363				 new_bufoffset);
364	}
365
366	screen->wide_chars = True;
367	xw->num_ptrs = new_bufoffset;
368	screen->visbuf = &screen->allbuf[MAX_PTRS * savelines];
369
370	/*
371	 * Switch the pointers back before we start painting on the screen.
372	 */
373	if (screen->alternate)
374	    SwitchBufPtrs(screen);
375
376	update_font_utf8_mode();
377	SetVTFont(xw, screen->menu_font_number, True, NULL);
378    }
379    TRACE(("...ChangeToWide\n"));
380}
381#endif
382
383/*
384 * Clear cells, no side-effects.
385 */
386void
387ClearCells(XtermWidget xw, int flags, unsigned len, int row, int col)
388{
389    if (len != 0) {
390	TScreen *screen = &(xw->screen);
391	flags |= TERM_COLOR_FLAGS(xw);
392
393	memset(SCRN_BUF_CHARS(screen, row) + col, ' ', len);
394	memset(SCRN_BUF_ATTRS(screen, row) + col, flags, len);
395
396	if_OPT_EXT_COLORS(screen, {
397	    memset(SCRN_BUF_FGRND(screen, row) + col,
398		   xw->sgr_foreground, len);
399	    memset(SCRN_BUF_BGRND(screen, row) + col,
400		   xw->cur_background, len);
401	});
402	if_OPT_ISO_TRADITIONAL_COLORS(screen, {
403	    memset(SCRN_BUF_COLOR(screen, row) + col,
404		   (int) xtermColorPair(xw), len);
405	});
406	if_OPT_DEC_CHRSET({
407	    memset(SCRN_BUF_CSETS(screen, row) + col,
408		   curXtermChrSet(xw, row), len);
409	});
410	if_OPT_WIDE_CHARS(screen, {
411	    int off;
412	    for (off = OFF_WIDEC; off < MAX_PTRS; ++off) {
413		memset(SCREEN_PTR(screen, row, off) + col, 0, len);
414	    }
415	});
416    }
417}
418
419/*
420 * Clear data in the screen-structure (no I/O).
421 * Check for wide-character damage as well, clearing the damaged cells.
422 */
423void
424ScrnClearCells(XtermWidget xw, int row, int col, unsigned len)
425{
426#if OPT_WIDE_CHARS
427    TScreen *screen = &(xw->screen);
428#endif
429    int flags = 0;
430
431    if_OPT_WIDE_CHARS(screen, {
432	int kl;
433	int kr;
434	if (DamagedCells(screen, len, &kl, &kr, INX2ROW(screen, row), col)
435	    && kr >= kl) {
436	    ClearCells(xw, flags, (unsigned) (kr - kl + 1), row, kl);
437	}
438    });
439    ClearCells(xw, flags, len, row, col);
440}
441
442/*
443 * Disown the selection and repaint the area that is highlighted so it is no
444 * longer highlighted.
445 */
446void
447ScrnDisownSelection(XtermWidget xw)
448{
449    if (ScrnHaveSelection(&(xw->screen))) {
450	if (xw->screen.keepSelection) {
451	    UnhiliteSelection(xw);
452	} else {
453	    DisownSelection(xw);
454	}
455    }
456}
457
458/*
459 * Writes str into buf at screen's current row and column.  Characters are set
460 * to match flags.
461 */
462void
463ScrnWriteText(XtermWidget xw,
464	      PAIRED_CHARS(Char * str, Char * str2),
465	      unsigned flags,
466	      unsigned cur_fg_bg,
467	      unsigned length)
468{
469    TScreen *screen = &(xw->screen);
470#if OPT_ISO_COLORS
471#if OPT_EXT_COLORS
472    Char *fbf = 0;
473    Char *fbb = 0;
474#else
475    Char *fb = 0;
476#endif
477#endif
478#if OPT_DEC_CHRSET
479    Char *cb = 0;
480#endif
481    Char *attrs;
482    int avail = MaxCols(screen) - screen->cur_col;
483    Char *chars;
484#if OPT_WIDE_CHARS
485    Char starcol1, starcol2;
486#endif
487    unsigned real_width = visual_width(PAIRED_CHARS(str, str2), length);
488
489    (void) cur_fg_bg;
490
491    if (avail <= 0)
492	return;
493    if (length > (unsigned) avail)
494	length = avail;
495    if (length == 0 || real_width == 0)
496	return;
497
498    chars = SCRN_BUF_CHARS(screen, screen->cur_row) + screen->cur_col;
499    attrs = SCRN_BUF_ATTRS(screen, screen->cur_row) + screen->cur_col;
500
501    if_OPT_EXT_COLORS(screen, {
502	fbf = SCRN_BUF_FGRND(screen, screen->cur_row) + screen->cur_col;
503	fbb = SCRN_BUF_BGRND(screen, screen->cur_row) + screen->cur_col;
504    });
505    if_OPT_ISO_TRADITIONAL_COLORS(screen, {
506	fb = SCRN_BUF_COLOR(screen, screen->cur_row) + screen->cur_col;
507    });
508    if_OPT_DEC_CHRSET({
509	cb = SCRN_BUF_CSETS(screen, screen->cur_row) + screen->cur_col;
510    });
511
512#if OPT_WIDE_CHARS
513    starcol1 = *chars;
514    starcol2 = chars[length - 1];
515#endif
516
517    /* write blanks if we're writing invisible text */
518    if (flags & INVISIBLE) {
519	memset(chars, ' ', length);
520    } else {
521	memcpy(chars, str, length);	/* This can stand for the present. If it
522					   is wrong, we will scribble over it */
523    }
524
525#if OPT_BLINK_TEXT
526    if ((flags & BLINK) && !(screen->blink_as_bold)) {
527	ScrnSetBlinked(screen, screen->cur_row);
528    }
529#endif
530
531#define ERROR_1 0x20
532#define ERROR_2 0x00
533    if_OPT_WIDE_CHARS(screen, {
534
535	Char *char2;
536
537	if (real_width != length) {
538	    Char *char1 = chars;
539	    char2 = SCRN_BUF_WIDEC(screen, screen->cur_row);
540	    char2 += screen->cur_col;
541	    if (screen->cur_col && starcol1 == HIDDEN_LO && *char2 == HIDDEN_HI
542		&& iswide(PACK_PAIR(char1, char2, -1))) {
543		char1[-1] = ERROR_1;
544		char2[-1] = ERROR_2;
545	    }
546	    /* if we are overwriting the right hand half of a
547	       wide character, make the other half vanish */
548	    while (length) {
549		int ch = PACK_PAIR(str, str2, 0);
550
551		*char1 = *str;
552		char1++;
553		str++;
554
555		if (str2) {
556		    *char2 = *str2;
557		    str2++;
558		} else
559		    *char2 = 0;
560		char2++;
561		length--;
562
563		if (iswide(ch)) {
564		    *char1 = HIDDEN_LO;
565		    *char2 = HIDDEN_HI;
566		    char1++;
567		    char2++;
568		}
569	    }
570
571	    if (*char1 == HIDDEN_LO
572		&& *char2 == HIDDEN_HI
573		&& char1[-1] == HIDDEN_LO
574		&& char2[-1] == HIDDEN_HI) {
575		*char1 = ERROR_1;
576		*char2 = ERROR_2;
577	    }
578	    /* if we are overwriting the left hand half of a
579	       wide character, make the other half vanish */
580	} else {
581
582	    if ((char2 = SCRN_BUF_WIDEC(screen, screen->cur_row)) != 0) {
583		char2 += screen->cur_col;
584		if (screen->cur_col && starcol1 == HIDDEN_LO && *char2 == HIDDEN_HI
585		    && iswide(PACK_PAIR(chars, char2, -1))) {
586		    chars[-1] = ERROR_1;
587		    char2[-1] = ERROR_2;
588		}
589		/* if we are overwriting the right hand half of a
590		   wide character, make the other half vanish */
591		if (chars[length] == HIDDEN_LO && char2[length] == HIDDEN_HI &&
592		    iswide(PACK_PAIR(chars, char2, length - 1))) {
593		    chars[length] = ERROR_1;
594		    char2[length] = ERROR_2;
595		}
596		/* if we are overwriting the left hand half of a
597		   wide character, make the other half vanish */
598		if ((flags & INVISIBLE) || (str2 == 0))
599		    memset(char2, 0, length);
600		else
601		    memcpy(char2, str2, length);
602	    }
603	}
604    });
605
606    flags &= ATTRIBUTES;
607    flags |= CHARDRAWN;
608    memset(attrs, (Char) flags, real_width);
609
610    if_OPT_WIDE_CHARS(screen, {
611	int off;
612	for (off = OFF_FINAL; off < MAX_PTRS; ++off) {
613	    memset(SCREEN_PTR(screen,
614			      screen->cur_row,
615			      off) + screen->cur_col,
616		   0, real_width);
617	}
618    });
619    if_OPT_EXT_COLORS(screen, {
620	memset(fbf, (int) ExtractForeground(cur_fg_bg), real_width);
621	memset(fbb, (int) ExtractBackground(cur_fg_bg), real_width);
622    });
623    if_OPT_ISO_TRADITIONAL_COLORS(screen, {
624	memset(fb, (int) cur_fg_bg, real_width);
625    });
626    if_OPT_DEC_CHRSET({
627	memset(cb, curXtermChrSet(xw, screen->cur_row), real_width);
628    });
629
630    if_OPT_WIDE_CHARS(screen, {
631	screen->last_written_col = screen->cur_col + real_width - 1;
632	screen->last_written_row = screen->cur_row;
633    });
634
635    if_OPT_XMC_GLITCH(screen, {
636	Resolve_XMC(xw);
637    });
638}
639
640/*
641 * Saves pointers to the n lines beginning at sb + where, and clears the lines
642 */
643static void
644ScrnClearLines(XtermWidget xw, ScrnBuf sb, int where, unsigned n, unsigned size)
645{
646    TScreen *screen = &(xw->screen);
647    int i, j;
648    size_t len = ScrnPointers(screen, n);
649    int last = (n * MAX_PTRS);
650
651    TRACE(("ScrnClearLines(where %d, n %d, size %d)\n", where, n, size));
652
653    assert(n != 0);
654    assert(size != 0);
655
656    /* save n lines at where */
657    memcpy((char *) screen->save_ptr,
658	   (char *) &sb[MAX_PTRS * where],
659	   len);
660
661    /* clear contents of old rows */
662    if (TERM_COLOR_FLAGS(xw)) {
663	int flags = TERM_COLOR_FLAGS(xw);
664	for (i = 0; i < last; i += MAX_PTRS) {
665	    for (j = 0; j < MAX_PTRS; j++) {
666		if (j < BUF_HEAD)
667		    screen->save_ptr[i + j] = 0;
668		else if (j == OFF_ATTRS)
669		    memset(screen->save_ptr[i + j], flags, size);
670#if OPT_ISO_COLORS
671#if OPT_EXT_COLORS
672		else if (j == OFF_FGRND)
673		    memset(screen->save_ptr[i + j], xw->sgr_foreground, size);
674		else if (j == OFF_BGRND)
675		    memset(screen->save_ptr[i + j], xw->cur_background, size);
676#else
677		else if (j == OFF_COLOR)
678		    memset(screen->save_ptr[i + j], (int)
679			   xtermColorPair(xw), size);
680#endif
681#endif
682		else
683		    bzero(screen->save_ptr[i + j], size);
684	    }
685	}
686    } else {
687	for (i = 0; i < last; i += MAX_PTRS) {
688	    for (j = 0; j < BUF_HEAD; j++)
689		screen->save_ptr[i + j] = 0;
690	    for (j = BUF_HEAD; j < MAX_PTRS; j++)
691		bzero(screen->save_ptr[i + j], size);
692	}
693    }
694}
695
696size_t
697ScrnPointers(TScreen * screen, size_t len)
698{
699    len *= MAX_PTRS;
700
701    if (len > screen->save_len) {
702	if (screen->save_len)
703	    screen->save_ptr = TypeRealloc(ScrnPtr, len, screen->save_ptr);
704	else
705	    screen->save_ptr = TypeMallocN(ScrnPtr, len);
706	screen->save_len = len;
707	if (screen->save_ptr == 0)
708	    SysError(ERROR_SAVE_PTR);
709    }
710    return len * sizeof(ScrnPtr);
711}
712
713/*
714 * Inserts n blank lines at sb + where, treating last as a bottom margin.
715 * size is the size of each entry in sb.
716 */
717void
718ScrnInsertLine(XtermWidget xw, ScrnBuf sb, int last, int where,
719	       unsigned n, unsigned size)
720{
721    TScreen *screen = &(xw->screen);
722    size_t len = ScrnPointers(screen, n);
723
724    assert(where >= 0);
725    assert(last >= (int) n);
726    assert(last >= where);
727
728    assert(n != 0);
729    assert(size != 0);
730    assert(MAX_PTRS > 0);
731
732    /* save n lines at bottom */
733    ScrnClearLines(xw, sb, (last -= n - 1), n, size);
734
735    /*
736     * WARNING, overlapping copy operation.  Move down lines (pointers).
737     *
738     *   +----|---------|--------+
739     *
740     * is copied in the array to:
741     *
742     *   +--------|---------|----+
743     */
744    assert(last >= where);
745    memmove((char *) &sb[MAX_PTRS * (where + n)],
746	    (char *) &sb[MAX_PTRS * where],
747	    MAX_PTRS * sizeof(char *) * (last - where));
748
749    /* reuse storage for new lines at where */
750    memcpy((char *) &sb[MAX_PTRS * where],
751	   (char *) screen->save_ptr,
752	   len);
753}
754
755/*
756 * Deletes n lines at sb + where, treating last as a bottom margin.
757 * size is the size of each entry in sb.
758 */
759void
760ScrnDeleteLine(XtermWidget xw, ScrnBuf sb, int last, int where,
761	       unsigned n, unsigned size)
762{
763    TScreen *screen = &(xw->screen);
764
765    assert(where >= 0);
766    assert(last >= where + (int) n - 1);
767
768    assert(n != 0);
769    assert(size != 0);
770    assert(MAX_PTRS > 0);
771
772    ScrnClearLines(xw, sb, where, n, size);
773
774    /* move up lines */
775    memmove((char *) &sb[MAX_PTRS * where],
776	    (char *) &sb[MAX_PTRS * (where + n)],
777	    MAX_PTRS * sizeof(char *) * ((last -= n - 1) - where));
778
779    /* reuse storage for new bottom lines */
780    memcpy((char *) &sb[MAX_PTRS * last],
781	   (char *) screen->save_ptr,
782	   MAX_PTRS * sizeof(char *) * n);
783}
784
785/*
786 * Inserts n blanks in screen at current row, col.  Size is the size of each
787 * row.
788 */
789void
790ScrnInsertChar(XtermWidget xw, unsigned n)
791{
792#define Target (data + col + n)
793#define Source (data + col)
794
795    TScreen *screen = &(xw->screen);
796    ScrnBuf sb = screen->visbuf;
797    int last = MaxCols(screen);
798    int row = screen->cur_row;
799    int col = screen->cur_col;
800    Char *data;
801    size_t nbytes;
802
803    if (last <= (int) (col + n)) {
804	if (last <= col)
805	    return;
806	n = last - col;
807    }
808    nbytes = (last - (col + n));
809
810    assert(screen->cur_col >= 0);
811    assert(screen->cur_row >= 0);
812    assert(n > 0);
813    assert(last > (int) n);
814
815    if_OPT_WIDE_CHARS(screen, {
816	int xx = INX2ROW(screen, screen->cur_row);
817	int kl;
818	int kr = screen->cur_col;
819	if (DamagedCells(screen, n, &kl, (int *) 0, xx, kr) && kr > kl) {
820	    ClearCells(xw, 0, (unsigned) (kr - kl + 1), row, kl);
821	}
822	kr = screen->max_col - n + 1;
823	if (DamagedCells(screen, n, &kl, (int *) 0, xx, kr) && kr > kl) {
824	    ClearCells(xw, 0, (unsigned) (kr - kl + 1), row, kl);
825	}
826    });
827
828    data = BUF_CHARS(sb, row);
829    memmove(Target, Source, nbytes);
830
831    data = BUF_ATTRS(sb, row);
832    memmove(Target, Source, nbytes);
833
834    if_OPT_EXT_COLORS(screen, {
835	data = BUF_FGRND(sb, row);
836	memmove(Target, Source, nbytes);
837	data = BUF_BGRND(sb, row);
838	memmove(Target, Source, nbytes);
839    });
840    if_OPT_ISO_TRADITIONAL_COLORS(screen, {
841	data = BUF_COLOR(sb, row);
842	memmove(Target, Source, nbytes);
843    });
844    if_OPT_DEC_CHRSET({
845	data = BUF_CSETS(sb, row);
846	memmove(Target, Source, nbytes);
847    });
848    if_OPT_WIDE_CHARS(screen, {
849	int off;
850	for (off = OFF_WIDEC; off < MAX_PTRS; ++off) {
851	    data = BUFFER_PTR(sb, row, off);
852	    memmove(Target, Source, nbytes);
853	}
854    });
855    ClearCells(xw, CHARDRAWN, n, row, col);
856
857#undef Source
858#undef Target
859}
860
861/*
862 * Deletes n characters at current row, col.
863 */
864void
865ScrnDeleteChar(XtermWidget xw, unsigned n)
866{
867#define Target (data + col)
868#define Source (data + col + n)
869
870    TScreen *screen = &(xw->screen);
871    ScrnBuf sb = screen->visbuf;
872    int last = MaxCols(screen);
873    int row = screen->cur_row;
874    int col = screen->cur_col;
875    Char *data;
876    size_t nbytes;
877
878    if (last <= (int) (col + n)) {
879	if (last <= col)
880	    return;
881	n = last - col;
882    }
883    nbytes = (last - (col + n));
884
885    assert(screen->cur_col >= 0);
886    assert(screen->cur_row >= 0);
887    assert(n > 0);
888    assert(last > (int) n);
889
890    if_OPT_WIDE_CHARS(screen, {
891	int kl;
892	int kr;
893	if (DamagedCells(screen, n, &kl, &kr,
894			 INX2ROW(screen, screen->cur_row),
895			 screen->cur_col))
896	    ClearCells(xw, 0, (unsigned) (kr - kl + 1), row, kl);
897    });
898
899    data = BUF_CHARS(sb, row);
900    memmove(Target, Source, nbytes);
901
902    data = BUF_ATTRS(sb, row);
903    memmove(Target, Source, nbytes);
904
905    if_OPT_EXT_COLORS(screen, {
906	data = BUF_FGRND(sb, row);
907	memmove(Target, Source, nbytes);
908	data = BUF_BGRND(sb, row);
909	memmove(Target, Source, nbytes);
910    });
911    if_OPT_ISO_TRADITIONAL_COLORS(screen, {
912	data = BUF_COLOR(sb, row);
913	memmove(Target, Source, nbytes);
914    });
915    if_OPT_DEC_CHRSET({
916	data = BUF_CSETS(sb, row);
917	memmove(Target, Source, nbytes);
918    });
919    if_OPT_WIDE_CHARS(screen, {
920	int off;
921	for (off = OFF_WIDEC; off < MAX_PTRS; ++off) {
922	    data = BUFFER_PTR(sb, row, off);
923	    memmove(Target, Source, nbytes);
924	}
925    });
926    ClearCells(xw, 0, n, row, (int) (last - n));
927    ScrnClrWrapped(screen, row);
928
929#undef Source
930#undef Target
931}
932
933/*
934 * Repaints the area enclosed by the parameters.
935 * Requires: (toprow, leftcol), (toprow + nrows, leftcol + ncols) are
936 * 	     coordinates of characters in screen;
937 *	     nrows and ncols positive.
938 *	     all dimensions are based on single-characters.
939 */
940void
941ScrnRefresh(XtermWidget xw,
942	    int toprow,
943	    int leftcol,
944	    int nrows,
945	    int ncols,
946	    Bool force)		/* ... leading/trailing spaces */
947{
948    TScreen *screen = &(xw->screen);
949    int y = toprow * FontHeight(screen) + screen->border;
950    int row;
951    int maxrow = toprow + nrows - 1;
952    int scrollamt = screen->scroll_amt;
953    int max = screen->max_row;
954    int gc_changes = 0;
955#ifdef __CYGWIN__
956    static char first_time = 1;
957#endif
958    static int recurse = 0;
959
960    TRACE(("ScrnRefresh (%d,%d) - (%d,%d)%s\n",
961	   toprow, leftcol,
962	   nrows, ncols,
963	   force ? " force" : ""));
964
965    if (screen->cursorp.col >= leftcol
966	&& screen->cursorp.col <= (leftcol + ncols - 1)
967	&& screen->cursorp.row >= ROW2INX(screen, toprow)
968	&& screen->cursorp.row <= ROW2INX(screen, maxrow))
969	screen->cursor_state = OFF;
970
971    for (row = toprow; row <= maxrow; y += FontHeight(screen), row++) {
972#if OPT_ISO_COLORS
973#if OPT_EXT_COLORS
974	Char *fbf = 0;
975	Char *fbb = 0;
976#define ColorOf(col) (unsigned) ((fbf[col] << 8) | fbb[col])
977#else
978	Char *fb = 0;
979#define ColorOf(col) (unsigned) (fb[col])
980#endif
981#endif
982#if OPT_DEC_CHRSET
983	Char *cb = 0;
984#endif
985#if OPT_WIDE_CHARS
986	int wideness = 0;
987	Char *widec = 0;
988#define WIDEC_PTR(cell) widec ? &widec[cell] : 0
989#define BLANK_CEL(cell) ((chars[cell] == ' ') && (widec == 0 || widec[cell] == 0))
990#else
991#define BLANK_CEL(cell) (chars[cell] == ' ')
992#endif
993	Char cs = 0;
994	Char *chars;
995	Char *attrs;
996	int col = leftcol;
997	int maxcol = leftcol + ncols - 1;
998	int hi_col = maxcol;
999	int lastind;
1000	unsigned flags;
1001	unsigned test;
1002	unsigned fg_bg = 0, fg = 0, bg = 0;
1003	int x;
1004	GC gc;
1005	Bool hilite;
1006
1007	(void) fg;
1008	(void) bg;
1009
1010	if (row < screen->top_marg || row > screen->bot_marg)
1011	    lastind = row;
1012	else
1013	    lastind = row - scrollamt;
1014
1015	TRACE(("ScrnRefresh row=%d lastind=%d/%d\n", row, lastind, max));
1016	if (lastind < 0 || lastind > max)
1017	    continue;
1018
1019	chars = SCRN_BUF_CHARS(screen, ROW2INX(screen, lastind));
1020	attrs = SCRN_BUF_ATTRS(screen, ROW2INX(screen, lastind));
1021
1022	if_OPT_DEC_CHRSET({
1023	    cb = SCRN_BUF_CSETS(screen, ROW2INX(screen, lastind));
1024	});
1025
1026	if_OPT_WIDE_CHARS(screen, {
1027	    widec = SCRN_BUF_WIDEC(screen, ROW2INX(screen, lastind));
1028	});
1029
1030	if_OPT_WIDE_CHARS(screen, {
1031	    /* This fixes an infinite recursion bug, that leads
1032	       to display anomalies. It seems to be related to
1033	       problems with the selection. */
1034	    if (recurse < 3) {
1035		/* adjust to redraw all of a widechar if we just wanted
1036		   to draw the right hand half */
1037		if (leftcol > 0 &&
1038		    (PACK_PAIR(chars, widec, leftcol)) == HIDDEN_CHAR &&
1039		    iswide(PACK_PAIR(chars, widec, leftcol - 1))) {
1040		    leftcol--;
1041		    ncols++;
1042		    col = leftcol;
1043		}
1044	    } else {
1045		fprintf(stderr, "This should not happen. Why is it so?\n");
1046	    }
1047	});
1048
1049	if (row < screen->startH.row || row > screen->endH.row ||
1050	    (row == screen->startH.row && maxcol < screen->startH.col) ||
1051	    (row == screen->endH.row && col >= screen->endH.col)) {
1052#if OPT_DEC_CHRSET
1053	    /*
1054	     * Temporarily change dimensions to double-sized characters so
1055	     * we can reuse the recursion on this function.
1056	     */
1057	    if (CSET_DOUBLE(*cb)) {
1058		col /= 2;
1059		maxcol /= 2;
1060	    }
1061#endif
1062	    /*
1063	     * If row does not intersect selection; don't hilite blanks.
1064	     */
1065	    if (!force) {
1066		while (col <= maxcol && (attrs[col] & ~BOLD) == 0 &&
1067		       BLANK_CEL(col))
1068		    col++;
1069
1070		while (col <= maxcol && (attrs[maxcol] & ~BOLD) == 0 &&
1071		       BLANK_CEL(maxcol))
1072		    maxcol--;
1073	    }
1074#if OPT_DEC_CHRSET
1075	    if (CSET_DOUBLE(*cb)) {
1076		col *= 2;
1077		maxcol *= 2;
1078	    }
1079#endif
1080	    hilite = False;
1081	} else {
1082	    /* row intersects selection; split into pieces of single type */
1083	    if (row == screen->startH.row && col < screen->startH.col) {
1084		recurse++;
1085		ScrnRefresh(xw, row, col, 1, screen->startH.col - col,
1086			    force);
1087		col = screen->startH.col;
1088	    }
1089	    if (row == screen->endH.row && maxcol >= screen->endH.col) {
1090		recurse++;
1091		ScrnRefresh(xw, row, screen->endH.col, 1,
1092			    maxcol - screen->endH.col + 1, force);
1093		maxcol = screen->endH.col - 1;
1094	    }
1095
1096	    /*
1097	     * If we're highlighting because the user is doing cut/paste,
1098	     * trim the trailing blanks from the highlighted region so we're
1099	     * showing the actual extent of the text that'll be cut.  If
1100	     * we're selecting a blank line, we'll highlight one column
1101	     * anyway.
1102	     *
1103	     * We don't do this if the mouse-hilite mode is set because that
1104	     * would be too confusing.
1105	     *
1106	     * The default if the highlightSelection resource isn't set will
1107	     * highlight the whole width of the terminal, which is easy to
1108	     * see, but harder to use (because trailing blanks aren't as
1109	     * apparent).
1110	     */
1111	    if (screen->highlight_selection
1112		&& screen->send_mouse_pos != VT200_HIGHLIGHT_MOUSE) {
1113		hi_col = screen->max_col;
1114		while (hi_col > 0 && !(attrs[hi_col] & CHARDRAWN))
1115		    hi_col--;
1116	    }
1117
1118	    /* remaining piece should be hilited */
1119	    hilite = True;
1120	}
1121
1122	if (col > maxcol)
1123	    continue;
1124
1125	/*
1126	 * Go back to double-sized character dimensions if the line has
1127	 * double-width characters.  Note that 'hi_col' is already in the
1128	 * right units.
1129	 */
1130	if_OPT_DEC_CHRSET({
1131	    if (CSET_DOUBLE(*cb)) {
1132		col /= 2;
1133		maxcol /= 2;
1134	    }
1135	    cs = cb[col];
1136	});
1137
1138	flags = attrs[col];
1139#if OPT_WIDE_CHARS
1140	if (widec)
1141	    wideness = iswide(PACK_PAIR(chars, widec, col));
1142	else
1143	    wideness = 0;
1144#endif
1145	if_OPT_EXT_COLORS(screen, {
1146	    fbf = SCRN_BUF_FGRND(screen, ROW2INX(screen, lastind));
1147	    fbb = SCRN_BUF_BGRND(screen, ROW2INX(screen, lastind));
1148	    fg_bg = ColorOf(col);
1149	    /* this combines them, then splits them again.  but
1150	       extract_fg does more, so seems reasonable */
1151	    fg = extract_fg(xw, fg_bg, flags);
1152	    bg = extract_bg(xw, fg_bg, flags);
1153	});
1154	if_OPT_ISO_TRADITIONAL_COLORS(screen, {
1155	    fb = SCRN_BUF_COLOR(screen, ROW2INX(screen, lastind));
1156	    fg_bg = ColorOf(col);
1157	    fg = extract_fg(xw, fg_bg, flags);
1158	    bg = extract_bg(xw, fg_bg, flags);
1159	});
1160
1161	gc = updatedXtermGC(xw, flags, fg_bg, hilite);
1162	gc_changes |= (flags & (FG_COLOR | BG_COLOR));
1163
1164	x = CurCursorX(screen, ROW2INX(screen, row), col);
1165	lastind = col;
1166
1167	for (; col <= maxcol; col++) {
1168	    if ((attrs[col] != flags)
1169		|| (hilite && (col > hi_col))
1170#if OPT_ISO_COLORS
1171		|| ((flags & FG_COLOR)
1172		    && (extract_fg(xw, ColorOf(col), attrs[col]) != fg))
1173		|| ((flags & BG_COLOR)
1174		    && (extract_bg(xw, ColorOf(col), attrs[col]) != bg))
1175#endif
1176#if OPT_WIDE_CHARS
1177		|| (widec
1178		    && ((iswide(PACK_PAIR(chars, widec, col))) != wideness)
1179		    && !((PACK_PAIR(chars, widec, col)) == HIDDEN_CHAR))
1180#endif
1181#if OPT_DEC_CHRSET
1182		|| (cb[col] != cs)
1183#endif
1184		) {
1185		assert(col >= lastind);
1186		TRACE(("ScrnRefresh looping drawXtermText %d..%d:%s\n",
1187		       lastind, col,
1188		       visibleChars(PAIRED_CHARS(&chars[lastind],
1189						 WIDEC_PTR(lastind)),
1190				    (unsigned) (col - lastind))));
1191
1192		test = flags;
1193		checkVeryBoldColors(test, fg);
1194
1195		x = drawXtermText(xw, test & DRAWX_MASK, gc, x, y,
1196				  cs,
1197				  PAIRED_CHARS(&chars[lastind], WIDEC_PTR(lastind)),
1198				  (unsigned) (col - lastind), 0);
1199
1200		if_OPT_WIDE_CHARS(screen, {
1201		    int i;
1202		    int off;
1203		    for (off = OFF_FINAL; off < MAX_PTRS; off += 2) {
1204			Char *com_lo = BUFFER_PTR(screen->visbuf,
1205						  ROW2INX(screen, row),
1206						  off + 0);
1207			Char *com_hi = BUFFER_PTR(screen->visbuf,
1208						  ROW2INX(screen, row),
1209						  off + 1);
1210			for (i = lastind; i < col; i++) {
1211			    int my_x = CurCursorX(screen,
1212						  ROW2INX(screen, row),
1213						  i);
1214			    int base = PACK_PAIR(chars, widec, i);
1215			    int combo = PACK_PAIR(com_lo, com_hi, i);
1216
1217			    if (iswide(base))
1218				my_x = CurCursorX(screen,
1219						  ROW2INX(screen, row),
1220						  i - 1);
1221
1222			    if (combo != 0)
1223				drawXtermText(xw,
1224					      (test & DRAWX_MASK)
1225					      | NOBACKGROUND,
1226					      gc, my_x, y, cs,
1227					      PAIRED_CHARS(com_lo + i,
1228							   com_hi + i),
1229					      1, iswide(base));
1230			}
1231		    }
1232		});
1233
1234		resetXtermGC(xw, flags, hilite);
1235
1236		lastind = col;
1237
1238		if (hilite && (col > hi_col))
1239		    hilite = False;
1240
1241		flags = attrs[col];
1242		if_OPT_EXT_COLORS(screen, {
1243		    fg_bg = ColorOf(col);
1244		    fg = extract_fg(xw, fg_bg, flags);
1245		    bg = extract_bg(xw, fg_bg, flags);
1246		});
1247		if_OPT_ISO_TRADITIONAL_COLORS(screen, {
1248		    fg_bg = ColorOf(col);
1249		    fg = extract_fg(xw, fg_bg, flags);
1250		    bg = extract_bg(xw, fg_bg, flags);
1251		});
1252		if_OPT_DEC_CHRSET({
1253		    cs = cb[col];
1254		});
1255#if OPT_WIDE_CHARS
1256		if (widec)
1257		    wideness = iswide(PACK_PAIR(chars, widec, col));
1258#endif
1259
1260		gc = updatedXtermGC(xw, flags, fg_bg, hilite);
1261		gc_changes |= (flags & (FG_COLOR | BG_COLOR));
1262	    }
1263
1264	    if (chars[col] == 0) {
1265#if OPT_WIDE_CHARS
1266		if (widec == 0 || widec[col] == 0)
1267#endif
1268		    chars[col] = ' ';
1269	    }
1270	}
1271
1272	assert(col >= lastind);
1273	TRACE(("ScrnRefresh calling drawXtermText %d..%d:%s\n",
1274	       lastind, col,
1275	       visibleChars(PAIRED_CHARS(&chars[lastind], WIDEC_PTR(lastind)),
1276			    (unsigned) (col - lastind))));
1277
1278	test = flags;
1279	checkVeryBoldColors(test, fg);
1280
1281	drawXtermText(xw, test & DRAWX_MASK, gc, x, y,
1282		      cs,
1283		      PAIRED_CHARS(&chars[lastind], WIDEC_PTR(lastind)),
1284		      (unsigned) (col - lastind), 0);
1285
1286	if_OPT_WIDE_CHARS(screen, {
1287	    int i;
1288	    int off;
1289	    for (off = OFF_FINAL; off < MAX_PTRS; off += 2) {
1290		Char *com_lo = BUFFER_PTR(screen->visbuf,
1291					  ROW2INX(screen, row),
1292					  off + 0);
1293		Char *com_hi = BUFFER_PTR(screen->visbuf,
1294					  ROW2INX(screen, row),
1295					  off + 1);
1296		for (i = lastind; i < col; i++) {
1297		    int my_x = CurCursorX(screen,
1298					  ROW2INX(screen, row),
1299					  i);
1300		    int base = PACK_PAIR(chars, widec, i);
1301		    int combo = PACK_PAIR(com_lo, com_hi, i);
1302
1303		    if (iswide(base))
1304			my_x = CurCursorX(screen,
1305					  ROW2INX(screen, row),
1306					  i - 1);
1307
1308		    if (combo != 0)
1309			drawXtermText(xw,
1310				      (test & DRAWX_MASK)
1311				      | NOBACKGROUND,
1312				      gc, my_x, y, cs,
1313				      PAIRED_CHARS(com_lo + i,
1314						   com_hi + i),
1315				      1, iswide(base));
1316		}
1317	    }
1318	});
1319
1320	resetXtermGC(xw, flags, hilite);
1321    }
1322
1323    /*
1324     * If we're in color mode, reset the various GC's to the current
1325     * screen foreground and background so that other functions (e.g.,
1326     * ClearRight) will get the correct colors.
1327     */
1328    if_OPT_ISO_COLORS(screen, {
1329	if (gc_changes & FG_COLOR)
1330	    SGR_Foreground(xw, xw->cur_foreground);
1331	if (gc_changes & BG_COLOR)
1332	    SGR_Background(xw, xw->cur_background);
1333    });
1334
1335#if defined(__CYGWIN__) && defined(TIOCSWINSZ)
1336    if (first_time == 1) {
1337	TTYSIZE_STRUCT ts;
1338
1339	first_time = 0;
1340	TTYSIZE_ROWS(ts) = nrows;
1341	TTYSIZE_COLS(ts) = ncols;
1342	ts.ws_xpixel = xw->core.width;
1343	ts.ws_ypixel = xw->core.height;
1344	SET_TTYSIZE(screen->respond, ts);
1345    }
1346#endif
1347    recurse--;
1348}
1349
1350/*
1351 * Call this wrapper to ScrnRefresh() when the data has changed.  If the
1352 * refresh region overlaps the selection, we will release the primary selection.
1353 */
1354void
1355ScrnUpdate(XtermWidget xw,
1356	   int toprow,
1357	   int leftcol,
1358	   int nrows,
1359	   int ncols,
1360	   Bool force)		/* ... leading/trailing spaces */
1361{
1362    TScreen *screen = &(xw->screen);
1363
1364    if (ScrnHaveSelection(screen)
1365	&& (toprow <= screen->endH.row)
1366	&& (toprow + nrows - 1 >= screen->startH.row)) {
1367	ScrnDisownSelection(xw);
1368    }
1369    ScrnRefresh(xw, toprow, leftcol, nrows, ncols, force);
1370}
1371
1372/*
1373 * Sets the rows first though last of the buffer of screen to spaces.
1374 * Requires first <= last; first, last are rows of screen->buf.
1375 */
1376void
1377ClearBufRows(XtermWidget xw,
1378	     int first,
1379	     int last)
1380{
1381    TScreen *screen = &(xw->screen);
1382    unsigned len = MaxCols(screen);
1383    int row;
1384
1385    TRACE(("ClearBufRows %d..%d\n", first, last));
1386    for (row = first; row <= last; row++) {
1387	if_OPT_DEC_CHRSET({
1388	    /* clearing the whole row resets the doublesize characters */
1389	    SCRN_ROW_CSET(screen, row) = CSET_SWL;
1390	});
1391	ScrnClrWrapped(screen, row);
1392	ClearCells(xw, 0, len, row, 0);
1393    }
1394}
1395
1396/*
1397  Resizes screen:
1398  1. If new window would have fractional characters, sets window size so as to
1399  discard fractional characters and returns -1.
1400  Minimum screen size is 1 X 1.
1401  Note that this causes another ExposeWindow event.
1402  2. Enlarges screen->buf if necessary.  New space is appended to the bottom
1403  and to the right
1404  3. Reduces  screen->buf if necessary.  Old space is removed from the bottom
1405  and from the right
1406  4. Cursor is positioned as closely to its former position as possible
1407  5. Sets screen->max_row and screen->max_col to reflect new size
1408  6. Maintains the inner border (and clears the border on the screen).
1409  7. Clears origin mode and sets scrolling region to be entire screen.
1410  8. Returns 0
1411  */
1412int
1413ScreenResize(XtermWidget xw,
1414	     int width,
1415	     int height,
1416	     unsigned *flags)
1417{
1418    TScreen *screen = &(xw->screen);
1419    int code, rows, cols;
1420    int border = 2 * screen->border;
1421    int move_down_by = 0;
1422#ifdef TTYSIZE_STRUCT
1423    TTYSIZE_STRUCT ts;
1424#endif
1425    Window tw = VWindow(screen);
1426
1427    TRACE(("ScreenResize %dx%d border %d font %dx%d\n",
1428	   height, width, border,
1429	   FontHeight(screen), FontWidth(screen)));
1430
1431    assert(width > 0);
1432    assert(height > 0);
1433
1434    if (screen->is_running) {
1435	/* clear the right and bottom internal border because of NorthWest
1436	   gravity might have left junk on the right and bottom edges */
1437	if (width >= FullWidth(screen)) {
1438	    XClearArea(screen->display, tw,
1439		       FullWidth(screen), 0,	/* right edge */
1440		       0, (unsigned) height,	/* from top to bottom */
1441		       False);
1442	}
1443	if (height >= FullHeight(screen)) {
1444	    XClearArea(screen->display, tw,
1445		       0, FullHeight(screen),	/* bottom */
1446		       (unsigned) width, 0,	/* all across the bottom */
1447		       False);
1448	}
1449    }
1450
1451    TRACE(("...computing rows/cols: %.2f %.2f\n",
1452	   (double) (height - border) / FontHeight(screen),
1453	   (double) (width - border - ScrollbarWidth(screen)) / FontWidth(screen)));
1454
1455    rows = (height - border) / FontHeight(screen);
1456    cols = (width - border - ScrollbarWidth(screen)) / FontWidth(screen);
1457    if (rows < 1)
1458	rows = 1;
1459    if (cols < 1)
1460	cols = 1;
1461
1462    /* update buffers if the screen has changed size */
1463    if (MaxRows(screen) != rows || MaxCols(screen) != cols) {
1464	int savelines = (screen->scrollWidget
1465			 ? screen->savelines
1466			 : 0);
1467	int delta_rows = rows - MaxRows(screen);
1468
1469	TRACE(("...ScreenResize chars %dx%d\n", rows, cols));
1470
1471	if (screen->is_running) {
1472	    if (screen->cursor_state)
1473		HideCursor();
1474	    if (screen->alternate
1475		&& xw->misc.resizeGravity == SouthWestGravity)
1476		/* swap buffer pointers back to make this work */
1477		SwitchBufPtrs(screen);
1478	    if (screen->altbuf)
1479		(void) Reallocate(xw,
1480				  &screen->altbuf,
1481				  &screen->abuf_address,
1482				  rows,
1483				  cols,
1484				  MaxRows(screen),
1485				  MaxCols(screen));
1486	    move_down_by = Reallocate(xw,
1487				      &screen->allbuf,
1488				      &screen->sbuf_address,
1489				      rows + savelines, cols,
1490				      MaxRows(screen) + savelines,
1491				      MaxCols(screen));
1492	    screen->visbuf = &screen->allbuf[MAX_PTRS * savelines];
1493	}
1494
1495	AdjustSavedCursor(xw, move_down_by);
1496	set_max_row(screen, screen->max_row + delta_rows);
1497	set_max_col(screen, cols - 1);
1498
1499	if (screen->is_running) {
1500	    if (xw->misc.resizeGravity == SouthWestGravity) {
1501		screen->savedlines -= move_down_by;
1502		if (screen->savedlines < 0)
1503		    screen->savedlines = 0;
1504		if (screen->savedlines > screen->savelines)
1505		    screen->savedlines = screen->savelines;
1506		if (screen->topline < -screen->savedlines)
1507		    screen->topline = -screen->savedlines;
1508		set_cur_row(screen, screen->cur_row + move_down_by);
1509		screen->cursorp.row += move_down_by;
1510		ScrollSelection(screen, move_down_by, True);
1511
1512		if (screen->alternate)
1513		    SwitchBufPtrs(screen);	/* put the pointers back */
1514	    }
1515	}
1516
1517	/* adjust scrolling region */
1518	set_tb_margins(screen, 0, screen->max_row);
1519	*flags &= ~ORIGIN;
1520
1521	if (screen->cur_row > screen->max_row)
1522	    set_cur_row(screen, screen->max_row);
1523	if (screen->cur_col > screen->max_col)
1524	    set_cur_col(screen, screen->max_col);
1525
1526	screen->fullVwin.height = height - border;
1527	screen->fullVwin.width = width - border - screen->fullVwin.sb_info.width;
1528
1529    } else if (FullHeight(screen) == height && FullWidth(screen) == width)
1530	return (0);		/* nothing has changed at all */
1531
1532    screen->fullVwin.fullheight = height;
1533    screen->fullVwin.fullwidth = width;
1534
1535    ResizeScrollBar(xw);
1536    ResizeSelection(screen, rows, cols);
1537
1538#ifndef NO_ACTIVE_ICON
1539    if (screen->iconVwin.window) {
1540	XWindowChanges changes;
1541	screen->iconVwin.width =
1542	    MaxCols(screen) * screen->iconVwin.f_width;
1543
1544	screen->iconVwin.height =
1545	    MaxRows(screen) * screen->iconVwin.f_height;
1546
1547	changes.width = screen->iconVwin.fullwidth =
1548	    screen->iconVwin.width + 2 * xw->misc.icon_border_width;
1549	changes.height = screen->iconVwin.fullheight =
1550	    screen->iconVwin.height + 2 * xw->misc.icon_border_width;
1551	changes.border_width = xw->misc.icon_border_width;
1552
1553	TRACE(("resizing icon window %dx%d\n", changes.height, changes.width));
1554	XConfigureWindow(XtDisplay(xw), screen->iconVwin.window,
1555			 CWWidth | CWHeight | CWBorderWidth, &changes);
1556    }
1557#endif /* NO_ACTIVE_ICON */
1558
1559#ifdef TTYSIZE_STRUCT
1560    /* Set tty's idea of window size */
1561    TTYSIZE_ROWS(ts) = rows;
1562    TTYSIZE_COLS(ts) = cols;
1563#ifdef USE_STRUCT_WINSIZE
1564    ts.ws_xpixel = width;
1565    ts.ws_ypixel = height;
1566#endif
1567    code = SET_TTYSIZE(screen->respond, ts);
1568    TRACE(("return %d from SET_TTYSIZE %dx%d\n", code, rows, cols));
1569    (void) code;
1570
1571#if defined(SIGWINCH) && defined(USE_STRUCT_TTYSIZE)
1572    if (screen->pid > 1) {
1573	int pgrp;
1574
1575	TRACE(("getting process-group\n"));
1576	if (ioctl(screen->respond, TIOCGPGRP, &pgrp) != -1) {
1577	    TRACE(("sending SIGWINCH to process group %d\n", pgrp));
1578	    kill_process_group(pgrp, SIGWINCH);
1579	}
1580    }
1581#endif /* SIGWINCH */
1582
1583#else
1584    TRACE(("ScreenResize cannot do anything to pty\n"));
1585#endif /* TTYSIZE_STRUCT */
1586    return (0);
1587}
1588
1589/*
1590 * Return true if any character cell starting at [row,col], for len-cells is
1591 * nonnull.
1592 */
1593Bool
1594non_blank_line(TScreen * screen,
1595	       int row,
1596	       int col,
1597	       int len)
1598{
1599    ScrnBuf sb = screen->visbuf;
1600    int i;
1601    Char *ptr = BUF_CHARS(sb, row);
1602
1603    for (i = col; i < len; i++) {
1604	if (ptr[i])
1605	    return True;
1606    }
1607
1608    if_OPT_WIDE_CHARS(screen, {
1609	if ((ptr = BUF_WIDEC(sb, row)) != 0) {
1610	    for (i = col; i < len; i++) {
1611		if (ptr[i])
1612		    return True;
1613	    }
1614	}
1615    });
1616
1617    return False;
1618}
1619
1620/*
1621 * Rectangle parameters start from one.
1622 */
1623#define minRectRow(screen) (getMinRow(screen) + 1)
1624#define minRectCol(screen) (getMinCol(screen) + 1)
1625#define maxRectRow(screen) (getMaxRow(screen) + 1)
1626#define maxRectCol(screen) (getMaxCol(screen) + 1)
1627
1628static int
1629limitedParseRow(XtermWidget xw, TScreen * screen, int row)
1630{
1631    int min_row = minRectRow(screen);
1632    int max_row = maxRectRow(screen);
1633
1634    if (row < min_row)
1635	row = min_row;
1636    else if (row > max_row)
1637	row = max_row;
1638    return row;
1639}
1640
1641static int
1642limitedParseCol(XtermWidget xw, TScreen * screen, int col)
1643{
1644    int min_col = minRectCol(screen);
1645    int max_col = maxRectCol(screen);
1646
1647    (void) xw;
1648    if (col < min_col)
1649	col = min_col;
1650    else if (col > max_col)
1651	col = max_col;
1652    return col;
1653}
1654
1655#define LimitedParse(num, func, dft) \
1656	func(xw, screen, (nparams > num) ? params[num] : dft)
1657
1658/*
1659 * Copy the rectangle boundaries into a struct, providing default values as
1660 * needed.
1661 */
1662void
1663xtermParseRect(XtermWidget xw, int nparams, int *params, XTermRect * target)
1664{
1665    TScreen *screen = &(xw->screen);
1666
1667    memset(target, 0, sizeof(*target));
1668    target->top = LimitedParse(0, limitedParseRow, minRectRow(screen));
1669    target->left = LimitedParse(1, limitedParseCol, minRectCol(screen));
1670    target->bottom = LimitedParse(2, limitedParseRow, maxRectRow(screen));
1671    target->right = LimitedParse(3, limitedParseCol, maxRectCol(screen));
1672    TRACE(("parsed rectangle %d,%d %d,%d\n",
1673	   target->top,
1674	   target->left,
1675	   target->bottom,
1676	   target->right));
1677}
1678
1679static Bool
1680validRect(XtermWidget xw, XTermRect * target)
1681{
1682    TScreen *screen = &(xw->screen);
1683
1684    TRACE(("comparing against screensize %dx%d\n",
1685	   maxRectRow(screen),
1686	   maxRectCol(screen)));
1687    return (target != 0
1688	    && target->top >= minRectRow(screen)
1689	    && target->left >= minRectCol(screen)
1690	    && target->top <= target->bottom
1691	    && target->left <= target->right
1692	    && target->top <= maxRectRow(screen)
1693	    && target->right <= maxRectCol(screen));
1694}
1695
1696/*
1697 * Fills a rectangle with the given 8-bit character and video-attributes.
1698 * Colors and double-size attribute are unmodified.
1699 */
1700void
1701ScrnFillRectangle(XtermWidget xw,
1702		  XTermRect * target,
1703		  int value,
1704		  unsigned flags,
1705		  Bool keepColors)
1706{
1707    TScreen *screen = &(xw->screen);
1708
1709    TRACE(("filling rectangle with '%c' flags %#x\n", value, flags));
1710    if (validRect(xw, target)) {
1711	unsigned left = target->left - 1;
1712	unsigned size = target->right - left;
1713	Char attrs = flags;
1714	int row, col;
1715
1716	attrs &= ATTRIBUTES;
1717	attrs |= CHARDRAWN;
1718	for (row = target->bottom - 1; row >= (target->top - 1); row--) {
1719	    TRACE(("filling %d [%d..%d]\n", row, left, left + size));
1720
1721	    /*
1722	     * Fill attributes, preserving "protected" flag, as well as
1723	     * colors if asked.
1724	     */
1725	    for (col = left; col < target->right; ++col) {
1726		Char temp = SCRN_BUF_ATTRS(screen, row)[col];
1727		if (!keepColors) {
1728		    temp &= ~(FG_COLOR | BG_COLOR);
1729		}
1730		temp = attrs | (temp & (FG_COLOR | BG_COLOR | PROTECTED));
1731		temp |= CHARDRAWN;
1732		SCRN_BUF_ATTRS(screen, row)[col] = temp;
1733#if OPT_ISO_COLORS
1734		if (attrs & (FG_COLOR | BG_COLOR)) {
1735		    if_OPT_EXT_COLORS(screen, {
1736			SCRN_BUF_FGRND(screen, row)[col] = xw->sgr_foreground;
1737			SCRN_BUF_BGRND(screen, row)[col] = xw->cur_background;
1738		    });
1739		    if_OPT_ISO_TRADITIONAL_COLORS(screen, {
1740			SCRN_BUF_COLOR(screen, row)[col] = xtermColorPair(xw);
1741		    });
1742		}
1743#endif
1744	    }
1745
1746	    memset(SCRN_BUF_CHARS(screen, row) + left, (Char) value, size);
1747	    if_OPT_WIDE_CHARS(screen, {
1748		int off;
1749		for (off = OFF_WIDEC; off < MAX_PTRS; ++off) {
1750		    memset(SCREEN_PTR(screen, row, off) + left, 0, size);
1751		}
1752	    })
1753	}
1754	ScrnUpdate(xw,
1755		   target->top - 1,
1756		   target->left - 1,
1757		   (target->bottom - target->top) + 1,
1758		   (target->right - target->left) + 1,
1759		   False);
1760    }
1761}
1762
1763#if OPT_DEC_RECTOPS
1764/*
1765 * Copies the source rectangle to the target location, including video
1766 * attributes.
1767 *
1768 * This implementation ignores page numbers.
1769 *
1770 * The reference manual does not indicate if it handles overlapping copy
1771 * properly - so we make a local copy of the source rectangle first, then apply
1772 * the target from that.
1773 */
1774void
1775ScrnCopyRectangle(XtermWidget xw, XTermRect * source, int nparam, int *params)
1776{
1777    TScreen *screen = &(xw->screen);
1778
1779    TRACE(("copying rectangle\n"));
1780
1781    if (validRect(xw, source)) {
1782	XTermRect target;
1783	xtermParseRect(xw,
1784		       ((nparam > 3) ? 2 : (nparam - 1)),
1785		       params,
1786		       &target);
1787	if (validRect(xw, &target)) {
1788	    unsigned high = (source->bottom - source->top) + 1;
1789	    unsigned wide = (source->right - source->left) + 1;
1790	    unsigned size = (high * wide * MAX_PTRS);
1791	    int row, col, n, j;
1792
1793	    Char *cells = TypeMallocN(Char, size);
1794
1795	    if (cells == 0)
1796		return;
1797
1798	    TRACE(("OK - make copy %dx%d\n", high, wide));
1799	    target.bottom = target.top + (high - 1);
1800	    target.right = target.left + (wide - 1);
1801
1802	    for (row = source->top - 1; row < source->bottom; ++row) {
1803		for (col = source->left - 1; col < source->right; ++col) {
1804		    n = (((1 + row - source->top) * wide)
1805			 + (1 + col - source->left)) * MAX_PTRS;
1806		    for (j = OFF_ATTRS; j < MAX_PTRS; ++j)
1807			cells[n + j] = SCREEN_PTR(screen, row, j)[col];
1808		}
1809	    }
1810	    for (row = target.top - 1; row < target.bottom; ++row) {
1811		for (col = target.left - 1; col < target.right; ++col) {
1812		    if (row >= getMinRow(screen)
1813			&& row <= getMaxRow(screen)
1814			&& col >= getMinCol(screen)
1815			&& col <= getMaxCol(screen)) {
1816			n = (((1 + row - target.top) * wide)
1817			     + (1 + col - target.left)) * MAX_PTRS;
1818			for (j = OFF_ATTRS; j < MAX_PTRS; ++j)
1819			    SCREEN_PTR(screen, row, j)[col] = cells[n + j];
1820			SCRN_BUF_ATTRS(screen, row)[col] |= CHARDRAWN;
1821		    }
1822		}
1823	    }
1824	    free(cells);
1825
1826	    ScrnUpdate(xw,
1827		       (target.top - 1),
1828		       (target.left - 1),
1829		       (target.bottom - target.top) + 1,
1830		       ((target.right - target.left) + 1),
1831		       False);
1832	}
1833    }
1834}
1835
1836/*
1837 * Modifies the video-attributes only - so selection (not a video attribute) is
1838 * unaffected.  Colors and double-size flags are unaffected as well.
1839 *
1840 * FIXME: our representation for "invisible" does not work with this operation,
1841 * since the attribute byte is fully-allocated for other flags.  The logic
1842 * is shown for INVISIBLE because it's harmless, and useful in case the
1843 * CHARDRAWN or PROTECTED flags are reassigned.
1844 */
1845void
1846ScrnMarkRectangle(XtermWidget xw,
1847		  XTermRect * target,
1848		  Bool reverse,
1849		  int nparam,
1850		  int *params)
1851{
1852    TScreen *screen = &(xw->screen);
1853    Bool exact = (screen->cur_decsace == 2);
1854
1855    TRACE(("%s %s\n",
1856	   reverse ? "reversing" : "marking",
1857	   (exact
1858	    ? "rectangle"
1859	    : "region")));
1860
1861    if (validRect(xw, target)) {
1862	int top = target->top - 1;
1863	int bottom = target->bottom - 1;
1864	int row, col;
1865	int n;
1866
1867	for (row = top; row <= bottom; ++row) {
1868	    int left = ((exact || (row == top))
1869			? (target->left - 1)
1870			: getMinCol(screen));
1871	    int right = ((exact || (row == bottom))
1872			 ? (target->right - 1)
1873			 : getMaxCol(screen));
1874
1875	    TRACE(("marking %d [%d..%d]\n", row, left, right));
1876	    for (col = left; col <= right; ++col) {
1877		unsigned flags = SCRN_BUF_ATTRS(screen, row)[col];
1878
1879		for (n = 0; n < nparam; ++n) {
1880#if OPT_TRACE
1881		    if (row == top && col == left)
1882			TRACE(("attr param[%d] %d\n", n + 1, params[n]));
1883#endif
1884		    if (reverse) {
1885			switch (params[n]) {
1886			case 1:
1887			    flags ^= BOLD;
1888			    break;
1889			case 4:
1890			    flags ^= UNDERLINE;
1891			    break;
1892			case 5:
1893			    flags ^= BLINK;
1894			    break;
1895			case 7:
1896			    flags ^= INVERSE;
1897			    break;
1898			case 8:
1899			    flags ^= INVISIBLE;
1900			    break;
1901			}
1902		    } else {
1903			switch (params[n]) {
1904			case 0:
1905			    flags &= ~SGR_MASK;
1906			    break;
1907			case 1:
1908			    flags |= BOLD;
1909			    break;
1910			case 4:
1911			    flags |= UNDERLINE;
1912			    break;
1913			case 5:
1914			    flags |= BLINK;
1915			    break;
1916			case 7:
1917			    flags |= INVERSE;
1918			    break;
1919			case 8:
1920			    flags |= INVISIBLE;
1921			    break;
1922			case 22:
1923			    flags &= ~BOLD;
1924			    break;
1925			case 24:
1926			    flags &= ~UNDERLINE;
1927			    break;
1928			case 25:
1929			    flags &= ~BLINK;
1930			    break;
1931			case 27:
1932			    flags &= ~INVERSE;
1933			    break;
1934			case 28:
1935			    flags &= ~INVISIBLE;
1936			    break;
1937			}
1938		    }
1939		}
1940#if OPT_TRACE
1941		if (row == top && col == left)
1942		    TRACE(("first mask-change is %#x\n",
1943			   SCRN_BUF_ATTRS(screen, row)[col] ^ flags));
1944#endif
1945		SCRN_BUF_ATTRS(screen, row)[col] = flags;
1946	    }
1947	}
1948	ScrnRefresh(xw,
1949		    (target->top - 1),
1950		    (exact ? (target->left - 1) : getMinCol(screen)),
1951		    (target->bottom - target->top) + 1,
1952		    (exact
1953		     ? ((target->right - target->left) + 1)
1954		     : (getMaxCol(screen) - getMinCol(screen) + 1)),
1955		    False);
1956    }
1957}
1958
1959/*
1960 * Resets characters to space, except where prohibited by DECSCA.  Video
1961 * attributes (including color) are untouched.
1962 */
1963void
1964ScrnWipeRectangle(XtermWidget xw,
1965		  XTermRect * target)
1966{
1967    TScreen *screen = &(xw->screen);
1968
1969    TRACE(("wiping rectangle\n"));
1970
1971    if (validRect(xw, target)) {
1972	int top = target->top - 1;
1973	int bottom = target->bottom - 1;
1974	int row, col;
1975
1976	for (row = top; row <= bottom; ++row) {
1977	    int left = (target->left - 1);
1978	    int right = (target->right - 1);
1979
1980	    TRACE(("wiping %d [%d..%d]\n", row, left, right));
1981	    for (col = left; col <= right; ++col) {
1982		if (!((screen->protected_mode == DEC_PROTECT)
1983		      && (SCRN_BUF_ATTRS(screen, row)[col] & PROTECTED))) {
1984		    SCRN_BUF_ATTRS(screen, row)[col] |= CHARDRAWN;
1985		    SCRN_BUF_CHARS(screen, row)[col] = ' ';
1986		    if_OPT_WIDE_CHARS(screen, {
1987			int off;
1988			for (off = OFF_WIDEC; off < MAX_PTRS; ++off) {
1989			    memset(SCREEN_PTR(screen, row, off) + col, 0, 1);
1990			}
1991		    })
1992		}
1993	    }
1994	}
1995	ScrnUpdate(xw,
1996		   (target->top - 1),
1997		   (target->left - 1),
1998		   (target->bottom - target->top) + 1,
1999		   ((target->right - target->left) + 1),
2000		   False);
2001    }
2002}
2003#endif /* OPT_DEC_RECTOPS */
2004