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