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