util.c revision 492d43a5
1/* $XTermId: util.c,v 1.541 2010/10/11 00:32:28 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    if (screen->show_wrap_marks) {
1347	ShowWrapMarks(xw, screen->cur_row, ld);
1348    }
1349    screen->do_wrap = False;
1350}
1351
1352/*
1353 * Clear first part of cursor's line, inclusive.
1354 */
1355static void
1356ClearLeft(XtermWidget xw)
1357{
1358    TScreen *screen = TScreenOf(xw);
1359    unsigned len = (unsigned) screen->cur_col + 1;
1360
1361    assert(screen->cur_col >= 0);
1362    if (AddToVisible(xw)) {
1363	if_OPT_WIDE_CHARS(screen, {
1364	    int row = screen->cur_row;
1365	    int kl;
1366	    int kr;
1367	    if (DamagedCurCells(screen, 1, &kl, &kr) && kr >= kl) {
1368		ClearInLine2(xw, 0, row, kl, (unsigned) (kr - kl + 1));
1369	    }
1370	});
1371	(void) ClearInLine(xw, screen->cur_row, 0, len);
1372    } else {
1373	ScrnClearCells(xw, screen->cur_row, 0, len);
1374    }
1375}
1376
1377/*
1378 * Erase the cursor's line.
1379 */
1380static void
1381ClearLine(XtermWidget xw)
1382{
1383    TScreen *screen = TScreenOf(xw);
1384    unsigned len = (unsigned) MaxCols(screen);
1385
1386    assert(screen->max_col >= 0);
1387    (void) ClearInLine(xw, screen->cur_row, 0, len);
1388}
1389
1390void
1391ClearScreen(XtermWidget xw)
1392{
1393    TScreen *screen = TScreenOf(xw);
1394    int top;
1395
1396    if (screen->cursor_state)
1397	HideCursor();
1398
1399    ScrnDisownSelection(xw);
1400    screen->do_wrap = False;
1401    if ((top = INX2ROW(screen, 0)) <= screen->max_row) {
1402	if (screen->scroll_amt)
1403	    FlushScroll(xw);
1404	ClearCurBackground(xw,
1405			   top * FontHeight(screen) + screen->border,
1406			   OriginX(screen),
1407			   (unsigned) ((screen->max_row - top + 1)
1408				       * FontHeight(screen)),
1409			   (unsigned) Width(screen));
1410    }
1411    ClearBufRows(xw, 0, screen->max_row);
1412}
1413
1414/*
1415 * If we've written protected text DEC-style, and are issuing a non-DEC
1416 * erase, temporarily reset the protected_mode flag so that the erase will
1417 * ignore the protected flags.
1418 */
1419void
1420do_erase_line(XtermWidget xw, int param, int mode)
1421{
1422    TScreen *screen = TScreenOf(xw);
1423    int saved_mode = screen->protected_mode;
1424
1425    if (saved_mode == DEC_PROTECT
1426	&& saved_mode != mode) {
1427	screen->protected_mode = OFF_PROTECT;
1428    }
1429
1430    switch (param) {
1431    case -1:			/* DEFAULT */
1432    case 0:
1433	ClearRight(xw, -1);
1434	break;
1435    case 1:
1436	ClearLeft(xw);
1437	break;
1438    case 2:
1439	ClearLine(xw);
1440	break;
1441    }
1442    screen->protected_mode = saved_mode;
1443}
1444
1445/*
1446 * Just like 'do_erase_line()', except that this intercepts ED controls.  If we
1447 * clear the whole screen, we'll get the return-value from ClearInLine, and
1448 * find if there were any protected characters left.  If not, reset the
1449 * protected mode flag in the screen data (it's slower).
1450 */
1451void
1452do_erase_display(XtermWidget xw, int param, int mode)
1453{
1454    TScreen *screen = TScreenOf(xw);
1455    int saved_mode = screen->protected_mode;
1456
1457    if (saved_mode == DEC_PROTECT
1458	&& saved_mode != mode)
1459	screen->protected_mode = OFF_PROTECT;
1460
1461    switch (param) {
1462    case -1:			/* DEFAULT */
1463    case 0:
1464	if (screen->cur_row == 0
1465	    && screen->cur_col == 0) {
1466	    screen->protected_mode = saved_mode;
1467	    do_erase_display(xw, 2, mode);
1468	    saved_mode = screen->protected_mode;
1469	} else
1470	    ClearBelow(xw);
1471	break;
1472
1473    case 1:
1474	if (screen->cur_row == screen->max_row
1475	    && screen->cur_col == screen->max_col) {
1476	    screen->protected_mode = saved_mode;
1477	    do_erase_display(xw, 2, mode);
1478	    saved_mode = screen->protected_mode;
1479	} else
1480	    ClearAbove(xw);
1481	break;
1482
1483    case 2:
1484	/*
1485	 * We use 'ClearScreen()' throughout the remainder of the
1486	 * program for places where we don't care if the characters are
1487	 * protected or not.  So we modify the logic around this call
1488	 * on 'ClearScreen()' to handle protected characters.
1489	 */
1490	if (screen->protected_mode != OFF_PROTECT) {
1491	    int row;
1492	    int rc = 1;
1493	    unsigned len = (unsigned) MaxCols(screen);
1494
1495	    assert(screen->max_col >= 0);
1496	    for (row = 0; row <= screen->max_row; row++)
1497		rc &= ClearInLine(xw, row, 0, len);
1498	    if (rc != 0)
1499		saved_mode = OFF_PROTECT;
1500	} else {
1501	    ClearScreen(xw);
1502	}
1503	break;
1504
1505    case 3:
1506	/* xterm addition - erase saved lines. */
1507	screen->savedlines = 0;
1508	ScrollBarDrawThumb(screen->scrollWidget);
1509	break;
1510    }
1511    screen->protected_mode = saved_mode;
1512}
1513
1514static void
1515CopyWait(XtermWidget xw)
1516{
1517    TScreen *screen = TScreenOf(xw);
1518    XEvent reply;
1519    XEvent *rep = &reply;
1520
1521    while (1) {
1522	XWindowEvent(screen->display, VWindow(screen),
1523		     ExposureMask, &reply);
1524	switch (reply.type) {
1525	case Expose:
1526	    HandleExposure(xw, &reply);
1527	    break;
1528	case NoExpose:
1529	case GraphicsExpose:
1530	    if (screen->incopy <= 0) {
1531		screen->incopy = 1;
1532		if (screen->scrolls > 0)
1533		    screen->scrolls--;
1534	    }
1535	    if (reply.type == GraphicsExpose)
1536		HandleExposure(xw, &reply);
1537
1538	    if ((reply.type == NoExpose) ||
1539		((XExposeEvent *) rep)->count == 0) {
1540		if (screen->incopy <= 0 && screen->scrolls > 0)
1541		    screen->scrolls--;
1542		if (screen->scrolls == 0) {
1543		    screen->incopy = 0;
1544		    return;
1545		}
1546		screen->incopy = -1;
1547	    }
1548	    break;
1549	}
1550    }
1551}
1552
1553/*
1554 * used by vertical_copy_area and and horizontal_copy_area
1555 */
1556static void
1557copy_area(XtermWidget xw,
1558	  int src_x,
1559	  int src_y,
1560	  unsigned width,
1561	  unsigned height,
1562	  int dest_x,
1563	  int dest_y)
1564{
1565    TScreen *screen = TScreenOf(xw);
1566
1567    if (width != 0 && height != 0) {
1568	/* wait for previous CopyArea to complete unless
1569	   multiscroll is enabled and active */
1570	if (screen->incopy && screen->scrolls == 0)
1571	    CopyWait(xw);
1572	screen->incopy = -1;
1573
1574	/* save for translating Expose events */
1575	screen->copy_src_x = src_x;
1576	screen->copy_src_y = src_y;
1577	screen->copy_width = width;
1578	screen->copy_height = height;
1579	screen->copy_dest_x = dest_x;
1580	screen->copy_dest_y = dest_y;
1581
1582	XCopyArea(screen->display,
1583		  VWindow(screen), VWindow(screen),
1584		  NormalGC(xw, screen),
1585		  src_x, src_y, width, height, dest_x, dest_y);
1586    }
1587}
1588
1589/*
1590 * use when inserting or deleting characters on the current line
1591 */
1592static void
1593horizontal_copy_area(XtermWidget xw,
1594		     int firstchar,	/* char pos on screen to start copying at */
1595		     int nchars,
1596		     int amount)	/* number of characters to move right */
1597{
1598    TScreen *screen = TScreenOf(xw);
1599    LineData *ld;
1600
1601    if ((ld = getLineData(screen, screen->cur_row)) != 0) {
1602	int src_x = LineCursorX(screen, ld, firstchar);
1603	int src_y = CursorY(screen, screen->cur_row);
1604
1605	copy_area(xw, src_x, src_y,
1606		  (unsigned) (nchars * LineFontWidth(screen, ld)),
1607		  (unsigned) FontHeight(screen),
1608		  src_x + amount * LineFontWidth(screen, ld), src_y);
1609    }
1610}
1611
1612/*
1613 * use when inserting or deleting lines from the screen
1614 */
1615static void
1616vertical_copy_area(XtermWidget xw,
1617		   int firstline,	/* line on screen to start copying at */
1618		   int nlines,
1619		   int amount)	/* number of lines to move up (neg=down) */
1620{
1621    TScreen *screen = TScreenOf(xw);
1622
1623    if (nlines > 0) {
1624	int src_x = OriginX(screen);
1625	int src_y = firstline * FontHeight(screen) + screen->border;
1626
1627	copy_area(xw, src_x, src_y,
1628		  (unsigned) Width(screen),
1629		  (unsigned) (nlines * FontHeight(screen)),
1630		  src_x, src_y - amount * FontHeight(screen));
1631	if (screen->show_wrap_marks) {
1632	    LineData *ld;
1633	    int row;
1634	    for (row = firstline; row < firstline + nlines; ++row) {
1635		if ((ld = getLineData(screen, row)) != 0) {
1636		    ShowWrapMarks(xw, row, ld);
1637		}
1638	    }
1639	}
1640    }
1641}
1642
1643/*
1644 * use when scrolling the entire screen
1645 */
1646void
1647scrolling_copy_area(XtermWidget xw,
1648		    int firstline,	/* line on screen to start copying at */
1649		    int nlines,
1650		    int amount)	/* number of lines to move up (neg=down) */
1651{
1652
1653    if (nlines > 0) {
1654	vertical_copy_area(xw, firstline, nlines, amount);
1655    }
1656}
1657
1658/*
1659 * Handler for Expose events on the VT widget.
1660 * Returns 1 iff the area where the cursor was got refreshed.
1661 */
1662int
1663HandleExposure(XtermWidget xw, XEvent * event)
1664{
1665    TScreen *screen = TScreenOf(xw);
1666    XExposeEvent *reply = (XExposeEvent *) event;
1667
1668#ifndef NO_ACTIVE_ICON
1669    if (reply->window == screen->iconVwin.window) {
1670	WhichVWin(screen) = &screen->iconVwin;
1671	TRACE(("HandleExposure - icon"));
1672    } else {
1673	WhichVWin(screen) = &screen->fullVwin;
1674	TRACE(("HandleExposure - normal"));
1675    }
1676    TRACE((" event %d,%d %dx%d\n",
1677	   reply->y,
1678	   reply->x,
1679	   reply->height,
1680	   reply->width));
1681#endif /* NO_ACTIVE_ICON */
1682
1683    /* if not doing CopyArea or if this is a GraphicsExpose, don't translate */
1684    if (!screen->incopy || event->type != Expose)
1685	return handle_translated_exposure(xw, reply->x, reply->y,
1686					  reply->width,
1687					  reply->height);
1688    else {
1689	/* compute intersection of area being copied with
1690	   area being exposed. */
1691	int both_x1 = Max(screen->copy_src_x, reply->x);
1692	int both_y1 = Max(screen->copy_src_y, reply->y);
1693	int both_x2 = Min(screen->copy_src_x + (int) screen->copy_width,
1694			  (reply->x + (int) reply->width));
1695	int both_y2 = Min(screen->copy_src_y + (int) screen->copy_height,
1696			  (reply->y + (int) reply->height));
1697	int value = 0;
1698
1699	/* was anything copied affected? */
1700	if (both_x2 > both_x1 && both_y2 > both_y1) {
1701	    /* do the copied area */
1702	    value = handle_translated_exposure
1703		(xw, reply->x + screen->copy_dest_x - screen->copy_src_x,
1704		 reply->y + screen->copy_dest_y - screen->copy_src_y,
1705		 reply->width, reply->height);
1706	}
1707	/* was anything not copied affected? */
1708	if (reply->x < both_x1 || reply->y < both_y1
1709	    || reply->x + reply->width > both_x2
1710	    || reply->y + reply->height > both_y2)
1711	    value = handle_translated_exposure(xw, reply->x, reply->y,
1712					       reply->width, reply->height);
1713
1714	return value;
1715    }
1716}
1717
1718static void
1719set_background(XtermWidget xw, int color GCC_UNUSED)
1720{
1721    TScreen *screen = TScreenOf(xw);
1722    Pixel c = getXtermBackground(xw, xw->flags, color);
1723
1724    TRACE(("set_background(%d) %#lx\n", color, c));
1725    XSetWindowBackground(screen->display, VShellWindow, c);
1726    XSetWindowBackground(screen->display, VWindow(screen), c);
1727}
1728
1729/*
1730 * Called by the ExposeHandler to do the actual repaint after the coordinates
1731 * have been translated to allow for any CopyArea in progress.
1732 * The rectangle passed in is pixel coordinates.
1733 */
1734static int
1735handle_translated_exposure(XtermWidget xw,
1736			   int rect_x,
1737			   int rect_y,
1738			   int rect_width,
1739			   int rect_height)
1740{
1741    TScreen *screen = TScreenOf(xw);
1742    int toprow, leftcol, nrows, ncols;
1743    int x0, x1;
1744    int y0, y1;
1745    int result = 0;
1746
1747    TRACE(("handle_translated_exposure at %d,%d size %dx%d\n",
1748	   rect_y, rect_x, rect_height, rect_width));
1749
1750    x0 = (rect_x - OriginX(screen));
1751    x1 = (x0 + rect_width);
1752
1753    y0 = (rect_y - OriginY(screen));
1754    y1 = (y0 + rect_height);
1755
1756    if ((x0 < 0 ||
1757	 y0 < 0 ||
1758	 x1 > Width(screen) ||
1759	 y1 > Height(screen))) {
1760	set_background(xw, -1);
1761	XClearArea(screen->display, VWindow(screen),
1762		   rect_x,
1763		   rect_y,
1764		   (unsigned) rect_width,
1765		   (unsigned) rect_height, False);
1766    }
1767    toprow = y0 / FontHeight(screen);
1768    if (toprow < 0)
1769	toprow = 0;
1770
1771    leftcol = x0 / FontWidth(screen);
1772    if (leftcol < 0)
1773	leftcol = 0;
1774
1775    nrows = (y1 - 1) / FontHeight(screen) - toprow + 1;
1776    ncols = (x1 - 1) / FontWidth(screen) - leftcol + 1;
1777    toprow -= screen->scrolls;
1778    if (toprow < 0) {
1779	nrows += toprow;
1780	toprow = 0;
1781    }
1782    if (toprow + nrows > MaxRows(screen))
1783	nrows = MaxRows(screen) - toprow;
1784    if (leftcol + ncols > MaxCols(screen))
1785	ncols = MaxCols(screen) - leftcol;
1786
1787    if (nrows > 0 && ncols > 0) {
1788	ScrnRefresh(xw, toprow, leftcol, nrows, ncols, True);
1789	first_map_occurred();
1790	if (screen->cur_row >= toprow &&
1791	    screen->cur_row < toprow + nrows &&
1792	    screen->cur_col >= leftcol &&
1793	    screen->cur_col < leftcol + ncols) {
1794	    result = 1;
1795	}
1796
1797    }
1798    TRACE(("...handle_translated_exposure %d\n", result));
1799    return (result);
1800}
1801
1802/***====================================================================***/
1803
1804void
1805GetColors(XtermWidget xw, ScrnColors * pColors)
1806{
1807    TScreen *screen = TScreenOf(xw);
1808    int n;
1809
1810    pColors->which = 0;
1811    for (n = 0; n < NCOLORS; ++n) {
1812	SET_COLOR_VALUE(pColors, n, T_COLOR(screen, n));
1813    }
1814}
1815
1816void
1817ChangeColors(XtermWidget xw, ScrnColors * pNew)
1818{
1819    Bool repaint = False;
1820    TScreen *screen = TScreenOf(xw);
1821    VTwin *win = WhichVWin(screen);
1822
1823    TRACE(("ChangeColors\n"));
1824
1825    if (COLOR_DEFINED(pNew, TEXT_CURSOR)) {
1826	T_COLOR(screen, TEXT_CURSOR) = COLOR_VALUE(pNew, TEXT_CURSOR);
1827	TRACE(("... TEXT_CURSOR: %#lx\n", T_COLOR(screen, TEXT_CURSOR)));
1828	/* no repaint needed */
1829    } else if ((T_COLOR(screen, TEXT_CURSOR) == T_COLOR(screen, TEXT_FG)) &&
1830	       (COLOR_DEFINED(pNew, TEXT_FG))) {
1831	if (T_COLOR(screen, TEXT_CURSOR) != COLOR_VALUE(pNew, TEXT_FG)) {
1832	    T_COLOR(screen, TEXT_CURSOR) = COLOR_VALUE(pNew, TEXT_FG);
1833	    TRACE(("... TEXT_CURSOR: %#lx\n", T_COLOR(screen, TEXT_CURSOR)));
1834	    if (screen->Vshow)
1835		repaint = True;
1836	}
1837    }
1838
1839    if (COLOR_DEFINED(pNew, TEXT_FG)) {
1840	Pixel fg = COLOR_VALUE(pNew, TEXT_FG);
1841	T_COLOR(screen, TEXT_FG) = fg;
1842	TRACE(("... TEXT_FG: %#lx\n", T_COLOR(screen, TEXT_FG)));
1843	if (screen->Vshow) {
1844	    setCgsFore(xw, win, gcNorm, fg);
1845	    setCgsBack(xw, win, gcNormReverse, fg);
1846	    setCgsFore(xw, win, gcBold, fg);
1847	    setCgsBack(xw, win, gcBoldReverse, fg);
1848	    repaint = True;
1849	}
1850    }
1851
1852    if (COLOR_DEFINED(pNew, TEXT_BG)) {
1853	Pixel bg = COLOR_VALUE(pNew, TEXT_BG);
1854	T_COLOR(screen, TEXT_BG) = bg;
1855	TRACE(("... TEXT_BG: %#lx\n", T_COLOR(screen, TEXT_BG)));
1856	if (screen->Vshow) {
1857	    setCgsBack(xw, win, gcNorm, bg);
1858	    setCgsFore(xw, win, gcNormReverse, bg);
1859	    setCgsBack(xw, win, gcBold, bg);
1860	    setCgsFore(xw, win, gcBoldReverse, bg);
1861	    set_background(xw, -1);
1862	    repaint = True;
1863	}
1864    }
1865#if OPT_HIGHLIGHT_COLOR
1866    if (COLOR_DEFINED(pNew, HIGHLIGHT_BG)) {
1867	if (T_COLOR(screen, HIGHLIGHT_BG) != COLOR_VALUE(pNew, HIGHLIGHT_BG)) {
1868	    T_COLOR(screen, HIGHLIGHT_BG) = COLOR_VALUE(pNew, HIGHLIGHT_BG);
1869	    TRACE(("... HIGHLIGHT_BG: %#lx\n", T_COLOR(screen, HIGHLIGHT_BG)));
1870	    if (screen->Vshow)
1871		repaint = True;
1872	}
1873    }
1874    if (COLOR_DEFINED(pNew, HIGHLIGHT_FG)) {
1875	if (T_COLOR(screen, HIGHLIGHT_FG) != COLOR_VALUE(pNew, HIGHLIGHT_FG)) {
1876	    T_COLOR(screen, HIGHLIGHT_FG) = COLOR_VALUE(pNew, HIGHLIGHT_FG);
1877	    TRACE(("... HIGHLIGHT_FG: %#lx\n", T_COLOR(screen, HIGHLIGHT_FG)));
1878	    if (screen->Vshow)
1879		repaint = True;
1880	}
1881    }
1882#endif
1883
1884    if (COLOR_DEFINED(pNew, MOUSE_FG) || (COLOR_DEFINED(pNew, MOUSE_BG))) {
1885	if (COLOR_DEFINED(pNew, MOUSE_FG)) {
1886	    T_COLOR(screen, MOUSE_FG) = COLOR_VALUE(pNew, MOUSE_FG);
1887	    TRACE(("... MOUSE_FG: %#lx\n", T_COLOR(screen, MOUSE_FG)));
1888	}
1889	if (COLOR_DEFINED(pNew, MOUSE_BG)) {
1890	    T_COLOR(screen, MOUSE_BG) = COLOR_VALUE(pNew, MOUSE_BG);
1891	    TRACE(("... MOUSE_BG: %#lx\n", T_COLOR(screen, MOUSE_BG)));
1892	}
1893
1894	if (screen->Vshow) {
1895	    recolor_cursor(screen,
1896			   screen->pointer_cursor,
1897			   T_COLOR(screen, MOUSE_FG),
1898			   T_COLOR(screen, MOUSE_BG));
1899	    XDefineCursor(screen->display, VWindow(screen),
1900			  screen->pointer_cursor);
1901	}
1902#if OPT_TEK4014
1903	if (TEK4014_SHOWN(xw)) {
1904	    TekScreen *tekscr = TekScreenOf(tekWidget);
1905	    Window tekwin = TWindow(tekscr);
1906	    if (tekwin) {
1907		recolor_cursor(screen,
1908			       tekscr->arrow,
1909			       T_COLOR(screen, MOUSE_FG),
1910			       T_COLOR(screen, MOUSE_BG));
1911		XDefineCursor(screen->display, tekwin, tekscr->arrow);
1912	    }
1913	}
1914#endif
1915	/* no repaint needed */
1916    }
1917
1918    if (COLOR_DEFINED(pNew, TEXT_FG) ||
1919	COLOR_DEFINED(pNew, TEXT_BG) ||
1920	COLOR_DEFINED(pNew, TEXT_CURSOR)) {
1921	if (set_cursor_gcs(xw) && screen->Vshow) {
1922	    repaint = True;
1923	}
1924    }
1925#if OPT_TEK4014
1926    if (COLOR_DEFINED(pNew, TEK_FG) ||
1927	COLOR_DEFINED(pNew, TEK_BG)) {
1928	ChangeTekColors(tekWidget, screen, pNew);
1929	if (TEK4014_SHOWN(xw)) {
1930	    TekRepaint(tekWidget);
1931	}
1932    } else if (COLOR_DEFINED(pNew, TEK_CURSOR)) {
1933	ChangeTekColors(tekWidget, screen, pNew);
1934    }
1935#endif
1936    if (repaint)
1937	xtermRepaint(xw);
1938}
1939
1940void
1941xtermClear(XtermWidget xw)
1942{
1943    TScreen *screen = TScreenOf(xw);
1944
1945    TRACE(("xtermClear\n"));
1946    XClearWindow(screen->display, VWindow(screen));
1947}
1948
1949void
1950xtermRepaint(XtermWidget xw)
1951{
1952    TScreen *screen = TScreenOf(xw);
1953
1954    TRACE(("xtermRepaint\n"));
1955    xtermClear(xw);
1956    ScrnRefresh(xw, 0, 0, MaxRows(screen), MaxCols(screen), True);
1957}
1958
1959/***====================================================================***/
1960
1961Boolean
1962isDefaultForeground(const char *name)
1963{
1964    return (Boolean) ! x_strcasecmp(name, XtDefaultForeground);
1965}
1966
1967Boolean
1968isDefaultBackground(const char *name)
1969{
1970    return (Boolean) ! x_strcasecmp(name, XtDefaultBackground);
1971}
1972
1973#if OPT_WIDE_CHARS
1974/*
1975 * Check for Unicode BIDI control characters, which may be miscategorized via
1976 * wcwidth() and iswprint() as zero-width printable characters.
1977 */
1978Boolean
1979isWideControl(unsigned ch)
1980{
1981    Boolean result;
1982
1983    switch (ch) {
1984    case 0x200E:
1985    case 0x200F:
1986    case 0x202A:
1987    case 0x202B:
1988    case 0x202C:
1989    case 0x202D:
1990    case 0x202E:
1991	result = True;
1992	break;
1993    default:
1994	result = False;
1995	break;
1996    }
1997    return result;
1998}
1999#endif
2000
2001/***====================================================================***/
2002
2003typedef struct {
2004    Pixel fg;
2005    Pixel bg;
2006} ToSwap;
2007
2008#if OPT_HIGHLIGHT_COLOR
2009#define hc_param ,Bool hilite_color
2010#define hc_value ,screen->hilite_color
2011#else
2012#define hc_param		/* nothing */
2013#define hc_value		/* nothing */
2014#endif
2015
2016/*
2017 * Use this to swap the foreground/background color values in the resource
2018 * data, and to build up a list of the pairs which must be swapped in the
2019 * GC cache.
2020 */
2021static void
2022swapLocally(ToSwap * list, int *count, ColorRes * fg, ColorRes * bg hc_param)
2023{
2024    ColorRes tmp;
2025    int n;
2026    Boolean found = False;
2027
2028#if OPT_COLOR_RES
2029    Pixel fg_color = fg->value;
2030    Pixel bg_color = bg->value;
2031#else
2032    Pixel fg_color = *fg;
2033    Pixel bg_color = *bg;
2034#endif
2035
2036#if OPT_HIGHLIGHT_COLOR
2037    if ((fg_color != bg_color) || !hilite_color)
2038#endif
2039    {
2040	EXCHANGE(*fg, *bg, tmp);
2041	for (n = 0; n < *count; ++n) {
2042	    if ((list[n].fg == fg_color && list[n].bg == bg_color)
2043		|| (list[n].fg == bg_color && list[n].bg == fg_color)) {
2044		found = True;
2045		break;
2046	    }
2047	}
2048	if (!found) {
2049	    list[*count].fg = fg_color;
2050	    list[*count].bg = bg_color;
2051	    *count = *count + 1;
2052	    TRACE(("swapLocally fg %#lx, bg %#lx ->%d\n",
2053		   fg_color, bg_color, *count));
2054	}
2055    }
2056}
2057
2058static void
2059reallySwapColors(XtermWidget xw, ToSwap * list, int count)
2060{
2061    int j, k;
2062
2063    TRACE(("reallySwapColors\n"));
2064    for (j = 0; j < count; ++j) {
2065	for_each_text_gc(k) {
2066	    redoCgs(xw, list[j].fg, list[j].bg, (CgsEnum) k);
2067	}
2068    }
2069}
2070
2071static void
2072swapVTwinGCs(XtermWidget xw, VTwin * win)
2073{
2074    swapCgs(xw, win, gcNorm, gcNormReverse);
2075    swapCgs(xw, win, gcBold, gcBoldReverse);
2076}
2077
2078void
2079ReverseVideo(XtermWidget xw)
2080{
2081    TScreen *screen = TScreenOf(xw);
2082    ToSwap listToSwap[5];
2083    int numToSwap = 0;
2084
2085    TRACE(("ReverseVideo\n"));
2086
2087    /*
2088     * Swap SGR foreground and background colors.  By convention, these are
2089     * the colors assigned to "black" (SGR #0) and "white" (SGR #7).  Also,
2090     * SGR #8 and SGR #15 are the bold (or bright) versions of SGR #0 and
2091     * #7, respectively.
2092     *
2093     * We don't swap colors that happen to match the screen's foreground
2094     * and background because that tends to produce bizarre effects.
2095     */
2096#define swapAnyColor(name,a,b) swapLocally(listToSwap, &numToSwap, &(screen->name[a]), &(screen->name[b]) hc_value)
2097#define swapAColor(a,b) swapAnyColor(Acolors, a, b)
2098    if_OPT_ISO_COLORS(screen, {
2099	swapAColor(0, 7);
2100	swapAColor(8, 15);
2101    });
2102
2103    if (T_COLOR(screen, TEXT_CURSOR) == T_COLOR(screen, TEXT_FG))
2104	T_COLOR(screen, TEXT_CURSOR) = T_COLOR(screen, TEXT_BG);
2105
2106#define swapTColor(a,b) swapAnyColor(Tcolors, a, b)
2107    swapTColor(TEXT_FG, TEXT_BG);
2108    swapTColor(MOUSE_FG, MOUSE_BG);
2109
2110    reallySwapColors(xw, listToSwap, numToSwap);
2111
2112    swapVTwinGCs(xw, &(screen->fullVwin));
2113#ifndef NO_ACTIVE_ICON
2114    swapVTwinGCs(xw, &(screen->iconVwin));
2115#endif /* NO_ACTIVE_ICON */
2116
2117    xw->misc.re_verse = (Boolean) ! xw->misc.re_verse;
2118
2119    if (XtIsRealized((Widget) xw)) {
2120	xtermDisplayCursor(xw);
2121    }
2122#if OPT_TEK4014
2123    if (TEK4014_SHOWN(xw)) {
2124	TekScreen *tekscr = TekScreenOf(tekWidget);
2125	Window tekwin = TWindow(tekscr);
2126	recolor_cursor(screen,
2127		       tekscr->arrow,
2128		       T_COLOR(screen, MOUSE_FG),
2129		       T_COLOR(screen, MOUSE_BG));
2130	XDefineCursor(screen->display, tekwin, tekscr->arrow);
2131    }
2132#endif
2133
2134    if (screen->scrollWidget)
2135	ScrollBarReverseVideo(screen->scrollWidget);
2136
2137    if (XtIsRealized((Widget) xw)) {
2138	set_background(xw, -1);
2139    }
2140#if OPT_TEK4014
2141    TekReverseVideo(tekWidget);
2142#endif
2143    if (XtIsRealized((Widget) xw)) {
2144	xtermRepaint(xw);
2145    }
2146#if OPT_TEK4014
2147    if (TEK4014_SHOWN(xw)) {
2148	TekRepaint(tekWidget);
2149    }
2150#endif
2151    ReverseOldColors();
2152    set_cursor_gcs(xw);
2153    update_reversevideo();
2154    TRACE(("...ReverseVideo\n"));
2155}
2156
2157void
2158recolor_cursor(TScreen * screen,
2159	       Cursor cursor,	/* X cursor ID to set */
2160	       unsigned long fg,	/* pixel indexes to look up */
2161	       unsigned long bg)	/* pixel indexes to look up */
2162{
2163    Display *dpy = screen->display;
2164    XColor colordefs[2];	/* 0 is foreground, 1 is background */
2165
2166    colordefs[0].pixel = fg;
2167    colordefs[1].pixel = bg;
2168    XQueryColors(dpy, DefaultColormap(dpy, DefaultScreen(dpy)),
2169		 colordefs, 2);
2170    XRecolorCursor(dpy, cursor, colordefs, colordefs + 1);
2171    return;
2172}
2173
2174#if OPT_RENDERFONT
2175static XftColor *
2176getXftColor(XtermWidget xw, Pixel pixel)
2177{
2178#define CACHE_SIZE  4
2179    static struct {
2180	XftColor color;
2181	int use;
2182    } cache[CACHE_SIZE];
2183    static int use;
2184    int i;
2185    int oldest, oldestuse;
2186    XColor color;
2187
2188    oldestuse = 0x7fffffff;
2189    oldest = 0;
2190    for (i = 0; i < CACHE_SIZE; i++) {
2191	if (cache[i].use) {
2192	    if (cache[i].color.pixel == pixel) {
2193		cache[i].use = ++use;
2194		return &cache[i].color;
2195	    }
2196	}
2197	if (cache[i].use < oldestuse) {
2198	    oldestuse = cache[i].use;
2199	    oldest = i;
2200	}
2201    }
2202    i = oldest;
2203    color.pixel = pixel;
2204    XQueryColor(TScreenOf(xw)->display, xw->core.colormap, &color);
2205    cache[i].color.color.red = color.red;
2206    cache[i].color.color.green = color.green;
2207    cache[i].color.color.blue = color.blue;
2208    cache[i].color.color.alpha = 0xffff;
2209    cache[i].color.pixel = pixel;
2210    cache[i].use = ++use;
2211    return &cache[i].color;
2212}
2213
2214/*
2215 * The cell-width is related to, but not the same as the wide-character width.
2216 * We will only get useful values from wcwidth() for codes above 255.
2217 * Otherwise, interpret according to internal data.
2218 */
2219#if OPT_RENDERWIDE
2220
2221#if OPT_C1_PRINT
2222#define XtermCellWidth(xw, ch) \
2223	(((ch) == 0 || (ch) == 127) \
2224	  ? 0 \
2225	  : (((ch) < 256) \
2226	      ? (((ch) >= 128 && (ch) < 160) \
2227	          ? (TScreenOf(xw)->c1_printable ? 1 : 0) \
2228	          : 1) \
2229	      : my_wcwidth(ch)))
2230#else
2231#define XtermCellWidth(xw, ch) \
2232	(((ch) == 0 || (ch) == 127) \
2233	  ? 0 \
2234	  : (((ch) < 256) \
2235	      ? 1 \
2236	      : my_wcwidth(ch)))
2237#endif
2238
2239#endif /* OPT_RENDERWIDE */
2240
2241#define XFT_FONT(name) screen->name.font
2242
2243#if OPT_ISO_COLORS
2244#define UseBoldFont(screen) (!(screen)->colorBDMode || ((screen)->veryBoldColors & BOLD))
2245#else
2246#define UseBoldFont(screen) 1
2247#endif
2248/*
2249 * fontconfig/Xft combination prior to 2.2 has a problem with
2250 * CJK truetype 'double-width' (bi-width/monospace) fonts leading
2251 * to the 's p a c e d o u t' rendering. Consequently, we can't
2252 * rely on XftDrawString8/16  when one of  those fonts is used.
2253 * Instead, we need to roll out our own using XftDrawCharSpec.
2254 * A patch in the same spirit (but in a rather different form)
2255 * was applied to gnome vte and gtk2 port of vim.
2256 * See http://bugzilla.mozilla.org/show_bug.cgi?id=196312
2257 */
2258static int
2259xtermXftDrawString(XtermWidget xw,
2260		   unsigned flags GCC_UNUSED,
2261		   XftColor * color,
2262		   XftFont * font,
2263		   int x,
2264		   int y,
2265		   IChar * text,
2266		   Cardinal len,
2267		   Bool really)
2268{
2269    TScreen *screen = TScreenOf(xw);
2270    int ncells = 0;
2271
2272    if (len != 0) {
2273#if OPT_RENDERWIDE
2274	XftCharSpec *sbuf;
2275	XftFont *wfont;
2276	Cardinal src, dst;
2277	XftFont *lastFont = 0;
2278	XftFont *currFont = 0;
2279	Cardinal start = 0;
2280	int charWidth;
2281	int fontnum = screen->menu_font_number;
2282	int fwidth = FontWidth(screen);
2283
2284#if OPT_ISO_COLORS
2285	if ((flags & UNDERLINE)
2286	    && !screen->colorULMode
2287	    && screen->italicULMode
2288	    && XFT_FONT(renderWideItal[fontnum])) {
2289	    wfont = XFT_FONT(renderWideItal[fontnum]);
2290	} else
2291#endif
2292	    if ((flags & BOLDATTR(screen))
2293		&& UseBoldFont(screen)
2294		&& XFT_FONT(renderWideBold[fontnum])) {
2295	    wfont = XFT_FONT(renderWideBold[fontnum]);
2296	} else {
2297	    wfont = XFT_FONT(renderWideNorm[fontnum]);
2298	}
2299
2300	BumpTypedBuffer(XftCharSpec, len);
2301	sbuf = BfBuf(XftCharSpec);
2302
2303	for (src = dst = 0; src < len; src++) {
2304	    FcChar32 wc = *text++;
2305
2306	    charWidth = XtermCellWidth(xw, (wchar_t) wc);
2307	    if (charWidth < 0)
2308		continue;
2309
2310	    sbuf[dst].ucs4 = wc;
2311	    sbuf[dst].x = (short) (x + fwidth * ncells);
2312	    sbuf[dst].y = (short) (y);
2313
2314	    currFont = (charWidth == 2 && wfont != 0) ? wfont : font;
2315	    ncells += charWidth;
2316
2317	    if (lastFont != currFont) {
2318		if ((lastFont != 0) && really) {
2319		    XftDrawCharSpec(screen->renderDraw,
2320				    color,
2321				    lastFont,
2322				    sbuf + start,
2323				    (int) (dst - start));
2324		}
2325		start = dst;
2326		lastFont = currFont;
2327	    }
2328	    ++dst;
2329	}
2330	if ((dst != start) && really) {
2331	    XftDrawCharSpec(screen->renderDraw,
2332			    color,
2333			    lastFont,
2334			    sbuf + start,
2335			    (int) (dst - start));
2336	}
2337#else /* !OPT_RENDERWIDE */
2338	if (really) {
2339	    XftChar8 *buffer;
2340	    int dst;
2341
2342	    BumpTypedBuffer(XftChar8, len);
2343	    buffer = BfBuf(XftChar8);
2344
2345	    for (dst = 0; dst < (int) len; ++dst)
2346		buffer[dst] = CharOf(text[dst]);
2347
2348	    XftDrawString8(screen->renderDraw,
2349			   color,
2350			   font,
2351			   x, y, buffer, (int) len);
2352	}
2353	ncells = (int) len;
2354#endif
2355    }
2356    return ncells;
2357}
2358#define xtermXftWidth(xw, flags, color, font, x, y, chars, len) \
2359   xtermXftDrawString(xw, flags, color, font, x, y, chars, len, False)
2360#endif /* OPT_RENDERFONT */
2361
2362#if OPT_WIDE_CHARS
2363/*
2364 * Map characters commonly "fixed" by groff back to their ASCII equivalents.
2365 * Also map other useful equivalents.
2366 */
2367unsigned
2368AsciiEquivs(unsigned ch)
2369{
2370    switch (ch) {
2371    case 0x2010:		/* groff "-" */
2372    case 0x2011:
2373    case 0x2012:
2374    case 0x2013:
2375    case 0x2014:
2376    case 0x2015:
2377    case 0x2212:		/* groff "\-" */
2378	ch = '-';
2379	break;
2380    case 0x2018:		/* groff "`" */
2381	ch = '`';
2382	break;
2383    case 0x2019:		/* groff ' */
2384	ch = '\'';
2385	break;
2386    case 0x201C:		/* groff lq */
2387    case 0x201D:		/* groff rq */
2388	ch = '"';
2389	break;
2390    case 0x2329:		/* groff ".URL" */
2391	ch = '<';
2392	break;
2393    case 0x232a:		/* groff ".URL" */
2394	ch = '>';
2395	break;
2396    default:
2397	if (ch >= 0xff01 && ch <= 0xff5e) {
2398	    /* "Fullwidth" codes (actually double-width) */
2399	    ch -= 0xff00;
2400	    ch += ANSI_SPA;
2401	    break;
2402	}
2403    }
2404    return ch;
2405}
2406
2407/*
2408 * Actually this should be called "groff_workaround()" - for the places where
2409 * groff stomps on compatibility.  Still, if enough people get used to it,
2410 * this might someday become a quasi-standard.
2411 */
2412static int
2413ucs_workaround(XtermWidget xw,
2414	       unsigned ch,
2415	       unsigned flags,
2416	       GC gc,
2417	       int x,
2418	       int y,
2419	       int chrset,
2420	       int on_wide)
2421{
2422    TScreen *screen = TScreenOf(xw);
2423    int fixed = False;
2424
2425    if (screen->wide_chars && screen->utf8_mode && ch > 256) {
2426	IChar eqv = (IChar) AsciiEquivs(ch);
2427
2428	if (eqv != (IChar) ch) {
2429	    int width = my_wcwidth((int) ch);
2430
2431	    do {
2432		drawXtermText(xw,
2433			      flags,
2434			      gc,
2435			      x,
2436			      y,
2437			      chrset,
2438			      &eqv,
2439			      1,
2440			      on_wide);
2441		x += FontWidth(screen);
2442		eqv = '?';
2443	    } while (width-- > 1);
2444
2445	    fixed = True;
2446	} else if (ch == HIDDEN_CHAR) {
2447	    fixed = True;
2448	}
2449    }
2450    return fixed;
2451}
2452#endif
2453
2454/*
2455 * Use this when the characters will not fill the cell area properly.  Fill the
2456 * area where we'll write the characters, otherwise we'll get gaps between
2457 * them, e.g., in the original background color.
2458 *
2459 * The cursor is a special case, because the XFillRectangle call only uses the
2460 * foreground, while we've set the cursor color in the background.  So we need
2461 * a special GC for that.
2462 */
2463static void
2464xtermFillCells(XtermWidget xw,
2465	       unsigned flags,
2466	       GC gc,
2467	       int x,
2468	       int y,
2469	       Cardinal len)
2470{
2471    TScreen *screen = TScreenOf(xw);
2472    VTwin *currentWin = WhichVWin(screen);
2473
2474    if (!(flags & NOBACKGROUND)) {
2475	CgsEnum srcId = getCgsId(xw, currentWin, gc);
2476	CgsEnum dstId = gcMAX;
2477	Pixel fg = getCgsFore(xw, currentWin, gc);
2478	Pixel bg = getCgsBack(xw, currentWin, gc);
2479
2480	switch (srcId) {
2481	case gcVTcursNormal:
2482	case gcVTcursReverse:
2483	    dstId = gcVTcursOutline;
2484	    break;
2485	case gcVTcursFilled:
2486	case gcVTcursOutline:
2487	    /* FIXME */
2488	    break;
2489	case gcNorm:
2490	    dstId = gcNormReverse;
2491	    break;
2492	case gcNormReverse:
2493	    dstId = gcNorm;
2494	    break;
2495	case gcBold:
2496	    dstId = gcBoldReverse;
2497	    break;
2498	case gcBoldReverse:
2499	    dstId = gcBold;
2500	    break;
2501#if OPT_BOX_CHARS
2502	case gcLine:
2503	case gcDots:
2504	    /* FIXME */
2505	    break;
2506#endif
2507#if OPT_DEC_CHRSET
2508	case gcCNorm:
2509	case gcCBold:
2510	    /* FIXME */
2511	    break;
2512#endif
2513#if OPT_WIDE_CHARS
2514	case gcWide:
2515	    dstId = gcWideReverse;
2516	    break;
2517	case gcWBold:
2518	    dstId = gcBoldReverse;
2519	    break;
2520	case gcWideReverse:
2521	case gcWBoldReverse:
2522	    /* FIXME */
2523	    break;
2524#endif
2525#if OPT_TEK4014
2526	case gcTKcurs:
2527	    /* FIXME */
2528	    break;
2529#endif
2530	case gcMAX:
2531	    break;
2532	}
2533
2534	if (dstId != gcMAX) {
2535	    setCgsFore(xw, currentWin, dstId, bg);
2536	    setCgsBack(xw, currentWin, dstId, fg);
2537
2538	    XFillRectangle(screen->display, VWindow(screen),
2539			   getCgsGC(xw, currentWin, dstId),
2540			   x, y,
2541			   len * (Cardinal) FontWidth(screen),
2542			   (unsigned) FontHeight(screen));
2543	}
2544    }
2545}
2546
2547#if OPT_TRACE
2548static void
2549xtermSetClipRectangles(Display * dpy,
2550		       GC gc,
2551		       int x,
2552		       int y,
2553		       XRectangle * rp,
2554		       Cardinal nr,
2555		       int order)
2556{
2557#if 0
2558    TScreen *screen = TScreenOf(term);
2559    Drawable draw = VWindow(screen);
2560
2561    XSetClipMask(dpy, gc, None);
2562    XDrawRectangle(screen->display, draw, gc,
2563		   x + rp->x - 1,
2564		   y + rp->y - 1,
2565		   rp->width,
2566		   rp->height);
2567#endif
2568
2569    XSetClipRectangles(dpy, gc,
2570		       x, y, rp, (int) nr, order);
2571    TRACE(("clipping @(%3d,%3d) (%3d,%3d)..(%3d,%3d)\n",
2572	   y, x,
2573	   rp->y, rp->x, rp->height, rp->width));
2574}
2575
2576#else
2577#define xtermSetClipRectangles(dpy, gc, x, y, rp, nr, order) \
2578	    XSetClipRectangles(dpy, gc, x, y, rp, (int) nr, order)
2579#endif
2580
2581#if OPT_CLIP_BOLD
2582/*
2583 * This special case is a couple of percent slower, but avoids a lot of pixel
2584 * trash in rxcurses' hanoi.cmd demo (e.g., 10x20 font).
2585 */
2586#define beginClipping(screen,gc,pwidth,plength) \
2587	    if (screen->use_clipping && (pwidth > 2)) { \
2588		XRectangle clip; \
2589		int clip_x = x; \
2590		int clip_y = y - FontHeight(screen) + FontDescent(screen); \
2591		clip.x = 0; \
2592		clip.y = 0; \
2593		clip.height = (unsigned short) FontHeight(screen); \
2594		clip.width = (unsigned short) (pwidth * plength); \
2595		xtermSetClipRectangles(screen->display, gc, \
2596				       clip_x, clip_y, \
2597				       &clip, 1, Unsorted); \
2598	    }
2599#define endClipping(screen,gc) \
2600	    XSetClipMask(screen->display, gc, None)
2601#else
2602#define beginClipping(screen,gc,pwidth,plength)		/* nothing */
2603#define endClipping(screen,gc)	/* nothing */
2604#endif /* OPT_CLIP_BOLD */
2605
2606#if OPT_CLIP_BOLD && OPT_RENDERFONT && defined(HAVE_XFTDRAWSETCLIP) && defined(HAVE_XFTDRAWSETCLIPRECTANGLES)
2607#define beginXftClipping(screen,px,py,plength) \
2608	    if (screen->use_clipping && (FontWidth(screen) > 2)) { \
2609		XRectangle clip; \
2610		int clip_x = px; \
2611		int clip_y = py - FontHeight(screen) + FontDescent(screen); \
2612		clip.x = 0; \
2613		clip.y = 0; \
2614		clip.height = (unsigned short) (FontHeight(screen)); \
2615		clip.width = (unsigned short) (FontWidth(screen) * plength); \
2616		XftDrawSetClipRectangles (screen->renderDraw, \
2617					  clip_x, clip_y, \
2618					  &clip, 1); \
2619	    }
2620#define endXftClipping(screen) \
2621	    XftDrawSetClip (screen->renderDraw, 0)
2622#else
2623#define beginXftClipping(screen,px,py,plength)	/* nothing */
2624#define endXftClipping(screen)	/* nothing */
2625#endif /* OPT_CLIP_BOLD */
2626
2627#if OPT_RENDERFONT
2628static int
2629drawClippedXftString(XtermWidget xw,
2630		     unsigned flags,
2631		     XftFont * font,
2632		     XftColor * fg_color,
2633		     int x,
2634		     int y,
2635		     IChar * text,
2636		     Cardinal len)
2637{
2638    int ncells = xtermXftWidth(xw, flags,
2639			       fg_color,
2640			       font, x, y,
2641			       text,
2642			       len);
2643    TScreen *screen = TScreenOf(xw);
2644
2645    beginXftClipping(screen, x, y, ncells);
2646    xtermXftDrawString(xw, flags,
2647		       fg_color,
2648		       font, x, y,
2649		       text,
2650		       len,
2651		       True);
2652    endXftClipping(screen);
2653    return ncells;
2654}
2655#endif
2656
2657#ifndef NO_ACTIVE_ICON
2658#define WhichVFontData(screen,name) \
2659		(IsIcon(screen) ? &((screen)->fnt_icon) \
2660				: &((screen)->name))
2661#else
2662#define WhichVFontData(screen,name) \
2663				(&((screen)->name))
2664#endif
2665
2666/*
2667 * Draws text with the specified combination of bold/underline.  The return
2668 * value is the updated x position.
2669 */
2670int
2671drawXtermText(XtermWidget xw,
2672	      unsigned flags,
2673	      GC gc,
2674	      int x,
2675	      int y,
2676	      int chrset,
2677	      IChar * text,
2678	      Cardinal len,
2679	      int on_wide)
2680{
2681    TScreen *screen = TScreenOf(xw);
2682    Cardinal real_length = len;
2683    Cardinal underline_len = 0;
2684    /* Intended width of the font to draw (as opposed to the actual width of
2685       the X font, and the width of the default font) */
2686    int font_width = ((flags & DOUBLEWFONT) ? 2 : 1) * screen->fnt_wide;
2687    Bool did_ul = False;
2688
2689#if OPT_WIDE_CHARS
2690    if (text == 0)
2691	return 0;
2692#endif
2693#if OPT_DEC_CHRSET
2694    if (CSET_DOUBLE(chrset)) {
2695	/* We could try drawing double-size characters in the icon, but
2696	 * given that the icon font is usually nil or nil2, there
2697	 * doesn't seem to be much point.
2698	 */
2699	int inx = 0;
2700	GC gc2 = ((!IsIcon(screen) && screen->font_doublesize)
2701		  ? xterm_DoubleGC(xw, (unsigned) chrset, flags, gc, &inx)
2702		  : 0);
2703
2704	TRACE(("DRAWTEXT%c[%4d,%4d] (%d)%3d:%s\n",
2705	       screen->cursor_state == OFF ? ' ' : '*',
2706	       y, x, chrset, len,
2707	       visibleIChars(text, len)));
2708
2709	if (gc2 != 0) {		/* draw actual double-sized characters */
2710	    XFontStruct *fs = screen->double_fonts[inx].fs;
2711
2712#if OPT_RENDERFONT
2713	    if (!UsingRenderFont(xw))
2714#endif
2715	    {
2716		XRectangle rect, *rp = &rect;
2717		Cardinal nr = 1;
2718
2719		font_width *= 2;
2720		flags |= DOUBLEWFONT;
2721
2722		rect.x = 0;
2723		rect.y = 0;
2724		rect.width = (unsigned short) ((int) len * font_width);
2725		rect.height = (unsigned short) (FontHeight(screen));
2726
2727		TRACE(("drawing %s\n", visibleChrsetName((unsigned) chrset)));
2728		switch (chrset) {
2729		case CSET_DHL_TOP:
2730		    rect.y = (short) -(fs->ascent / 2);
2731		    y -= rect.y;
2732		    flags |= DOUBLEHFONT;
2733		    break;
2734		case CSET_DHL_BOT:
2735		    rect.y = (short) (rect.height - (fs->ascent / 2));
2736		    y -= rect.y;
2737		    flags |= DOUBLEHFONT;
2738		    break;
2739		default:
2740		    nr = 0;
2741		    break;
2742		}
2743
2744		if (nr) {
2745		    xtermSetClipRectangles(screen->display, gc2,
2746					   x, y, rp, nr, YXBanded);
2747		} else {
2748		    XSetClipMask(screen->display, gc2, None);
2749		}
2750	    }
2751
2752	    /* Call ourselves recursively with the new gc */
2753
2754	    /*
2755	     * If we're trying to use proportional font, or if the
2756	     * font server didn't give us what we asked for wrt
2757	     * width, position each character independently.
2758	     */
2759	    if (screen->fnt_prop
2760		|| (fs->min_bounds.width != fs->max_bounds.width)
2761		|| (fs->min_bounds.width != 2 * FontWidth(screen))) {
2762		/* It is hard to fall-through to the main
2763		   branch: in a lot of places the check
2764		   for the cached font info is for
2765		   normal/bold fonts only. */
2766		while (len--) {
2767		    x = drawXtermText(xw, flags, gc2,
2768				      x, y, 0,
2769				      text++,
2770				      1, on_wide);
2771		    x += FontWidth(screen);
2772		}
2773	    } else {
2774		x = drawXtermText(xw, flags, gc2,
2775				  x, y, 0,
2776				  text,
2777				  len, on_wide);
2778		x += (int) len *FontWidth(screen);
2779	    }
2780
2781	    TRACE(("drawtext [%4d,%4d]\n", y, x));
2782	} else {		/* simulate double-sized characters */
2783	    unsigned need = 2 * len;
2784	    IChar *temp = TypeMallocN(IChar, need);
2785	    unsigned n = 0;
2786
2787	    while (len--) {
2788		temp[n++] = *text++;
2789		temp[n++] = ' ';
2790	    }
2791	    x = drawXtermText(xw,
2792			      flags,
2793			      gc,
2794			      x, y,
2795			      0,
2796			      temp,
2797			      n,
2798			      on_wide);
2799	    free(temp);
2800	}
2801	return x;
2802    }
2803#endif
2804#if OPT_RENDERFONT
2805    if (UsingRenderFont(xw)) {
2806	VTwin *currentWin = WhichVWin(screen);
2807	Display *dpy = screen->display;
2808	XftFont *font;
2809	XGCValues values;
2810	int fontnum = screen->menu_font_number;
2811	int ncells;
2812
2813	if (!screen->renderDraw) {
2814	    int scr;
2815	    Drawable draw = VWindow(screen);
2816	    Visual *visual;
2817
2818	    scr = DefaultScreen(dpy);
2819	    visual = DefaultVisual(dpy, scr);
2820	    screen->renderDraw = XftDrawCreate(dpy, draw, visual,
2821					       DefaultColormap(dpy, scr));
2822	}
2823#if OPT_ISO_COLORS
2824	if ((flags & UNDERLINE)
2825	    && !screen->colorULMode
2826	    && screen->italicULMode
2827	    && XFT_FONT(renderFontItal[fontnum])) {
2828	    font = XFT_FONT(renderFontItal[fontnum]);
2829	    did_ul = True;
2830	} else
2831#endif
2832	    if ((flags & BOLDATTR(screen))
2833		&& UseBoldFont(screen)
2834		&& XFT_FONT(renderFontBold[fontnum])) {
2835	    font = XFT_FONT(renderFontBold[fontnum]);
2836	} else {
2837	    font = XFT_FONT(renderFontNorm[fontnum]);
2838	}
2839	values.foreground = getCgsFore(xw, currentWin, gc);
2840	values.background = getCgsBack(xw, currentWin, gc);
2841
2842	if (!(flags & NOBACKGROUND)) {
2843	    XftColor *bg_color = getXftColor(xw, values.background);
2844	    ncells = xtermXftWidth(xw, flags,
2845				   bg_color,
2846				   font, x, y,
2847				   text,
2848				   len);
2849	    XftDrawRect(screen->renderDraw,
2850			bg_color,
2851			x, y,
2852			(unsigned) (ncells * FontWidth(screen)),
2853			(unsigned) FontHeight(screen));
2854	}
2855
2856	y += font->ascent;
2857#if OPT_BOX_CHARS
2858	{
2859	    /* adding code to substitute simulated line-drawing characters */
2860	    int last, first = 0;
2861	    Dimension old_wide, old_high = 0;
2862	    int curX = x;
2863
2864	    for (last = 0; last < (int) len; last++) {
2865		Boolean replace = False;
2866		Boolean missing = False;
2867		unsigned ch = (unsigned) text[last];
2868		int nc;
2869#if OPT_WIDE_CHARS
2870
2871		if (xtermIsDecGraphic(ch)) {
2872		    /*
2873		     * Xft generally does not have the line-drawing characters
2874		     * in cells 1-31.  Assume this (we cannot inspect the
2875		     * picture easily...), and attempt to fill in from real
2876		     * line-drawing character in the font at the Unicode
2877		     * position.  Failing that, use our own box-characters.
2878		     */
2879		    if (screen->force_box_chars
2880			|| xtermXftMissing(xw, font, dec2ucs(ch))) {
2881			missing = 1;
2882		    } else {
2883			ch = dec2ucs(ch);
2884			replace = True;
2885		    }
2886		} else if (ch >= 256) {
2887		    /*
2888		     * If we're reading UTF-8 from the client, we may have a
2889		     * line-drawing character.  Translate it back to our
2890		     * box-code if Xft tells us that the glyph is missing.
2891		     */
2892		    if_OPT_WIDE_CHARS(screen, {
2893			unsigned part = ucs2dec(ch);
2894			if (xtermIsDecGraphic(part) &&
2895			    (screen->force_box_chars
2896			     || xtermXftMissing(xw, font, ch))) {
2897			    ch = part;
2898			    missing = True;
2899			}
2900		    });
2901		}
2902#else
2903		if (xtermIsDecGraphic(ch)) {
2904		    /*
2905		     * Xft generally does not have the line-drawing characters
2906		     * in cells 1-31.  Check for this, and attempt to fill in
2907		     * from real line-drawing character in the font at the
2908		     * Unicode position.  Failing that, use our own
2909		     * box-characters.
2910		     */
2911		    if (xtermXftMissing(xw, font, ch)) {
2912			missing = 1;
2913		    }
2914		}
2915#endif
2916
2917		/*
2918		 * If we now have one of our box-codes, draw it directly.
2919		 */
2920		if (missing || replace) {
2921		    /* line drawing character time */
2922		    if (last > first) {
2923			nc = drawClippedXftString(xw,
2924						  flags,
2925						  font,
2926						  getXftColor(xw, values.foreground),
2927						  curX,
2928						  y,
2929						  text + first,
2930						  (Cardinal) (last - first));
2931			curX += nc * FontWidth(screen);
2932			underline_len += (Cardinal) nc;
2933		    }
2934		    if (missing) {
2935			old_wide = screen->fnt_wide;
2936			old_high = screen->fnt_high;
2937			screen->fnt_wide = (Dimension) FontWidth(screen);
2938			screen->fnt_high = (Dimension) FontHeight(screen);
2939			xtermDrawBoxChar(xw, ch, flags, gc,
2940					 curX, y - FontAscent(screen), 1);
2941			curX += FontWidth(screen);
2942			underline_len += 1;
2943			screen->fnt_wide = old_wide;
2944			screen->fnt_high = old_high;
2945		    } else {
2946			IChar ch2 = (IChar) ch;
2947			nc = drawClippedXftString(xw,
2948						  flags,
2949						  font,
2950						  getXftColor(xw, values.foreground),
2951						  curX,
2952						  y,
2953						  &ch2,
2954						  1);
2955			curX += nc * FontWidth(screen);
2956			underline_len += (Cardinal) nc;
2957		    }
2958		    first = last + 1;
2959		}
2960	    }
2961	    if (last > first) {
2962		underline_len += (Cardinal)
2963		    drawClippedXftString(xw,
2964					 flags,
2965					 font,
2966					 getXftColor(xw, values.foreground),
2967					 curX,
2968					 y,
2969					 text + first,
2970					 (Cardinal) (last - first));
2971	    }
2972	}
2973#else
2974	{
2975	    underline_len += (Cardinal)
2976		drawClippedXftString(xw,
2977				     flags,
2978				     font,
2979				     getXftColor(xw, values.foreground),
2980				     x,
2981				     y,
2982				     text,
2983				     len);
2984	}
2985#endif /* OPT_BOX_CHARS */
2986
2987	if ((flags & UNDERLINE) && screen->underline && !did_ul) {
2988	    if (FontDescent(screen) > 1)
2989		y++;
2990	    XDrawLine(screen->display, VWindow(screen), gc,
2991		      x, y,
2992		      x + (int) underline_len * FontWidth(screen) - 1,
2993		      y);
2994	}
2995	return x + (int) len *FontWidth(screen);
2996    }
2997#endif /* OPT_RENDERFONT */
2998    /*
2999     * If we're asked to display a proportional font, do this with a fixed
3000     * pitch.  Yes, it's ugly.  But we cannot distinguish the use of xterm
3001     * as a dumb terminal vs its use as in fullscreen programs such as vi.
3002     * Hint: do not try to use a proportional font in the icon.
3003     */
3004    if (!IsIcon(screen) && !(flags & CHARBYCHAR) && screen->fnt_prop) {
3005	int adj, width;
3006	XTermFonts *font = ((flags & BOLDATTR(screen))
3007			    ? WhichVFontData(screen, fnts[fBold])
3008			    : WhichVFontData(screen, fnts[fNorm]));
3009
3010	while (len--) {
3011	    int cells = WideCells(*text);
3012#if OPT_BOX_CHARS
3013#if OPT_WIDE_CHARS
3014	    if (*text == HIDDEN_CHAR) {
3015		++text;
3016		continue;
3017	    } else
3018#endif
3019	    if (IsXtermMissingChar(screen, *text, font)) {
3020		adj = 0;
3021	    } else
3022#endif
3023	    {
3024		if_WIDE_OR_NARROW(screen, {
3025		    XChar2b temp[1];
3026		    temp[0].byte2 = LO_BYTE(*text);
3027		    temp[0].byte1 = HI_BYTE(*text);
3028		    width = XTextWidth16(font->fs, temp, 1);
3029		}
3030		, {
3031		    char temp[1];
3032		    temp[0] = (char) LO_BYTE(*text);
3033		    width = XTextWidth(font->fs, temp, 1);
3034		});
3035		adj = (FontWidth(screen) - width) / 2;
3036		if (adj < 0)
3037		    adj = 0;
3038	    }
3039	    xtermFillCells(xw, flags, gc, x, y, (Cardinal) cells);
3040	    x = drawXtermText(xw,
3041			      flags | NOBACKGROUND | CHARBYCHAR,
3042			      gc, x + adj, y, chrset,
3043			      text++, 1, on_wide) - adj;
3044	}
3045	return x;
3046    }
3047#if OPT_BOX_CHARS
3048    /* If the font is incomplete, draw some substitutions */
3049    if (!IsIcon(screen)
3050	&& !(flags & NOTRANSLATION)
3051	&& (!screen->fnt_boxes || screen->force_box_chars)) {
3052	/* Fill in missing box-characters.
3053	   Find regions without missing characters, and draw
3054	   them calling ourselves recursively.  Draw missing
3055	   characters via xtermDrawBoxChar(). */
3056	XTermFonts *font = ((flags & BOLDATTR(screen))
3057			    ? WhichVFontData(screen, fnts[fBold])
3058			    : WhichVFontData(screen, fnts[fNorm]));
3059	int last, first = 0;
3060	Bool drewBoxes = False;
3061
3062	for (last = 0; last < (int) len; last++) {
3063	    unsigned ch = (unsigned) text[last];
3064	    Bool isMissing;
3065	    int ch_width;
3066#if OPT_WIDE_CHARS
3067
3068	    if (ch == HIDDEN_CHAR) {
3069		if (last > first) {
3070		    x = drawXtermText(xw, flags | NOTRANSLATION, gc,
3071				      x, y,
3072				      chrset, text + first,
3073				      (unsigned) (last - first), on_wide);
3074		}
3075		first = last + 1;
3076		drewBoxes = True;
3077		continue;
3078	    }
3079	    ch_width = my_wcwidth((int) ch);
3080	    isMissing =
3081		IsXtermMissingChar(screen, ch,
3082				   ((on_wide || ch_width > 1)
3083				    && okFont(NormalWFont(screen)))
3084				   ? WhichVFontData(screen, fnts[fWide])
3085				   : font);
3086#else
3087	    isMissing = IsXtermMissingChar(screen, ch, font);
3088	    ch_width = 1;
3089#endif
3090	    /*
3091	     * If the character is not missing, but we're in wide-character
3092	     * mode and the character happens to be a wide-character that
3093	     * corresponds to the line-drawing set, allow the forceBoxChars
3094	     * resource (or menu entry) to force it to display using our
3095	     * tables.
3096	     */
3097	    if_OPT_WIDE_CHARS(screen, {
3098		if (!isMissing
3099		    && ch > 255
3100		    && ucs2dec(ch) < 32
3101		    && TScreenOf(xw)->force_box_chars) {
3102		    ch = ucs2dec(ch);
3103		    isMissing = True;
3104		}
3105	    });
3106
3107	    if (isMissing) {
3108		if (last > first) {
3109		    x = drawXtermText(xw, flags | NOTRANSLATION, gc,
3110				      x, y,
3111				      chrset, text + first,
3112				      (unsigned) (last - first), on_wide);
3113		}
3114#if OPT_WIDE_CHARS
3115		if (ucs_workaround(xw, ch, flags, gc,
3116				   x, y,
3117				   chrset, on_wide)) {
3118		    /*
3119		     * if true, we drew at least one cell whether or not it is
3120		     * printable
3121		     */
3122		    if (ch_width <= 0)
3123			ch_width = 1;
3124		} else
3125#endif
3126		{
3127		    if (ch_width <= 0)
3128			ch_width = 1;
3129		    xtermDrawBoxChar(xw, ch, flags, gc,
3130				     x, y,
3131				     ch_width);
3132		}
3133		x += (ch_width * FontWidth(screen));
3134		first = last + 1;
3135		drewBoxes = True;
3136	    }
3137	}
3138	if (last <= first) {
3139	    return x;
3140	}
3141	text += first;
3142	len = (Cardinal) (last - first);
3143	flags |= NOTRANSLATION;
3144	if (drewBoxes) {
3145	    return drawXtermText(xw,
3146				 flags,
3147				 gc,
3148				 x,
3149				 y,
3150				 chrset,
3151				 text,
3152				 len,
3153				 on_wide);
3154	}
3155    }
3156#endif /* OPT_BOX_CHARS */
3157    /*
3158     * Behave as if the font has (maybe Unicode-replacements for) drawing
3159     * characters in the range 1-31 (either we were not asked to ignore them,
3160     * or the caller made sure that there is none).
3161     */
3162    TRACE(("drawtext%c[%4d,%4d] (%d) %d:%s\n",
3163	   screen->cursor_state == OFF ? ' ' : '*',
3164	   y, x, chrset, len,
3165	   visibleIChars(text, len)));
3166    y += FontAscent(screen);
3167
3168#if OPT_WIDE_CHARS
3169
3170    if (screen->wide_chars || screen->unicode_font) {
3171	XChar2b *buffer;
3172	Bool needWide = False;
3173	int ascent_adjust = 0;
3174	int src, dst;
3175
3176	BumpTypedBuffer(XChar2b, len);
3177	buffer = BfBuf(XChar2b);
3178
3179	for (src = dst = 0; src < (int) len; src++) {
3180	    IChar ch = text[src];
3181
3182	    if (ch == HIDDEN_CHAR)
3183		continue;
3184
3185	    if (!needWide
3186		&& !IsIcon(screen)
3187		&& ((on_wide || my_wcwidth((int) ch) > 1)
3188		    && okFont(NormalWFont(screen)))) {
3189		needWide = True;
3190	    }
3191
3192	    /*
3193	     * bitmap-fonts are limited to 16-bits.
3194	     */
3195#if OPT_WIDER_ICHAR
3196	    if (ch > 0xffff) {
3197		ch = UCS_REPL;
3198	    }
3199#endif
3200	    buffer[dst].byte2 = LO_BYTE(ch);
3201	    buffer[dst].byte1 = HI_BYTE(ch);
3202#if OPT_MINI_LUIT
3203#define UCS2SBUF(value)	buffer[dst].byte2 = LO_BYTE(value);\
3204	    		buffer[dst].byte1 = HI_BYTE(value)
3205
3206#define Map2Sbuf(from,to) (text[src] == from) { UCS2SBUF(to); }
3207
3208	    if (screen->latin9_mode && !screen->utf8_mode && text[src] < 256) {
3209
3210		/* see http://www.cs.tut.fi/~jkorpela/latin9.html */
3211		/* *INDENT-OFF* */
3212		if Map2Sbuf(0xa4, 0x20ac)
3213		else if Map2Sbuf(0xa6, 0x0160)
3214		else if Map2Sbuf(0xa8, 0x0161)
3215		else if Map2Sbuf(0xb4, 0x017d)
3216		else if Map2Sbuf(0xb8, 0x017e)
3217		else if Map2Sbuf(0xbc, 0x0152)
3218		else if Map2Sbuf(0xbd, 0x0153)
3219		else if Map2Sbuf(0xbe, 0x0178)
3220		/* *INDENT-ON* */
3221
3222	    }
3223	    if (screen->unicode_font
3224		&& (text[src] == ANSI_DEL ||
3225		    text[src] < ANSI_SPA)) {
3226		unsigned ni = dec2ucs((unsigned) ((text[src] == ANSI_DEL)
3227						  ? 0
3228						  : text[src]));
3229		UCS2SBUF(ni);
3230	    }
3231#endif /* OPT_MINI_LUIT */
3232	    ++dst;
3233	}
3234	/* FIXME This is probably wrong. But it works. */
3235	underline_len = len;
3236
3237	/* Set the drawing font */
3238	if (!(flags & (DOUBLEHFONT | DOUBLEWFONT))) {
3239	    VTwin *currentWin = WhichVWin(screen);
3240	    VTFontEnum fntId;
3241	    CgsEnum cgsId;
3242	    Pixel fg = getCgsFore(xw, currentWin, gc);
3243	    Pixel bg = getCgsBack(xw, currentWin, gc);
3244
3245	    if (needWide
3246		&& (okFont(NormalWFont(screen)) || okFont(BoldWFont(screen)))) {
3247		if ((flags & BOLDATTR(screen)) != 0
3248		    && okFont(BoldWFont(screen))) {
3249		    fntId = fWBold;
3250		    cgsId = gcWBold;
3251		} else {
3252		    fntId = fWide;
3253		    cgsId = gcWide;
3254		}
3255	    } else if ((flags & BOLDATTR(screen)) != 0
3256		       && okFont(BoldFont(screen))) {
3257		fntId = fBold;
3258		cgsId = gcBold;
3259	    } else {
3260		fntId = fNorm;
3261		cgsId = gcNorm;
3262	    }
3263
3264	    setCgsFore(xw, currentWin, cgsId, fg);
3265	    setCgsBack(xw, currentWin, cgsId, bg);
3266	    gc = getCgsGC(xw, currentWin, cgsId);
3267
3268	    if (fntId != fNorm) {
3269		XFontStruct *thisFp = WhichVFont(screen, fnts[fntId].fs);
3270		ascent_adjust = (thisFp->ascent
3271				 - NormalFont(screen)->ascent);
3272		if (thisFp->max_bounds.width ==
3273		    NormalFont(screen)->max_bounds.width * 2) {
3274		    underline_len = real_length = (Cardinal) (dst * 2);
3275		} else if (cgsId == gcWide || cgsId == gcWBold) {
3276		    underline_len = real_length = (Cardinal) (dst * 2);
3277		    xtermFillCells(xw,
3278				   flags,
3279				   gc,
3280				   x,
3281				   y - thisFp->ascent,
3282				   real_length);
3283		}
3284	    }
3285	}
3286
3287	if (flags & NOBACKGROUND) {
3288	    XDrawString16(screen->display,
3289			  VWindow(screen), gc,
3290			  x, y + ascent_adjust,
3291			  buffer, dst);
3292	} else {
3293	    XDrawImageString16(screen->display,
3294			       VWindow(screen), gc,
3295			       x, y + ascent_adjust,
3296			       buffer, dst);
3297	}
3298
3299	if ((flags & BOLDATTR(screen)) && screen->enbolden) {
3300	    beginClipping(screen, gc, (Cardinal) font_width, len);
3301	    XDrawString16(screen->display, VWindow(screen), gc,
3302			  x + 1,
3303			  y + ascent_adjust,
3304			  buffer, dst);
3305	    endClipping(screen, gc);
3306	}
3307
3308    } else
3309#endif /* OPT_WIDE_CHARS */
3310    {
3311	int length = (int) len;	/* X should have used unsigned */
3312#if OPT_WIDE_CHARS
3313	char *buffer;
3314	int dst;
3315
3316	BumpTypedBuffer(char, len);
3317	buffer = BfBuf(char);
3318
3319	for (dst = 0; dst < length; ++dst)
3320	    buffer[dst] = (char) LO_BYTE(text[dst]);
3321#else
3322	char *buffer = (char *) text;
3323#endif
3324
3325	if (flags & NOBACKGROUND) {
3326	    XDrawString(screen->display, VWindow(screen), gc,
3327			x, y, buffer, length);
3328	} else {
3329	    XDrawImageString(screen->display, VWindow(screen), gc,
3330			     x, y, buffer, length);
3331	}
3332	underline_len = (Cardinal) length;
3333	if ((flags & BOLDATTR(screen)) && screen->enbolden) {
3334	    beginClipping(screen, gc, font_width, length);
3335	    XDrawString(screen->display, VWindow(screen), gc,
3336			x + 1, y, buffer, length);
3337	    endClipping(screen, gc);
3338	}
3339    }
3340
3341    if ((flags & UNDERLINE) && screen->underline && !did_ul) {
3342	if (FontDescent(screen) > 1)
3343	    y++;
3344	XDrawLine(screen->display, VWindow(screen), gc,
3345		  x, y, (x + (int) underline_len * font_width - 1), y);
3346    }
3347
3348    return x + (int) real_length *FontWidth(screen);
3349}
3350
3351#if OPT_WIDE_CHARS
3352/*
3353 * Allocate buffer - workaround for wide-character interfaces.
3354 */
3355void
3356allocXtermChars(ScrnPtr * buffer, Cardinal length)
3357{
3358    if (*buffer == 0) {
3359	*buffer = (ScrnPtr) XtMalloc(length);
3360    } else {
3361	*buffer = (ScrnPtr) XtRealloc((char *) *buffer, length);
3362    }
3363}
3364#endif
3365
3366/* set up size hints for window manager; min 1 char by 1 char */
3367void
3368xtermSizeHints(XtermWidget xw, int scrollbarWidth)
3369{
3370    TScreen *screen = TScreenOf(xw);
3371
3372    TRACE(("xtermSizeHints\n"));
3373    TRACE(("   border    %d\n", xw->core.border_width));
3374    TRACE(("   scrollbar %d\n", scrollbarWidth));
3375
3376    xw->hints.base_width = 2 * screen->border + scrollbarWidth;
3377    xw->hints.base_height = 2 * screen->border;
3378
3379#if OPT_TOOLBAR
3380    TRACE(("   toolbar   %d\n", ToolbarHeight(xw)));
3381
3382    xw->hints.base_height += ToolbarHeight(xw);
3383    xw->hints.base_height += BorderWidth(xw) * 2;
3384    xw->hints.base_width += BorderWidth(xw) * 2;
3385#endif
3386
3387    xw->hints.width_inc = FontWidth(screen);
3388    xw->hints.height_inc = FontHeight(screen);
3389    xw->hints.min_width = xw->hints.base_width + xw->hints.width_inc;
3390    xw->hints.min_height = xw->hints.base_height + xw->hints.height_inc;
3391
3392    xw->hints.width = MaxCols(screen) * FontWidth(screen) + xw->hints.min_width;
3393    xw->hints.height = MaxRows(screen) * FontHeight(screen) + xw->hints.min_height;
3394
3395    xw->hints.flags |= (PSize | PBaseSize | PMinSize | PResizeInc);
3396
3397    TRACE_HINTS(&(xw->hints));
3398}
3399
3400void
3401getXtermSizeHints(XtermWidget xw)
3402{
3403    TScreen *screen = TScreenOf(xw);
3404    long supp;
3405
3406    if (!XGetWMNormalHints(screen->display, XtWindow(SHELL_OF(xw)),
3407			   &xw->hints, &supp))
3408	memset(&xw->hints, 0, sizeof(xw->hints));
3409    TRACE_HINTS(&(xw->hints));
3410}
3411
3412/*
3413 * Returns a GC, selected according to the font (reverse/bold/normal) that is
3414 * required for the current position (implied).  The GC is updated with the
3415 * current screen foreground and background colors.
3416 */
3417GC
3418updatedXtermGC(XtermWidget xw, unsigned flags, unsigned fg_bg, Bool hilite)
3419{
3420    TScreen *screen = TScreenOf(xw);
3421    VTwin *win = WhichVWin(screen);
3422    CgsEnum cgsId = gcMAX;
3423    unsigned my_fg = extract_fg(xw, fg_bg, flags);
3424    unsigned my_bg = extract_bg(xw, fg_bg, flags);
3425    Pixel fg_pix = getXtermForeground(xw, flags, my_fg);
3426    Pixel bg_pix = getXtermBackground(xw, flags, my_bg);
3427    Pixel xx_pix;
3428#if OPT_HIGHLIGHT_COLOR
3429    Pixel selbg_pix = T_COLOR(screen, HIGHLIGHT_BG);
3430    Pixel selfg_pix = T_COLOR(screen, HIGHLIGHT_FG);
3431    Boolean always = screen->hilite_color;
3432    Boolean use_selbg = (Boolean) (always ||
3433				   isNotForeground(xw, fg_pix, bg_pix, selbg_pix));
3434    Boolean use_selfg = (Boolean) (always &&
3435				   isNotBackground(xw, fg_pix, bg_pix, selfg_pix));
3436#endif
3437
3438    (void) fg_bg;
3439    (void) my_bg;
3440    (void) my_fg;
3441
3442    /*
3443     * Discard video attributes overridden by colorXXXMode's.
3444     */
3445    checkVeryBoldColors(flags, my_fg);
3446
3447    if (ReverseOrHilite(screen, flags, hilite)) {
3448	if (flags & BOLDATTR(screen)) {
3449	    cgsId = gcBoldReverse;
3450	} else {
3451	    cgsId = gcNormReverse;
3452	}
3453
3454#if OPT_HIGHLIGHT_COLOR
3455	if (!screen->hilite_color) {
3456	    if (selbg_pix != T_COLOR(screen, TEXT_FG)
3457		&& selbg_pix != fg_pix
3458		&& selbg_pix != bg_pix
3459		&& selbg_pix != xw->dft_foreground) {
3460		bg_pix = fg_pix;
3461		fg_pix = selbg_pix;
3462	    }
3463	}
3464#endif
3465	EXCHANGE(fg_pix, bg_pix, xx_pix);
3466#if OPT_HIGHLIGHT_COLOR
3467	if (screen->hilite_color) {
3468	    if (screen->hilite_reverse) {
3469		if (use_selbg) {
3470		    if (use_selfg)
3471			bg_pix = fg_pix;
3472		    else
3473			fg_pix = bg_pix;
3474		}
3475		if (use_selbg)
3476		    bg_pix = selbg_pix;
3477		if (use_selfg)
3478		    fg_pix = selfg_pix;
3479	    }
3480	}
3481#endif
3482    } else {
3483	if (flags & BOLDATTR(screen)) {
3484	    cgsId = gcBold;
3485	} else {
3486	    cgsId = gcNorm;
3487	}
3488    }
3489#if OPT_HIGHLIGHT_COLOR
3490    if (!screen->hilite_color || !screen->hilite_reverse) {
3491	if (hilite && !screen->hilite_reverse) {
3492	    if (use_selbg)
3493		bg_pix = selbg_pix;
3494	    if (use_selfg)
3495		fg_pix = selfg_pix;
3496	}
3497    }
3498#endif
3499
3500#if OPT_BLINK_TEXT
3501    if ((screen->blink_state == ON) && (!screen->blink_as_bold) && (flags & BLINK)) {
3502	fg_pix = bg_pix;
3503    }
3504#endif
3505
3506    setCgsFore(xw, win, cgsId, fg_pix);
3507    setCgsBack(xw, win, cgsId, bg_pix);
3508    return getCgsGC(xw, win, cgsId);
3509}
3510
3511/*
3512 * Resets the foreground/background of the GC returned by 'updatedXtermGC()'
3513 * to the values that would be set in SGR_Foreground and SGR_Background. This
3514 * duplicates some logic, but only modifies 1/4 as many GC's.
3515 */
3516void
3517resetXtermGC(XtermWidget xw, unsigned flags, Bool hilite)
3518{
3519    TScreen *screen = TScreenOf(xw);
3520    VTwin *win = WhichVWin(screen);
3521    CgsEnum cgsId = gcMAX;
3522    Pixel fg_pix = getXtermForeground(xw, flags, xw->cur_foreground);
3523    Pixel bg_pix = getXtermBackground(xw, flags, xw->cur_background);
3524
3525    checkVeryBoldColors(flags, xw->cur_foreground);
3526
3527    if (ReverseOrHilite(screen, flags, hilite)) {
3528	if (flags & BOLDATTR(screen)) {
3529	    cgsId = gcBoldReverse;
3530	} else {
3531	    cgsId = gcNormReverse;
3532	}
3533
3534	setCgsFore(xw, win, cgsId, bg_pix);
3535	setCgsBack(xw, win, cgsId, fg_pix);
3536
3537    } else {
3538	if (flags & BOLDATTR(screen)) {
3539	    cgsId = gcBold;
3540	} else {
3541	    cgsId = gcNorm;
3542	}
3543
3544	setCgsFore(xw, win, cgsId, fg_pix);
3545	setCgsBack(xw, win, cgsId, bg_pix);
3546    }
3547}
3548
3549#if OPT_ISO_COLORS
3550/*
3551 * Extract the foreground-color index from a color pair.
3552 * If we've got BOLD or UNDERLINE color-mode active, those will be used.
3553 */
3554unsigned
3555extract_fg(XtermWidget xw, unsigned color, unsigned flags)
3556{
3557    unsigned fg = ExtractForeground(color);
3558
3559    if (TScreenOf(xw)->colorAttrMode
3560	|| (fg == ExtractBackground(color))) {
3561	fg = MapToColorMode(fg, TScreenOf(xw), flags);
3562    }
3563    return fg;
3564}
3565
3566/*
3567 * Extract the background-color index from a color pair.
3568 * If we've got INVERSE color-mode active, that will be used.
3569 */
3570unsigned
3571extract_bg(XtermWidget xw, unsigned color, unsigned flags)
3572{
3573    unsigned bg = ExtractBackground(color);
3574
3575    if (TScreenOf(xw)->colorAttrMode
3576	|| (bg == ExtractForeground(color))) {
3577	if (TScreenOf(xw)->colorRVMode && (flags & INVERSE))
3578	    bg = COLOR_RV;
3579    }
3580    return bg;
3581}
3582
3583/*
3584 * Combine the current foreground and background into a single 8-bit number.
3585 * Note that we're storing the SGR foreground, since cur_foreground may be set
3586 * to COLOR_UL, COLOR_BD or COLOR_BL, which would make the code larger than 8
3587 * bits.
3588 *
3589 * This assumes that fg/bg are equal when we override with one of the special
3590 * attribute colors.
3591 */
3592CellColor
3593makeColorPair(int fg, int bg)
3594{
3595    unsigned my_bg = (bg >= 0) && (bg < NUM_ANSI_COLORS) ? (unsigned) bg : 0;
3596    unsigned my_fg = (fg >= 0) && (fg < NUM_ANSI_COLORS) ? (unsigned) fg : my_bg;
3597
3598    return (CellColor) (my_fg | (my_bg << COLOR_BITS));
3599}
3600
3601/*
3602 * Using the "current" SGR background, clear a rectangle.
3603 */
3604void
3605ClearCurBackground(XtermWidget xw,
3606		   int top,
3607		   int left,
3608		   unsigned height,
3609		   unsigned width)
3610{
3611    TScreen *screen = TScreenOf(xw);
3612
3613    TRACE(("ClearCurBackground(%d,%d,%d,%d) %d\n",
3614	   top, left, height, width, xw->cur_background));
3615
3616    if (VWindow(screen)) {
3617	set_background(xw, xw->cur_background);
3618
3619	XClearArea(screen->display, VWindow(screen),
3620		   left, top, width, height, False);
3621
3622	set_background(xw, -1);
3623    }
3624}
3625#endif /* OPT_ISO_COLORS */
3626
3627/*
3628 * Returns a single base character for the given cell.
3629 */
3630unsigned
3631getXtermCell(TScreen * screen, int row, int col)
3632{
3633    LineData *ld = getLineData(screen, row);
3634
3635    assert(ld && (col < (int) ld->lineSize));
3636    return ((ld && (col < (int) ld->lineSize))
3637	    ? ld->charData[col]
3638	    : (unsigned) ' ');
3639}
3640
3641/*
3642 * Sets a single base character for the given cell.
3643 */
3644void
3645putXtermCell(TScreen * screen, int row, int col, int ch)
3646{
3647    LineData *ld = getLineData(screen, row);
3648
3649    assert(ld && (col < (int) ld->lineSize));
3650    if (ld && (col < (int) ld->lineSize)) {
3651	ld->charData[col] = (CharData) ch;
3652	if_OPT_WIDE_CHARS(screen, {
3653	    size_t off;
3654	    for_each_combData(off, ld) {
3655		ld->combData[off][col] = 0;
3656	    }
3657	});
3658    }
3659}
3660
3661#if OPT_WIDE_CHARS
3662/*
3663 * Add a combining character for the given cell
3664 */
3665void
3666addXtermCombining(TScreen * screen, int row, int col, unsigned ch)
3667{
3668    if (ch != 0) {
3669	LineData *ld = getLineData(screen, row);
3670	size_t off;
3671
3672	TRACE(("addXtermCombining %d,%d %#x (%d)\n",
3673	       row, col, ch, my_wcwidth((wchar_t) ch)));
3674
3675	for_each_combData(off, ld) {
3676	    if (!ld->combData[off][col]) {
3677		ld->combData[off][col] = (CharData) ch;
3678		break;
3679	    }
3680	}
3681    }
3682}
3683#endif
3684
3685#ifdef HAVE_CONFIG_H
3686#ifdef USE_MY_MEMMOVE
3687void *
3688my_memmove(void *s1, void *s2, size_t n)
3689{
3690    if (n != 0) {
3691	char *p1 = (char *) s1;
3692	char *p2 = (char *) s2;
3693
3694	if ((p1 + n > p2) && (p2 + n > p1)) {
3695	    static char *bfr;
3696	    static size_t length;
3697	    size_t j;
3698	    if (length < n) {
3699		length = (n * 3) / 2;
3700		bfr = ((bfr != 0)
3701		       ? TypeRealloc(char, length, bfr)
3702		       : TypeMallocN(char, length));
3703		if (bfr == NULL)
3704		    SysError(ERROR_MMALLOC);
3705	    }
3706	    for (j = 0; j < n; j++)
3707		bfr[j] = p2[j];
3708	    p2 = bfr;
3709	}
3710	while (n-- != 0)
3711	    p1[n] = p2[n];
3712    }
3713    return s1;
3714}
3715#endif /* USE_MY_MEMMOVE */
3716
3717#ifndef HAVE_STRERROR
3718char *
3719my_strerror(int n)
3720{
3721    extern char *sys_errlist[];
3722    extern int sys_nerr;
3723    if (n > 0 && n < sys_nerr)
3724	return sys_errlist[n];
3725    return "?";
3726}
3727#endif
3728#endif
3729
3730void
3731update_keyboard_type(void)
3732{
3733    update_delete_del();
3734    update_tcap_fkeys();
3735    update_old_fkeys();
3736    update_hp_fkeys();
3737    update_sco_fkeys();
3738    update_sun_fkeys();
3739    update_sun_kbd();
3740}
3741
3742void
3743set_keyboard_type(XtermWidget xw, xtermKeyboardType type, Bool set)
3744{
3745    xtermKeyboardType save = xw->keyboard.type;
3746
3747    TRACE(("set_keyboard_type(%s, %s) currently %s\n",
3748	   visibleKeyboardType(type),
3749	   BtoS(set),
3750	   visibleKeyboardType(xw->keyboard.type)));
3751    if (set) {
3752	xw->keyboard.type = type;
3753    } else {
3754	xw->keyboard.type = keyboardIsDefault;
3755    }
3756
3757    if (save != xw->keyboard.type) {
3758	update_keyboard_type();
3759    }
3760}
3761
3762void
3763toggle_keyboard_type(XtermWidget xw, xtermKeyboardType type)
3764{
3765    xtermKeyboardType save = xw->keyboard.type;
3766
3767    TRACE(("toggle_keyboard_type(%s) currently %s\n",
3768	   visibleKeyboardType(type),
3769	   visibleKeyboardType(xw->keyboard.type)));
3770    if (xw->keyboard.type == type) {
3771	xw->keyboard.type = keyboardIsDefault;
3772    } else {
3773	xw->keyboard.type = type;
3774    }
3775
3776    if (save != xw->keyboard.type) {
3777	update_keyboard_type();
3778    }
3779}
3780
3781void
3782init_keyboard_type(XtermWidget xw, xtermKeyboardType type, Bool set)
3783{
3784    static Bool wasSet = False;
3785
3786    TRACE(("init_keyboard_type(%s, %s) currently %s\n",
3787	   visibleKeyboardType(type),
3788	   BtoS(set),
3789	   visibleKeyboardType(xw->keyboard.type)));
3790    if (set) {
3791	if (wasSet) {
3792	    fprintf(stderr, "Conflicting keyboard type option (%u/%u)\n",
3793		    xw->keyboard.type, type);
3794	}
3795	xw->keyboard.type = type;
3796	wasSet = True;
3797	update_keyboard_type();
3798    }
3799}
3800
3801/*
3802 * If the keyboardType resource is set, use that, overriding the individual
3803 * boolean resources for different keyboard types.
3804 */
3805void
3806decode_keyboard_type(XtermWidget xw, XTERM_RESOURCE * rp)
3807{
3808#define DATA(n, t, f) { n, t, XtOffsetOf(XTERM_RESOURCE, f) }
3809#define FLAG(n) *(Boolean *)(((char *)rp) + table[n].offset)
3810    static struct {
3811	const char *name;
3812	xtermKeyboardType type;
3813	unsigned offset;
3814    } table[] = {
3815#if OPT_HP_FUNC_KEYS
3816	DATA(NAME_HP_KT, keyboardIsHP, hpFunctionKeys),
3817#endif
3818#if OPT_SCO_FUNC_KEYS
3819	    DATA(NAME_SCO_KT, keyboardIsSCO, scoFunctionKeys),
3820#endif
3821#if OPT_SUN_FUNC_KEYS
3822	    DATA(NAME_SUN_KT, keyboardIsSun, sunFunctionKeys),
3823#endif
3824#if OPT_SUNPC_KBD
3825	    DATA(NAME_VT220_KT, keyboardIsVT220, sunKeyboard),
3826#endif
3827#if OPT_TCAP_FKEYS
3828	    DATA(NAME_TCAP_KT, keyboardIsTermcap, termcapKeys),
3829#endif
3830    };
3831    Cardinal n;
3832
3833    TRACE(("decode_keyboard_type(%s)\n", rp->keyboardType));
3834    if (!x_strcasecmp(rp->keyboardType, "unknown")) {
3835	/*
3836	 * Let the individual resources comprise the keyboard-type.
3837	 */
3838	for (n = 0; n < XtNumber(table); ++n)
3839	    init_keyboard_type(xw, table[n].type, FLAG(n));
3840    } else if (!x_strcasecmp(rp->keyboardType, "default")) {
3841	/*
3842	 * Set the keyboard-type to the Sun/PC type, allowing modified
3843	 * function keys, etc.
3844	 */
3845	for (n = 0; n < XtNumber(table); ++n)
3846	    init_keyboard_type(xw, table[n].type, False);
3847    } else {
3848	Bool found = False;
3849
3850	/*
3851	 * Choose an individual keyboard type.
3852	 */
3853	for (n = 0; n < XtNumber(table); ++n) {
3854	    if (!x_strcasecmp(rp->keyboardType, table[n].name + 1)) {
3855		FLAG(n) = True;
3856		found = True;
3857	    } else {
3858		FLAG(n) = False;
3859	    }
3860	    init_keyboard_type(xw, table[n].type, FLAG(n));
3861	}
3862	if (!found) {
3863	    fprintf(stderr,
3864		    "KeyboardType resource \"%s\" not found\n",
3865		    rp->keyboardType);
3866	}
3867    }
3868#undef DATA
3869#undef FLAG
3870}
3871
3872#if OPT_WIDE_CHARS
3873#if defined(HAVE_WCHAR_H) && defined(HAVE_WCWIDTH)
3874/*
3875 * If xterm is running in a UTF-8 locale, it is still possible to encounter
3876 * old runtime configurations which yield incomplete or inaccurate data.
3877 */
3878static Bool
3879systemWcwidthOk(int samplesize, int samplepass)
3880{
3881    wchar_t n;
3882    int oops = 0;
3883
3884    for (n = 21; n <= 25; ++n) {
3885	int code = (int) dec2ucs((unsigned) n);
3886	int system_code = wcwidth(code);
3887	int intern_code = mk_wcwidth(code);
3888
3889	/*
3890	 * Solaris 10 wcwidth() returns "2" for all of the line-drawing (page
3891	 * 0x2500) and most of the geometric shapes (a few are excluded, just
3892	 * to make it more difficult to use).  Do a sanity check to avoid using
3893	 * it.
3894	 */
3895	if ((system_code < 0 && intern_code >= 1)
3896	    || (system_code >= 0 && intern_code != system_code)) {
3897	    TRACE(("systemWcwidthOk: broken system line-drawing wcwidth\n"));
3898	    oops += (samplepass + 1);
3899	    break;
3900	}
3901    }
3902
3903    for (n = 0; n < (wchar_t) samplesize; ++n) {
3904	int system_code = wcwidth(n);
3905	int intern_code = mk_wcwidth(n);
3906
3907	/*
3908	 * Since mk_wcwidth() is designed to check for nonspacing characters,
3909	 * and has rough range-checks for double-width characters, it will
3910	 * generally not detect cases where a code has not been assigned.
3911	 *
3912	 * Some experimentation with GNU libc suggests that up to 1/4 of the
3913	 * codes would differ, simply because the runtime library would have a
3914	 * table listing the unassigned codes, and return -1 for those.  If
3915	 * mk_wcwidth() has no information about a code, it returns 1.  On the
3916	 * other hand, if the runtime returns a positive number, the two should
3917	 * agree.
3918	 *
3919	 * The "up to" is measured for 4k, 8k, 16k of data.  With only 1k, the
3920	 * number of differences was only 77.  However, that is only one
3921	 * system, and this is only a sanity check to avoid using broken
3922	 * libraries.
3923	 */
3924	if ((system_code < 0 && intern_code >= 1)
3925	    || (system_code >= 0 && intern_code != system_code)) {
3926	    ++oops;
3927	}
3928    }
3929    TRACE(("systemWcwidthOk: %d/%d mismatches, allowed %d\n",
3930	   oops, samplesize, samplepass));
3931    return (oops <= samplepass);
3932}
3933#endif /* HAVE_WCWIDTH */
3934
3935void
3936decode_wcwidth(XtermWidget xw)
3937{
3938    int mode = ((xw->misc.cjk_width ? 2 : 0)
3939		+ (xw->misc.mk_width ? 1 : 0)
3940		+ 1);
3941
3942    switch (mode) {
3943    default:
3944#if defined(HAVE_WCHAR_H) && defined(HAVE_WCWIDTH)
3945	if (xtermEnvUTF8() &&
3946	    systemWcwidthOk(xw->misc.mk_samplesize, xw->misc.mk_samplepass)) {
3947	    my_wcwidth = wcwidth;
3948	    TRACE(("using system wcwidth() function\n"));
3949	    break;
3950	}
3951	/* FALLTHRU */
3952#endif
3953    case 2:
3954	my_wcwidth = &mk_wcwidth;
3955	TRACE(("using MK wcwidth() function\n"));
3956	break;
3957    case 3:
3958    case 4:
3959	my_wcwidth = &mk_wcwidth_cjk;
3960	TRACE(("using MK-CJK wcwidth() function\n"));
3961	break;
3962    }
3963
3964    for (first_widechar = 128; first_widechar < 4500; ++first_widechar) {
3965	if (my_wcwidth((int) first_widechar) > 1) {
3966	    TRACE(("first_widechar %#x\n", first_widechar));
3967	    break;
3968	}
3969    }
3970}
3971#endif
3972