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