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