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