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