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