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