util.c revision 5307cd1a
1/* $XTermId: util.c,v 1.932 2023/05/28 13:29:49 tom Exp $ */
2
3/*
4 * Copyright 1999-2022,2023 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/***====================================================================***/
2704
2705typedef struct {
2706    Pixel fg;
2707    Pixel bg;
2708} ToSwap;
2709
2710#if OPT_HIGHLIGHT_COLOR
2711#define hc_param ,Bool hilite_color
2712#define hc_value ,screen->hilite_color
2713#else
2714#define hc_param		/* nothing */
2715#define hc_value		/* nothing */
2716#endif
2717
2718/*
2719 * Use this to swap the foreground/background color values in the resource
2720 * data, and to build up a list of the pairs which must be swapped in the
2721 * GC cache.
2722 */
2723static void
2724swapLocally(ToSwap * list, int *count, ColorRes * fg, ColorRes * bg hc_param)
2725{
2726    ColorRes tmp;
2727    Boolean found = False;
2728
2729    Pixel fg_color = fg->value;
2730    Pixel bg_color = bg->value;
2731
2732#if OPT_HIGHLIGHT_COLOR
2733    if ((fg_color != bg_color) || !hilite_color)
2734#endif
2735    {
2736	int n;
2737
2738	EXCHANGE(*fg, *bg, tmp);
2739	for (n = 0; n < *count; ++n) {
2740	    if ((list[n].fg == fg_color && list[n].bg == bg_color)
2741		|| (list[n].fg == bg_color && list[n].bg == fg_color)) {
2742		found = True;
2743		break;
2744	    }
2745	}
2746	if (!found) {
2747	    list[*count].fg = fg_color;
2748	    list[*count].bg = bg_color;
2749	    *count = *count + 1;
2750	    TRACE(("swapLocally fg %#lx, bg %#lx ->%d\n",
2751		   fg_color, bg_color, *count));
2752	}
2753    }
2754}
2755
2756static void
2757reallySwapColors(XtermWidget xw, ToSwap * list, int count)
2758{
2759    int j, k;
2760
2761    TRACE(("reallySwapColors\n"));
2762    for (j = 0; j < count; ++j) {
2763	for_each_text_gc(k) {
2764	    redoCgs(xw, list[j].fg, list[j].bg, (CgsEnum) k);
2765	}
2766    }
2767    FreeMarkGCs(xw);
2768}
2769
2770static void
2771swapVTwinGCs(XtermWidget xw, VTwin *win)
2772{
2773    swapCgs(xw, win, gcNorm, gcNormReverse);
2774    swapCgs(xw, win, gcBold, gcBoldReverse);
2775}
2776
2777void
2778ReverseVideo(XtermWidget xw)
2779{
2780    TScreen *screen = TScreenOf(xw);
2781    ToSwap listToSwap[5];
2782    int numToSwap = 0;
2783
2784    TRACE(("ReverseVideo now %s\n", BtoS(xw->misc.re_verse)));
2785
2786    /*
2787     * Swap SGR foreground and background colors.  By convention, these are
2788     * the colors assigned to "black" (SGR #0) and "white" (SGR #7).  Also,
2789     * SGR #8 and SGR #15 are the bold (or bright) versions of SGR #0 and
2790     * #7, respectively.
2791     *
2792     * We don't swap colors that happen to match the screen's foreground
2793     * and background because that tends to produce bizarre effects.
2794     */
2795#define swapAnyColor(name,a,b) swapLocally(listToSwap, &numToSwap, &(screen->name[a]), &(screen->name[b]) hc_value)
2796#define swapAColor(a,b) swapAnyColor(Acolors, a, b)
2797    if_OPT_ISO_COLORS(screen, {
2798	swapAColor(0, 7);
2799	swapAColor(8, 15);
2800    });
2801
2802    if (T_COLOR(screen, TEXT_CURSOR) == T_COLOR(screen, TEXT_FG)) {
2803	T_COLOR(screen, TEXT_CURSOR) = T_COLOR(screen, TEXT_BG);
2804    }
2805#define swapTColor(a,b) swapAnyColor(Tcolors, a, b)
2806    swapTColor(TEXT_FG, TEXT_BG);
2807    swapTColor(MOUSE_FG, MOUSE_BG);
2808
2809    reallySwapColors(xw, listToSwap, numToSwap);
2810
2811    swapVTwinGCs(xw, &(screen->fullVwin));
2812#ifndef NO_ACTIVE_ICON
2813    swapVTwinGCs(xw, &(screen->iconVwin));
2814#endif /* NO_ACTIVE_ICON */
2815
2816    xw->misc.re_verse = (Boolean) !xw->misc.re_verse;
2817    TRACE(("...swapping done, set ReverseVideo %s\n", BtoS(xw->misc.re_verse)));
2818
2819    if (XtIsRealized((Widget) xw)) {
2820	xtermDisplayPointer(xw);
2821    }
2822#if OPT_TEK4014
2823    if (TEK4014_SHOWN(xw)) {
2824	TekScreen *tekscr = TekScreenOf(tekWidget);
2825	Window tekwin = TWindow(tekscr);
2826	recolor_cursor(screen,
2827		       tekscr->arrow,
2828		       T_COLOR(screen, MOUSE_FG),
2829		       T_COLOR(screen, MOUSE_BG));
2830	XDefineCursor(screen->display, tekwin, tekscr->arrow);
2831    }
2832#endif
2833
2834    if (screen->scrollWidget)
2835	ScrollBarReverseVideo(screen->scrollWidget);
2836
2837    if (XtIsRealized((Widget) xw)) {
2838	set_background(xw, -1);
2839    }
2840#if OPT_TEK4014
2841    TekReverseVideo(xw, tekWidget);
2842#endif
2843    if (XtIsRealized((Widget) xw)) {
2844	xtermRepaint(xw);
2845    }
2846#if OPT_TEK4014
2847    if (TEK4014_SHOWN(xw)) {
2848	TekRepaint(tekWidget);
2849    }
2850#endif
2851    ReverseOldColors(xw);
2852    set_cursor_gcs(xw);
2853    update_reversevideo();
2854    TRACE(("...ReverseVideo now %s\n", BtoS(xw->misc.re_verse)));
2855}
2856
2857void
2858recolor_cursor(TScreen *screen,
2859	       Cursor cursor,	/* X cursor ID to set */
2860	       unsigned long fg,	/* pixel indexes to look up */
2861	       unsigned long bg)	/* pixel indexes to look up */
2862{
2863    Display *dpy = screen->display;
2864    XColor colordefs[2];	/* 0 is foreground, 1 is background */
2865
2866    colordefs[0].pixel = fg;
2867    colordefs[1].pixel = bg;
2868    XQueryColors(dpy, DefaultColormap(dpy, DefaultScreen(dpy)),
2869		 colordefs, 2);
2870    XRecolorCursor(dpy, cursor, colordefs, colordefs + 1);
2871    cleanup_colored_cursor();
2872    return;
2873}
2874
2875#if OPT_RENDERFONT
2876#define XFT_CACHE_LIMIT ((unsigned)(~0) >> 1)
2877#define XFT_CACHE_SIZE  16
2878typedef struct {
2879    XftColor color;
2880    unsigned use;
2881} XftColorCache;
2882
2883static int
2884compare_xft_color_cache(const void *a, const void *b)
2885{
2886    return (int) (((const XftColorCache *) a)->use -
2887		  ((const XftColorCache *) b)->use);
2888}
2889
2890static XftColor *
2891getXftColor(XtermWidget xw, Pixel pixel)
2892{
2893    static XftColorCache cache[XFT_CACHE_SIZE + 1];
2894    static unsigned latest_use;
2895    int i;
2896    int oldest;
2897    unsigned oldest_use;
2898    XColor color;
2899    Boolean found = False;
2900
2901    (void) xw;
2902    oldest_use = XFT_CACHE_LIMIT;
2903    oldest = 0;
2904    if (latest_use == XFT_CACHE_LIMIT) {
2905	latest_use = 0;
2906	qsort(cache, (size_t) XFT_CACHE_SIZE, sizeof(XftColorCache), compare_xft_color_cache);
2907	for (i = 0; i < XFT_CACHE_SIZE; i++) {
2908	    if (cache[i].use) {
2909		cache[i].use = ++latest_use;
2910	    }
2911	}
2912    }
2913    for (i = 0; i < XFT_CACHE_SIZE; i++) {
2914	if (cache[i].use) {
2915	    if (cache[i].color.pixel == pixel) {
2916		found = True;
2917		break;
2918	    }
2919	}
2920	if (cache[i].use < oldest_use) {
2921	    oldest_use = cache[i].use;
2922	    oldest = i;
2923	}
2924    }
2925    if (!found) {
2926	i = oldest;
2927	color.pixel = pixel;
2928	(void) QueryOneColor(xw, &color);
2929	cache[i].color.color.red = color.red;
2930	cache[i].color.color.green = color.green;
2931	cache[i].color.color.blue = color.blue;
2932	cache[i].color.color.alpha = 0xffff;
2933	cache[i].color.pixel = pixel;
2934    }
2935    cache[i].use = ++latest_use;
2936    return &cache[i].color;
2937}
2938
2939/*
2940 * The cell-width is related to, but not the same as the wide-character width.
2941 * We will only get useful values from wcwidth() for codes above 255.
2942 * Otherwise, interpret according to internal data.
2943 */
2944#if OPT_RENDERWIDE
2945
2946#if OPT_C1_PRINT
2947#define XtermCellWidth(xw, ch) \
2948	(((ch) == 0 || (ch) == 127) \
2949	  ? 0 \
2950	  : (((ch) < 256) \
2951	      ? (((ch) >= 128 && (ch) < 160) \
2952		  ? (TScreenOf(xw)->c1_printable ? 1 : 0) \
2953		  : 1) \
2954	      : CharWidth(TScreenOf(xw), ch)))
2955#else
2956#define XtermCellWidth(xw, ch) \
2957	(((ch) == 0 || (ch) == 127) \
2958	  ? 0 \
2959	  : (((ch) < 256) \
2960	      ? 1 \
2961	      : CharWidth(TScreenOf(xw), ch)))
2962#endif
2963
2964#endif /* OPT_RENDERWIDE */
2965
2966#define XFT_DATA(which) getMyXftFont(params->xw, which, fontnum)
2967
2968#if OPT_ISO_COLORS
2969#define UseBoldFont(screen) (!(screen)->colorBDMode || ((screen)->veryBoldColors & BOLD))
2970#else
2971#define UseBoldFont(screen) 1
2972#endif
2973
2974#if OPT_RENDERWIDE
2975/*
2976 * Find Xft (truetype) double-width font for the given normal/bold attributes.
2977 */
2978static XTermXftFonts *
2979getWideXftFont(XTermDraw * params,
2980	       unsigned attr_flags)
2981{
2982    TScreen *screen = TScreenOf(params->xw);
2983    int fontnum = screen->menu_font_number;
2984    XTermXftFonts *result = 0;
2985
2986#if OPT_WIDE_ATTRS
2987    if ((attr_flags & ATR_ITALIC)
2988#if OPT_ISO_COLORS
2989	&& !screen->colorITMode
2990#endif
2991	) {
2992	if ((attr_flags & BOLDATTR(screen))
2993	    && UseBoldFont(screen)
2994	    && XFT_DATA(fWBtal)) {
2995	    result = XFT_DATA(fWBtal);
2996	} else if (XFT_DATA(fWItal)) {
2997	    result = XFT_DATA(fWItal);
2998	}
2999    }
3000    if (result != 0) {
3001	;			/* skip the other tests */
3002    } else
3003#endif
3004#if OPT_ISO_COLORS
3005	if ((attr_flags & UNDERLINE)
3006	    && !screen->colorULMode
3007	    && screen->italicULMode
3008	    && XFT_DATA(fWItal)) {
3009	result = XFT_DATA(fWItal);
3010    } else
3011#endif
3012	if ((attr_flags & BOLDATTR(screen))
3013	    && UseBoldFont(screen)
3014	    && XFT_DATA(fWBold)) {
3015	result = XFT_DATA(fWBold);
3016    } else {
3017	result = XFT_DATA(fWide);
3018    }
3019    return result;
3020}
3021#endif /* OPT_RENDERWIDE */
3022
3023/*
3024 * Find Xft (truetype) single-width font for the given normal/bold attributes.
3025 */
3026static XTermXftFonts *
3027getNormXftFont(XTermDraw * params,
3028	       unsigned attr_flags,
3029	       Bool *did_ul)
3030{
3031    TScreen *screen = TScreenOf(params->xw);
3032    int fontnum = screen->menu_font_number;
3033    XTermXftFonts *result = NULL;
3034
3035    (void) did_ul;
3036#if OPT_DEC_CHRSET
3037    if (CSET_DOUBLE(params->real_chrset)) {
3038	result = xterm_DoubleFT(params, params->real_chrset, attr_flags);
3039    }
3040    if (result != 0) {
3041	;			/* found a usable double-sized font */
3042    } else
3043#endif
3044#if OPT_WIDE_ATTRS
3045	if ((attr_flags & ATR_ITALIC)
3046#if OPT_ISO_COLORS
3047	    && !screen->colorITMode
3048#endif
3049	) {
3050	if ((attr_flags & BOLDATTR(screen))
3051	    && UseBoldFont(screen)
3052	    && XFT_DATA(fBtal)) {
3053	    result = XFT_DATA(fBtal);
3054	} else if (XFT_DATA(fItal)) {
3055	    result = XFT_DATA(fItal);
3056	}
3057    }
3058    if (result != NULL) {
3059	;			/* skip the other tests */
3060    } else
3061#endif
3062#if OPT_ISO_COLORS
3063	if ((attr_flags & UNDERLINE)
3064	    && !screen->colorULMode
3065	    && screen->italicULMode
3066	    && XFT_DATA(fItal)) {
3067	result = XFT_DATA(fItal);
3068	*did_ul = True;
3069    } else
3070#endif
3071	if ((attr_flags & BOLDATTR(screen))
3072	    && UseBoldFont(screen)
3073	    && XFT_DATA(fBold)) {
3074	result = XFT_DATA(fBold);
3075    } else {
3076	result = XFT_DATA(fNorm);
3077    }
3078    return result;
3079}
3080
3081#if OPT_RENDERWIDE
3082#define pickXftData(width, nf, wf) (((width == 2) && ((wf) != NULL) && XftFp(wf) != NULL) ? (wf) : (nf))
3083#define pickXftFont(width, nf, wf) (((width == 2) && ((wf) != NULL)) ? (wf) : (nf))
3084#else
3085#define pickXftData(width, nf, wf) (nf)
3086#define pickXftFont(width, nf, wf) (nf)
3087#endif
3088
3089/*
3090 * fontconfig/Xft combination prior to 2.2 has a problem with
3091 * CJK truetype 'double-width' (bi-width/monospace) fonts leading
3092 * to the 's p a c e d o u t' rendering. Consequently, we can't
3093 * rely on XftDrawString8/16 when one of those fonts is used.
3094 * Instead, we need to roll out our own using XftDrawCharSpec.
3095 * A patch in the same spirit (but in a rather different form)
3096 * was applied to gnome vte and gtk2 port of vim.
3097 * See http://bugzilla.mozilla.org/show_bug.cgi?id=196312
3098 */
3099static int
3100xtermXftDrawString(XTermDraw * params,
3101		   unsigned attr_flags,
3102		   XftColor *color,
3103		   XftFont *font,
3104		   int x,
3105		   int y,
3106		   const IChar *text,
3107		   Cardinal len,
3108		   Bool really)
3109{
3110    TScreen *screen = TScreenOf(params->xw);
3111    int ncells = 0;
3112
3113    (void) attr_flags;
3114    if (len != 0) {
3115#if OPT_RENDERWIDE
3116	XftCharSpec *sbuf;
3117	XTermXftFonts *wdata = getWideXftFont(params, attr_flags);
3118	XftFont *wfont = XftFp(wdata);
3119	Cardinal src, dst;
3120	XftFont *lastFont = 0;
3121	XftFont *currFont = 0;
3122	Cardinal start = 0;
3123	int charWidth;
3124	int fwidth = FontWidth(screen);
3125#if OPT_DEC_CHRSET
3126	Boolean forceDbl = CSET_DOUBLE(params->real_chrset);
3127#else
3128	Boolean forceDbl = False;
3129#endif
3130
3131	BumpTypedBuffer(XftCharSpec, 2 * len);
3132	sbuf = BfBuf(XftCharSpec);
3133
3134	for (src = dst = 0; src < len; src++) {
3135	    FcChar32 wc = *text++;
3136
3137	    charWidth = XtermCellWidth(params->xw, (wchar_t) wc);
3138	    if (charWidth < 0)
3139		continue;
3140
3141	    sbuf[dst].ucs4 = wc;
3142	    sbuf[dst].x = (short) (x + fwidth * ncells);
3143	    sbuf[dst].y = (short) (y);
3144
3145	    currFont = pickXftFont(charWidth, font, wfont);
3146	    ncells += charWidth;
3147
3148	    if (lastFont != currFont) {
3149		if ((lastFont != 0) && really) {
3150		    XftDrawCharSpec(screen->renderDraw,
3151				    color,
3152				    lastFont,
3153				    sbuf + start,
3154				    (int) (dst - start));
3155		}
3156		start = dst;
3157		lastFont = currFont;
3158	    }
3159	    ++dst;
3160
3161	    if (forceDbl && charWidth < 2) {
3162		sbuf[dst].ucs4 = ' ';
3163		sbuf[dst].x = (short) (x + fwidth * ncells);
3164		sbuf[dst].y = (short) (y);
3165		++dst;
3166		ncells += charWidth;
3167	    }
3168	}
3169	if ((dst != start) && really) {
3170	    XftDrawCharSpec(screen->renderDraw,
3171			    color,
3172			    lastFont,
3173			    sbuf + start,
3174			    (int) (dst - start));
3175	}
3176#else /* !OPT_RENDERWIDE */
3177	if (really) {
3178	    XftChar8 *buffer;
3179	    int dst;
3180
3181	    BumpTypedBuffer(XftChar8, len);
3182	    buffer = BfBuf(XftChar8);
3183
3184	    for (dst = 0; dst < (int) len; ++dst)
3185		buffer[dst] = CharOf(text[dst]);
3186
3187	    XftDrawString8(screen->renderDraw,
3188			   color,
3189			   font,
3190			   x, y, buffer, (int) len);
3191	}
3192	ncells = (int) len;
3193#endif
3194	xtermNeedSwap(params->xw, 1);
3195    }
3196    return ncells;
3197}
3198#define xtermXftWidth(params, attr_flags, color, font, x, y, chars, len) \
3199   xtermXftDrawString(params, attr_flags, color, font, x, y, chars, len, False)
3200#endif /* OPT_RENDERFONT */
3201
3202#if OPT_WIDE_CHARS
3203/*
3204 * Map characters commonly "fixed" by groff back to their ASCII equivalents.
3205 * Also map other useful equivalents.
3206 */
3207unsigned
3208AsciiEquivs(unsigned ch)
3209{
3210    switch (ch) {
3211    case 0x2010:		/* groff "-" */
3212    case 0x2011:
3213    case 0x2012:
3214    case 0x2013:
3215    case 0x2014:
3216    case 0x2015:
3217    case 0x2212:		/* groff "\-" */
3218	ch = '-';
3219	break;
3220    case 0x2018:		/* groff "`" */
3221	ch = '`';
3222	break;
3223    case 0x2019:		/* groff ' */
3224	ch = '\'';
3225	break;
3226    case 0x201C:		/* groff lq */
3227    case 0x201D:		/* groff rq */
3228	ch = '"';
3229	break;
3230    case 0x2329:		/* groff ".URL" */
3231	ch = '<';
3232	break;
3233    case 0x232a:		/* groff ".URL" */
3234	ch = '>';
3235	break;
3236    default:
3237	if (ch >= 0xff01 && ch <= 0xff5e) {
3238	    /* "Fullwidth" codes (actually double-width) */
3239	    ch -= 0xff00;
3240	    ch += ANSI_SPA;
3241	    break;
3242	}
3243    }
3244    return ch;
3245}
3246
3247/*
3248 * Actually this should be called "groff_workaround()" - for the places where
3249 * groff stomps on compatibility.  Still, if enough people get used to it,
3250 * this might someday become a quasi-standard.
3251 */
3252#if OPT_BOX_CHARS
3253static int
3254ucs_workaround(XTermDraw * params,
3255	       unsigned ch,
3256	       GC gc,
3257	       int x,
3258	       int y)
3259{
3260    TScreen *screen = TScreenOf(params->xw);
3261    int fixed = False;
3262
3263    if (screen->wide_chars && screen->utf8_mode && ch > 256) {
3264	IChar eqv = (IChar) AsciiEquivs(ch);
3265
3266	if (eqv != (IChar) ch) {
3267	    int width = CharWidth(screen, ch);
3268
3269	    do {
3270		drawXtermText(params,
3271			      gc,
3272			      x,
3273			      y,
3274			      &eqv,
3275			      1);
3276		x += FontWidth(screen);
3277		eqv = BAD_ASCII;
3278	    } while (width-- > 1);
3279
3280	    fixed = True;
3281	} else if (ch == HIDDEN_CHAR) {
3282	    fixed = True;
3283	}
3284    }
3285    return fixed;
3286}
3287#endif /* OPT_BOX_CHARS */
3288#endif /* OPT_WIDE_CHARS */
3289
3290/*
3291 * Use this when the characters will not fill the cell area properly.  Fill the
3292 * area where we'll write the characters, otherwise we'll get gaps between
3293 * them, e.g., in the original background color.
3294 *
3295 * The cursor is a special case, because the XFillRectangle call only uses the
3296 * foreground, while we've set the cursor color in the background.  So we need
3297 * a special GC for that.
3298 */
3299static void
3300xtermFillCells(XTermDraw * params,
3301	       GC gc,
3302	       int x,
3303	       int y,
3304	       Cardinal len)
3305{
3306    TScreen *screen = TScreenOf(params->xw);
3307    VTwin *currentWin = WhichVWin(screen);
3308
3309    if (!(params->draw_flags & NOBACKGROUND)) {
3310	CgsEnum srcId = getCgsId(params->xw, currentWin, gc);
3311	CgsEnum dstId = gcMAX;
3312	Pixel fg = getCgsFore(params->xw, currentWin, gc);
3313	Pixel bg = getCgsBack(params->xw, currentWin, gc);
3314
3315	switch (srcId) {
3316	case gcVTcursNormal:
3317	case gcVTcursReverse:
3318	    dstId = gcVTcursOutline;
3319	    break;
3320	case gcVTcursFilled:
3321	case gcVTcursOutline:
3322	    break;
3323	case gcNorm:
3324	    dstId = gcNormReverse;
3325	    break;
3326	case gcNormReverse:
3327	    dstId = gcNorm;
3328	    break;
3329	case gcBold:
3330	    dstId = gcBoldReverse;
3331	    break;
3332	case gcBoldReverse:
3333	    dstId = gcBold;
3334	    break;
3335	case gcBorder:
3336	case gcFiller:
3337	    dstId = srcId;
3338	    break;
3339#if OPT_BOX_CHARS
3340	case gcLine:
3341	case gcDots:
3342	    break;
3343#endif
3344#if OPT_DEC_CHRSET
3345	case gcCNorm:
3346	case gcCBold:
3347	    break;
3348#endif
3349#if OPT_WIDE_CHARS
3350	case gcWide:
3351	    dstId = gcWideReverse;
3352	    break;
3353	case gcWBold:
3354	    dstId = gcBoldReverse;
3355	    break;
3356	case gcWideReverse:
3357	case gcWBoldReverse:
3358	    break;
3359#endif
3360#if OPT_TEK4014
3361	case gcTKcurs:
3362	    break;
3363#endif
3364	case gcMAX:
3365	    break;
3366	}
3367
3368	if (dstId != gcMAX) {
3369	    setCgsFore(params->xw, currentWin, dstId, bg);
3370	    setCgsBack(params->xw, currentWin, dstId, fg);
3371
3372	    XFillRectangle(screen->display, VDrawable(screen),
3373			   getCgsGC(params->xw, currentWin, dstId),
3374			   x, y,
3375			   len * (Cardinal) FontWidth(screen),
3376			   (unsigned) FontHeight(screen));
3377	}
3378    }
3379}
3380
3381#if OPT_TRACE
3382static void
3383xtermSetClipRectangles(Display *dpy,
3384		       GC gc,
3385		       int x,
3386		       int y,
3387		       XRectangle * rp,
3388		       Cardinal nr,
3389		       int order)
3390{
3391#if 0
3392    TScreen *screen = TScreenOf(term);
3393    Drawable draw = VDrawable(screen);
3394
3395    XSetClipMask(dpy, gc, None);
3396    XDrawRectangle(screen->display, draw, gc,
3397		   x + rp->x - 1,
3398		   y + rp->y - 1,
3399		   rp->width,
3400		   rp->height);
3401#endif
3402
3403    XSetClipRectangles(dpy, gc,
3404		       x, y, rp, (int) nr, order);
3405    TRACE(("clipping @(%3d,%3d) (%3d,%3d)..(%3d,%3d)\n",
3406	   y, x,
3407	   rp->y, rp->x, rp->height, rp->width));
3408}
3409
3410#else
3411#define xtermSetClipRectangles(dpy, gc, x, y, rp, nr, order) \
3412	    XSetClipRectangles(dpy, gc, x, y, rp, (int) nr, order)
3413#endif
3414
3415#if OPT_CLIP_BOLD
3416/*
3417 * This special case is a couple of percent slower, but avoids a lot of pixel
3418 * trash in rxcurses' hanoi.cmd demo (e.g., 10x20 font).
3419 */
3420#define beginClipping(screen,gc,pwidth,plength) \
3421	if (pwidth > 2) { \
3422	    if (screen->use_clipping) { \
3423		XRectangle clip; \
3424		int clip_x = x; \
3425		int clip_y = y - FontHeight(screen) + FontDescent(screen); \
3426		clip.x = 0; \
3427		clip.y = 0; \
3428		clip.height = (unsigned short) FontHeight(screen); \
3429		clip.width = (unsigned short) ((pwidth) * (plength)); \
3430		xtermSetClipRectangles(screen->display, gc, \
3431				       clip_x, clip_y, \
3432				       &clip, 1, Unsorted); \
3433	    } else if (screen->use_border_clipping) { \
3434		XRectangle clip; \
3435		clip.x = 0; \
3436		clip.y = 0; \
3437		clip.height = (unsigned short) Height(screen); \
3438		clip.width = (unsigned short) Width(screen); \
3439		xtermSetClipRectangles(screen->display, gc, \
3440				       0, 0, \
3441				       &clip, 1, Unsorted); \
3442	    } \
3443	}
3444#define endClipping(screen,gc) \
3445	    XSetClipMask(screen->display, gc, None)
3446#else
3447#define beginClipping(screen,gc,pwidth,plength)		/* nothing */
3448#define endClipping(screen,gc)	/* nothing */
3449#endif /* OPT_CLIP_BOLD */
3450
3451#if OPT_RENDERFONT
3452static int
3453drawClippedXftString(XTermDraw * params,
3454		     unsigned attr_flags,
3455		     XftFont *font,
3456		     XftColor *fg_color,
3457		     int x,
3458		     int y,
3459		     const IChar *text,
3460		     Cardinal len)
3461{
3462    int ncells = xtermXftWidth(params, attr_flags,
3463			       fg_color,
3464			       font, x, y,
3465			       text,
3466			       len);
3467    XtermWidget xw = params->xw;
3468    TScreen *screen = TScreenOf(xw);
3469    int fontHigh = FontHeight(screen);
3470    int fontWide = FontWidth(screen);
3471
3472    if (fontWide > 2) {
3473	int plength = (ncells ? ncells : 1);
3474	Boolean halfHigh = False;
3475#if OPT_DEC_CHRSET
3476	Boolean halfWide = False;
3477
3478	switch (params->real_chrset) {
3479	case CSET_SWL:
3480	    break;
3481	case CSET_DWL:
3482	    halfWide = True;
3483	    break;
3484	case CSET_DHL_TOP:
3485	    halfHigh = True;
3486	    halfWide = True;
3487	    break;
3488	case CSET_DHL_BOT:
3489	    halfHigh = True;
3490	    halfWide = True;
3491	    break;
3492	}
3493	if (CSET_DOUBLE(params->real_chrset)) {
3494	    fontHigh = font->height;
3495	    fontWide = font->max_advance_width;
3496	    /* check if this is really a double-height font */
3497#define DoubleScale(macro,field) \
3498	    (int) (macro(screen) * (1.0 + (Max(20, xw->misc.field) / 100.)))
3499	    if (halfHigh) {
3500		int wantHigh = DoubleScale(FontHeight, limit_fontheight);
3501		halfHigh = (fontHigh >= wantHigh);
3502		TRACE(("comparing fontHigh %d/%d vs %d:"
3503		       " double-height %s for %s\n",
3504		       fontHigh, FontHeight(screen),
3505		       wantHigh, BtoS(halfHigh),
3506		       visibleDblChrset(params->real_chrset)));
3507	    }
3508	    fontHigh = (halfHigh
3509			? (2 * FontHeight(screen))
3510			: FontHeight(screen));
3511	    /* check if this is really a double-width font */
3512	    if (halfWide) {
3513		int wantWide = DoubleScale(FontWidth, limit_fontwidth);
3514		halfWide = (fontWide >= wantWide);
3515		TRACE(("comparing fontWide %d/%d vs %d:"
3516		       " double-width %s for %s\n",
3517		       fontWide, FontWidth(screen),
3518		       wantWide, BtoS(halfWide),
3519		       visibleDblChrset(params->real_chrset)));
3520	    }
3521	    fontWide = (halfWide
3522			? (2 * FontWidth(screen))
3523			: FontWidth(screen));
3524	}
3525#endif
3526	if (screen->use_clipping || halfHigh) {
3527	    XRectangle clip;
3528	    double adds = ((double) screen->scale_height - 1.0f) * fontHigh;
3529	    int height = dimRound(adds + fontHigh);
3530	    int descnt = dimRound(adds / 2.0) + FontDescent(screen);
3531	    int clip_x = (x);
3532	    int clip_y = (y) - height + descnt;
3533
3534	    clip.x = 0;
3535	    clip.y = 0;
3536	    clip.height = (Dimension) height;
3537	    clip.width = (Dimension) (fontWide * (Dimension) (plength));
3538
3539#if OPT_DEC_CHRSET
3540	    if (halfHigh) {
3541		int adjust;
3542
3543		clip.height = (unsigned short) FontHeight(screen);
3544		clip_y += descnt;
3545		if (params->real_chrset == CSET_DHL_BOT) {
3546		    y -= clip.height;
3547		}
3548		adjust = ((clip_y - OriginY(screen)) % FontHeight(screen));
3549		if (adjust) {
3550		    if (adjust > FontHeight(screen) / 2)
3551			adjust -= FontHeight(screen);
3552		    clip_y -= adjust;
3553		}
3554	    }
3555#endif
3556	    XftDrawSetClipRectangles(screen->renderDraw,
3557				     clip_x, clip_y,
3558				     &clip, 1);
3559	} else if (screen->use_border_clipping) {
3560	    XRectangle clip;
3561
3562	    clip.x = (Position) OriginX(screen);
3563	    clip.y = (Position) OriginY(screen);
3564	    clip.height = (Dimension) Height(screen);
3565	    clip.width = (Dimension) Width(screen);
3566
3567	    XftDrawSetClipRectangles(screen->renderDraw,
3568				     0, 0,
3569				     &clip, 1);
3570	}
3571    }
3572
3573    xtermXftDrawString(params, attr_flags,
3574		       fg_color,
3575		       font, x, y + ScaleShift(screen),
3576		       text,
3577		       len,
3578		       True);
3579    XftDrawSetClip(screen->renderDraw, 0);
3580    return ncells;
3581}
3582#endif
3583
3584#ifndef NO_ACTIVE_ICON
3585#define WhichVFontData(screen,name) \
3586		(IsIcon(screen) ? getIconicFont(screen) \
3587				: GetNormalFont(screen, name))
3588#else
3589#define WhichVFontData(screen,name) \
3590				GetNormalFont(screen, name)
3591#endif
3592
3593static void
3594drawUnderline(XtermWidget xw,
3595	      GC gc,
3596	      unsigned attr_flags,
3597	      unsigned underline_len,
3598	      int font_width,
3599	      int x,
3600	      int y,
3601	      Bool did_ul)
3602{
3603    TScreen *screen = TScreenOf(xw);
3604
3605    if (screen->underline && !did_ul) {
3606	int repeat = 0;
3607	int descent = FontDescent(screen);
3608	int length = x + (int) underline_len * font_width - 1;
3609
3610#if OPT_WIDE_ATTRS
3611	if ((attr_flags & ATR_STRIKEOUT)) {
3612	    int where = y - ((3 * FontAscent(screen)) / 8);
3613	    XDrawLine(screen->display, VDrawable(screen), gc,
3614		      x, where,
3615		      length,
3616		      where);
3617	}
3618	if ((attr_flags & ATR_DBL_UNDER)) {
3619	    repeat = 2;
3620	} else
3621#endif
3622	if ((attr_flags & UNDERLINE)) {
3623	    repeat = 1;
3624	}
3625	while (repeat-- > 0) {
3626	    if (descent-- > 1)
3627		y++;
3628	    XDrawLine(screen->display, VDrawable(screen), gc,
3629		      x, y,
3630		      length,
3631		      y);
3632	}
3633    }
3634}
3635
3636#if OPT_WIDE_ATTRS
3637/*
3638 * As a special case, we are currently allowing italic fonts to be inexact
3639 * matches for the normal font's size.  That introduces a problem:  either the
3640 * ascent or descent may be shorter, leaving a gap that has to be filled in.
3641 * Or they may be larger, requiring clipping.  Check for both cases.
3642 */
3643static int
3644fixupItalics(XTermDraw * params,
3645	     GC gc,
3646	     XTermFonts * curFont,
3647	     int y, int x,
3648	     int font_width,
3649	     Cardinal len)
3650{
3651    TScreen *screen = TScreenOf(params->xw);
3652    VTwin *cgsWin = WhichVWin(screen);
3653    XFontStruct *realFp = curFont->fs;
3654    XFontStruct *thisFp = getCgsFont(params->xw, cgsWin, gc)->fs;
3655    int need_clipping = 0;
3656    int need_filling = 0;
3657
3658    if (thisFp->ascent > realFp->ascent)
3659	need_clipping = 1;
3660    else if (thisFp->ascent < realFp->ascent)
3661	need_filling = 1;
3662
3663    if (thisFp->descent > realFp->descent)
3664	need_clipping = 1;
3665    else if (thisFp->descent < realFp->descent)
3666	need_filling = 1;
3667
3668    if (need_clipping) {
3669	beginClipping(screen, gc, font_width, (int) len);
3670    }
3671    if (need_filling) {
3672	xtermFillCells(params,
3673		       gc,
3674		       x,
3675		       y - realFp->ascent,
3676		       len);
3677    }
3678    return need_clipping;
3679}
3680#endif
3681
3682#if OPT_DEC_CHRSET
3683static int
3684fakeDoubleChars(XTermDraw * params,
3685		GC gc,
3686		int y,
3687		int x,
3688		const IChar *text,
3689		Cardinal len)
3690{
3691    unsigned need = 2 * len;
3692    IChar *temp = TypeMallocN(IChar, need);
3693
3694    if (temp != 0) {
3695	unsigned n = 0;
3696	XTermDraw recur = *params;
3697
3698	recur.this_chrset = CSET_SWL;
3699
3700	while (len--) {
3701	    temp[n++] = *text++;
3702	    temp[n++] = ' ';
3703	}
3704	x = drawXtermText(&recur,
3705			  gc,
3706			  x, y,
3707			  temp,
3708			  n);
3709	free(temp);
3710    }
3711    return x;
3712}
3713#endif /* OPT_DEC_CHRSET */
3714
3715#define SetMissing(tag) \
3716	TRACE(("%s %s: missing %d %04X\n", __FILE__, tag, missing, ch)); \
3717	missing = 1
3718
3719#define MaxImageString 255
3720
3721#define UCS2SBUF(value)	buffer2[dst].byte2 = LO_BYTE(value);\
3722			buffer2[dst].byte1 = HI_BYTE(value)
3723
3724#if OPT_WIDE_CHARS
3725static int
3726xtermDrawMissing(TScreen *screen, unsigned flags, GC gc, int x, int y, int ncells)
3727{
3728    /* *INDENT-EQLS* */
3729    int width   = FontWidth(screen) * ncells;
3730    int height  = FontHeight(screen);
3731    int descent = FontDescent(screen);
3732    int thick   = Max((height / 16), 1);
3733    int thick2  = 2 * thick;
3734    int yhigh   = height - thick2;
3735    int too_big = (flags & (DOUBLEWFONT | DOUBLEHFONT)) != 0;
3736
3737    TRACE(("*missing [%4d,%4d] %dx%d (%d)/%d%s%s%s%s\n",
3738	   y, x,
3739	   height, width,
3740	   ncells,
3741	   descent,
3742	   flags ? " recur" : "",
3743	   flags & DOUBLEFIRST ? "*" : "",
3744	   flags & DOUBLEWFONT ? ":W" : "",
3745	   flags & DOUBLEHFONT ? ":H" : ""));
3746    if (width > thick2 && height > thick2) {
3747	if (too_big) {
3748	    if (flags & DOUBLEWFONT) {
3749		width *= 2;
3750	    }
3751	    if (flags & DOUBLEHFONT) {
3752		if (flags & DOUBLEFIRST) {
3753		    height *= 2;
3754		    height -= thick;
3755		} else {
3756		    height += thick;
3757		}
3758		descent *= 2;
3759	    }
3760	} else {
3761	    beginClipping(screen, gc, (Cardinal) width, (Cardinal) ncells);
3762	}
3763#if OPT_BOX_CHARS
3764	if (screen->force_all_chars)
3765#endif
3766	{
3767	    int xpos = x;
3768	    int ypos = (y - height + descent + thick);
3769	    unsigned high = (unsigned) yhigh;
3770	    unsigned wide = (unsigned) (width - thick2);
3771
3772	    XSetLineAttributes(screen->display, gc,
3773			       (unsigned) thick,
3774			       LineOnOffDash,
3775			       CapProjecting,
3776			       JoinMiter);
3777	    XDrawImageString(screen->display, VDrawable(screen), gc,
3778			     x, y,
3779			     "    ",
3780			     Min(4, ncells));
3781	    XDrawRectangle(screen->display,
3782			   VDrawable(screen), gc,
3783			   xpos, ypos,
3784			   wide, high);
3785	}
3786	if (!too_big) {
3787	    endClipping(screen, gc);
3788	}
3789    }
3790    return x + width;
3791}
3792
3793static int
3794xtermPartString16(TScreen *screen, unsigned flags, GC gc, int x, int y, int length)
3795{
3796    if (length > 0) {
3797	XChar2b *buffer2 = BfBuf(XChar2b);
3798	Char *buffer1 = BfBuf(Char);
3799	int n;
3800	Bool eightBit = True;
3801
3802	for (n = 0; n < length; ++n) {
3803	    if (buffer2[n].byte1 != 0) {
3804		eightBit = False;
3805		break;
3806	    }
3807	    buffer1[n] = buffer2[n].byte2;
3808	}
3809
3810	if ((flags & NOBACKGROUND)) {
3811	    if (eightBit) {
3812		XDrawString(screen->display,
3813			    VDrawable(screen), gc,
3814			    x, y,
3815			    (char *) buffer1, length);
3816	    } else {
3817		XDrawString16(screen->display,
3818			      VDrawable(screen), gc,
3819			      x, y,
3820			      buffer2, length);
3821	    }
3822	} else {
3823	    int b_pos;
3824	    int b_max = MaxImageString;
3825	    for (b_pos = 0; b_pos < length; b_pos += b_max) {
3826		if (b_pos + b_max > length)
3827		    b_max = (length - b_pos);
3828		if (eightBit) {
3829		    XDrawImageString(screen->display,
3830				     VDrawable(screen), gc,
3831				     x + (b_pos * FontWidth(screen)),
3832				     y,
3833				     (char *) buffer1 + b_pos,
3834				     b_max);
3835		} else {
3836		    XDrawImageString16(screen->display,
3837				       VDrawable(screen), gc,
3838				       x + (b_pos * FontWidth(screen)),
3839				       y,
3840				       buffer2 + b_pos,
3841				       b_max);
3842		}
3843	    }
3844	}
3845	x += length * FontWidth(screen);
3846    }
3847    return x;
3848}
3849
3850static int
3851xtermFullString16(XtermWidget xw, unsigned flags, GC gc, int x, int y, int length)
3852{
3853    TScreen *screen = TScreenOf(xw);
3854    int src, dst;
3855    IChar *mapped = BfBuf(IChar);
3856    XChar2b *buffer2 = BfBuf(XChar2b);
3857    VTwin *currentWin = WhichVWin(screen);
3858    XTermFonts *xf = getCgsFont(xw, currentWin, gc);
3859    XTermFonts *fp = xf ? xf : getNormalFont(screen, fNorm);
3860
3861    for (src = dst = 0; src < (int) length; src++) {
3862	IChar ch = mapped[src];
3863	int ch_width = CharWidth(screen, ch);
3864	/*
3865	 * X11 bitmap-fonts are limited to 16-bits.
3866	 */
3867	if (
3868#if OPT_WIDER_ICHAR
3869	       (ch > NARROW_ICHAR) ||
3870#endif
3871	       xtermMissingChar(ch, fp)) {
3872	    x = xtermPartString16(screen, flags, gc, x, y, dst);
3873	    x = xtermDrawMissing(screen, flags, gc, x, y, ch_width);
3874	    dst = 0;
3875	} else {
3876	    UCS2SBUF(ch);
3877	    ++dst;
3878	}
3879    }
3880
3881    x = xtermPartString16(screen, flags, gc, x, y, dst);
3882    return x;
3883}
3884#endif /* OPT_WIDE_CHARS */
3885
3886static void
3887xtermPartString(TScreen *screen, unsigned flags, GC gc, int x, int y, int length)
3888{
3889    Char *buffer1 = BfBuf(Char);
3890
3891    if ((flags & NOBACKGROUND)) {
3892	XDrawString(screen->display, VDrawable(screen), gc,
3893		    x, y, (char *) buffer1, length);
3894    } else {
3895	int b_pos;
3896	int b_max = MaxImageString;
3897	for (b_pos = 0; b_pos < length; b_pos += b_max) {
3898	    if (b_pos + b_max > length)
3899		b_max = (length - b_pos);
3900	    XDrawImageString(screen->display,
3901			     VDrawable(screen), gc,
3902			     x + (b_pos * FontWidth(screen)),
3903			     y,
3904			     (char *) (buffer1 + b_pos),
3905			     b_max);
3906	}
3907    }
3908}
3909
3910static void
3911xtermDrawString(TScreen *screen, unsigned flags, GC gc, int x, int y, int length)
3912{
3913    IChar *mapped = BfBuf(IChar);
3914    Char *buffer1 = BfBuf(Char);
3915
3916    int dst;
3917
3918    for (dst = 0; dst < length; ++dst)
3919	buffer1[dst] = (Char) LO_BYTE(mapped[dst]);
3920
3921    xtermPartString(screen, flags, gc, x, y, length);
3922}
3923
3924/*
3925 * Draws text with the specified combination of bold/underline.  The return
3926 * value is the updated x position.
3927 */
3928int
3929drawXtermText(XTermDraw * params,
3930	      GC gc,
3931	      int start_x,
3932	      int start_y,
3933	      const IChar *text,
3934	      Cardinal len)
3935{
3936    XTermDraw recur = *params;
3937    TScreen *screen = TScreenOf(recur.xw);
3938    int x = start_x;
3939    int y = start_y;
3940    int y_shift = ScaleShift(screen);
3941    Cardinal real_length = len;
3942    Cardinal underline_len = 0;
3943    /* Intended width of the font to draw (as opposed to the actual width of
3944       the X font, and the width of the default font) */
3945    int font_width = (((recur.draw_flags & DOUBLEWFONT) ? 2 : 1)
3946		      * screen->fnt_wide);
3947    Bool did_ul = False;
3948    XTermFonts *curFont;
3949
3950#if OPT_WIDE_ATTRS
3951    int need_clipping = 0;
3952#endif
3953
3954#if OPT_WIDE_CHARS
3955    Bool need_wide;
3956#endif
3957
3958#if OPT_WIDE_CHARS
3959    if (text == 0)
3960	return 0;
3961#endif
3962    TRACE(("DRAWTEXT%c[%4d,%4d] (%d)%3d:%s\n",
3963	   screen->cursor_state == OFF ? ' ' : '*',
3964	   y, x, recur.this_chrset, len,
3965	   visibleIChars(text, len)));
3966
3967#if OPT_DEC_CHRSET
3968    if (CSET_DOUBLE(recur.this_chrset)) {
3969	/* We could try drawing double-size characters in the icon, but
3970	 * given that the icon font is usually nil or nil2, there
3971	 * doesn't seem to be much point.
3972	 */
3973	int inx = 0;
3974	GC gc2;
3975
3976#if OPT_RENDERFONT
3977	if (UsingRenderFont(recur.xw)) {
3978	    if (!IsIcon(screen) && screen->font_doublesize) {
3979		int next;
3980		TRACE(("drawing %s\n", visibleDblChrset((unsigned) recur.this_chrset)));
3981		recur.real_chrset = recur.this_chrset;
3982		recur.this_chrset = CSET_SWL;
3983		next = drawXtermText(&recur,
3984				     gc,
3985				     x, y,
3986				     text,
3987				     len);
3988		x += (next - x) * 2;
3989	    } else {
3990		x = fakeDoubleChars(&recur,
3991				    gc, y, x,
3992				    text, len);
3993	    }
3994	} else
3995#endif
3996	    if ((!IsIcon(screen) && screen->font_doublesize)
3997		&& (gc2 = xterm_DoubleGC(&recur, gc, &inx)) != 0) {
3998	    /* draw actual double-sized characters */
3999	    XFontStruct *fs = getDoubleFont(screen, inx)->fs;
4000	    XRectangle rect, *rp = &rect;
4001	    Cardinal nr = 1;
4002	    Cardinal nlen;
4003	    int ncells = (int) len;
4004
4005	    font_width *= 2;
4006	    recur.draw_flags |= DOUBLEWFONT;
4007
4008	    for (nlen = 0; nlen < len; ++nlen) {
4009		int ch_width = CharWidth(screen, text[nlen]);
4010		if (ch_width > 1)
4011		    ncells += (ch_width - 1);
4012	    }
4013
4014	    rect.x = 0;
4015	    rect.y = 0;
4016	    rect.width = (unsigned short) (ncells * font_width);
4017	    rect.height = (unsigned short) (FontHeight(screen));
4018
4019	    TRACE(("drawing %s\n", visibleDblChrset((unsigned) recur.this_chrset)));
4020	    switch (recur.this_chrset) {
4021	    case CSET_DHL_TOP:
4022		rect.y = (short) -(fs->ascent / 2);
4023		y -= rect.y;
4024		recur.draw_flags |= (DOUBLEHFONT | DOUBLEFIRST);
4025		break;
4026	    case CSET_DHL_BOT:
4027		rect.y = (short) (rect.height - (fs->ascent / 2));
4028		y -= rect.y;
4029		recur.draw_flags |= DOUBLEHFONT;
4030		recur.draw_flags &= (unsigned) ~DOUBLEFIRST;
4031		break;
4032	    case CSET_DWL:
4033		break;
4034	    default:
4035		nr = 0;
4036		break;
4037	    }
4038
4039	    if (nr) {
4040		xtermSetClipRectangles(screen->display, gc2,
4041				       x, y, rp, nr, YXBanded);
4042		xtermFillCells(&recur, gc, x, y + rect.y,
4043			       (Cardinal) ncells * 2);
4044	    } else {
4045		XSetClipMask(screen->display, gc2, None);
4046	    }
4047
4048	    /* Call ourselves recursively with the new gc */
4049
4050	    /*
4051	     * If we're trying to use proportional font, or if the
4052	     * font server didn't give us what we asked for wrt
4053	     * width, position each character independently.
4054	     */
4055	    if (screen->fnt_prop
4056		|| (fs->min_bounds.width != fs->max_bounds.width)
4057		|| (fs->min_bounds.width != 2 * FontWidth(screen))) {
4058		/* It is hard to fall-through to the main
4059		   branch: in a lot of places the check
4060		   for the cached font info is for
4061		   normal/bold fonts only. */
4062		XTermDraw param2 = recur;
4063		param2.this_chrset = CSET_SWL;
4064		while (len--) {
4065		    int next = drawXtermText(&param2,
4066					     gc2,
4067					     x, y,
4068					     text++,
4069					     1);
4070		    x += (next - x) * 2;
4071		}
4072	    } else {
4073		int next;
4074		XTermDraw param2 = recur;
4075		param2.this_chrset = CSET_SWL;
4076		next = drawXtermText(&param2,
4077				     gc2,
4078				     x, y,
4079				     text,
4080				     len);
4081		x += (next - x) * 2;
4082	    }
4083
4084	} else {		/* simulate double-sized characters */
4085	    x = fakeDoubleChars(&recur,
4086				gc, y, x,
4087				text, len);
4088	}
4089	TRACE(("DrewText [%4d,%4d] @%d\n", y, x, __LINE__));
4090	return x;
4091    }
4092#endif
4093#if OPT_RENDERFONT
4094    if (UsingRenderFont(recur.xw)) {
4095	VTwin *currentWin = WhichVWin(screen);
4096	Display *dpy = screen->display;
4097	XTermXftFonts *ndata;
4098	XGCValues values;
4099#if OPT_WIDE_CHARS && OPT_BOX_CHARS
4100	XTermXftFonts *ndata0;
4101#endif
4102#if OPT_RENDERWIDE && OPT_BOX_CHARS
4103	XTermXftFonts *wdata;
4104	XTermXftFonts *wdata0;
4105#endif
4106	int ncells = 0;
4107
4108#if OPT_DEC_CHRSET
4109	/*
4110	 * If this is a VT100-style double-width font, ensure that everything
4111	 * is printed using two-columns per character.
4112	 */
4113	Boolean forceDbl = CSET_DOUBLE(recur.real_chrset);
4114#else
4115	Boolean forceDbl = False;
4116#endif
4117
4118	(void) forceDbl;
4119	/*
4120	 * Defer creating the drawable until we need it.
4121	 */
4122	if (!screen->renderDraw) {
4123	    int scr;
4124	    Drawable draw = VDrawable(screen);
4125	    Visual *visual;
4126
4127	    scr = DefaultScreen(dpy);
4128	    visual = DefaultVisual(dpy, scr);
4129	    screen->renderDraw = XftDrawCreate(dpy, draw, visual,
4130					       DefaultColormap(dpy, scr));
4131	}
4132
4133	/*
4134	 * ndata0/wdata0 provide fallback to non-bolded Xft font if a glyph is
4135	 * not found in the bold version.
4136	 */
4137#define IS_BOLD  (recur.attr_flags & BOLDATTR(screen))
4138#define NOT_BOLD (recur.attr_flags & ~BOLDATTR(screen))
4139	ndata = getNormXftFont(&recur, recur.attr_flags, &did_ul);
4140#if OPT_WIDE_CHARS && OPT_BOX_CHARS
4141	ndata0 = IS_BOLD ? getNormXftFont(&recur, NOT_BOLD, &did_ul) : ndata;
4142#endif
4143#if OPT_RENDERWIDE && OPT_BOX_CHARS
4144	wdata = getWideXftFont(&recur, recur.attr_flags);
4145	wdata0 = IS_BOLD ? getWideXftFont(&recur, NOT_BOLD) : wdata;
4146#endif
4147
4148#define GET_XFT_FG() getXftColor(recur.xw, values.foreground)
4149#define GET_XFT_BG() getXftColor(recur.xw, values.background)
4150
4151	values.foreground = getCgsFore(recur.xw, currentWin, gc);
4152	values.background = getCgsBack(recur.xw, currentWin, gc);
4153
4154	if (!(recur.draw_flags & NOBACKGROUND)) {
4155	    XftColor *bg_color = GET_XFT_BG();
4156	    int nc = xtermXftWidth(&recur, recur.attr_flags,
4157				   bg_color,
4158				   XftFp(ndata), x, y,
4159				   text,
4160				   len);
4161	    XftDrawRect(screen->renderDraw,
4162			bg_color,
4163			x, y,
4164			(unsigned) (nc * FontWidth(screen)),
4165			(unsigned) FontHeight(screen));
4166	}
4167
4168	y += XftFp(ndata)->ascent;
4169#if OPT_BOX_CHARS
4170	{
4171	    /* adding code to substitute simulated line-drawing characters */
4172	    int last, first = 0;
4173	    int curX = x;
4174
4175	    for (last = 0; last < (int) len; last++) {
4176		Boolean replace = False;
4177		Boolean missing = False;
4178		unsigned ch = (unsigned) text[last];
4179		int ch_width = CharWidth(screen, ch);
4180		int filler = 0;
4181#if OPT_WIDE_CHARS
4182		int needed = forceDbl ? 2 : ch_width;
4183		XTermXftFonts *currData = pickXftData(needed, ndata, wdata);
4184		XftFont *tempFont = 0;
4185#define CURR_TEMP (tempFont ? tempFont : XftFp(currData))
4186
4187		if (xtermIsDecGraphic(ch) || ch == 0) {
4188		    /*
4189		     * Xft generally does not have the line-drawing characters
4190		     * in cells 0-31.  Assume this (we cannot inspect the
4191		     * picture easily...), and attempt to fill in from real
4192		     * line-drawing character in the font at the Unicode
4193		     * position.  Failing that, use our own box-characters.
4194		     */
4195		    if (screen->force_box_chars
4196			|| screen->broken_box_chars
4197			|| xtermXftMissing(recur.xw,
4198					   currData, 0,
4199					   XftFp(currData),
4200					   dec2ucs(screen, ch))) {
4201			SetMissing("case 1");
4202		    } else {
4203			ch = dec2ucs(screen, ch);
4204			replace = True;
4205		    }
4206		} else if (ch >= 256) {
4207		    /*
4208		     * If we're reading UTF-8 from the client, we may have a
4209		     * line-drawing character.  Translate it back to our
4210		     * box-code if Xft tells us that the glyph is missing.
4211		     */
4212		    if_OPT_WIDE_CHARS(screen, {
4213			unsigned part = ucs2dec(screen, ch);
4214			if (xtermIsDecGraphic(part)) {
4215			    if (screen->force_box_chars
4216				|| screen->broken_box_chars) {
4217				SetMissing("case 2");
4218				ch = part;
4219			    }
4220			}
4221			if (!missing && xtermXftMissing(recur.xw,
4222							currData, 0,
4223							XftFp(currData), ch)) {
4224			    int found = findXftGlyph(recur.xw, currData, ch);
4225			    XftFont *test;
4226			    if (found >= 0) {
4227				test = XftFpN(currData, found);
4228			    } else {
4229				test = pickXftFont(needed,
4230						   XftFp(ndata0),
4231						   XftFp(wdata0));
4232			    }
4233			    if (!xtermXftMissing(recur.xw,
4234						 currData, found,
4235						 test, ch)) {
4236				tempFont = test;
4237				replace = True;
4238				filler = 0;
4239			    } else if ((part = AsciiEquivs(ch)) != ch) {
4240				filler = needed - 1;
4241				ch = part;
4242				replace = True;
4243			    } else if (ch != HIDDEN_CHAR) {
4244				SetMissing("case 3");
4245			    }
4246			}
4247		    });
4248		}
4249#else
4250		XTermXftFonts *currData = ndata;
4251#define CURR_TEMP XftFp(currData)
4252		if (xtermIsDecGraphic(ch)) {
4253		    /*
4254		     * Xft generally does not have the line-drawing characters
4255		     * in cells 1-31.  Check for this, and attempt to fill in
4256		     * from real line-drawing character in the font at the
4257		     * Unicode position.  Failing that, use our own
4258		     * box-characters.
4259		     */
4260		    if (xtermXftMissing(recur.xw,
4261					currData, 0,
4262					XftFp(currData), ch)) {
4263			SetMissing("case 4");
4264		    }
4265		}
4266#endif
4267
4268		/*
4269		 * If we now have one of our box-codes, draw it directly.
4270		 */
4271		if (missing || replace) {
4272		    /* line drawing character time */
4273		    if (last > first) {
4274			int nc = drawClippedXftString(&recur,
4275						      recur.attr_flags,
4276						      XftFp(currData),
4277						      GET_XFT_FG(),
4278						      curX,
4279						      y,
4280						      text + first,
4281						      (Cardinal) (last - first));
4282			curX += nc * FontWidth(screen);
4283			underline_len += (Cardinal) nc;
4284		    }
4285		    if (missing) {
4286			Dimension old_wide = screen->fnt_wide;
4287			Dimension old_high = screen->fnt_high;
4288			screen->fnt_wide = (Dimension) FontWidth(screen);
4289			screen->fnt_high = (Dimension) FontHeight(screen);
4290
4291			xtermDrawBoxChar(&recur, ch,
4292					 gc,
4293					 curX,
4294					 y - FontAscent(screen),
4295					 1,
4296					 True);
4297			curX += FontWidth(screen);
4298			underline_len += 1;
4299			screen->fnt_wide = old_wide;
4300			screen->fnt_high = old_high;
4301		    } else {
4302			IChar ch2 = (IChar) ch;
4303			int nc = drawClippedXftString(&recur,
4304						      recur.attr_flags,
4305						      CURR_TEMP,
4306						      GET_XFT_FG(),
4307						      curX,
4308						      y,
4309						      &ch2,
4310						      1);
4311			curX += nc * FontWidth(screen);
4312			underline_len += (Cardinal) nc;
4313			if (filler) {
4314			    ch2 = ' ';
4315			    nc = drawClippedXftString(&recur,
4316						      recur.attr_flags,
4317						      CURR_TEMP,
4318						      GET_XFT_FG(),
4319						      curX,
4320						      y,
4321						      &ch2,
4322						      1);
4323			    curX += nc * FontWidth(screen);
4324			    underline_len += (Cardinal) nc;
4325			}
4326		    }
4327		    first = last + 1;
4328		}
4329		if (ch_width > 0)
4330		    ncells += ch_width;
4331	    }
4332	    if (last > first) {
4333		int nc = drawClippedXftString(&recur,
4334					      recur.attr_flags,
4335					      XftFp(ndata),
4336					      GET_XFT_FG(),
4337					      curX,
4338					      y,
4339					      text + first,
4340					      (Cardinal) (last - first));
4341		underline_len += (Cardinal) nc;
4342	    }
4343	}
4344#else
4345	{
4346	    int nc = drawClippedXftString(&recur,
4347					  recur.attr_flags,
4348					  XftFp(ndata),
4349					  GET_XFT_FG(),
4350					  x,
4351					  y,
4352					  text,
4353					  len);
4354	    underline_len += (Cardinal) nc;
4355	    ncells = nc;
4356	}
4357#endif /* OPT_BOX_CHARS */
4358
4359	drawUnderline(recur.xw,
4360		      gc,
4361		      recur.attr_flags,
4362		      underline_len,
4363		      FontWidth(screen),
4364		      x,
4365		      y + y_shift,
4366		      did_ul);
4367
4368	x += (int) ncells *FontWidth(screen);
4369
4370	TRACE(("DrewText [%4d,%4d] @%d\n", y, x, __LINE__));
4371	return x;
4372    }
4373#endif /* OPT_RENDERFONT */
4374    curFont = ((recur.attr_flags & BOLDATTR(screen))
4375	       ? WhichVFontData(screen, fBold)
4376	       : WhichVFontData(screen, fNorm));
4377    /*
4378     * If we're asked to display a proportional font, do this with a fixed
4379     * pitch.  Yes, it's ugly.  But we cannot distinguish the use of xterm
4380     * as a dumb terminal vs its use as in fullscreen programs such as vi.
4381     * Hint: do not try to use a proportional font in the icon.
4382     */
4383    if (!IsIcon(screen) && !(recur.draw_flags & CHARBYCHAR) && screen->fnt_prop) {
4384	int adj, width;
4385
4386	while (len--) {
4387	    int cells = WideCells(*text);
4388#if OPT_BOX_CHARS
4389#if OPT_WIDE_CHARS
4390	    if (*text == HIDDEN_CHAR) {
4391		++text;
4392		continue;
4393	    } else
4394#endif
4395	    if (IsXtermMissingChar(screen, *text, curFont)) {
4396		adj = 0;
4397	    } else
4398#endif
4399	    {
4400		if_WIDE_OR_NARROW(screen, {
4401		    XChar2b temp[1];
4402		    temp[0].byte2 = LO_BYTE(*text);
4403		    temp[0].byte1 = HI_BYTE(*text);
4404		    width = XTextWidth16(curFont->fs, temp, 1);
4405		}
4406		, {
4407		    char temp[1];
4408		    temp[0] = (char) LO_BYTE(*text);
4409		    width = XTextWidth(curFont->fs, temp, 1);
4410		});
4411		adj = (FontWidth(screen) - width) / 2;
4412		if (adj < 0)
4413		    adj = 0;
4414	    }
4415	    xtermFillCells(&recur, gc, x, y, (Cardinal) cells);
4416	    recur.draw_flags |= (NOBACKGROUND | CHARBYCHAR);
4417	    x = drawXtermText(&recur,
4418			      gc, x + adj, y,
4419			      text++, 1) - adj;
4420	}
4421
4422	TRACE(("DrewText [%4d,%4d] @%d\n", y, x, __LINE__));
4423	return x;
4424    }
4425#if OPT_BOX_CHARS
4426    /*
4427     * Draw some substitutions, if needed.  The font may not include the
4428     * line-drawing set, or it may be incomplete (in which case we'll draw an
4429     * empty space via xtermDrawBoxChar), or we may be told to force our
4430     * line-drawing.
4431     *
4432     * The empty space is a special case which can be overridden with the
4433     * showMissingGlyphs resource to produce an outline.  Not all fonts in
4434     * "modern" (sic) X provide an empty space; some use a thick outline or
4435     * something like the replacement character.  If you would rather not see
4436     * that, you can set assumeAllChars.
4437     */
4438    if (!IsIcon(screen)
4439	&& !(recur.draw_flags & NOTRANSLATION)
4440	&& (!screen->fnt_boxes
4441	    || (FontIsIncomplete(curFont) && !screen->assume_all_chars)
4442	    || recur.xw->work.force_wideFont
4443	    || screen->force_box_chars)) {
4444	/*
4445	 * Fill in missing box-characters.  Find regions without missing
4446	 * characters, and draw them calling ourselves recursively.  Draw
4447	 * missing characters via xtermDrawBoxChar().
4448	 */
4449	int last, first = 0;
4450	Bool drewBoxes = False;
4451
4452	for (last = 0; last < (int) len; last++) {
4453	    unsigned ch = (unsigned) text[last];
4454	    Bool isMissing;
4455	    int ch_width;
4456#if OPT_WIDE_CHARS
4457
4458	    if (ch == HIDDEN_CHAR) {
4459		if (last > first) {
4460		    XTermDraw param2 = recur;
4461		    param2.draw_flags |= NOTRANSLATION;
4462		    x = drawXtermText(&param2,
4463				      gc,
4464				      x, y,
4465				      text + first,
4466				      (unsigned) (last - first));
4467		}
4468		first = last + 1;
4469		drewBoxes = True;
4470		continue;
4471	    }
4472	    ch_width = CharWidth(screen, ch);
4473	    isMissing =
4474		IsXtermMissingChar(screen, ch,
4475				   ((recur.on_wide || ch_width > 1)
4476				    && okFont(NormalWFont(screen)))
4477				   ? WhichVFontData(screen, fWide)
4478				   : curFont);
4479#else
4480	    isMissing = IsXtermMissingChar(screen, ch, curFont);
4481	    ch_width = 1;
4482#endif
4483	    /*
4484	     * If the character is not missing, but we're in wide-character
4485	     * mode and the character happens to be a wide-character that
4486	     * corresponds to the line-drawing set, allow the forceBoxChars
4487	     * resource (or menu entry) to force it to display using our
4488	     * tables.
4489	     */
4490	    if_OPT_WIDE_CHARS(screen, {
4491		if (!isMissing
4492		    && TScreenOf(recur.xw)->force_box_chars) {
4493		    if (ch > 255
4494			&& ucs2dec(screen, ch) < 32) {
4495			ch = ucs2dec(screen, ch);
4496			isMissing = True;
4497		    } else if (ch < 32) {
4498			isMissing = True;
4499		    }
4500		}
4501	    });
4502
4503	    if (isMissing) {
4504		if (last > first) {
4505		    XTermDraw param2 = recur;
4506		    param2.draw_flags |= NOTRANSLATION;
4507		    x = drawXtermText(&recur,
4508				      gc,
4509				      x, y,
4510				      text + first,
4511				      (unsigned) (last - first));
4512		}
4513#if OPT_WIDE_CHARS
4514		if (ch_width <= 0 && ch < 32)
4515		    ch_width = 1;	/* special case for line-drawing */
4516		else if (ch_width < 0)
4517		    ch_width = 1;	/* special case for combining char */
4518		if (!ucs_workaround(&recur, ch, gc, x, y)) {
4519		    xtermDrawBoxChar(&recur, ch, gc, x, y, ch_width, False);
4520		}
4521#else
4522		xtermDrawBoxChar(&recur, ch, gc, x, y, ch_width, False);
4523#endif
4524		x += (ch_width * FontWidth(screen));
4525		first = last + 1;
4526		drewBoxes = True;
4527	    } else if (ch_width == 2
4528		       && recur.xw->work.force_wideFont
4529		       && !(recur.draw_flags & NOTRANSLATION)) {
4530		XTermDraw param2 = recur;
4531		param2.draw_flags |= NOTRANSLATION;
4532		/*
4533		 * Not a "box" character, but a special case treated similarly.
4534		 */
4535		(void) drawXtermText(&param2,
4536				     gc,
4537				     x, y,
4538				     text + first,
4539				     (unsigned) (1 + last - first));
4540		x += (ch_width * FontWidth(screen));
4541		first = last + 1;
4542		drewBoxes = True;
4543	    }
4544	}
4545	if (last <= first) {
4546	    TRACE(("DrewText [%4d,%4d] @%d\n", y, x, __LINE__));
4547	    return x;
4548	}
4549	text += first;
4550	len = (Cardinal) (last - first);
4551	recur.draw_flags |= NOTRANSLATION;
4552	if (drewBoxes) {
4553	    x = drawXtermText(&recur,
4554			      gc,
4555			      x,
4556			      y,
4557			      text,
4558			      len);
4559	    TRACE(("DrewText [%4d,%4d] @%d\n", y, x, __LINE__));
4560	    return x;
4561	}
4562    }
4563#endif /* OPT_BOX_CHARS */
4564    /*
4565     * Behave as if the font has (maybe Unicode-replacements for) drawing
4566     * characters in the range 1-31 (either we were not asked to ignore them,
4567     * or the caller made sure that there is none).
4568     */
4569#if OPT_WIDE_ATTRS
4570#define AttrFlags() recur.attr_flags
4571#define DrawFlags() recur.draw_flags
4572#else
4573#define AttrFlags() (recur.attr_flags & DRAWX_MASK)
4574#define DrawFlags() (recur.draw_flags & (unsigned)~DRAWX_MASK)
4575#endif
4576    TRACE(("drawtext%c[%4d,%4d] {%#x,%#x} (%d) %d:%s\n",
4577	   screen->cursor_state == OFF ? ' ' : '*',
4578	   y, x,
4579	   AttrFlags(),
4580	   DrawFlags(),
4581	   recur.this_chrset, len,
4582	   visibleIChars(text, len)));
4583    if (screen->scale_height != 1.0f) {
4584	xtermFillCells(&recur, gc, x, y, (Cardinal) len);
4585    }
4586    y += FontAscent(screen);
4587
4588#if OPT_WIDE_CHARS
4589
4590    need_wide = False;
4591    if (screen->wide_chars || screen->unicode_font) {
4592	int src;
4593	for (src = 0; src < (int) len; src++) {
4594	    IChar ch = text[src];
4595	    if (ch > 0xff) {
4596		need_wide = True;
4597		break;
4598	    }
4599	}
4600    }
4601
4602    if (need_wide) {
4603	IChar *mapped;
4604	Bool needWide = False;
4605	int src, dst;
4606	Bool useBoldFont;
4607	int ascent_adjust = 0;
4608
4609	BumpTypedBuffer(XChar2b, len);
4610	BumpTypedBuffer(Char, len);
4611	BumpTypedBuffer(IChar, len);
4612	mapped = BfBuf(IChar);
4613
4614	/* transform characters first, to decide how to draw them */
4615	for (src = 0; src < (int) len; src++) {
4616	    IChar ch = text[src];
4617	    if (ch != HIDDEN_CHAR) {
4618#if OPT_BOX_CHARS
4619		if ((screen->fnt_boxes == 1) && (ch >= 256)) {
4620		    unsigned part = ucs2dec(screen, ch);
4621		    if (part < 32)
4622			ch = (IChar) part;
4623		}
4624#endif
4625#if OPT_MINI_LUIT
4626#define Map2Sbuf(from,to) (ch == from) { ch = to; }
4627		if (screen->latin9_mode && !screen->utf8_mode && ch < 256) {
4628
4629		    /* see http://www.cs.tut.fi/~jkorpela/latin9.html */
4630		    /* *INDENT-OFF* */
4631		    if Map2Sbuf(0xa4, 0x20ac)
4632		    else if Map2Sbuf(0xa6, 0x0160)
4633		    else if Map2Sbuf(0xa8, 0x0161)
4634		    else if Map2Sbuf(0xb4, 0x017d)
4635		    else if Map2Sbuf(0xb8, 0x017e)
4636		    else if Map2Sbuf(0xbc, 0x0152)
4637		    else if Map2Sbuf(0xbd, 0x0153)
4638		    else if Map2Sbuf(0xbe, 0x0178)
4639		    /* *INDENT-ON* */
4640
4641		}
4642		if (screen->unicode_font
4643		    && (ch == ANSI_DEL ||
4644			ch < ANSI_SPA)) {
4645		    ch = (IChar) dec2ucs(screen,
4646					 (unsigned) ((ch == ANSI_DEL) ? 0 : ch));
4647		}
4648#endif /* OPT_MINI_LUIT */
4649	    }
4650	    mapped[src] = ch;
4651	}
4652
4653	for (src = dst = 0; src < (int) len; src++) {
4654	    IChar ch = mapped[src];
4655
4656	    if (ch != HIDDEN_CHAR) {
4657		int ch_width = CharWidth(screen, ch);
4658		if (!needWide
4659		    && !IsIcon(screen)
4660		    && ((recur.on_wide || ch_width > 1)
4661			&& okFont(NormalWFont(screen)))) {
4662		    needWide = True;
4663		}
4664		++dst;
4665	    }
4666	}
4667
4668	/*
4669	 * Check for special case where the bold font lacks glyphs found in the
4670	 * normal font, and drop down to normal fonts with overstriking to help
4671	 * show the actual characters.
4672	 */
4673	useBoldFont = ((recur.attr_flags & BOLDATTR(screen)) != 0);
4674	if (useBoldFont) {
4675	    XTermFonts *norm = 0;
4676	    XTermFonts *bold = 0;
4677	    Bool noBold, noNorm;
4678
4679	    (void) norm;
4680	    if (needWide && okFont(BoldWFont(screen))) {
4681		norm = WhichVFontData(screen, fWide);
4682		bold = WhichVFontData(screen, fWBold);
4683	    } else if (okFont(BoldFont(screen))) {
4684		norm = WhichVFontData(screen, fNorm);
4685		bold = WhichVFontData(screen, fBold);
4686	    } else {
4687		useBoldFont = False;
4688	    }
4689
4690	    if (useBoldFont && FontIsIncomplete(bold)) {
4691		for (src = 0; src < (int) len; src++) {
4692		    IChar ch = mapped[src];
4693
4694		    if (ch == HIDDEN_CHAR)
4695			continue;
4696
4697		    noBold = IsXtermMissingChar(screen, ch, bold);
4698		    if (noBold) {
4699			noNorm = IsXtermMissingChar(screen, ch, norm);
4700			if (!noNorm) {
4701			    useBoldFont = False;
4702			    break;
4703			}
4704		    }
4705		}
4706	    }
4707	}
4708
4709	/* FIXME This is probably wrong. But it works. */
4710	underline_len = len;
4711
4712	/* Set the drawing font */
4713	if (!(recur.draw_flags & (DOUBLEHFONT | DOUBLEWFONT))) {
4714	    VTwin *currentWin = WhichVWin(screen);
4715	    VTFontEnum fntId;
4716	    CgsEnum cgsId;
4717	    Pixel fg = getCgsFore(recur.xw, currentWin, gc);
4718	    Pixel bg = getCgsBack(recur.xw, currentWin, gc);
4719
4720	    if (needWide
4721		&& useBoldFont
4722		&& okFont(BoldWFont(screen))) {
4723		fntId = fWBold;
4724		cgsId = gcWBold;
4725	    } else if (needWide) {
4726		fntId = fWide;
4727		cgsId = gcWide;
4728	    } else if (useBoldFont) {
4729		fntId = fBold;
4730		cgsId = gcBold;
4731	    } else {
4732		fntId = fNorm;
4733		cgsId = gcNorm;
4734	    }
4735
4736	    setCgsFore(recur.xw, currentWin, cgsId, fg);
4737	    setCgsBack(recur.xw, currentWin, cgsId, bg);
4738	    gc = getCgsGC(recur.xw, currentWin, cgsId);
4739
4740#if OPT_WIDE_ATTRS
4741#if OPT_DEC_CHRSET
4742	    if (!(CSET_DOUBLE(recur.this_chrset) || (recur.draw_flags & DOUBLEWFONT)))
4743#endif
4744	    {
4745		need_clipping = fixupItalics(&recur,
4746					     gc,
4747					     getCgsFont(recur.xw,
4748							currentWin, gc),
4749					     y, x, font_width, len);
4750	    }
4751#endif
4752	    if (fntId != fNorm) {
4753		XFontStruct *thisFp = WhichVFont(screen, fntId);
4754		ascent_adjust = (thisFp->ascent
4755				 - NormalFont(screen)->ascent);
4756		if (thisFp->max_bounds.width ==
4757		    NormalFont(screen)->max_bounds.width * 2) {
4758		    underline_len = real_length = (Cardinal) (dst * 2);
4759		} else if (cgsId == gcWide || cgsId == gcWBold) {
4760		    underline_len = real_length = (Cardinal) (dst * 2);
4761		    xtermFillCells(&recur,
4762				   gc,
4763				   x,
4764				   y - thisFp->ascent,
4765				   real_length);
4766		}
4767	    }
4768	}
4769
4770	xtermFullString16(recur.xw, recur.draw_flags, gc,
4771			  x, y + y_shift + ascent_adjust, dst);
4772#if OPT_WIDE_ATTRS
4773	if (need_clipping) {
4774	    endClipping(screen, gc);
4775	}
4776#endif
4777
4778	if ((recur.attr_flags & BOLDATTR(screen)) && (screen->enbolden || !useBoldFont)) {
4779	    if (!(recur.draw_flags & (DOUBLEWFONT | DOUBLEHFONT))) {
4780		beginClipping(screen, gc, (Cardinal) font_width, len);
4781	    }
4782	    xtermFullString16(recur.xw, recur.draw_flags | NOBACKGROUND,
4783			      gc, x + 1, y + y_shift + ascent_adjust, dst);
4784	    if (!(recur.draw_flags & (DOUBLEWFONT | DOUBLEHFONT))) {
4785		endClipping(screen, gc);
4786	    }
4787	}
4788
4789    } else
4790#endif /* OPT_WIDE_CHARS */
4791    {
4792	IChar *mapped;
4793	Cardinal dst;
4794
4795	BumpTypedBuffer(IChar, len);
4796	BumpTypedBuffer(Char, len);
4797
4798	mapped = BfBuf(IChar);
4799	for (dst = 0; dst < len; ++dst)
4800	    mapped[dst] = text[dst];
4801
4802#if OPT_WIDE_ATTRS
4803#if OPT_DEC_CHRSET
4804	if (!(CSET_DOUBLE(recur.this_chrset) || (recur.draw_flags & DOUBLEWFONT)))
4805#endif
4806	{
4807	    need_clipping = fixupItalics(&recur, gc, curFont,
4808					 y, x, font_width, len);
4809	}
4810#endif
4811
4812	xtermDrawString(screen, recur.draw_flags, gc,
4813			x, y + y_shift, (int) len);
4814
4815#if OPT_WIDE_ATTRS
4816	if (need_clipping) {
4817	    endClipping(screen, gc);
4818	}
4819#endif
4820	underline_len = len;
4821	if ((recur.attr_flags & BOLDATTR(screen)) && screen->enbolden) {
4822	    if (!(recur.draw_flags & (DOUBLEWFONT | DOUBLEHFONT))) {
4823		beginClipping(screen, gc, font_width, (int) len);
4824	    }
4825	    xtermDrawString(screen, (recur.draw_flags | NOBACKGROUND),
4826			    gc, x + 1, y + y_shift, (int) len);
4827	    if (!(recur.draw_flags & (DOUBLEWFONT | DOUBLEHFONT))) {
4828		endClipping(screen, gc);
4829	    }
4830	}
4831    }
4832
4833    drawUnderline(recur.xw,
4834		  gc,
4835		  recur.attr_flags,
4836		  underline_len,
4837		  font_width,
4838		  x,
4839		  y + y_shift,
4840		  did_ul);
4841
4842    x += ((int) real_length) * FontWidth(screen);
4843    TRACE(("DrewText [%4d,%4d] @%d\n", y, x, __LINE__));
4844    return x;
4845}
4846
4847#if OPT_WIDE_CHARS
4848/*
4849 * Allocate buffer - workaround for wide-character interfaces.
4850 */
4851void
4852allocXtermChars(ScrnPtr *buffer, Cardinal length)
4853{
4854    if (*buffer == 0) {
4855	*buffer = (ScrnPtr) XtMalloc(length);
4856    } else {
4857	*buffer = (ScrnPtr) XtRealloc((char *) *buffer, length);
4858    }
4859}
4860#endif
4861
4862/* set up size hints for window manager; min 1 char by 1 char */
4863void
4864xtermSizeHints(XtermWidget xw, int scrollbarWidth)
4865{
4866    TScreen *screen = TScreenOf(xw);
4867
4868    TRACE(("xtermSizeHints\n"));
4869    TRACE(("   border    %d\n", xw->core.border_width));
4870    TRACE(("   scrollbar %d\n", scrollbarWidth));
4871
4872    xw->hints.base_width = 2 * screen->border + scrollbarWidth;
4873    xw->hints.base_height = 2 * screen->border;
4874
4875#if OPT_TOOLBAR
4876    TRACE(("   toolbar   %d\n", ToolbarHeight(xw)));
4877
4878    xw->hints.base_height += ToolbarHeight(xw);
4879    xw->hints.base_height += BorderWidth(xw) * 2;
4880    xw->hints.base_width += BorderWidth(xw) * 2;
4881#endif
4882
4883    if (xw->misc.resizeByPixel) {
4884	xw->hints.width_inc = 1;
4885	xw->hints.height_inc = 1;
4886    } else {
4887	xw->hints.width_inc = FontWidth(screen);
4888	xw->hints.height_inc = FontHeight(screen);
4889    }
4890    xw->hints.min_width = xw->hints.base_width + xw->hints.width_inc;
4891    xw->hints.min_height = xw->hints.base_height + xw->hints.height_inc;
4892
4893    xw->hints.width = MaxCols(screen) * FontWidth(screen) + xw->hints.min_width;
4894    xw->hints.height = MaxRows(screen) * FontHeight(screen) + xw->hints.min_height;
4895
4896    xw->hints.flags |= (PSize | PBaseSize | PMinSize | PResizeInc);
4897
4898    TRACE_HINTS(&(xw->hints));
4899}
4900
4901void
4902getXtermSizeHints(XtermWidget xw)
4903{
4904    TScreen *screen = TScreenOf(xw);
4905    long supp;
4906
4907    if (!XGetWMNormalHints(screen->display, VShellWindow(xw),
4908			   &xw->hints, &supp))
4909	memset(&xw->hints, 0, sizeof(xw->hints));
4910    TRACE_HINTS(&(xw->hints));
4911}
4912
4913CgsEnum
4914whichXtermCgs(XtermWidget xw, unsigned attr_flags, Bool hilite)
4915{
4916    TScreen *screen = TScreenOf(xw);
4917    CgsEnum cgsId = gcMAX;
4918
4919    if (ReverseOrHilite(screen, attr_flags, hilite)) {
4920	if (attr_flags & BOLDATTR(screen)) {
4921	    cgsId = gcBoldReverse;
4922	} else {
4923	    cgsId = gcNormReverse;
4924	}
4925    } else {
4926	if (attr_flags & BOLDATTR(screen)) {
4927	    cgsId = gcBold;
4928	} else {
4929	    cgsId = gcNorm;
4930	}
4931    }
4932    return cgsId;
4933}
4934
4935/*
4936 * Returns a GC, selected according to the font (reverse/bold/normal) that is
4937 * required for the current position (implied).  The GC is updated with the
4938 * current screen foreground and background colors.
4939 */
4940GC
4941updatedXtermGC(XtermWidget xw, unsigned attr_flags, CellColor fg_bg,
4942	       Bool hilite)
4943{
4944    TScreen *screen = TScreenOf(xw);
4945    VTwin *win = WhichVWin(screen);
4946    CgsEnum cgsId = whichXtermCgs(xw, attr_flags, hilite);
4947    Pixel my_fg = extract_fg(xw, fg_bg, attr_flags);
4948    Pixel my_bg = extract_bg(xw, fg_bg, attr_flags);
4949    Pixel fg_pix = getXtermFG(xw, attr_flags, (int) my_fg);
4950    Pixel bg_pix = getXtermBG(xw, attr_flags, (int) my_bg);
4951    Pixel xx_pix;
4952#if OPT_HIGHLIGHT_COLOR
4953    Boolean reverse2 = ((attr_flags & INVERSE) && hilite);
4954    Pixel selbg_pix = T_COLOR(screen, HIGHLIGHT_BG);
4955    Pixel selfg_pix = T_COLOR(screen, HIGHLIGHT_FG);
4956    Boolean always = screen->hilite_color;
4957    Boolean use_selbg = (Boolean) (always &&
4958				   isNotForeground(xw, fg_pix, bg_pix, selbg_pix));
4959    Boolean use_selfg = (Boolean) (always &&
4960				   isNotBackground(xw, fg_pix, bg_pix, selfg_pix));
4961#endif
4962
4963    (void) fg_bg;
4964    (void) my_bg;
4965    (void) my_fg;
4966
4967    /*
4968     * Discard video attributes overridden by colorXXXMode's.
4969     */
4970    checkVeryBoldColors(attr_flags, my_fg);
4971
4972    if (ReverseOrHilite(screen, attr_flags, hilite)) {
4973#if OPT_HIGHLIGHT_COLOR
4974	if (!screen->hilite_color) {
4975	    if (selbg_pix != T_COLOR(screen, TEXT_FG)
4976		&& selbg_pix != fg_pix
4977		&& selbg_pix != bg_pix
4978		&& selbg_pix != xw->dft_foreground) {
4979		bg_pix = fg_pix;
4980		fg_pix = selbg_pix;
4981	    }
4982	}
4983#endif
4984	EXCHANGE(fg_pix, bg_pix, xx_pix);
4985#if OPT_HIGHLIGHT_COLOR
4986	if (screen->hilite_color) {
4987	    if (screen->hilite_reverse) {
4988		if (use_selbg) {
4989		    if (use_selfg) {
4990			bg_pix = fg_pix;
4991		    } else {
4992			fg_pix = bg_pix;
4993			bg_pix = selbg_pix;
4994		    }
4995		}
4996		if (use_selfg)
4997		    fg_pix = selfg_pix;
4998	    }
4999	}
5000#endif
5001    } else if ((attr_flags & INVERSE) && hilite) {
5002#if OPT_HIGHLIGHT_COLOR
5003	if (!screen->hilite_color) {
5004	    if (selbg_pix != T_COLOR(screen, TEXT_FG)
5005		&& selbg_pix != fg_pix
5006		&& selbg_pix != bg_pix
5007		&& selbg_pix != xw->dft_foreground) {
5008		bg_pix = fg_pix;
5009		fg_pix = selbg_pix;
5010	    }
5011	}
5012#endif
5013	/* double-reverse... EXCHANGE(fg_pix, bg_pix, xx_pix); */
5014#if OPT_HIGHLIGHT_COLOR
5015	if (screen->hilite_color) {
5016	    if (screen->hilite_reverse) {
5017		if (use_selbg) {
5018		    if (use_selfg ^ reverse2) {
5019			bg_pix = fg_pix;
5020		    } else {
5021			fg_pix = bg_pix;
5022		    }
5023		    if (reverse2) {
5024			fg_pix = selbg_pix;
5025		    } else {
5026			bg_pix = selbg_pix;
5027		    }
5028		}
5029		if (use_selfg) {
5030		    if (reverse2) {
5031			bg_pix = selfg_pix;
5032		    } else {
5033			fg_pix = selfg_pix;
5034		    }
5035		}
5036	    }
5037	}
5038#endif
5039    }
5040#if OPT_HIGHLIGHT_COLOR
5041    if (!screen->hilite_color || !screen->hilite_reverse) {
5042	if (hilite && !screen->hilite_reverse) {
5043	    if (use_selbg) {
5044		if (reverse2)
5045		    fg_pix = selbg_pix;
5046		else
5047		    bg_pix = selbg_pix;
5048	    }
5049	    if (use_selfg) {
5050		if (reverse2)
5051		    bg_pix = selfg_pix;
5052		else
5053		    fg_pix = selfg_pix;
5054	    }
5055	}
5056    }
5057#endif
5058
5059#if OPT_BLINK_TEXT
5060    if ((screen->blink_state == ON) &&
5061	(!screen->blink_as_bold) &&
5062	(attr_flags & BLINK)) {
5063	fg_pix = bg_pix;
5064    }
5065#endif
5066
5067    setCgsFore(xw, win, cgsId, fg_pix);
5068    setCgsBack(xw, win, cgsId, bg_pix);
5069    return getCgsGC(xw, win, cgsId);
5070}
5071
5072/*
5073 * Resets the foreground/background of the GC returned by 'updatedXtermGC()'
5074 * to the values that would be set in SGR_Foreground and SGR_Background. This
5075 * duplicates some logic, but only modifies 1/4 as many GC's.
5076 */
5077void
5078resetXtermGC(XtermWidget xw, unsigned attr_flags, Bool hilite)
5079{
5080    TScreen *screen = TScreenOf(xw);
5081    VTwin *win = WhichVWin(screen);
5082    CgsEnum cgsId = whichXtermCgs(xw, attr_flags, hilite);
5083    Pixel fg_pix = getXtermFG(xw, attr_flags, xw->cur_foreground);
5084    Pixel bg_pix = getXtermBG(xw, attr_flags, xw->cur_background);
5085
5086    checkVeryBoldColors(attr_flags, xw->cur_foreground);
5087
5088    if (ReverseOrHilite(screen, attr_flags, hilite)) {
5089	setCgsFore(xw, win, cgsId, bg_pix);
5090	setCgsBack(xw, win, cgsId, fg_pix);
5091    } else {
5092	setCgsFore(xw, win, cgsId, fg_pix);
5093	setCgsBack(xw, win, cgsId, bg_pix);
5094    }
5095}
5096
5097#if OPT_ISO_COLORS
5098/*
5099 * Extract the foreground-color index from a color pair.
5100 * If we've got BOLD or UNDERLINE color-mode active, those will be used.
5101 */
5102Pixel
5103extract_fg(XtermWidget xw, CellColor color, unsigned attr_flags)
5104{
5105    unsigned fg = ExtractForeground(color);
5106
5107    if (TScreenOf(xw)->colorAttrMode
5108	|| (fg == ExtractBackground(color))) {
5109	fg = MapToColorMode(fg, TScreenOf(xw), attr_flags);
5110    }
5111    return fg;
5112}
5113
5114/*
5115 * Extract the background-color index from a color pair.
5116 * If we've got INVERSE color-mode active, that will be used.
5117 */
5118Pixel
5119extract_bg(XtermWidget xw, CellColor color, unsigned attr_flags)
5120{
5121    unsigned bg = ExtractBackground(color);
5122
5123    if (TScreenOf(xw)->colorAttrMode
5124	|| (bg == ExtractForeground(color))) {
5125	if (TScreenOf(xw)->colorRVMode && (attr_flags & INVERSE))
5126	    bg = COLOR_RV;
5127    }
5128    return bg;
5129}
5130
5131/*
5132 * Combine the current foreground and background into a single 8-bit number.
5133 * Note that we're storing the SGR foreground, since cur_foreground may be set
5134 * to COLOR_UL, COLOR_BD or COLOR_BL, which would make the code larger than 8
5135 * bits.
5136 *
5137 * This assumes that fg/bg are equal when we override with one of the special
5138 * attribute colors.
5139 */
5140CellColor
5141makeColorPair(XtermWidget xw)
5142{
5143    CellColor result;
5144
5145#if OPT_DIRECT_COLOR
5146    result.fg = xw->cur_foreground;
5147    result.bg = xw->cur_background;
5148#else
5149    int fg = xw->cur_foreground;
5150    int bg = xw->cur_background;
5151    unsigned my_bg = okIndexedColor(bg) ? (unsigned) bg : 0;
5152    unsigned my_fg = okIndexedColor(fg) ? (unsigned) fg : my_bg;
5153
5154    result = (CellColor) (my_fg | (my_bg << COLOR_BITS));
5155#endif
5156
5157    return result;
5158}
5159
5160/*
5161 * Using the "current" SGR background, clear a rectangle.
5162 */
5163void
5164ClearCurBackground(XtermWidget xw,
5165		   int top,
5166		   int left,
5167		   unsigned height,
5168		   unsigned width,
5169		   unsigned fw)
5170{
5171    TScreen *screen = TScreenOf(xw);
5172    int actual_rows = PlusStatusLine(screen, screen->max_row + 1);
5173    Boolean visible = (((int) width > 0)
5174		       && ((left + (int) width) <= screen->max_col + 1)
5175		       && (((int) height + top) <= actual_rows));
5176
5177    TRACE(("ClearCurBackground %d,%d %dx%d%s with %d %s\n",
5178	   top, left, height, width,
5179	   IsStatusShown(screen) ? "*" : "",
5180	   xw->cur_background,
5181	   visible ? "(ok)" : "(err)"));
5182
5183    if (VWindow(screen) && visible) {
5184	set_background(xw, xw->cur_background);
5185
5186	xtermClear2(xw,
5187		    CursorX2(screen, left, fw),
5188		    CursorY2(screen, top),
5189		    (width * fw),
5190		    (height * (unsigned) FontHeight(screen)));
5191
5192	set_background(xw, -1);
5193    }
5194}
5195#endif /* OPT_ISO_COLORS */
5196
5197Pixel
5198getXtermBackground(XtermWidget xw, unsigned attr_flags, int color)
5199{
5200    Pixel result = T_COLOR(TScreenOf(xw), TEXT_BG);
5201
5202#if OPT_ISO_COLORS
5203    if (color >= 0) {
5204	if_OPT_DIRECT_COLOR2_else(TScreenOf(xw), (attr_flags & ATR_DIRECT_BG), {
5205	    result = (Pixel) color;
5206	}) if ((attr_flags & BG_COLOR) && (color < MAXCOLORS)) {
5207	    result = GET_COLOR_RES(xw, TScreenOf(xw)->Acolors[color]);
5208	}
5209    }
5210#else
5211    (void) attr_flags;
5212    (void) color;
5213#endif
5214    return result;
5215}
5216
5217#if OPT_ISO_COLORS && OPT_WIDE_ATTRS
5218#if OPT_SGR2_HASH
5219typedef struct _DimColorHT {
5220    Pixel org;
5221    Pixel dim;
5222} DimColorHT;
5223
5224static unsigned
5225jhash1(unsigned char *key, size_t len)
5226{
5227    unsigned hash;
5228    size_t i;
5229
5230    for (hash = 0, i = 0; i < len; ++i) {
5231	hash += key[i];
5232	hash += (hash << 10);
5233	hash ^= (hash >> 6);
5234    }
5235    hash += (hash << 3);
5236    hash ^= (hash >> 11);
5237    hash += (hash << 15);
5238    return hash;
5239}
5240
5241static unsigned
5242computeFaint(XtermWidget xw, unsigned value, unsigned compare)
5243{
5244    TScreen *screen = TScreenOf(xw);
5245    if (screen->faint_relative) {
5246	value = (unsigned) ((value + compare) / 2);
5247    } else {
5248	value = (unsigned) ((2 * value) / 3);
5249    }
5250    return value;
5251}
5252#endif /* OPT_SGR2_HASH */
5253#endif /* OPT_ISO_COLORS && OPT_WIDE_ATTRS */
5254
5255Pixel
5256getXtermForeground(XtermWidget xw, unsigned attr_flags, int color)
5257{
5258    Pixel result = T_COLOR(TScreenOf(xw), TEXT_FG);
5259
5260#if OPT_ISO_COLORS
5261    if_OPT_DIRECT_COLOR2_else(TScreenOf(xw), (attr_flags & ATR_DIRECT_FG), {
5262	result = (Pixel) color;
5263    })
5264	if ((attr_flags & FG_COLOR) &&
5265	    (color >= 0 && color < MAXCOLORS)) {
5266	result = GET_COLOR_RES(xw, TScreenOf(xw)->Acolors[color]);
5267    }
5268#else
5269    (void) attr_flags;
5270    (void) color;
5271#endif
5272
5273#if OPT_ISO_COLORS && OPT_WIDE_ATTRS
5274    if ((attr_flags & ATR_FAINT)) {
5275#if OPT_SGR2_HASH
5276#define DIM_IT(n) work.n = (unsigned short) computeFaint(xw, work.n, bkg.n)
5277#define SizeOfHT ((unsigned) sizeof(unsigned long) * CHAR_BIT)
5278	static DimColorHT ht[SizeOfHT];
5279	Pixel bg = T_COLOR(TScreenOf(xw), TEXT_BG);
5280	XColor work;
5281	Pixel p;
5282
5283	if ((color >= 0)
5284	    || (result != (Pixel) color)) {
5285	    static unsigned long have = 0;
5286	    static Boolean have_bg = False;
5287	    static XColor bkg;
5288
5289	    /* cache bkg color in r/g/b */
5290	    if (!have_bg || bg != bkg.pixel) {
5291		bkg.pixel = bg;
5292		have_bg = QueryOneColor(xw, &bkg);
5293		have = 0;	/* invalidate color cache */
5294	    }
5295	    if (have_bg) {
5296		unsigned hv;
5297		hv = jhash1((unsigned char *) &result, sizeof(result));
5298		hv %= SizeOfHT;
5299
5300		if ((have & (1UL << hv))
5301		    && ht[hv].org == result) {
5302		    result = ht[hv].dim;	/* return cached color */
5303		} else {
5304		    work.pixel = result;
5305		    if (QueryOneColor(xw, &work)) {
5306			DIM_IT(red);
5307			DIM_IT(green);
5308			DIM_IT(blue);
5309			p = result;
5310			if (allocateBestRGB(xw, &work)) {
5311			    result = work.pixel;
5312			}
5313
5314			/* cache the result */
5315			have |= (1UL << hv);
5316			ht[hv].org = p;
5317			ht[hv].dim = result;
5318		    }
5319		}
5320	    }
5321	}
5322#else /* !OPT_SGR2_HASH */
5323#define DIM_IT(n) work.n = (unsigned short) ((2 * (unsigned)work.n) / 3)
5324	static Pixel last_in;
5325	static Pixel last_out;
5326	if ((result != last_in)
5327	    && ((color >= 0)
5328		|| (result != (Pixel) color))) {
5329	    XColor work;
5330	    last_in = result;
5331	    work.pixel = result;
5332	    if (QueryOneColor(xw, &work)) {
5333		DIM_IT(red);
5334		DIM_IT(green);
5335		DIM_IT(blue);
5336		if (allocateBestRGB(xw, &work)) {
5337		    result = work.pixel;
5338		}
5339	    }
5340	    last_out = result;
5341	} else {
5342	    result = last_out;
5343	}
5344#endif /* OPT_SGR2_HASH */
5345    }
5346#endif
5347    return result;
5348}
5349
5350/*
5351 * Returns a single base character for the given cell.
5352 */
5353unsigned
5354getXtermCell(TScreen *screen, int row, int col)
5355{
5356    CLineData *ld = getLineData(screen, row);
5357
5358    return ((ld && (col < (int) ld->lineSize))
5359	    ? ld->charData[col]
5360	    : (unsigned) ' ');
5361}
5362
5363/*
5364 * Sets a single base character for the given cell.
5365 */
5366void
5367putXtermCell(TScreen *screen, int row, int col, int ch)
5368{
5369    LineData *ld = getLineData(screen, row);
5370
5371    if (ld && (col < (int) ld->lineSize)) {
5372	ld->charData[col] = (CharData) ch;
5373	if_OPT_WIDE_CHARS(screen, {
5374	    size_t off;
5375	    for_each_combData(off, ld) {
5376		ld->combData[off][col] = 0;
5377	    }
5378	});
5379    }
5380}
5381
5382#if OPT_WIDE_CHARS
5383/*
5384 * Add a combining character for the given cell
5385 */
5386void
5387addXtermCombining(TScreen *screen, int row, int col, unsigned ch)
5388{
5389    if (ch != 0) {
5390	LineData *ld = getLineData(screen, row);
5391	size_t off;
5392
5393	TRACE(("addXtermCombining %d,%d U+%04X (%d)\n",
5394	       row, col, ch, CharWidth(screen, ch)));
5395
5396	for_each_combData(off, ld) {
5397	    if (!ld->combData[off][col]) {
5398		ld->combData[off][col] = (CharData) ch;
5399		break;
5400	    }
5401	}
5402    }
5403}
5404
5405unsigned
5406getXtermCombining(TScreen *screen, int row, int col, int off)
5407{
5408    CLineData *ld = getLineData(screen, row);
5409    return (ld->combSize ? ld->combData[off][col] : 0U);
5410}
5411#endif
5412
5413void
5414update_keyboard_type(void)
5415{
5416    update_delete_del();
5417    update_tcap_fkeys();
5418    update_old_fkeys();
5419    update_hp_fkeys();
5420    update_sco_fkeys();
5421    update_sun_fkeys();
5422    update_sun_kbd();
5423}
5424
5425void
5426set_keyboard_type(XtermWidget xw, xtermKeyboardType type, Bool set)
5427{
5428    xtermKeyboardType save = xw->keyboard.type;
5429
5430    TRACE(("set_keyboard_type(%s, %s) currently %s\n",
5431	   visibleKeyboardType(type),
5432	   BtoS(set),
5433	   visibleKeyboardType(xw->keyboard.type)));
5434    if (set) {
5435	xw->keyboard.type = type;
5436    } else {
5437	xw->keyboard.type = keyboardIsDefault;
5438    }
5439
5440    if (save != xw->keyboard.type) {
5441	update_keyboard_type();
5442    }
5443}
5444
5445void
5446toggle_keyboard_type(XtermWidget xw, xtermKeyboardType type)
5447{
5448    xtermKeyboardType save = xw->keyboard.type;
5449
5450    TRACE(("toggle_keyboard_type(%s) currently %s\n",
5451	   visibleKeyboardType(type),
5452	   visibleKeyboardType(xw->keyboard.type)));
5453    if (xw->keyboard.type == type) {
5454	xw->keyboard.type = keyboardIsDefault;
5455    } else {
5456	xw->keyboard.type = type;
5457    }
5458
5459    if (save != xw->keyboard.type) {
5460	update_keyboard_type();
5461    }
5462}
5463
5464const char *
5465visibleKeyboardType(xtermKeyboardType type)
5466{
5467    const char *result = "?";
5468    switch (type) {
5469	CASETYPE(keyboardIsLegacy);	/* bogus vt220 codes for F1-F4, etc. */
5470	CASETYPE(keyboardIsDefault);
5471	CASETYPE(keyboardIsHP);
5472	CASETYPE(keyboardIsSCO);
5473	CASETYPE(keyboardIsSun);
5474	CASETYPE(keyboardIsTermcap);
5475	CASETYPE(keyboardIsVT220);
5476    }
5477    return result;
5478}
5479
5480static void
5481init_keyboard_type(XtermWidget xw, xtermKeyboardType type, Bool set)
5482{
5483    TRACE(("init_keyboard_type(%s, %s) currently %s\n",
5484	   visibleKeyboardType(type),
5485	   BtoS(set),
5486	   visibleKeyboardType(xw->keyboard.type)));
5487    if (set) {
5488	/*
5489	 * Check for conflicts, e.g., if someone asked for both Sun and HP
5490	 * function keys.
5491	 */
5492	if (guard_keyboard_type) {
5493	    xtermWarning("Conflicting keyboard type option (%s/%s)\n",
5494			 visibleKeyboardType(xw->keyboard.type),
5495			 visibleKeyboardType(type));
5496	}
5497	xw->keyboard.type = type;
5498	guard_keyboard_type = True;
5499	update_keyboard_type();
5500    }
5501}
5502
5503/*
5504 * If the keyboardType resource is set, use that, overriding the individual
5505 * boolean resources for different keyboard types.
5506 */
5507void
5508decode_keyboard_type(XtermWidget xw, XTERM_RESOURCE * rp)
5509{
5510#define DATA(n, t, f) { n, t, XtOffsetOf(XTERM_RESOURCE, f) }
5511#define FLAG(n) *(Boolean *)(((char *)rp) + table[n].offset)
5512    static struct {
5513	const char *name;
5514	xtermKeyboardType type;
5515	unsigned offset;
5516    } table[] = {
5517	DATA(NAME_OLD_KT, keyboardIsLegacy, oldKeyboard),
5518#if OPT_HP_FUNC_KEYS
5519	    DATA(NAME_HP_KT, keyboardIsHP, hpFunctionKeys),
5520#endif
5521#if OPT_SCO_FUNC_KEYS
5522	    DATA(NAME_SCO_KT, keyboardIsSCO, scoFunctionKeys),
5523#endif
5524#if OPT_SUN_FUNC_KEYS
5525	    DATA(NAME_SUN_KT, keyboardIsSun, sunFunctionKeys),
5526#endif
5527#if OPT_SUNPC_KBD
5528	    DATA(NAME_VT220_KT, keyboardIsVT220, sunKeyboard),
5529#endif
5530#if OPT_TCAP_FKEYS
5531	    DATA(NAME_TCAP_KT, keyboardIsTermcap, termcapKeys),
5532#endif
5533    };
5534    Cardinal n;
5535    TScreen *screen = TScreenOf(xw);
5536
5537    TRACE(("decode_keyboard_type(%s)\n", rp->keyboardType));
5538    if (!x_strcasecmp(rp->keyboardType, "unknown")) {
5539	/*
5540	 * Let the individual resources comprise the keyboard-type.
5541	 */
5542	for (n = 0; n < XtNumber(table); ++n)
5543	    init_keyboard_type(xw, table[n].type, FLAG(n));
5544    } else if (!x_strcasecmp(rp->keyboardType, "default")) {
5545	/*
5546	 * Set the keyboard-type to the Sun/PC type, allowing modified
5547	 * function keys, etc.
5548	 */
5549	for (n = 0; n < XtNumber(table); ++n)
5550	    init_keyboard_type(xw, table[n].type, False);
5551    } else {
5552	Bool found = False;
5553
5554	/*
5555	 * Special case: oldXtermFKeys should have been like the others.
5556	 */
5557	if (!x_strcasecmp(rp->keyboardType, NAME_OLD_KT)) {
5558	    TRACE(("special case, setting oldXtermFKeys\n"));
5559	    screen->old_fkeys = True;
5560	    screen->old_fkeys0 = True;
5561	}
5562
5563	/*
5564	 * Choose an individual keyboard type.
5565	 */
5566	for (n = 0; n < XtNumber(table); ++n) {
5567	    if (!x_strcasecmp(rp->keyboardType, table[n].name + 1)) {
5568		FLAG(n) = True;
5569		found = True;
5570	    } else {
5571		FLAG(n) = False;
5572	    }
5573	    init_keyboard_type(xw, table[n].type, FLAG(n));
5574	}
5575	if (!found) {
5576	    xtermWarning("KeyboardType resource \"%s\" not found\n",
5577			 rp->keyboardType);
5578	}
5579    }
5580#undef DATA
5581#undef FLAG
5582}
5583
5584#if OPT_WIDE_CHARS
5585#if defined(HAVE_WCHAR_H) && defined(HAVE_WCWIDTH)
5586/*
5587 * If xterm is running in a UTF-8 locale, it is still possible to encounter
5588 * old runtime configurations which yield incomplete or inaccurate data.
5589 */
5590static Bool
5591systemWcwidthOk(int samplesize, int samplepass)
5592{
5593    wchar_t n;
5594    int oops = 0;
5595
5596    for (n = 21; n <= 25; ++n) {
5597	wchar_t code = (wchar_t) dec2ucs(NULL, (unsigned) n);
5598	int system_code = wcwidth(code);
5599	int intern_code = mk_wcwidth(code);
5600
5601	/*
5602	 * Solaris 10 wcwidth() returns "2" for all of the line-drawing (page
5603	 * 0x2500) and most of the geometric shapes (a few are excluded, just
5604	 * to make it more difficult to use).  Do a sanity check to avoid using
5605	 * it.
5606	 */
5607	if ((system_code < 0 && intern_code >= 1)
5608	    || (system_code >= 0 && intern_code != system_code)) {
5609	    TRACE(("systemWcwidthOk: broken system line-drawing wcwidth\n"));
5610	    oops += (samplepass + 1);
5611	    break;
5612	}
5613    }
5614
5615    for (n = 0; n < (wchar_t) samplesize; ++n) {
5616	int system_code = wcwidth(n);
5617	int intern_code = mk_wcwidth(n);
5618
5619	/*
5620	 * When this check was originally implemented, there were few if any
5621	 * libraries with full Unicode coverage.  Time passes, and it is
5622	 * possible to make a full comparison of the BMP.  There are some
5623	 * differences: mk_wcwidth() marks some codes as combining and some
5624	 * as single-width, differing from GNU libc.
5625	 */
5626	if ((system_code < 0 && intern_code >= 1)
5627	    || (system_code >= 0 && intern_code != system_code)) {
5628	    TRACE((".. width(U+%04X) = %d, expected %d\n",
5629		   (unsigned) n, system_code, intern_code));
5630	    if (++oops > samplepass)
5631		break;
5632	}
5633    }
5634    TRACE(("systemWcwidthOk: %d/%d mismatches, allowed %d\n",
5635	   oops, (int) n, samplepass));
5636    return (oops <= samplepass);
5637}
5638#endif /* HAVE_WCWIDTH */
5639
5640void
5641decode_wcwidth(XtermWidget xw)
5642{
5643    int mode = ((xw->misc.cjk_width ? 2 : 0)
5644		+ (xw->misc.mk_width ? 1 : 0)
5645		+ 1);
5646
5647    switch (mode) {
5648    default:
5649#if defined(HAVE_WCHAR_H) && defined(HAVE_WCWIDTH)
5650	if (xtermEnvUTF8() &&
5651	    systemWcwidthOk(xw->misc.mk_samplesize, xw->misc.mk_samplepass)) {
5652	    my_wcwidth = wcwidth;
5653	    TRACE(("using system wcwidth() function\n"));
5654	    break;
5655	}
5656#endif
5657	/* FALLTHRU */
5658    case 2:
5659	my_wcwidth = &mk_wcwidth;
5660	TRACE(("using MK wcwidth() function\n"));
5661	break;
5662    case 3:
5663	/* FALLTHRU */
5664    case 4:
5665	my_wcwidth = &mk_wcwidth_cjk;
5666	TRACE(("using MK-CJK wcwidth() function\n"));
5667	break;
5668    }
5669
5670    for (first_widechar = 128; first_widechar < 4500; ++first_widechar) {
5671	if (my_wcwidth((wchar_t) first_widechar) > 1) {
5672	    TRACE(("first_widechar %#x\n", first_widechar));
5673	    break;
5674	}
5675    }
5676}
5677#endif
5678
5679/*
5680 * Extend a (normally) boolean resource value by checking for additional values
5681 * which will be mapped into true/false.
5682 */
5683int
5684extendedBoolean(const char *value, const FlagList * table, Cardinal limit)
5685{
5686    int result = -1;
5687    long check;
5688    char *next;
5689    Cardinal n;
5690
5691    if ((x_strcasecmp(value, "true") == 0)
5692	|| (x_strcasecmp(value, "yes") == 0)
5693	|| (x_strcasecmp(value, "on") == 0)) {
5694	result = True;
5695    } else if ((x_strcasecmp(value, "false") == 0)
5696	       || (x_strcasecmp(value, "no") == 0)
5697	       || (x_strcasecmp(value, "off") == 0)) {
5698	result = False;
5699    } else if ((check = strtol(value, &next, 0)) >= 0 && FullS2L(value, next)) {
5700	if (check >= (long) limit)	/* i.e., past False=0, True=1 */
5701	    check = True;
5702	result = (int) check;
5703    } else {
5704	for (n = 0; n < limit - 2; ++n) {
5705	    if (table[n].name == NULL) {
5706		break;
5707	    } else if (x_strcasecmp(value, table[n].name) == 0) {
5708		result = table[n].code;
5709		break;
5710	    }
5711	}
5712    }
5713
5714    if (result < 0) {
5715	xtermWarning("Unrecognized keyword: %s\n", value);
5716	result = False;
5717    }
5718
5719    TRACE(("extendedBoolean(%s) = %d\n", value, result));
5720    return result;
5721}
5722
5723/*
5724 * Something like round() from math library, but round() is less widely-used
5725 * than xterm.  Also, there are no negative numbers to complicate this.
5726 */
5727int
5728dimRound(double value)
5729{
5730    int result = (int) value;
5731    if (result < value)
5732	++result;
5733    return result;
5734}
5735
5736/*
5737 * Find the geometry of the specified Xinerama screen
5738 */
5739static void
5740find_xinerama_screen(Display *display, int screen, struct Xinerama_geometry *ret)
5741{
5742#ifdef HAVE_X11_EXTENSIONS_XINERAMA_H
5743    XineramaScreenInfo *screens;
5744    int nb_screens;
5745
5746    if (screen == -1)		/* already inited */
5747	return;
5748    screens = XineramaQueryScreens(display, &nb_screens);
5749    if (screen >= nb_screens) {
5750	xtermWarning("Xinerama screen %d does not exist\n", screen);
5751	return;
5752    }
5753    if (screen == -2) {
5754	int ptr_x, ptr_y;
5755	int dummy_int, i;
5756	unsigned dummy_uint;
5757	Window dummy_win;
5758	if (nb_screens == 0)
5759	    return;
5760	XQueryPointer(display, DefaultRootWindow(display),
5761		      &dummy_win, &dummy_win,
5762		      &ptr_x, &ptr_y,
5763		      &dummy_int, &dummy_int, &dummy_uint);
5764	for (i = 0; i < nb_screens; i++) {
5765	    if ((ptr_x - screens[i].x_org) < screens[i].width &&
5766		(ptr_y - screens[i].y_org) < screens[i].height) {
5767		screen = i;
5768		break;
5769	    }
5770	}
5771	if (screen < 0) {
5772	    xtermWarning("Mouse not in any Xinerama screen, using 0\n");
5773	    screen = 0;
5774	}
5775    }
5776    ret->scr_x = screens[screen].x_org;
5777    ret->scr_y = screens[screen].y_org;
5778    ret->scr_w = screens[screen].width;
5779    ret->scr_h = screens[screen].height;
5780#else /* HAVE_X11_EXTENSIONS_XINERAMA_H */
5781    (void) display;
5782    (void) ret;
5783    if (screen > 0)
5784	xtermWarning("Xinerama support not enabled\n");
5785#endif /* HAVE_X11_EXTENSIONS_XINERAMA_H */
5786}
5787
5788/*
5789 * Parse the screen code after the @ in a geometry string.
5790 */
5791static void
5792parse_xinerama_screen(Display *display, const char *str, struct Xinerama_geometry *ret)
5793{
5794    int screen = -1;
5795    char *end;
5796
5797    if (*str == 'g') {
5798	screen = -1;
5799	str++;
5800    } else if (*str == 'c') {
5801	screen = -2;
5802	str++;
5803    } else {
5804	long s = strtol(str, &end, 0);
5805	if (FullS2L(str, end) && ((int) s >= 0)) {
5806	    screen = (int) s;
5807	    str = end;
5808	}
5809    }
5810    if (*str) {
5811	xtermWarning("invalid Xinerama specification '%s'\n", str);
5812	return;
5813    }
5814    if (screen == -1)		/* already done */
5815	return;
5816    find_xinerama_screen(display, screen, ret);
5817}
5818
5819/*
5820 * Parse a geometry string with extra Xinerama specification:
5821 * <w>x<h>+<x>+<y>@<screen>.
5822 */
5823int
5824XParseXineramaGeometry(Display *display, char *parsestring, struct Xinerama_geometry *ret)
5825{
5826    char *at, buf[128];
5827
5828    ret->scr_x = 0;
5829    ret->scr_y = 0;
5830    ret->scr_w = DisplayWidth(display, DefaultScreen(display));
5831    ret->scr_h = DisplayHeight(display, DefaultScreen(display));
5832    at = strchr(parsestring, '@');
5833    if (at != NULL && (size_t) (at - parsestring) < sizeof(buf) - 1) {
5834	memcpy(buf, parsestring, (size_t) (at - parsestring));
5835	buf[at - parsestring] = 0;
5836	parsestring = buf;
5837	parse_xinerama_screen(display, at + 1, ret);
5838    }
5839    return ((strlen(parsestring) <= MAX_U_STRING)
5840	    ? XParseGeometry(parsestring, &ret->x, &ret->y, &ret->w, &ret->h)
5841	    : 0);
5842}
5843
5844#if USE_DOUBLE_BUFFER
5845Window
5846VDrawable(TScreen *screen)
5847{
5848    screen->needSwap = 1;
5849    return WhichVWin(screen)->drawable;
5850}
5851#endif
5852
5853#if OPT_RENDERFONT
5854#ifndef discardRenderDraw
5855void
5856discardRenderDraw(TScreen *screen)
5857{
5858    if (screen->renderDraw) {
5859	XftDrawDestroy(screen->renderDraw);
5860	screen->renderDraw = NULL;
5861    }
5862}
5863#endif
5864#endif /* OPT_RENDERFONT */
5865
5866char *
5867xtermSetLocale(int category, String after)
5868{
5869    char *before = x_strdup(setlocale(category, 0));
5870
5871    (void) setlocale(category, after);
5872    TRACE(("before setlocale :%s\n", NonNull(before)));
5873    TRACE(("updated locale   :%s\n", NonNull(setlocale(category, 0))));
5874    return before;
5875}
5876
5877void
5878xtermResetLocale(int category, char *before)
5879{
5880    (void) setlocale(category, before);
5881    free(before);
5882    TRACE(("restored locale  :%s\n", NonNull(setlocale(category, 0))));
5883}
5884