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