util.c revision 956cc18d
1/* $XTermId: util.c,v 1.489 2009/09/10 09:22:43 tom Exp $ */
2
3/*
4 * Copyright 1999-2008,2009 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 <ctype.h>
67#include <assert.h>
68
69#if OPT_WIDE_CHARS
70#if defined(HAVE_WCHAR_H) && defined(HAVE_WCWIDTH)
71#include <wchar.h>
72#endif
73#include <wcwidth.h>
74#endif
75
76static int handle_translated_exposure(XtermWidget xw,
77				      int rect_x,
78				      int rect_y,
79				      int rect_width,
80				      int rect_height);
81static void ClearLeft(XtermWidget xw);
82static void CopyWait(XtermWidget xw);
83static void horizontal_copy_area(XtermWidget xw,
84				 int firstchar,
85				 int nchars,
86				 int amount);
87static void vertical_copy_area(XtermWidget xw,
88			       int firstline,
89			       int nlines,
90			       int amount);
91
92#if OPT_WIDE_CHARS
93int (*my_wcwidth) (wchar_t);
94#endif
95
96#if OPT_WIDE_CHARS
97/*
98 * We will modify the 'n' cells beginning at the current position.
99 * Some of those cells may be part of multi-column characters, including
100 * carryover from the left.  Find the limits of the multi-column characters
101 * that we should fill with blanks, return true if filling is needed.
102 */
103int
104DamagedCells(TScreen * screen, unsigned n, int *klp, int *krp, int row, int col)
105{
106    LineData *ld = getLineData(screen, row);
107    int kl = col;
108    int kr = col + (int) n;
109
110    if (ld->charData[kl] == HIDDEN_CHAR) {
111	while (kl > 0) {
112	    if (ld->charData[--kl] != HIDDEN_CHAR) {
113		break;
114	    }
115	}
116    } else {
117	kl = col + 1;
118    }
119    if (ld->charData[kr] == HIDDEN_CHAR) {
120	while (kr < screen->max_col) {
121	    if (ld->charData[++kr] != HIDDEN_CHAR) {
122		--kr;
123		break;
124	    }
125	}
126    } else {
127	kr = col - 1;
128    }
129    if (klp)
130	*klp = kl;
131    if (krp)
132	*krp = kr;
133    return (kr >= kl);
134}
135
136int
137DamagedCurCells(TScreen * screen, unsigned n, int *klp, int *krp)
138{
139    return DamagedCells(screen, n, klp, krp, screen->cur_row, screen->cur_col);
140}
141#endif /* OPT_WIDE_CHARS */
142
143/*
144 * These routines are used for the jump scroll feature
145 */
146void
147FlushScroll(XtermWidget xw)
148{
149    TScreen *screen = &(xw->screen);
150    int i;
151    int shift = INX2ROW(screen, 0);
152    int bot = screen->max_row - shift;
153    int refreshtop;
154    int refreshheight;
155    int scrolltop;
156    int scrollheight;
157
158    if (screen->cursor_state)
159	HideCursor();
160    if (screen->scroll_amt > 0) {
161	refreshheight = screen->refresh_amt;
162	scrollheight = screen->bot_marg - screen->top_marg -
163	    refreshheight + 1;
164	if ((refreshtop = screen->bot_marg - refreshheight + 1 + shift) >
165	    (i = screen->max_row - screen->scroll_amt + 1))
166	    refreshtop = i;
167	if (screen->scrollWidget
168	    && !screen->whichBuf
169	    && screen->top_marg == 0) {
170	    scrolltop = 0;
171	    if ((scrollheight += shift) > i)
172		scrollheight = i;
173	    if ((i = screen->bot_marg - bot) > 0 &&
174		(refreshheight -= i) < screen->scroll_amt)
175		refreshheight = screen->scroll_amt;
176	    if ((i = screen->savedlines) < screen->savelines) {
177		if ((i += screen->scroll_amt) >
178		    screen->savelines)
179		    i = screen->savelines;
180		screen->savedlines = i;
181		ScrollBarDrawThumb(screen->scrollWidget);
182	    }
183	} else {
184	    scrolltop = screen->top_marg + shift;
185	    if ((i = bot - (screen->bot_marg - screen->refresh_amt +
186			    screen->scroll_amt)) > 0) {
187		if (bot < screen->bot_marg)
188		    refreshheight = screen->scroll_amt + i;
189	    } else {
190		scrollheight += i;
191		refreshheight = screen->scroll_amt;
192		if ((i = screen->top_marg + screen->scroll_amt -
193		     1 - bot) > 0) {
194		    refreshtop += i;
195		    refreshheight -= i;
196		}
197	    }
198	}
199    } else {
200	refreshheight = -screen->refresh_amt;
201	scrollheight = screen->bot_marg - screen->top_marg -
202	    refreshheight + 1;
203	refreshtop = screen->top_marg + shift;
204	scrolltop = refreshtop + refreshheight;
205	if ((i = screen->bot_marg - bot) > 0)
206	    scrollheight -= i;
207	if ((i = screen->top_marg + refreshheight - 1 - bot) > 0)
208	    refreshheight -= i;
209    }
210    scrolling_copy_area(xw, scrolltop + screen->scroll_amt,
211			scrollheight, screen->scroll_amt);
212    ScrollSelection(screen, -(screen->scroll_amt), False);
213    screen->scroll_amt = 0;
214    screen->refresh_amt = 0;
215    if (refreshheight > 0) {
216	ClearCurBackground(xw,
217			   (int) refreshtop * FontHeight(screen) + screen->border,
218			   (int) OriginX(screen),
219			   (unsigned) (refreshheight * FontHeight(screen)),
220			   (unsigned) Width(screen));
221	ScrnRefresh(xw, refreshtop, 0, refreshheight,
222		    MaxCols(screen), False);
223    }
224    return;
225}
226
227/*
228 * Returns true if there are lines off-screen due to scrolling which should
229 * include the current line.  If false, the line is visible and we should
230 * paint it now rather than waiting for the line to become visible.
231 */
232int
233AddToRefresh(XtermWidget xw)
234{
235    TScreen *screen = &(xw->screen);
236    int amount = screen->refresh_amt;
237    int row = screen->cur_row;
238    int result;
239
240    if (amount == 0) {
241	result = 0;
242    } else if (amount > 0) {
243	int bottom;
244
245	if (row == (bottom = screen->bot_marg) - amount) {
246	    screen->refresh_amt++;
247	    result = 1;
248	} else {
249	    result = (row >= bottom - amount + 1 && row <= bottom);
250	}
251    } else {
252	int top;
253
254	amount = -amount;
255	if (row == (top = screen->top_marg) + amount) {
256	    screen->refresh_amt--;
257	    result = 1;
258	} else {
259	    result = (row <= top + amount - 1 && row >= top);
260	}
261    }
262
263    /*
264     * If this line is visible, and there are scrolled-off lines, flush out
265     * those which are now visible.
266     */
267    if (!result && screen->scroll_amt)
268	FlushScroll(xw);
269
270    return result;
271}
272
273/*
274 * Returns true if the current row is in the visible area (it should be for
275 * screen operations) and incidentally flush the scrolled-in lines which
276 * have newly become visible.
277 */
278static Bool
279AddToVisible(XtermWidget xw)
280{
281    TScreen *screen = &(xw->screen);
282    Bool result = False;
283
284    if (INX2ROW(screen, screen->cur_row) <= screen->max_row) {
285	if (!AddToRefresh(xw)) {
286	    result = True;
287	}
288    }
289    return result;
290}
291
292/*
293 * If we're scrolling, leave the selection intact if possible.
294 * If it will bump into one of the extremes of the saved-lines, truncate that.
295 * If the selection is not contained within the scrolled region, clear it.
296 */
297static void
298adjustHiliteOnFwdScroll(XtermWidget xw, int amount, Bool all_lines)
299{
300    TScreen *screen = &(xw->screen);
301    int lo_row = (all_lines
302		  ? (screen->bot_marg - screen->savelines)
303		  : screen->top_marg);
304    int hi_row = screen->bot_marg;
305
306    TRACE2(("adjustSelection FWD %s by %d (%s)\n",
307	    screen->whichBuf ? "alternate" : "normal",
308	    amount,
309	    all_lines ? "all" : "visible"));
310    TRACE2(("  before highlite %d.%d .. %d.%d\n",
311	    screen->startH.row,
312	    screen->startH.col,
313	    screen->endH.row,
314	    screen->endH.col));
315    TRACE2(("  margins %d..%d\n", screen->top_marg, screen->bot_marg));
316    TRACE2(("  limits  %d..%d\n", lo_row, hi_row));
317
318    if (screen->startH.row >= lo_row
319	&& screen->startH.row - amount < lo_row) {
320	/* truncate the selection because its start would move out of region */
321	if (lo_row + amount <= screen->endH.row) {
322	    TRACE2(("truncate selection by changing start %d.%d to %d.%d\n",
323		    screen->startH.row,
324		    screen->startH.col,
325		    lo_row + amount,
326		    0));
327	    screen->startH.row = lo_row + amount;
328	    screen->startH.col = 0;
329	} else {
330	    TRACE2(("deselect because %d.%d .. %d.%d shifted %d is outside margins %d..%d\n",
331		    screen->startH.row,
332		    screen->startH.col,
333		    screen->endH.row,
334		    screen->endH.col,
335		    -amount,
336		    lo_row,
337		    hi_row));
338	    ScrnDisownSelection(xw);
339	}
340    } else if (screen->startH.row <= hi_row && screen->endH.row > hi_row) {
341	ScrnDisownSelection(xw);
342    } else if (screen->startH.row < lo_row && screen->endH.row > lo_row) {
343	ScrnDisownSelection(xw);
344    }
345
346    TRACE2(("  after highlite %d.%d .. %d.%d\n",
347	    screen->startH.row,
348	    screen->startH.col,
349	    screen->endH.row,
350	    screen->endH.col));
351}
352
353/*
354 * This is the same as adjustHiliteOnFwdScroll(), but reversed.  In this case,
355 * only the visible lines are affected.
356 */
357static void
358adjustHiliteOnBakScroll(XtermWidget xw, int amount)
359{
360    TScreen *screen = &(xw->screen);
361    int lo_row = screen->top_marg;
362    int hi_row = screen->bot_marg;
363
364    TRACE2(("adjustSelection BAK %s by %d (%s)\n",
365	    screen->whichBuf ? "alternate" : "normal",
366	    amount,
367	    "visible"));
368    TRACE2(("  before highlite %d.%d .. %d.%d\n",
369	    screen->startH.row,
370	    screen->startH.col,
371	    screen->endH.row,
372	    screen->endH.col));
373    TRACE2(("  margins %d..%d\n", screen->top_marg, screen->bot_marg));
374
375    if (screen->endH.row >= hi_row
376	&& screen->endH.row + amount > hi_row) {
377	/* truncate the selection because its start would move out of region */
378	if (hi_row - amount >= screen->startH.row) {
379	    TRACE2(("truncate selection by changing start %d.%d to %d.%d\n",
380		    screen->startH.row,
381		    screen->startH.col,
382		    hi_row - amount,
383		    0));
384	    screen->endH.row = hi_row - amount;
385	    screen->endH.col = 0;
386	} else {
387	    TRACE2(("deselect because %d.%d .. %d.%d shifted %d is outside margins %d..%d\n",
388		    screen->startH.row,
389		    screen->startH.col,
390		    screen->endH.row,
391		    screen->endH.col,
392		    amount,
393		    lo_row,
394		    hi_row));
395	    ScrnDisownSelection(xw);
396	}
397    } else if (screen->endH.row >= lo_row && screen->startH.row < lo_row) {
398	ScrnDisownSelection(xw);
399    } else if (screen->endH.row > hi_row && screen->startH.row > hi_row) {
400	ScrnDisownSelection(xw);
401    }
402
403    TRACE2(("  after highlite %d.%d .. %d.%d\n",
404	    screen->startH.row,
405	    screen->startH.col,
406	    screen->endH.row,
407	    screen->endH.col));
408}
409
410/*
411 * scrolls the screen by amount lines, erases bottom, doesn't alter
412 * cursor position (i.e. cursor moves down amount relative to text).
413 * All done within the scrolling region, of course.
414 * requires: amount > 0
415 */
416void
417xtermScroll(XtermWidget xw, int amount)
418{
419    TScreen *screen = &(xw->screen);
420    int i = screen->bot_marg - screen->top_marg + 1;
421    int shift;
422    int bot;
423    int refreshtop = 0;
424    int refreshheight;
425    int scrolltop;
426    int scrollheight;
427    Boolean scroll_all_lines = (Boolean) (screen->scrollWidget
428					  && !screen->whichBuf
429					  && screen->top_marg == 0);
430
431    TRACE(("xtermScroll count=%d\n", amount));
432
433    screen->cursor_busy += 1;
434    screen->cursor_moved = True;
435
436    if (screen->cursor_state)
437	HideCursor();
438
439    if (amount > i)
440	amount = i;
441
442    if (ScrnHaveSelection(screen))
443	adjustHiliteOnFwdScroll(xw, amount, scroll_all_lines);
444
445    if (screen->jumpscroll) {
446	if (screen->scroll_amt > 0) {
447	    if (!screen->fastscroll) {
448		if (screen->refresh_amt + amount > i)
449		    FlushScroll(xw);
450	    }
451	    screen->scroll_amt += amount;
452	    screen->refresh_amt += amount;
453	} else {
454	    if (!screen->fastscroll) {
455		if (screen->scroll_amt < 0)
456		    FlushScroll(xw);
457	    }
458	    screen->scroll_amt = amount;
459	    screen->refresh_amt = amount;
460	}
461	refreshheight = 0;
462    } else {
463	ScrollSelection(screen, -(amount), False);
464	if (amount == i) {
465	    ClearScreen(xw);
466	    screen->cursor_busy -= 1;
467	    return;
468	}
469
470	shift = INX2ROW(screen, 0);
471	bot = screen->max_row - shift;
472	scrollheight = i - amount;
473	refreshheight = amount;
474
475	if ((refreshtop = screen->bot_marg - refreshheight + 1 + shift) >
476	    (i = screen->max_row - refreshheight + 1))
477	    refreshtop = i;
478
479	if (scroll_all_lines) {
480	    scrolltop = 0;
481	    if ((scrollheight += shift) > i)
482		scrollheight = i;
483	    if ((i = screen->savedlines) < screen->savelines) {
484		if ((i += amount) > screen->savelines)
485		    i = screen->savelines;
486		screen->savedlines = i;
487		ScrollBarDrawThumb(screen->scrollWidget);
488	    }
489	} else {
490	    scrolltop = screen->top_marg + shift;
491	    if ((i = screen->bot_marg - bot) > 0) {
492		scrollheight -= i;
493		if ((i = screen->top_marg + amount - 1 - bot) >= 0) {
494		    refreshtop += i;
495		    refreshheight -= i;
496		}
497	    }
498	}
499
500	if (screen->multiscroll && amount == 1 &&
501	    screen->topline == 0 && screen->top_marg == 0 &&
502	    screen->bot_marg == screen->max_row) {
503	    if (screen->incopy < 0 && screen->scrolls == 0)
504		CopyWait(xw);
505	    screen->scrolls++;
506	}
507
508	scrolling_copy_area(xw, scrolltop + amount, scrollheight, amount);
509
510	if (refreshheight > 0) {
511	    ClearCurBackground(xw,
512			       (int) refreshtop * FontHeight(screen) + screen->border,
513			       (int) OriginX(screen),
514			       (unsigned) (refreshheight * FontHeight(screen)),
515			       (unsigned) Width(screen));
516	    if (refreshheight > shift)
517		refreshheight = shift;
518	}
519    }
520
521    if (amount > 0) {
522	if (scroll_all_lines) {
523	    ScrnDeleteLine(xw,
524			   screen->saveBuf_index,
525			   screen->bot_marg + screen->savelines,
526			   0,
527			   (unsigned) amount);
528	} else {
529	    ScrnDeleteLine(xw,
530			   screen->visbuf,
531			   screen->bot_marg,
532			   screen->top_marg,
533			   (unsigned) amount);
534	}
535    }
536
537    if (refreshheight > 0) {
538	ScrnRefresh(xw, refreshtop, 0, refreshheight,
539		    MaxCols(screen), False);
540    }
541
542    screen->cursor_busy -= 1;
543    return;
544}
545
546/*
547 * Reverse scrolls the screen by amount lines, erases top, doesn't alter
548 * cursor position (i.e. cursor moves up amount relative to text).
549 * All done within the scrolling region, of course.
550 * Requires: amount > 0
551 */
552void
553RevScroll(XtermWidget xw, int amount)
554{
555    TScreen *screen = &(xw->screen);
556    int i = screen->bot_marg - screen->top_marg + 1;
557    int shift;
558    int bot;
559    int refreshtop;
560    int refreshheight;
561    int scrolltop;
562    int scrollheight;
563
564    TRACE(("RevScroll count=%d\n", amount));
565
566    screen->cursor_busy += 1;
567    screen->cursor_moved = True;
568
569    if (screen->cursor_state)
570	HideCursor();
571
572    if (amount > i)
573	amount = i;
574
575    if (ScrnHaveSelection(screen))
576	adjustHiliteOnBakScroll(xw, amount);
577
578    if (screen->jumpscroll) {
579	if (screen->scroll_amt < 0) {
580	    if (-screen->refresh_amt + amount > i)
581		FlushScroll(xw);
582	    screen->scroll_amt -= amount;
583	    screen->refresh_amt -= amount;
584	} else {
585	    if (screen->scroll_amt > 0)
586		FlushScroll(xw);
587	    screen->scroll_amt = -amount;
588	    screen->refresh_amt = -amount;
589	}
590    } else {
591	shift = INX2ROW(screen, 0);
592	bot = screen->max_row - shift;
593	refreshheight = amount;
594	scrollheight = screen->bot_marg - screen->top_marg -
595	    refreshheight + 1;
596	refreshtop = screen->top_marg + shift;
597	scrolltop = refreshtop + refreshheight;
598	if ((i = screen->bot_marg - bot) > 0)
599	    scrollheight -= i;
600	if ((i = screen->top_marg + refreshheight - 1 - bot) > 0)
601	    refreshheight -= i;
602
603	if (screen->multiscroll && amount == 1 &&
604	    screen->topline == 0 && screen->top_marg == 0 &&
605	    screen->bot_marg == screen->max_row) {
606	    if (screen->incopy < 0 && screen->scrolls == 0)
607		CopyWait(xw);
608	    screen->scrolls++;
609	}
610
611	scrolling_copy_area(xw, scrolltop - amount, scrollheight, -amount);
612
613	if (refreshheight > 0) {
614	    ClearCurBackground(xw,
615			       (int) refreshtop * FontHeight(screen) + screen->border,
616			       (int) OriginX(screen),
617			       (unsigned) (refreshheight * FontHeight(screen)),
618			       (unsigned) Width(screen));
619	}
620    }
621    if (amount > 0) {
622	ScrnInsertLine(xw,
623		       screen->visbuf,
624		       screen->bot_marg,
625		       screen->top_marg,
626		       (unsigned) amount);
627    }
628    screen->cursor_busy -= 1;
629    return;
630}
631
632/*
633 * write a string str of length len onto the screen at
634 * the current cursor position.  update cursor position.
635 */
636void
637WriteText(XtermWidget xw, IChar * str, Cardinal len)
638{
639    TScreen *screen = &(xw->screen);
640    LineData *ld = 0;
641    unsigned test;
642    unsigned flags = xw->flags;
643    CellColor fg_bg = makeColorPair(xw->cur_foreground, xw->cur_background);
644    unsigned cells = visual_width(str, len);
645    GC currentGC;
646
647    TRACE(("WriteText (%2d,%2d) %3d:%s\n",
648	   screen->cur_row,
649	   screen->cur_col,
650	   len, visibleIChar(str, len)));
651
652    if (ScrnHaveSelection(screen)
653	&& ScrnIsLineInSelection(screen, INX2ROW(screen, screen->cur_row))) {
654	ScrnDisownSelection(xw);
655    }
656
657    /* if we are in insert-mode, reserve space for the new cells */
658    if (flags & INSERT) {
659	InsertChar(xw, cells);
660    }
661
662    if (AddToVisible(xw)
663	&& (ld = getLineData(screen, screen->cur_row)) != 0) {
664	if (screen->cursor_state)
665	    HideCursor();
666
667	/*
668	 * If we overwrite part of a multi-column character, fill the rest
669	 * of it with blanks.
670	 */
671	if_OPT_WIDE_CHARS(screen, {
672	    int kl;
673	    int kr;
674	    if (DamagedCurCells(screen, cells, &kl, &kr))
675		ClearInLine(xw, screen->cur_row, kl, (unsigned) (kr - kl + 1));
676	});
677
678	if (flags & INVISIBLE) {
679	    Cardinal n;
680	    for (n = 0; n < cells; ++n)
681		str[n] = ' ';
682	}
683
684	TRACE(("WriteText calling drawXtermText (%d) (%d,%d)\n",
685	       LineCharSet(screen, ld),
686	       screen->cur_col,
687	       screen->cur_row));
688
689	test = flags;
690	checkVeryBoldColors(test, xw->cur_foreground);
691
692	/* make sure that the correct GC is current */
693	currentGC = updatedXtermGC(xw, flags, fg_bg, False);
694
695	drawXtermText(xw, test & DRAWX_MASK, currentGC,
696		      LineCursorX(screen, ld, screen->cur_col),
697		      CursorY(screen, screen->cur_row),
698		      LineCharSet(screen, ld),
699		      str, len, 0);
700
701	resetXtermGC(xw, flags, False);
702    }
703
704    ScrnWriteText(xw, str, flags, fg_bg, len);
705    CursorForward(screen, (int) cells);
706#if OPT_ZICONBEEP
707    /* Flag icon name with "***"  on window output when iconified.
708     */
709    if (resource.zIconBeep && mapstate == IsUnmapped && !screen->zIconBeep_flagged) {
710	static char *icon_name;
711	static Arg args[] =
712	{
713	    {XtNiconName, (XtArgVal) & icon_name}
714	};
715
716	icon_name = NULL;
717	XtGetValues(toplevel, args, XtNumber(args));
718
719	if (icon_name != NULL) {
720	    screen->zIconBeep_flagged = True;
721	    ChangeIconName(xw, icon_name);
722	}
723	xtermBell(xw, resource.zIconBeep, XkbBI_Info);
724    }
725    mapstate = -1;
726#endif /* OPT_ZICONBEEP */
727    return;
728}
729
730/*
731 * If cursor not in scrolling region, returns.  Else,
732 * inserts n blank lines at the cursor's position.  Lines above the
733 * bottom margin are lost.
734 */
735void
736InsertLine(XtermWidget xw, int n)
737{
738    TScreen *screen = &(xw->screen);
739    int i;
740    int shift;
741    int bot;
742    int refreshtop;
743    int refreshheight;
744    int scrolltop;
745    int scrollheight;
746
747    if (!ScrnIsLineInMargins(screen, INX2ROW(screen, screen->cur_row)))
748	return;
749
750    TRACE(("InsertLine count=%d\n", n));
751
752    if (screen->cursor_state)
753	HideCursor();
754
755    if (ScrnHaveSelection(screen)
756	&& ScrnAreLinesInSelection(screen, screen->top_marg, screen->bot_marg)) {
757	ScrnDisownSelection(xw);
758    }
759
760    screen->do_wrap = False;
761    if (n > (i = screen->bot_marg - screen->cur_row + 1))
762	n = i;
763    if (screen->jumpscroll) {
764	if (screen->scroll_amt <= 0 &&
765	    screen->cur_row <= -screen->refresh_amt) {
766	    if (-screen->refresh_amt + n > MaxRows(screen))
767		FlushScroll(xw);
768	    screen->scroll_amt -= n;
769	    screen->refresh_amt -= n;
770	} else {
771	    if (screen->scroll_amt)
772		FlushScroll(xw);
773	}
774    }
775    if (!screen->scroll_amt) {
776	shift = INX2ROW(screen, 0);
777	bot = screen->max_row - shift;
778	refreshheight = n;
779	scrollheight = screen->bot_marg - screen->cur_row - refreshheight + 1;
780	refreshtop = screen->cur_row + shift;
781	scrolltop = refreshtop + refreshheight;
782	if ((i = screen->bot_marg - bot) > 0)
783	    scrollheight -= i;
784	if ((i = screen->cur_row + refreshheight - 1 - bot) > 0)
785	    refreshheight -= i;
786	vertical_copy_area(xw, scrolltop - n, scrollheight, -n);
787	if (refreshheight > 0) {
788	    ClearCurBackground(xw,
789			       (int) refreshtop * FontHeight(screen) + screen->border,
790			       (int) OriginX(screen),
791			       (unsigned) (refreshheight * FontHeight(screen)),
792			       (unsigned) Width(screen));
793	}
794    }
795    if (n > 0) {
796	ScrnInsertLine(xw,
797		       screen->visbuf,
798		       screen->bot_marg,
799		       screen->cur_row,
800		       (unsigned) n);
801    }
802}
803
804/*
805 * If cursor not in scrolling region, returns.  Else, deletes n lines
806 * at the cursor's position, lines added at bottom margin are blank.
807 */
808void
809DeleteLine(XtermWidget xw, int n)
810{
811    TScreen *screen = &(xw->screen);
812    int i;
813    int shift;
814    int bot;
815    int refreshtop;
816    int refreshheight;
817    int scrolltop;
818    int scrollheight;
819    Boolean scroll_all_lines = (Boolean) (screen->scrollWidget
820					  && !screen->whichBuf
821					  && screen->cur_row == 0);
822
823    if (!ScrnIsLineInMargins(screen, INX2ROW(screen, screen->cur_row)))
824	return;
825
826    TRACE(("DeleteLine count=%d\n", n));
827
828    if (screen->cursor_state)
829	HideCursor();
830
831    if (ScrnHaveSelection(screen)
832	&& ScrnAreLinesInSelection(screen, screen->top_marg, screen->bot_marg)) {
833	ScrnDisownSelection(xw);
834    }
835
836    screen->do_wrap = False;
837    if (n > (i = screen->bot_marg - screen->cur_row + 1))
838	n = i;
839    if (screen->jumpscroll) {
840	if (screen->scroll_amt >= 0 && screen->cur_row == screen->top_marg) {
841	    if (screen->refresh_amt + n > MaxRows(screen))
842		FlushScroll(xw);
843	    screen->scroll_amt += n;
844	    screen->refresh_amt += n;
845	} else {
846	    if (screen->scroll_amt)
847		FlushScroll(xw);
848	}
849    }
850    if (!screen->scroll_amt) {
851
852	shift = INX2ROW(screen, 0);
853	bot = screen->max_row - shift;
854	scrollheight = i - n;
855	refreshheight = n;
856	if ((refreshtop = screen->bot_marg - refreshheight + 1 + shift) >
857	    (i = screen->max_row - refreshheight + 1))
858	    refreshtop = i;
859	if (scroll_all_lines) {
860	    scrolltop = 0;
861	    if ((scrollheight += shift) > i)
862		scrollheight = i;
863	    if ((i = screen->savedlines) < screen->savelines) {
864		if ((i += n) > screen->savelines)
865		    i = screen->savelines;
866		screen->savedlines = i;
867		ScrollBarDrawThumb(screen->scrollWidget);
868	    }
869	} else {
870	    scrolltop = screen->cur_row + shift;
871	    if ((i = screen->bot_marg - bot) > 0) {
872		scrollheight -= i;
873		if ((i = screen->cur_row + n - 1 - bot) >= 0) {
874		    refreshheight -= i;
875		}
876	    }
877	}
878	vertical_copy_area(xw, scrolltop + n, scrollheight, n);
879	if (refreshheight > 0) {
880	    ClearCurBackground(xw,
881			       (int) refreshtop * FontHeight(screen) + screen->border,
882			       (int) OriginX(screen),
883			       (unsigned) (refreshheight * FontHeight(screen)),
884			       (unsigned) Width(screen));
885	}
886    }
887    /* adjust screen->buf */
888    if (n > 0) {
889	if (scroll_all_lines)
890	    ScrnDeleteLine(xw,
891			   screen->saveBuf_index,
892			   screen->bot_marg + screen->savelines,
893			   0,
894			   (unsigned) n);
895	else
896	    ScrnDeleteLine(xw,
897			   screen->visbuf,
898			   screen->bot_marg,
899			   screen->cur_row,
900			   (unsigned) n);
901    }
902}
903
904/*
905 * Insert n blanks at the cursor's position, no wraparound
906 */
907void
908InsertChar(XtermWidget xw, unsigned n)
909{
910    TScreen *screen = &(xw->screen);
911    LineData *ld;
912    unsigned limit;
913    int row = INX2ROW(screen, screen->cur_row);
914
915    if (screen->cursor_state)
916	HideCursor();
917
918    TRACE(("InsertChar count=%d\n", n));
919
920    if (ScrnHaveSelection(screen)
921	&& ScrnIsLineInSelection(screen, row)) {
922	ScrnDisownSelection(xw);
923    }
924    screen->do_wrap = False;
925
926    assert(screen->cur_col <= screen->max_col);
927    limit = (unsigned) (MaxCols(screen) - screen->cur_col);
928
929    if (n > limit)
930	n = limit;
931
932    assert(n != 0);
933    if (AddToVisible(xw)
934	&& (ld = getLineData(screen, screen->cur_row)) != 0) {
935	int col = MaxCols(screen) - (int) n;
936
937	/*
938	 * If we shift part of a multi-column character, fill the rest
939	 * of it with blanks.  Do similar repair for the text which will
940	 * be shifted into the right-margin.
941	 */
942	if_OPT_WIDE_CHARS(screen, {
943	    int kl;
944	    int kr = screen->cur_col;
945	    if (DamagedCurCells(screen, n, &kl, (int *) 0) && kr > kl) {
946		ClearInLine(xw, screen->cur_row, kl, (unsigned) (kr - kl + 1));
947	    }
948	    kr = screen->max_col - (int) n + 1;
949	    if (DamagedCells(screen, n, &kl, (int *) 0,
950			     screen->cur_row,
951			     kr) && kr > kl) {
952		ClearInLine(xw, screen->cur_row, kl, (unsigned) (kr - kl + 1));
953	    }
954	});
955
956#if OPT_DEC_CHRSET
957	if (CSET_DOUBLE(GetLineDblCS(ld))) {
958	    col = MaxCols(screen) / 2 - (int) n;
959	}
960#endif
961	/*
962	 * prevent InsertChar from shifting the end of a line over
963	 * if it is being appended to
964	 */
965	if (non_blank_line(screen, screen->cur_row,
966			   screen->cur_col, MaxCols(screen))) {
967	    horizontal_copy_area(xw, screen->cur_col,
968				 col - screen->cur_col,
969				 (int) n);
970	}
971
972	ClearCurBackground(xw,
973			   CursorY(screen, screen->cur_row),
974			   LineCursorX(screen, ld, screen->cur_col),
975			   (unsigned) FontHeight(screen),
976			   n * (unsigned) LineFontWidth(screen, ld));
977    }
978    /* adjust screen->buf */
979    ScrnInsertChar(xw, n);
980}
981
982/*
983 * Deletes n chars at the cursor's position, no wraparound.
984 */
985void
986DeleteChar(XtermWidget xw, unsigned n)
987{
988    TScreen *screen = &(xw->screen);
989    LineData *ld;
990    unsigned limit;
991    int row = INX2ROW(screen, screen->cur_row);
992
993    if (screen->cursor_state)
994	HideCursor();
995
996    TRACE(("DeleteChar count=%d\n", n));
997
998    if (ScrnHaveSelection(screen)
999	&& ScrnIsLineInSelection(screen, row)) {
1000	ScrnDisownSelection(xw);
1001    }
1002    screen->do_wrap = False;
1003
1004    assert(screen->cur_col <= screen->max_col);
1005    limit = (unsigned) (MaxCols(screen) - screen->cur_col);
1006
1007    if (n > limit)
1008	n = limit;
1009
1010    assert(n != 0);
1011    if (AddToVisible(xw)
1012	&& (ld = getLineData(screen, screen->cur_row)) != 0) {
1013	int col = MaxCols(screen) - (int) n;
1014
1015	/*
1016	 * If we delete part of a multi-column character, fill the rest
1017	 * of it with blanks.
1018	 */
1019	if_OPT_WIDE_CHARS(screen, {
1020	    int kl;
1021	    int kr;
1022	    if (DamagedCurCells(screen, n, &kl, &kr))
1023		ClearInLine(xw, screen->cur_row, kl, (unsigned) (kr - kl + 1));
1024	});
1025
1026#if OPT_DEC_CHRSET
1027	if (CSET_DOUBLE(GetLineDblCS(ld))) {
1028	    col = MaxCols(screen) / 2 - (int) n;
1029	}
1030#endif
1031	horizontal_copy_area(xw,
1032			     (screen->cur_col + (int) n),
1033			     col - screen->cur_col,
1034			     -((int) n));
1035
1036	ClearCurBackground(xw,
1037			   CursorY(screen, screen->cur_row),
1038			   LineCursorX(screen, ld, col),
1039			   (unsigned) FontHeight(screen),
1040			   n * (unsigned) LineFontWidth(screen, ld));
1041    }
1042    if (n != 0) {
1043	/* adjust screen->buf */
1044	ScrnDeleteChar(xw, n);
1045    }
1046}
1047
1048/*
1049 * Clear from cursor position to beginning of display, inclusive.
1050 */
1051static void
1052ClearAbove(XtermWidget xw)
1053{
1054    TScreen *screen = &(xw->screen);
1055
1056    if (screen->protected_mode != OFF_PROTECT) {
1057	int row;
1058	unsigned len = (unsigned) MaxCols(screen);
1059
1060	assert(screen->max_col >= 0);
1061	for (row = 0; row <= screen->max_row; row++)
1062	    ClearInLine(xw, row, 0, len);
1063    } else {
1064	int top, height;
1065
1066	if (screen->cursor_state)
1067	    HideCursor();
1068	if ((top = INX2ROW(screen, 0)) <= screen->max_row) {
1069	    if (screen->scroll_amt)
1070		FlushScroll(xw);
1071	    if ((height = screen->cur_row + top) > screen->max_row)
1072		height = screen->max_row;
1073	    if ((height -= top) > 0) {
1074		ClearCurBackground(xw,
1075				   top * FontHeight(screen) + screen->border,
1076				   OriginX(screen),
1077				   (unsigned) (height * FontHeight(screen)),
1078				   (unsigned) (Width(screen)));
1079	    }
1080	}
1081	ClearBufRows(xw, 0, screen->cur_row - 1);
1082    }
1083
1084    if (INX2ROW(screen, screen->cur_row) <= screen->max_row)
1085	ClearLeft(xw);
1086}
1087
1088/*
1089 * Clear from cursor position to end of display, inclusive.
1090 */
1091static void
1092ClearBelow(XtermWidget xw)
1093{
1094    TScreen *screen = &(xw->screen);
1095
1096    ClearRight(xw, -1);
1097
1098    if (screen->protected_mode != OFF_PROTECT) {
1099	int row;
1100	unsigned len = (unsigned) MaxCols(screen);
1101
1102	assert(screen->max_col >= 0);
1103	for (row = screen->cur_row + 1; row <= screen->max_row; row++)
1104	    ClearInLine(xw, row, 0, len);
1105    } else {
1106	int top;
1107
1108	if ((top = INX2ROW(screen, screen->cur_row)) <= screen->max_row) {
1109	    if (screen->scroll_amt)
1110		FlushScroll(xw);
1111	    if (++top <= screen->max_row) {
1112		ClearCurBackground(xw,
1113				   top * FontHeight(screen) + screen->border,
1114				   OriginX(screen),
1115				   (unsigned) ((screen->max_row - top + 1)
1116					       * FontHeight(screen)),
1117				   (unsigned) (Width(screen)));
1118	    }
1119	}
1120	ClearBufRows(xw, screen->cur_row + 1, screen->max_row);
1121    }
1122}
1123
1124/*
1125 * Clear the given row, for the given range of columns, returning 1 if no
1126 * protected characters were found, 0 otherwise.
1127 */
1128static int
1129ClearInLine2(XtermWidget xw, int flags, int row, int col, unsigned len)
1130{
1131    TScreen *screen = &(xw->screen);
1132    LineData *ld;
1133    int rc = 1;
1134
1135    TRACE(("ClearInLine(row=%d, col=%d, len=%d) vs %d..%d\n",
1136	   row, col, len,
1137	   screen->startH.row,
1138	   screen->startH.col));
1139
1140    if (ScrnHaveSelection(screen)
1141	&& ScrnIsLineInSelection(screen, row)) {
1142	ScrnDisownSelection(xw);
1143    }
1144
1145    if (col + (int) len >= MaxCols(screen)) {
1146	len = (unsigned) (MaxCols(screen) - col);
1147    }
1148
1149    /* If we've marked protected text on the screen, we'll have to
1150     * check each time we do an erase.
1151     */
1152    if (screen->protected_mode != OFF_PROTECT) {
1153	unsigned n;
1154	Char *attrs = getLineData(screen, row)->attribs + col;
1155	int saved_mode = screen->protected_mode;
1156	Bool done;
1157
1158	/* disable this branch during recursion */
1159	screen->protected_mode = OFF_PROTECT;
1160
1161	do {
1162	    done = True;
1163	    for (n = 0; n < len; n++) {
1164		if (attrs[n] & PROTECTED) {
1165		    rc = 0;	/* found a protected segment */
1166		    if (n != 0) {
1167			ClearInLine(xw, row, col, n);
1168		    }
1169		    while ((n < len)
1170			   && (attrs[n] & PROTECTED)) {
1171			n++;
1172		    }
1173		    done = False;
1174		    break;
1175		}
1176	    }
1177	    /* setup for another segment, past the protected text */
1178	    if (!done) {
1179		attrs += n;
1180		col += (int) n;
1181		len -= n;
1182	    }
1183	} while (!done);
1184
1185	screen->protected_mode = saved_mode;
1186	if (len <= 0)
1187	    return 0;
1188    }
1189    /* fall through to the final non-protected segment */
1190
1191    if (screen->cursor_state)
1192	HideCursor();
1193    screen->do_wrap = False;
1194
1195    if (AddToVisible(xw)
1196	&& (ld = getLineData(screen, row)) != 0) {
1197
1198	ClearCurBackground(xw,
1199			   CursorY(screen, row),
1200			   LineCursorX(screen, ld, col),
1201			   (unsigned) FontHeight(screen),
1202			   len * (unsigned) LineFontWidth(screen, ld));
1203    }
1204
1205    if (len != 0) {
1206	ClearCells(xw, flags, len, row, col);
1207    }
1208
1209    return rc;
1210}
1211
1212int
1213ClearInLine(XtermWidget xw, int row, int col, unsigned len)
1214{
1215    TScreen *screen = &(xw->screen);
1216    int flags = 0;
1217
1218    /*
1219     * If we're clearing to the end of the line, we won't count this as
1220     * "drawn" characters.  We'll only do cut/paste on "drawn" characters,
1221     * so this has the effect of suppressing trailing blanks from a
1222     * selection.
1223     */
1224    if (col + (int) len < MaxCols(screen)) {
1225	flags |= CHARDRAWN;
1226    }
1227    return ClearInLine2(xw, flags, row, col, len);
1228}
1229
1230/*
1231 * Clear the next n characters on the cursor's line, including the cursor's
1232 * position.
1233 */
1234void
1235ClearRight(XtermWidget xw, int n)
1236{
1237    TScreen *screen = &(xw->screen);
1238    LineData *ld;
1239    unsigned len = (unsigned) (MaxCols(screen) - screen->cur_col);
1240
1241    assert(screen->max_col >= 0);
1242    assert(screen->max_col >= screen->cur_col);
1243
1244    if (n < 0)			/* the remainder of the line */
1245	n = MaxCols(screen);
1246    if (n == 0)			/* default for 'ECH' */
1247	n = 1;
1248
1249    if (len > (unsigned) n)
1250	len = (unsigned) n;
1251
1252    ld = getLineData(screen, screen->cur_row);
1253    if (AddToVisible(xw)) {
1254	if_OPT_WIDE_CHARS(screen, {
1255	    int col = screen->cur_col;
1256	    int row = screen->cur_row;
1257	    int kl;
1258	    int kr;
1259	    int xx;
1260	    if (DamagedCurCells(screen, len, &kl, &kr) && kr >= kl) {
1261		xx = col;
1262		if (kl < xx) {
1263		    ClearInLine2(xw, 0, row, kl, (unsigned) (xx - kl));
1264		}
1265		xx = col + (int) len - 1;
1266		if (kr > xx) {
1267		    ClearInLine2(xw, 0, row, xx + 1, (unsigned) (kr - xx));
1268		}
1269	    }
1270	});
1271	(void) ClearInLine(xw, screen->cur_row, screen->cur_col, len);
1272    } else {
1273	ScrnClearCells(xw, screen->cur_row, screen->cur_col, len);
1274    }
1275
1276    /* with the right part cleared, we can't be wrapping */
1277    LineClrWrapped(ld);
1278    screen->do_wrap = False;
1279}
1280
1281/*
1282 * Clear first part of cursor's line, inclusive.
1283 */
1284static void
1285ClearLeft(XtermWidget xw)
1286{
1287    TScreen *screen = &(xw->screen);
1288    unsigned len = (unsigned) screen->cur_col + 1;
1289    assert(screen->cur_col >= 0);
1290
1291    if (AddToVisible(xw)) {
1292	if_OPT_WIDE_CHARS(screen, {
1293	    int row = screen->cur_row;
1294	    int kl;
1295	    int kr;
1296	    if (DamagedCurCells(screen, 1, &kl, &kr) && kr >= kl) {
1297		ClearInLine2(xw, 0, row, kl, (unsigned) (kr - kl + 1));
1298	    }
1299	});
1300	(void) ClearInLine(xw, screen->cur_row, 0, len);
1301    } else {
1302	ScrnClearCells(xw, screen->cur_row, 0, len);
1303    }
1304}
1305
1306/*
1307 * Erase the cursor's line.
1308 */
1309static void
1310ClearLine(XtermWidget xw)
1311{
1312    TScreen *screen = &(xw->screen);
1313    unsigned len = (unsigned) MaxCols(screen);
1314
1315    assert(screen->max_col >= 0);
1316    (void) ClearInLine(xw, screen->cur_row, 0, len);
1317}
1318
1319void
1320ClearScreen(XtermWidget xw)
1321{
1322    TScreen *screen = &(xw->screen);
1323    int top;
1324
1325    if (screen->cursor_state)
1326	HideCursor();
1327
1328    ScrnDisownSelection(xw);
1329    screen->do_wrap = False;
1330    if ((top = INX2ROW(screen, 0)) <= screen->max_row) {
1331	if (screen->scroll_amt)
1332	    FlushScroll(xw);
1333	ClearCurBackground(xw,
1334			   top * FontHeight(screen) + screen->border,
1335			   OriginX(screen),
1336			   (unsigned) ((screen->max_row - top + 1)
1337				       * FontHeight(screen)),
1338			   (unsigned) Width(screen));
1339    }
1340    ClearBufRows(xw, 0, screen->max_row);
1341}
1342
1343/*
1344 * If we've written protected text DEC-style, and are issuing a non-DEC
1345 * erase, temporarily reset the protected_mode flag so that the erase will
1346 * ignore the protected flags.
1347 */
1348void
1349do_erase_line(XtermWidget xw, int param, int mode)
1350{
1351    TScreen *screen = &(xw->screen);
1352    int saved_mode = screen->protected_mode;
1353
1354    if (saved_mode == DEC_PROTECT
1355	&& saved_mode != mode) {
1356	screen->protected_mode = OFF_PROTECT;
1357    }
1358
1359    switch (param) {
1360    case -1:			/* DEFAULT */
1361    case 0:
1362	ClearRight(xw, -1);
1363	break;
1364    case 1:
1365	ClearLeft(xw);
1366	break;
1367    case 2:
1368	ClearLine(xw);
1369	break;
1370    }
1371    screen->protected_mode = saved_mode;
1372}
1373
1374/*
1375 * Just like 'do_erase_line()', except that this intercepts ED controls.  If we
1376 * clear the whole screen, we'll get the return-value from ClearInLine, and
1377 * find if there were any protected characters left.  If not, reset the
1378 * protected mode flag in the screen data (it's slower).
1379 */
1380void
1381do_erase_display(XtermWidget xw, int param, int mode)
1382{
1383    TScreen *screen = &(xw->screen);
1384    int saved_mode = screen->protected_mode;
1385
1386    if (saved_mode == DEC_PROTECT
1387	&& saved_mode != mode)
1388	screen->protected_mode = OFF_PROTECT;
1389
1390    switch (param) {
1391    case -1:			/* DEFAULT */
1392    case 0:
1393	if (screen->cur_row == 0
1394	    && screen->cur_col == 0) {
1395	    screen->protected_mode = saved_mode;
1396	    do_erase_display(xw, 2, mode);
1397	    saved_mode = screen->protected_mode;
1398	} else
1399	    ClearBelow(xw);
1400	break;
1401
1402    case 1:
1403	if (screen->cur_row == screen->max_row
1404	    && screen->cur_col == screen->max_col) {
1405	    screen->protected_mode = saved_mode;
1406	    do_erase_display(xw, 2, mode);
1407	    saved_mode = screen->protected_mode;
1408	} else
1409	    ClearAbove(xw);
1410	break;
1411
1412    case 2:
1413	/*
1414	 * We use 'ClearScreen()' throughout the remainder of the
1415	 * program for places where we don't care if the characters are
1416	 * protected or not.  So we modify the logic around this call
1417	 * on 'ClearScreen()' to handle protected characters.
1418	 */
1419	if (screen->protected_mode != OFF_PROTECT) {
1420	    int row;
1421	    int rc = 1;
1422	    unsigned len = (unsigned) MaxCols(screen);
1423
1424	    assert(screen->max_col >= 0);
1425	    for (row = 0; row <= screen->max_row; row++)
1426		rc &= ClearInLine(xw, row, 0, len);
1427	    if (rc != 0)
1428		saved_mode = OFF_PROTECT;
1429	} else {
1430	    ClearScreen(xw);
1431	}
1432	break;
1433
1434    case 3:
1435	/* xterm addition - erase saved lines. */
1436	screen->savedlines = 0;
1437	ScrollBarDrawThumb(screen->scrollWidget);
1438	break;
1439    }
1440    screen->protected_mode = saved_mode;
1441}
1442
1443static void
1444CopyWait(XtermWidget xw)
1445{
1446    TScreen *screen = &(xw->screen);
1447    XEvent reply;
1448    XEvent *rep = &reply;
1449
1450    while (1) {
1451	XWindowEvent(screen->display, VWindow(screen),
1452		     ExposureMask, &reply);
1453	switch (reply.type) {
1454	case Expose:
1455	    HandleExposure(xw, &reply);
1456	    break;
1457	case NoExpose:
1458	case GraphicsExpose:
1459	    if (screen->incopy <= 0) {
1460		screen->incopy = 1;
1461		if (screen->scrolls > 0)
1462		    screen->scrolls--;
1463	    }
1464	    if (reply.type == GraphicsExpose)
1465		HandleExposure(xw, &reply);
1466
1467	    if ((reply.type == NoExpose) ||
1468		((XExposeEvent *) rep)->count == 0) {
1469		if (screen->incopy <= 0 && screen->scrolls > 0)
1470		    screen->scrolls--;
1471		if (screen->scrolls == 0) {
1472		    screen->incopy = 0;
1473		    return;
1474		}
1475		screen->incopy = -1;
1476	    }
1477	    break;
1478	}
1479    }
1480}
1481
1482/*
1483 * used by vertical_copy_area and and horizontal_copy_area
1484 */
1485static void
1486copy_area(XtermWidget xw,
1487	  int src_x,
1488	  int src_y,
1489	  unsigned width,
1490	  unsigned height,
1491	  int dest_x,
1492	  int dest_y)
1493{
1494    TScreen *screen = &(xw->screen);
1495
1496    if (width != 0 && height != 0) {
1497	/* wait for previous CopyArea to complete unless
1498	   multiscroll is enabled and active */
1499	if (screen->incopy && screen->scrolls == 0)
1500	    CopyWait(xw);
1501	screen->incopy = -1;
1502
1503	/* save for translating Expose events */
1504	screen->copy_src_x = src_x;
1505	screen->copy_src_y = src_y;
1506	screen->copy_width = width;
1507	screen->copy_height = height;
1508	screen->copy_dest_x = dest_x;
1509	screen->copy_dest_y = dest_y;
1510
1511	XCopyArea(screen->display,
1512		  VWindow(screen), VWindow(screen),
1513		  NormalGC(xw, screen),
1514		  src_x, src_y, width, height, dest_x, dest_y);
1515    }
1516}
1517
1518/*
1519 * use when inserting or deleting characters on the current line
1520 */
1521static void
1522horizontal_copy_area(XtermWidget xw,
1523		     int firstchar,	/* char pos on screen to start copying at */
1524		     int nchars,
1525		     int amount)	/* number of characters to move right */
1526{
1527    TScreen *screen = &(xw->screen);
1528    LineData *ld;
1529
1530    if ((ld = getLineData(screen, screen->cur_row)) != 0) {
1531	int src_x = LineCursorX(screen, ld, firstchar);
1532	int src_y = CursorY(screen, screen->cur_row);
1533
1534	copy_area(xw, src_x, src_y,
1535		  (unsigned) (nchars * LineFontWidth(screen, ld)),
1536		  (unsigned) FontHeight(screen),
1537		  src_x + amount * LineFontWidth(screen, ld), src_y);
1538    }
1539}
1540
1541/*
1542 * use when inserting or deleting lines from the screen
1543 */
1544static void
1545vertical_copy_area(XtermWidget xw,
1546		   int firstline,	/* line on screen to start copying at */
1547		   int nlines,
1548		   int amount)	/* number of lines to move up (neg=down) */
1549{
1550    TScreen *screen = &(xw->screen);
1551
1552    if (nlines > 0) {
1553	int src_x = OriginX(screen);
1554	int src_y = firstline * FontHeight(screen) + screen->border;
1555
1556	copy_area(xw, src_x, src_y,
1557		  (unsigned) Width(screen),
1558		  (unsigned) (nlines * FontHeight(screen)),
1559		  src_x, src_y - amount * FontHeight(screen));
1560    }
1561}
1562
1563/*
1564 * use when scrolling the entire screen
1565 */
1566void
1567scrolling_copy_area(XtermWidget xw,
1568		    int firstline,	/* line on screen to start copying at */
1569		    int nlines,
1570		    int amount)	/* number of lines to move up (neg=down) */
1571{
1572
1573    if (nlines > 0) {
1574	vertical_copy_area(xw, firstline, nlines, amount);
1575    }
1576}
1577
1578/*
1579 * Handler for Expose events on the VT widget.
1580 * Returns 1 iff the area where the cursor was got refreshed.
1581 */
1582int
1583HandleExposure(XtermWidget xw, XEvent * event)
1584{
1585    TScreen *screen = &(xw->screen);
1586    XExposeEvent *reply = (XExposeEvent *) event;
1587
1588#ifndef NO_ACTIVE_ICON
1589    if (reply->window == screen->iconVwin.window) {
1590	WhichVWin(screen) = &screen->iconVwin;
1591	TRACE(("HandleExposure - icon"));
1592    } else {
1593	WhichVWin(screen) = &screen->fullVwin;
1594	TRACE(("HandleExposure - normal"));
1595    }
1596    TRACE((" event %d,%d %dx%d\n",
1597	   reply->y,
1598	   reply->x,
1599	   reply->height,
1600	   reply->width));
1601#endif /* NO_ACTIVE_ICON */
1602
1603    /* if not doing CopyArea or if this is a GraphicsExpose, don't translate */
1604    if (!screen->incopy || event->type != Expose)
1605	return handle_translated_exposure(xw, reply->x, reply->y,
1606					  reply->width,
1607					  reply->height);
1608    else {
1609	/* compute intersection of area being copied with
1610	   area being exposed. */
1611	int both_x1 = Max(screen->copy_src_x, reply->x);
1612	int both_y1 = Max(screen->copy_src_y, reply->y);
1613	int both_x2 = Min(screen->copy_src_x + (int) screen->copy_width,
1614			  (reply->x + (int) reply->width));
1615	int both_y2 = Min(screen->copy_src_y + (int) screen->copy_height,
1616			  (reply->y + (int) reply->height));
1617	int value = 0;
1618
1619	/* was anything copied affected? */
1620	if (both_x2 > both_x1 && both_y2 > both_y1) {
1621	    /* do the copied area */
1622	    value = handle_translated_exposure
1623		(xw, reply->x + screen->copy_dest_x - screen->copy_src_x,
1624		 reply->y + screen->copy_dest_y - screen->copy_src_y,
1625		 reply->width, reply->height);
1626	}
1627	/* was anything not copied affected? */
1628	if (reply->x < both_x1 || reply->y < both_y1
1629	    || reply->x + reply->width > both_x2
1630	    || reply->y + reply->height > both_y2)
1631	    value = handle_translated_exposure(xw, reply->x, reply->y,
1632					       reply->width, reply->height);
1633
1634	return value;
1635    }
1636}
1637
1638static void
1639set_background(XtermWidget xw, int color GCC_UNUSED)
1640{
1641    TScreen *screen = &(xw->screen);
1642    Pixel c = getXtermBackground(xw, xw->flags, color);
1643
1644    TRACE(("set_background(%d) %#lx\n", color, c));
1645    XSetWindowBackground(screen->display, VShellWindow, c);
1646    XSetWindowBackground(screen->display, VWindow(screen), c);
1647}
1648
1649/*
1650 * Called by the ExposeHandler to do the actual repaint after the coordinates
1651 * have been translated to allow for any CopyArea in progress.
1652 * The rectangle passed in is pixel coordinates.
1653 */
1654static int
1655handle_translated_exposure(XtermWidget xw,
1656			   int rect_x,
1657			   int rect_y,
1658			   int rect_width,
1659			   int rect_height)
1660{
1661    TScreen *screen = &(xw->screen);
1662    int toprow, leftcol, nrows, ncols;
1663    int x0, x1;
1664    int y0, y1;
1665    int result = 0;
1666
1667    TRACE(("handle_translated_exposure at %d,%d size %dx%d\n",
1668	   rect_y, rect_x, rect_height, rect_width));
1669
1670    x0 = (rect_x - OriginX(screen));
1671    x1 = (x0 + rect_width);
1672
1673    y0 = (rect_y - OriginY(screen));
1674    y1 = (y0 + rect_height);
1675
1676    if ((x0 < 0 ||
1677	 y0 < 0 ||
1678	 x1 > Width(screen) ||
1679	 y1 > Height(screen))) {
1680	set_background(xw, -1);
1681	XClearArea(screen->display, VWindow(screen),
1682		   rect_x,
1683		   rect_y,
1684		   (unsigned) rect_width,
1685		   (unsigned) rect_height, False);
1686    }
1687    toprow = y0 / FontHeight(screen);
1688    if (toprow < 0)
1689	toprow = 0;
1690
1691    leftcol = x0 / FontWidth(screen);
1692    if (leftcol < 0)
1693	leftcol = 0;
1694
1695    nrows = (y1 - 1) / FontHeight(screen) - toprow + 1;
1696    ncols = (x1 - 1) / FontWidth(screen) - leftcol + 1;
1697    toprow -= screen->scrolls;
1698    if (toprow < 0) {
1699	nrows += toprow;
1700	toprow = 0;
1701    }
1702    if (toprow + nrows > MaxRows(screen))
1703	nrows = MaxRows(screen) - toprow;
1704    if (leftcol + ncols > MaxCols(screen))
1705	ncols = MaxCols(screen) - leftcol;
1706
1707    if (nrows > 0 && ncols > 0) {
1708	ScrnRefresh(xw, toprow, leftcol, nrows, ncols, True);
1709	first_map_occurred();
1710	if (screen->cur_row >= toprow &&
1711	    screen->cur_row < toprow + nrows &&
1712	    screen->cur_col >= leftcol &&
1713	    screen->cur_col < leftcol + ncols) {
1714	    result = 1;
1715	}
1716
1717    }
1718    TRACE(("...handle_translated_exposure %d\n", result));
1719    return (result);
1720}
1721
1722/***====================================================================***/
1723
1724void
1725GetColors(XtermWidget xw, ScrnColors * pColors)
1726{
1727    TScreen *screen = &xw->screen;
1728    int n;
1729
1730    pColors->which = 0;
1731    for (n = 0; n < NCOLORS; ++n) {
1732	SET_COLOR_VALUE(pColors, n, T_COLOR(screen, n));
1733    }
1734}
1735
1736void
1737ChangeColors(XtermWidget xw, ScrnColors * pNew)
1738{
1739    Bool repaint = False;
1740    TScreen *screen = &xw->screen;
1741    VTwin *win = WhichVWin(screen);
1742
1743    TRACE(("ChangeColors\n"));
1744
1745    if (COLOR_DEFINED(pNew, TEXT_CURSOR)) {
1746	T_COLOR(screen, TEXT_CURSOR) = COLOR_VALUE(pNew, TEXT_CURSOR);
1747	TRACE(("... TEXT_CURSOR: %#lx\n", T_COLOR(screen, TEXT_CURSOR)));
1748	/* no repaint needed */
1749    } else if ((T_COLOR(screen, TEXT_CURSOR) == T_COLOR(screen, TEXT_FG)) &&
1750	       (COLOR_DEFINED(pNew, TEXT_FG))) {
1751	T_COLOR(screen, TEXT_CURSOR) = COLOR_VALUE(pNew, TEXT_FG);
1752	TRACE(("... TEXT_CURSOR: %#lx\n", T_COLOR(screen, TEXT_CURSOR)));
1753	repaint = screen->Vshow;
1754    }
1755
1756    if (COLOR_DEFINED(pNew, TEXT_FG)) {
1757	Pixel fg = COLOR_VALUE(pNew, TEXT_FG);
1758	T_COLOR(screen, TEXT_FG) = fg;
1759	TRACE(("... TEXT_FG: %#lx\n", T_COLOR(screen, TEXT_FG)));
1760	if (screen->Vshow) {
1761	    setCgsFore(xw, win, gcNorm, fg);
1762	    setCgsBack(xw, win, gcNormReverse, fg);
1763	    setCgsFore(xw, win, gcBold, fg);
1764	    setCgsBack(xw, win, gcBoldReverse, fg);
1765	    repaint = True;
1766	}
1767    }
1768
1769    if (COLOR_DEFINED(pNew, TEXT_BG)) {
1770	Pixel bg = COLOR_VALUE(pNew, TEXT_BG);
1771	T_COLOR(screen, TEXT_BG) = bg;
1772	TRACE(("... TEXT_BG: %#lx\n", T_COLOR(screen, TEXT_BG)));
1773	if (screen->Vshow) {
1774	    setCgsBack(xw, win, gcNorm, bg);
1775	    setCgsFore(xw, win, gcNormReverse, bg);
1776	    setCgsBack(xw, win, gcBold, bg);
1777	    setCgsFore(xw, win, gcBoldReverse, bg);
1778	    set_background(xw, -1);
1779	    repaint = True;
1780	}
1781    }
1782#if OPT_HIGHLIGHT_COLOR
1783    if (COLOR_DEFINED(pNew, HIGHLIGHT_BG)) {
1784	T_COLOR(screen, HIGHLIGHT_BG) = COLOR_VALUE(pNew, HIGHLIGHT_BG);
1785	TRACE(("... HIGHLIGHT_BG: %#lx\n", T_COLOR(screen, HIGHLIGHT_BG)));
1786	repaint = screen->Vshow;
1787    }
1788    if (COLOR_DEFINED(pNew, HIGHLIGHT_FG)) {
1789	T_COLOR(screen, HIGHLIGHT_FG) = COLOR_VALUE(pNew, HIGHLIGHT_FG);
1790	TRACE(("... HIGHLIGHT_FG: %#lx\n", T_COLOR(screen, HIGHLIGHT_FG)));
1791	repaint = screen->Vshow;
1792    }
1793#endif
1794
1795    if (COLOR_DEFINED(pNew, MOUSE_FG) || (COLOR_DEFINED(pNew, MOUSE_BG))) {
1796	if (COLOR_DEFINED(pNew, MOUSE_FG)) {
1797	    T_COLOR(screen, MOUSE_FG) = COLOR_VALUE(pNew, MOUSE_FG);
1798	    TRACE(("... MOUSE_FG: %#lx\n", T_COLOR(screen, MOUSE_FG)));
1799	}
1800	if (COLOR_DEFINED(pNew, MOUSE_BG)) {
1801	    T_COLOR(screen, MOUSE_BG) = COLOR_VALUE(pNew, MOUSE_BG);
1802	    TRACE(("... MOUSE_BG: %#lx\n", T_COLOR(screen, MOUSE_BG)));
1803	}
1804
1805	if (screen->Vshow) {
1806	    recolor_cursor(screen,
1807			   screen->pointer_cursor,
1808			   T_COLOR(screen, MOUSE_FG),
1809			   T_COLOR(screen, MOUSE_BG));
1810	    XDefineCursor(screen->display, VWindow(screen),
1811			  screen->pointer_cursor);
1812	}
1813#if OPT_TEK4014
1814	if (TEK4014_SHOWN(xw)) {
1815	    TekScreen *tekscr = &(tekWidget->screen);
1816	    Window tekwin = TWindow(tekscr);
1817	    if (tekwin) {
1818		recolor_cursor(screen,
1819			       tekscr->arrow,
1820			       T_COLOR(screen, MOUSE_FG),
1821			       T_COLOR(screen, MOUSE_BG));
1822		XDefineCursor(screen->display, tekwin, tekscr->arrow);
1823	    }
1824	}
1825#endif
1826	/* no repaint needed */
1827    }
1828
1829    if (COLOR_DEFINED(pNew, TEXT_FG) ||
1830	COLOR_DEFINED(pNew, TEXT_BG) ||
1831	COLOR_DEFINED(pNew, TEXT_CURSOR)) {
1832	set_cursor_gcs(xw);
1833    }
1834#if OPT_TEK4014
1835    if (COLOR_DEFINED(pNew, TEK_FG) ||
1836	COLOR_DEFINED(pNew, TEK_BG)) {
1837	ChangeTekColors(tekWidget, screen, pNew);
1838	if (TEK4014_SHOWN(xw)) {
1839	    TekRepaint(tekWidget);
1840	}
1841    } else if (COLOR_DEFINED(pNew, TEK_CURSOR)) {
1842	ChangeTekColors(tekWidget, screen, pNew);
1843    }
1844#endif
1845    if (repaint)
1846	xtermRepaint(xw);
1847}
1848
1849void
1850xtermClear(XtermWidget xw)
1851{
1852    TScreen *screen = &xw->screen;
1853
1854    TRACE(("xtermClear\n"));
1855    XClearWindow(screen->display, VWindow(screen));
1856}
1857
1858void
1859xtermRepaint(XtermWidget xw)
1860{
1861    TScreen *screen = &xw->screen;
1862
1863    TRACE(("xtermRepaint\n"));
1864    xtermClear(xw);
1865    ScrnRefresh(xw, 0, 0, MaxRows(screen), MaxCols(screen), True);
1866}
1867
1868/***====================================================================***/
1869
1870Boolean
1871isDefaultForeground(const char *name)
1872{
1873    return (Boolean) ! x_strcasecmp(name, XtDefaultForeground);
1874}
1875
1876Boolean
1877isDefaultBackground(const char *name)
1878{
1879    return (Boolean) ! x_strcasecmp(name, XtDefaultBackground);
1880}
1881
1882#if OPT_WIDE_CHARS
1883/*
1884 * Check for Unicode BIDI control characters, which may be miscategorized via
1885 * wcwidth() and iswprint() as zero-width printable characters.
1886 */
1887Boolean
1888isWideControl(unsigned ch)
1889{
1890    Boolean result;
1891
1892    switch (ch) {
1893    case 0x200E:
1894    case 0x200F:
1895    case 0x202A:
1896    case 0x202B:
1897    case 0x202C:
1898    case 0x202D:
1899    case 0x202E:
1900	result = True;
1901	break;
1902    default:
1903	result = False;
1904	break;
1905    }
1906    return result;
1907}
1908#endif
1909
1910/***====================================================================***/
1911
1912typedef struct {
1913    Pixel fg;
1914    Pixel bg;
1915} ToSwap;
1916
1917#if OPT_HIGHLIGHT_COLOR
1918#define hc_param ,Bool hilite_color
1919#define hc_value ,screen->hilite_color
1920#else
1921#define hc_param		/* nothing */
1922#define hc_value		/* nothing */
1923#endif
1924
1925/*
1926 * Use this to swap the foreground/background color values in the resource
1927 * data, and to build up a list of the pairs which must be swapped in the
1928 * GC cache.
1929 */
1930static void
1931swapLocally(ToSwap * list, int *count, ColorRes * fg, ColorRes * bg hc_param)
1932{
1933    ColorRes tmp;
1934    int n;
1935    Boolean found = False;
1936
1937#if OPT_COLOR_RES
1938    Pixel fg_color = fg->value;
1939    Pixel bg_color = bg->value;
1940#else
1941    Pixel fg_color = *fg;
1942    Pixel bg_color = *bg;
1943#endif
1944
1945#if OPT_HIGHLIGHT_COLOR
1946    if ((fg_color != bg_color) || !hilite_color)
1947#endif
1948    {
1949	EXCHANGE(*fg, *bg, tmp);
1950	for (n = 0; n < *count; ++n) {
1951	    if ((list[n].fg == fg_color && list[n].bg == bg_color)
1952		|| (list[n].fg == bg_color && list[n].bg == fg_color)) {
1953		found = True;
1954		break;
1955	    }
1956	}
1957	if (!found) {
1958	    list[*count].fg = fg_color;
1959	    list[*count].bg = bg_color;
1960	    *count = *count + 1;
1961	    TRACE(("swapLocally fg %#lx, bg %#lx ->%d\n",
1962		   fg_color, bg_color, *count));
1963	}
1964    }
1965}
1966
1967static void
1968reallySwapColors(XtermWidget xw, ToSwap * list, int count)
1969{
1970    int j, k;
1971
1972    TRACE(("reallySwapColors\n"));
1973    for (j = 0; j < count; ++j) {
1974	for_each_text_gc(k) {
1975	    redoCgs(xw, list[j].fg, list[j].bg, (CgsEnum) k);
1976	}
1977    }
1978}
1979
1980static void
1981swapVTwinGCs(XtermWidget xw, VTwin * win)
1982{
1983    swapCgs(xw, win, gcNorm, gcNormReverse);
1984    swapCgs(xw, win, gcBold, gcBoldReverse);
1985}
1986
1987void
1988ReverseVideo(XtermWidget xw)
1989{
1990    TScreen *screen = &xw->screen;
1991    ToSwap listToSwap[5];
1992    int numToSwap = 0;
1993
1994    TRACE(("ReverseVideo\n"));
1995
1996    /*
1997     * Swap SGR foreground and background colors.  By convention, these are
1998     * the colors assigned to "black" (SGR #0) and "white" (SGR #7).  Also,
1999     * SGR #8 and SGR #15 are the bold (or bright) versions of SGR #0 and
2000     * #7, respectively.
2001     *
2002     * We don't swap colors that happen to match the screen's foreground
2003     * and background because that tends to produce bizarre effects.
2004     */
2005#define swapAnyColor(name,a,b) swapLocally(listToSwap, &numToSwap, &(screen->name[a]), &(screen->name[b]) hc_value)
2006#define swapAColor(a,b) swapAnyColor(Acolors, a, b)
2007    if_OPT_ISO_COLORS(screen, {
2008	swapAColor(0, 7);
2009	swapAColor(8, 15);
2010    });
2011
2012    if (T_COLOR(screen, TEXT_CURSOR) == T_COLOR(screen, TEXT_FG))
2013	T_COLOR(screen, TEXT_CURSOR) = T_COLOR(screen, TEXT_BG);
2014
2015#define swapTColor(a,b) swapAnyColor(Tcolors, a, b)
2016    swapTColor(TEXT_FG, TEXT_BG);
2017    swapTColor(MOUSE_FG, MOUSE_BG);
2018
2019    reallySwapColors(xw, listToSwap, numToSwap);
2020
2021    swapVTwinGCs(xw, &(screen->fullVwin));
2022#ifndef NO_ACTIVE_ICON
2023    swapVTwinGCs(xw, &(screen->iconVwin));
2024#endif /* NO_ACTIVE_ICON */
2025
2026    xw->misc.re_verse = (Boolean) ! xw->misc.re_verse;
2027
2028    if (XtIsRealized((Widget) xw)) {
2029	xtermDisplayCursor(xw);
2030    }
2031#if OPT_TEK4014
2032    if (TEK4014_SHOWN(xw)) {
2033	TekScreen *tekscr = &(tekWidget->screen);
2034	Window tekwin = TWindow(tekscr);
2035	recolor_cursor(screen,
2036		       tekscr->arrow,
2037		       T_COLOR(screen, MOUSE_FG),
2038		       T_COLOR(screen, MOUSE_BG));
2039	XDefineCursor(screen->display, tekwin, tekscr->arrow);
2040    }
2041#endif
2042
2043    if (screen->scrollWidget)
2044	ScrollBarReverseVideo(screen->scrollWidget);
2045
2046    if (XtIsRealized((Widget) xw)) {
2047	set_background(xw, -1);
2048    }
2049#if OPT_TEK4014
2050    TekReverseVideo(tekWidget);
2051#endif
2052    if (XtIsRealized((Widget) xw)) {
2053	xtermRepaint(xw);
2054    }
2055#if OPT_TEK4014
2056    if (TEK4014_SHOWN(xw)) {
2057	TekRepaint(tekWidget);
2058    }
2059#endif
2060    ReverseOldColors();
2061    set_cursor_gcs(xw);
2062    update_reversevideo();
2063    TRACE(("...ReverseVideo\n"));
2064}
2065
2066void
2067recolor_cursor(TScreen * screen,
2068	       Cursor cursor,	/* X cursor ID to set */
2069	       unsigned long fg,	/* pixel indexes to look up */
2070	       unsigned long bg)	/* pixel indexes to look up */
2071{
2072    Display *dpy = screen->display;
2073    XColor colordefs[2];	/* 0 is foreground, 1 is background */
2074
2075    colordefs[0].pixel = fg;
2076    colordefs[1].pixel = bg;
2077    XQueryColors(dpy, DefaultColormap(dpy, DefaultScreen(dpy)),
2078		 colordefs, 2);
2079    XRecolorCursor(dpy, cursor, colordefs, colordefs + 1);
2080    return;
2081}
2082
2083#if OPT_RENDERFONT
2084static XftColor *
2085getXftColor(XtermWidget xw, Pixel pixel)
2086{
2087#define CACHE_SIZE  4
2088    static struct {
2089	XftColor color;
2090	int use;
2091    } cache[CACHE_SIZE];
2092    static int use;
2093    int i;
2094    int oldest, oldestuse;
2095    XColor color;
2096
2097    oldestuse = 0x7fffffff;
2098    oldest = 0;
2099    for (i = 0; i < CACHE_SIZE; i++) {
2100	if (cache[i].use) {
2101	    if (cache[i].color.pixel == pixel) {
2102		cache[i].use = ++use;
2103		return &cache[i].color;
2104	    }
2105	}
2106	if (cache[i].use < oldestuse) {
2107	    oldestuse = cache[i].use;
2108	    oldest = i;
2109	}
2110    }
2111    i = oldest;
2112    color.pixel = pixel;
2113    XQueryColor(xw->screen.display, xw->core.colormap, &color);
2114    cache[i].color.color.red = color.red;
2115    cache[i].color.color.green = color.green;
2116    cache[i].color.color.blue = color.blue;
2117    cache[i].color.color.alpha = 0xffff;
2118    cache[i].color.pixel = pixel;
2119    cache[i].use = ++use;
2120    return &cache[i].color;
2121}
2122
2123/*
2124 * The cell-width is related to, but not the same as the wide-character width.
2125 * We will only get useful values from wcwidth() for codes above 255.
2126 * Otherwise, interpret according to internal data.
2127 */
2128#if OPT_RENDERWIDE
2129
2130#if OPT_C1_PRINT
2131#define XtermCellWidth(xw, ch) \
2132	(((ch) == 0 || (ch) == 127) \
2133	  ? 0 \
2134	  : (((ch) < 256) \
2135	      ? (((ch) >= 128 && (ch) < 160) \
2136	          ? ((xw)->screen.c1_printable ? 1 : 0) \
2137	          : 1) \
2138	      : my_wcwidth(ch)))
2139#else
2140#define XtermCellWidth(xw, ch) \
2141	(((ch) == 0 || (ch) == 127) \
2142	  ? 0 \
2143	  : (((ch) < 256) \
2144	      ? 1 \
2145	      : my_wcwidth(ch)))
2146#endif
2147
2148#endif /* OPT_RENDERWIDE */
2149
2150#define XFT_FONT(name) screen->name.font
2151
2152/*
2153 * fontconfig/Xft combination prior to 2.2 has a problem with
2154 * CJK truetype 'double-width' (bi-width/monospace) fonts leading
2155 * to the 's p a c e d o u t' rendering. Consequently, we can't
2156 * rely on XftDrawString8/16  when one of  those fonts is used.
2157 * Instead, we need to roll out our own using XftDrawCharSpec.
2158 * A patch in the same spirit (but in a rather different form)
2159 * was applied to gnome vte and gtk2 port of vim.
2160 * See http://bugzilla.mozilla.org/show_bug.cgi?id=196312
2161 */
2162static int
2163xtermXftDrawString(XtermWidget xw,
2164		   unsigned flags GCC_UNUSED,
2165		   XftColor * color,
2166		   XftFont * font,
2167		   int x,
2168		   int y,
2169		   IChar * text,
2170		   Cardinal len,
2171		   Bool really)
2172{
2173    TScreen *screen = &(xw->screen);
2174    int ncells = 0;
2175
2176    if (len != 0) {
2177#if OPT_RENDERWIDE
2178	XftCharSpec *sbuf;
2179	XftFont *wfont;
2180	Cardinal src, dst;
2181	XftFont *lastFont = 0;
2182	XftFont *currFont = 0;
2183	Cardinal start = 0;
2184	int charWidth;
2185	int fontnum = screen->menu_font_number;
2186	int fwidth = FontWidth(screen);
2187
2188#if OPT_ISO_COLORS
2189	if ((flags & UNDERLINE)
2190	    && screen->italicULMode
2191	    && XFT_FONT(renderWideItal[fontnum])) {
2192	    wfont = XFT_FONT(renderWideItal[fontnum]);
2193	} else
2194#endif
2195	    if ((flags & BOLDATTR(screen))
2196		&& XFT_FONT(renderWideBold[fontnum])) {
2197	    wfont = XFT_FONT(renderWideBold[fontnum]);
2198	} else {
2199	    wfont = XFT_FONT(renderWideNorm[fontnum]);
2200	}
2201
2202	BumpTypedBuffer(XftCharSpec, len);
2203	sbuf = BfBuf(XftCharSpec);
2204
2205	for (src = dst = 0; src < len; src++) {
2206	    FcChar32 wc = *text++;
2207
2208	    charWidth = XtermCellWidth(xw, (wchar_t) wc);
2209	    if (charWidth < 0)
2210		continue;
2211
2212	    sbuf[dst].ucs4 = wc;
2213	    sbuf[dst].x = (short) (x + fwidth * ncells);
2214	    sbuf[dst].y = (short) (y);
2215
2216	    currFont = (charWidth == 2 && wfont != 0) ? wfont : font;
2217	    ncells += charWidth;
2218
2219	    if (lastFont != currFont) {
2220		if ((lastFont != 0) && really) {
2221		    XftDrawCharSpec(screen->renderDraw,
2222				    color,
2223				    lastFont,
2224				    sbuf + start,
2225				    (int) (dst - start));
2226		}
2227		start = dst;
2228		lastFont = currFont;
2229	    }
2230	    ++dst;
2231	}
2232	if ((dst != start) && really) {
2233	    XftDrawCharSpec(screen->renderDraw,
2234			    color,
2235			    lastFont,
2236			    sbuf + start,
2237			    (int) (dst - start));
2238	}
2239#else /* !OPT_RENDERWIDE */
2240	if (really) {
2241	    XftChar8 *buffer;
2242	    int dst;
2243
2244	    BumpTypedBuffer(XftChar8, len);
2245	    buffer = BfBuf(XftChar8);
2246
2247	    for (dst = 0; dst < (int) len; ++dst)
2248		buffer[dst] = CharOf(text[dst]);
2249
2250	    XftDrawString8(screen->renderDraw,
2251			   color,
2252			   font,
2253			   x, y, buffer, (int) len);
2254	}
2255	ncells = (int) len;
2256#endif
2257    }
2258    return ncells;
2259}
2260#define xtermXftWidth(xw, flags, color, font, x, y, chars, len) \
2261   xtermXftDrawString(xw, flags, color, font, x, y, chars, len, False)
2262#endif /* OPT_RENDERFONT */
2263
2264#if OPT_WIDE_CHARS
2265/*
2266 * Map characters commonly "fixed" by groff back to their ASCII equivalents.
2267 * Also map other useful equivalents.
2268 */
2269unsigned
2270AsciiEquivs(unsigned ch)
2271{
2272    switch (ch) {
2273    case 0x2010:		/* groff "-" */
2274    case 0x2011:
2275    case 0x2012:
2276    case 0x2013:
2277    case 0x2014:
2278    case 0x2015:
2279    case 0x2212:		/* groff "\-" */
2280	ch = '-';
2281	break;
2282    case 0x2018:		/* groff "`" */
2283	ch = '`';
2284	break;
2285    case 0x2019:		/* groff ' */
2286	ch = '\'';
2287	break;
2288    case 0x201C:		/* groff lq */
2289    case 0x201D:		/* groff rq */
2290	ch = '"';
2291	break;
2292    case 0x2329:		/* groff ".URL" */
2293	ch = '<';
2294	break;
2295    case 0x232a:		/* groff ".URL" */
2296	ch = '>';
2297	break;
2298    default:
2299	if (ch >= 0xff01 && ch <= 0xff5e) {
2300	    /* "Fullwidth" codes (actually double-width) */
2301	    ch -= 0xff00;
2302	    ch += ANSI_SPA;
2303	    break;
2304	}
2305    }
2306    return ch;
2307}
2308
2309/*
2310 * Actually this should be called "groff_workaround()" - for the places where
2311 * groff stomps on compatibility.  Still, if enough people get used to it,
2312 * this might someday become a quasi-standard.
2313 */
2314static int
2315ucs_workaround(XtermWidget xw,
2316	       unsigned ch,
2317	       unsigned flags,
2318	       GC gc,
2319	       int x,
2320	       int y,
2321	       int chrset,
2322	       int on_wide)
2323{
2324    TScreen *screen = &(xw->screen);
2325    int fixed = False;
2326
2327    if (screen->wide_chars && screen->utf8_mode && ch > 256) {
2328	IChar eqv = (IChar) AsciiEquivs(ch);
2329
2330	if (eqv != (IChar) ch) {
2331	    int width = my_wcwidth((int) ch);
2332
2333	    do {
2334		drawXtermText(xw,
2335			      flags,
2336			      gc,
2337			      x,
2338			      y,
2339			      chrset,
2340			      &eqv,
2341			      1,
2342			      on_wide);
2343		x += FontWidth(screen);
2344		eqv = '?';
2345	    } while (width-- > 1);
2346
2347	    fixed = True;
2348	} else if (ch == HIDDEN_CHAR) {
2349	    fixed = True;
2350	}
2351    }
2352    return fixed;
2353}
2354#endif
2355
2356/*
2357 * Use this when the characters will not fill the cell area properly.  Fill the
2358 * area where we'll write the characters, otherwise we'll get gaps between
2359 * them, e.g., in the original background color.
2360 *
2361 * The cursor is a special case, because the XFillRectangle call only uses the
2362 * foreground, while we've set the cursor color in the background.  So we need
2363 * a special GC for that.
2364 */
2365static void
2366xtermFillCells(XtermWidget xw,
2367	       unsigned flags,
2368	       GC gc,
2369	       int x,
2370	       int y,
2371	       Cardinal len)
2372{
2373    TScreen *screen = &(xw->screen);
2374    VTwin *currentWin = WhichVWin(screen);
2375
2376    if (!(flags & NOBACKGROUND)) {
2377	CgsEnum srcId = getCgsId(xw, currentWin, gc);
2378	CgsEnum dstId = gcMAX;
2379	Pixel fg = getCgsFore(xw, currentWin, gc);
2380	Pixel bg = getCgsBack(xw, currentWin, gc);
2381
2382	switch (srcId) {
2383	case gcVTcursNormal:
2384	case gcVTcursReverse:
2385	    dstId = gcVTcursOutline;
2386	    break;
2387	case gcVTcursFilled:
2388	case gcVTcursOutline:
2389	    /* FIXME */
2390	    break;
2391	case gcNorm:
2392	    dstId = gcNormReverse;
2393	    break;
2394	case gcNormReverse:
2395	    dstId = gcNorm;
2396	    break;
2397	case gcBold:
2398	    dstId = gcBoldReverse;
2399	    break;
2400	case gcBoldReverse:
2401	    dstId = gcBold;
2402	    break;
2403#if OPT_BOX_CHARS
2404	case gcLine:
2405	case gcDots:
2406	    /* FIXME */
2407	    break;
2408#endif
2409#if OPT_DEC_CHRSET
2410	case gcCNorm:
2411	case gcCBold:
2412	    /* FIXME */
2413	    break;
2414#endif
2415#if OPT_WIDE_CHARS
2416	case gcWide:
2417	    dstId = gcWideReverse;
2418	    break;
2419	case gcWBold:
2420	    dstId = gcBoldReverse;
2421	    break;
2422	case gcWideReverse:
2423	case gcWBoldReverse:
2424	    /* FIXME */
2425	    break;
2426#endif
2427#if OPT_TEK4014
2428	case gcTKcurs:
2429	    /* FIXME */
2430	    break;
2431#endif
2432	case gcMAX:
2433	    break;
2434	}
2435
2436	if (dstId != gcMAX) {
2437	    setCgsFore(xw, currentWin, dstId, bg);
2438	    setCgsBack(xw, currentWin, dstId, fg);
2439
2440	    XFillRectangle(screen->display, VWindow(screen),
2441			   getCgsGC(xw, currentWin, dstId),
2442			   x, y,
2443			   len * (Cardinal) FontWidth(screen),
2444			   (unsigned) FontHeight(screen));
2445	}
2446    }
2447}
2448
2449#if OPT_TRACE
2450static void
2451xtermSetClipRectangles(Display * dpy,
2452		       GC gc,
2453		       int x,
2454		       int y,
2455		       XRectangle * rp,
2456		       Cardinal nr,
2457		       int order)
2458{
2459#if 0
2460    TScreen *screen = &(term->screen);
2461    Drawable draw = VWindow(screen);
2462
2463    XSetClipMask(dpy, gc, None);
2464    XDrawRectangle(screen->display, draw, gc,
2465		   x + rp->x - 1,
2466		   y + rp->y - 1,
2467		   rp->width,
2468		   rp->height);
2469#endif
2470
2471    XSetClipRectangles(dpy, gc,
2472		       x, y, rp, (int) nr, order);
2473    TRACE(("clipping @(%3d,%3d) (%3d,%3d)..(%3d,%3d)\n",
2474	   y, x,
2475	   rp->y, rp->x, rp->height, rp->width));
2476}
2477
2478#else
2479#define xtermSetClipRectangles(dpy, gc, x, y, rp, nr, order) \
2480	    XSetClipRectangles(dpy, gc, x, y, rp, (int) nr, order)
2481#endif
2482
2483#if OPT_CLIP_BOLD
2484/*
2485 * This special case is a couple of percent slower, but avoids a lot of pixel
2486 * trash in rxcurses' hanoi.cmd demo (e.g., 10x20 font).
2487 */
2488#define beginClipping(screen,gc,pwidth,plength) \
2489	    if (screen->use_clipping && (pwidth > 2)) { \
2490		XRectangle clip; \
2491		int clip_x = x; \
2492		int clip_y = y - FontHeight(screen) + FontDescent(screen); \
2493		clip.x = 0; \
2494		clip.y = 0; \
2495		clip.height = (unsigned short) FontHeight(screen); \
2496		clip.width = (unsigned short) (pwidth * plength); \
2497		xtermSetClipRectangles(screen->display, gc, \
2498				       clip_x, clip_y, \
2499				       &clip, 1, Unsorted); \
2500	    }
2501#define endClipping(screen,gc) \
2502	    XSetClipMask(screen->display, gc, None)
2503#else
2504#define beginClipping(screen,gc,pwidth,plength)		/* nothing */
2505#define endClipping(screen,gc)	/* nothing */
2506#endif /* OPT_CLIP_BOLD */
2507
2508#if OPT_CLIP_BOLD && OPT_RENDERFONT && defined(HAVE_XFTDRAWSETCLIP) && defined(HAVE_XFTDRAWSETCLIPRECTANGLES)
2509#define beginXftClipping(screen,px,py,plength) \
2510	    if (screen->use_clipping && (FontWidth(screen) > 2)) { \
2511		XRectangle clip; \
2512		int clip_x = px; \
2513		int clip_y = py - FontHeight(screen) + FontDescent(screen); \
2514		clip.x = 0; \
2515		clip.y = 0; \
2516		clip.height = (unsigned short) (FontHeight(screen)); \
2517		clip.width = (unsigned short) (FontWidth(screen) * plength); \
2518		XftDrawSetClipRectangles (screen->renderDraw, \
2519					  clip_x, clip_y, \
2520					  &clip, 1); \
2521	    }
2522#define endXftClipping(screen) \
2523	    XftDrawSetClip (screen->renderDraw, 0)
2524#else
2525#define beginXftClipping(screen,px,py,plength)	/* nothing */
2526#define endXftClipping(screen)	/* nothing */
2527#endif /* OPT_CLIP_BOLD */
2528
2529#if OPT_RENDERFONT
2530static int
2531drawClippedXftString(XtermWidget xw,
2532		     unsigned flags,
2533		     XftFont * font,
2534		     XftColor * fg_color,
2535		     int x,
2536		     int y,
2537		     IChar * text,
2538		     Cardinal len)
2539{
2540    int ncells = xtermXftWidth(xw, flags,
2541			       fg_color,
2542			       font, x, y,
2543			       text,
2544			       len);
2545    TScreen *screen = &(xw->screen);
2546
2547    beginXftClipping(screen, x, y, ncells);
2548    xtermXftDrawString(xw, flags,
2549		       fg_color,
2550		       font, x, y,
2551		       text,
2552		       len,
2553		       True);
2554    endXftClipping(screen);
2555    return ncells;
2556}
2557#endif
2558
2559#ifndef NO_ACTIVE_ICON
2560#define WhichVFontData(screen,name) \
2561		(IsIcon(screen) ? &((screen)->fnt_icon) \
2562				: &((screen)->name))
2563#else
2564#define WhichVFontData(screen,name) \
2565				(&((screen)->name))
2566#endif
2567
2568/*
2569 * Draws text with the specified combination of bold/underline.  The return
2570 * value is the updated x position.
2571 */
2572int
2573drawXtermText(XtermWidget xw,
2574	      unsigned flags,
2575	      GC gc,
2576	      int x,
2577	      int y,
2578	      int chrset,
2579	      IChar * text,
2580	      Cardinal len,
2581	      int on_wide)
2582{
2583    TScreen *screen = &(xw->screen);
2584    Cardinal real_length = len;
2585    Cardinal underline_len = 0;
2586    /* Intended width of the font to draw (as opposed to the actual width of
2587       the X font, and the width of the default font) */
2588    int font_width = ((flags & DOUBLEWFONT) ? 2 : 1) * screen->fnt_wide;
2589    Bool did_ul = False;
2590
2591#if OPT_WIDE_CHARS
2592    if (text == 0)
2593	return 0;
2594#endif
2595#if OPT_DEC_CHRSET
2596    if (CSET_DOUBLE(chrset)) {
2597	/* We could try drawing double-size characters in the icon, but
2598	 * given that the icon font is usually nil or nil2, there
2599	 * doesn't seem to be much point.
2600	 */
2601	int inx = 0;
2602	GC gc2 = ((!IsIcon(screen) && screen->font_doublesize)
2603		  ? xterm_DoubleGC(xw, (unsigned) chrset, flags, gc, &inx)
2604		  : 0);
2605
2606	TRACE(("DRAWTEXT%c[%4d,%4d] (%d)%3d:%s\n",
2607	       screen->cursor_state == OFF ? ' ' : '*',
2608	       y, x, chrset, len,
2609	       visibleIChars(text, len)));
2610
2611	if (gc2 != 0) {		/* draw actual double-sized characters */
2612	    XFontStruct *fs = screen->double_fonts[inx].fs;
2613
2614#if OPT_RENDERFONT
2615	    if (!xw->misc.render_font || IsIconWin(screen, WhichVWin(screen)))
2616#endif
2617	    {
2618		XRectangle rect, *rp = &rect;
2619		Cardinal nr = 1;
2620
2621		font_width *= 2;
2622		flags |= DOUBLEWFONT;
2623
2624		rect.x = 0;
2625		rect.y = 0;
2626		rect.width = (unsigned short) ((int) len * font_width);
2627		rect.height = (unsigned short) (FontHeight(screen));
2628
2629		TRACE(("drawing %s\n", visibleChrsetName((unsigned) chrset)));
2630		switch (chrset) {
2631		case CSET_DHL_TOP:
2632		    rect.y = (short) -(fs->ascent / 2);
2633		    y -= rect.y;
2634		    flags |= DOUBLEHFONT;
2635		    break;
2636		case CSET_DHL_BOT:
2637		    rect.y = (short) (rect.height - (fs->ascent / 2));
2638		    y -= rect.y;
2639		    flags |= DOUBLEHFONT;
2640		    break;
2641		default:
2642		    nr = 0;
2643		    break;
2644		}
2645
2646		if (nr) {
2647		    xtermSetClipRectangles(screen->display, gc2,
2648					   x, y, rp, nr, YXBanded);
2649		} else {
2650		    XSetClipMask(screen->display, gc2, None);
2651		}
2652	    }
2653
2654	    /* Call ourselves recursively with the new gc */
2655
2656	    /*
2657	     * If we're trying to use proportional font, or if the
2658	     * font server didn't give us what we asked for wrt
2659	     * width, position each character independently.
2660	     */
2661	    if (screen->fnt_prop
2662		|| (fs->min_bounds.width != fs->max_bounds.width)
2663		|| (fs->min_bounds.width != 2 * FontWidth(screen))) {
2664		/* It is hard to fall-through to the main
2665		   branch: in a lot of places the check
2666		   for the cached font info is for
2667		   normal/bold fonts only. */
2668		while (len--) {
2669		    x = drawXtermText(xw, flags, gc2,
2670				      x, y, 0,
2671				      text++,
2672				      1, on_wide);
2673		    x += FontWidth(screen);
2674		}
2675	    } else {
2676		x = drawXtermText(xw, flags, gc2,
2677				  x, y, 0,
2678				  text,
2679				  len, on_wide);
2680		x += (int) len *FontWidth(screen);
2681	    }
2682
2683	    TRACE(("drawtext [%4d,%4d]\n", y, x));
2684	} else {		/* simulate double-sized characters */
2685	    unsigned need = 2 * len;
2686	    IChar *temp = TypeMallocN(IChar, need);
2687	    unsigned n = 0;
2688
2689	    while (len--) {
2690		temp[n++] = *text++;
2691		temp[n++] = ' ';
2692	    }
2693	    x = drawXtermText(xw,
2694			      flags,
2695			      gc,
2696			      x, y,
2697			      0,
2698			      temp,
2699			      n,
2700			      on_wide);
2701	    free(temp);
2702	}
2703	return x;
2704    }
2705#endif
2706#if OPT_RENDERFONT
2707    if (UsingRenderFont(xw)) {
2708	VTwin *currentWin = WhichVWin(screen);
2709	Display *dpy = screen->display;
2710	XftFont *font;
2711	XGCValues values;
2712	int fontnum = screen->menu_font_number;
2713	int ncells;
2714
2715	if (!screen->renderDraw) {
2716	    int scr;
2717	    Drawable draw = VWindow(screen);
2718	    Visual *visual;
2719
2720	    scr = DefaultScreen(dpy);
2721	    visual = DefaultVisual(dpy, scr);
2722	    screen->renderDraw = XftDrawCreate(dpy, draw, visual,
2723					       DefaultColormap(dpy, scr));
2724	}
2725#if OPT_ISO_COLORS
2726	if ((flags & UNDERLINE)
2727	    && screen->italicULMode
2728	    && XFT_FONT(renderFontItal[fontnum])) {
2729	    font = XFT_FONT(renderFontItal[fontnum]);
2730	    did_ul = True;
2731	} else
2732#endif
2733	    if ((flags & BOLDATTR(screen))
2734		&& XFT_FONT(renderFontBold[fontnum])) {
2735	    font = XFT_FONT(renderFontBold[fontnum]);
2736	} else {
2737	    font = XFT_FONT(renderFontNorm[fontnum]);
2738	}
2739	values.foreground = getCgsFore(xw, currentWin, gc);
2740	values.background = getCgsBack(xw, currentWin, gc);
2741
2742	if (!(flags & NOBACKGROUND)) {
2743	    XftColor *bg_color = getXftColor(xw, values.background);
2744	    ncells = xtermXftWidth(xw, flags,
2745				   bg_color,
2746				   font, x, y,
2747				   text,
2748				   len);
2749	    XftDrawRect(screen->renderDraw,
2750			bg_color,
2751			x, y,
2752			(unsigned) (ncells * FontWidth(screen)),
2753			(unsigned) FontHeight(screen));
2754	}
2755
2756	y += font->ascent;
2757#if OPT_BOX_CHARS
2758	{
2759	    /* adding code to substitute simulated line-drawing characters */
2760	    int last, first = 0;
2761	    Dimension old_wide, old_high = 0;
2762	    int curX = x;
2763
2764	    for (last = 0; last < (int) len; last++) {
2765		Boolean replace = False;
2766		Boolean missing = False;
2767		unsigned ch = (unsigned) text[last];
2768		int nc;
2769#if OPT_WIDE_CHARS
2770
2771		if (xtermIsDecGraphic(ch)) {
2772		    /*
2773		     * Xft generally does not have the line-drawing characters
2774		     * in cells 1-31.  Check for this, and attempt to fill in
2775		     * from real line-drawing character in the font at the
2776		     * Unicode position.  Failing that, use our own
2777		     * box-characters.
2778		     */
2779		    if (xtermXftMissing(xw, font, ch)) {
2780			if (screen->force_box_chars
2781			    || xtermXftMissing(xw, font, dec2ucs(ch))) {
2782			    missing = 1;
2783			} else {
2784			    ch = dec2ucs(ch);
2785			    replace = True;
2786			}
2787		    }
2788		} else if (ch > 256) {
2789		    /*
2790		     * If we're reading UTF-8 from the client, we may have a
2791		     * line-drawing character.  Translate it back to our
2792		     * box-code if Xft tells us that the glyph is missing.
2793		     */
2794		    if_OPT_WIDE_CHARS(screen, {
2795			unsigned part = ucs2dec(ch);
2796			if (xtermIsDecGraphic(part) &&
2797			    (screen->force_box_chars
2798			     || xtermXftMissing(xw, font, ch))) {
2799			    ch = part;
2800			    missing = True;
2801			}
2802		    });
2803		}
2804#else
2805		if (xtermIsDecGraphic(ch)) {
2806		    /*
2807		     * Xft generally does not have the line-drawing characters
2808		     * in cells 1-31.  Check for this, and attempt to fill in
2809		     * from real line-drawing character in the font at the
2810		     * Unicode position.  Failing that, use our own
2811		     * box-characters.
2812		     */
2813		    if (xtermXftMissing(xw, font, ch)) {
2814			missing = 1;
2815		    }
2816		}
2817#endif
2818
2819		/*
2820		 * If we now have one of our box-codes, draw it directly.
2821		 */
2822		if (missing || replace) {
2823		    /* line drawing character time */
2824		    if (last > first) {
2825			nc = drawClippedXftString(xw,
2826						  flags,
2827						  font,
2828						  getXftColor(xw, values.foreground),
2829						  curX,
2830						  y,
2831						  text + first,
2832						  (Cardinal) (last - first));
2833			curX += nc * FontWidth(screen);
2834			underline_len += (Cardinal) nc;
2835		    }
2836		    if (missing) {
2837			old_wide = screen->fnt_wide;
2838			old_high = screen->fnt_high;
2839			screen->fnt_wide = (Dimension) FontWidth(screen);
2840			screen->fnt_high = (Dimension) FontHeight(screen);
2841			xtermDrawBoxChar(xw, ch, flags, gc,
2842					 curX, y - FontAscent(screen), 1);
2843			curX += FontWidth(screen);
2844			underline_len += 1;
2845			screen->fnt_wide = old_wide;
2846			screen->fnt_high = old_high;
2847		    } else {
2848			IChar ch2 = (IChar) ch;
2849			nc = drawClippedXftString(xw,
2850						  flags,
2851						  font,
2852						  getXftColor(xw, values.foreground),
2853						  curX,
2854						  y,
2855						  &ch2,
2856						  1);
2857			curX += nc * FontWidth(screen);
2858			underline_len += (Cardinal) nc;
2859		    }
2860		    first = last + 1;
2861		}
2862	    }
2863	    if (last > first) {
2864		underline_len += (Cardinal)
2865		    drawClippedXftString(xw,
2866					 flags,
2867					 font,
2868					 getXftColor(xw, values.foreground),
2869					 curX,
2870					 y,
2871					 text + first,
2872					 (Cardinal) (last - first));
2873	    }
2874	}
2875#else
2876	{
2877	    underline_len += (Cardinal)
2878		drawClippedXftString(xw,
2879				     flags,
2880				     font,
2881				     getXftColor(xw, values.foreground),
2882				     x,
2883				     y,
2884				     text,
2885				     len);
2886	}
2887#endif /* OPT_BOX_CHARS */
2888
2889	if ((flags & UNDERLINE) && screen->underline && !did_ul) {
2890	    if (FontDescent(screen) > 1)
2891		y++;
2892	    XDrawLine(screen->display, VWindow(screen), gc,
2893		      x, y,
2894		      x + (int) underline_len * FontWidth(screen) - 1,
2895		      y);
2896	}
2897	return x + (int) len *FontWidth(screen);
2898    }
2899#endif /* OPT_RENDERFONT */
2900    /*
2901     * If we're asked to display a proportional font, do this with a fixed
2902     * pitch.  Yes, it's ugly.  But we cannot distinguish the use of xterm
2903     * as a dumb terminal vs its use as in fullscreen programs such as vi.
2904     * Hint: do not try to use a proportional font in the icon.
2905     */
2906    if (!IsIcon(screen) && !(flags & CHARBYCHAR) && screen->fnt_prop) {
2907	int adj, width;
2908	XTermFonts *font = ((flags & BOLDATTR(screen))
2909			    ? WhichVFontData(screen, fnts[fBold])
2910			    : WhichVFontData(screen, fnts[fNorm]));
2911
2912	xtermFillCells(xw, flags, gc, x, y, len);
2913
2914	while (len--) {
2915#if OPT_BOX_CHARS
2916	    if (IsXtermMissingChar(screen, *text, font)) {
2917
2918		width = 1;
2919		if_OPT_WIDE_CHARS(screen, {
2920		    width = my_wcwidth((wchar_t) (*text)) * FontWidth(screen);
2921		});
2922		adj = 0;
2923	    } else
2924#endif
2925	    {
2926		if_WIDE_OR_NARROW(screen, {
2927		    XChar2b temp[1];
2928		    temp[0].byte2 = LO_BYTE(*text);
2929		    temp[0].byte1 = HI_BYTE(*text);
2930		    width = XTextWidth16(font->fs, temp, 1);
2931		}
2932		, {
2933		    char temp[1];
2934		    temp[0] = (char) LO_BYTE(*text);
2935		    width = XTextWidth(font->fs, temp, 1);
2936		});
2937		adj = (FontWidth(screen) - width) / 2;
2938	    }
2939	    x = drawXtermText(xw,
2940			      flags | NOBACKGROUND | CHARBYCHAR,
2941			      gc, x + adj, y, chrset,
2942			      text++, 1, on_wide) - adj;
2943	}
2944	return x;
2945    }
2946#if OPT_BOX_CHARS
2947    /* If the font is incomplete, draw some substitutions */
2948    if (!IsIcon(screen)
2949	&& !(flags & NOTRANSLATION)
2950	&& (!screen->fnt_boxes || screen->force_box_chars)) {
2951	/* Fill in missing box-characters.
2952	   Find regions without missing characters, and draw
2953	   them calling ourselves recursively.  Draw missing
2954	   characters via xtermDrawBoxChar(). */
2955	XTermFonts *font = ((flags & BOLDATTR(screen))
2956			    ? WhichVFontData(screen, fnts[fBold])
2957			    : WhichVFontData(screen, fnts[fNorm]));
2958	int last, first = 0;
2959	Bool drewBoxes = False;
2960
2961	for (last = 0; last < (int) len; last++) {
2962	    unsigned ch = (unsigned) text[last];
2963	    Bool isMissing;
2964	    int ch_width;
2965#if OPT_WIDE_CHARS
2966
2967	    if (ch == HIDDEN_CHAR) {
2968		if (last > first) {
2969		    x = drawXtermText(xw, flags | NOTRANSLATION, gc,
2970				      x, y,
2971				      chrset, text + first,
2972				      (unsigned) (last - first), on_wide);
2973		}
2974		first = last + 1;
2975		drewBoxes = True;
2976		continue;
2977	    }
2978	    ch_width = my_wcwidth((int) ch);
2979	    isMissing =
2980		IsXtermMissingChar(screen, ch,
2981				   ((on_wide || ch_width > 1)
2982				    && okFont(NormalWFont(screen)))
2983				   ? WhichVFontData(screen, fnts[fWide])
2984				   : font);
2985#else
2986	    isMissing = IsXtermMissingChar(screen, ch, font);
2987	    ch_width = 1;
2988#endif
2989	    /*
2990	     * If the character is not missing, but we're in wide-character
2991	     * mode and the character happens to be a wide-character that
2992	     * corresponds to the line-drawing set, allow the forceBoxChars
2993	     * resource (or menu entry) to force it to display using our
2994	     * tables.
2995	     */
2996	    if_OPT_WIDE_CHARS(screen, {
2997		if (!isMissing
2998		    && ch > 255
2999		    && ucs2dec(ch) < 32
3000		    && xw->screen.force_box_chars) {
3001		    ch = ucs2dec(ch);
3002		    isMissing = True;
3003		}
3004	    });
3005
3006	    if (isMissing) {
3007		if (last > first) {
3008		    x = drawXtermText(xw, flags | NOTRANSLATION, gc,
3009				      x, y,
3010				      chrset, text + first,
3011				      (unsigned) (last - first), on_wide);
3012		}
3013#if OPT_WIDE_CHARS
3014		if (ucs_workaround(xw, ch, flags, gc,
3015				   x, y,
3016				   chrset, on_wide)) {
3017		    /*
3018		     * if true, we drew at least one cell whether or not it is
3019		     * printable
3020		     */
3021		    if (ch_width <= 0)
3022			ch_width = 1;
3023		} else
3024#endif
3025		{
3026		    if (ch_width <= 0)
3027			ch_width = 1;
3028		    xtermDrawBoxChar(xw, ch, flags, gc,
3029				     x, y,
3030				     ch_width);
3031		}
3032		x += (ch_width * FontWidth(screen));
3033		first = last + 1;
3034		drewBoxes = True;
3035	    }
3036	}
3037	if (last <= first) {
3038	    return x;
3039	}
3040	text += first;
3041	len = (Cardinal) (last - first);
3042	flags |= NOTRANSLATION;
3043	if (drewBoxes) {
3044	    return drawXtermText(xw,
3045				 flags,
3046				 gc,
3047				 x,
3048				 y,
3049				 chrset,
3050				 text,
3051				 len,
3052				 on_wide);
3053	}
3054    }
3055#endif /* OPT_BOX_CHARS */
3056    /*
3057     * Behave as if the font has (maybe Unicode-replacements for) drawing
3058     * characters in the range 1-31 (either we were not asked to ignore them,
3059     * or the caller made sure that there is none).
3060     */
3061    TRACE(("drawtext%c[%4d,%4d] (%d) %d:%s\n",
3062	   screen->cursor_state == OFF ? ' ' : '*',
3063	   y, x, chrset, len,
3064	   visibleIChars(text, len)));
3065    y += FontAscent(screen);
3066
3067#if OPT_WIDE_CHARS
3068
3069    if (screen->wide_chars || screen->unicode_font) {
3070	XChar2b *buffer;
3071	Bool needWide = False;
3072	int ascent_adjust = 0;
3073	int src, dst;
3074
3075	BumpTypedBuffer(XChar2b, len);
3076	buffer = BfBuf(XChar2b);
3077
3078	for (src = dst = 0; src < (int) len; src++) {
3079	    IChar ch = text[src];
3080
3081	    if (ch == HIDDEN_CHAR)
3082		continue;
3083
3084	    if (!needWide
3085		&& !IsIcon(screen)
3086		&& ((on_wide || my_wcwidth((int) ch) > 1)
3087		    && okFont(NormalWFont(screen)))) {
3088		needWide = True;
3089	    }
3090
3091	    /*
3092	     * bitmap-fonts are limited to 16-bits.
3093	     */
3094#if OPT_WIDER_ICHAR
3095	    if (ch > 0xffff) {
3096		ch = UCS_REPL;
3097	    }
3098#endif
3099	    buffer[dst].byte2 = LO_BYTE(ch);
3100	    buffer[dst].byte1 = HI_BYTE(ch);
3101#if OPT_MINI_LUIT
3102#define UCS2SBUF(value)	buffer[dst].byte2 = LO_BYTE(value);\
3103	    		buffer[dst].byte1 = HI_BYTE(value)
3104
3105#define Map2Sbuf(from,to) (text[src] == from) { UCS2SBUF(to); }
3106
3107	    if (screen->latin9_mode && !screen->utf8_mode && text[src] < 256) {
3108
3109		/* see http://www.cs.tut.fi/~jkorpela/latin9.html */
3110		/* *INDENT-OFF* */
3111		if Map2Sbuf(0xa4, 0x20ac)
3112		else if Map2Sbuf(0xa6, 0x0160)
3113		else if Map2Sbuf(0xa8, 0x0161)
3114		else if Map2Sbuf(0xb4, 0x017d)
3115		else if Map2Sbuf(0xb8, 0x017e)
3116		else if Map2Sbuf(0xbc, 0x0152)
3117		else if Map2Sbuf(0xbd, 0x0153)
3118		else if Map2Sbuf(0xbe, 0x0178)
3119		/* *INDENT-ON* */
3120
3121	    }
3122	    if (screen->unicode_font
3123		&& (text[src] == ANSI_DEL ||
3124		    text[src] < ANSI_SPA)) {
3125		unsigned ni = dec2ucs((unsigned) ((text[src] == ANSI_DEL)
3126						  ? 0
3127						  : text[src]));
3128		UCS2SBUF(ni);
3129	    }
3130#endif /* OPT_MINI_LUIT */
3131	    ++dst;
3132	}
3133	/* FIXME This is probably wrong. But it works. */
3134	underline_len = len;
3135
3136	/* Set the drawing font */
3137	if (!(flags & (DOUBLEHFONT | DOUBLEWFONT))) {
3138	    VTwin *currentWin = WhichVWin(screen);
3139	    VTFontEnum fntId;
3140	    CgsEnum cgsId;
3141	    Pixel fg = getCgsFore(xw, currentWin, gc);
3142	    Pixel bg = getCgsBack(xw, currentWin, gc);
3143
3144	    if (needWide
3145		&& (okFont(NormalWFont(screen)) || okFont(BoldWFont(screen)))) {
3146		if ((flags & BOLDATTR(screen)) != 0
3147		    && okFont(BoldWFont(screen))) {
3148		    fntId = fWBold;
3149		    cgsId = gcWBold;
3150		} else {
3151		    fntId = fWide;
3152		    cgsId = gcWide;
3153		}
3154	    } else if ((flags & BOLDATTR(screen)) != 0
3155		       && okFont(BoldFont(screen))) {
3156		fntId = fBold;
3157		cgsId = gcBold;
3158	    } else {
3159		fntId = fNorm;
3160		cgsId = gcNorm;
3161	    }
3162
3163	    setCgsFore(xw, currentWin, cgsId, fg);
3164	    setCgsBack(xw, currentWin, cgsId, bg);
3165	    gc = getCgsGC(xw, currentWin, cgsId);
3166
3167	    if (fntId != fNorm) {
3168		XFontStruct *thisFp = WhichVFont(screen, fnts[fntId].fs);
3169		ascent_adjust = (thisFp->ascent
3170				 - NormalFont(screen)->ascent);
3171		if (thisFp->max_bounds.width ==
3172		    NormalFont(screen)->max_bounds.width * 2) {
3173		    underline_len = real_length = (Cardinal) (dst * 2);
3174		} else if (cgsId == gcWide || cgsId == gcWBold) {
3175		    underline_len = real_length = (Cardinal) (dst * 2);
3176		    xtermFillCells(xw,
3177				   flags,
3178				   gc,
3179				   x,
3180				   y - thisFp->ascent,
3181				   real_length);
3182		}
3183	    }
3184	}
3185
3186	if (flags & NOBACKGROUND) {
3187	    XDrawString16(screen->display,
3188			  VWindow(screen), gc,
3189			  x, y + ascent_adjust,
3190			  buffer, dst);
3191	} else {
3192	    XDrawImageString16(screen->display,
3193			       VWindow(screen), gc,
3194			       x, y + ascent_adjust,
3195			       buffer, dst);
3196	}
3197
3198	if ((flags & BOLDATTR(screen)) && screen->enbolden) {
3199	    beginClipping(screen, gc, (Cardinal) font_width, len);
3200	    XDrawString16(screen->display, VWindow(screen), gc,
3201			  x + 1,
3202			  y + ascent_adjust,
3203			  buffer, dst);
3204	    endClipping(screen, gc);
3205	}
3206
3207    } else
3208#endif /* OPT_WIDE_CHARS */
3209    {
3210	int length = (int) len;	/* X should have used unsigned */
3211#if OPT_WIDE_CHARS
3212	char *buffer;
3213	int dst;
3214
3215	BumpTypedBuffer(char, len);
3216	buffer = BfBuf(char);
3217
3218	for (dst = 0; dst < length; ++dst)
3219	    buffer[dst] = (char) LO_BYTE(text[dst]);
3220#else
3221	char *buffer = (char *) text;
3222#endif
3223
3224	if (flags & NOBACKGROUND) {
3225	    XDrawString(screen->display, VWindow(screen), gc,
3226			x, y, buffer, length);
3227	} else {
3228	    XDrawImageString(screen->display, VWindow(screen), gc,
3229			     x, y, buffer, length);
3230	}
3231	underline_len = (Cardinal) length;
3232	if ((flags & BOLDATTR(screen)) && screen->enbolden) {
3233	    beginClipping(screen, gc, font_width, length);
3234	    XDrawString(screen->display, VWindow(screen), gc,
3235			x + 1, y, buffer, length);
3236	    endClipping(screen, gc);
3237	}
3238    }
3239
3240    if ((flags & UNDERLINE) && screen->underline && !did_ul) {
3241	if (FontDescent(screen) > 1)
3242	    y++;
3243	XDrawLine(screen->display, VWindow(screen), gc,
3244		  x, y, (x + (int) underline_len * font_width - 1), y);
3245    }
3246
3247    return x + (int) real_length *FontWidth(screen);
3248}
3249
3250#if OPT_WIDE_CHARS
3251/*
3252 * Allocate buffer - workaround for wide-character interfaces.
3253 */
3254void
3255allocXtermChars(ScrnPtr * buffer, Cardinal length)
3256{
3257    if (*buffer == 0) {
3258	*buffer = (ScrnPtr) XtMalloc(length);
3259    } else {
3260	*buffer = (ScrnPtr) XtRealloc((char *) *buffer, length);
3261    }
3262}
3263#endif
3264
3265/* set up size hints for window manager; min 1 char by 1 char */
3266void
3267xtermSizeHints(XtermWidget xw, int scrollbarWidth)
3268{
3269    TScreen *screen = &xw->screen;
3270
3271    TRACE(("xtermSizeHints\n"));
3272    TRACE(("   border    %d\n", xw->core.border_width));
3273    TRACE(("   scrollbar %d\n", scrollbarWidth));
3274
3275    xw->hints.base_width = 2 * screen->border + scrollbarWidth;
3276    xw->hints.base_height = 2 * screen->border;
3277
3278#if OPT_TOOLBAR
3279    TRACE(("   toolbar   %d\n", ToolbarHeight(xw)));
3280
3281    xw->hints.base_height += ToolbarHeight(xw);
3282    xw->hints.base_height += BorderWidth(xw) * 2;
3283    xw->hints.base_width += BorderWidth(xw) * 2;
3284#endif
3285
3286    xw->hints.width_inc = FontWidth(screen);
3287    xw->hints.height_inc = FontHeight(screen);
3288    xw->hints.min_width = xw->hints.base_width + xw->hints.width_inc;
3289    xw->hints.min_height = xw->hints.base_height + xw->hints.height_inc;
3290
3291    xw->hints.width = MaxCols(screen) * FontWidth(screen) + xw->hints.min_width;
3292    xw->hints.height = MaxRows(screen) * FontHeight(screen) + xw->hints.min_height;
3293
3294    xw->hints.flags |= (PSize | PBaseSize | PMinSize | PResizeInc);
3295
3296    TRACE_HINTS(&(xw->hints));
3297}
3298
3299void
3300getXtermSizeHints(XtermWidget xw)
3301{
3302    TScreen *screen = &xw->screen;
3303    long supp;
3304
3305    if (!XGetWMNormalHints(screen->display, XtWindow(SHELL_OF(xw)),
3306			   &xw->hints, &supp))
3307	memset(&xw->hints, 0, sizeof(xw->hints));
3308    TRACE_HINTS(&(xw->hints));
3309}
3310
3311/*
3312 * Returns a GC, selected according to the font (reverse/bold/normal) that is
3313 * required for the current position (implied).  The GC is updated with the
3314 * current screen foreground and background colors.
3315 */
3316GC
3317updatedXtermGC(XtermWidget xw, unsigned flags, CellColor fg_bg, Bool hilite)
3318{
3319    TScreen *screen = &(xw->screen);
3320    VTwin *win = WhichVWin(screen);
3321    CgsEnum cgsId = gcMAX;
3322    unsigned my_fg = extract_fg(xw, fg_bg, flags);
3323    unsigned my_bg = extract_bg(xw, fg_bg, flags);
3324    Pixel fg_pix = getXtermForeground(xw, flags, my_fg);
3325    Pixel bg_pix = getXtermBackground(xw, flags, my_bg);
3326    Pixel xx_pix;
3327#if OPT_HIGHLIGHT_COLOR
3328    Pixel selbg_pix = T_COLOR(screen, HIGHLIGHT_BG);
3329    Pixel selfg_pix = T_COLOR(screen, HIGHLIGHT_FG);
3330    Boolean always = screen->hilite_color;
3331    Boolean use_selbg = (Boolean) (always ||
3332				   isNotForeground(xw, fg_pix, bg_pix, selbg_pix));
3333    Boolean use_selfg = (Boolean) (always &&
3334				   isNotBackground(xw, fg_pix, bg_pix, selfg_pix));
3335#endif
3336
3337    (void) fg_bg;
3338    (void) my_bg;
3339    (void) my_fg;
3340
3341    checkVeryBoldColors(flags, my_fg);
3342
3343    if (ReverseOrHilite(screen, flags, hilite)) {
3344	if (flags & BOLDATTR(screen)) {
3345	    cgsId = gcBoldReverse;
3346	} else {
3347	    cgsId = gcNormReverse;
3348	}
3349
3350#if OPT_HIGHLIGHT_COLOR
3351	if (!screen->hilite_color) {
3352	    if (selbg_pix != T_COLOR(screen, TEXT_FG)
3353		&& selbg_pix != fg_pix
3354		&& selbg_pix != bg_pix
3355		&& selbg_pix != xw->dft_foreground) {
3356		bg_pix = fg_pix;
3357		fg_pix = selbg_pix;
3358	    }
3359	}
3360#endif
3361	EXCHANGE(fg_pix, bg_pix, xx_pix);
3362#if OPT_HIGHLIGHT_COLOR
3363	if (screen->hilite_color) {
3364	    if (screen->hilite_reverse) {
3365		if (use_selbg) {
3366		    if (use_selfg)
3367			bg_pix = fg_pix;
3368		    else
3369			fg_pix = bg_pix;
3370		}
3371		if (use_selbg)
3372		    bg_pix = selbg_pix;
3373		if (use_selfg)
3374		    fg_pix = selfg_pix;
3375	    }
3376	}
3377#endif
3378    } else {
3379	if (flags & BOLDATTR(screen)) {
3380	    cgsId = gcBold;
3381	} else {
3382	    cgsId = gcNorm;
3383	}
3384    }
3385#if OPT_HIGHLIGHT_COLOR
3386    if (!screen->hilite_color || !screen->hilite_reverse) {
3387	if (hilite && !screen->hilite_reverse) {
3388	    if (use_selbg)
3389		bg_pix = selbg_pix;
3390	    if (use_selfg)
3391		fg_pix = selfg_pix;
3392	}
3393    }
3394#endif
3395
3396#if OPT_BLINK_TEXT
3397    if ((screen->blink_state == ON) && (!screen->blink_as_bold) && (flags & BLINK)) {
3398	fg_pix = bg_pix;
3399    }
3400#endif
3401
3402    setCgsFore(xw, win, cgsId, fg_pix);
3403    setCgsBack(xw, win, cgsId, bg_pix);
3404    return getCgsGC(xw, win, cgsId);
3405}
3406
3407/*
3408 * Resets the foreground/background of the GC returned by 'updatedXtermGC()'
3409 * to the values that would be set in SGR_Foreground and SGR_Background. This
3410 * duplicates some logic, but only modifies 1/4 as many GC's.
3411 */
3412void
3413resetXtermGC(XtermWidget xw, unsigned flags, Bool hilite)
3414{
3415    TScreen *screen = &(xw->screen);
3416    VTwin *win = WhichVWin(screen);
3417    CgsEnum cgsId = gcMAX;
3418    Pixel fg_pix = getXtermForeground(xw, flags, xw->cur_foreground);
3419    Pixel bg_pix = getXtermBackground(xw, flags, xw->cur_background);
3420
3421    checkVeryBoldColors(flags, xw->cur_foreground);
3422
3423    if (ReverseOrHilite(screen, flags, hilite)) {
3424	if (flags & BOLDATTR(screen)) {
3425	    cgsId = gcBoldReverse;
3426	} else {
3427	    cgsId = gcNormReverse;
3428	}
3429
3430	setCgsFore(xw, win, cgsId, bg_pix);
3431	setCgsBack(xw, win, cgsId, fg_pix);
3432
3433    } else {
3434	if (flags & BOLDATTR(screen)) {
3435	    cgsId = gcBold;
3436	} else {
3437	    cgsId = gcNorm;
3438	}
3439
3440	setCgsFore(xw, win, cgsId, fg_pix);
3441	setCgsBack(xw, win, cgsId, bg_pix);
3442    }
3443}
3444
3445#if OPT_ISO_COLORS
3446/*
3447 * Extract the foreground-color index from a one-byte color pair.  If we've got
3448 * BOLD or UNDERLINE color-mode active, those will be used.
3449 */
3450unsigned
3451extract_fg(XtermWidget xw, CellColor color, unsigned flags)
3452{
3453    unsigned fg = ExtractForeground(color);
3454
3455    if (xw->screen.colorAttrMode
3456	|| (fg == ExtractBackground(color))) {
3457	if (xw->screen.colorULMode && (flags & UNDERLINE))
3458	    fg = COLOR_UL;
3459	if (xw->screen.colorBDMode && (flags & BOLD))
3460	    fg = COLOR_BD;
3461	if (xw->screen.colorBLMode && (flags & BLINK))
3462	    fg = COLOR_BL;
3463    }
3464    return fg;
3465}
3466
3467/*
3468 * Extract the background-color index from a one-byte color pair.
3469 * If we've got INVERSE color-mode active, that will be used.
3470 */
3471unsigned
3472extract_bg(XtermWidget xw, CellColor color, unsigned flags)
3473{
3474    unsigned bg = ExtractBackground(color);
3475
3476    if (xw->screen.colorAttrMode
3477	|| (bg == ExtractForeground(color))) {
3478	if (xw->screen.colorRVMode && (flags & INVERSE))
3479	    bg = COLOR_RV;
3480    }
3481    return bg;
3482}
3483
3484/*
3485 * Combine the current foreground and background into a single 8-bit number.
3486 * Note that we're storing the SGR foreground, since cur_foreground may be set
3487 * to COLOR_UL, COLOR_BD or COLOR_BL, which would make the code larger than 8
3488 * bits.
3489 *
3490 * This assumes that fg/bg are equal when we override with one of the special
3491 * attribute colors.
3492 */
3493CellColor
3494makeColorPair(int fg, int bg)
3495{
3496    unsigned my_bg = (bg >= 0) && (bg < NUM_ANSI_COLORS) ? (unsigned) bg : 0;
3497    unsigned my_fg = (fg >= 0) && (fg < NUM_ANSI_COLORS) ? (unsigned) fg : my_bg;
3498
3499    return (CellColor) (my_fg | (my_bg << COLOR_BITS));
3500}
3501
3502/*
3503 * Using the "current" SGR background, clear a rectangle.
3504 */
3505void
3506ClearCurBackground(XtermWidget xw,
3507		   int top,
3508		   int left,
3509		   unsigned height,
3510		   unsigned width)
3511{
3512    TScreen *screen = &(xw->screen);
3513
3514    TRACE(("ClearCurBackground(%d,%d,%d,%d) %d\n",
3515	   top, left, height, width, xw->cur_background));
3516
3517    if (VWindow(screen)) {
3518	set_background(xw, xw->cur_background);
3519
3520	XClearArea(screen->display, VWindow(screen),
3521		   left, top, width, height, False);
3522
3523	set_background(xw, -1);
3524    }
3525}
3526#endif /* OPT_ISO_COLORS */
3527
3528/*
3529 * Returns a single base character for the given cell.
3530 */
3531unsigned
3532getXtermCell(TScreen * screen, int row, int col)
3533{
3534    LineData *ld = getLineData(screen, row);
3535
3536    return ld->charData[col];
3537}
3538
3539/*
3540 * Sets a single base character for the given cell.
3541 */
3542void
3543putXtermCell(TScreen * screen, int row, int col, int ch)
3544{
3545    LineData *ld = getLineData(screen, row);
3546
3547    ld->charData[col] = (CharData) ch;
3548    if_OPT_WIDE_CHARS(screen, {
3549	size_t off;
3550	for_each_combData(off, ld) {
3551	    ld->combData[off][col] = 0;
3552	}
3553    });
3554}
3555
3556#if OPT_WIDE_CHARS
3557/*
3558 * Add a combining character for the given cell
3559 */
3560void
3561addXtermCombining(TScreen * screen, int row, int col, unsigned ch)
3562{
3563    if (ch != 0) {
3564	LineData *ld = getLineData(screen, row);
3565	size_t off;
3566
3567	TRACE(("addXtermCombining %d,%d %#x (%d)\n",
3568	       row, col, ch, my_wcwidth((wchar_t) ch)));
3569
3570	for_each_combData(off, ld) {
3571	    if (!ld->combData[off][col]) {
3572		ld->combData[off][col] = (CharData) ch;
3573		break;
3574	    }
3575	}
3576    }
3577}
3578#endif
3579
3580#ifdef HAVE_CONFIG_H
3581#ifdef USE_MY_MEMMOVE
3582void *
3583my_memmove(void *s1, void *s2, size_t n)
3584{
3585    if (n != 0) {
3586	char *p1 = (char *) s1;
3587	char *p2 = (char *) s2;
3588
3589	if ((p1 + n > p2) && (p2 + n > p1)) {
3590	    static char *bfr;
3591	    static size_t length;
3592	    size_t j;
3593	    if (length < n) {
3594		length = (n * 3) / 2;
3595		bfr = ((bfr != 0)
3596		       ? TypeRealloc(char, length, bfr)
3597		       : TypeMallocN(char, length));
3598		if (bfr == NULL)
3599		    SysError(ERROR_MMALLOC);
3600	    }
3601	    for (j = 0; j < n; j++)
3602		bfr[j] = p2[j];
3603	    p2 = bfr;
3604	}
3605	while (n-- != 0)
3606	    p1[n] = p2[n];
3607    }
3608    return s1;
3609}
3610#endif /* USE_MY_MEMMOVE */
3611
3612#ifndef HAVE_STRERROR
3613char *
3614my_strerror(int n)
3615{
3616    extern char *sys_errlist[];
3617    extern int sys_nerr;
3618    if (n > 0 && n < sys_nerr)
3619	return sys_errlist[n];
3620    return "?";
3621}
3622#endif
3623#endif
3624
3625void
3626update_keyboard_type(void)
3627{
3628    update_delete_del();
3629    update_tcap_fkeys();
3630    update_old_fkeys();
3631    update_hp_fkeys();
3632    update_sco_fkeys();
3633    update_sun_fkeys();
3634    update_sun_kbd();
3635}
3636
3637void
3638set_keyboard_type(XtermWidget xw, xtermKeyboardType type, Bool set)
3639{
3640    xtermKeyboardType save = xw->keyboard.type;
3641
3642    TRACE(("set_keyboard_type(%s, %s) currently %s\n",
3643	   visibleKeyboardType(type),
3644	   BtoS(set),
3645	   visibleKeyboardType(xw->keyboard.type)));
3646    if (set) {
3647	xw->keyboard.type = type;
3648    } else {
3649	xw->keyboard.type = keyboardIsDefault;
3650    }
3651
3652    if (save != xw->keyboard.type) {
3653	update_keyboard_type();
3654    }
3655}
3656
3657void
3658toggle_keyboard_type(XtermWidget xw, xtermKeyboardType type)
3659{
3660    xtermKeyboardType save = xw->keyboard.type;
3661
3662    TRACE(("toggle_keyboard_type(%s) currently %s\n",
3663	   visibleKeyboardType(type),
3664	   visibleKeyboardType(xw->keyboard.type)));
3665    if (xw->keyboard.type == type) {
3666	xw->keyboard.type = keyboardIsDefault;
3667    } else {
3668	xw->keyboard.type = type;
3669    }
3670
3671    if (save != xw->keyboard.type) {
3672	update_keyboard_type();
3673    }
3674}
3675
3676void
3677init_keyboard_type(XtermWidget xw, xtermKeyboardType type, Bool set)
3678{
3679    static Bool wasSet = False;
3680
3681    TRACE(("init_keyboard_type(%s, %s) currently %s\n",
3682	   visibleKeyboardType(type),
3683	   BtoS(set),
3684	   visibleKeyboardType(xw->keyboard.type)));
3685    if (set) {
3686	if (wasSet) {
3687	    fprintf(stderr, "Conflicting keyboard type option (%u/%u)\n",
3688		    xw->keyboard.type, type);
3689	}
3690	xw->keyboard.type = type;
3691	wasSet = True;
3692	update_keyboard_type();
3693    }
3694}
3695
3696/*
3697 * If the keyboardType resource is set, use that, overriding the individual
3698 * boolean resources for different keyboard types.
3699 */
3700void
3701decode_keyboard_type(XtermWidget xw, XTERM_RESOURCE * rp)
3702{
3703#define DATA(n, t, f) { n, t, XtOffsetOf(XTERM_RESOURCE, f) }
3704#define FLAG(n) *(Boolean *)(((char *)rp) + table[n].offset)
3705    static struct {
3706	const char *name;
3707	xtermKeyboardType type;
3708	unsigned offset;
3709    } table[] = {
3710#if OPT_HP_FUNC_KEYS
3711	DATA(NAME_HP_KT, keyboardIsHP, hpFunctionKeys),
3712#endif
3713#if OPT_SCO_FUNC_KEYS
3714	    DATA(NAME_SCO_KT, keyboardIsSCO, scoFunctionKeys),
3715#endif
3716#if OPT_SUN_FUNC_KEYS
3717	    DATA(NAME_SUN_KT, keyboardIsSun, sunFunctionKeys),
3718#endif
3719#if OPT_SUNPC_KBD
3720	    DATA(NAME_VT220_KT, keyboardIsVT220, sunKeyboard),
3721#endif
3722#if OPT_TCAP_FKEYS
3723	    DATA(NAME_TCAP_KT, keyboardIsTermcap, termcapKeys),
3724#endif
3725    };
3726    Cardinal n;
3727
3728    TRACE(("decode_keyboard_type(%s)\n", rp->keyboardType));
3729    if (!x_strcasecmp(rp->keyboardType, "unknown")) {
3730	/*
3731	 * Let the individual resources comprise the keyboard-type.
3732	 */
3733	for (n = 0; n < XtNumber(table); ++n)
3734	    init_keyboard_type(xw, table[n].type, FLAG(n));
3735    } else if (!x_strcasecmp(rp->keyboardType, "default")) {
3736	/*
3737	 * Set the keyboard-type to the Sun/PC type, allowing modified
3738	 * function keys, etc.
3739	 */
3740	for (n = 0; n < XtNumber(table); ++n)
3741	    init_keyboard_type(xw, table[n].type, False);
3742    } else {
3743	Bool found = False;
3744
3745	/*
3746	 * Choose an individual keyboard type.
3747	 */
3748	for (n = 0; n < XtNumber(table); ++n) {
3749	    if (!x_strcasecmp(rp->keyboardType, table[n].name + 1)) {
3750		FLAG(n) = True;
3751		found = True;
3752	    } else {
3753		FLAG(n) = False;
3754	    }
3755	    init_keyboard_type(xw, table[n].type, FLAG(n));
3756	}
3757	if (!found) {
3758	    fprintf(stderr,
3759		    "KeyboardType resource \"%s\" not found\n",
3760		    rp->keyboardType);
3761	}
3762    }
3763#undef DATA
3764#undef FLAG
3765}
3766
3767#if OPT_WIDE_CHARS
3768#if defined(HAVE_WCHAR_H) && defined(HAVE_WCWIDTH)
3769/*
3770 * If xterm is running in a UTF-8 locale, it is still possible to encounter
3771 * old runtime configurations which yield incomplete or inaccurate data.
3772 */
3773static Bool
3774systemWcwidthOk(int samplesize, int samplepass)
3775{
3776    wchar_t n;
3777    int oops = 0;
3778
3779    for (n = 21; n <= 25; ++n) {
3780	int code = (int) dec2ucs((unsigned) n);
3781	int system_code = wcwidth(code);
3782	int intern_code = mk_wcwidth(code);
3783
3784	/*
3785	 * Solaris 10 wcwidth() returns "2" for all of the line-drawing (page
3786	 * 0x2500) and most of the geometric shapes (a few are excluded, just
3787	 * to make it more difficult to use).  Do a sanity check to avoid using
3788	 * it.
3789	 */
3790	if ((system_code < 0 && intern_code >= 1)
3791	    || (system_code >= 0 && intern_code != system_code)) {
3792	    TRACE(("systemWcwidthOk: broken system line-drawing wcwidth\n"));
3793	    oops += (samplepass + 1);
3794	    break;
3795	}
3796    }
3797
3798    for (n = 0; n < (wchar_t) samplesize; ++n) {
3799	int system_code = wcwidth(n);
3800	int intern_code = mk_wcwidth(n);
3801
3802	/*
3803	 * Since mk_wcwidth() is designed to check for nonspacing characters,
3804	 * and has rough range-checks for double-width characters, it will
3805	 * generally not detect cases where a code has not been assigned.
3806	 *
3807	 * Some experimentation with GNU libc suggests that up to 1/4 of the
3808	 * codes would differ, simply because the runtime library would have a
3809	 * table listing the unassigned codes, and return -1 for those.  If
3810	 * mk_wcwidth() has no information about a code, it returns 1.  On the
3811	 * other hand, if the runtime returns a positive number, the two should
3812	 * agree.
3813	 *
3814	 * The "up to" is measured for 4k, 8k, 16k of data.  With only 1k, the
3815	 * number of differences was only 77.  However, that is only one
3816	 * system, and this is only a sanity check to avoid using broken
3817	 * libraries.
3818	 */
3819	if ((system_code < 0 && intern_code >= 1)
3820	    || (system_code >= 0 && intern_code != system_code)) {
3821	    ++oops;
3822	}
3823    }
3824    TRACE(("systemWcwidthOk: %d/%d mismatches, allowed %d\n",
3825	   oops, samplesize, samplepass));
3826    return (oops <= samplepass);
3827}
3828#endif /* HAVE_WCWIDTH */
3829
3830void
3831decode_wcwidth(int mode, int samplesize, int samplepass)
3832{
3833    switch (mode) {
3834    default:
3835#if defined(HAVE_WCHAR_H) && defined(HAVE_WCWIDTH)
3836	if (xtermEnvUTF8() && systemWcwidthOk(samplesize, samplepass)) {
3837	    my_wcwidth = wcwidth;
3838	    TRACE(("using system wcwidth() function\n"));
3839	    break;
3840	}
3841	/* FALLTHRU */
3842#else
3843	(void) samplesize;
3844	(void) samplepass;
3845#endif
3846    case 2:
3847	my_wcwidth = &mk_wcwidth;
3848	TRACE(("using MK wcwidth() function\n"));
3849	break;
3850    case 3:
3851    case 4:
3852	my_wcwidth = &mk_wcwidth_cjk;
3853	TRACE(("using MK-CJK wcwidth() function\n"));
3854	break;
3855    }
3856}
3857#endif
3858