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