util.c revision ae137402
1/* $XTermId: util.c,v 1.878 2021/06/06 23:14:52 Stelios.Bounanos 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/* util.c */
56
57#include <xterm.h>
58
59#include <data.h>
60#include <error.h>
61#include <menu.h>
62#include <fontutils.h>
63#include <xstrings.h>
64
65#include <stdio.h>
66#include <string.h>
67#include <ctype.h>
68#include <assert.h>
69
70#if OPT_WIDE_CHARS
71#if defined(HAVE_WCHAR_H) && defined(HAVE_WCWIDTH)
72#include <wchar.h>
73#endif
74#include <wcwidth.h>
75#endif
76
77#ifdef HAVE_X11_EXTENSIONS_XINERAMA_H
78#include <X11/extensions/Xinerama.h>
79#endif /* HAVE_X11_EXTENSIONS_XINERAMA_H */
80
81#include <graphics.h>
82
83static int handle_translated_exposure(XtermWidget xw,
84				      int rect_x,
85				      int rect_y,
86				      int rect_width,
87				      int rect_height);
88static void ClearLeft(XtermWidget xw);
89static void CopyWait(XtermWidget xw);
90static void horizontal_copy_area(XtermWidget xw,
91				 int firstchar,
92				 int nchars,
93				 int amount);
94static void vertical_copy_area(XtermWidget xw,
95			       int firstline,
96			       int nlines,
97			       int amount,
98			       int left,
99			       int right);
100
101#if OPT_WIDE_CHARS
102unsigned first_widechar;
103int (*my_wcwidth) (wchar_t);
104#endif
105
106#if OPT_WIDE_CHARS
107/*
108 * We will modify the 'n' cells beginning at the current position.
109 * Some of those cells may be part of multi-column characters, including
110 * carryover from the left.  Find the limits of the multi-column characters
111 * that we should fill with blanks, return true if filling is needed.
112 */
113int
114DamagedCells(TScreen *screen, unsigned n, int *klp, int *krp, int row, int col)
115{
116    CLineData *ld = getLineData(screen, row);
117    int result = False;
118
119    assert(ld);
120    if (col < (int) ld->lineSize) {
121	int nn = (int) n;
122	int kl = col;
123	int kr = col + nn;
124
125	if (kr >= (int) ld->lineSize) {
126	    nn = (ld->lineSize - col - 1);
127	    kr = col + nn;
128	}
129
130	if (nn > 0) {
131	    assert(kl < (int) ld->lineSize);
132	    if (ld->charData[kl] == HIDDEN_CHAR) {
133		while (kl > 0) {
134		    if (ld->charData[--kl] != HIDDEN_CHAR) {
135			break;
136		    }
137		}
138	    } else {
139		kl = col + 1;
140	    }
141
142	    assert(kr < (int) ld->lineSize);
143	    if (ld->charData[kr] == HIDDEN_CHAR) {
144		while (kr < screen->max_col) {
145		    assert((kr + 1) < (int) ld->lineSize);
146		    if (ld->charData[++kr] != HIDDEN_CHAR) {
147			--kr;
148			break;
149		    }
150		}
151	    } else {
152		kr = col - 1;
153	    }
154
155	    if (klp)
156		*klp = kl;
157	    if (krp)
158		*krp = kr;
159	    result = (kr >= kl);
160	}
161    }
162
163    return result;
164}
165
166int
167DamagedCurCells(TScreen *screen, unsigned n, int *klp, int *krp)
168{
169    return DamagedCells(screen, n, klp, krp, screen->cur_row, screen->cur_col);
170}
171#endif /* OPT_WIDE_CHARS */
172
173/*
174 * These routines are used for the jump scroll feature
175 */
176void
177FlushScroll(XtermWidget xw)
178{
179    TScreen *screen = TScreenOf(xw);
180    int i;
181    int shift = INX2ROW(screen, 0);
182    int bot = screen->max_row - shift;
183    int refreshtop;
184    int refreshheight;
185    int scrolltop;
186    int scrollheight;
187    int left = ScrnLeftMargin(xw);
188    int right = ScrnRightMargin(xw);
189    Boolean full_lines = (Boolean) ((left == 0) && (right == screen->max_col));
190
191    if (screen->cursor_state)
192	HideCursor(xw);
193
194    TRACE(("FlushScroll %s-lines scroll:%d refresh %d\n",
195	   full_lines ? "full" : "partial",
196	   screen->scroll_amt,
197	   screen->refresh_amt));
198
199    if (screen->scroll_amt > 0) {
200	/*
201	 * Lines will be scrolled "up".
202	 */
203	refreshheight = screen->refresh_amt;
204	scrollheight = screen->bot_marg - screen->top_marg - refreshheight + 1;
205	refreshtop = screen->bot_marg - refreshheight + 1 + shift;
206	i = screen->max_row - screen->scroll_amt + 1;
207	if (refreshtop > i) {
208	    refreshtop = i;
209	}
210
211	/*
212	 * If this is the normal (not alternate) screen, and the top margin is
213	 * at the top of the screen, then we will shift full lines scrolled out
214	 * of the scrolling region into the saved-lines.
215	 */
216	if (screen->scrollWidget
217	    && !screen->whichBuf
218	    && full_lines
219	    && screen->top_marg == 0) {
220	    scrolltop = 0;
221	    scrollheight += shift;
222	    if (scrollheight > i)
223		scrollheight = i;
224	    i = screen->bot_marg - bot;
225	    if (i > 0) {
226		refreshheight -= i;
227		if (refreshheight < screen->scroll_amt) {
228		    refreshheight = screen->scroll_amt;
229		}
230	    }
231	    i = screen->savedlines;
232	    if (i < screen->savelines) {
233		i += screen->scroll_amt;
234		if (i > screen->savelines) {
235		    i = screen->savelines;
236		}
237		screen->savedlines = i;
238		ScrollBarDrawThumb(xw, 1);
239	    }
240	} else {
241	    scrolltop = screen->top_marg + shift;
242	    i = bot - (screen->bot_marg - screen->refresh_amt + screen->scroll_amt);
243	    if (i > 0) {
244		if (bot < screen->bot_marg) {
245		    refreshheight = screen->scroll_amt + i;
246		}
247	    } else {
248		scrollheight += i;
249		refreshheight = screen->scroll_amt;
250		i = screen->top_marg + screen->scroll_amt - 1 - bot;
251		if (i > 0) {
252		    refreshtop += i;
253		    refreshheight -= i;
254		}
255	    }
256	}
257    } else {
258	/*
259	 * Lines will be scrolled "down".
260	 */
261	refreshheight = -screen->refresh_amt;
262	scrollheight = screen->bot_marg - screen->top_marg - refreshheight + 1;
263	refreshtop = screen->top_marg + shift;
264	scrolltop = refreshtop + refreshheight;
265	i = screen->bot_marg - bot;
266	if (i > 0) {
267	    scrollheight -= i;
268	}
269	i = screen->top_marg + refreshheight - 1 - bot;
270	if (i > 0) {
271	    refreshheight -= i;
272	}
273    }
274
275    vertical_copy_area(xw,
276		       scrolltop + screen->scroll_amt,
277		       scrollheight,
278		       screen->scroll_amt,
279		       left,
280		       right);
281    ScrollSelection(screen, -(screen->scroll_amt), False);
282    screen->scroll_amt = 0;
283    screen->refresh_amt = 0;
284
285    if (refreshheight > 0) {
286	ClearCurBackground(xw,
287			   refreshtop,
288			   left,
289			   (unsigned) refreshheight,
290			   (unsigned) (right + 1 - left),
291			   (unsigned) FontWidth(screen));
292	ScrnRefresh(xw,
293		    refreshtop,
294		    0,
295		    refreshheight,
296		    MaxCols(screen),
297		    False);
298    }
299    xtermTimedDbe(xw);
300    return;
301}
302
303/*
304 * Returns true if there are lines off-screen due to scrolling which should
305 * include the current line.  If false, the line is visible and we should
306 * paint it now rather than waiting for the line to become visible.
307 */
308static Bool
309AddToRefresh(XtermWidget xw)
310{
311    TScreen *screen = TScreenOf(xw);
312    int amount = screen->refresh_amt;
313    int row = screen->cur_row;
314    Bool result;
315
316    if (amount == 0) {
317	result = False;
318    } else if (amount > 0) {
319	int bottom;
320
321	if (row == (bottom = screen->bot_marg) - amount) {
322	    screen->refresh_amt++;
323	    result = True;
324	} else {
325	    result = (row >= bottom - amount + 1 && row <= bottom);
326	}
327    } else {
328	int top;
329
330	amount = -amount;
331	if (row == (top = screen->top_marg) + amount) {
332	    screen->refresh_amt--;
333	    result = True;
334	} else {
335	    result = (row <= top + amount - 1 && row >= top);
336	}
337    }
338
339    /*
340     * If this line is visible, and there are scrolled-off lines, flush out
341     * those which are now visible.
342     */
343    if (!result && screen->scroll_amt)
344	FlushScroll(xw);
345
346    return result;
347}
348
349/*
350 * Returns true if the current row is in the visible area (it should be for
351 * screen operations) and incidentally flush the scrolled-in lines which
352 * have newly become visible.
353 */
354static Bool
355AddToVisible(XtermWidget xw)
356{
357    TScreen *screen = TScreenOf(xw);
358    Bool result = False;
359
360    if (INX2ROW(screen, screen->cur_row) <= screen->max_row) {
361	if (!AddToRefresh(xw)) {
362	    result = True;
363	}
364    }
365    return result;
366}
367
368/*
369 * If we're scrolling, leave the selection intact if possible.
370 * If it will bump into one of the extremes of the saved-lines, truncate that.
371 * If the selection is not entirely contained within the margins and not
372 * entirely outside the margins, clear it.
373 */
374static void
375adjustHiliteOnFwdScroll(XtermWidget xw, int amount, Bool all_lines)
376{
377    TScreen *screen = TScreenOf(xw);
378    int lo_row = (all_lines
379		  ? (screen->bot_marg - screen->savelines)
380		  : screen->top_marg);
381    int hi_row = screen->bot_marg;
382    int left = ScrnLeftMargin(xw);
383    int right = ScrnRightMargin(xw);
384
385    TRACE2(("adjustSelection FWD %s by %d (%s)\n",
386	    screen->whichBuf ? "alternate" : "normal",
387	    amount,
388	    all_lines ? "all" : "visible"));
389    TRACE2(("  before highlite %d.%d .. %d.%d\n",
390	    screen->startH.row,
391	    screen->startH.col,
392	    screen->endH.row,
393	    screen->endH.col));
394    TRACE2(("  margins %d..%d\n", screen->top_marg, screen->bot_marg));
395    TRACE2(("  limits  %d..%d\n", lo_row, hi_row));
396
397    if ((left > 0 || right < screen->max_col) &&
398	((screen->startH.row >= lo_row &&
399	  screen->startH.row - amount <= hi_row) ||
400	 (screen->endH.row >= lo_row &&
401	  screen->endH.row - amount <= hi_row))) {
402	/*
403	 * This could be improved slightly by excluding the special case where
404	 * the selection is on a single line outside left/right margins.
405	 */
406	TRACE2(("deselect because selection overlaps with scrolled partial-line\n"));
407	ScrnDisownSelection(xw);
408    } else if (screen->startH.row >= lo_row
409	       && screen->startH.row - amount < lo_row) {
410	/* truncate the selection because its start would move out of region */
411	if (lo_row + amount <= screen->endH.row) {
412	    TRACE2(("truncate selection by changing start %d.%d to %d.%d\n",
413		    screen->startH.row,
414		    screen->startH.col,
415		    lo_row + amount,
416		    0));
417	    screen->startH.row = lo_row + amount;
418	    screen->startH.col = 0;
419	} else {
420	    TRACE2(("deselect because %d.%d .. %d.%d shifted %d is outside margins %d..%d\n",
421		    screen->startH.row,
422		    screen->startH.col,
423		    screen->endH.row,
424		    screen->endH.col,
425		    -amount,
426		    lo_row,
427		    hi_row));
428	    ScrnDisownSelection(xw);
429	}
430    } else if (screen->startH.row <= hi_row && screen->endH.row > hi_row) {
431	TRACE2(("deselect because selection straddles top-margin\n"));
432	ScrnDisownSelection(xw);
433    } else if (screen->startH.row < lo_row && screen->endH.row > lo_row) {
434	TRACE2(("deselect because selection straddles bottom-margin\n"));
435	ScrnDisownSelection(xw);
436    }
437
438    TRACE2(("  after highlite %d.%d .. %d.%d\n",
439	    screen->startH.row,
440	    screen->startH.col,
441	    screen->endH.row,
442	    screen->endH.col));
443}
444
445/*
446 * This is the same as adjustHiliteOnFwdScroll(), but reversed.  In this case,
447 * only the visible lines are affected.
448 */
449static void
450adjustHiliteOnBakScroll(XtermWidget xw, int amount)
451{
452    TScreen *screen = TScreenOf(xw);
453    int lo_row = screen->top_marg;
454    int hi_row = screen->bot_marg;
455
456    TRACE2(("adjustSelection BAK %s by %d (%s)\n",
457	    screen->whichBuf ? "alternate" : "normal",
458	    amount,
459	    "visible"));
460    TRACE2(("  before highlite %d.%d .. %d.%d\n",
461	    screen->startH.row,
462	    screen->startH.col,
463	    screen->endH.row,
464	    screen->endH.col));
465    TRACE2(("  margins %d..%d\n", screen->top_marg, screen->bot_marg));
466
467    if (screen->endH.row >= hi_row
468	&& screen->endH.row + amount > hi_row) {
469	/* truncate the selection because its start would move out of region */
470	if (hi_row - amount >= screen->startH.row) {
471	    TRACE2(("truncate selection by changing start %d.%d to %d.%d\n",
472		    screen->startH.row,
473		    screen->startH.col,
474		    hi_row - amount,
475		    0));
476	    screen->endH.row = hi_row - amount;
477	    screen->endH.col = 0;
478	} else {
479	    TRACE2(("deselect because %d.%d .. %d.%d shifted %d is outside margins %d..%d\n",
480		    screen->startH.row,
481		    screen->startH.col,
482		    screen->endH.row,
483		    screen->endH.col,
484		    amount,
485		    lo_row,
486		    hi_row));
487	    ScrnDisownSelection(xw);
488	}
489    } else if (screen->endH.row >= lo_row && screen->startH.row < lo_row) {
490	ScrnDisownSelection(xw);
491    } else if (screen->endH.row > hi_row && screen->startH.row > hi_row) {
492	ScrnDisownSelection(xw);
493    }
494
495    TRACE2(("  after highlite %d.%d .. %d.%d\n",
496	    screen->startH.row,
497	    screen->startH.col,
498	    screen->endH.row,
499	    screen->endH.col));
500}
501
502/*
503 * Move cells in LineData's on the current screen to simulate scrolling by the
504 * given amount of lines.
505 */
506static void
507scrollInMargins(XtermWidget xw, int amount, int top)
508{
509    TScreen *screen = TScreenOf(xw);
510    LineData *src;
511    LineData *dst;
512    int row;
513    int left = ScrnLeftMargin(xw);
514    int right = ScrnRightMargin(xw);
515    int length = right + 1 - left;
516
517    if_OPT_WIDE_CHARS(screen, {
518	if (amount != 0) {
519	    for (row = top; row <= screen->bot_marg; ++row) {
520		LineData *ld;
521		if ((ld = getLineData(screen, row + amount)) != 0) {
522		    if (left > 0) {
523			if (ld->charData[left] == HIDDEN_CHAR) {
524			    Clear1Cell(ld, left - 1);
525			    Clear1Cell(ld, left);
526			}
527		    }
528		    if (right + 1 < (int) ld->lineSize) {
529			if (ld->charData[right + 1] == HIDDEN_CHAR) {
530			    Clear1Cell(ld, right);
531			    Clear1Cell(ld, right + 1);
532			}
533		    }
534		}
535	    }
536	}
537    });
538
539    if (amount > 0) {
540	for (row = top; row <= screen->bot_marg - amount; ++row) {
541	    if ((src = getLineData(screen, row + amount)) != 0
542		&& (dst = getLineData(screen, row)) != 0) {
543		CopyCells(screen, src, dst, left, length, False);
544	    }
545	}
546	while (row <= screen->bot_marg) {
547	    ClearCells(xw, 0, (unsigned) length, row, left);
548	    ++row;
549	}
550    } else if (amount < 0) {
551	for (row = screen->bot_marg; row >= top - amount; --row) {
552	    if ((src = getLineData(screen, row + amount)) != 0
553		&& (dst = getLineData(screen, row)) != 0) {
554		CopyCells(screen, src, dst, left, length, True);
555	    }
556	}
557	while (row >= top) {
558	    ClearCells(xw, 0, (unsigned) length, row, left);
559	    --row;
560	}
561    }
562}
563
564#if OPT_WIDE_CHARS
565/*
566 * If we're repainting a section of wide-characters that, e.g., ClearCells has
567 * repaired when finding double-cell characters, then we should account for
568 * that in the repaint.
569 */
570static void
571ScrnUpdate2(XtermWidget xw,
572	    int toprow,
573	    int leftcol,
574	    int nrows,
575	    int ncols,
576	    Bool force)
577{
578    if_OPT_WIDE_CHARS(TScreenOf(xw), {
579	if (leftcol + ncols <= TScreenOf(xw)->max_col)
580	    ncols++;
581	if (leftcol > 0) {
582	    leftcol--;
583	    ncols++;
584	}
585    });
586    ScrnUpdate(xw, toprow, leftcol, nrows, ncols, force);
587}
588#else
589#define ScrnUpdate2(xw, toprow, leftcol, nrows, ncols, force) \
590	ScrnUpdate(xw, toprow, leftcol, nrows, ncols, force)
591#endif
592
593/*
594 * scrolls the screen by amount lines, erases bottom, doesn't alter
595 * cursor position (i.e. cursor moves down amount relative to text).
596 * All done within the scrolling region, of course.
597 * requires: amount > 0
598 */
599void
600xtermScroll(XtermWidget xw, int amount)
601{
602    TScreen *screen = TScreenOf(xw);
603    int i;
604    int refreshtop = 0;
605    int refreshheight;
606    Boolean save_wrap = screen->do_wrap;
607    int left = ScrnLeftMargin(xw);
608    int right = ScrnRightMargin(xw);
609    Boolean scroll_all_lines = (Boolean) (screen->scrollWidget
610					  && !screen->whichBuf
611					  && screen->top_marg == 0);
612    Boolean scroll_full_line = ((left == 0) && (right == screen->max_col));
613
614    TRACE(("xtermScroll count=%d\n", amount));
615
616    screen->cursor_busy += 1;
617    screen->cursor_moved = True;
618
619    if (screen->cursor_state)
620	HideCursor(xw);
621
622    i = screen->bot_marg - screen->top_marg + 1;
623    if (amount > i)
624	amount = i;
625
626    if (!scroll_full_line) {
627	refreshheight = 0;
628    } else
629#if OPT_SCROLL_LOCK
630	if ((screen->allowScrollLock && screen->scroll_lock)
631	    || (screen->autoScrollLock && screen->topline < 0)) {
632	refreshheight = 0;
633	screen->scroll_amt = 0;
634	screen->refresh_amt = 0;
635	if (--(screen->topline) < -screen->savelines) {
636	    screen->topline = -screen->savelines;
637	    screen->scroll_dirty = True;
638	}
639	if (++(screen->savedlines) > screen->savelines) {
640	    screen->savedlines = screen->savelines;
641	}
642    } else
643#endif
644    {
645	if (ScrnHaveSelection(screen))
646	    adjustHiliteOnFwdScroll(xw, amount, scroll_all_lines);
647
648	if (screen->jumpscroll) {
649	    if (screen->scroll_amt > 0) {
650		if (!screen->fastscroll) {
651		    if (screen->refresh_amt + amount > i)
652			FlushScroll(xw);
653		}
654		screen->scroll_amt += amount;
655		screen->refresh_amt += amount;
656	    } else {
657		if (!screen->fastscroll) {
658		    if (screen->scroll_amt < 0)
659			FlushScroll(xw);
660		}
661		screen->scroll_amt = amount;
662		screen->refresh_amt = amount;
663	    }
664	    refreshheight = 0;
665	} else {
666	    int scrolltop;
667	    int scrollheight;
668	    int shift;
669	    int bot;
670
671	    ScrollSelection(screen, -(amount), False);
672	    if (amount == i) {
673		ClearScreen(xw);
674		goto done;
675	    }
676
677	    shift = INX2ROW(screen, 0);
678	    bot = screen->max_row - shift;
679	    scrollheight = i - amount;
680	    refreshheight = amount;
681
682	    if ((refreshtop = screen->bot_marg - refreshheight + 1 + shift) >
683		(i = screen->max_row - refreshheight + 1))
684		refreshtop = i;
685
686	    if (scroll_all_lines) {
687		scrolltop = 0;
688		if ((scrollheight += shift) > i)
689		    scrollheight = i;
690		if ((i = screen->savedlines) < screen->savelines) {
691		    if ((i += amount) > screen->savelines)
692			i = screen->savelines;
693		    screen->savedlines = i;
694		    ScrollBarDrawThumb(xw, 1);
695		}
696	    } else {
697		scrolltop = screen->top_marg + shift;
698		if ((i = screen->bot_marg - bot) > 0) {
699		    scrollheight -= i;
700		    if ((i = screen->top_marg + amount - 1 - bot) >= 0) {
701			refreshtop += i;
702			refreshheight -= i;
703		    }
704		}
705	    }
706
707	    if (screen->multiscroll && amount == 1 &&
708		screen->topline == 0 && screen->top_marg == 0 &&
709		screen->bot_marg == screen->max_row) {
710		if (screen->incopy < 0 && screen->scrolls == 0)
711		    CopyWait(xw);
712		screen->scrolls++;
713	    }
714
715	    vertical_copy_area(xw,
716			       scrolltop + amount,
717			       scrollheight,
718			       amount,
719			       left,
720			       right);
721
722	    if (refreshheight > 0) {
723		ClearCurBackground(xw,
724				   refreshtop,
725				   left,
726				   (unsigned) refreshheight,
727				   (unsigned) (right + 1 - left),
728				   (unsigned) FontWidth(screen));
729		if (refreshheight > shift)
730		    refreshheight = shift;
731	    }
732	}
733    }
734
735    if (amount > 0) {
736	if (left > 0 || right < screen->max_col) {
737	    scrollInMargins(xw, amount, screen->top_marg);
738	    ScrnUpdate2(xw,
739			screen->top_marg,
740			left,
741			screen->bot_marg + 1 - screen->top_marg,
742			right + 1 - left,
743			True);
744	} else if (scroll_all_lines) {
745	    ScrnDeleteLine(xw,
746			   screen->saveBuf_index,
747			   screen->bot_marg + screen->savelines,
748			   0,
749			   (unsigned) amount);
750	} else {
751	    ScrnDeleteLine(xw,
752			   screen->visbuf,
753			   screen->bot_marg,
754			   screen->top_marg,
755			   (unsigned) amount);
756	}
757    }
758
759    scroll_displayed_graphics(xw, amount);
760
761    if (refreshheight > 0) {
762	ScrnRefresh(xw,
763		    refreshtop,
764		    left,
765		    refreshheight,
766		    right + 1 - left,
767		    False);
768    }
769
770  done:
771    screen->do_wrap = save_wrap;
772    screen->cursor_busy -= 1;
773    return;
774}
775
776/*
777 * This is from ISO 6429, not found in any of DEC's terminals.
778 */
779void
780xtermScrollLR(XtermWidget xw, int amount, Bool toLeft)
781{
782    if (amount > 0) {
783	xtermColScroll(xw, amount, toLeft, ScrnLeftMargin(xw));
784    }
785}
786
787/*
788 * Implement DECBI/DECFI (back/forward column index)
789 */
790void
791xtermColIndex(XtermWidget xw, Bool toLeft)
792{
793    TScreen *screen = TScreenOf(xw);
794
795    if (toLeft) {
796	if (ScrnIsColInMargins(screen, screen->cur_col)) {
797	    if (screen->cur_col == ScrnLeftMargin(xw)) {
798		xtermColScroll(xw, 1, False, screen->cur_col);
799	    } else {
800		CursorBack(xw, 1);
801	    }
802	} else {
803	    CursorBack(xw, 1);
804	}
805    } else {
806	if (ScrnIsColInMargins(screen, screen->cur_col)) {
807	    if (screen->cur_col == ScrnRightMargin(xw)) {
808		xtermColScroll(xw, 1, True, ScrnLeftMargin(xw));
809	    } else {
810		CursorForward(xw, 1);
811	    }
812	} else {
813	    CursorForward(xw, 1);
814	}
815    }
816}
817
818/*
819 * Implement DECDC/DECIC (delete/insert column)
820 */
821void
822xtermColScroll(XtermWidget xw, int amount, Bool toLeft, int at_col)
823{
824    TScreen *screen = TScreenOf(xw);
825
826    if (amount > 0) {
827	int min_row;
828	int max_row;
829
830	if (ScrnHaveRowMargins(screen)) {
831	    min_row = screen->top_marg;
832	    max_row = screen->bot_marg;
833	} else {
834	    min_row = 0;
835	    max_row = screen->max_row;
836	}
837
838	if (screen->cur_row >= min_row
839	    && screen->cur_row <= max_row
840	    && screen->cur_col >= screen->lft_marg
841	    && screen->cur_col <= screen->rgt_marg) {
842	    int save_row = screen->cur_row;
843	    int save_col = screen->cur_col;
844	    int row;
845
846	    screen->cur_col = at_col;
847	    if (toLeft) {
848		for (row = min_row; row <= max_row; row++) {
849		    screen->cur_row = row;
850		    ScrnDeleteChar(xw, (unsigned) amount);
851		}
852	    } else {
853		for (row = min_row; row <= max_row; row++) {
854		    screen->cur_row = row;
855		    ScrnInsertChar(xw, (unsigned) amount);
856		}
857	    }
858	    screen->cur_row = save_row;
859	    screen->cur_col = save_col;
860	    xtermRepaint(xw);
861	}
862    }
863}
864
865/*
866 * Reverse scrolls the screen by amount lines, erases top, doesn't alter
867 * cursor position (i.e. cursor moves up amount relative to text).
868 * All done within the scrolling region, of course.
869 * Requires: amount > 0
870 */
871void
872RevScroll(XtermWidget xw, int amount)
873{
874    TScreen *screen = TScreenOf(xw);
875    int i = screen->bot_marg - screen->top_marg + 1;
876    int left = ScrnLeftMargin(xw);
877    int right = ScrnRightMargin(xw);
878    Boolean scroll_full_line = ((left == 0) && (right == screen->max_col));
879
880    TRACE(("RevScroll count=%d\n", amount));
881
882    screen->cursor_busy += 1;
883    screen->cursor_moved = True;
884
885    if (screen->cursor_state)
886	HideCursor(xw);
887
888    if (amount > i)
889	amount = i;
890
891    if (ScrnHaveSelection(screen))
892	adjustHiliteOnBakScroll(xw, amount);
893
894    if (!scroll_full_line) {
895	;
896    } else if (screen->jumpscroll) {
897	if (screen->scroll_amt < 0) {
898	    if (-screen->refresh_amt + amount > i)
899		FlushScroll(xw);
900	    screen->scroll_amt -= amount;
901	    screen->refresh_amt -= amount;
902	} else {
903	    if (screen->scroll_amt > 0)
904		FlushScroll(xw);
905	    screen->scroll_amt = -amount;
906	    screen->refresh_amt = -amount;
907	}
908    } else {
909	int shift = INX2ROW(screen, 0);
910	int bot = screen->max_row - shift;
911	int refreshheight = amount;
912	int refreshtop = screen->top_marg + shift;
913	int scrollheight = (screen->bot_marg
914			    - screen->top_marg - refreshheight + 1);
915	int scrolltop = refreshtop + refreshheight;
916
917	if ((i = screen->bot_marg - bot) > 0)
918	    scrollheight -= i;
919	if ((i = screen->top_marg + refreshheight - 1 - bot) > 0)
920	    refreshheight -= i;
921
922	if (screen->multiscroll && amount == 1 &&
923	    screen->topline == 0 && screen->top_marg == 0 &&
924	    screen->bot_marg == screen->max_row) {
925	    if (screen->incopy < 0 && screen->scrolls == 0)
926		CopyWait(xw);
927	    screen->scrolls++;
928	}
929
930	vertical_copy_area(xw,
931			   scrolltop - amount,
932			   scrollheight,
933			   -amount,
934			   left,
935			   right);
936
937	if (refreshheight > 0) {
938	    ClearCurBackground(xw,
939			       refreshtop,
940			       left,
941			       (unsigned) refreshheight,
942			       (unsigned) (right + 1 - left),
943			       (unsigned) FontWidth(screen));
944	}
945    }
946    if (amount > 0) {
947	if (left > 0 || right < screen->max_col) {
948	    scrollInMargins(xw, -amount, screen->top_marg);
949	    ScrnUpdate2(xw,
950			screen->top_marg,
951			left,
952			screen->bot_marg + 1 - screen->top_marg,
953			right + 1 - left,
954			True);
955	} else {
956	    ScrnInsertLine(xw,
957			   screen->visbuf,
958			   screen->bot_marg,
959			   screen->top_marg,
960			   (unsigned) amount);
961	}
962    }
963    screen->cursor_busy -= 1;
964    return;
965}
966
967#if OPT_ZICONBEEP
968void
969initZIconBeep(void)
970{
971    if (resource.zIconBeep > 100 || resource.zIconBeep < -100) {
972	resource.zIconBeep = 0;	/* was 100, but I prefer to defaulting off. */
973	xtermWarning("a number between -100 and 100 is required for zIconBeep.  0 used by default\n");
974    }
975}
976
977static char *
978getIconName(void)
979{
980    static char *icon_name;
981    static Arg args[] =
982    {
983	{XtNiconName, (XtArgVal) & icon_name}
984    };
985
986    icon_name = NULL;
987    XtGetValues(toplevel, args, XtNumber(args));
988    return icon_name;
989}
990
991static void
992setZIconBeep(XtermWidget xw)
993{
994    TScreen *screen = TScreenOf(xw);
995
996    /* Flag icon name with "***"  on window output when iconified.
997     */
998    if (resource.zIconBeep && mapstate == IsUnmapped && !screen->zIconBeep_flagged) {
999	char *icon_name = getIconName();
1000	if (icon_name != NULL) {
1001	    screen->zIconBeep_flagged = True;
1002	    ChangeIconName(xw, icon_name);
1003	}
1004	xtermBell(xw, XkbBI_Info, 0);
1005    }
1006    mapstate = -1;
1007}
1008
1009/*
1010 * If warning should be given then give it
1011 */
1012Boolean
1013showZIconBeep(XtermWidget xw, char *name)
1014{
1015    Boolean code = False;
1016
1017    if (resource.zIconBeep && TScreenOf(xw)->zIconBeep_flagged) {
1018	char *format = resource.zIconFormat;
1019	char *newname = malloc(strlen(name) + strlen(format) + 2);
1020	if (!newname) {
1021	    xtermWarning("malloc failed in showZIconBeep\n");
1022	} else {
1023	    char *marker = strstr(format, "%s");
1024	    char *result = newname;
1025	    if (marker != 0) {
1026		size_t skip = (size_t) (marker - format);
1027		if (skip) {
1028		    strncpy(result, format, skip);
1029		    result += skip;
1030		}
1031		strcpy(result, name);
1032		strcat(result, marker + 2);
1033	    } else {
1034		strcpy(result, format);
1035		strcat(result, name);
1036	    }
1037	    ChangeGroup(xw, XtNiconName, newname);
1038	    free(newname);
1039	}
1040	code = True;
1041    }
1042    return code;
1043}
1044
1045/*
1046 * Restore the icon name, resetting the state for zIconBeep.
1047 */
1048void
1049resetZIconBeep(XtermWidget xw)
1050{
1051    TScreen *screen = TScreenOf(xw);
1052
1053    if (screen->zIconBeep_flagged) {
1054	char *icon_name = getIconName();
1055	screen->zIconBeep_flagged = False;
1056	if (icon_name != NULL) {
1057	    char *buf = malloc(strlen(icon_name) + 1);
1058	    if (buf == NULL) {
1059		screen->zIconBeep_flagged = True;
1060	    } else {
1061		char *format = resource.zIconFormat;
1062		char *marker = strstr(format, "%s");
1063		Boolean found = False;
1064
1065		if (marker != 0) {
1066		    if (marker == format
1067			|| !strncmp(icon_name, format, (size_t) (marker - format))) {
1068			found = True;
1069			strcpy(buf, icon_name + (marker - format));
1070			marker += 2;
1071			if (*marker != '\0') {
1072			    size_t len_m = strlen(marker);
1073			    size_t len_b = strlen(buf);
1074			    if (len_m < len_b
1075				&& !strcmp(buf + len_b - len_m, marker)) {
1076				buf[len_b - len_m] = '\0';
1077			    }
1078			}
1079		    }
1080		} else if (!strncmp(icon_name, format, strlen(format))) {
1081		    strcpy(buf, icon_name + strlen(format));
1082		    found = True;
1083		}
1084		if (found)
1085		    ChangeIconName(xw, buf);
1086		free(buf);
1087	    }
1088	}
1089    }
1090}
1091#else
1092#define setZIconBeep(xw)	/* nothing */
1093#endif /* OPT_ZICONBEEP */
1094
1095/*
1096 * write a string str of length len onto the screen at
1097 * the current cursor position.  update cursor position.
1098 */
1099void
1100WriteText(XtermWidget xw, IChar *str, Cardinal len)
1101{
1102    TScreen *screen = TScreenOf(xw);
1103    XTermDraw params;
1104    CLineData *ld = 0;
1105    unsigned attr_flags = xw->flags;
1106    CellColor fg_bg = xtermColorPair(xw);
1107    unsigned cells = visual_width(str, len);
1108    GC currentGC;
1109
1110    TRACE(("WriteText %d (%2d,%2d) %3d:%s\n",
1111	   screen->topline,
1112	   screen->cur_row,
1113	   screen->cur_col,
1114	   len, visibleIChars(str, len)));
1115
1116    if (cells + (unsigned) screen->cur_col > (unsigned) MaxCols(screen)) {
1117	cells = (unsigned) (MaxCols(screen) - screen->cur_col);
1118    }
1119
1120    if (ScrnHaveSelection(screen)
1121	&& ScrnIsRowInSelection(screen, INX2ROW(screen, screen->cur_row))) {
1122	ScrnDisownSelection(xw);
1123    }
1124#if OPT_ISO_COLORS
1125    /* if colorBDMode is set, and enabled */
1126    if (screen->colorBDMode &&
1127	screen->boldColors &&
1128	!hasDirectFG(attr_flags) &&
1129    /* and bold foreground color on bold background color */
1130	GetCellColorFG(fg_bg) > COLOR_7 &&
1131	GetCellColorFG(fg_bg) < MIN_ANSI_COLORS &&
1132    /* and both colors are the same */
1133	GetCellColorFG(fg_bg) == GetCellColorBG(fg_bg))
1134	/* clear BOLD flag, else it will be colorBD on bold background color */
1135	UIntClr(attr_flags, BOLD);
1136#endif
1137
1138    /* if we are in insert-mode, reserve space for the new cells */
1139    if (attr_flags & INSERT) {
1140	InsertChar(xw, cells);
1141    }
1142
1143    if (AddToVisible(xw)
1144	&& ((ld = getLineData(screen, screen->cur_row))) != 0) {
1145	unsigned test;
1146
1147	if (screen->cursor_state)
1148	    HideCursor(xw);
1149
1150	/*
1151	 * If we overwrite part of a multi-column character, fill the rest
1152	 * of it with blanks.
1153	 */
1154	if_OPT_WIDE_CHARS(screen, {
1155	    int kl;
1156	    int kr;
1157	    if (DamagedCurCells(screen, cells, &kl, &kr))
1158		ClearInLine(xw, screen->cur_row, kl, (unsigned) (kr - kl + 1));
1159	});
1160
1161	if (attr_flags & INVISIBLE) {
1162	    Cardinal n;
1163	    for (n = 0; n < cells; ++n)
1164		str[n] = ' ';
1165	}
1166
1167	TRACE(("WriteText calling drawXtermText (%d) (%d,%d)\n",
1168	       LineCharSet(screen, ld),
1169	       screen->cur_row,
1170	       screen->cur_col));
1171
1172	test = attr_flags;
1173#if OPT_ISO_COLORS
1174	{
1175	    int fg;
1176	    if (screen->colorAttrMode) {
1177		fg = MapToColorMode(xw->cur_foreground, screen, attr_flags);
1178	    } else {
1179		fg = xw->cur_foreground;
1180	    }
1181	    checkVeryBoldColors(test, fg);
1182	}
1183#endif
1184
1185	/* make sure that the correct GC is current */
1186	currentGC = updatedXtermGC(xw, attr_flags, fg_bg, False);
1187
1188	/* *INDENT-EQLS* */
1189	params.xw          = xw;
1190	params.attr_flags  = (test & DRAWX_MASK);
1191	params.draw_flags  = 0;
1192	params.this_chrset = LineCharSet(screen, ld);
1193	params.real_chrset = CSET_SWL;
1194	params.on_wide     = 0;
1195
1196	drawXtermText(&params,
1197		      currentGC,
1198		      LineCursorX(screen, ld, screen->cur_col),
1199		      CursorY(screen, screen->cur_row),
1200		      str, len);
1201
1202	resetXtermGC(xw, attr_flags, False);
1203    }
1204
1205    ScrnWriteText(xw, str, attr_flags, fg_bg, len);
1206    CursorForward(xw, (int) cells);
1207    setZIconBeep(xw);
1208    return;
1209}
1210
1211/*
1212 * If cursor not in scrolling region, returns.  Else,
1213 * inserts n blank lines at the cursor's position.  Lines above the
1214 * bottom margin are lost.
1215 */
1216void
1217InsertLine(XtermWidget xw, int n)
1218{
1219    TScreen *screen = TScreenOf(xw);
1220    int i;
1221    int left = ScrnLeftMargin(xw);
1222    int right = ScrnRightMargin(xw);
1223    Boolean scroll_full_line = ((left == 0) && (right == screen->max_col));
1224
1225    if (!ScrnIsRowInMargins(screen, screen->cur_row)
1226	|| screen->cur_col < left
1227	|| screen->cur_col > right)
1228	return;
1229
1230    TRACE(("InsertLine count=%d\n", n));
1231
1232    set_cur_col(screen, ScrnLeftMargin(xw));
1233    if (screen->cursor_state)
1234	HideCursor(xw);
1235
1236    if (ScrnHaveSelection(screen)
1237	&& ScrnAreRowsInSelection(screen,
1238				  INX2ROW(screen, screen->top_marg),
1239				  INX2ROW(screen, screen->cur_row - 1))
1240	&& ScrnAreRowsInSelection(screen,
1241				  INX2ROW(screen, screen->cur_row),
1242				  INX2ROW(screen, screen->bot_marg))) {
1243	ScrnDisownSelection(xw);
1244    }
1245
1246    ResetWrap(screen);
1247    if (n > (i = screen->bot_marg - screen->cur_row + 1))
1248	n = i;
1249    if (screen->jumpscroll && scroll_full_line) {
1250	if (screen->scroll_amt <= 0 &&
1251	    screen->cur_row <= -screen->refresh_amt) {
1252	    if (-screen->refresh_amt + n > MaxRows(screen))
1253		FlushScroll(xw);
1254	    screen->scroll_amt -= n;
1255	    screen->refresh_amt -= n;
1256	} else {
1257	    if (screen->scroll_amt)
1258		FlushScroll(xw);
1259	}
1260    }
1261    if (!screen->scroll_amt && scroll_full_line) {
1262	int shift = INX2ROW(screen, 0);
1263	int bot = screen->max_row - shift;
1264	int refreshheight = n;
1265	int refreshtop = screen->cur_row + shift;
1266	int scrolltop = refreshtop + refreshheight;
1267	int scrollheight = (screen->bot_marg
1268			    - screen->cur_row - refreshheight + 1);
1269
1270	if ((i = screen->bot_marg - bot) > 0)
1271	    scrollheight -= i;
1272	if ((i = screen->cur_row + refreshheight - 1 - bot) > 0)
1273	    refreshheight -= i;
1274	vertical_copy_area(xw, scrolltop - n, scrollheight, -n, left, right);
1275	if (refreshheight > 0) {
1276	    ClearCurBackground(xw,
1277			       refreshtop,
1278			       left,
1279			       (unsigned) refreshheight,
1280			       (unsigned) (right + 1 - left),
1281			       (unsigned) FontWidth(screen));
1282	}
1283    }
1284    if (n > 0) {
1285	if (scroll_full_line) {
1286	    ScrnInsertLine(xw,
1287			   screen->visbuf,
1288			   screen->bot_marg,
1289			   screen->cur_row,
1290			   (unsigned) n);
1291	} else {
1292	    scrollInMargins(xw, -n, screen->cur_row);
1293	    ScrnUpdate2(xw,
1294			screen->cur_row,
1295			left,
1296			screen->bot_marg + 1 - screen->cur_row,
1297			right + 1 - left,
1298			True);
1299	}
1300    }
1301}
1302
1303/*
1304 * If cursor not in scrolling region, returns.  Else, deletes n lines
1305 * at the cursor's position, lines added at bottom margin are blank.
1306 */
1307void
1308DeleteLine(XtermWidget xw, int n)
1309{
1310    TScreen *screen = TScreenOf(xw);
1311    int i;
1312    int left = ScrnLeftMargin(xw);
1313    int right = ScrnRightMargin(xw);
1314    Boolean scroll_all_lines = (Boolean) (screen->scrollWidget
1315					  && !screen->whichBuf
1316					  && screen->cur_row == 0);
1317    Boolean scroll_full_line = ((left == 0) && (right == screen->max_col));
1318
1319    if (!ScrnIsRowInMargins(screen, screen->cur_row) ||
1320	!ScrnIsColInMargins(screen, screen->cur_col))
1321	return;
1322
1323    TRACE(("DeleteLine count=%d\n", n));
1324
1325    set_cur_col(screen, ScrnLeftMargin(xw));
1326    if (screen->cursor_state)
1327	HideCursor(xw);
1328
1329    if (n > (i = screen->bot_marg - screen->cur_row + 1)) {
1330	n = i;
1331    }
1332    if (ScrnHaveSelection(screen)
1333	&& ScrnAreRowsInSelection(screen,
1334				  INX2ROW(screen, screen->cur_row),
1335				  INX2ROW(screen, screen->cur_row + n - 1))) {
1336	ScrnDisownSelection(xw);
1337    }
1338
1339    ResetWrap(screen);
1340    if (screen->jumpscroll && scroll_full_line) {
1341	if (screen->scroll_amt >= 0 && screen->cur_row == screen->top_marg) {
1342	    if (screen->refresh_amt + n > MaxRows(screen))
1343		FlushScroll(xw);
1344	    screen->scroll_amt += n;
1345	    screen->refresh_amt += n;
1346	} else {
1347	    if (screen->scroll_amt)
1348		FlushScroll(xw);
1349	}
1350    }
1351
1352    /* adjust screen->buf */
1353    if (n > 0) {
1354	if (left > 0 || right < screen->max_col) {
1355	    scrollInMargins(xw, n, screen->cur_row);
1356	} else if (scroll_all_lines) {
1357	    ScrnDeleteLine(xw,
1358			   screen->saveBuf_index,
1359			   screen->bot_marg + screen->savelines,
1360			   0,
1361			   (unsigned) n);
1362	} else {
1363	    ScrnDeleteLine(xw,
1364			   screen->visbuf,
1365			   screen->bot_marg,
1366			   screen->cur_row,
1367			   (unsigned) n);
1368	}
1369    }
1370
1371    /* repaint the screen, as needed */
1372    if (!scroll_full_line) {
1373	ScrnUpdate2(xw,
1374		    screen->cur_row,
1375		    left,
1376		    screen->bot_marg + 1 - screen->cur_row,
1377		    right + 1 - left,
1378		    True);
1379    } else if (!screen->scroll_amt) {
1380	int shift = INX2ROW(screen, 0);
1381	int bot = screen->max_row - shift;
1382	int refreshtop;
1383	int refreshheight = n;
1384	int scrolltop;
1385	int scrollheight = i - n;
1386
1387	if ((refreshtop = screen->bot_marg - refreshheight + 1 + shift) >
1388	    (i = screen->max_row - refreshheight + 1))
1389	    refreshtop = i;
1390	if (scroll_all_lines) {
1391	    scrolltop = 0;
1392	    if ((scrollheight += shift) > i)
1393		scrollheight = i;
1394	    if ((i = screen->savedlines) < screen->savelines) {
1395		if ((i += n) > screen->savelines)
1396		    i = screen->savelines;
1397		screen->savedlines = i;
1398		ScrollBarDrawThumb(xw, 1);
1399	    }
1400	} else {
1401	    scrolltop = screen->cur_row + shift;
1402	    if ((i = screen->bot_marg - bot) > 0) {
1403		scrollheight -= i;
1404		if ((i = screen->cur_row + n - 1 - bot) >= 0) {
1405		    refreshheight -= i;
1406		}
1407	    }
1408	}
1409	vertical_copy_area(xw, scrolltop + n, scrollheight, n, left, right);
1410	if (shift > 0 && refreshheight > 0) {
1411	    int rows = refreshheight;
1412	    if (rows > shift)
1413		rows = shift;
1414	    ScrnUpdate(xw, refreshtop, 0, rows, MaxCols(screen), True);
1415	    refreshtop += shift;
1416	    refreshheight -= shift;
1417	}
1418	if (refreshheight > 0) {
1419	    ClearCurBackground(xw,
1420			       refreshtop,
1421			       left,
1422			       (unsigned) refreshheight,
1423			       (unsigned) (right + 1 - left),
1424			       (unsigned) FontWidth(screen));
1425	}
1426    }
1427}
1428
1429/*
1430 * Insert n blanks at the cursor's position, no wraparound
1431 */
1432void
1433InsertChar(XtermWidget xw, unsigned n)
1434{
1435    TScreen *screen = TScreenOf(xw);
1436    CLineData *ld;
1437    unsigned limit;
1438    int row = INX2ROW(screen, screen->cur_row);
1439    int left = ScrnLeftMargin(xw);
1440    int right = ScrnRightMargin(xw);
1441
1442    if (screen->cursor_state)
1443	HideCursor(xw);
1444
1445    TRACE(("InsertChar count=%d\n", n));
1446
1447    if (ScrnHaveSelection(screen)
1448	&& ScrnIsRowInSelection(screen, row)) {
1449	ScrnDisownSelection(xw);
1450    }
1451    ResetWrap(screen);
1452
1453    limit = (unsigned) (right + 1 - screen->cur_col);
1454
1455    if (n > limit)
1456	n = limit;
1457
1458    if (screen->cur_col < left || screen->cur_col > right) {
1459	n = 0;
1460    } else if (AddToVisible(xw)
1461	       && (ld = getLineData(screen, screen->cur_row)) != 0) {
1462	int col = right + 1 - (int) n;
1463
1464	/*
1465	 * If we shift part of a multi-column character, fill the rest
1466	 * of it with blanks.  Do similar repair for the text which will
1467	 * be shifted into the right-margin.
1468	 */
1469	if_OPT_WIDE_CHARS(screen, {
1470	    int kl;
1471	    int kr = screen->cur_col;
1472	    if (DamagedCurCells(screen, n, &kl, (int *) 0) && kr > kl) {
1473		ClearInLine(xw, screen->cur_row, kl, (unsigned) (kr - kl + 1));
1474	    }
1475	    kr = screen->max_col - (int) n + 1;
1476	    if (DamagedCells(screen, n, &kl, (int *) 0,
1477			     screen->cur_row,
1478			     kr) && kr > kl) {
1479		ClearInLine(xw, screen->cur_row, kl, (unsigned) (kr - kl + 1));
1480	    }
1481	});
1482
1483#if OPT_DEC_CHRSET
1484	if (CSET_DOUBLE(GetLineDblCS(ld))) {
1485	    col = MaxCols(screen) / 2 - (int) n;
1486	}
1487#endif
1488	/*
1489	 * prevent InsertChar from shifting the end of a line over
1490	 * if it is being appended to
1491	 */
1492	if (non_blank_line(screen, screen->cur_row,
1493			   screen->cur_col, MaxCols(screen))) {
1494	    horizontal_copy_area(xw, screen->cur_col,
1495				 col - screen->cur_col,
1496				 (int) n);
1497	}
1498
1499	ClearCurBackground(xw,
1500			   INX2ROW(screen, screen->cur_row),
1501			   screen->cur_col,
1502			   1U,
1503			   n,
1504			   (unsigned) LineFontWidth(screen, ld));
1505    }
1506    if (n != 0) {
1507	/* adjust screen->buf */
1508	ScrnInsertChar(xw, n);
1509    }
1510}
1511
1512/*
1513 * Deletes n chars at the cursor's position, no wraparound.
1514 */
1515void
1516DeleteChar(XtermWidget xw, unsigned n)
1517{
1518    TScreen *screen = TScreenOf(xw);
1519    CLineData *ld;
1520    unsigned limit;
1521    int row = INX2ROW(screen, screen->cur_row);
1522    int right = ScrnRightMargin(xw);
1523
1524    if (screen->cursor_state)
1525	HideCursor(xw);
1526
1527    if (!ScrnIsColInMargins(screen, screen->cur_col))
1528	return;
1529
1530    TRACE(("DeleteChar count=%d\n", n));
1531
1532    if (ScrnHaveSelection(screen)
1533	&& ScrnIsRowInSelection(screen, row)) {
1534	ScrnDisownSelection(xw);
1535    }
1536    ResetWrap(screen);
1537
1538    limit = (unsigned) (right + 1 - screen->cur_col);
1539
1540    if (n > limit)
1541	n = limit;
1542
1543    if (AddToVisible(xw)
1544	&& (ld = getLineData(screen, screen->cur_row)) != 0) {
1545	int col = right + 1 - (int) n;
1546
1547	/*
1548	 * If we delete part of a multi-column character, fill the rest
1549	 * of it with blanks.
1550	 */
1551	if_OPT_WIDE_CHARS(screen, {
1552	    int kl;
1553	    int kr;
1554	    if (DamagedCurCells(screen, n, &kl, &kr))
1555		ClearInLine(xw, screen->cur_row, kl, (unsigned) (kr - kl + 1));
1556	});
1557
1558#if OPT_DEC_CHRSET
1559	if (CSET_DOUBLE(GetLineDblCS(ld))) {
1560	    col = MaxCols(screen) / 2 - (int) n;
1561	}
1562#endif
1563	horizontal_copy_area(xw,
1564			     (screen->cur_col + (int) n),
1565			     col - screen->cur_col,
1566			     -((int) n));
1567
1568	ClearCurBackground(xw,
1569			   INX2ROW(screen, screen->cur_row),
1570			   col,
1571			   1U,
1572			   n,
1573			   (unsigned) LineFontWidth(screen, ld));
1574    }
1575    if (n != 0) {
1576	/* adjust screen->buf */
1577	ScrnDeleteChar(xw, n);
1578    }
1579}
1580
1581/*
1582 * Clear from cursor position to beginning of display, inclusive.
1583 */
1584static void
1585ClearAbove(XtermWidget xw)
1586{
1587    TScreen *screen = TScreenOf(xw);
1588
1589    if (screen->protected_mode != OFF_PROTECT) {
1590	int row;
1591	unsigned len = (unsigned) MaxCols(screen);
1592
1593	assert(screen->max_col >= 0);
1594	for (row = 0; row < screen->cur_row; row++)
1595	    ClearInLine(xw, row, 0, len);
1596	ClearInLine(xw, screen->cur_row, 0, (unsigned) screen->cur_col);
1597    } else {
1598	int top;
1599
1600	if (screen->cursor_state)
1601	    HideCursor(xw);
1602	if ((top = INX2ROW(screen, 0)) <= screen->max_row) {
1603	    int height;
1604
1605	    if (screen->scroll_amt)
1606		FlushScroll(xw);
1607	    if ((height = screen->cur_row + top) > screen->max_row)
1608		height = screen->max_row + 1;
1609	    if ((height -= top) > 0) {
1610		chararea_clear_displayed_graphics(screen,
1611						  0,
1612						  top,
1613						  MaxCols(screen),
1614						  height);
1615
1616		ClearCurBackground(xw,
1617				   top,
1618				   0,
1619				   (unsigned) height,
1620				   (unsigned) MaxCols(screen),
1621				   (unsigned) FontWidth(screen));
1622	    }
1623	}
1624	ClearBufRows(xw, 0, screen->cur_row - 1);
1625    }
1626
1627    ClearLeft(xw);
1628}
1629
1630/*
1631 * Clear from cursor position to end of display, inclusive.
1632 */
1633static void
1634ClearBelow(XtermWidget xw)
1635{
1636    TScreen *screen = TScreenOf(xw);
1637
1638    ClearRight(xw, -1);
1639
1640    if (screen->protected_mode != OFF_PROTECT) {
1641	int row;
1642	unsigned len = (unsigned) MaxCols(screen);
1643
1644	assert(screen->max_col >= 0);
1645	for (row = screen->cur_row + 1; row <= screen->max_row; row++)
1646	    ClearInLine(xw, row, 0, len);
1647    } else {
1648	int top;
1649
1650	if ((top = INX2ROW(screen, screen->cur_row)) <= screen->max_row) {
1651	    if (screen->scroll_amt)
1652		FlushScroll(xw);
1653	    if (++top <= screen->max_row) {
1654		chararea_clear_displayed_graphics(screen,
1655						  0,
1656						  top,
1657						  MaxCols(screen),
1658						  (screen->max_row - top + 1));
1659		ClearCurBackground(xw,
1660				   top,
1661				   0,
1662				   (unsigned) (screen->max_row - top + 1),
1663				   (unsigned) MaxCols(screen),
1664				   (unsigned) FontWidth(screen));
1665	    }
1666	}
1667	ClearBufRows(xw, screen->cur_row + 1, screen->max_row);
1668    }
1669}
1670
1671/*
1672 * Clear the given row, for the given range of columns, returning 1 if no
1673 * protected characters were found, 0 otherwise.
1674 */
1675static int
1676ClearInLine2(XtermWidget xw, int flags, int row, int col, unsigned len)
1677{
1678    TScreen *screen = TScreenOf(xw);
1679    CLineData *ld;
1680    int rc = 1;
1681
1682    TRACE(("ClearInLine(row=%d, col=%d, len=%d) vs %d..%d\n",
1683	   row, col, len,
1684	   screen->startH.row,
1685	   screen->startH.col));
1686
1687    if (ScrnHaveSelection(screen)
1688	&& ScrnIsRowInSelection(screen, row)) {
1689	ScrnDisownSelection(xw);
1690    }
1691
1692    if (col + (int) len >= MaxCols(screen)) {
1693	len = (unsigned) (MaxCols(screen) - col);
1694    }
1695
1696    /* If we've marked protected text on the screen, we'll have to
1697     * check each time we do an erase.
1698     */
1699    if (screen->protected_mode != OFF_PROTECT) {
1700	unsigned n;
1701	IAttr *attrs = getLineData(screen, row)->attribs + col;
1702	int saved_mode = screen->protected_mode;
1703	Bool done;
1704
1705	/* disable this branch during recursion */
1706	screen->protected_mode = OFF_PROTECT;
1707
1708	do {
1709	    done = True;
1710	    for (n = 0; n < len; n++) {
1711		if (attrs[n] & PROTECTED) {
1712		    rc = 0;	/* found a protected segment */
1713		    if (n != 0) {
1714			ClearInLine(xw, row, col, n);
1715		    }
1716		    while ((n < len)
1717			   && (attrs[n] & PROTECTED)) {
1718			n++;
1719		    }
1720		    done = False;
1721		    break;
1722		}
1723	    }
1724	    /* setup for another segment, past the protected text */
1725	    if (!done) {
1726		attrs += n;
1727		col += (int) n;
1728		len -= n;
1729	    }
1730	} while (!done);
1731
1732	screen->protected_mode = saved_mode;
1733	if ((int) len <= 0) {
1734	    return 0;
1735	}
1736    }
1737    /* fall through to the final non-protected segment */
1738
1739    if (screen->cursor_state)
1740	HideCursor(xw);
1741    ResetWrap(screen);
1742
1743    if (AddToVisible(xw)
1744	&& (ld = getLineData(screen, row)) != 0) {
1745
1746	ClearCurBackground(xw,
1747			   INX2ROW(screen, row),
1748			   col,
1749			   1U,
1750			   len,
1751			   (unsigned) LineFontWidth(screen, ld));
1752    }
1753
1754    if (len != 0) {
1755	ClearCells(xw, flags, len, row, col);
1756    }
1757
1758    return rc;
1759}
1760
1761int
1762ClearInLine(XtermWidget xw, int row, int col, unsigned len)
1763{
1764    TScreen *screen = TScreenOf(xw);
1765    int flags = 0;
1766
1767    /*
1768     * If we're clearing to the end of the line, we won't count this as
1769     * "drawn" characters.  We'll only do cut/paste on "drawn" characters,
1770     * so this has the effect of suppressing trailing blanks from a
1771     * selection.
1772     */
1773    if (col + (int) len < MaxCols(screen)) {
1774	flags |= CHARDRAWN;
1775    }
1776    return ClearInLine2(xw, flags, row, col, len);
1777}
1778
1779/*
1780 * Clear the next n characters on the cursor's line, including the cursor's
1781 * position.
1782 */
1783void
1784ClearRight(XtermWidget xw, int n)
1785{
1786    TScreen *screen = TScreenOf(xw);
1787    LineData *ld;
1788    unsigned len = (unsigned) (MaxCols(screen) - screen->cur_col);
1789
1790    assert(screen->max_col >= 0);
1791    assert(screen->max_col >= screen->cur_col);
1792
1793    if (n < 0)			/* the remainder of the line */
1794	n = MaxCols(screen);
1795    if (n == 0)			/* default for 'ECH' */
1796	n = 1;
1797
1798    if (len > (unsigned) n)
1799	len = (unsigned) n;
1800
1801    ld = getLineData(screen, screen->cur_row);
1802    if (AddToVisible(xw)) {
1803	if_OPT_WIDE_CHARS(screen, {
1804	    int col = screen->cur_col;
1805	    int row = screen->cur_row;
1806	    int kl;
1807	    int kr;
1808	    if (DamagedCurCells(screen, len, &kl, &kr) && kr >= kl) {
1809		int xx = col;
1810		if (kl < xx) {
1811		    ClearInLine2(xw, 0, row, kl, (unsigned) (xx - kl));
1812		}
1813		xx = col + (int) len - 1;
1814		if (kr > xx) {
1815		    ClearInLine2(xw, 0, row, xx + 1, (unsigned) (kr - xx));
1816		}
1817	    }
1818	});
1819	(void) ClearInLine(xw, screen->cur_row, screen->cur_col, len);
1820    } else {
1821	ScrnClearCells(xw, screen->cur_row, screen->cur_col, len);
1822    }
1823
1824    /* with the right part cleared, we can't be wrapping */
1825    LineClrWrapped(ld);
1826    ShowWrapMarks(xw, screen->cur_row, ld);
1827    ResetWrap(screen);
1828}
1829
1830/*
1831 * Clear first part of cursor's line, inclusive.
1832 */
1833static void
1834ClearLeft(XtermWidget xw)
1835{
1836    TScreen *screen = TScreenOf(xw);
1837    unsigned len = (unsigned) screen->cur_col + 1;
1838
1839    assert(screen->cur_col >= 0);
1840    if (AddToVisible(xw)) {
1841	if_OPT_WIDE_CHARS(screen, {
1842	    int row = screen->cur_row;
1843	    int kl;
1844	    int kr;
1845	    if (DamagedCurCells(screen, 1, &kl, &kr) && kr >= kl) {
1846		ClearInLine2(xw, 0, row, kl, (unsigned) (kr - kl + 1));
1847	    }
1848	});
1849	(void) ClearInLine(xw, screen->cur_row, 0, len);
1850    } else {
1851	ScrnClearCells(xw, screen->cur_row, 0, len);
1852    }
1853}
1854
1855/*
1856 * Erase the cursor's line.
1857 */
1858static void
1859ClearLine(XtermWidget xw)
1860{
1861    TScreen *screen = TScreenOf(xw);
1862    unsigned len = (unsigned) MaxCols(screen);
1863
1864    assert(screen->max_col >= 0);
1865    (void) ClearInLine(xw, screen->cur_row, 0, len);
1866}
1867
1868void
1869ClearScreen(XtermWidget xw)
1870{
1871    TScreen *screen = TScreenOf(xw);
1872    int top;
1873
1874    TRACE(("ClearScreen\n"));
1875
1876    if (screen->cursor_state)
1877	HideCursor(xw);
1878
1879    ScrnDisownSelection(xw);
1880    ResetWrap(screen);
1881    if ((top = INX2ROW(screen, 0)) <= screen->max_row) {
1882	if (screen->scroll_amt)
1883	    FlushScroll(xw);
1884	chararea_clear_displayed_graphics(screen,
1885					  0,
1886					  top,
1887					  MaxCols(screen),
1888					  (screen->max_row - top + 1));
1889	ClearCurBackground(xw,
1890			   top,
1891			   0,
1892			   (unsigned) (screen->max_row - top + 1),
1893			   (unsigned) MaxCols(screen),
1894			   (unsigned) FontWidth(screen));
1895    }
1896    ClearBufRows(xw, 0, screen->max_row);
1897}
1898
1899/*
1900 * If we've written protected text DEC-style, and are issuing a non-DEC
1901 * erase, temporarily reset the protected_mode flag so that the erase will
1902 * ignore the protected flags.
1903 */
1904void
1905do_erase_char(XtermWidget xw, int param, int mode)
1906{
1907    TScreen *screen = TScreenOf(xw);
1908    int saved_mode = screen->protected_mode;
1909
1910    if (saved_mode == DEC_PROTECT
1911	&& saved_mode != mode) {
1912	screen->protected_mode = OFF_PROTECT;
1913    }
1914
1915    ClearRight(xw, param);
1916    screen->protected_mode = saved_mode;
1917}
1918
1919void
1920do_erase_line(XtermWidget xw, int param, int mode)
1921{
1922    TScreen *screen = TScreenOf(xw);
1923    int saved_mode = screen->protected_mode;
1924
1925    if (saved_mode == DEC_PROTECT
1926	&& saved_mode != mode) {
1927	screen->protected_mode = OFF_PROTECT;
1928    }
1929
1930    switch (param) {
1931    case -1:			/* DEFAULT */
1932    case 0:
1933	ClearRight(xw, -1);
1934	break;
1935    case 1:
1936	ClearLeft(xw);
1937	break;
1938    case 2:
1939	ClearLine(xw);
1940	break;
1941    }
1942    screen->protected_mode = saved_mode;
1943}
1944
1945/*
1946 * Just like 'do_erase_line()', except that this intercepts ED controls.  If we
1947 * clear the whole screen, we'll get the return-value from ClearInLine, and
1948 * find if there were any protected characters left.  If not, reset the
1949 * protected mode flag in the screen data (it's slower).
1950 */
1951void
1952do_erase_display(XtermWidget xw, int param, int mode)
1953{
1954    TScreen *screen = TScreenOf(xw);
1955    int saved_mode = screen->protected_mode;
1956
1957    if (saved_mode == DEC_PROTECT
1958	&& saved_mode != mode)
1959	screen->protected_mode = OFF_PROTECT;
1960
1961    switch (param) {
1962    case -1:			/* DEFAULT */
1963    case 0:
1964	if (screen->cur_row == 0
1965	    && screen->cur_col == 0) {
1966	    screen->protected_mode = saved_mode;
1967	    do_erase_display(xw, 2, mode);
1968	    saved_mode = screen->protected_mode;
1969	} else
1970	    ClearBelow(xw);
1971	break;
1972
1973    case 1:
1974	if (screen->cur_row == screen->max_row
1975	    && screen->cur_col == screen->max_col) {
1976	    screen->protected_mode = saved_mode;
1977	    do_erase_display(xw, 2, mode);
1978	    saved_mode = screen->protected_mode;
1979	} else
1980	    ClearAbove(xw);
1981	break;
1982
1983    case 2:
1984	/*
1985	 * We use 'ClearScreen()' throughout the remainder of the
1986	 * program for places where we don't care if the characters are
1987	 * protected or not.  So we modify the logic around this call
1988	 * on 'ClearScreen()' to handle protected characters.
1989	 */
1990	if (screen->protected_mode != OFF_PROTECT) {
1991	    int row;
1992	    int rc = 1;
1993	    unsigned len = (unsigned) MaxCols(screen);
1994
1995	    assert(screen->max_col >= 0);
1996	    for (row = 0; row <= screen->max_row; row++)
1997		rc &= ClearInLine(xw, row, 0, len);
1998	    if (rc != 0)
1999		saved_mode = OFF_PROTECT;
2000	} else {
2001	    ClearScreen(xw);
2002	}
2003	break;
2004
2005    case 3:
2006	/* xterm addition - erase saved lines. */
2007	if (screen->eraseSavedLines) {
2008	    screen->savedlines = 0;
2009	    ScrollBarDrawThumb(xw, 1);
2010	}
2011	break;
2012    }
2013    screen->protected_mode = saved_mode;
2014}
2015
2016static Boolean
2017screen_has_data(XtermWidget xw)
2018{
2019    TScreen *screen = TScreenOf(xw);
2020    Boolean result = False;
2021    int row;
2022
2023    for (row = 0; row < screen->max_row; ++row) {
2024	CLineData *ld;
2025
2026	if ((ld = getLineData(screen, row)) != 0) {
2027	    int col;
2028
2029	    for (col = 0; col < screen->max_col; ++col) {
2030		if (ld->attribs[col] & CHARDRAWN) {
2031		    result = True;
2032		    break;
2033		}
2034	    }
2035	}
2036	if (result)
2037	    break;
2038    }
2039    return result;
2040}
2041
2042/*
2043 * Like tiXtraScroll, perform a scroll up of the page contents.  In this case,
2044 * it happens for the special case when erasing the whole display starting from
2045 * the upper-left corner of the screen.
2046 */
2047void
2048do_cd_xtra_scroll(XtermWidget xw)
2049{
2050    TScreen *screen = TScreenOf(xw);
2051
2052    if (xw->misc.cdXtraScroll
2053	&& screen->cur_col == 0
2054	&& screen->cur_row == 0
2055	&& screen_has_data(xw)) {
2056	xtermScroll(xw, screen->max_row);
2057    }
2058}
2059
2060/*
2061 * Scroll the page up (saving it).  This is called when doing terminal
2062 * initialization (ti) or exiting from that (te).
2063 */
2064void
2065do_ti_xtra_scroll(XtermWidget xw)
2066{
2067    TScreen *screen = TScreenOf(xw);
2068
2069    if (xw->misc.tiXtraScroll) {
2070	xtermScroll(xw, screen->max_row);
2071    }
2072}
2073
2074static void
2075CopyWait(XtermWidget xw)
2076{
2077    TScreen *screen = TScreenOf(xw);
2078    XEvent reply;
2079    XEvent *rep = &reply;
2080#ifndef NO_ACTIVE_ICON
2081    int retries = 0;
2082#endif
2083
2084#if USE_DOUBLE_BUFFER
2085    if (resource.buffered)
2086	return;
2087#endif
2088
2089    for (;;) {
2090#ifndef NO_ACTIVE_ICON
2091	if (xw->work.active_icon != eiFalse) {
2092	    /*
2093	     * The XWindowEvent call blocks until an event is available.  That
2094	     * can hang when using active-icon and iconifying/deiconifying
2095	     * while the terminal is receiving lots of output.  Checking with
2096	     * this call on the other hand may lose exposure events which
2097	     * arrive too late.  As a compromise, try several times with a
2098	     * time-delay before assuming no more events are available.
2099	     */
2100	    if (XCheckWindowEvent(screen->display,
2101				  VWindow(screen),
2102				  ExposureMask,
2103				  &reply)) {
2104		retries = 0;
2105	    } else {
2106		if (++retries >= 1000)
2107		    return;
2108		usleep(100U);	/* wait 0.1msec */
2109		continue;
2110	    }
2111	} else
2112#endif
2113	    XWindowEvent(screen->display, VWindow(screen), ExposureMask, &reply);
2114	switch (reply.type) {
2115	case Expose:
2116	    HandleExposure(xw, &reply);
2117	    break;
2118	case NoExpose:
2119	case GraphicsExpose:
2120	    if (screen->incopy <= 0) {
2121		screen->incopy = 1;
2122		if (screen->scrolls > 0)
2123		    screen->scrolls--;
2124	    }
2125	    if (reply.type == GraphicsExpose)
2126		HandleExposure(xw, &reply);
2127
2128	    if ((reply.type == NoExpose) ||
2129		((XExposeEvent *) rep)->count == 0) {
2130		if (screen->incopy <= 0 && screen->scrolls > 0)
2131		    screen->scrolls--;
2132		if (screen->scrolls == 0) {
2133		    screen->incopy = 0;
2134		    return;
2135		}
2136		screen->incopy = -1;
2137	    }
2138	    break;
2139	}
2140    }
2141}
2142
2143/*
2144 * used by vertical_copy_area and and horizontal_copy_area
2145 */
2146static void
2147copy_area(XtermWidget xw,
2148	  int src_x,
2149	  int src_y,
2150	  unsigned width,
2151	  unsigned height,
2152	  int dest_x,
2153	  int dest_y)
2154{
2155    TScreen *screen = TScreenOf(xw);
2156
2157    if (width != 0 && height != 0) {
2158	/* wait for previous CopyArea to complete unless
2159	   multiscroll is enabled and active */
2160	if (screen->incopy && screen->scrolls == 0)
2161	    CopyWait(xw);
2162	screen->incopy = -1;
2163
2164	/* save for translating Expose events */
2165	screen->copy_src_x = src_x;
2166	screen->copy_src_y = src_y;
2167	screen->copy_width = width;
2168	screen->copy_height = height;
2169	screen->copy_dest_x = dest_x;
2170	screen->copy_dest_y = dest_y;
2171
2172	XCopyArea(screen->display,
2173		  VDrawable(screen), VDrawable(screen),
2174		  NormalGC(xw, screen),
2175		  src_x, src_y, width, height, dest_x, dest_y);
2176    }
2177}
2178
2179/*
2180 * use when inserting or deleting characters on the current line
2181 */
2182static void
2183horizontal_copy_area(XtermWidget xw,
2184		     int firstchar,	/* char pos on screen to start copying at */
2185		     int nchars,
2186		     int amount)	/* number of characters to move right */
2187{
2188    TScreen *screen = TScreenOf(xw);
2189    CLineData *ld;
2190
2191    if ((ld = getLineData(screen, screen->cur_row)) != 0) {
2192	int src_x = LineCursorX(screen, ld, firstchar);
2193	int src_y = CursorY(screen, screen->cur_row);
2194
2195	copy_area(xw, src_x, src_y,
2196		  (unsigned) (nchars * LineFontWidth(screen, ld)),
2197		  (unsigned) FontHeight(screen),
2198		  src_x + amount * LineFontWidth(screen, ld), src_y);
2199    }
2200}
2201
2202/*
2203 * use when inserting or deleting lines from the screen
2204 */
2205static void
2206vertical_copy_area(XtermWidget xw,
2207		   int firstline,	/* line on screen to start copying at */
2208		   int nlines,
2209		   int amount,	/* number of lines to move up (neg=down) */
2210		   int left,
2211		   int right)
2212{
2213    TScreen *screen = TScreenOf(xw);
2214
2215    TRACE(("vertical_copy_area - firstline=%d nlines=%d left=%d right=%d amount=%d\n",
2216	   firstline, nlines, left, right, amount));
2217
2218    if (nlines > 0) {
2219	int src_x = CursorX(screen, left);
2220	int src_y = firstline * FontHeight(screen) + screen->border;
2221	unsigned int w = (unsigned) ((right + 1 - left) * FontWidth(screen));
2222	unsigned int h = (unsigned) (nlines * FontHeight(screen));
2223	int dst_x = src_x;
2224	int dst_y = src_y - amount * FontHeight(screen);
2225
2226	copy_area(xw, src_x, src_y, w, h, dst_x, dst_y);
2227
2228	if (screen->show_wrap_marks) {
2229	    int row;
2230	    int first = firstline - amount;
2231	    int last = firstline + nlines + amount;
2232
2233	    for (row = first; row < last; ++row) {
2234		CLineData *ld;
2235		int mapped = amount + row + screen->topline;
2236
2237		if ((ld = getLineData(screen, mapped)) != 0) {
2238		    ShowWrapMarks(xw, row, ld);
2239		}
2240	    }
2241	}
2242    }
2243}
2244
2245/*
2246 * use when scrolling the entire screen
2247 */
2248void
2249scrolling_copy_area(XtermWidget xw,
2250		    int firstline,	/* line on screen to start copying at */
2251		    int nlines,
2252		    int amount)	/* number of lines to move up (neg=down) */
2253{
2254
2255    if (nlines > 0) {
2256	vertical_copy_area(xw, firstline, nlines, amount, 0, TScreenOf(xw)->max_col);
2257    }
2258}
2259
2260/*
2261 * Handler for Expose events on the VT widget.
2262 * Returns 1 iff the area where the cursor was got refreshed.
2263 */
2264int
2265HandleExposure(XtermWidget xw, XEvent *event)
2266{
2267    TScreen *screen = TScreenOf(xw);
2268    XExposeEvent *reply = (XExposeEvent *) event;
2269
2270#ifndef NO_ACTIVE_ICON
2271    if (reply->window == screen->iconVwin.window) {
2272	WhichVWin(screen) = &screen->iconVwin;
2273	TRACE(("HandleExposure - icon\n"));
2274    } else {
2275	WhichVWin(screen) = &screen->fullVwin;
2276	TRACE(("HandleExposure - normal\n"));
2277    }
2278    TRACE((" event %d,%d %dx%d\n",
2279	   reply->y,
2280	   reply->x,
2281	   reply->height,
2282	   reply->width));
2283#endif /* NO_ACTIVE_ICON */
2284
2285    /* if not doing CopyArea or if this is a GraphicsExpose, don't translate */
2286    if (!screen->incopy || event->type != Expose) {
2287	return handle_translated_exposure(xw, reply->x, reply->y,
2288					  reply->width,
2289					  reply->height);
2290    } else {
2291	/* compute intersection of area being copied with
2292	   area being exposed. */
2293	int both_x1 = Max(screen->copy_src_x, reply->x);
2294	int both_y1 = Max(screen->copy_src_y, reply->y);
2295	int both_x2 = Min(screen->copy_src_x + (int) screen->copy_width,
2296			  (reply->x + (int) reply->width));
2297	int both_y2 = Min(screen->copy_src_y + (int) screen->copy_height,
2298			  (reply->y + (int) reply->height));
2299	int value = 0;
2300
2301	/* was anything copied affected? */
2302	if (both_x2 > both_x1 && both_y2 > both_y1) {
2303	    /* do the copied area */
2304	    value = handle_translated_exposure
2305		(xw, reply->x + screen->copy_dest_x - screen->copy_src_x,
2306		 reply->y + screen->copy_dest_y - screen->copy_src_y,
2307		 reply->width, reply->height);
2308	}
2309	/* was anything not copied affected? */
2310	if (reply->x < both_x1 || reply->y < both_y1
2311	    || reply->x + reply->width > both_x2
2312	    || reply->y + reply->height > both_y2)
2313	    value = handle_translated_exposure(xw, reply->x, reply->y,
2314					       reply->width, reply->height);
2315
2316	return value;
2317    }
2318}
2319
2320static void
2321set_background(XtermWidget xw, int color)
2322{
2323    TScreen *screen = TScreenOf(xw);
2324    Pixel c = getXtermBG(xw, xw->flags, color);
2325
2326#if OPT_WIDE_ATTRS
2327    TRACE(("set_background(%d) %#lx %s\n", color, c,
2328	   ((xw->flags & ATR_DIRECT_BG)
2329	    ? "direct"
2330	    : "indexed")));
2331#else
2332    TRACE(("set_background(%d) %#lx\n", color, c));
2333#endif
2334    XSetWindowBackground(screen->display, VShellWindow(xw), c);
2335    XSetWindowBackground(screen->display, VWindow(screen), c);
2336    initBorderGC(xw, WhichVWin(screen));
2337}
2338
2339void
2340xtermClear2(XtermWidget xw, int x, int y, unsigned width, unsigned height)
2341{
2342    TScreen *screen = TScreenOf(xw);
2343    VTwin *vwin = WhichVWin(screen);
2344    Drawable draw = VDrawable(screen);
2345    GC gc;
2346
2347    if ((gc = vwin->border_gc) != 0) {
2348	int vmark1 = screen->border;
2349	int vmark2 = vwin->height + vmark1;
2350	int hmark1 = OriginX(screen);
2351	int hmark2 = vwin->width + hmark1;
2352	if (y < vmark1) {
2353	    int yy = y + (int) height;
2354	    int h1 = (yy <= vmark1) ? (yy - y) : (vmark1 - y);
2355	    XFillRectangle(screen->display, draw, gc,
2356			   x, y, width, (unsigned) h1);
2357	    if (yy > vmark1) {
2358		xtermClear2(xw, x, vmark1, width, (unsigned) (yy - vmark1));
2359	    }
2360	} else if (y < vmark2) {
2361	    int yy = y + (int) height;
2362	    int h2 = (yy <= vmark2) ? (yy - y) : (vmark2 - y);
2363	    int xb = x;
2364	    int xx = x + (int) width;
2365	    int ww = (int) width;
2366	    if (x < hmark1) {
2367		int w1 = (xx <= hmark1) ? (xx - x) : (hmark1 - x);
2368		XFillRectangle(screen->display, draw, gc,
2369			       x, y, (unsigned) w1, (unsigned) h2);
2370		x += w1;
2371		ww -= w1;
2372	    }
2373	    if ((ww > 0) && (x < hmark2)) {
2374		int w2 = (xx <= hmark2) ? (xx - x) : (hmark2 - x);
2375#if USE_DOUBLE_BUFFER
2376		if (resource.buffered) {
2377		    XFillRectangle(screen->display, draw,
2378				   FillerGC(xw, screen),
2379				   x, y, (unsigned) w2, (unsigned) h2);
2380		} else
2381#endif
2382		    XClearArea(screen->display, VWindow(screen),
2383			       x, y, (unsigned) w2, (unsigned) h2, False);
2384		x += w2;
2385		ww -= w2;
2386	    }
2387	    if (ww > 0) {
2388		XFillRectangle(screen->display, draw, gc,
2389			       x, y, (unsigned) ww, (unsigned) h2);
2390	    }
2391	    if (yy > vmark2) {
2392		xtermClear2(xw, xb, vmark2, width, (unsigned) (yy - vmark2));
2393	    }
2394	} else {
2395	    XFillRectangle(screen->display, draw, gc, x, y, width, height);
2396	}
2397    } else {
2398#if USE_DOUBLE_BUFFER
2399	if (resource.buffered) {
2400	    gc = FillerGC(xw, screen);
2401	    XFillRectangle(screen->display, draw, gc,
2402			   x, y, width, height);
2403	} else
2404#endif
2405	    XClearArea(screen->display,
2406		       VWindow(screen),
2407		       x, y, width, height, False);
2408    }
2409}
2410
2411/*
2412 * Called by the ExposeHandler to do the actual repaint after the coordinates
2413 * have been translated to allow for any CopyArea in progress.
2414 * The rectangle passed in is pixel coordinates.
2415 */
2416static int
2417handle_translated_exposure(XtermWidget xw,
2418			   int rect_x,
2419			   int rect_y,
2420			   int rect_width,
2421			   int rect_height)
2422{
2423    TScreen *screen = TScreenOf(xw);
2424    int toprow, leftcol, nrows, ncols;
2425    int x0, x1;
2426    int y0, y1;
2427    int result = 0;
2428
2429    TRACE(("handle_translated_exposure at %d,%d size %dx%d\n",
2430	   rect_y, rect_x, rect_height, rect_width));
2431
2432    x0 = (rect_x - OriginX(screen));
2433    x1 = (x0 + rect_width);
2434
2435    y0 = (rect_y - OriginY(screen));
2436    y1 = (y0 + rect_height);
2437
2438    if ((x0 < 0 ||
2439	 y0 < 0 ||
2440	 x1 > Width(screen) ||
2441	 y1 > Height(screen))) {
2442	set_background(xw, -1);
2443	xtermClear2(xw,
2444		    rect_x,
2445		    rect_y,
2446		    (unsigned) rect_width,
2447		    (unsigned) rect_height);
2448    }
2449    toprow = y0 / FontHeight(screen);
2450    if (toprow < 0)
2451	toprow = 0;
2452
2453    leftcol = x0 / FontWidth(screen);
2454    if (leftcol < 0)
2455	leftcol = 0;
2456
2457    nrows = (y1 - 1) / FontHeight(screen) - toprow + 1;
2458    ncols = (x1 - 1) / FontWidth(screen) - leftcol + 1;
2459    toprow -= screen->scrolls;
2460    if (toprow < 0) {
2461	nrows += toprow;
2462	toprow = 0;
2463    }
2464    if (toprow + nrows > MaxRows(screen))
2465	nrows = MaxRows(screen) - toprow;
2466    if (leftcol + ncols > MaxCols(screen))
2467	ncols = MaxCols(screen) - leftcol;
2468
2469    if (nrows > 0 && ncols > 0) {
2470	ScrnRefresh(xw, toprow, leftcol, nrows, ncols, True);
2471	first_map_occurred();
2472	if (screen->cur_row >= toprow &&
2473	    screen->cur_row < toprow + nrows &&
2474	    screen->cur_col >= leftcol &&
2475	    screen->cur_col < leftcol + ncols) {
2476	    result = 1;
2477	}
2478
2479    }
2480    TRACE(("...handle_translated_exposure %d\n", result));
2481    return (result);
2482}
2483
2484/***====================================================================***/
2485
2486void
2487GetColors(XtermWidget xw, ScrnColors * pColors)
2488{
2489    TScreen *screen = TScreenOf(xw);
2490    int n;
2491
2492    pColors->which = 0;
2493    for (n = 0; n < NCOLORS; ++n) {
2494	SET_COLOR_VALUE(pColors, n, T_COLOR(screen, n));
2495    }
2496}
2497
2498void
2499ChangeColors(XtermWidget xw, ScrnColors * pNew)
2500{
2501    Bool repaint = False;
2502    TScreen *screen = TScreenOf(xw);
2503    VTwin *win = WhichVWin(screen);
2504
2505    TRACE(("ChangeColors\n"));
2506
2507    if (COLOR_DEFINED(pNew, TEXT_CURSOR)) {
2508	T_COLOR(screen, TEXT_CURSOR) = COLOR_VALUE(pNew, TEXT_CURSOR);
2509	TRACE(("... TEXT_CURSOR: %#lx\n", T_COLOR(screen, TEXT_CURSOR)));
2510	FreeMarkGCs(xw);
2511	/* no repaint needed */
2512    } else if ((T_COLOR(screen, TEXT_CURSOR) == T_COLOR(screen, TEXT_FG)) &&
2513	       (COLOR_DEFINED(pNew, TEXT_FG))) {
2514	if (T_COLOR(screen, TEXT_CURSOR) != COLOR_VALUE(pNew, TEXT_FG)) {
2515	    T_COLOR(screen, TEXT_CURSOR) = COLOR_VALUE(pNew, TEXT_FG);
2516	    TRACE(("... TEXT_CURSOR: %#lx\n", T_COLOR(screen, TEXT_CURSOR)));
2517	    if (screen->Vshow)
2518		repaint = True;
2519	}
2520	FreeMarkGCs(xw);
2521    }
2522
2523    if (COLOR_DEFINED(pNew, TEXT_FG)) {
2524	Pixel fg = COLOR_VALUE(pNew, TEXT_FG);
2525	T_COLOR(screen, TEXT_FG) = fg;
2526	TRACE(("... TEXT_FG: %#lx\n", T_COLOR(screen, TEXT_FG)));
2527	if (screen->Vshow) {
2528	    setCgsFore(xw, win, gcNorm, fg);
2529	    setCgsBack(xw, win, gcNormReverse, fg);
2530	    setCgsFore(xw, win, gcBold, fg);
2531	    setCgsBack(xw, win, gcBoldReverse, fg);
2532	    repaint = True;
2533	}
2534	FreeMarkGCs(xw);
2535    }
2536
2537    if (COLOR_DEFINED(pNew, TEXT_BG)) {
2538	Pixel bg = COLOR_VALUE(pNew, TEXT_BG);
2539	T_COLOR(screen, TEXT_BG) = bg;
2540	TRACE(("... TEXT_BG: %#lx\n", T_COLOR(screen, TEXT_BG)));
2541	if (screen->Vshow) {
2542	    setCgsBack(xw, win, gcNorm, bg);
2543	    setCgsFore(xw, win, gcNormReverse, bg);
2544	    setCgsBack(xw, win, gcBold, bg);
2545	    setCgsFore(xw, win, gcBoldReverse, bg);
2546	    set_background(xw, -1);
2547	    repaint = True;
2548	}
2549    }
2550#if OPT_HIGHLIGHT_COLOR
2551    if (COLOR_DEFINED(pNew, HIGHLIGHT_BG)) {
2552	if (T_COLOR(screen, HIGHLIGHT_BG) != COLOR_VALUE(pNew, HIGHLIGHT_BG)) {
2553	    T_COLOR(screen, HIGHLIGHT_BG) = COLOR_VALUE(pNew, HIGHLIGHT_BG);
2554	    TRACE(("... HIGHLIGHT_BG: %#lx\n", T_COLOR(screen, HIGHLIGHT_BG)));
2555	    if (screen->Vshow)
2556		repaint = True;
2557	}
2558    }
2559    if (COLOR_DEFINED(pNew, HIGHLIGHT_FG)) {
2560	if (T_COLOR(screen, HIGHLIGHT_FG) != COLOR_VALUE(pNew, HIGHLIGHT_FG)) {
2561	    T_COLOR(screen, HIGHLIGHT_FG) = COLOR_VALUE(pNew, HIGHLIGHT_FG);
2562	    TRACE(("... HIGHLIGHT_FG: %#lx\n", T_COLOR(screen, HIGHLIGHT_FG)));
2563	    if (screen->Vshow)
2564		repaint = True;
2565	}
2566    }
2567#endif
2568
2569    if (COLOR_DEFINED(pNew, MOUSE_FG) || (COLOR_DEFINED(pNew, MOUSE_BG))) {
2570	if (COLOR_DEFINED(pNew, MOUSE_FG)) {
2571	    T_COLOR(screen, MOUSE_FG) = COLOR_VALUE(pNew, MOUSE_FG);
2572	    TRACE(("... MOUSE_FG: %#lx\n", T_COLOR(screen, MOUSE_FG)));
2573	}
2574	if (COLOR_DEFINED(pNew, MOUSE_BG)) {
2575	    T_COLOR(screen, MOUSE_BG) = COLOR_VALUE(pNew, MOUSE_BG);
2576	    TRACE(("... MOUSE_BG: %#lx\n", T_COLOR(screen, MOUSE_BG)));
2577	}
2578
2579	if (screen->Vshow) {
2580	    recolor_cursor(screen,
2581			   screen->pointer_cursor,
2582			   T_COLOR(screen, MOUSE_FG),
2583			   T_COLOR(screen, MOUSE_BG));
2584	    XDefineCursor(screen->display, VWindow(screen),
2585			  screen->pointer_cursor);
2586	}
2587#if OPT_TEK4014
2588	if (TEK4014_SHOWN(xw)) {
2589	    TekScreen *tekscr = TekScreenOf(tekWidget);
2590	    Window tekwin = TWindow(tekscr);
2591	    if (tekwin) {
2592		recolor_cursor(screen,
2593			       tekscr->arrow,
2594			       T_COLOR(screen, MOUSE_FG),
2595			       T_COLOR(screen, MOUSE_BG));
2596		XDefineCursor(screen->display, tekwin, tekscr->arrow);
2597	    }
2598	}
2599#endif
2600	/* no repaint needed */
2601    }
2602
2603    if (COLOR_DEFINED(pNew, TEXT_FG) ||
2604	COLOR_DEFINED(pNew, TEXT_BG) ||
2605	COLOR_DEFINED(pNew, TEXT_CURSOR)) {
2606	if (set_cursor_gcs(xw) && screen->Vshow) {
2607	    repaint = True;
2608	}
2609    }
2610#if OPT_TEK4014
2611    if (COLOR_DEFINED(pNew, TEK_FG) ||
2612	COLOR_DEFINED(pNew, TEK_BG)) {
2613	ChangeTekColors(tekWidget, screen, pNew);
2614	if (TEK4014_SHOWN(xw)) {
2615	    TekRepaint(tekWidget);
2616	}
2617    } else if (COLOR_DEFINED(pNew, TEK_CURSOR)) {
2618	ChangeTekColors(tekWidget, screen, pNew);
2619    }
2620#endif
2621    if (repaint)
2622	xtermRepaint(xw);
2623}
2624
2625void
2626xtermClear(XtermWidget xw)
2627{
2628    TScreen *screen = TScreenOf(xw);
2629
2630    TRACE(("xtermClear\n"));
2631    xtermClear2(xw, 0, 0, FullWidth(screen), FullHeight(screen));
2632}
2633
2634void
2635xtermRepaint(XtermWidget xw)
2636{
2637    TScreen *screen = TScreenOf(xw);
2638
2639    TRACE(("xtermRepaint\n"));
2640    xtermClear(xw);
2641    ScrnRefresh(xw, 0, 0, MaxRows(screen), MaxCols(screen), True);
2642}
2643
2644/***====================================================================***/
2645
2646Boolean
2647isDefaultForeground(const char *name)
2648{
2649    return (Boolean) !x_strcasecmp(name, XtDefaultForeground);
2650}
2651
2652Boolean
2653isDefaultBackground(const char *name)
2654{
2655    return (Boolean) !x_strcasecmp(name, XtDefaultBackground);
2656}
2657
2658#if OPT_WIDE_CHARS
2659/*
2660 * Check for Unicode BIDI control characters, which may be miscategorized via
2661 * wcwidth() and iswprint() as zero-width printable characters.
2662 */
2663Boolean
2664isWideControl(unsigned ch)
2665{
2666    Boolean result;
2667
2668    switch (ch) {
2669    case 0x200E:
2670    case 0x200F:
2671    case 0x202A:
2672    case 0x202B:
2673    case 0x202C:
2674    case 0x202D:
2675    case 0x202E:
2676	result = True;
2677	break;
2678    default:
2679	result = False;
2680	break;
2681    }
2682    return result;
2683}
2684#endif
2685
2686/***====================================================================***/
2687
2688typedef struct {
2689    Pixel fg;
2690    Pixel bg;
2691} ToSwap;
2692
2693#if OPT_HIGHLIGHT_COLOR
2694#define hc_param ,Bool hilite_color
2695#define hc_value ,screen->hilite_color
2696#else
2697#define hc_param		/* nothing */
2698#define hc_value		/* nothing */
2699#endif
2700
2701/*
2702 * Use this to swap the foreground/background color values in the resource
2703 * data, and to build up a list of the pairs which must be swapped in the
2704 * GC cache.
2705 */
2706static void
2707swapLocally(ToSwap * list, int *count, ColorRes * fg, ColorRes * bg hc_param)
2708{
2709    ColorRes tmp;
2710    Boolean found = False;
2711
2712#if OPT_COLOR_RES
2713    Pixel fg_color = fg->value;
2714    Pixel bg_color = bg->value;
2715#else
2716    Pixel fg_color = *fg;
2717    Pixel bg_color = *bg;
2718#endif
2719
2720#if OPT_HIGHLIGHT_COLOR
2721    if ((fg_color != bg_color) || !hilite_color)
2722#endif
2723    {
2724	int n;
2725
2726	EXCHANGE(*fg, *bg, tmp);
2727	for (n = 0; n < *count; ++n) {
2728	    if ((list[n].fg == fg_color && list[n].bg == bg_color)
2729		|| (list[n].fg == bg_color && list[n].bg == fg_color)) {
2730		found = True;
2731		break;
2732	    }
2733	}
2734	if (!found) {
2735	    list[*count].fg = fg_color;
2736	    list[*count].bg = bg_color;
2737	    *count = *count + 1;
2738	    TRACE(("swapLocally fg %#lx, bg %#lx ->%d\n",
2739		   fg_color, bg_color, *count));
2740	}
2741    }
2742}
2743
2744static void
2745reallySwapColors(XtermWidget xw, ToSwap * list, int count)
2746{
2747    int j, k;
2748
2749    TRACE(("reallySwapColors\n"));
2750    for (j = 0; j < count; ++j) {
2751	for_each_text_gc(k) {
2752	    redoCgs(xw, list[j].fg, list[j].bg, (CgsEnum) k);
2753	}
2754    }
2755    FreeMarkGCs(xw);
2756}
2757
2758static void
2759swapVTwinGCs(XtermWidget xw, VTwin *win)
2760{
2761    swapCgs(xw, win, gcNorm, gcNormReverse);
2762    swapCgs(xw, win, gcBold, gcBoldReverse);
2763}
2764
2765void
2766ReverseVideo(XtermWidget xw)
2767{
2768    TScreen *screen = TScreenOf(xw);
2769    ToSwap listToSwap[5];
2770    int numToSwap = 0;
2771
2772    TRACE(("ReverseVideo now %s\n", BtoS(xw->misc.re_verse)));
2773
2774    /*
2775     * Swap SGR foreground and background colors.  By convention, these are
2776     * the colors assigned to "black" (SGR #0) and "white" (SGR #7).  Also,
2777     * SGR #8 and SGR #15 are the bold (or bright) versions of SGR #0 and
2778     * #7, respectively.
2779     *
2780     * We don't swap colors that happen to match the screen's foreground
2781     * and background because that tends to produce bizarre effects.
2782     */
2783#define swapAnyColor(name,a,b) swapLocally(listToSwap, &numToSwap, &(screen->name[a]), &(screen->name[b]) hc_value)
2784#define swapAColor(a,b) swapAnyColor(Acolors, a, b)
2785    if_OPT_ISO_COLORS(screen, {
2786	swapAColor(0, 7);
2787	swapAColor(8, 15);
2788    });
2789
2790    if (T_COLOR(screen, TEXT_CURSOR) == T_COLOR(screen, TEXT_FG)) {
2791	T_COLOR(screen, TEXT_CURSOR) = T_COLOR(screen, TEXT_BG);
2792    }
2793#define swapTColor(a,b) swapAnyColor(Tcolors, a, b)
2794    swapTColor(TEXT_FG, TEXT_BG);
2795    swapTColor(MOUSE_FG, MOUSE_BG);
2796
2797    reallySwapColors(xw, listToSwap, numToSwap);
2798
2799    swapVTwinGCs(xw, &(screen->fullVwin));
2800#ifndef NO_ACTIVE_ICON
2801    swapVTwinGCs(xw, &(screen->iconVwin));
2802#endif /* NO_ACTIVE_ICON */
2803
2804    xw->misc.re_verse = (Boolean) !xw->misc.re_verse;
2805    TRACE(("...swapping done, set ReverseVideo %s\n", BtoS(xw->misc.re_verse)));
2806
2807    if (XtIsRealized((Widget) xw)) {
2808	xtermDisplayPointer(xw);
2809    }
2810#if OPT_TEK4014
2811    if (TEK4014_SHOWN(xw)) {
2812	TekScreen *tekscr = TekScreenOf(tekWidget);
2813	Window tekwin = TWindow(tekscr);
2814	recolor_cursor(screen,
2815		       tekscr->arrow,
2816		       T_COLOR(screen, MOUSE_FG),
2817		       T_COLOR(screen, MOUSE_BG));
2818	XDefineCursor(screen->display, tekwin, tekscr->arrow);
2819    }
2820#endif
2821
2822    if (screen->scrollWidget)
2823	ScrollBarReverseVideo(screen->scrollWidget);
2824
2825    if (XtIsRealized((Widget) xw)) {
2826	set_background(xw, -1);
2827    }
2828#if OPT_TEK4014
2829    TekReverseVideo(xw, tekWidget);
2830#endif
2831    if (XtIsRealized((Widget) xw)) {
2832	xtermRepaint(xw);
2833    }
2834#if OPT_TEK4014
2835    if (TEK4014_SHOWN(xw)) {
2836	TekRepaint(tekWidget);
2837    }
2838#endif
2839    ReverseOldColors(xw);
2840    set_cursor_gcs(xw);
2841    update_reversevideo();
2842    TRACE(("...ReverseVideo now %s\n", BtoS(xw->misc.re_verse)));
2843}
2844
2845void
2846recolor_cursor(TScreen *screen,
2847	       Cursor cursor,	/* X cursor ID to set */
2848	       unsigned long fg,	/* pixel indexes to look up */
2849	       unsigned long bg)	/* pixel indexes to look up */
2850{
2851    Display *dpy = screen->display;
2852    XColor colordefs[2];	/* 0 is foreground, 1 is background */
2853
2854    colordefs[0].pixel = fg;
2855    colordefs[1].pixel = bg;
2856    XQueryColors(dpy, DefaultColormap(dpy, DefaultScreen(dpy)),
2857		 colordefs, 2);
2858    XRecolorCursor(dpy, cursor, colordefs, colordefs + 1);
2859    cleanup_colored_cursor();
2860    return;
2861}
2862
2863#if OPT_RENDERFONT
2864#define XFT_CACHE_LIMIT ((unsigned)(~0) >> 1)
2865#define XFT_CACHE_SIZE  16
2866typedef struct {
2867    XftColor color;
2868    unsigned use;
2869} XftColorCache;
2870
2871static int
2872compare_xft_color_cache(const void *a, const void *b)
2873{
2874    return (int) (((const XftColorCache *) a)->use -
2875		  ((const XftColorCache *) b)->use);
2876}
2877
2878static XftColor *
2879getXftColor(XtermWidget xw, Pixel pixel)
2880{
2881    static XftColorCache cache[XFT_CACHE_SIZE + 1];
2882    static unsigned latest_use;
2883    int i;
2884    int oldest;
2885    unsigned oldest_use;
2886    XColor color;
2887    Boolean found = False;
2888
2889    oldest_use = XFT_CACHE_LIMIT;
2890    oldest = 0;
2891    if (latest_use == XFT_CACHE_LIMIT) {
2892	latest_use = 0;
2893	qsort(cache, (size_t) XFT_CACHE_SIZE, sizeof(XftColorCache), compare_xft_color_cache);
2894	for (i = 0; i < XFT_CACHE_SIZE; i++) {
2895	    if (cache[i].use) {
2896		cache[i].use = ++latest_use;
2897	    }
2898	}
2899    }
2900    for (i = 0; i < XFT_CACHE_SIZE; i++) {
2901	if (cache[i].use) {
2902	    if (cache[i].color.pixel == pixel) {
2903		found = True;
2904		break;
2905	    }
2906	}
2907	if (cache[i].use < oldest_use) {
2908	    oldest_use = cache[i].use;
2909	    oldest = i;
2910	}
2911    }
2912    if (!found) {
2913	i = oldest;
2914	color.pixel = pixel;
2915	XQueryColor(TScreenOf(xw)->display, xw->core.colormap, &color);
2916	cache[i].color.color.red = color.red;
2917	cache[i].color.color.green = color.green;
2918	cache[i].color.color.blue = color.blue;
2919	cache[i].color.color.alpha = 0xffff;
2920	cache[i].color.pixel = pixel;
2921    }
2922    cache[i].use = ++latest_use;
2923    return &cache[i].color;
2924}
2925
2926/*
2927 * The cell-width is related to, but not the same as the wide-character width.
2928 * We will only get useful values from wcwidth() for codes above 255.
2929 * Otherwise, interpret according to internal data.
2930 */
2931#if OPT_RENDERWIDE
2932
2933#if OPT_C1_PRINT
2934#define XtermCellWidth(xw, ch) \
2935	(((ch) == 0 || (ch) == 127) \
2936	  ? 0 \
2937	  : (((ch) < 256) \
2938	      ? (((ch) >= 128 && (ch) < 160) \
2939		  ? (TScreenOf(xw)->c1_printable ? 1 : 0) \
2940		  : 1) \
2941	      : CharWidth(ch)))
2942#else
2943#define XtermCellWidth(xw, ch) \
2944	(((ch) == 0 || (ch) == 127) \
2945	  ? 0 \
2946	  : (((ch) < 256) \
2947	      ? 1 \
2948	      : CharWidth(ch)))
2949#endif
2950
2951#endif /* OPT_RENDERWIDE */
2952
2953#define XFT_FONT(which) getXftFont(params->xw, which, fontnum)
2954
2955#if OPT_ISO_COLORS
2956#define UseBoldFont(screen) (!(screen)->colorBDMode || ((screen)->veryBoldColors & BOLD))
2957#else
2958#define UseBoldFont(screen) 1
2959#endif
2960
2961#if OPT_RENDERWIDE
2962/*
2963 * Find Xft (truetype) double-width font for the given normal/bold attributes.
2964 */
2965static XftFont *
2966getWideXftFont(XTermDraw * params,
2967	       unsigned attr_flags)
2968{
2969    TScreen *screen = TScreenOf(params->xw);
2970    int fontnum = screen->menu_font_number;
2971    XftFont *wfont = 0;
2972
2973#if OPT_WIDE_ATTRS
2974    if ((attr_flags & ATR_ITALIC)
2975#if OPT_ISO_COLORS
2976	&& !screen->colorITMode
2977#endif
2978	) {
2979	if ((attr_flags & BOLDATTR(screen))
2980	    && UseBoldFont(screen)
2981	    && XFT_FONT(fWBtal)) {
2982	    wfont = XFT_FONT(fWBtal);
2983	} else if (XFT_FONT(fWItal)) {
2984	    wfont = XFT_FONT(fWItal);
2985	}
2986    }
2987    if (wfont != 0) {
2988	;			/* skip the other tests */
2989    } else
2990#endif
2991#if OPT_ISO_COLORS
2992	if ((attr_flags & UNDERLINE)
2993	    && !screen->colorULMode
2994	    && screen->italicULMode
2995	    && XFT_FONT(fWItal)) {
2996	wfont = XFT_FONT(fWItal);
2997    } else
2998#endif
2999	if ((attr_flags & BOLDATTR(screen))
3000	    && UseBoldFont(screen)
3001	    && XFT_FONT(fWBold)) {
3002	wfont = XFT_FONT(fWBold);
3003    } else {
3004	wfont = XFT_FONT(fWide);
3005    }
3006    return wfont;
3007}
3008#endif /* OPT_RENDERWIDE */
3009
3010/*
3011 * Find Xft (truetype) single-width font for the given normal/bold attributes.
3012 */
3013static XftFont *
3014getNormXftFont(XTermDraw * params,
3015	       unsigned attr_flags,
3016	       Bool *did_ul)
3017{
3018    TScreen *screen = TScreenOf(params->xw);
3019    int fontnum = screen->menu_font_number;
3020    XftFont *font = 0;
3021
3022    (void) did_ul;
3023#if OPT_DEC_CHRSET
3024    if (CSET_DOUBLE(params->real_chrset)) {
3025	font = xterm_DoubleFT(params, params->real_chrset, attr_flags);
3026    }
3027    if (font != 0) {
3028	;			/* found a usable double-sized font */
3029    } else
3030#endif
3031#if OPT_WIDE_ATTRS
3032	if ((attr_flags & ATR_ITALIC)
3033#if OPT_ISO_COLORS
3034	    && !screen->colorITMode
3035#endif
3036	) {
3037	if ((attr_flags & BOLDATTR(screen))
3038	    && UseBoldFont(screen)
3039	    && XFT_FONT(fBtal)) {
3040	    font = XFT_FONT(fBtal);
3041	} else if (XFT_FONT(fItal)) {
3042	    font = XFT_FONT(fItal);
3043	}
3044    }
3045    if (font != 0) {
3046	;			/* skip the other tests */
3047    } else
3048#endif
3049#if OPT_ISO_COLORS
3050	if ((attr_flags & UNDERLINE)
3051	    && !screen->colorULMode
3052	    && screen->italicULMode
3053	    && XFT_FONT(fItal)) {
3054	font = XFT_FONT(fItal);
3055	*did_ul = True;
3056    } else
3057#endif
3058	if ((attr_flags & BOLDATTR(screen))
3059	    && UseBoldFont(screen)
3060	    && XFT_FONT(fBold)) {
3061	font = XFT_FONT(fBold);
3062    } else {
3063	font = XFT_FONT(fNorm);
3064    }
3065    return font;
3066}
3067
3068#if OPT_RENDERWIDE
3069#define pickXftFont(width, nf, wf) ((width == 2 && wf != 0) ? wf : nf)
3070#else
3071#define pickXftFont(width, nf, wf) (nf)
3072#endif
3073
3074/*
3075 * fontconfig/Xft combination prior to 2.2 has a problem with
3076 * CJK truetype 'double-width' (bi-width/monospace) fonts leading
3077 * to the 's p a c e d o u t' rendering. Consequently, we can't
3078 * rely on XftDrawString8/16 when one of those fonts is used.
3079 * Instead, we need to roll out our own using XftDrawCharSpec.
3080 * A patch in the same spirit (but in a rather different form)
3081 * was applied to gnome vte and gtk2 port of vim.
3082 * See http://bugzilla.mozilla.org/show_bug.cgi?id=196312
3083 */
3084static int
3085xtermXftDrawString(XTermDraw * params,
3086		   unsigned attr_flags,
3087		   XftColor *color,
3088		   XftFont *font,
3089		   int x,
3090		   int y,
3091		   const IChar *text,
3092		   Cardinal len,
3093		   Bool really)
3094{
3095    TScreen *screen = TScreenOf(params->xw);
3096    int ncells = 0;
3097
3098    (void) attr_flags;
3099    if (len != 0) {
3100#if OPT_RENDERWIDE
3101	XftCharSpec *sbuf;
3102	XftFont *wfont = getWideXftFont(params, attr_flags);
3103	Cardinal src, dst;
3104	XftFont *lastFont = 0;
3105	XftFont *currFont = 0;
3106	Cardinal start = 0;
3107	int charWidth;
3108	int fwidth = FontWidth(screen);
3109#if OPT_DEC_CHRSET
3110	Boolean forceDbl = CSET_DOUBLE(params->real_chrset);
3111#else
3112	Boolean forceDbl = False;
3113#endif
3114
3115	BumpTypedBuffer(XftCharSpec, 2 * len);
3116	sbuf = BfBuf(XftCharSpec);
3117
3118	for (src = dst = 0; src < len; src++) {
3119	    FcChar32 wc = *text++;
3120
3121	    charWidth = XtermCellWidth(params->xw, (wchar_t) wc);
3122	    if (charWidth < 0)
3123		continue;
3124
3125	    sbuf[dst].ucs4 = wc;
3126	    sbuf[dst].x = (short) (x + fwidth * ncells);
3127	    sbuf[dst].y = (short) (y);
3128
3129	    currFont = pickXftFont(charWidth, font, wfont);
3130	    ncells += charWidth;
3131
3132	    if (lastFont != currFont) {
3133		if ((lastFont != 0) && really) {
3134		    XftDrawCharSpec(screen->renderDraw,
3135				    color,
3136				    lastFont,
3137				    sbuf + start,
3138				    (int) (dst - start));
3139		}
3140		start = dst;
3141		lastFont = currFont;
3142	    }
3143	    ++dst;
3144
3145	    if (forceDbl && charWidth < 2) {
3146		sbuf[dst].ucs4 = ' ';
3147		sbuf[dst].x = (short) (x + fwidth * ncells);
3148		sbuf[dst].y = (short) (y);
3149		++dst;
3150		ncells += charWidth;
3151	    }
3152	}
3153	if ((dst != start) && really) {
3154	    XftDrawCharSpec(screen->renderDraw,
3155			    color,
3156			    lastFont,
3157			    sbuf + start,
3158			    (int) (dst - start));
3159	}
3160#else /* !OPT_RENDERWIDE */
3161	if (really) {
3162	    XftChar8 *buffer;
3163	    int dst;
3164
3165	    BumpTypedBuffer(XftChar8, len);
3166	    buffer = BfBuf(XftChar8);
3167
3168	    for (dst = 0; dst < (int) len; ++dst)
3169		buffer[dst] = CharOf(text[dst]);
3170
3171	    XftDrawString8(screen->renderDraw,
3172			   color,
3173			   font,
3174			   x, y, buffer, (int) len);
3175	}
3176	ncells = (int) len;
3177#endif
3178	xtermNeedSwap(params->xw, 1);
3179    }
3180    return ncells;
3181}
3182#define xtermXftWidth(params, attr_flags, color, font, x, y, chars, len) \
3183   xtermXftDrawString(params, attr_flags, color, font, x, y, chars, len, False)
3184#endif /* OPT_RENDERFONT */
3185
3186#if OPT_WIDE_CHARS
3187/*
3188 * Map characters commonly "fixed" by groff back to their ASCII equivalents.
3189 * Also map other useful equivalents.
3190 */
3191unsigned
3192AsciiEquivs(unsigned ch)
3193{
3194    switch (ch) {
3195    case 0x2010:		/* groff "-" */
3196    case 0x2011:
3197    case 0x2012:
3198    case 0x2013:
3199    case 0x2014:
3200    case 0x2015:
3201    case 0x2212:		/* groff "\-" */
3202	ch = '-';
3203	break;
3204    case 0x2018:		/* groff "`" */
3205	ch = '`';
3206	break;
3207    case 0x2019:		/* groff ' */
3208	ch = '\'';
3209	break;
3210    case 0x201C:		/* groff lq */
3211    case 0x201D:		/* groff rq */
3212	ch = '"';
3213	break;
3214    case 0x2329:		/* groff ".URL" */
3215	ch = '<';
3216	break;
3217    case 0x232a:		/* groff ".URL" */
3218	ch = '>';
3219	break;
3220    default:
3221	if (ch >= 0xff01 && ch <= 0xff5e) {
3222	    /* "Fullwidth" codes (actually double-width) */
3223	    ch -= 0xff00;
3224	    ch += ANSI_SPA;
3225	    break;
3226	}
3227    }
3228    return ch;
3229}
3230
3231/*
3232 * Actually this should be called "groff_workaround()" - for the places where
3233 * groff stomps on compatibility.  Still, if enough people get used to it,
3234 * this might someday become a quasi-standard.
3235 */
3236#if OPT_BOX_CHARS
3237static int
3238ucs_workaround(XTermDraw * params,
3239	       unsigned ch,
3240	       GC gc,
3241	       int x,
3242	       int y)
3243{
3244    TScreen *screen = TScreenOf(params->xw);
3245    int fixed = False;
3246
3247    if (screen->wide_chars && screen->utf8_mode && ch > 256) {
3248	IChar eqv = (IChar) AsciiEquivs(ch);
3249
3250	if (eqv != (IChar) ch) {
3251	    int width = CharWidth(ch);
3252
3253	    do {
3254		drawXtermText(params,
3255			      gc,
3256			      x,
3257			      y,
3258			      &eqv,
3259			      1);
3260		x += FontWidth(screen);
3261		eqv = BAD_ASCII;
3262	    } while (width-- > 1);
3263
3264	    fixed = True;
3265	} else if (ch == HIDDEN_CHAR) {
3266	    fixed = True;
3267	}
3268    }
3269    return fixed;
3270}
3271#endif /* OPT_BOX_CHARS */
3272#endif /* OPT_WIDE_CHARS */
3273
3274/*
3275 * Use this when the characters will not fill the cell area properly.  Fill the
3276 * area where we'll write the characters, otherwise we'll get gaps between
3277 * them, e.g., in the original background color.
3278 *
3279 * The cursor is a special case, because the XFillRectangle call only uses the
3280 * foreground, while we've set the cursor color in the background.  So we need
3281 * a special GC for that.
3282 */
3283static void
3284xtermFillCells(XTermDraw * params,
3285	       GC gc,
3286	       int x,
3287	       int y,
3288	       Cardinal len)
3289{
3290    TScreen *screen = TScreenOf(params->xw);
3291    VTwin *currentWin = WhichVWin(screen);
3292
3293    if (!(params->draw_flags & NOBACKGROUND)) {
3294	CgsEnum srcId = getCgsId(params->xw, currentWin, gc);
3295	CgsEnum dstId = gcMAX;
3296	Pixel fg = getCgsFore(params->xw, currentWin, gc);
3297	Pixel bg = getCgsBack(params->xw, currentWin, gc);
3298
3299	switch (srcId) {
3300	case gcVTcursNormal:
3301	case gcVTcursReverse:
3302	    dstId = gcVTcursOutline;
3303	    break;
3304	case gcVTcursFilled:
3305	case gcVTcursOutline:
3306	    /* FIXME */
3307	    break;
3308	case gcNorm:
3309	    dstId = gcNormReverse;
3310	    break;
3311	case gcNormReverse:
3312	    dstId = gcNorm;
3313	    break;
3314	case gcBold:
3315	    dstId = gcBoldReverse;
3316	    break;
3317	case gcBoldReverse:
3318	    dstId = gcBold;
3319	    break;
3320	case gcBorder:
3321	case gcFiller:
3322	    dstId = srcId;
3323	    break;
3324#if OPT_BOX_CHARS
3325	case gcLine:
3326	case gcDots:
3327	    /* FIXME */
3328	    break;
3329#endif
3330#if OPT_DEC_CHRSET
3331	case gcCNorm:
3332	case gcCBold:
3333	    /* FIXME */
3334	    break;
3335#endif
3336#if OPT_WIDE_CHARS
3337	case gcWide:
3338	    dstId = gcWideReverse;
3339	    break;
3340	case gcWBold:
3341	    dstId = gcBoldReverse;
3342	    break;
3343	case gcWideReverse:
3344	case gcWBoldReverse:
3345	    /* FIXME */
3346	    break;
3347#endif
3348#if OPT_TEK4014
3349	case gcTKcurs:
3350	    /* FIXME */
3351	    break;
3352#endif
3353	case gcMAX:
3354	    break;
3355	}
3356
3357	if (dstId != gcMAX) {
3358	    setCgsFore(params->xw, currentWin, dstId, bg);
3359	    setCgsBack(params->xw, currentWin, dstId, fg);
3360
3361	    XFillRectangle(screen->display, VDrawable(screen),
3362			   getCgsGC(params->xw, currentWin, dstId),
3363			   x, y,
3364			   len * (Cardinal) FontWidth(screen),
3365			   (unsigned) FontHeight(screen));
3366	}
3367    }
3368}
3369
3370#if OPT_TRACE
3371static void
3372xtermSetClipRectangles(Display *dpy,
3373		       GC gc,
3374		       int x,
3375		       int y,
3376		       XRectangle * rp,
3377		       Cardinal nr,
3378		       int order)
3379{
3380#if 0
3381    TScreen *screen = TScreenOf(term);
3382    Drawable draw = VDrawable(screen);
3383
3384    XSetClipMask(dpy, gc, None);
3385    XDrawRectangle(screen->display, draw, gc,
3386		   x + rp->x - 1,
3387		   y + rp->y - 1,
3388		   rp->width,
3389		   rp->height);
3390#endif
3391
3392    XSetClipRectangles(dpy, gc,
3393		       x, y, rp, (int) nr, order);
3394    TRACE(("clipping @(%3d,%3d) (%3d,%3d)..(%3d,%3d)\n",
3395	   y, x,
3396	   rp->y, rp->x, rp->height, rp->width));
3397}
3398
3399#else
3400#define xtermSetClipRectangles(dpy, gc, x, y, rp, nr, order) \
3401	    XSetClipRectangles(dpy, gc, x, y, rp, (int) nr, order)
3402#endif
3403
3404#if OPT_CLIP_BOLD
3405/*
3406 * This special case is a couple of percent slower, but avoids a lot of pixel
3407 * trash in rxcurses' hanoi.cmd demo (e.g., 10x20 font).
3408 */
3409#define beginClipping(screen,gc,pwidth,plength) \
3410	if (pwidth > 2) { \
3411	    if (screen->use_clipping) { \
3412		XRectangle clip; \
3413		int clip_x = x; \
3414		int clip_y = y - FontHeight(screen) + FontDescent(screen); \
3415		clip.x = 0; \
3416		clip.y = 0; \
3417		clip.height = (unsigned short) FontHeight(screen); \
3418		clip.width = (unsigned short) ((pwidth) * (plength)); \
3419		xtermSetClipRectangles(screen->display, gc, \
3420				       clip_x, clip_y, \
3421				       &clip, 1, Unsorted); \
3422	    } else if (screen->use_border_clipping) { \
3423		XRectangle clip; \
3424		clip.x = 0; \
3425		clip.y = 0; \
3426		clip.height = (unsigned short) Height(screen); \
3427		clip.width = (unsigned short) Width(screen); \
3428		xtermSetClipRectangles(screen->display, gc, \
3429				       0, 0, \
3430				       &clip, 1, Unsorted); \
3431	    } \
3432	}
3433#define endClipping(screen,gc) \
3434	    XSetClipMask(screen->display, gc, None)
3435#else
3436#define beginClipping(screen,gc,pwidth,plength)		/* nothing */
3437#define endClipping(screen,gc)	/* nothing */
3438#endif /* OPT_CLIP_BOLD */
3439
3440#if OPT_RENDERFONT
3441static int
3442drawClippedXftString(XTermDraw * params,
3443		     unsigned attr_flags,
3444		     XftFont *font,
3445		     XftColor *fg_color,
3446		     int x,
3447		     int y,
3448		     const IChar *text,
3449		     Cardinal len)
3450{
3451    int ncells = xtermXftWidth(params, attr_flags,
3452			       fg_color,
3453			       font, x, y,
3454			       text,
3455			       len);
3456    TScreen *screen = TScreenOf(params->xw);
3457    int fontHigh = FontHeight(screen);
3458    int fontWide = FontWidth(screen);
3459
3460    if (fontWide > 2) {
3461	int plength = (ncells ? ncells : 1);
3462	Boolean halfHigh = False;
3463#if OPT_DEC_CHRSET
3464	Boolean halfWide = False;
3465
3466	switch (params->real_chrset) {
3467	case CSET_SWL:
3468	    break;
3469	case CSET_DWL:
3470	    halfWide = True;
3471	    break;
3472	case CSET_DHL_TOP:
3473	    halfHigh = True;
3474	    halfWide = True;
3475	    break;
3476	case CSET_DHL_BOT:
3477	    halfHigh = True;
3478	    halfWide = True;
3479	    break;
3480	}
3481	if (CSET_DOUBLE(params->real_chrset)) {
3482	    fontHigh = font->height;
3483	    fontWide = font->max_advance_width;
3484	    /* check if this is really a double-height font */
3485	    if (halfHigh) {
3486		int wantHigh = (int) (FontHeight(screen) * 1.8);
3487		halfHigh = (fontHigh >= wantHigh);
3488		TRACE(("comparing fontHigh %d/%d vs %d:"
3489		       " double-height %s for %s\n",
3490		       fontHigh, FontHeight(screen),
3491		       wantHigh, BtoS(halfHigh),
3492		       visibleDblChrset(params->real_chrset)));
3493	    }
3494	    fontHigh = (halfHigh
3495			? (2 * FontHeight(screen))
3496			: FontHeight(screen));
3497	    /* check if this is really a double-width font */
3498	    if (halfWide) {
3499		int wantWide = (int) (FontWidth(screen) * 1.8);
3500		halfWide = (fontWide >= wantWide);
3501		TRACE(("comparing fontWide %d/%d vs %d:"
3502		       " double-width %s for %s\n",
3503		       fontWide, FontWidth(screen),
3504		       wantWide, BtoS(halfWide),
3505		       visibleDblChrset(params->real_chrset)));
3506	    }
3507	    fontWide = (halfWide
3508			? (2 * FontWidth(screen))
3509			: FontWidth(screen));
3510	}
3511#endif
3512	if (screen->use_clipping || halfHigh) {
3513	    XRectangle clip;
3514	    double adds = ((double) screen->scale_height - 1.0f) * fontHigh;
3515	    int height = dimRound(adds + fontHigh);
3516	    int descnt = dimRound(adds / 2.0) + FontDescent(screen);
3517	    int clip_x = (x);
3518	    int clip_y = (y) - height + descnt;
3519
3520	    clip.x = 0;
3521	    clip.y = 0;
3522	    clip.height = (Dimension) height;
3523	    clip.width = (Dimension) (fontWide * (Dimension) (plength));
3524
3525#if OPT_DEC_CHRSET
3526	    if (halfHigh) {
3527		int adjust;
3528
3529		clip.height = (unsigned short) FontHeight(screen);
3530		clip_y += descnt;
3531		if (params->real_chrset == CSET_DHL_BOT) {
3532		    y -= clip.height;
3533		}
3534		adjust = ((clip_y - OriginY(screen)) % FontHeight(screen));
3535		if (adjust) {
3536		    if (adjust > FontHeight(screen) / 2)
3537			adjust -= FontHeight(screen);
3538		    clip_y -= adjust;
3539		}
3540	    }
3541#endif
3542	    XftDrawSetClipRectangles(screen->renderDraw,
3543				     clip_x, clip_y,
3544				     &clip, 1);
3545	} else if (screen->use_border_clipping) {
3546	    XRectangle clip;
3547
3548	    clip.x = (Position) OriginX(screen);
3549	    clip.y = (Position) OriginY(screen);
3550	    clip.height = (Dimension) Height(screen);
3551	    clip.width = (Dimension) Width(screen);
3552
3553	    XftDrawSetClipRectangles(screen->renderDraw,
3554				     0, 0,
3555				     &clip, 1);
3556	}
3557    }
3558
3559    xtermXftDrawString(params, attr_flags,
3560		       fg_color,
3561		       font, x, y + ScaleShift(screen),
3562		       text,
3563		       len,
3564		       True);
3565    XftDrawSetClip(screen->renderDraw, 0);
3566    return ncells;
3567}
3568#endif
3569
3570#ifndef NO_ACTIVE_ICON
3571#define WhichVFontData(screen,name) \
3572		(IsIcon(screen) ? getIconicFont(screen) \
3573				: GetNormalFont(screen, name))
3574#else
3575#define WhichVFontData(screen,name) \
3576				GetNormalFont(screen, name)
3577#endif
3578
3579static void
3580drawUnderline(XtermWidget xw,
3581	      GC gc,
3582	      unsigned attr_flags,
3583	      unsigned underline_len,
3584	      int font_width,
3585	      int x,
3586	      int y,
3587	      Bool did_ul)
3588{
3589    TScreen *screen = TScreenOf(xw);
3590
3591    if (screen->underline && !did_ul) {
3592	int repeat = 0;
3593	int descent = FontDescent(screen);
3594	int length = x + (int) underline_len * font_width - 1;
3595
3596#if OPT_WIDE_ATTRS
3597	if ((attr_flags & ATR_STRIKEOUT)) {
3598	    int where = y - ((3 * FontAscent(screen)) / 8);
3599	    XDrawLine(screen->display, VDrawable(screen), gc,
3600		      x, where,
3601		      length,
3602		      where);
3603	}
3604	if ((attr_flags & ATR_DBL_UNDER)) {
3605	    repeat = 2;
3606	} else
3607#endif
3608	if ((attr_flags & UNDERLINE)) {
3609	    repeat = 1;
3610	}
3611	while (repeat-- > 0) {
3612	    if (descent-- > 1)
3613		y++;
3614	    XDrawLine(screen->display, VDrawable(screen), gc,
3615		      x, y,
3616		      length,
3617		      y);
3618	}
3619    }
3620}
3621
3622#if OPT_WIDE_ATTRS
3623/*
3624 * As a special case, we are currently allowing italic fonts to be inexact
3625 * matches for the normal font's size.  That introduces a problem:  either the
3626 * ascent or descent may be shorter, leaving a gap that has to be filled in.
3627 * Or they may be larger, requiring clipping.  Check for both cases.
3628 */
3629static int
3630fixupItalics(XTermDraw * params,
3631	     GC gc,
3632	     XTermFonts * curFont,
3633	     int y, int x,
3634	     int font_width,
3635	     Cardinal len)
3636{
3637    TScreen *screen = TScreenOf(params->xw);
3638    VTwin *cgsWin = WhichVWin(screen);
3639    XFontStruct *realFp = curFont->fs;
3640    XFontStruct *thisFp = getCgsFont(params->xw, cgsWin, gc)->fs;
3641    int need_clipping = 0;
3642    int need_filling = 0;
3643
3644    if (thisFp->ascent > realFp->ascent)
3645	need_clipping = 1;
3646    else if (thisFp->ascent < realFp->ascent)
3647	need_filling = 1;
3648
3649    if (thisFp->descent > realFp->descent)
3650	need_clipping = 1;
3651    else if (thisFp->descent < realFp->descent)
3652	need_filling = 1;
3653
3654    if (need_clipping) {
3655	beginClipping(screen, gc, font_width, (int) len);
3656    }
3657    if (need_filling) {
3658	xtermFillCells(params,
3659		       gc,
3660		       x,
3661		       y - realFp->ascent,
3662		       len);
3663    }
3664    return need_clipping;
3665}
3666#endif
3667
3668#if OPT_DEC_CHRSET
3669static int
3670fakeDoubleChars(XTermDraw * params,
3671		GC gc,
3672		int y,
3673		int x,
3674		const IChar *text,
3675		Cardinal len)
3676{
3677    unsigned need = 2 * len;
3678    IChar *temp = TypeMallocN(IChar, need);
3679
3680    if (temp != 0) {
3681	unsigned n = 0;
3682	XTermDraw recur = *params;
3683
3684	recur.this_chrset = CSET_SWL;
3685
3686	while (len--) {
3687	    temp[n++] = *text++;
3688	    temp[n++] = ' ';
3689	}
3690	x = drawXtermText(&recur,
3691			  gc,
3692			  x, y,
3693			  temp,
3694			  n);
3695	free(temp);
3696    }
3697    return x;
3698}
3699#endif /* OPT_DEC_CHRSET */
3700
3701#define SetMissing(tag) \
3702	TRACE(("%s %s: missing %d %04X\n", __FILE__, tag, missing, ch)); \
3703	missing = 1
3704
3705#define MaxImageString 255
3706
3707/*
3708 * Draws text with the specified combination of bold/underline.  The return
3709 * value is the updated x position.
3710 */
3711int
3712drawXtermText(XTermDraw * params,
3713	      GC gc,
3714	      int start_x,
3715	      int start_y,
3716	      const IChar *text,
3717	      Cardinal len)
3718{
3719    XTermDraw recur = *params;
3720    TScreen *screen = TScreenOf(recur.xw);
3721    int x = start_x;
3722    int y = start_y;
3723    int y_shift = ScaleShift(screen);
3724    Cardinal real_length = len;
3725    Cardinal underline_len = 0;
3726    /* Intended width of the font to draw (as opposed to the actual width of
3727       the X font, and the width of the default font) */
3728    int font_width = (((recur.draw_flags & DOUBLEWFONT) ? 2 : 1)
3729		      * screen->fnt_wide);
3730    Bool did_ul = False;
3731    XTermFonts *curFont;
3732#if OPT_WIDE_ATTRS
3733    int need_clipping = 0;
3734#endif
3735
3736#if OPT_WIDE_CHARS
3737    if (text == 0)
3738	return 0;
3739#endif
3740    TRACE(("DRAWTEXT%c[%4d,%4d] (%d)%3d:%s\n",
3741	   screen->cursor_state == OFF ? ' ' : '*',
3742	   y, x, recur.this_chrset, len,
3743	   visibleIChars(text, len)));
3744
3745#if OPT_DEC_CHRSET
3746    if (CSET_DOUBLE(recur.this_chrset)) {
3747	/* We could try drawing double-size characters in the icon, but
3748	 * given that the icon font is usually nil or nil2, there
3749	 * doesn't seem to be much point.
3750	 */
3751	int inx = 0;
3752	GC gc2;
3753
3754#if OPT_RENDERFONT
3755	if (UsingRenderFont(recur.xw)) {
3756	    if (!IsIcon(screen) && screen->font_doublesize) {
3757		TRACE(("drawing %s\n", visibleDblChrset((unsigned) recur.this_chrset)));
3758		recur.real_chrset = recur.this_chrset;
3759		recur.this_chrset = CSET_SWL;
3760		x = drawXtermText(&recur,
3761				  gc,
3762				  x, y,
3763				  text,
3764				  len);
3765		x += ((int) len) * FontWidth(screen);
3766	    } else {
3767		x = fakeDoubleChars(&recur,
3768				    gc, y, x,
3769				    text, len);
3770	    }
3771	} else
3772#endif
3773	    if ((!IsIcon(screen) && screen->font_doublesize)
3774		&& (gc2 = xterm_DoubleGC(&recur, gc, &inx)) != 0) {
3775	    /* draw actual double-sized characters */
3776	    XFontStruct *fs = getDoubleFont(screen, inx)->fs;
3777	    XRectangle rect, *rp = &rect;
3778	    Cardinal nr = 1;
3779
3780	    font_width *= 2;
3781	    recur.draw_flags |= DOUBLEWFONT;
3782
3783	    rect.x = 0;
3784	    rect.y = 0;
3785	    rect.width = (unsigned short) ((int) len * font_width);
3786	    rect.height = (unsigned short) (FontHeight(screen));
3787
3788	    TRACE(("drawing %s\n", visibleDblChrset((unsigned) recur.this_chrset)));
3789	    switch (recur.this_chrset) {
3790	    case CSET_DHL_TOP:
3791		rect.y = (short) -(fs->ascent / 2);
3792		y -= rect.y;
3793		recur.draw_flags |= DOUBLEHFONT;
3794		break;
3795	    case CSET_DHL_BOT:
3796		rect.y = (short) (rect.height - (fs->ascent / 2));
3797		y -= rect.y;
3798		recur.draw_flags |= DOUBLEHFONT;
3799		break;
3800	    case CSET_DWL:
3801		break;
3802	    default:
3803		nr = 0;
3804		break;
3805	    }
3806
3807	    if (nr) {
3808		xtermSetClipRectangles(screen->display, gc2,
3809				       x, y, rp, nr, YXBanded);
3810		xtermFillCells(&recur, gc, x, y + rect.y, len * 2);
3811	    } else {
3812		XSetClipMask(screen->display, gc2, None);
3813	    }
3814
3815	    /* Call ourselves recursively with the new gc */
3816
3817	    /*
3818	     * If we're trying to use proportional font, or if the
3819	     * font server didn't give us what we asked for wrt
3820	     * width, position each character independently.
3821	     */
3822	    if (screen->fnt_prop
3823		|| (fs->min_bounds.width != fs->max_bounds.width)
3824		|| (fs->min_bounds.width != 2 * FontWidth(screen))) {
3825		/* It is hard to fall-through to the main
3826		   branch: in a lot of places the check
3827		   for the cached font info is for
3828		   normal/bold fonts only. */
3829		XTermDraw param2 = recur;
3830		param2.this_chrset = CSET_SWL;
3831		while (len--) {
3832		    x = drawXtermText(&param2,
3833				      gc2,
3834				      x, y,
3835				      text++,
3836				      1);
3837		    x += FontWidth(screen);
3838		}
3839	    } else {
3840		XTermDraw param2 = recur;
3841		param2.this_chrset = CSET_SWL;
3842		x = drawXtermText(&param2,
3843				  gc2,
3844				  x, y,
3845				  text,
3846				  len);
3847		x += (int) len *FontWidth(screen);
3848	    }
3849
3850	} else {		/* simulate double-sized characters */
3851	    x = fakeDoubleChars(&recur,
3852				gc, y, x,
3853				text, len);
3854	}
3855	TRACE(("DrewText [%4d,%4d]\n", y, x));
3856	return x;
3857    }
3858#endif
3859#if OPT_RENDERFONT
3860    if (UsingRenderFont(recur.xw)) {
3861	VTwin *currentWin = WhichVWin(screen);
3862	Display *dpy = screen->display;
3863	XftFont *font;
3864	XftFont *font0;
3865	XGCValues values;
3866#if OPT_RENDERWIDE
3867	XftFont *wfont;
3868	XftFont *wfont0;
3869#endif
3870
3871#if OPT_DEC_CHRSET
3872	/*
3873	 * If this is a VT100-style double-width font, ensure that everything
3874	 * is printed using two-columns per character.
3875	 */
3876	Boolean forceDbl = CSET_DOUBLE(recur.real_chrset);
3877#else
3878	Boolean forceDbl = False;
3879#endif
3880
3881	(void) forceDbl;
3882	/*
3883	 * Defer creating the drawable until we need it.
3884	 */
3885	if (!screen->renderDraw) {
3886	    int scr;
3887	    Drawable draw = VDrawable(screen);
3888	    Visual *visual;
3889
3890	    scr = DefaultScreen(dpy);
3891	    visual = DefaultVisual(dpy, scr);
3892	    screen->renderDraw = XftDrawCreate(dpy, draw, visual,
3893					       DefaultColormap(dpy, scr));
3894	}
3895
3896	/*
3897	 * font0/wfont0 provide fallback to non-bolded Xft font if a glyph is
3898	 * not found in the bold version.
3899	 */
3900#define IS_BOLD  (recur.attr_flags & BOLDATTR(screen))
3901#define NOT_BOLD (recur.attr_flags & ~BOLDATTR(screen))
3902	font = getNormXftFont(&recur, recur.attr_flags, &did_ul);
3903	font0 = IS_BOLD ? getNormXftFont(&recur, NOT_BOLD, &did_ul) : font;
3904	(void) font0;
3905#if OPT_RENDERWIDE
3906	wfont = getWideXftFont(&recur, recur.attr_flags);
3907	wfont0 = IS_BOLD ? getWideXftFont(&recur, NOT_BOLD) : wfont;
3908#endif
3909
3910#define GET_XFT_FG() getXftColor(recur.xw, values.foreground)
3911#define GET_XFT_BG() getXftColor(recur.xw, values.background)
3912
3913	values.foreground = getCgsFore(recur.xw, currentWin, gc);
3914	values.background = getCgsBack(recur.xw, currentWin, gc);
3915
3916	if (!(recur.draw_flags & NOBACKGROUND)) {
3917	    XftColor *bg_color = GET_XFT_BG();
3918	    int ncells = xtermXftWidth(&recur, recur.attr_flags,
3919				       bg_color,
3920				       font, x, y,
3921				       text,
3922				       len);
3923	    XftDrawRect(screen->renderDraw,
3924			bg_color,
3925			x, y,
3926			(unsigned) (ncells * FontWidth(screen)),
3927			(unsigned) FontHeight(screen));
3928	}
3929
3930	y += font->ascent;
3931#if OPT_BOX_CHARS
3932	{
3933	    /* adding code to substitute simulated line-drawing characters */
3934	    int last, first = 0;
3935	    int curX = x;
3936
3937	    for (last = 0; last < (int) len; last++) {
3938		Boolean replace = False;
3939		Boolean missing = False;
3940		unsigned ch = (unsigned) text[last];
3941		int filler = 0;
3942#if OPT_WIDE_CHARS
3943		int needed = forceDbl ? 2 : CharWidth(ch);
3944		XftFont *currFont = pickXftFont(needed, font, wfont);
3945		XftFont *tempFont = 0;
3946#define CURR_TEMP (tempFont ? tempFont : currFont)
3947
3948		if (xtermIsDecGraphic(ch) || ch == 0) {
3949		    /*
3950		     * Xft generally does not have the line-drawing characters
3951		     * in cells 0-31.  Assume this (we cannot inspect the
3952		     * picture easily...), and attempt to fill in from real
3953		     * line-drawing character in the font at the Unicode
3954		     * position.  Failing that, use our own box-characters.
3955		     */
3956		    if (screen->force_box_chars
3957			|| screen->broken_box_chars
3958			|| xtermXftMissing(recur.xw,
3959					   currFont,
3960					   dec2ucs(screen, ch))) {
3961			SetMissing("case 1");
3962		    } else {
3963			ch = dec2ucs(screen, ch);
3964			replace = True;
3965		    }
3966		} else if (ch >= 256) {
3967		    /*
3968		     * If we're reading UTF-8 from the client, we may have a
3969		     * line-drawing character.  Translate it back to our
3970		     * box-code if Xft tells us that the glyph is missing.
3971		     */
3972		    if_OPT_WIDE_CHARS(screen, {
3973			unsigned part = ucs2dec(screen, ch);
3974			if (xtermIsDecGraphic(part)) {
3975			    if (screen->force_box_chars
3976				|| screen->broken_box_chars) {
3977				SetMissing("case 2");
3978				ch = part;
3979			    }
3980			}
3981			if (!missing && xtermXftMissing(recur.xw, currFont, ch)) {
3982			    XftFont *test = findXftGlyph(recur.xw, currFont, ch);
3983			    if (test == 0)
3984				test = pickXftFont(needed, font0, wfont0);
3985			    if (!xtermXftMissing(recur.xw, test, ch)) {
3986				tempFont = test;
3987				replace = True;
3988				filler = 0;
3989			    } else if ((part = AsciiEquivs(ch)) != ch) {
3990				filler = needed - 1;
3991				ch = part;
3992				replace = True;
3993			    } else if (ch != HIDDEN_CHAR) {
3994				SetMissing("case 3");
3995			    }
3996			}
3997		    });
3998		}
3999#else
4000		XftFont *currFont = font;
4001#define CURR_TEMP (currFont)
4002		if (xtermIsDecGraphic(ch)) {
4003		    /*
4004		     * Xft generally does not have the line-drawing characters
4005		     * in cells 1-31.  Check for this, and attempt to fill in
4006		     * from real line-drawing character in the font at the
4007		     * Unicode position.  Failing that, use our own
4008		     * box-characters.
4009		     */
4010		    if (xtermXftMissing(recur.xw, currFont, ch)) {
4011			SetMissing("case 4");
4012		    }
4013		}
4014#endif
4015
4016		/*
4017		 * If we now have one of our box-codes, draw it directly.
4018		 */
4019		if (missing || replace) {
4020		    /* line drawing character time */
4021		    if (last > first) {
4022			int nc = drawClippedXftString(&recur,
4023						      recur.attr_flags,
4024						      currFont,
4025						      GET_XFT_FG(),
4026						      curX,
4027						      y,
4028						      text + first,
4029						      (Cardinal) (last - first));
4030			curX += nc * FontWidth(screen);
4031			underline_len += (Cardinal) nc;
4032		    }
4033		    if (missing) {
4034			Dimension old_wide = screen->fnt_wide;
4035			Dimension old_high = screen->fnt_high;
4036			screen->fnt_wide = (Dimension) FontWidth(screen);
4037			screen->fnt_high = (Dimension) FontHeight(screen);
4038
4039			xtermDrawBoxChar(&recur, ch,
4040					 gc,
4041					 curX,
4042					 y - FontAscent(screen),
4043					 1,
4044					 True);
4045			curX += FontWidth(screen);
4046			underline_len += 1;
4047			screen->fnt_wide = old_wide;
4048			screen->fnt_high = old_high;
4049		    } else {
4050			IChar ch2 = (IChar) ch;
4051			int nc = drawClippedXftString(&recur,
4052						      recur.attr_flags,
4053						      CURR_TEMP,
4054						      GET_XFT_FG(),
4055						      curX,
4056						      y,
4057						      &ch2,
4058						      1);
4059			curX += nc * FontWidth(screen);
4060			underline_len += (Cardinal) nc;
4061			if (filler) {
4062			    ch2 = ' ';
4063			    nc = drawClippedXftString(&recur,
4064						      recur.attr_flags,
4065						      CURR_TEMP,
4066						      GET_XFT_FG(),
4067						      curX,
4068						      y,
4069						      &ch2,
4070						      1);
4071			    curX += nc * FontWidth(screen);
4072			    underline_len += (Cardinal) nc;
4073			}
4074		    }
4075		    first = last + 1;
4076		}
4077	    }
4078	    if (last > first) {
4079		underline_len += (Cardinal)
4080		    drawClippedXftString(&recur,
4081					 recur.attr_flags,
4082					 font,
4083					 GET_XFT_FG(),
4084					 curX,
4085					 y,
4086					 text + first,
4087					 (Cardinal) (last - first));
4088	    }
4089	}
4090#else
4091	{
4092	    underline_len += (Cardinal)
4093		drawClippedXftString(&recur,
4094				     recur.attr_flags,
4095				     font,
4096				     GET_XFT_FG(),
4097				     x,
4098				     y,
4099				     text,
4100				     len);
4101	}
4102#endif /* OPT_BOX_CHARS */
4103
4104	drawUnderline(recur.xw,
4105		      gc,
4106		      recur.attr_flags,
4107		      underline_len,
4108		      FontWidth(screen),
4109		      x,
4110		      y + y_shift,
4111		      did_ul);
4112
4113	x += (int) len *FontWidth(screen);
4114
4115	return x;
4116    }
4117#endif /* OPT_RENDERFONT */
4118    curFont = ((recur.attr_flags & BOLDATTR(screen))
4119	       ? WhichVFontData(screen, fBold)
4120	       : WhichVFontData(screen, fNorm));
4121    /*
4122     * If we're asked to display a proportional font, do this with a fixed
4123     * pitch.  Yes, it's ugly.  But we cannot distinguish the use of xterm
4124     * as a dumb terminal vs its use as in fullscreen programs such as vi.
4125     * Hint: do not try to use a proportional font in the icon.
4126     */
4127    if (!IsIcon(screen) && !(recur.draw_flags & CHARBYCHAR) && screen->fnt_prop) {
4128	int adj, width;
4129
4130	while (len--) {
4131	    int cells = WideCells(*text);
4132#if OPT_BOX_CHARS
4133#if OPT_WIDE_CHARS
4134	    if (*text == HIDDEN_CHAR) {
4135		++text;
4136		continue;
4137	    } else
4138#endif
4139	    if (IsXtermMissingChar(screen, *text, curFont)) {
4140		adj = 0;
4141	    } else
4142#endif
4143	    {
4144		if_WIDE_OR_NARROW(screen, {
4145		    XChar2b temp[1];
4146		    temp[0].byte2 = LO_BYTE(*text);
4147		    temp[0].byte1 = HI_BYTE(*text);
4148		    width = XTextWidth16(curFont->fs, temp, 1);
4149		}
4150		, {
4151		    char temp[1];
4152		    temp[0] = (char) LO_BYTE(*text);
4153		    width = XTextWidth(curFont->fs, temp, 1);
4154		});
4155		adj = (FontWidth(screen) - width) / 2;
4156		if (adj < 0)
4157		    adj = 0;
4158	    }
4159	    xtermFillCells(&recur, gc, x, y, (Cardinal) cells);
4160	    recur.draw_flags |= (NOBACKGROUND | CHARBYCHAR);
4161	    x = drawXtermText(&recur,
4162			      gc, x + adj, y,
4163			      text++, 1) - adj;
4164	}
4165
4166	return x;
4167    }
4168#if OPT_BOX_CHARS
4169    /*
4170     * Draw some substitutions, if needed.  The font may not include the
4171     * line-drawing set, or it may be incomplete (in which case we'll draw an
4172     * empty space via xtermDrawBoxChar), or we may be told to force our
4173     * line-drawing.
4174     *
4175     * The empty space is a special case which can be overridden with the
4176     * showMissingGlyphs resource to produce an outline.  Not all fonts in
4177     * "modern" (sic) X provide an empty space; some use a thick outline or
4178     * something like the replacement character.  If you would rather not see
4179     * that, you can set assumeAllChars.
4180     */
4181    if (!IsIcon(screen)
4182	&& !(recur.draw_flags & NOTRANSLATION)
4183	&& (!screen->fnt_boxes
4184	    || (FontIsIncomplete(curFont) && !screen->assume_all_chars)
4185	    || screen->force_box_chars)) {
4186	/*
4187	 * Fill in missing box-characters.  Find regions without missing
4188	 * characters, and draw them calling ourselves recursively.  Draw
4189	 * missing characters via xtermDrawBoxChar().
4190	 */
4191	int last, first = 0;
4192	Bool drewBoxes = False;
4193
4194	for (last = 0; last < (int) len; last++) {
4195	    unsigned ch = (unsigned) text[last];
4196	    Bool isMissing;
4197	    int ch_width;
4198#if OPT_WIDE_CHARS
4199
4200	    if (ch == HIDDEN_CHAR) {
4201		if (last > first) {
4202		    XTermDraw param2 = recur;
4203		    param2.draw_flags |= NOTRANSLATION;
4204		    x = drawXtermText(&param2,
4205				      gc,
4206				      x, y,
4207				      text + first,
4208				      (unsigned) (last - first));
4209		}
4210		first = last + 1;
4211		drewBoxes = True;
4212		continue;
4213	    }
4214	    ch_width = CharWidth(ch);
4215	    isMissing =
4216		IsXtermMissingChar(screen, ch,
4217				   ((recur.on_wide || ch_width > 1)
4218				    && okFont(NormalWFont(screen)))
4219				   ? WhichVFontData(screen, fWide)
4220				   : curFont);
4221#else
4222	    isMissing = IsXtermMissingChar(screen, ch, curFont);
4223	    ch_width = 1;
4224#endif
4225	    /*
4226	     * If the character is not missing, but we're in wide-character
4227	     * mode and the character happens to be a wide-character that
4228	     * corresponds to the line-drawing set, allow the forceBoxChars
4229	     * resource (or menu entry) to force it to display using our
4230	     * tables.
4231	     */
4232	    if_OPT_WIDE_CHARS(screen, {
4233		if (!isMissing
4234		    && TScreenOf(recur.xw)->force_box_chars) {
4235		    if (ch > 255
4236			&& ucs2dec(screen, ch) < 32) {
4237			ch = ucs2dec(screen, ch);
4238			isMissing = True;
4239		    } else if (ch < 32) {
4240			isMissing = True;
4241		    }
4242		}
4243	    });
4244
4245	    if (isMissing) {
4246		if (last > first) {
4247		    XTermDraw param2 = recur;
4248		    param2.draw_flags |= NOTRANSLATION;
4249		    x = drawXtermText(&recur,
4250				      gc,
4251				      x, y,
4252				      text + first,
4253				      (unsigned) (last - first));
4254		}
4255#if OPT_WIDE_CHARS
4256		if (ch_width <= 0 && ch < 32)
4257		    ch_width = 1;	/* special case for line-drawing */
4258		else if (ch_width < 0)
4259		    ch_width = 1;	/* special case for combining char */
4260		if (!ucs_workaround(&recur, ch, gc, x, y)) {
4261		    xtermDrawBoxChar(&recur, ch, gc, x, y, ch_width, False);
4262		}
4263#else
4264		xtermDrawBoxChar(&recur, ch, gc, x, y, ch_width, False);
4265#endif
4266		x += (ch_width * FontWidth(screen));
4267		first = last + 1;
4268		drewBoxes = True;
4269	    }
4270	}
4271	if (last <= first) {
4272	    return x;
4273	}
4274	text += first;
4275	len = (Cardinal) (last - first);
4276	recur.draw_flags |= NOTRANSLATION;
4277	if (drewBoxes) {
4278	    return drawXtermText(&recur,
4279				 gc,
4280				 x,
4281				 y,
4282				 text,
4283				 len);
4284	}
4285    }
4286#endif /* OPT_BOX_CHARS */
4287    /*
4288     * Behave as if the font has (maybe Unicode-replacements for) drawing
4289     * characters in the range 1-31 (either we were not asked to ignore them,
4290     * or the caller made sure that there is none).
4291     */
4292#if OPT_WIDE_ATTRS
4293#define AttrFlags() recur.attr_flags
4294#define DrawFlags() recur.draw_flags
4295#else
4296#define AttrFlags() (recur.attr_flags & DRAWX_MASK)
4297#define DrawFlags() (recur.draw_flags & (unsigned)~DRAWX_MASK)
4298#endif
4299    TRACE(("drawtext%c[%4d,%4d] {%#x,%#x} (%d) %d:%s\n",
4300	   screen->cursor_state == OFF ? ' ' : '*',
4301	   y, x,
4302	   AttrFlags(),
4303	   DrawFlags(),
4304	   recur.this_chrset, len,
4305	   visibleIChars(text, len)));
4306    if (screen->scale_height != 1.0f) {
4307	xtermFillCells(&recur, gc, x, y, (Cardinal) len);
4308    }
4309    y += FontAscent(screen);
4310
4311#if OPT_WIDE_CHARS
4312
4313    if (screen->wide_chars || screen->unicode_font) {
4314	XChar2b *buffer;
4315	Bool needWide = False;
4316	int src, dst;
4317	Bool useBoldFont;
4318	int ascent_adjust = 0;
4319
4320	BumpTypedBuffer(XChar2b, len);
4321	buffer = BfBuf(XChar2b);
4322
4323	for (src = dst = 0; src < (int) len; src++) {
4324	    IChar ch = text[src];
4325
4326	    if (ch == HIDDEN_CHAR)
4327		continue;
4328
4329#if OPT_BOX_CHARS
4330	    if ((screen->fnt_boxes == 1) && (ch >= 256)) {
4331		unsigned part = ucs2dec(screen, ch);
4332		if (part < 32)
4333		    ch = (IChar) part;
4334	    }
4335#endif
4336
4337	    if (!needWide
4338		&& !IsIcon(screen)
4339		&& ((recur.on_wide || CharWidth(ch) > 1)
4340		    && okFont(NormalWFont(screen)))) {
4341		needWide = True;
4342	    }
4343#if OPT_WIDER_ICHAR
4344	    /*
4345	     * bitmap-fonts are limited to 16-bits.
4346	     */
4347	    if (ch > NARROW_ICHAR) {
4348		ch = 0;
4349	    }
4350#endif
4351	    buffer[dst].byte2 = LO_BYTE(ch);
4352	    buffer[dst].byte1 = HI_BYTE(ch);
4353#if OPT_MINI_LUIT
4354#define UCS2SBUF(value)	buffer[dst].byte2 = LO_BYTE(value);\
4355			buffer[dst].byte1 = HI_BYTE(value)
4356
4357#define Map2Sbuf(from,to) (text[src] == from) { UCS2SBUF(to); }
4358
4359	    if (screen->latin9_mode && !screen->utf8_mode && text[src] < 256) {
4360
4361		/* see http://www.cs.tut.fi/~jkorpela/latin9.html */
4362		/* *INDENT-OFF* */
4363		if Map2Sbuf(0xa4, 0x20ac)
4364		else if Map2Sbuf(0xa6, 0x0160)
4365		else if Map2Sbuf(0xa8, 0x0161)
4366		else if Map2Sbuf(0xb4, 0x017d)
4367		else if Map2Sbuf(0xb8, 0x017e)
4368		else if Map2Sbuf(0xbc, 0x0152)
4369		else if Map2Sbuf(0xbd, 0x0153)
4370		else if Map2Sbuf(0xbe, 0x0178)
4371		/* *INDENT-ON* */
4372
4373	    }
4374	    if (screen->unicode_font
4375		&& (text[src] == ANSI_DEL ||
4376		    text[src] < ANSI_SPA)) {
4377		unsigned ni = dec2ucs(screen,
4378				      (unsigned) ((text[src] == ANSI_DEL)
4379						  ? 0
4380						  : text[src]));
4381		UCS2SBUF(ni);
4382	    }
4383#endif /* OPT_MINI_LUIT */
4384	    ++dst;
4385	}
4386
4387	/*
4388	 * Check for special case where the bold font lacks glyphs found in the
4389	 * normal font, and drop down to normal fonts with overstriking to help
4390	 * show the actual characters.
4391	 */
4392	useBoldFont = ((recur.attr_flags & BOLDATTR(screen)) != 0);
4393	if (useBoldFont) {
4394	    XTermFonts *norm = 0;
4395	    XTermFonts *bold = 0;
4396	    Bool noBold, noNorm;
4397
4398	    if (needWide && okFont(BoldWFont(screen))) {
4399		norm = WhichVFontData(screen, fWide);
4400		bold = WhichVFontData(screen, fWBold);
4401	    } else if (okFont(BoldFont(screen))) {
4402		norm = WhichVFontData(screen, fNorm);
4403		bold = WhichVFontData(screen, fBold);
4404	    } else {
4405		useBoldFont = False;
4406	    }
4407
4408	    if (useBoldFont && FontIsIncomplete(bold)) {
4409		for (src = 0; src < (int) len; src++) {
4410		    IChar ch = text[src];
4411
4412		    if (ch == HIDDEN_CHAR)
4413			continue;
4414
4415		    noBold = IsXtermMissingChar(screen, ch, bold);
4416		    if (noBold) {
4417			noNorm = IsXtermMissingChar(screen, ch, norm);
4418			if (!noNorm) {
4419			    useBoldFont = False;
4420			    break;
4421			}
4422		    }
4423		}
4424	    }
4425	}
4426
4427	/* FIXME This is probably wrong. But it works. */
4428	underline_len = len;
4429
4430	/* Set the drawing font */
4431	if (!(recur.draw_flags & (DOUBLEHFONT | DOUBLEWFONT))) {
4432	    VTwin *currentWin = WhichVWin(screen);
4433	    VTFontEnum fntId;
4434	    CgsEnum cgsId;
4435	    Pixel fg = getCgsFore(recur.xw, currentWin, gc);
4436	    Pixel bg = getCgsBack(recur.xw, currentWin, gc);
4437
4438	    if (needWide
4439		&& useBoldFont
4440		&& okFont(BoldWFont(screen))) {
4441		fntId = fWBold;
4442		cgsId = gcWBold;
4443	    } else if (needWide) {
4444		fntId = fWide;
4445		cgsId = gcWide;
4446	    } else if (useBoldFont) {
4447		fntId = fBold;
4448		cgsId = gcBold;
4449	    } else {
4450		fntId = fNorm;
4451		cgsId = gcNorm;
4452	    }
4453
4454	    setCgsFore(recur.xw, currentWin, cgsId, fg);
4455	    setCgsBack(recur.xw, currentWin, cgsId, bg);
4456	    gc = getCgsGC(recur.xw, currentWin, cgsId);
4457
4458#if OPT_WIDE_ATTRS
4459#if OPT_DEC_CHRSET
4460	    if (!(CSET_DOUBLE(recur.this_chrset) || (recur.draw_flags & DOUBLEWFONT)))
4461#endif
4462		need_clipping = fixupItalics(&recur,
4463					     gc,
4464					     getCgsFont(recur.xw,
4465							currentWin, gc),
4466					     y, x, font_width, len);
4467#endif
4468	    if (fntId != fNorm) {
4469		XFontStruct *thisFp = WhichVFont(screen, fntId);
4470		ascent_adjust = (thisFp->ascent
4471				 - NormalFont(screen)->ascent);
4472		if (thisFp->max_bounds.width ==
4473		    NormalFont(screen)->max_bounds.width * 2) {
4474		    underline_len = real_length = (Cardinal) (dst * 2);
4475		} else if (cgsId == gcWide || cgsId == gcWBold) {
4476		    underline_len = real_length = (Cardinal) (dst * 2);
4477		    xtermFillCells(&recur,
4478				   gc,
4479				   x,
4480				   y - thisFp->ascent,
4481				   real_length);
4482		}
4483	    }
4484	}
4485
4486	if (recur.draw_flags & NOBACKGROUND) {
4487	    XDrawString16(screen->display,
4488			  VDrawable(screen), gc,
4489			  x, y + y_shift + ascent_adjust,
4490			  buffer, dst);
4491	} else if (dst <= MaxImageString) {
4492	    XDrawImageString16(screen->display,
4493			       VDrawable(screen), gc,
4494			       x, y + y_shift + ascent_adjust,
4495			       buffer, dst);
4496	} else {
4497	    int b_pos;
4498	    int b_max = MaxImageString;
4499	    for (b_pos = 0; b_pos < dst; b_pos += b_max) {
4500		if (b_pos + b_max > dst)
4501		    b_max = (dst - b_pos);
4502		XDrawImageString16(screen->display,
4503				   VDrawable(screen), gc,
4504				   x + (b_pos * FontWidth(screen)),
4505				   y + y_shift + ascent_adjust,
4506				   buffer + b_pos,
4507				   b_max);
4508	    }
4509	}
4510#if OPT_WIDE_ATTRS
4511	if (need_clipping) {
4512	    endClipping(screen, gc);
4513	}
4514#endif
4515
4516	if ((recur.attr_flags & BOLDATTR(screen)) && (screen->enbolden || !useBoldFont)) {
4517	    if (!(recur.draw_flags & (DOUBLEWFONT | DOUBLEHFONT))) {
4518		beginClipping(screen, gc, (Cardinal) font_width, len);
4519	    }
4520	    XDrawString16(screen->display, VDrawable(screen), gc,
4521			  x + 1,
4522			  y + y_shift + ascent_adjust,
4523			  buffer, dst);
4524	    if (!(recur.draw_flags & (DOUBLEWFONT | DOUBLEHFONT))) {
4525		endClipping(screen, gc);
4526	    }
4527	}
4528
4529    } else
4530#endif /* OPT_WIDE_CHARS */
4531    {
4532	int length = (int) len;	/* X should have used unsigned */
4533#if OPT_WIDE_CHARS
4534	char *buffer;
4535	int dst;
4536
4537	BumpTypedBuffer(char, len);
4538	buffer = BfBuf(char);
4539
4540	for (dst = 0; dst < length; ++dst)
4541	    buffer[dst] = (char) LO_BYTE(text[dst]);
4542#else
4543	char *buffer = (char *) text;
4544#endif
4545
4546#if OPT_WIDE_ATTRS
4547#if OPT_DEC_CHRSET
4548	if (!(CSET_DOUBLE(recur.this_chrset) || (recur.draw_flags & DOUBLEWFONT)))
4549#endif
4550	    need_clipping = fixupItalics(&recur, gc, curFont,
4551					 y, x, font_width, len);
4552#endif
4553
4554	if (recur.draw_flags & NOBACKGROUND) {
4555	    XDrawString(screen->display, VDrawable(screen), gc,
4556			x, y + y_shift, buffer, length);
4557	} else if (length <= MaxImageString) {
4558	    XDrawImageString(screen->display, VDrawable(screen), gc,
4559			     x, y + y_shift, buffer, length);
4560	} else {
4561	    int b_pos;
4562	    int b_max = MaxImageString;
4563	    for (b_pos = 0; b_pos < length; b_pos += b_max) {
4564		if (b_pos + b_max > length)
4565		    b_max = (length - b_pos);
4566		XDrawImageString(screen->display,
4567				 VDrawable(screen), gc,
4568				 x + (b_pos * FontWidth(screen)),
4569				 y + y_shift,
4570				 buffer + b_pos,
4571				 b_max);
4572	    }
4573	}
4574
4575#if OPT_WIDE_ATTRS
4576	if (need_clipping) {
4577	    endClipping(screen, gc);
4578	}
4579#endif
4580	underline_len = (Cardinal) length;
4581	if ((recur.attr_flags & BOLDATTR(screen)) && screen->enbolden) {
4582	    if (!(recur.draw_flags & (DOUBLEWFONT | DOUBLEHFONT))) {
4583		beginClipping(screen, gc, font_width, length);
4584	    }
4585	    XDrawString(screen->display, VDrawable(screen), gc,
4586			x + 1, y + y_shift, buffer, length);
4587	    if (!(recur.draw_flags & (DOUBLEWFONT | DOUBLEHFONT))) {
4588		endClipping(screen, gc);
4589	    }
4590	}
4591    }
4592
4593    drawUnderline(recur.xw,
4594		  gc,
4595		  recur.attr_flags,
4596		  underline_len,
4597		  font_width,
4598		  x,
4599		  y + y_shift,
4600		  did_ul);
4601
4602    x += ((int) real_length) * FontWidth(screen);
4603    return x;
4604}
4605
4606#if OPT_WIDE_CHARS
4607/*
4608 * Allocate buffer - workaround for wide-character interfaces.
4609 */
4610void
4611allocXtermChars(ScrnPtr *buffer, Cardinal length)
4612{
4613    if (*buffer == 0) {
4614	*buffer = (ScrnPtr) XtMalloc(length);
4615    } else {
4616	*buffer = (ScrnPtr) XtRealloc((char *) *buffer, length);
4617    }
4618}
4619#endif
4620
4621/* set up size hints for window manager; min 1 char by 1 char */
4622void
4623xtermSizeHints(XtermWidget xw, int scrollbarWidth)
4624{
4625    TScreen *screen = TScreenOf(xw);
4626
4627    TRACE(("xtermSizeHints\n"));
4628    TRACE(("   border    %d\n", xw->core.border_width));
4629    TRACE(("   scrollbar %d\n", scrollbarWidth));
4630
4631    xw->hints.base_width = 2 * screen->border + scrollbarWidth;
4632    xw->hints.base_height = 2 * screen->border;
4633
4634#if OPT_TOOLBAR
4635    TRACE(("   toolbar   %d\n", ToolbarHeight(xw)));
4636
4637    xw->hints.base_height += ToolbarHeight(xw);
4638    xw->hints.base_height += BorderWidth(xw) * 2;
4639    xw->hints.base_width += BorderWidth(xw) * 2;
4640#endif
4641
4642    if (xw->misc.resizeByPixel) {
4643	xw->hints.width_inc = 1;
4644	xw->hints.height_inc = 1;
4645    } else {
4646	xw->hints.width_inc = FontWidth(screen);
4647	xw->hints.height_inc = FontHeight(screen);
4648    }
4649    xw->hints.min_width = xw->hints.base_width + xw->hints.width_inc;
4650    xw->hints.min_height = xw->hints.base_height + xw->hints.height_inc;
4651
4652    xw->hints.width = MaxCols(screen) * FontWidth(screen) + xw->hints.min_width;
4653    xw->hints.height = MaxRows(screen) * FontHeight(screen) + xw->hints.min_height;
4654
4655    xw->hints.flags |= (PSize | PBaseSize | PMinSize | PResizeInc);
4656
4657    TRACE_HINTS(&(xw->hints));
4658}
4659
4660void
4661getXtermSizeHints(XtermWidget xw)
4662{
4663    TScreen *screen = TScreenOf(xw);
4664    long supp;
4665
4666    if (!XGetWMNormalHints(screen->display, VShellWindow(xw),
4667			   &xw->hints, &supp))
4668	memset(&xw->hints, 0, sizeof(xw->hints));
4669    TRACE_HINTS(&(xw->hints));
4670}
4671
4672CgsEnum
4673whichXtermCgs(XtermWidget xw, unsigned attr_flags, Bool hilite)
4674{
4675    TScreen *screen = TScreenOf(xw);
4676    CgsEnum cgsId = gcMAX;
4677
4678    if (ReverseOrHilite(screen, attr_flags, hilite)) {
4679	if (attr_flags & BOLDATTR(screen)) {
4680	    cgsId = gcBoldReverse;
4681	} else {
4682	    cgsId = gcNormReverse;
4683	}
4684    } else {
4685	if (attr_flags & BOLDATTR(screen)) {
4686	    cgsId = gcBold;
4687	} else {
4688	    cgsId = gcNorm;
4689	}
4690    }
4691    return cgsId;
4692}
4693
4694/*
4695 * Returns a GC, selected according to the font (reverse/bold/normal) that is
4696 * required for the current position (implied).  The GC is updated with the
4697 * current screen foreground and background colors.
4698 */
4699GC
4700updatedXtermGC(XtermWidget xw, unsigned attr_flags, CellColor fg_bg,
4701	       Bool hilite)
4702{
4703    TScreen *screen = TScreenOf(xw);
4704    VTwin *win = WhichVWin(screen);
4705    CgsEnum cgsId = whichXtermCgs(xw, attr_flags, hilite);
4706    Pixel my_fg = extract_fg(xw, fg_bg, attr_flags);
4707    Pixel my_bg = extract_bg(xw, fg_bg, attr_flags);
4708    Pixel fg_pix = getXtermFG(xw, attr_flags, (int) my_fg);
4709    Pixel bg_pix = getXtermBG(xw, attr_flags, (int) my_bg);
4710    Pixel xx_pix;
4711#if OPT_HIGHLIGHT_COLOR
4712    Boolean reverse2 = ((attr_flags & INVERSE) && hilite);
4713    Pixel selbg_pix = T_COLOR(screen, HIGHLIGHT_BG);
4714    Pixel selfg_pix = T_COLOR(screen, HIGHLIGHT_FG);
4715    Boolean always = screen->hilite_color;
4716    Boolean use_selbg = (Boolean) (always &&
4717				   isNotForeground(xw, fg_pix, bg_pix, selbg_pix));
4718    Boolean use_selfg = (Boolean) (always &&
4719				   isNotBackground(xw, fg_pix, bg_pix, selfg_pix));
4720#endif
4721
4722    (void) fg_bg;
4723    (void) my_bg;
4724    (void) my_fg;
4725
4726    /*
4727     * Discard video attributes overridden by colorXXXMode's.
4728     */
4729    checkVeryBoldColors(attr_flags, my_fg);
4730
4731    if (ReverseOrHilite(screen, attr_flags, hilite)) {
4732#if OPT_HIGHLIGHT_COLOR
4733	if (!screen->hilite_color) {
4734	    if (selbg_pix != T_COLOR(screen, TEXT_FG)
4735		&& selbg_pix != fg_pix
4736		&& selbg_pix != bg_pix
4737		&& selbg_pix != xw->dft_foreground) {
4738		bg_pix = fg_pix;
4739		fg_pix = selbg_pix;
4740	    }
4741	}
4742#endif
4743	EXCHANGE(fg_pix, bg_pix, xx_pix);
4744#if OPT_HIGHLIGHT_COLOR
4745	if (screen->hilite_color) {
4746	    if (screen->hilite_reverse) {
4747		if (use_selbg) {
4748		    if (use_selfg) {
4749			bg_pix = fg_pix;
4750		    } else {
4751			fg_pix = bg_pix;
4752			bg_pix = selbg_pix;
4753		    }
4754		}
4755		if (use_selfg)
4756		    fg_pix = selfg_pix;
4757	    }
4758	}
4759#endif
4760    } else if ((attr_flags & INVERSE) && hilite) {
4761#if OPT_HIGHLIGHT_COLOR
4762	if (!screen->hilite_color) {
4763	    if (selbg_pix != T_COLOR(screen, TEXT_FG)
4764		&& selbg_pix != fg_pix
4765		&& selbg_pix != bg_pix
4766		&& selbg_pix != xw->dft_foreground) {
4767		bg_pix = fg_pix;
4768		fg_pix = selbg_pix;
4769	    }
4770	}
4771#endif
4772	/* double-reverse... EXCHANGE(fg_pix, bg_pix, xx_pix); */
4773#if OPT_HIGHLIGHT_COLOR
4774	if (screen->hilite_color) {
4775	    if (screen->hilite_reverse) {
4776		if (use_selbg) {
4777		    if (use_selfg ^ reverse2) {
4778			bg_pix = fg_pix;
4779		    } else {
4780			fg_pix = bg_pix;
4781		    }
4782		    if (reverse2) {
4783			fg_pix = selbg_pix;
4784		    } else {
4785			bg_pix = selbg_pix;
4786		    }
4787		}
4788		if (use_selfg) {
4789		    if (reverse2) {
4790			bg_pix = selfg_pix;
4791		    } else {
4792			fg_pix = selfg_pix;
4793		    }
4794		}
4795	    }
4796	}
4797#endif
4798    }
4799#if OPT_HIGHLIGHT_COLOR
4800    if (!screen->hilite_color || !screen->hilite_reverse) {
4801	if (hilite && !screen->hilite_reverse) {
4802	    if (use_selbg) {
4803		if (reverse2)
4804		    fg_pix = selbg_pix;
4805		else
4806		    bg_pix = selbg_pix;
4807	    }
4808	    if (use_selfg) {
4809		if (reverse2)
4810		    bg_pix = selfg_pix;
4811		else
4812		    fg_pix = selfg_pix;
4813	    }
4814	}
4815    }
4816#endif
4817
4818#if OPT_BLINK_TEXT
4819    if ((screen->blink_state == ON) &&
4820	(!screen->blink_as_bold) &&
4821	(attr_flags & BLINK)) {
4822	fg_pix = bg_pix;
4823    }
4824#endif
4825
4826    setCgsFore(xw, win, cgsId, fg_pix);
4827    setCgsBack(xw, win, cgsId, bg_pix);
4828    return getCgsGC(xw, win, cgsId);
4829}
4830
4831/*
4832 * Resets the foreground/background of the GC returned by 'updatedXtermGC()'
4833 * to the values that would be set in SGR_Foreground and SGR_Background. This
4834 * duplicates some logic, but only modifies 1/4 as many GC's.
4835 */
4836void
4837resetXtermGC(XtermWidget xw, unsigned attr_flags, Bool hilite)
4838{
4839    TScreen *screen = TScreenOf(xw);
4840    VTwin *win = WhichVWin(screen);
4841    CgsEnum cgsId = whichXtermCgs(xw, attr_flags, hilite);
4842    Pixel fg_pix = getXtermFG(xw, attr_flags, xw->cur_foreground);
4843    Pixel bg_pix = getXtermBG(xw, attr_flags, xw->cur_background);
4844
4845    checkVeryBoldColors(attr_flags, xw->cur_foreground);
4846
4847    if (ReverseOrHilite(screen, attr_flags, hilite)) {
4848	setCgsFore(xw, win, cgsId, bg_pix);
4849	setCgsBack(xw, win, cgsId, fg_pix);
4850    } else {
4851	setCgsFore(xw, win, cgsId, fg_pix);
4852	setCgsBack(xw, win, cgsId, bg_pix);
4853    }
4854}
4855
4856#if OPT_ISO_COLORS
4857/*
4858 * Extract the foreground-color index from a color pair.
4859 * If we've got BOLD or UNDERLINE color-mode active, those will be used.
4860 */
4861Pixel
4862extract_fg(XtermWidget xw, CellColor color, unsigned attr_flags)
4863{
4864    unsigned fg = ExtractForeground(color);
4865
4866    if (TScreenOf(xw)->colorAttrMode
4867	|| (fg == ExtractBackground(color))) {
4868	fg = MapToColorMode(fg, TScreenOf(xw), attr_flags);
4869    }
4870    return fg;
4871}
4872
4873/*
4874 * Extract the background-color index from a color pair.
4875 * If we've got INVERSE color-mode active, that will be used.
4876 */
4877Pixel
4878extract_bg(XtermWidget xw, CellColor color, unsigned attr_flags)
4879{
4880    unsigned bg = ExtractBackground(color);
4881
4882    if (TScreenOf(xw)->colorAttrMode
4883	|| (bg == ExtractForeground(color))) {
4884	if (TScreenOf(xw)->colorRVMode && (attr_flags & INVERSE))
4885	    bg = COLOR_RV;
4886    }
4887    return bg;
4888}
4889
4890/*
4891 * Combine the current foreground and background into a single 8-bit number.
4892 * Note that we're storing the SGR foreground, since cur_foreground may be set
4893 * to COLOR_UL, COLOR_BD or COLOR_BL, which would make the code larger than 8
4894 * bits.
4895 *
4896 * This assumes that fg/bg are equal when we override with one of the special
4897 * attribute colors.
4898 */
4899CellColor
4900makeColorPair(XtermWidget xw)
4901{
4902    CellColor result;
4903
4904#if OPT_DIRECT_COLOR
4905    result.fg = xw->cur_foreground;
4906    result.bg = xw->cur_background;
4907#else
4908    int fg = xw->cur_foreground;
4909    int bg = xw->cur_background;
4910    unsigned my_bg = okIndexedColor(bg) ? (unsigned) bg : 0;
4911    unsigned my_fg = okIndexedColor(fg) ? (unsigned) fg : my_bg;
4912
4913    result = (CellColor) (my_fg | (my_bg << COLOR_BITS));
4914#endif
4915
4916    return result;
4917}
4918
4919/*
4920 * Using the "current" SGR background, clear a rectangle.
4921 */
4922void
4923ClearCurBackground(XtermWidget xw,
4924		   int top,
4925		   int left,
4926		   unsigned height,
4927		   unsigned width,
4928		   unsigned fw)
4929{
4930    TScreen *screen = TScreenOf(xw);
4931
4932    TRACE(("ClearCurBackground %d,%d %dx%d with %d\n",
4933	   top, left, height, width, xw->cur_background));
4934
4935    assert((int) width > 0);
4936    assert((left + (int) width) <= screen->max_col + 1);
4937    assert((int) height <= screen->max_row + 1);
4938
4939    if (VWindow(screen)) {
4940	set_background(xw, xw->cur_background);
4941
4942	xtermClear2(xw,
4943		    CursorX2(screen, left, fw),
4944		    CursorY2(screen, top),
4945		    (width * fw),
4946		    (height * (unsigned) FontHeight(screen)));
4947
4948	set_background(xw, -1);
4949    }
4950}
4951#endif /* OPT_ISO_COLORS */
4952
4953Pixel
4954getXtermBackground(XtermWidget xw, unsigned attr_flags, int color)
4955{
4956    Pixel result = T_COLOR(TScreenOf(xw), TEXT_BG);
4957
4958#if OPT_ISO_COLORS
4959    if (color >= 0) {
4960	if_OPT_DIRECT_COLOR2_else(TScreenOf(xw), (attr_flags & ATR_DIRECT_BG), {
4961	    result = (Pixel) color;
4962	}) if ((attr_flags & BG_COLOR) && (color < MAXCOLORS)) {
4963	    result = GET_COLOR_RES(xw, TScreenOf(xw)->Acolors[color]);
4964	}
4965    }
4966#else
4967    (void) attr_flags;
4968    (void) color;
4969#endif
4970    return result;
4971}
4972
4973Pixel
4974getXtermForeground(XtermWidget xw, unsigned attr_flags, int color)
4975{
4976    Pixel result = T_COLOR(TScreenOf(xw), TEXT_FG);
4977
4978#if OPT_ISO_COLORS
4979    if_OPT_DIRECT_COLOR2_else(TScreenOf(xw), (attr_flags & ATR_DIRECT_FG), {
4980	result = (Pixel) color;
4981    })
4982	if ((attr_flags & FG_COLOR) &&
4983	    (color >= 0 && color < MAXCOLORS)) {
4984	result = GET_COLOR_RES(xw, TScreenOf(xw)->Acolors[color]);
4985    }
4986#else
4987    (void) attr_flags;
4988    (void) color;
4989#endif
4990
4991#if OPT_WIDE_ATTRS
4992#define DIM_IT(n) work.n = (unsigned short) ((2 * (unsigned)work.n) / 3)
4993    if ((attr_flags & ATR_FAINT)) {
4994	static Pixel last_in;
4995	static Pixel last_out;
4996	if ((result != last_in)
4997	    && ((color >= 0)
4998		|| (result != (Pixel) color))) {
4999	    XColor work;
5000	    work.pixel = result;
5001	    last_in = result;
5002	    if (XQueryColor(TScreenOf(xw)->display, xw->core.colormap, &work)) {
5003		DIM_IT(red);
5004		DIM_IT(green);
5005		DIM_IT(blue);
5006		if (allocateBestRGB(xw, &work)) {
5007		    result = work.pixel;
5008		}
5009	    }
5010	    last_out = result;
5011	} else {
5012	    result = last_out;
5013	}
5014    }
5015#endif
5016    return result;
5017}
5018
5019/*
5020 * Returns a single base character for the given cell.
5021 */
5022unsigned
5023getXtermCell(TScreen *screen, int row, int col)
5024{
5025    CLineData *ld = getLineData(screen, row);
5026
5027    return ((ld && (col < (int) ld->lineSize))
5028	    ? ld->charData[col]
5029	    : (unsigned) ' ');
5030}
5031
5032/*
5033 * Sets a single base character for the given cell.
5034 */
5035void
5036putXtermCell(TScreen *screen, int row, int col, int ch)
5037{
5038    LineData *ld = getLineData(screen, row);
5039
5040    if (ld && (col < (int) ld->lineSize)) {
5041	ld->charData[col] = (CharData) ch;
5042	if_OPT_WIDE_CHARS(screen, {
5043	    size_t off;
5044	    for_each_combData(off, ld) {
5045		ld->combData[off][col] = 0;
5046	    }
5047	});
5048    }
5049}
5050
5051#if OPT_WIDE_CHARS
5052/*
5053 * Add a combining character for the given cell
5054 */
5055void
5056addXtermCombining(TScreen *screen, int row, int col, unsigned ch)
5057{
5058    if (ch != 0) {
5059	LineData *ld = getLineData(screen, row);
5060	size_t off;
5061
5062	TRACE(("addXtermCombining %d,%d U+%04X (%d)\n",
5063	       row, col, ch, CharWidth(ch)));
5064
5065	for_each_combData(off, ld) {
5066	    if (!ld->combData[off][col]) {
5067		ld->combData[off][col] = (CharData) ch;
5068		break;
5069	    }
5070	}
5071    }
5072}
5073
5074unsigned
5075getXtermCombining(TScreen *screen, int row, int col, int off)
5076{
5077    CLineData *ld = getLineData(screen, row);
5078    return (ld->combSize ? ld->combData[off][col] : 0U);
5079}
5080#endif
5081
5082void
5083update_keyboard_type(void)
5084{
5085    update_delete_del();
5086    update_tcap_fkeys();
5087    update_old_fkeys();
5088    update_hp_fkeys();
5089    update_sco_fkeys();
5090    update_sun_fkeys();
5091    update_sun_kbd();
5092}
5093
5094void
5095set_keyboard_type(XtermWidget xw, xtermKeyboardType type, Bool set)
5096{
5097    xtermKeyboardType save = xw->keyboard.type;
5098
5099    TRACE(("set_keyboard_type(%s, %s) currently %s\n",
5100	   visibleKeyboardType(type),
5101	   BtoS(set),
5102	   visibleKeyboardType(xw->keyboard.type)));
5103    if (set) {
5104	xw->keyboard.type = type;
5105    } else {
5106	xw->keyboard.type = keyboardIsDefault;
5107    }
5108
5109    if (save != xw->keyboard.type) {
5110	update_keyboard_type();
5111    }
5112}
5113
5114void
5115toggle_keyboard_type(XtermWidget xw, xtermKeyboardType type)
5116{
5117    xtermKeyboardType save = xw->keyboard.type;
5118
5119    TRACE(("toggle_keyboard_type(%s) currently %s\n",
5120	   visibleKeyboardType(type),
5121	   visibleKeyboardType(xw->keyboard.type)));
5122    if (xw->keyboard.type == type) {
5123	xw->keyboard.type = keyboardIsDefault;
5124    } else {
5125	xw->keyboard.type = type;
5126    }
5127
5128    if (save != xw->keyboard.type) {
5129	update_keyboard_type();
5130    }
5131}
5132
5133const char *
5134visibleKeyboardType(xtermKeyboardType type)
5135{
5136    const char *result = "?";
5137    switch (type) {
5138	CASETYPE(keyboardIsLegacy);	/* bogus vt220 codes for F1-F4, etc. */
5139	CASETYPE(keyboardIsDefault);
5140	CASETYPE(keyboardIsHP);
5141	CASETYPE(keyboardIsSCO);
5142	CASETYPE(keyboardIsSun);
5143	CASETYPE(keyboardIsTermcap);
5144	CASETYPE(keyboardIsVT220);
5145    }
5146    return result;
5147}
5148
5149static void
5150init_keyboard_type(XtermWidget xw, xtermKeyboardType type, Bool set)
5151{
5152    TRACE(("init_keyboard_type(%s, %s) currently %s\n",
5153	   visibleKeyboardType(type),
5154	   BtoS(set),
5155	   visibleKeyboardType(xw->keyboard.type)));
5156    if (set) {
5157	/*
5158	 * Check for conflicts, e.g., if someone asked for both Sun and HP
5159	 * function keys.
5160	 */
5161	if (guard_keyboard_type) {
5162	    xtermWarning("Conflicting keyboard type option (%s/%s)\n",
5163			 visibleKeyboardType(xw->keyboard.type),
5164			 visibleKeyboardType(type));
5165	}
5166	xw->keyboard.type = type;
5167	guard_keyboard_type = True;
5168	update_keyboard_type();
5169    }
5170}
5171
5172/*
5173 * If the keyboardType resource is set, use that, overriding the individual
5174 * boolean resources for different keyboard types.
5175 */
5176void
5177decode_keyboard_type(XtermWidget xw, XTERM_RESOURCE * rp)
5178{
5179#define DATA(n, t, f) { n, t, XtOffsetOf(XTERM_RESOURCE, f) }
5180#define FLAG(n) *(Boolean *)(((char *)rp) + table[n].offset)
5181    static struct {
5182	const char *name;
5183	xtermKeyboardType type;
5184	unsigned offset;
5185    } table[] = {
5186	DATA(NAME_OLD_KT, keyboardIsLegacy, oldKeyboard),
5187#if OPT_HP_FUNC_KEYS
5188	    DATA(NAME_HP_KT, keyboardIsHP, hpFunctionKeys),
5189#endif
5190#if OPT_SCO_FUNC_KEYS
5191	    DATA(NAME_SCO_KT, keyboardIsSCO, scoFunctionKeys),
5192#endif
5193#if OPT_SUN_FUNC_KEYS
5194	    DATA(NAME_SUN_KT, keyboardIsSun, sunFunctionKeys),
5195#endif
5196#if OPT_SUNPC_KBD
5197	    DATA(NAME_VT220_KT, keyboardIsVT220, sunKeyboard),
5198#endif
5199#if OPT_TCAP_FKEYS
5200	    DATA(NAME_TCAP_KT, keyboardIsTermcap, termcapKeys),
5201#endif
5202    };
5203    Cardinal n;
5204    TScreen *screen = TScreenOf(xw);
5205
5206    TRACE(("decode_keyboard_type(%s)\n", rp->keyboardType));
5207    if (!x_strcasecmp(rp->keyboardType, "unknown")) {
5208	/*
5209	 * Let the individual resources comprise the keyboard-type.
5210	 */
5211	for (n = 0; n < XtNumber(table); ++n)
5212	    init_keyboard_type(xw, table[n].type, FLAG(n));
5213    } else if (!x_strcasecmp(rp->keyboardType, "default")) {
5214	/*
5215	 * Set the keyboard-type to the Sun/PC type, allowing modified
5216	 * function keys, etc.
5217	 */
5218	for (n = 0; n < XtNumber(table); ++n)
5219	    init_keyboard_type(xw, table[n].type, False);
5220    } else {
5221	Bool found = False;
5222
5223	/*
5224	 * Special case: oldXtermFKeys should have been like the others.
5225	 */
5226	if (!x_strcasecmp(rp->keyboardType, NAME_OLD_KT)) {
5227	    TRACE(("special case, setting oldXtermFKeys\n"));
5228	    screen->old_fkeys = True;
5229	    screen->old_fkeys0 = True;
5230	}
5231
5232	/*
5233	 * Choose an individual keyboard type.
5234	 */
5235	for (n = 0; n < XtNumber(table); ++n) {
5236	    if (!x_strcasecmp(rp->keyboardType, table[n].name + 1)) {
5237		FLAG(n) = True;
5238		found = True;
5239	    } else {
5240		FLAG(n) = False;
5241	    }
5242	    init_keyboard_type(xw, table[n].type, FLAG(n));
5243	}
5244	if (!found) {
5245	    xtermWarning("KeyboardType resource \"%s\" not found\n",
5246			 rp->keyboardType);
5247	}
5248    }
5249#undef DATA
5250#undef FLAG
5251}
5252
5253#if OPT_WIDE_CHARS
5254#if defined(HAVE_WCHAR_H) && defined(HAVE_WCWIDTH)
5255/*
5256 * If xterm is running in a UTF-8 locale, it is still possible to encounter
5257 * old runtime configurations which yield incomplete or inaccurate data.
5258 */
5259static Bool
5260systemWcwidthOk(int samplesize, int samplepass)
5261{
5262    wchar_t n;
5263    int oops = 0;
5264
5265    for (n = 21; n <= 25; ++n) {
5266	wchar_t code = (wchar_t) dec2ucs(NULL, (unsigned) n);
5267	int system_code = wcwidth(code);
5268	int intern_code = mk_wcwidth(code);
5269
5270	/*
5271	 * Solaris 10 wcwidth() returns "2" for all of the line-drawing (page
5272	 * 0x2500) and most of the geometric shapes (a few are excluded, just
5273	 * to make it more difficult to use).  Do a sanity check to avoid using
5274	 * it.
5275	 */
5276	if ((system_code < 0 && intern_code >= 1)
5277	    || (system_code >= 0 && intern_code != system_code)) {
5278	    TRACE(("systemWcwidthOk: broken system line-drawing wcwidth\n"));
5279	    oops += (samplepass + 1);
5280	    break;
5281	}
5282    }
5283
5284    for (n = 0; n < (wchar_t) samplesize; ++n) {
5285	int system_code = wcwidth(n);
5286	int intern_code = mk_wcwidth(n);
5287
5288	/*
5289	 * When this check was originally implemented, there were few if any
5290	 * libraries with full Unicode coverage.  Time passes, and it is
5291	 * possible to make a full comparison of the BMP.  There are some
5292	 * differences: mk_wcwidth() marks some codes as combining and some
5293	 * as single-width, differing from GNU libc.
5294	 */
5295	if ((system_code < 0 && intern_code >= 1)
5296	    || (system_code >= 0 && intern_code != system_code)) {
5297	    TRACE((".. width(U+%04X) = %d, expected %d\n",
5298		   (unsigned) n, system_code, intern_code));
5299	    if (++oops > samplepass)
5300		break;
5301	}
5302    }
5303    TRACE(("systemWcwidthOk: %d/%d mismatches, allowed %d\n",
5304	   oops, (int) n, samplepass));
5305    return (oops <= samplepass);
5306}
5307#endif /* HAVE_WCWIDTH */
5308
5309void
5310decode_wcwidth(XtermWidget xw)
5311{
5312    int mode = ((xw->misc.cjk_width ? 2 : 0)
5313		+ (xw->misc.mk_width ? 1 : 0)
5314		+ 1);
5315
5316    switch (mode) {
5317    default:
5318#if defined(HAVE_WCHAR_H) && defined(HAVE_WCWIDTH)
5319	if (xtermEnvUTF8() &&
5320	    systemWcwidthOk(xw->misc.mk_samplesize, xw->misc.mk_samplepass)) {
5321	    my_wcwidth = wcwidth;
5322	    TRACE(("using system wcwidth() function\n"));
5323	    break;
5324	}
5325#endif
5326	/* FALLTHRU */
5327    case 2:
5328	my_wcwidth = &mk_wcwidth;
5329	TRACE(("using MK wcwidth() function\n"));
5330	break;
5331    case 3:
5332	/* FALLTHRU */
5333    case 4:
5334	my_wcwidth = &mk_wcwidth_cjk;
5335	TRACE(("using MK-CJK wcwidth() function\n"));
5336	break;
5337    }
5338
5339    for (first_widechar = 128; first_widechar < 4500; ++first_widechar) {
5340	if (my_wcwidth((wchar_t) first_widechar) > 1) {
5341	    TRACE(("first_widechar %#x\n", first_widechar));
5342	    break;
5343	}
5344    }
5345}
5346#endif
5347
5348/*
5349 * Extend a (normally) boolean resource value by checking for additional values
5350 * which will be mapped into true/false.
5351 */
5352int
5353extendedBoolean(const char *value, const FlagList * table, Cardinal limit)
5354{
5355    int result = -1;
5356    long check;
5357    char *next;
5358    Cardinal n;
5359
5360    if ((x_strcasecmp(value, "true") == 0)
5361	|| (x_strcasecmp(value, "yes") == 0)
5362	|| (x_strcasecmp(value, "on") == 0)) {
5363	result = True;
5364    } else if ((x_strcasecmp(value, "false") == 0)
5365	       || (x_strcasecmp(value, "no") == 0)
5366	       || (x_strcasecmp(value, "off") == 0)) {
5367	result = False;
5368    } else if ((check = strtol(value, &next, 0)) >= 0 && FullS2L(value, next)) {
5369	if (check >= (long) limit)	/* i.e., past False=0, True=1 */
5370	    check = True;
5371	result = (int) check;
5372    } else {
5373	for (n = 0; n < limit - 2; ++n) {
5374	    if (table[n].name == NULL) {
5375		break;
5376	    } else if (x_strcasecmp(value, table[n].name) == 0) {
5377		result = table[n].code;
5378		break;
5379	    }
5380	}
5381    }
5382
5383    if (result < 0) {
5384	xtermWarning("Unrecognized keyword: %s\n", value);
5385	result = False;
5386    }
5387
5388    TRACE(("extendedBoolean(%s) = %d\n", value, result));
5389    return result;
5390}
5391
5392/*
5393 * Something like round() from math library, but round() is less widely-used
5394 * than xterm.  Also, there are no negative numbers to complicate this.
5395 */
5396int
5397dimRound(double value)
5398{
5399    int result = (int) value;
5400    if (result < value)
5401	++result;
5402    return result;
5403}
5404
5405/*
5406 * Find the geometry of the specified Xinerama screen
5407 */
5408static void
5409find_xinerama_screen(Display *display, int screen, struct Xinerama_geometry *ret)
5410{
5411#ifdef HAVE_X11_EXTENSIONS_XINERAMA_H
5412    XineramaScreenInfo *screens;
5413    int nb_screens;
5414
5415    if (screen == -1)		/* already inited */
5416	return;
5417    screens = XineramaQueryScreens(display, &nb_screens);
5418    if (screen >= nb_screens) {
5419	xtermWarning("Xinerama screen %d does not exist\n", screen);
5420	return;
5421    }
5422    if (screen == -2) {
5423	int ptr_x, ptr_y;
5424	int dummy_int, i;
5425	unsigned dummy_uint;
5426	Window dummy_win;
5427	if (nb_screens == 0)
5428	    return;
5429	XQueryPointer(display, DefaultRootWindow(display),
5430		      &dummy_win, &dummy_win,
5431		      &ptr_x, &ptr_y,
5432		      &dummy_int, &dummy_int, &dummy_uint);
5433	for (i = 0; i < nb_screens; i++) {
5434	    if ((ptr_x - screens[i].x_org) < screens[i].width &&
5435		(ptr_y - screens[i].y_org) < screens[i].height) {
5436		screen = i;
5437		break;
5438	    }
5439	}
5440	if (screen < 0) {
5441	    xtermWarning("Mouse not in any Xinerama screen, using 0\n");
5442	    screen = 0;
5443	}
5444    }
5445    ret->scr_x = screens[screen].x_org;
5446    ret->scr_y = screens[screen].y_org;
5447    ret->scr_w = screens[screen].width;
5448    ret->scr_h = screens[screen].height;
5449#else /* HAVE_X11_EXTENSIONS_XINERAMA_H */
5450    (void) display;
5451    (void) ret;
5452    if (screen > 0)
5453	xtermWarning("Xinerama support not enabled\n");
5454#endif /* HAVE_X11_EXTENSIONS_XINERAMA_H */
5455}
5456
5457/*
5458 * Parse the screen code after the @ in a geometry string.
5459 */
5460static void
5461parse_xinerama_screen(Display *display, const char *str, struct Xinerama_geometry *ret)
5462{
5463    int screen = -1;
5464    char *end;
5465
5466    if (*str == 'g') {
5467	screen = -1;
5468	str++;
5469    } else if (*str == 'c') {
5470	screen = -2;
5471	str++;
5472    } else {
5473	long s = strtol(str, &end, 0);
5474	if (FullS2L(str, end) && ((int) s >= 0)) {
5475	    screen = (int) s;
5476	    str = end;
5477	}
5478    }
5479    if (*str) {
5480	xtermWarning("invalid Xinerama specification '%s'\n", str);
5481	return;
5482    }
5483    if (screen == -1)		/* already done */
5484	return;
5485    find_xinerama_screen(display, screen, ret);
5486}
5487
5488/*
5489 * Parse a geometry string with extra Xinerama specification:
5490 * <w>x<h>+<x>+<y>@<screen>.
5491 */
5492int
5493XParseXineramaGeometry(Display *display, char *parsestring, struct Xinerama_geometry *ret)
5494{
5495    char *at, buf[128];
5496
5497    ret->scr_x = 0;
5498    ret->scr_y = 0;
5499    ret->scr_w = DisplayWidth(display, DefaultScreen(display));
5500    ret->scr_h = DisplayHeight(display, DefaultScreen(display));
5501    at = strchr(parsestring, '@');
5502    if (at != NULL && (size_t) (at - parsestring) < sizeof(buf) - 1) {
5503	memcpy(buf, parsestring, (size_t) (at - parsestring));
5504	buf[at - parsestring] = 0;
5505	parsestring = buf;
5506	parse_xinerama_screen(display, at + 1, ret);
5507    }
5508    return ((strlen(parsestring) <= MAX_U_STRING)
5509	    ? XParseGeometry(parsestring, &ret->x, &ret->y, &ret->w, &ret->h)
5510	    : 0);
5511}
5512
5513#if USE_DOUBLE_BUFFER
5514Window
5515VDrawable(TScreen *screen)
5516{
5517    screen->needSwap = 1;
5518    return WhichVWin(screen)->drawable;
5519}
5520#endif
5521
5522#if OPT_RENDERFONT
5523#ifndef discardRenderDraw
5524void
5525discardRenderDraw(TScreen *screen)
5526{
5527    if (
5528#if USE_DOUBLE_BUFFER
5529	   resource.buffered &&
5530#endif
5531	   screen->renderDraw) {
5532	XftDrawDestroy(screen->renderDraw);
5533	screen->renderDraw = NULL;
5534    }
5535}
5536#endif
5537#endif /* OPT_RENDERFONT */
5538
5539char *
5540xtermSetLocale(int category, String after)
5541{
5542    char *before = x_strdup(setlocale(category, 0));
5543
5544    (void) setlocale(category, after);
5545    TRACE(("before setlocale :%s\n", NonNull(before)));
5546    TRACE(("updated locale   :%s\n", NonNull(setlocale(category, 0))));
5547    return before;
5548}
5549
5550void
5551xtermResetLocale(int category, char *before)
5552{
5553    (void) setlocale(category, before);
5554    free(before);
5555    TRACE(("restored locale  :%s\n", NonNull(setlocale(category, 0))));
5556}
5557