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