Home | History | Annotate | Line # | Download | only in dist
      1 /* $XTermId: util.c,v 1.956 2024/12/01 20:14:26 tom Exp $ */
      2 
      3 /*
      4  * Copyright 1999-2023,2024 by Thomas E. Dickey
      5  *
      6  *                         All Rights Reserved
      7  *
      8  * Permission is hereby granted, free of charge, to any person obtaining a
      9  * copy of this software and associated documentation files (the
     10  * "Software"), to deal in the Software without restriction, including
     11  * without limitation the rights to use, copy, modify, merge, publish,
     12  * distribute, sublicense, and/or sell copies of the Software, and to
     13  * permit persons to whom the Software is furnished to do so, subject to
     14  * the following conditions:
     15  *
     16  * The above copyright notice and this permission notice shall be included
     17  * in all copies or substantial portions of the Software.
     18  *
     19  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
     20  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
     21  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
     22  * IN NO EVENT SHALL THE ABOVE LISTED COPYRIGHT HOLDER(S) BE LIABLE FOR ANY
     23  * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
     24  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
     25  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
     26  *
     27  * Except as contained in this notice, the name(s) of the above copyright
     28  * holders shall not be used in advertising or otherwise to promote the
     29  * sale, use or other dealings in this Software without prior written
     30  * authorization.
     31  *
     32  *
     33  * Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts.
     34  *
     35  *                         All Rights Reserved
     36  *
     37  * Permission to use, copy, modify, and distribute this software and its
     38  * documentation for any purpose and without fee is hereby granted,
     39  * provided that the above copyright notice appear in all copies and that
     40  * both that copyright notice and this permission notice appear in
     41  * supporting documentation, and that the name of Digital Equipment
     42  * Corporation not be used in advertising or publicity pertaining to
     43  * distribution of the software without specific, written prior permission.
     44  *
     45  *
     46  * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
     47  * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
     48  * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
     49  * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
     50  * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
     51  * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
     52  * SOFTWARE.
     53  */
     54 
     55 /* util.c */
     56 
     57 #include <xterm.h>
     58 
     59 #include <data.h>
     60 #include <error.h>
     61 #include <menu.h>
     62 #include <fontutils.h>
     63 #include <xstrings.h>
     64 
     65 #include <stdio.h>
     66 #include <string.h>
     67 #include <ctype.h>
     68 #include <assert.h>
     69 
     70 #if OPT_WIDE_CHARS
     71 #if defined(HAVE_WCHAR_H) && defined(HAVE_WCWIDTH)
     72 #include <wchar.h>
     73 #endif
     74 #include <wcwidth.h>
     75 #endif
     76 
     77 #ifdef HAVE_X11_EXTENSIONS_XINERAMA_H
     78 #include <X11/extensions/Xinerama.h>
     79 #endif /* HAVE_X11_EXTENSIONS_XINERAMA_H */
     80 
     81 #include <graphics.h>
     82 
     83 #define IncrementSavedLines(amount) \
     84 	    if (screen->savedlines < screen->savelines) { \
     85 		if ((screen->savedlines += amount) > screen->savelines) \
     86 		    screen->savedlines = screen->savelines; \
     87 		ScrollBarDrawThumb(xw, 1); \
     88 	    }
     89 
     90 static int handle_translated_exposure(XtermWidget xw,
     91 				      int rect_x,
     92 				      int rect_y,
     93 				      int rect_width,
     94 				      int rect_height);
     95 static void ClearLeft(XtermWidget xw);
     96 static void CopyWait(XtermWidget xw);
     97 static void horizontal_copy_area(XtermWidget xw,
     98 				 int firstchar,
     99 				 int nchars,
    100 				 int amount);
    101 static void vertical_copy_area(XtermWidget xw,
    102 			       int firstline,
    103 			       int nlines,
    104 			       int amount,
    105 			       int left,
    106 			       int right);
    107 
    108 #if OPT_WIDE_CHARS
    109 unsigned first_widechar;
    110 int (*my_wcwidth) (wchar_t);
    111 #endif
    112 
    113 #if OPT_WIDE_CHARS
    114 /*
    115  * We will modify the 'n' cells beginning at the current position.
    116  * Some of those cells may be part of multi-column characters, including
    117  * carryover from the left.  Find the limits of the multi-column characters
    118  * that we should fill with blanks, return true if filling is needed.
    119  */
    120 int
    121 DamagedCells(TScreen *screen, unsigned n, int *klp, int *krp, int row, int col)
    122 {
    123     CLineData *ld = getLineData(screen, row);
    124     int result = False;
    125 
    126     assert(ld);
    127     if (col < (int) ld->lineSize) {
    128 	int nn = (int) n;
    129 	int kl = col;
    130 	int kr = col + nn;
    131 
    132 	if (kr >= (int) ld->lineSize) {
    133 	    nn = (ld->lineSize - col - 1);
    134 	    kr = col + nn;
    135 	}
    136 
    137 	if (nn > 0) {
    138 	    assert(kl < (int) ld->lineSize);
    139 	    if (ld->charData[kl] == HIDDEN_CHAR) {
    140 		while (kl > 0) {
    141 		    if (ld->charData[--kl] != HIDDEN_CHAR) {
    142 			break;
    143 		    }
    144 		}
    145 	    } else {
    146 		kl = col + 1;
    147 	    }
    148 
    149 	    assert(kr < (int) ld->lineSize);
    150 	    if (ld->charData[kr] == HIDDEN_CHAR) {
    151 		while (kr < screen->max_col) {
    152 		    assert((kr + 1) < (int) ld->lineSize);
    153 		    if (ld->charData[++kr] != HIDDEN_CHAR) {
    154 			--kr;
    155 			break;
    156 		    }
    157 		}
    158 	    } else {
    159 		kr = col - 1;
    160 	    }
    161 
    162 	    if (klp)
    163 		*klp = kl;
    164 	    if (krp)
    165 		*krp = kr;
    166 	    result = (kr >= kl);
    167 	}
    168     }
    169 
    170     return result;
    171 }
    172 
    173 int
    174 DamagedCurCells(TScreen *screen, unsigned n, int *klp, int *krp)
    175 {
    176     return DamagedCells(screen, n, klp, krp, screen->cur_row, screen->cur_col);
    177 }
    178 #endif /* OPT_WIDE_CHARS */
    179 
    180 /*
    181  * These routines are used for the jump scroll feature
    182  */
    183 void
    184 FlushScroll(XtermWidget xw)
    185 {
    186     TScreen *screen = TScreenOf(xw);
    187     int i;
    188     int shift = INX2ROW(screen, 0);
    189     int bot = screen->max_row - shift;
    190     int refreshtop;
    191     int refreshheight;
    192     int scrolltop;
    193     int scrollheight;
    194     int left = ScrnLeftMargin(xw);
    195     int right = ScrnRightMargin(xw);
    196     Boolean full_lines = (Boolean) ((left == 0) && (right == screen->max_col));
    197 
    198     if (screen->cursor_state)
    199 	HideCursor(xw);
    200 
    201     TRACE(("FlushScroll %s-lines scroll:%d refresh %d\n",
    202 	   full_lines ? "full" : "partial",
    203 	   screen->scroll_amt,
    204 	   screen->refresh_amt));
    205 
    206     if (screen->scroll_amt > 0) {
    207 	/*
    208 	 * Lines will be scrolled "up".
    209 	 */
    210 	refreshheight = screen->refresh_amt;
    211 	scrollheight = screen->bot_marg - screen->top_marg - refreshheight + 1;
    212 	refreshtop = screen->bot_marg - refreshheight + 1 + shift;
    213 	i = screen->max_row - screen->scroll_amt + 1;
    214 	if (refreshtop > i) {
    215 	    refreshtop = i;
    216 	}
    217 
    218 	/*
    219 	 * If this is the normal (not alternate) screen, and the top margin is
    220 	 * at the top of the screen, then we will shift full lines scrolled out
    221 	 * of the scrolling region into the saved-lines.
    222 	 */
    223 	if (screen->scrollWidget
    224 	    && !screen->whichBuf
    225 	    && full_lines
    226 	    && screen->top_marg == 0) {
    227 	    scrolltop = 0;
    228 	    scrollheight += shift;
    229 	    if (scrollheight > i)
    230 		scrollheight = i;
    231 	    i = screen->bot_marg - bot;
    232 	    if (i > 0) {
    233 		refreshheight -= i;
    234 		if (refreshheight < screen->scroll_amt) {
    235 		    refreshheight = screen->scroll_amt;
    236 		}
    237 	    }
    238 	    IncrementSavedLines(screen->scroll_amt);
    239 	} else {
    240 	    scrolltop = screen->top_marg + shift;
    241 	    i = bot - (screen->bot_marg - screen->refresh_amt + screen->scroll_amt);
    242 	    if (i > 0) {
    243 		if (bot < screen->bot_marg) {
    244 		    refreshheight = screen->scroll_amt + i;
    245 		}
    246 	    } else {
    247 		scrollheight += i;
    248 		refreshheight = screen->scroll_amt;
    249 		i = screen->top_marg + screen->scroll_amt - 1 - bot;
    250 		if (i > 0) {
    251 		    refreshtop += i;
    252 		    refreshheight -= i;
    253 		}
    254 	    }
    255 	}
    256     } else {
    257 	/*
    258 	 * Lines will be scrolled "down".
    259 	 */
    260 	refreshheight = -screen->refresh_amt;
    261 	scrollheight = screen->bot_marg - screen->top_marg - refreshheight + 1;
    262 	refreshtop = screen->top_marg + shift;
    263 	scrolltop = refreshtop + refreshheight;
    264 	i = screen->bot_marg - bot;
    265 	if (i > 0) {
    266 	    scrollheight -= i;
    267 	}
    268 	i = screen->top_marg + refreshheight - 1 - bot;
    269 	if (i > 0) {
    270 	    refreshheight -= i;
    271 	}
    272     }
    273 
    274     vertical_copy_area(xw,
    275 		       scrolltop + screen->scroll_amt,
    276 		       scrollheight,
    277 		       screen->scroll_amt,
    278 		       left,
    279 		       right);
    280     ScrollSelection(screen, -(screen->scroll_amt), False);
    281     screen->scroll_amt = 0;
    282     screen->refresh_amt = 0;
    283 
    284     if (refreshheight > 0) {
    285 	ClearCurBackground(xw,
    286 			   refreshtop,
    287 			   left,
    288 			   (unsigned) refreshheight,
    289 			   (unsigned) (right + 1 - left),
    290 			   (unsigned) FontWidth(screen));
    291 	ScrnRefresh(xw,
    292 		    refreshtop,
    293 		    0,
    294 		    refreshheight,
    295 		    MaxCols(screen),
    296 		    False);
    297     }
    298     xtermTimedDbe(xw);
    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  */
    307 static Bool
    308 AddToRefresh(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  */
    353 static Bool
    354 AddToVisible(XtermWidget xw)
    355 {
    356     TScreen *screen = TScreenOf(xw);
    357     Bool result = False;
    358 
    359     if (INX2ROW(screen, screen->cur_row) <= LastRowNumber(screen)) {
    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  */
    373 static void
    374 adjustHiliteOnFwdScroll(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  */
    448 static void
    449 adjustHiliteOnBakScroll(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  */
    505 static void
    506 scrollInMargins(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_OPT_WIDE_CHARS(screen, {
    517 	if (amount != 0) {
    518 	    for (row = top; row <= screen->bot_marg; ++row) {
    519 		LineData *ld;
    520 		if ((ld = getLineData(screen, row + amount)) != NULL) {
    521 		    if (left > 0) {
    522 			if (ld->charData[left] == HIDDEN_CHAR) {
    523 			    Clear1Cell(ld, left - 1);
    524 			    Clear1Cell(ld, left);
    525 			}
    526 		    }
    527 		    if (right + 1 < (int) ld->lineSize) {
    528 			if (ld->charData[right + 1] == HIDDEN_CHAR) {
    529 			    Clear1Cell(ld, right);
    530 			    Clear1Cell(ld, right + 1);
    531 			}
    532 		    }
    533 		}
    534 	    }
    535 	}
    536     });
    537 
    538     if (amount > 0) {
    539 	for (row = top; row <= screen->bot_marg - amount; ++row) {
    540 	    if ((src = getLineData(screen, row + amount)) != NULL
    541 		&& (dst = getLineData(screen, row)) != NULL) {
    542 		CopyCells(screen, src, dst, left, length, False);
    543 	    }
    544 	}
    545 	while (row <= screen->bot_marg) {
    546 	    ClearCells(xw, 0, (unsigned) length, row, left);
    547 	    ++row;
    548 	}
    549     } else if (amount < 0) {
    550 	for (row = screen->bot_marg; row >= top - amount; --row) {
    551 	    if ((src = getLineData(screen, row + amount)) != NULL
    552 		&& (dst = getLineData(screen, row)) != NULL) {
    553 		CopyCells(screen, src, dst, left, length, True);
    554 	    }
    555 	}
    556 	while (row >= top) {
    557 	    ClearCells(xw, 0, (unsigned) length, row, left);
    558 	    --row;
    559 	}
    560     }
    561 }
    562 
    563 #if OPT_WIDE_CHARS
    564 /*
    565  * If we're repainting a section of wide-characters that, e.g., ClearCells has
    566  * repaired when finding double-cell characters, then we should account for
    567  * that in the repaint.
    568  */
    569 static void
    570 ScrnUpdate2(XtermWidget xw,
    571 	    int toprow,
    572 	    int leftcol,
    573 	    int nrows,
    574 	    int ncols,
    575 	    Bool force)
    576 {
    577     if_OPT_WIDE_CHARS(TScreenOf(xw), {
    578 	if (leftcol + ncols <= TScreenOf(xw)->max_col)
    579 	    ncols++;
    580 	if (leftcol > 0) {
    581 	    leftcol--;
    582 	    ncols++;
    583 	}
    584     });
    585     ScrnUpdate(xw, toprow, leftcol, nrows, ncols, force);
    586 }
    587 #else
    588 #define ScrnUpdate2(xw, toprow, leftcol, nrows, ncols, force) \
    589 	ScrnUpdate(xw, toprow, leftcol, nrows, ncols, force)
    590 #endif
    591 
    592 /*
    593  * scrolls the screen by amount lines, erases bottom, doesn't alter
    594  * cursor position (i.e. cursor moves down amount relative to text).
    595  * All done within the scrolling region, of course.
    596  * requires: amount > 0
    597  */
    598 void
    599 xtermScroll(XtermWidget xw, int amount)
    600 {
    601     TScreen *screen = TScreenOf(xw);
    602     int i;
    603     int refreshtop = 0;
    604     int refreshheight;
    605     Boolean save_wrap = screen->do_wrap;
    606     int left = ScrnLeftMargin(xw);
    607     int right = ScrnRightMargin(xw);
    608     Boolean scroll_all_lines = (Boolean) (screen->scrollWidget
    609 					  && !screen->whichBuf
    610 					  && screen->top_marg == 0);
    611     Boolean scroll_full_line = ((left == 0) && (right == screen->max_col));
    612 
    613     TRACE(("xtermScroll count=%d (top %d, saved %d)\n", amount,
    614 	   screen->topline, screen->savelines));
    615 
    616     screen->cursor_busy += 1;
    617     screen->cursor_moved = True;
    618 
    619     if (screen->cursor_state)
    620 	HideCursor(xw);
    621 
    622     i = screen->bot_marg - screen->top_marg + 1;
    623     if (amount > i)
    624 	amount = i;
    625 
    626     if (!scroll_full_line) {
    627 	refreshheight = 0;
    628     } else
    629 #if OPT_SCROLL_LOCK
    630 	if ((screen->allowScrollLock && screen->scroll_lock)
    631 	    || (screen->autoScrollLock && screen->topline < 0)) {
    632 	refreshheight = 0;
    633 	screen->scroll_amt = 0;
    634 	screen->refresh_amt = 0;
    635 	if (--(screen->topline) < -screen->savelines) {
    636 	    screen->topline = -screen->savelines;
    637 	    screen->scroll_dirty = True;
    638 	}
    639 	if (++(screen->savedlines) > screen->savelines) {
    640 	    screen->savedlines = screen->savelines;
    641 	}
    642     } else
    643 #endif
    644     {
    645 	if (ScrnHaveSelection(screen))
    646 	    adjustHiliteOnFwdScroll(xw, amount, scroll_all_lines);
    647 
    648 	if (screen->jumpscroll) {
    649 	    if (screen->scroll_amt > 0) {
    650 		if (!screen->fastscroll) {
    651 		    if (screen->refresh_amt + amount > i)
    652 			FlushScroll(xw);
    653 		}
    654 		screen->scroll_amt += amount;
    655 		screen->refresh_amt += amount;
    656 	    } else {
    657 		if (!screen->fastscroll) {
    658 		    if (screen->scroll_amt < 0)
    659 			FlushScroll(xw);
    660 		}
    661 		screen->scroll_amt = amount;
    662 		screen->refresh_amt = amount;
    663 	    }
    664 	    refreshheight = 0;
    665 	} else {
    666 	    int scrolltop;
    667 	    int scrollheight;
    668 	    int shift;
    669 	    int bot;
    670 
    671 	    ScrollSelection(screen, -(amount), False);
    672 	    if (amount == i) {
    673 		ClearScreen(xw);
    674 		goto done;
    675 	    }
    676 
    677 	    shift = INX2ROW(screen, 0);
    678 	    bot = screen->max_row - shift;
    679 	    scrollheight = i - amount;
    680 	    refreshheight = amount;
    681 
    682 	    if ((refreshtop = screen->bot_marg - refreshheight + 1 + shift) >
    683 		(i = screen->max_row - refreshheight + 1))
    684 		refreshtop = i;
    685 
    686 	    if (scroll_all_lines) {
    687 		scrolltop = 0;
    688 		if ((scrollheight += shift) > i)
    689 		    scrollheight = i;
    690 		IncrementSavedLines(amount);
    691 	    } else {
    692 		scrolltop = screen->top_marg + shift;
    693 		if ((i = screen->bot_marg - bot) > 0) {
    694 		    scrollheight -= i;
    695 		    if ((i = screen->top_marg + amount - 1 - bot) >= 0) {
    696 			refreshtop += i;
    697 			refreshheight -= i;
    698 		    }
    699 		}
    700 	    }
    701 
    702 	    if (screen->multiscroll && amount == 1 &&
    703 		screen->topline == 0 && screen->top_marg == 0 &&
    704 		screen->bot_marg == screen->max_row) {
    705 		if (screen->incopy < 0 && screen->scrolls == 0)
    706 		    CopyWait(xw);
    707 		screen->scrolls++;
    708 	    }
    709 
    710 	    vertical_copy_area(xw,
    711 			       scrolltop + amount,
    712 			       scrollheight,
    713 			       amount,
    714 			       left,
    715 			       right);
    716 
    717 	    if (refreshheight > 0) {
    718 		ClearCurBackground(xw,
    719 				   refreshtop,
    720 				   left,
    721 				   (unsigned) refreshheight,
    722 				   (unsigned) (right + 1 - left),
    723 				   (unsigned) FontWidth(screen));
    724 		if (refreshheight > shift)
    725 		    refreshheight = shift;
    726 	    }
    727 	}
    728     }
    729 
    730     if (amount > 0) {
    731 	if (left > 0 || right < screen->max_col) {
    732 	    scrollInMargins(xw, amount, screen->top_marg);
    733 	    ScrnUpdate2(xw,
    734 			screen->top_marg,
    735 			left,
    736 			screen->bot_marg + 1 - screen->top_marg,
    737 			right + 1 - left,
    738 			True);
    739 	} else if (scroll_all_lines) {
    740 	    ScrnDeleteLine(xw,
    741 			   screen->saveBuf_index,
    742 			   screen->bot_marg + screen->savelines,
    743 			   0,
    744 			   (unsigned) amount);
    745 	} else {
    746 	    ScrnDeleteLine(xw,
    747 			   screen->visbuf,
    748 			   screen->bot_marg,
    749 			   screen->top_marg,
    750 			   (unsigned) amount);
    751 	}
    752     }
    753 
    754     scroll_displayed_graphics(xw, amount);
    755 
    756     if (refreshheight > 0) {
    757 	ScrnRefresh(xw,
    758 		    refreshtop,
    759 		    left,
    760 		    refreshheight,
    761 		    right + 1 - left,
    762 		    False);
    763     }
    764 
    765   done:
    766     screen->do_wrap = save_wrap;
    767     screen->cursor_busy -= 1;
    768     TRACE(("...xtermScroll count=%d (top %d, saved %d)\n", amount,
    769 	   screen->topline, screen->savelines));
    770     return;
    771 }
    772 
    773 /*
    774  * This is from ISO 6429, not found in any of DEC's terminals.
    775  */
    776 void
    777 xtermScrollLR(XtermWidget xw, int amount, Bool toLeft)
    778 {
    779     if (amount > 0) {
    780 	xtermColScroll(xw, amount, toLeft, ScrnLeftMargin(xw));
    781     }
    782 }
    783 
    784 /*
    785  * Implement DECBI/DECFI (back/forward column index)
    786  */
    787 void
    788 xtermColIndex(XtermWidget xw, Bool toLeft)
    789 {
    790     TScreen *screen = TScreenOf(xw);
    791 
    792     if (toLeft) {
    793 	if (ScrnIsColInMargins(screen, screen->cur_col)) {
    794 	    if (screen->cur_col == ScrnLeftMargin(xw)) {
    795 		xtermColScroll(xw, 1, False, screen->cur_col);
    796 	    } else {
    797 		CursorBack(xw, 1);
    798 	    }
    799 	} else {
    800 	    CursorBack(xw, 1);
    801 	}
    802     } else {
    803 	if (ScrnIsColInMargins(screen, screen->cur_col)) {
    804 	    if (screen->cur_col == ScrnRightMargin(xw)) {
    805 		xtermColScroll(xw, 1, True, ScrnLeftMargin(xw));
    806 	    } else {
    807 		CursorForward(xw, 1);
    808 	    }
    809 	} else {
    810 	    CursorForward(xw, 1);
    811 	}
    812     }
    813 }
    814 
    815 /*
    816  * Implement DECDC/DECIC (delete/insert column)
    817  */
    818 void
    819 xtermColScroll(XtermWidget xw, int amount, Bool toLeft, int at_col)
    820 {
    821     TScreen *screen = TScreenOf(xw);
    822 
    823     if (amount > 0) {
    824 	int min_row;
    825 	int max_row;
    826 
    827 	if (ScrnHaveRowMargins(screen)) {
    828 	    min_row = screen->top_marg;
    829 	    max_row = screen->bot_marg;
    830 	} else {
    831 	    min_row = 0;
    832 	    max_row = screen->max_row;
    833 	}
    834 
    835 	if (screen->cur_row >= min_row
    836 	    && screen->cur_row <= max_row
    837 	    && screen->cur_col >= screen->lft_marg
    838 	    && screen->cur_col <= screen->rgt_marg) {
    839 	    int save_row = screen->cur_row;
    840 	    int save_col = screen->cur_col;
    841 	    int row;
    842 
    843 	    screen->cur_col = at_col;
    844 	    if (toLeft) {
    845 		for (row = min_row; row <= max_row; row++) {
    846 		    screen->cur_row = row;
    847 		    ScrnDeleteChar(xw, (unsigned) amount);
    848 		}
    849 	    } else {
    850 		for (row = min_row; row <= max_row; row++) {
    851 		    screen->cur_row = row;
    852 		    ScrnInsertChar(xw, (unsigned) amount);
    853 		}
    854 	    }
    855 	    screen->cur_row = save_row;
    856 	    screen->cur_col = save_col;
    857 	    xtermRepaint(xw);
    858 	}
    859     }
    860 }
    861 
    862 /*
    863  * Reverse scrolls the screen by amount lines, erases top, doesn't alter
    864  * cursor position (i.e. cursor moves up amount relative to text).
    865  * All done within the scrolling region, of course.
    866  * Requires: amount > 0
    867  */
    868 void
    869 RevScroll(XtermWidget xw, int amount)
    870 {
    871     TScreen *screen = TScreenOf(xw);
    872     int i = screen->bot_marg - screen->top_marg + 1;
    873     int left = ScrnLeftMargin(xw);
    874     int right = ScrnRightMargin(xw);
    875     Boolean scroll_full_line = ((left == 0) && (right == screen->max_col));
    876 
    877     TRACE(("RevScroll count=%d\n", amount));
    878 
    879     screen->cursor_busy += 1;
    880     screen->cursor_moved = True;
    881 
    882     if (screen->cursor_state)
    883 	HideCursor(xw);
    884 
    885     if (amount > i)
    886 	amount = i;
    887 
    888     if (ScrnHaveSelection(screen))
    889 	adjustHiliteOnBakScroll(xw, amount);
    890 
    891     if (!scroll_full_line) {
    892 	;
    893     } else if (screen->jumpscroll) {
    894 	if (screen->scroll_amt < 0) {
    895 	    if (-screen->refresh_amt + amount > i)
    896 		FlushScroll(xw);
    897 	    screen->scroll_amt -= amount;
    898 	    screen->refresh_amt -= amount;
    899 	} else {
    900 	    if (screen->scroll_amt > 0)
    901 		FlushScroll(xw);
    902 	    screen->scroll_amt = -amount;
    903 	    screen->refresh_amt = -amount;
    904 	}
    905     } else {
    906 	int shift = INX2ROW(screen, 0);
    907 	int bot = screen->max_row - shift;
    908 	int refreshheight = amount;
    909 	int refreshtop = screen->top_marg + shift;
    910 	int scrollheight = (screen->bot_marg
    911 			    - screen->top_marg - refreshheight + 1);
    912 	int scrolltop = refreshtop + refreshheight;
    913 
    914 	if ((i = screen->bot_marg - bot) > 0)
    915 	    scrollheight -= i;
    916 	if ((i = screen->top_marg + refreshheight - 1 - bot) > 0)
    917 	    refreshheight -= i;
    918 
    919 	if (screen->multiscroll && amount == 1 &&
    920 	    screen->topline == 0 && screen->top_marg == 0 &&
    921 	    screen->bot_marg == screen->max_row) {
    922 	    if (screen->incopy < 0 && screen->scrolls == 0)
    923 		CopyWait(xw);
    924 	    screen->scrolls++;
    925 	}
    926 
    927 	vertical_copy_area(xw,
    928 			   scrolltop - amount,
    929 			   scrollheight,
    930 			   -amount,
    931 			   left,
    932 			   right);
    933 
    934 	if (refreshheight > 0) {
    935 	    ClearCurBackground(xw,
    936 			       refreshtop,
    937 			       left,
    938 			       (unsigned) refreshheight,
    939 			       (unsigned) (right + 1 - left),
    940 			       (unsigned) FontWidth(screen));
    941 	}
    942     }
    943     if (amount > 0) {
    944 	if (left > 0 || right < screen->max_col) {
    945 	    scrollInMargins(xw, -amount, screen->top_marg);
    946 	    ScrnUpdate2(xw,
    947 			screen->top_marg,
    948 			left,
    949 			screen->bot_marg + 1 - screen->top_marg,
    950 			right + 1 - left,
    951 			True);
    952 	} else {
    953 	    ScrnInsertLine(xw,
    954 			   screen->visbuf,
    955 			   screen->bot_marg,
    956 			   screen->top_marg,
    957 			   (unsigned) amount);
    958 	}
    959     }
    960     screen->cursor_busy -= 1;
    961     return;
    962 }
    963 
    964 #if OPT_ZICONBEEP
    965 void
    966 initZIconBeep(void)
    967 {
    968     if (resource.zIconBeep > 100 || resource.zIconBeep < -100) {
    969 	resource.zIconBeep = 0;	/* was 100, but I prefer to defaulting off. */
    970 	xtermWarning("a number between -100 and 100 is required for zIconBeep.  0 used by default\n");
    971     }
    972 }
    973 
    974 static char *
    975 getIconName(void)
    976 {
    977     static char *icon_name;
    978     static Arg args[] =
    979     {
    980 	{XtNiconName, (XtArgVal) & icon_name}
    981     };
    982 
    983     icon_name = NULL;
    984     XtGetValues(toplevel, args, XtNumber(args));
    985     return icon_name;
    986 }
    987 
    988 static void
    989 setZIconBeep(XtermWidget xw)
    990 {
    991     TScreen *screen = TScreenOf(xw);
    992 
    993     /* Flag icon name with "***"  on window output when iconified.
    994      */
    995     if (resource.zIconBeep && mapstate == IsUnmapped && !screen->zIconBeep_flagged) {
    996 	char *icon_name = getIconName();
    997 	if (icon_name != NULL) {
    998 	    screen->zIconBeep_flagged = True;
    999 	    ChangeIconName(xw, icon_name);
   1000 	}
   1001 	xtermBell(xw, XkbBI_Info, 0);
   1002     }
   1003     mapstate = -1;
   1004 }
   1005 
   1006 /*
   1007  * If warning should be given then give it
   1008  */
   1009 Boolean
   1010 showZIconBeep(XtermWidget xw, const char *name)
   1011 {
   1012     Boolean code = False;
   1013 
   1014     if (name == NULL)
   1015 	name = "";
   1016 
   1017     if (resource.zIconBeep && TScreenOf(xw)->zIconBeep_flagged) {
   1018 	char *format = resource.zIconFormat;
   1019 	char *newname = malloc(strlen(name) + strlen(format) + 2);
   1020 	if (!newname) {
   1021 	    xtermWarning("malloc failed in showZIconBeep\n");
   1022 	} else {
   1023 	    char *marker = strstr(format, "%s");
   1024 	    char *result = newname;
   1025 	    if (marker != NULL) {
   1026 		size_t skip = (size_t) (marker - format);
   1027 		if (skip) {
   1028 		    strncpy(result, format, skip);
   1029 		    result += skip;
   1030 		}
   1031 		strcpy(result, name);
   1032 		strcat(result, marker + 2);
   1033 	    } else {
   1034 		strcpy(result, format);
   1035 		strcat(result, name);
   1036 	    }
   1037 	    ChangeGroup(xw, XtNiconName, newname);
   1038 	    free(newname);
   1039 	}
   1040 	code = True;
   1041     }
   1042     return code;
   1043 }
   1044 
   1045 /*
   1046  * Restore the icon name, resetting the state for zIconBeep.
   1047  */
   1048 void
   1049 resetZIconBeep(XtermWidget xw)
   1050 {
   1051     TScreen *screen = TScreenOf(xw);
   1052 
   1053     if (screen->zIconBeep_flagged) {
   1054 	char *icon_name = getIconName();
   1055 	screen->zIconBeep_flagged = False;
   1056 	if (icon_name != NULL) {
   1057 	    char *buf = malloc(strlen(icon_name) + 1);
   1058 	    if (buf == NULL) {
   1059 		screen->zIconBeep_flagged = True;
   1060 	    } else {
   1061 		char *format = resource.zIconFormat;
   1062 		char *marker = strstr(format, "%s");
   1063 		Boolean found = False;
   1064 
   1065 		if (marker != NULL) {
   1066 		    if (marker == format
   1067 			|| !strncmp(icon_name, format, (size_t) (marker - format))) {
   1068 			found = True;
   1069 			strcpy(buf, icon_name + (marker - format));
   1070 			marker += 2;
   1071 			if (*marker != '\0') {
   1072 			    size_t len_m = strlen(marker);
   1073 			    size_t len_b = strlen(buf);
   1074 			    if (len_m < len_b
   1075 				&& !strcmp(buf + len_b - len_m, marker)) {
   1076 				buf[len_b - len_m] = '\0';
   1077 			    }
   1078 			}
   1079 		    }
   1080 		} else if (!strncmp(icon_name, format, strlen(format))) {
   1081 		    strcpy(buf, icon_name + strlen(format));
   1082 		    found = True;
   1083 		}
   1084 		if (found)
   1085 		    ChangeIconName(xw, buf);
   1086 		free(buf);
   1087 	    }
   1088 	}
   1089     }
   1090 }
   1091 #else
   1092 #define setZIconBeep(xw)	/* nothing */
   1093 #endif /* OPT_ZICONBEEP */
   1094 
   1095 /*
   1096  * Write a string onto the screen at the current cursor position.
   1097  * Update cursor position.
   1098  */
   1099 void
   1100 WriteText(XtermWidget xw, Cardinal offset, Cardinal length)
   1101 {
   1102     IChar *str = xw->work.write_text + offset;
   1103     TScreen *screen = TScreenOf(xw);
   1104     XTermDraw params;
   1105     CLineData *ld = NULL;
   1106     unsigned attr_flags = xw->flags;
   1107     CellColor fg_bg = xtermColorPair(xw);
   1108     unsigned cells = visual_width(str, length);
   1109     GC currentGC;
   1110 
   1111     TRACE(("WriteText %d (%2d,%2d) %3d:%s\n",
   1112 	   screen->topline,
   1113 	   screen->cur_row,
   1114 	   screen->cur_col,
   1115 	   length, visibleIChars(str, length)));
   1116 
   1117     if (cells + (unsigned) screen->cur_col > (unsigned) MaxCols(screen)) {
   1118 	cells = (unsigned) (MaxCols(screen) - screen->cur_col);
   1119     }
   1120 
   1121     if (screen->cur_row <= screen->max_row
   1122 	&& ScrnHaveSelection(screen)
   1123 	&& ScrnIsRowInSelection(screen, INX2ROW(screen, screen->cur_row))) {
   1124 	ScrnDisownSelection(xw);
   1125     }
   1126 #if OPT_ISO_COLORS
   1127     /* if colorBDMode is set, and enabled */
   1128     if (screen->colorBDMode &&
   1129 	screen->boldColors &&
   1130 	!hasDirectFG(attr_flags) &&
   1131     /* and bold foreground color on bold background color */
   1132 	GetCellColorFG(fg_bg) > COLOR_7 &&
   1133 	GetCellColorFG(fg_bg) < MIN_ANSI_COLORS &&
   1134     /* and both colors are the same */
   1135 	GetCellColorFG(fg_bg) == GetCellColorBG(fg_bg))
   1136 	/* clear BOLD flag, else it will be colorBD on bold background color */
   1137 	UIntClr(attr_flags, BOLD);
   1138 #endif
   1139 
   1140     /* if we are in insert-mode, reserve space for the new cells */
   1141     if (attr_flags & INSERT) {
   1142 	InsertChar(xw, cells);
   1143     }
   1144 
   1145     if (AddToVisible(xw)
   1146 	&& ((ld = getLineData(screen, screen->cur_row))) != NULL) {
   1147 	unsigned test;
   1148 
   1149 	if (screen->cursor_state)
   1150 	    HideCursor(xw);
   1151 
   1152 	/*
   1153 	 * If we overwrite part of a multi-column character, fill the rest
   1154 	 * of it with blanks.
   1155 	 */
   1156 	if_OPT_WIDE_CHARS(screen, {
   1157 	    int kl;
   1158 	    int kr;
   1159 	    if (DamagedCurCells(screen, cells, &kl, &kr))
   1160 		ClearInLine(xw, screen->cur_row, kl, (unsigned) (kr - kl + 1));
   1161 	});
   1162 
   1163 	TRACE(("WriteText calling drawXtermText (%d) (%d,%d)\n",
   1164 	       LineCharSet(screen, ld),
   1165 	       screen->cur_row,
   1166 	       screen->cur_col));
   1167 
   1168 	test = attr_flags;
   1169 #if OPT_ISO_COLORS
   1170 	{
   1171 	    int fg;
   1172 	    if (screen->colorAttrMode) {
   1173 		fg = MapToColorMode(xw->cur_foreground, screen, attr_flags);
   1174 	    } else {
   1175 		fg = xw->cur_foreground;
   1176 	    }
   1177 	    checkVeryBoldColors(test, fg);
   1178 	}
   1179 #endif
   1180 
   1181 	/* make sure that the correct GC is current */
   1182 	currentGC = updatedXtermGC(xw, attr_flags, fg_bg, False);
   1183 
   1184 	/* *INDENT-EQLS* */
   1185 	params.xw          = xw;
   1186 	params.attr_flags  = (test & DRAWX_MASK);
   1187 	params.draw_flags  = 0;
   1188 	params.this_chrset = LineCharSet(screen, ld);
   1189 	params.real_chrset = CSET_SWL;
   1190 	params.on_wide     = 0;
   1191 
   1192 	drawXtermText(&params,
   1193 		      currentGC,
   1194 		      LineCursorX(screen, ld, screen->cur_col),
   1195 		      CursorY(screen, screen->cur_row),
   1196 		      str, length);
   1197 
   1198 	resetXtermGC(xw, attr_flags, False);
   1199     }
   1200 
   1201     ScrnWriteText(xw, offset, length, attr_flags, fg_bg);
   1202     CursorForward(xw, (int) cells);
   1203 
   1204     if (screen->cur_row <= screen->max_row) {
   1205 	setZIconBeep(xw);
   1206     }
   1207     return;
   1208 }
   1209 
   1210 /*
   1211  * If cursor not in scrolling region, returns.  Else,
   1212  * inserts n blank lines at the cursor's position.  Lines above the
   1213  * bottom margin are lost.
   1214  */
   1215 void
   1216 InsertLine(XtermWidget xw, int n)
   1217 {
   1218     TScreen *screen = TScreenOf(xw);
   1219     int i;
   1220     int left = ScrnLeftMargin(xw);
   1221     int right = ScrnRightMargin(xw);
   1222     Boolean scroll_full_line = ((left == 0) && (right == screen->max_col));
   1223 
   1224     if (!ScrnIsRowInMargins(screen, screen->cur_row)
   1225 	|| screen->cur_col < left
   1226 	|| screen->cur_col > right)
   1227 	return;
   1228 
   1229     TRACE(("InsertLine count=%d\n", n));
   1230 
   1231     set_cur_col(screen, ScrnLeftMargin(xw));
   1232     if (screen->cursor_state)
   1233 	HideCursor(xw);
   1234 
   1235     if (ScrnHaveSelection(screen)
   1236 	&& ScrnAreRowsInSelection(screen,
   1237 				  INX2ROW(screen, screen->top_marg),
   1238 				  INX2ROW(screen, screen->cur_row - 1))
   1239 	&& ScrnAreRowsInSelection(screen,
   1240 				  INX2ROW(screen, screen->cur_row),
   1241 				  INX2ROW(screen, screen->bot_marg))) {
   1242 	ScrnDisownSelection(xw);
   1243     }
   1244 
   1245     ResetWrap(screen);
   1246     if (n > (i = screen->bot_marg - screen->cur_row + 1))
   1247 	n = i;
   1248     if (screen->jumpscroll && scroll_full_line) {
   1249 	if (screen->scroll_amt <= 0 &&
   1250 	    screen->cur_row <= -screen->refresh_amt) {
   1251 	    if (-screen->refresh_amt + n > MaxRows(screen))
   1252 		FlushScroll(xw);
   1253 	    screen->scroll_amt -= n;
   1254 	    screen->refresh_amt -= n;
   1255 	} else {
   1256 	    if (screen->scroll_amt)
   1257 		FlushScroll(xw);
   1258 	}
   1259     }
   1260     if (!screen->scroll_amt && scroll_full_line) {
   1261 	int shift = INX2ROW(screen, 0);
   1262 	int bot = screen->max_row - shift;
   1263 	int refreshheight = n;
   1264 	int refreshtop = screen->cur_row + shift;
   1265 	int scrolltop = refreshtop + refreshheight;
   1266 	int scrollheight = (screen->bot_marg
   1267 			    - screen->cur_row - refreshheight + 1);
   1268 
   1269 	if ((i = screen->bot_marg - bot) > 0)
   1270 	    scrollheight -= i;
   1271 	if ((i = screen->cur_row + refreshheight - 1 - bot) > 0)
   1272 	    refreshheight -= i;
   1273 	vertical_copy_area(xw, scrolltop - n, scrollheight, -n, left, right);
   1274 	if (refreshheight > 0) {
   1275 	    ClearCurBackground(xw,
   1276 			       refreshtop,
   1277 			       left,
   1278 			       (unsigned) refreshheight,
   1279 			       (unsigned) (right + 1 - left),
   1280 			       (unsigned) FontWidth(screen));
   1281 	}
   1282     }
   1283     if (n > 0) {
   1284 	if (scroll_full_line) {
   1285 	    ScrnInsertLine(xw,
   1286 			   screen->visbuf,
   1287 			   screen->bot_marg,
   1288 			   screen->cur_row,
   1289 			   (unsigned) n);
   1290 	} else {
   1291 	    scrollInMargins(xw, -n, screen->cur_row);
   1292 	    ScrnUpdate2(xw,
   1293 			screen->cur_row,
   1294 			left,
   1295 			screen->bot_marg + 1 - screen->cur_row,
   1296 			right + 1 - left,
   1297 			True);
   1298 	}
   1299     }
   1300 }
   1301 
   1302 /*
   1303  * If cursor not in scrolling region, returns.  Else, deletes n lines
   1304  * at the cursor's position, lines added at bottom margin are blank.
   1305  */
   1306 void
   1307 DeleteLine(XtermWidget xw, int n, Bool canSave)
   1308 {
   1309     TScreen *screen = TScreenOf(xw);
   1310     int i;
   1311     int left = ScrnLeftMargin(xw);
   1312     int right = ScrnRightMargin(xw);
   1313     Boolean scroll_all_lines = (Boolean) (screen->scrollWidget
   1314 					  && !screen->whichBuf
   1315 					  && screen->cur_row == 0);
   1316     Boolean scroll_full_line = ((left == 0) && (right == screen->max_col));
   1317 
   1318     if (!ScrnIsRowInMargins(screen, screen->cur_row) ||
   1319 	!ScrnIsColInMargins(screen, screen->cur_col))
   1320 	return;
   1321 
   1322     TRACE(("DeleteLine count=%d\n", n));
   1323 
   1324     set_cur_col(screen, ScrnLeftMargin(xw));
   1325     if (screen->cursor_state)
   1326 	HideCursor(xw);
   1327 
   1328     if (n > (i = screen->bot_marg - screen->cur_row + 1)) {
   1329 	n = i;
   1330     }
   1331     if (ScrnHaveSelection(screen)
   1332 	&& ScrnAreRowsInSelection(screen,
   1333 				  INX2ROW(screen, screen->cur_row),
   1334 				  INX2ROW(screen, screen->cur_row + n - 1))) {
   1335 	ScrnDisownSelection(xw);
   1336     }
   1337 
   1338     ResetWrap(screen);
   1339     if (screen->jumpscroll && scroll_full_line) {
   1340 	if (screen->scroll_amt >= 0 && screen->cur_row == screen->top_marg) {
   1341 	    if (screen->refresh_amt + n > MaxRows(screen))
   1342 		FlushScroll(xw);
   1343 	    if (canSave) {
   1344 		screen->scroll_amt += n;
   1345 		screen->refresh_amt += n;
   1346 	    }
   1347 	} else {
   1348 	    if (screen->scroll_amt)
   1349 		FlushScroll(xw);
   1350 	}
   1351     }
   1352 
   1353     /* adjust screen->buf */
   1354     if (n > 0) {
   1355 	if (left > 0 || right < screen->max_col) {
   1356 	    scrollInMargins(xw, n, screen->cur_row);
   1357 	} else if (canSave && scroll_all_lines) {
   1358 	    ScrnDeleteLine(xw,
   1359 			   screen->saveBuf_index,
   1360 			   screen->bot_marg + screen->savelines,
   1361 			   0,
   1362 			   (unsigned) n);
   1363 	} else {
   1364 	    ScrnDeleteLine(xw,
   1365 			   screen->visbuf,
   1366 			   screen->bot_marg,
   1367 			   screen->cur_row,
   1368 			   (unsigned) n);
   1369 	}
   1370     }
   1371 
   1372     /* repaint the screen, as needed */
   1373     if (!scroll_full_line) {
   1374 	ScrnUpdate2(xw,
   1375 		    screen->cur_row,
   1376 		    left,
   1377 		    screen->bot_marg + 1 - screen->cur_row,
   1378 		    right + 1 - left,
   1379 		    True);
   1380     } else if (!screen->scroll_amt) {
   1381 	int shift = INX2ROW(screen, 0);
   1382 	int bot = screen->max_row - shift;
   1383 	int refreshtop;
   1384 	int refreshheight = n;
   1385 	int scrolltop;
   1386 	int scrollheight = i - n;
   1387 
   1388 	if ((refreshtop = screen->bot_marg - refreshheight + 1 + shift) >
   1389 	    (i = screen->max_row - refreshheight + 1))
   1390 	    refreshtop = i;
   1391 	if (canSave && scroll_all_lines) {
   1392 	    scrolltop = 0;
   1393 	    if ((scrollheight += shift) > i)
   1394 		scrollheight = i;
   1395 	    IncrementSavedLines(n);
   1396 	} else {
   1397 	    scrolltop = screen->cur_row + shift;
   1398 	    if ((i = screen->bot_marg - bot) > 0) {
   1399 		scrollheight -= i;
   1400 		if ((i = screen->cur_row + n - 1 - bot) >= 0) {
   1401 		    refreshheight -= i;
   1402 		}
   1403 	    }
   1404 	}
   1405 	vertical_copy_area(xw, scrolltop + n, scrollheight, n, left, right);
   1406 	if (shift > 0 && refreshheight > 0) {
   1407 	    int rows = refreshheight;
   1408 	    if (rows > shift)
   1409 		rows = shift;
   1410 	    ScrnUpdate(xw, refreshtop, 0, rows, MaxCols(screen), True);
   1411 	    refreshtop += shift;
   1412 	    refreshheight -= shift;
   1413 	}
   1414 	if (refreshheight > 0) {
   1415 	    ClearCurBackground(xw,
   1416 			       refreshtop,
   1417 			       left,
   1418 			       (unsigned) refreshheight,
   1419 			       (unsigned) (right + 1 - left),
   1420 			       (unsigned) FontWidth(screen));
   1421 	}
   1422     }
   1423 }
   1424 
   1425 /*
   1426  * Insert n blanks at the cursor's position, no wraparound
   1427  */
   1428 void
   1429 InsertChar(XtermWidget xw, unsigned n)
   1430 {
   1431     TScreen *screen = TScreenOf(xw);
   1432     CLineData *ld;
   1433     unsigned limit;
   1434     int row = INX2ROW(screen, screen->cur_row);
   1435     int left = ScrnLeftMargin(xw);
   1436     int right = ScrnRightMargin(xw);
   1437 
   1438     if (screen->cursor_state)
   1439 	HideCursor(xw);
   1440 
   1441     TRACE(("InsertChar count=%d\n", n));
   1442 
   1443     if (ScrnHaveSelection(screen)
   1444 	&& ScrnIsRowInSelection(screen, row)) {
   1445 	ScrnDisownSelection(xw);
   1446     }
   1447     ResetWrap(screen);
   1448 
   1449     limit = (unsigned) (right + 1 - screen->cur_col);
   1450 
   1451     if (n > limit)
   1452 	n = limit;
   1453 
   1454     if (screen->cur_col < left || screen->cur_col > right) {
   1455 	n = 0;
   1456     } else if (AddToVisible(xw)
   1457 	       && (ld = getLineData(screen, screen->cur_row)) != NULL) {
   1458 	int col = right + 1 - (int) n;
   1459 
   1460 	/*
   1461 	 * If we shift part of a multi-column character, fill the rest
   1462 	 * of it with blanks.  Do similar repair for the text which will
   1463 	 * be shifted into the right-margin.
   1464 	 */
   1465 	if_OPT_WIDE_CHARS(screen, {
   1466 	    int kl;
   1467 	    int kr = screen->cur_col;
   1468 	    if (DamagedCurCells(screen, n, &kl, (int *) 0) && kr > kl) {
   1469 		ClearInLine(xw, screen->cur_row, kl, (unsigned) (kr - kl + 1));
   1470 	    }
   1471 	    kr = screen->max_col - (int) n + 1;
   1472 	    if (DamagedCells(screen, n, &kl, (int *) 0,
   1473 			     screen->cur_row,
   1474 			     kr) && kr > kl) {
   1475 		ClearInLine(xw, screen->cur_row, kl, (unsigned) (kr - kl + 1));
   1476 	    }
   1477 	});
   1478 
   1479 #if OPT_DEC_CHRSET
   1480 	if (CSET_DOUBLE(GetLineDblCS(ld))) {
   1481 	    col = MaxCols(screen) / 2 - (int) n;
   1482 	}
   1483 #endif
   1484 	/*
   1485 	 * prevent InsertChar from shifting the end of a line over
   1486 	 * if it is being appended to
   1487 	 */
   1488 	if (non_blank_line(screen, screen->cur_row,
   1489 			   screen->cur_col, MaxCols(screen))) {
   1490 	    horizontal_copy_area(xw, screen->cur_col,
   1491 				 col - screen->cur_col,
   1492 				 (int) n);
   1493 	}
   1494 
   1495 	ClearCurBackground(xw,
   1496 			   INX2ROW(screen, screen->cur_row),
   1497 			   screen->cur_col,
   1498 			   1U,
   1499 			   n,
   1500 			   (unsigned) LineFontWidth(screen, ld));
   1501     }
   1502     if (n != 0) {
   1503 	/* adjust screen->buf */
   1504 	ScrnInsertChar(xw, n);
   1505     }
   1506 }
   1507 
   1508 /*
   1509  * Deletes n chars at the cursor's position, no wraparound.
   1510  */
   1511 void
   1512 DeleteChar(XtermWidget xw, unsigned n)
   1513 {
   1514     TScreen *screen = TScreenOf(xw);
   1515     CLineData *ld;
   1516     unsigned limit;
   1517     int row = INX2ROW(screen, screen->cur_row);
   1518     int right = ScrnRightMargin(xw);
   1519 
   1520     if (screen->cursor_state)
   1521 	HideCursor(xw);
   1522 
   1523     if (!ScrnIsColInMargins(screen, screen->cur_col))
   1524 	return;
   1525 
   1526     TRACE(("DeleteChar count=%d\n", n));
   1527 
   1528     if (ScrnHaveSelection(screen)
   1529 	&& ScrnIsRowInSelection(screen, row)) {
   1530 	ScrnDisownSelection(xw);
   1531     }
   1532     ResetWrap(screen);
   1533 
   1534     limit = (unsigned) (right + 1 - screen->cur_col);
   1535 
   1536     if (n > limit)
   1537 	n = limit;
   1538 
   1539     if (AddToVisible(xw)
   1540 	&& (ld = getLineData(screen, screen->cur_row)) != NULL) {
   1541 	int col = right + 1 - (int) n;
   1542 
   1543 	/*
   1544 	 * If we delete part of a multi-column character, fill the rest
   1545 	 * of it with blanks.
   1546 	 */
   1547 	if_OPT_WIDE_CHARS(screen, {
   1548 	    int kl;
   1549 	    int kr;
   1550 	    if (DamagedCurCells(screen, n, &kl, &kr))
   1551 		ClearInLine(xw, screen->cur_row, kl, (unsigned) (kr - kl + 1));
   1552 	});
   1553 
   1554 #if OPT_DEC_CHRSET
   1555 	if (CSET_DOUBLE(GetLineDblCS(ld))) {
   1556 	    col = MaxCols(screen) / 2 - (int) n;
   1557 	}
   1558 #endif
   1559 	horizontal_copy_area(xw,
   1560 			     (screen->cur_col + (int) n),
   1561 			     col - screen->cur_col,
   1562 			     -((int) n));
   1563 
   1564 	ClearCurBackground(xw,
   1565 			   INX2ROW(screen, screen->cur_row),
   1566 			   col,
   1567 			   1U,
   1568 			   n,
   1569 			   (unsigned) LineFontWidth(screen, ld));
   1570     }
   1571     if (n != 0) {
   1572 	/* adjust screen->buf */
   1573 	ScrnDeleteChar(xw, n);
   1574     }
   1575 }
   1576 
   1577 /*
   1578  * Clear from cursor position to beginning of display, inclusive.
   1579  */
   1580 static void
   1581 ClearAbove(XtermWidget xw)
   1582 {
   1583     TScreen *screen = TScreenOf(xw);
   1584 
   1585     if (screen->protected_mode != OFF_PROTECT) {
   1586 	int row;
   1587 	unsigned len = (unsigned) MaxCols(screen);
   1588 
   1589 	assert(screen->max_col >= 0);
   1590 	for (row = 0; row < screen->cur_row; row++)
   1591 	    ClearInLine(xw, row, 0, len);
   1592 	ClearInLine(xw, screen->cur_row, 0, (unsigned) screen->cur_col);
   1593     } else {
   1594 	int top;
   1595 
   1596 	if (screen->cursor_state)
   1597 	    HideCursor(xw);
   1598 	if ((top = INX2ROW(screen, 0)) <= screen->max_row) {
   1599 	    int height;
   1600 
   1601 	    if (screen->scroll_amt)
   1602 		FlushScroll(xw);
   1603 	    if ((height = screen->cur_row + top) > screen->max_row)
   1604 		height = screen->max_row + 1;
   1605 	    if ((height -= top) > 0) {
   1606 		chararea_clear_displayed_graphics(screen,
   1607 						  0,
   1608 						  top,
   1609 						  MaxCols(screen),
   1610 						  height);
   1611 
   1612 		ClearCurBackground(xw,
   1613 				   top,
   1614 				   0,
   1615 				   (unsigned) height,
   1616 				   (unsigned) MaxCols(screen),
   1617 				   (unsigned) FontWidth(screen));
   1618 	    }
   1619 	}
   1620 	ClearBufRows(xw, 0, screen->cur_row - 1);
   1621     }
   1622 
   1623     ClearLeft(xw);
   1624 }
   1625 
   1626 /*
   1627  * Clear from cursor position to end of display, inclusive.
   1628  */
   1629 static void
   1630 ClearBelow(XtermWidget xw)
   1631 {
   1632     TScreen *screen = TScreenOf(xw);
   1633 
   1634     ClearRight(xw, -1);
   1635 
   1636     if (screen->protected_mode != OFF_PROTECT) {
   1637 	int row;
   1638 	unsigned len = (unsigned) MaxCols(screen);
   1639 
   1640 	assert(screen->max_col >= 0);
   1641 	for (row = screen->cur_row + 1; row <= screen->max_row; row++)
   1642 	    ClearInLine(xw, row, 0, len);
   1643     } else {
   1644 	int top;
   1645 
   1646 	if ((top = INX2ROW(screen, screen->cur_row)) <= screen->max_row) {
   1647 	    if (screen->scroll_amt)
   1648 		FlushScroll(xw);
   1649 	    if (++top <= screen->max_row) {
   1650 		chararea_clear_displayed_graphics(screen,
   1651 						  0,
   1652 						  top,
   1653 						  MaxCols(screen),
   1654 						  (screen->max_row - top + 1));
   1655 		ClearCurBackground(xw,
   1656 				   top,
   1657 				   0,
   1658 				   (unsigned) (screen->max_row - top + 1),
   1659 				   (unsigned) MaxCols(screen),
   1660 				   (unsigned) FontWidth(screen));
   1661 	    }
   1662 	}
   1663 	ClearBufRows(xw, screen->cur_row + 1, screen->max_row);
   1664     }
   1665 }
   1666 
   1667 /*
   1668  * Clear the given row, for the given range of columns, returning 1 if no
   1669  * protected characters were found, 0 otherwise.
   1670  */
   1671 static int
   1672 ClearInLine2(XtermWidget xw, int flags, int row, int col, unsigned len)
   1673 {
   1674     TScreen *screen = TScreenOf(xw);
   1675     CLineData *ld;
   1676     int rc = 1;
   1677 
   1678     TRACE(("ClearInLine(row=%d, col=%d, len=%d) vs %d..%d\n",
   1679 	   row, col, len,
   1680 	   screen->startH.row,
   1681 	   screen->startH.col));
   1682 
   1683     if (ScrnHaveSelection(screen)
   1684 	&& ScrnIsRowInSelection(screen, row)) {
   1685 	ScrnDisownSelection(xw);
   1686     }
   1687 
   1688     if (col + (int) len >= MaxCols(screen)) {
   1689 	len = (unsigned) (MaxCols(screen) - col);
   1690     }
   1691 
   1692     /* If we've marked protected text on the screen, we'll have to
   1693      * check each time we do an erase.
   1694      */
   1695     if (screen->protected_mode != OFF_PROTECT) {
   1696 	unsigned n;
   1697 	IAttr *attrs = getLineData(screen, row)->attribs + col;
   1698 	int saved_mode = screen->protected_mode;
   1699 	Bool done;
   1700 
   1701 	/* disable this branch during recursion */
   1702 	screen->protected_mode = OFF_PROTECT;
   1703 
   1704 	do {
   1705 	    done = True;
   1706 	    for (n = 0; n < len; n++) {
   1707 		if (attrs[n] & PROTECTED) {
   1708 		    rc = 0;	/* found a protected segment */
   1709 		    if (n != 0) {
   1710 			ClearInLine(xw, row, col, n);
   1711 		    }
   1712 		    while ((n < len)
   1713 			   && (attrs[n] & PROTECTED)) {
   1714 			n++;
   1715 		    }
   1716 		    done = False;
   1717 		    break;
   1718 		}
   1719 	    }
   1720 	    /* setup for another segment, past the protected text */
   1721 	    if (!done) {
   1722 		attrs += n;
   1723 		col += (int) n;
   1724 		len -= n;
   1725 	    }
   1726 	} while (!done);
   1727 
   1728 	screen->protected_mode = saved_mode;
   1729 	if ((int) len <= 0) {
   1730 	    return 0;
   1731 	}
   1732     }
   1733     /* fall through to the final non-protected segment */
   1734 
   1735     if (screen->cursor_state)
   1736 	HideCursor(xw);
   1737     ResetWrap(screen);
   1738 
   1739     if (AddToVisible(xw)
   1740 	&& (ld = getLineData(screen, row)) != NULL) {
   1741 
   1742 	ClearCurBackground(xw,
   1743 			   INX2ROW(screen, row),
   1744 			   col,
   1745 			   1U,
   1746 			   len,
   1747 			   (unsigned) LineFontWidth(screen, ld));
   1748     }
   1749 
   1750     if (len != 0) {
   1751 	ClearCells(xw, flags, len, row, col);
   1752     }
   1753 
   1754     return rc;
   1755 }
   1756 
   1757 int
   1758 ClearInLine(XtermWidget xw, int row, int col, unsigned len)
   1759 {
   1760     TScreen *screen = TScreenOf(xw);
   1761     int flags = 0;
   1762 
   1763     /*
   1764      * If we're clearing to the end of the line, we won't count this as
   1765      * "drawn" characters.  We'll only do cut/paste on "drawn" characters,
   1766      * so this has the effect of suppressing trailing blanks from a
   1767      * selection.
   1768      */
   1769     if (col + (int) len < MaxCols(screen)) {
   1770 	flags |= CHARDRAWN;
   1771     }
   1772     return ClearInLine2(xw, flags, row, col, len);
   1773 }
   1774 
   1775 /*
   1776  * Clear the next n characters on the cursor's line, including the cursor's
   1777  * position.
   1778  */
   1779 void
   1780 ClearRight(XtermWidget xw, int n)
   1781 {
   1782     TScreen *screen = TScreenOf(xw);
   1783     LineData *ld;
   1784     unsigned len = (unsigned) (MaxCols(screen) - screen->cur_col);
   1785 
   1786     assert(screen->max_col >= 0);
   1787     assert(screen->max_col >= screen->cur_col);
   1788 
   1789     if (n < 0)			/* the remainder of the line */
   1790 	n = MaxCols(screen);
   1791     if (n == 0)			/* default for 'ECH' */
   1792 	n = 1;
   1793 
   1794     if (len > (unsigned) n)
   1795 	len = (unsigned) n;
   1796 
   1797     ld = getLineData(screen, screen->cur_row);
   1798     if (AddToVisible(xw)) {
   1799 	if_OPT_WIDE_CHARS(screen, {
   1800 	    int col = screen->cur_col;
   1801 	    int row = screen->cur_row;
   1802 	    int kl;
   1803 	    int kr;
   1804 	    if (DamagedCurCells(screen, len, &kl, &kr) && kr >= kl) {
   1805 		int xx = col;
   1806 		if (kl < xx) {
   1807 		    ClearInLine2(xw, 0, row, kl, (unsigned) (xx - kl));
   1808 		}
   1809 		xx = col + (int) len - 1;
   1810 		if (kr > xx) {
   1811 		    ClearInLine2(xw, 0, row, xx + 1, (unsigned) (kr - xx));
   1812 		}
   1813 	    }
   1814 	});
   1815 	(void) ClearInLine(xw, screen->cur_row, screen->cur_col, len);
   1816     } else {
   1817 	ScrnClearCells(xw, screen->cur_row, screen->cur_col, len);
   1818     }
   1819 
   1820     /* with the right part cleared, we can't be wrapping */
   1821     LineClrWrapped(ld);
   1822     ShowWrapMarks(xw, screen->cur_row, ld);
   1823     ResetWrap(screen);
   1824 }
   1825 
   1826 /*
   1827  * Clear first part of cursor's line, inclusive.
   1828  */
   1829 static void
   1830 ClearLeft(XtermWidget xw)
   1831 {
   1832     TScreen *screen = TScreenOf(xw);
   1833     unsigned len = (unsigned) screen->cur_col + 1;
   1834 
   1835     assert(screen->cur_col >= 0);
   1836     if (AddToVisible(xw)) {
   1837 	if_OPT_WIDE_CHARS(screen, {
   1838 	    int row = screen->cur_row;
   1839 	    int kl;
   1840 	    int kr;
   1841 	    if (DamagedCurCells(screen, 1, &kl, &kr) && kr >= kl) {
   1842 		ClearInLine2(xw, 0, row, kl, (unsigned) (kr - kl + 1));
   1843 	    }
   1844 	});
   1845 	(void) ClearInLine(xw, screen->cur_row, 0, len);
   1846     } else {
   1847 	ScrnClearCells(xw, screen->cur_row, 0, len);
   1848     }
   1849 }
   1850 
   1851 /*
   1852  * Erase the cursor's line.
   1853  */
   1854 void
   1855 ClearLine(XtermWidget xw)
   1856 {
   1857     TScreen *screen = TScreenOf(xw);
   1858     unsigned len = (unsigned) MaxCols(screen);
   1859 
   1860     assert(screen->max_col >= 0);
   1861     (void) ClearInLine(xw, screen->cur_row, 0, len);
   1862 }
   1863 
   1864 void
   1865 ClearScreen(XtermWidget xw)
   1866 {
   1867     TScreen *screen = TScreenOf(xw);
   1868     int top;
   1869 
   1870     TRACE(("ClearScreen\n"));
   1871 
   1872     if (screen->cursor_state)
   1873 	HideCursor(xw);
   1874 
   1875     ScrnDisownSelection(xw);
   1876     ResetWrap(screen);
   1877     if ((top = INX2ROW(screen, 0)) <= screen->max_row) {
   1878 	if (screen->scroll_amt)
   1879 	    FlushScroll(xw);
   1880 	chararea_clear_displayed_graphics(screen,
   1881 					  0,
   1882 					  top,
   1883 					  MaxCols(screen),
   1884 					  (screen->max_row - top + 1));
   1885 	ClearCurBackground(xw,
   1886 			   top,
   1887 			   0,
   1888 			   (unsigned) (screen->max_row - top + 1),
   1889 			   (unsigned) MaxCols(screen),
   1890 			   (unsigned) FontWidth(screen));
   1891     }
   1892     ClearBufRows(xw, 0, screen->max_row);
   1893 }
   1894 
   1895 /*
   1896  * If we've written protected text DEC-style, and are issuing a non-DEC
   1897  * erase, temporarily reset the protected_mode flag so that the erase will
   1898  * ignore the protected flags.
   1899  */
   1900 void
   1901 do_erase_char(XtermWidget xw, int param, int mode)
   1902 {
   1903     TScreen *screen = TScreenOf(xw);
   1904     int saved_mode = screen->protected_mode;
   1905 
   1906     if (saved_mode == DEC_PROTECT
   1907 	&& saved_mode != mode) {
   1908 	screen->protected_mode = OFF_PROTECT;
   1909     }
   1910 
   1911     ClearRight(xw, param);
   1912     screen->protected_mode = saved_mode;
   1913 }
   1914 
   1915 void
   1916 do_erase_line(XtermWidget xw, int param, int mode)
   1917 {
   1918     TScreen *screen = TScreenOf(xw);
   1919     int saved_mode = screen->protected_mode;
   1920 
   1921     if (saved_mode == DEC_PROTECT
   1922 	&& saved_mode != mode) {
   1923 	screen->protected_mode = OFF_PROTECT;
   1924     }
   1925 
   1926     switch (param) {
   1927     case -1:			/* DEFAULT */
   1928     case 0:
   1929 	ClearRight(xw, -1);
   1930 	break;
   1931     case 1:
   1932 	ClearLeft(xw);
   1933 	break;
   1934     case 2:
   1935 	ClearLine(xw);
   1936 	break;
   1937     }
   1938     screen->protected_mode = saved_mode;
   1939 }
   1940 
   1941 /*
   1942  * Just like 'do_erase_line()', except that this intercepts ED controls.  If we
   1943  * clear the whole screen, we'll get the return-value from ClearInLine, and
   1944  * find if there were any protected characters left.  If not, reset the
   1945  * protected mode flag in the screen data (it's slower).
   1946  */
   1947 void
   1948 do_erase_display(XtermWidget xw, int param, int mode)
   1949 {
   1950     TScreen *screen = TScreenOf(xw);
   1951     int saved_mode = screen->protected_mode;
   1952 
   1953     if (saved_mode == DEC_PROTECT
   1954 	&& saved_mode != mode)
   1955 	screen->protected_mode = OFF_PROTECT;
   1956 
   1957     switch (param) {
   1958     case -1:			/* DEFAULT */
   1959     case 0:
   1960 	if (screen->cur_row == 0
   1961 	    && screen->cur_col == 0) {
   1962 	    screen->protected_mode = saved_mode;
   1963 	    do_erase_display(xw, 2, mode);
   1964 	    saved_mode = screen->protected_mode;
   1965 	} else
   1966 	    ClearBelow(xw);
   1967 	break;
   1968 
   1969     case 1:
   1970 	if (screen->cur_row == screen->max_row
   1971 	    && screen->cur_col == screen->max_col) {
   1972 	    screen->protected_mode = saved_mode;
   1973 	    do_erase_display(xw, 2, mode);
   1974 	    saved_mode = screen->protected_mode;
   1975 	} else
   1976 	    ClearAbove(xw);
   1977 	break;
   1978 
   1979     case 2:
   1980 	/*
   1981 	 * We use 'ClearScreen()' throughout the remainder of the
   1982 	 * program for places where we don't care if the characters are
   1983 	 * protected or not.  So we modify the logic around this call
   1984 	 * on 'ClearScreen()' to handle protected characters.
   1985 	 */
   1986 	if (screen->protected_mode != OFF_PROTECT) {
   1987 	    int row;
   1988 	    int rc = 1;
   1989 	    unsigned len = (unsigned) MaxCols(screen);
   1990 
   1991 	    assert(screen->max_col >= 0);
   1992 	    for (row = 0; row <= screen->max_row; row++)
   1993 		rc &= ClearInLine(xw, row, 0, len);
   1994 	    if (rc != 0)
   1995 		saved_mode = OFF_PROTECT;
   1996 	} else {
   1997 	    ClearScreen(xw);
   1998 	}
   1999 	break;
   2000 
   2001     case 3:
   2002 	/* xterm addition - erase saved lines. */
   2003 	if (screen->eraseSavedLines) {
   2004 	    screen->savedlines = 0;
   2005 	    ScrollBarDrawThumb(xw, 1);
   2006 	}
   2007 	break;
   2008     }
   2009     screen->protected_mode = saved_mode;
   2010 }
   2011 
   2012 static Boolean
   2013 row_has_data(TScreen *screen, int row)
   2014 {
   2015     Boolean result = False;
   2016     CLineData *ld;
   2017 
   2018     if ((ld = getLineData(screen, row)) != NULL) {
   2019 	int col;
   2020 
   2021 	for (col = 0; col < screen->max_col; ++col) {
   2022 	    if (ld->attribs[col] & CHARDRAWN && ld->charData[col] != ' ') {
   2023 		result = True;
   2024 		break;
   2025 	    }
   2026 	}
   2027     }
   2028     return result;
   2029 }
   2030 
   2031 static Boolean
   2032 screen_has_data(XtermWidget xw)
   2033 {
   2034     TScreen *screen = TScreenOf(xw);
   2035     Boolean result = False;
   2036     int row;
   2037 
   2038     for (row = 0; row < screen->max_row; ++row) {
   2039 	if (row_has_data(screen, row)) {
   2040 	    result = True;
   2041 	    break;
   2042 	}
   2043     }
   2044     return result;
   2045 }
   2046 
   2047 static void
   2048 do_extra_scroll(XtermWidget xw, Bool trimmed)
   2049 {
   2050     TScreen *screen = TScreenOf(xw);
   2051 
   2052     if (screen_has_data(xw)) {
   2053 	TRACE(("do_extra_scroll buffer=%d, trimmed=%s\n", screen->whichBuf,
   2054 	       BtoS(trimmed)));
   2055 	if (trimmed) {
   2056 	    int row;
   2057 	    Boolean hadData = (Boolean) ((screen->saved_fifo > 0)
   2058 					 ? row_has_data(screen, -1)
   2059 					 : False);
   2060 
   2061 	    for (row = 0; row < screen->max_row; ++row) {
   2062 		Boolean hasData = row_has_data(screen, row);
   2063 		if (hasData || hadData) {
   2064 		    LineData *dst = addScrollback(screen);
   2065 		    LineData *src = getLineData(screen, row);
   2066 		    copyLineData(dst, src);
   2067 		    IncrementSavedLines(1);
   2068 		}
   2069 		hadData = hasData;
   2070 	    }
   2071 	} else {
   2072 	    xtermScroll(xw, screen->max_row);
   2073 	    FlushScroll(xw);
   2074 	}
   2075 	xtermRepaint(xw);
   2076     }
   2077 }
   2078 
   2079 /*
   2080  * Like tiXtraScroll, perform a scroll up of the page contents.
   2081  *
   2082  * In this case, it happens for the special case when erasing the whole
   2083  * display, e.g., an erase-below starting from the upper-left corner of the
   2084  * screen, or if the erasure applies to the whole screen.
   2085  */
   2086 void
   2087 do_cd_xtra_scroll(XtermWidget xw, int param)
   2088 {
   2089     TScreen *screen = TScreenOf(xw);
   2090 
   2091     TRACE(("do_cd_xtra_scroll param %d, @%d,%d vs %d,%d\n", param,
   2092 	   screen->cur_row,
   2093 	   screen->cur_col,
   2094 	   ScrnTopMargin(xw),
   2095 	   ScrnLeftMargin(xw)));
   2096     if (xw->misc.cdXtraScroll
   2097 	&& (param == 2 ||
   2098 	    (param == 0
   2099 	     && screen->cur_col <= ScrnLeftMargin(xw)
   2100 	     && screen->cur_row <= ScrnTopMargin(xw)))) {
   2101 	do_extra_scroll(xw, (xw->misc.cdXtraScroll == edTrim));
   2102     }
   2103 }
   2104 
   2105 /*
   2106  * Scroll the page up (saving it).  This is called when doing terminal
   2107  * initialization (ti) or exiting from that (te).
   2108  */
   2109 void
   2110 do_ti_xtra_scroll(XtermWidget xw)
   2111 {
   2112     if (xw->misc.tiXtraScroll) {
   2113 	do_extra_scroll(xw, False);
   2114     }
   2115 }
   2116 
   2117 static void
   2118 CopyWait(XtermWidget xw)
   2119 {
   2120     TScreen *screen = TScreenOf(xw);
   2121     XEvent reply;
   2122     XEvent *rep = &reply;
   2123 #ifndef NO_ACTIVE_ICON
   2124     int retries = 0;
   2125 #endif
   2126 
   2127 #if USE_DOUBLE_BUFFER
   2128     if (resource.buffered)
   2129 	return;
   2130 #endif
   2131 
   2132     for (;;) {
   2133 #ifndef NO_ACTIVE_ICON
   2134 	if (xw->work.active_icon != eiFalse) {
   2135 	    /*
   2136 	     * The XWindowEvent call blocks until an event is available.  That
   2137 	     * can hang when using active-icon and iconifying/deiconifying
   2138 	     * while the terminal is receiving lots of output.  Checking with
   2139 	     * this call on the other hand may lose exposure events which
   2140 	     * arrive too late.  As a compromise, try several times with a
   2141 	     * time-delay before assuming no more events are available.
   2142 	     */
   2143 	    if (XCheckWindowEvent(screen->display,
   2144 				  VWindow(screen),
   2145 				  ExposureMask,
   2146 				  &reply)) {
   2147 		retries = 0;
   2148 	    } else {
   2149 		if (++retries >= 1000)
   2150 		    return;
   2151 		usleep(100U);	/* wait 0.1msec */
   2152 		continue;
   2153 	    }
   2154 	} else
   2155 #endif
   2156 	    XWindowEvent(screen->display, VWindow(screen), ExposureMask, &reply);
   2157 	switch (reply.type) {
   2158 	case Expose:
   2159 	    HandleExposure(xw, &reply);
   2160 	    break;
   2161 	case NoExpose:
   2162 	case GraphicsExpose:
   2163 	    if (screen->incopy <= 0) {
   2164 		screen->incopy = 1;
   2165 		if (screen->scrolls > 0)
   2166 		    screen->scrolls--;
   2167 	    }
   2168 	    if (reply.type == GraphicsExpose)
   2169 		HandleExposure(xw, &reply);
   2170 
   2171 	    if ((reply.type == NoExpose) ||
   2172 		((XExposeEvent *) rep)->count == 0) {
   2173 		if (screen->incopy <= 0 && screen->scrolls > 0)
   2174 		    screen->scrolls--;
   2175 		if (screen->scrolls == 0) {
   2176 		    screen->incopy = 0;
   2177 		    return;
   2178 		}
   2179 		screen->incopy = -1;
   2180 	    }
   2181 	    break;
   2182 	}
   2183     }
   2184 }
   2185 
   2186 /*
   2187  * used by vertical_copy_area and and horizontal_copy_area
   2188  */
   2189 static void
   2190 copy_area(XtermWidget xw,
   2191 	  int src_x,
   2192 	  int src_y,
   2193 	  unsigned width,
   2194 	  unsigned height,
   2195 	  int dest_x,
   2196 	  int dest_y)
   2197 {
   2198     TScreen *screen = TScreenOf(xw);
   2199 
   2200     if (width != 0 && height != 0) {
   2201 	/* wait for previous CopyArea to complete unless
   2202 	   multiscroll is enabled and active */
   2203 	if (screen->incopy && screen->scrolls == 0)
   2204 	    CopyWait(xw);
   2205 	screen->incopy = -1;
   2206 
   2207 	/* save for translating Expose events */
   2208 	screen->copy_src_x = src_x;
   2209 	screen->copy_src_y = src_y;
   2210 	screen->copy_width = width;
   2211 	screen->copy_height = height;
   2212 	screen->copy_dest_x = dest_x;
   2213 	screen->copy_dest_y = dest_y;
   2214 
   2215 	XCopyArea(screen->display,
   2216 		  VDrawable(screen), VDrawable(screen),
   2217 		  NormalGC(xw, screen),
   2218 		  src_x, src_y, width, height, dest_x, dest_y);
   2219     }
   2220 }
   2221 
   2222 /*
   2223  * use when inserting or deleting characters on the current line
   2224  */
   2225 static void
   2226 horizontal_copy_area(XtermWidget xw,
   2227 		     int firstchar,	/* char pos on screen to start copying at */
   2228 		     int nchars,
   2229 		     int amount)	/* number of characters to move right */
   2230 {
   2231     TScreen *screen = TScreenOf(xw);
   2232     CLineData *ld;
   2233 
   2234     if ((ld = getLineData(screen, screen->cur_row)) != NULL) {
   2235 	int src_x = LineCursorX(screen, ld, firstchar);
   2236 	int src_y = CursorY(screen, screen->cur_row);
   2237 
   2238 	copy_area(xw, src_x, src_y,
   2239 		  (unsigned) (nchars * LineFontWidth(screen, ld)),
   2240 		  (unsigned) FontHeight(screen),
   2241 		  src_x + amount * LineFontWidth(screen, ld), src_y);
   2242     }
   2243 }
   2244 
   2245 /*
   2246  * use when inserting or deleting lines from the screen
   2247  */
   2248 static void
   2249 vertical_copy_area(XtermWidget xw,
   2250 		   int firstline,	/* line on screen to start copying at */
   2251 		   int nlines,
   2252 		   int amount,	/* number of lines to move up (neg=down) */
   2253 		   int left,
   2254 		   int right)
   2255 {
   2256     TScreen *screen = TScreenOf(xw);
   2257 
   2258     TRACE(("vertical_copy_area - firstline=%d nlines=%d left=%d right=%d amount=%d\n",
   2259 	   firstline, nlines, left, right, amount));
   2260 
   2261     if (nlines > 0) {
   2262 	int src_x = CursorX(screen, left);
   2263 	int src_y = firstline * FontHeight(screen) + screen->border;
   2264 	unsigned int w = (unsigned) ((right + 1 - left) * FontWidth(screen));
   2265 	unsigned int h = (unsigned) (nlines * FontHeight(screen));
   2266 	int dst_x = src_x;
   2267 	int dst_y = src_y - amount * FontHeight(screen);
   2268 
   2269 	copy_area(xw, src_x, src_y, w, h, dst_x, dst_y);
   2270 
   2271 	if (screen->show_wrap_marks) {
   2272 	    int row;
   2273 	    int first = firstline - amount;
   2274 	    int last = firstline + nlines + amount;
   2275 
   2276 	    for (row = first; row < last; ++row) {
   2277 		CLineData *ld;
   2278 		int mapped = amount + row + screen->topline;
   2279 
   2280 		if ((ld = getLineData(screen, mapped)) != NULL) {
   2281 		    ShowWrapMarks(xw, row, ld);
   2282 		}
   2283 	    }
   2284 	}
   2285     }
   2286 }
   2287 
   2288 /*
   2289  * use when scrolling the entire screen
   2290  */
   2291 void
   2292 scrolling_copy_area(XtermWidget xw,
   2293 		    int firstline,	/* line on screen to start copying at */
   2294 		    int nlines,
   2295 		    int amount)	/* number of lines to move up (neg=down) */
   2296 {
   2297 
   2298     if (nlines > 0) {
   2299 	vertical_copy_area(xw, firstline, nlines, amount, 0, TScreenOf(xw)->max_col);
   2300     }
   2301 }
   2302 
   2303 /*
   2304  * Handler for Expose events on the VT widget.
   2305  * Returns 1 iff the area where the cursor was got refreshed.
   2306  */
   2307 int
   2308 HandleExposure(XtermWidget xw, XEvent *event)
   2309 {
   2310     TScreen *screen = TScreenOf(xw);
   2311     XExposeEvent *reply = (XExposeEvent *) event;
   2312 
   2313 #ifndef NO_ACTIVE_ICON
   2314     if (reply->window == screen->iconVwin.window) {
   2315 	WhichVWin(screen) = &screen->iconVwin;
   2316 	TRACE(("HandleExposure - icon\n"));
   2317     } else {
   2318 	WhichVWin(screen) = &screen->fullVwin;
   2319 	TRACE(("HandleExposure - normal\n"));
   2320     }
   2321     TRACE((" event %d,%d %dx%d\n",
   2322 	   reply->y,
   2323 	   reply->x,
   2324 	   reply->height,
   2325 	   reply->width));
   2326 #endif /* NO_ACTIVE_ICON */
   2327 
   2328     /* if not doing CopyArea or if this is a GraphicsExpose, don't translate */
   2329     if (!screen->incopy || event->type != Expose) {
   2330 	return handle_translated_exposure(xw, reply->x, reply->y,
   2331 					  reply->width,
   2332 					  reply->height);
   2333     } else {
   2334 	/* compute intersection of area being copied with
   2335 	   area being exposed. */
   2336 	int both_x1 = Max(screen->copy_src_x, reply->x);
   2337 	int both_y1 = Max(screen->copy_src_y, reply->y);
   2338 	int both_x2 = Min(screen->copy_src_x + (int) screen->copy_width,
   2339 			  (reply->x + (int) reply->width));
   2340 	int both_y2 = Min(screen->copy_src_y + (int) screen->copy_height,
   2341 			  (reply->y + (int) reply->height));
   2342 	int value = 0;
   2343 
   2344 	/* was anything copied affected? */
   2345 	if (both_x2 > both_x1 && both_y2 > both_y1) {
   2346 	    /* do the copied area */
   2347 	    value = handle_translated_exposure
   2348 		(xw, reply->x + screen->copy_dest_x - screen->copy_src_x,
   2349 		 reply->y + screen->copy_dest_y - screen->copy_src_y,
   2350 		 reply->width, reply->height);
   2351 	}
   2352 	/* was anything not copied affected? */
   2353 	if (reply->x < both_x1 || reply->y < both_y1
   2354 	    || reply->x + reply->width > both_x2
   2355 	    || reply->y + reply->height > both_y2)
   2356 	    value = handle_translated_exposure(xw, reply->x, reply->y,
   2357 					       reply->width, reply->height);
   2358 
   2359 	return value;
   2360     }
   2361 }
   2362 
   2363 static void
   2364 set_background(XtermWidget xw, int color)
   2365 {
   2366     TScreen *screen = TScreenOf(xw);
   2367     Pixel c = getXtermBG(xw, xw->flags, color);
   2368 
   2369 #if OPT_WIDE_ATTRS
   2370     TRACE(("set_background(%d) %#lx %s\n", color, c,
   2371 	   ((xw->flags & ATR_DIRECT_BG)
   2372 	    ? "direct"
   2373 	    : "indexed")));
   2374 #else
   2375     TRACE(("set_background(%d) %#lx\n", color, c));
   2376 #endif
   2377     XSetWindowBackground(screen->display, VShellWindow(xw), c);
   2378     XSetWindowBackground(screen->display, VWindow(screen), c);
   2379     initBorderGC(xw, WhichVWin(screen));
   2380 }
   2381 
   2382 void
   2383 xtermClear2(XtermWidget xw, int x, int y, unsigned width, unsigned height)
   2384 {
   2385     TScreen *screen = TScreenOf(xw);
   2386     VTwin *vwin = WhichVWin(screen);
   2387     Drawable draw = VDrawable(screen);
   2388     GC gc;
   2389 
   2390     if ((gc = vwin->border_gc) != NULL) {
   2391 	int vmark1 = screen->border;
   2392 	int vmark2 = vwin->height + vmark1;
   2393 	int hmark1 = OriginX(screen);
   2394 	int hmark2 = vwin->width + hmark1;
   2395 	if (y < vmark1) {
   2396 	    int yy = y + (int) height;
   2397 	    int h1 = (yy <= vmark1) ? (yy - y) : (vmark1 - y);
   2398 	    XFillRectangle(screen->display, draw, gc,
   2399 			   x, y, width, (unsigned) h1);
   2400 	    if (yy > vmark1) {
   2401 		xtermClear2(xw, x, vmark1, width, (unsigned) (yy - vmark1));
   2402 	    }
   2403 	} else if (y < vmark2) {
   2404 	    int yy = y + (int) height;
   2405 	    int h2 = (yy <= vmark2) ? (yy - y) : (vmark2 - y);
   2406 	    int xb = x;
   2407 	    int xx = x + (int) width;
   2408 	    int ww = (int) width;
   2409 	    if (x < hmark1) {
   2410 		int w1 = (xx <= hmark1) ? (xx - x) : (hmark1 - x);
   2411 		XFillRectangle(screen->display, draw, gc,
   2412 			       x, y, (unsigned) w1, (unsigned) h2);
   2413 		x += w1;
   2414 		ww -= w1;
   2415 	    }
   2416 	    if ((ww > 0) && (x < hmark2)) {
   2417 		int w2 = (xx <= hmark2) ? (xx - x) : (hmark2 - x);
   2418 #if USE_DOUBLE_BUFFER
   2419 		if (resource.buffered) {
   2420 		    XFillRectangle(screen->display, draw,
   2421 				   FillerGC(xw, screen),
   2422 				   x, y, (unsigned) w2, (unsigned) h2);
   2423 		} else
   2424 #endif
   2425 		    XClearArea(screen->display, VWindow(screen),
   2426 			       x, y, (unsigned) w2, (unsigned) h2, False);
   2427 		x += w2;
   2428 		ww -= w2;
   2429 	    }
   2430 	    if (ww > 0) {
   2431 		XFillRectangle(screen->display, draw, gc,
   2432 			       x, y, (unsigned) ww, (unsigned) h2);
   2433 	    }
   2434 	    if (yy > vmark2) {
   2435 		xtermClear2(xw, xb, vmark2, width, (unsigned) (yy - vmark2));
   2436 	    }
   2437 	} else {
   2438 	    XFillRectangle(screen->display, draw, gc, x, y, width, height);
   2439 	}
   2440     } else {
   2441 #if USE_DOUBLE_BUFFER
   2442 	if (resource.buffered) {
   2443 	    gc = FillerGC(xw, screen);
   2444 	    XFillRectangle(screen->display, draw, gc,
   2445 			   x, y, width, height);
   2446 	} else
   2447 #endif
   2448 	    XClearArea(screen->display,
   2449 		       VWindow(screen),
   2450 		       x, y, width, height, False);
   2451     }
   2452 }
   2453 
   2454 /*
   2455  * Called by the ExposeHandler to do the actual repaint after the coordinates
   2456  * have been translated to allow for any CopyArea in progress.
   2457  * The rectangle passed in is pixel coordinates.
   2458  */
   2459 static int
   2460 handle_translated_exposure(XtermWidget xw,
   2461 			   int rect_x,
   2462 			   int rect_y,
   2463 			   int rect_width,
   2464 			   int rect_height)
   2465 {
   2466     TScreen *screen = TScreenOf(xw);
   2467     int toprow, leftcol, nrows, ncols;
   2468     int x0, x1;
   2469     int y0, y1;
   2470     int result = 0;
   2471 
   2472     TRACE(("handle_translated_exposure at %d,%d size %dx%d\n",
   2473 	   rect_y, rect_x, rect_height, rect_width));
   2474 
   2475     x0 = (rect_x - OriginX(screen));
   2476     x1 = (x0 + rect_width);
   2477 
   2478     y0 = (rect_y - OriginY(screen));
   2479     y1 = (y0 + rect_height);
   2480 
   2481     if (x0 < 0 ||
   2482 	y0 < 0 ||
   2483 	x1 > Width(screen) ||
   2484 	y1 > PlusStatusLine(screen, Height(screen))) {
   2485 	set_background(xw, -1);
   2486 	xtermClear2(xw,
   2487 		    rect_x,
   2488 		    rect_y,
   2489 		    (unsigned) rect_width,
   2490 		    (unsigned) rect_height);
   2491     }
   2492     toprow = y0 / FontHeight(screen);
   2493     if (toprow < 0)
   2494 	toprow = 0;
   2495 
   2496     leftcol = x0 / FontWidth(screen);
   2497     if (leftcol < 0)
   2498 	leftcol = 0;
   2499 
   2500     nrows = (y1 - 1) / FontHeight(screen) - toprow + 1;
   2501     ncols = (x1 - 1) / FontWidth(screen) - leftcol + 1;
   2502     toprow -= screen->scrolls;
   2503     if (toprow < 0) {
   2504 	nrows += toprow;
   2505 	toprow = 0;
   2506     }
   2507     if (toprow + nrows > PlusStatusLine(screen, MaxRows(screen)))
   2508 	nrows = PlusStatusLine(screen, MaxRows(screen)) - toprow;
   2509     if (leftcol + ncols > MaxCols(screen))
   2510 	ncols = MaxCols(screen) - leftcol;
   2511 
   2512     if (nrows > 0 && ncols > 0) {
   2513 	ScrnRefresh(xw, toprow, leftcol, nrows, ncols, True);
   2514 	first_map_occurred();
   2515 	if (screen->cur_row >= toprow &&
   2516 	    screen->cur_row < toprow + nrows &&
   2517 	    screen->cur_col >= leftcol &&
   2518 	    screen->cur_col < leftcol + ncols) {
   2519 	    result = 1;
   2520 	}
   2521 
   2522     }
   2523     TRACE(("...handle_translated_exposure %d\n", result));
   2524     return (result);
   2525 }
   2526 
   2527 /***====================================================================***/
   2528 
   2529 void
   2530 GetColors(XtermWidget xw, ScrnColors * pColors)
   2531 {
   2532     TScreen *screen = TScreenOf(xw);
   2533     int n;
   2534 
   2535     pColors->which = 0;
   2536     for (n = 0; n < NCOLORS; ++n) {
   2537 	SET_COLOR_VALUE(pColors, n, T_COLOR(screen, n));
   2538     }
   2539 }
   2540 
   2541 Boolean
   2542 AssignFgColor(XtermWidget xw, Pixel fg)
   2543 {
   2544     Boolean repaint = False;
   2545     TScreen *screen = TScreenOf(xw);
   2546     VTwin *win = WhichVWin(screen);
   2547 
   2548     T_COLOR(screen, TEXT_FG) = fg;
   2549     TRACE(("... TEXT_FG: %#lx\n", fg));
   2550     if (screen->Vshow) {
   2551 	setCgsFore(xw, win, gcNorm, fg);
   2552 	setCgsBack(xw, win, gcNormReverse, fg);
   2553 	setCgsFore(xw, win, gcBold, fg);
   2554 	setCgsBack(xw, win, gcBoldReverse, fg);
   2555 	repaint = True;
   2556     }
   2557     FreeMarkGCs(xw);
   2558 
   2559     return repaint;
   2560 }
   2561 
   2562 Boolean
   2563 AssignBgColor(XtermWidget xw, Pixel bg)
   2564 {
   2565     Boolean repaint = False;
   2566     TScreen *screen = TScreenOf(xw);
   2567     VTwin *win = WhichVWin(screen);
   2568 
   2569     T_COLOR(screen, TEXT_BG) = bg;
   2570     TRACE(("... TEXT_BG: %#lx\n", bg));
   2571     if (screen->Vshow) {
   2572 	setCgsBack(xw, win, gcNorm, bg);
   2573 	setCgsFore(xw, win, gcNormReverse, bg);
   2574 	setCgsBack(xw, win, gcBold, bg);
   2575 	setCgsFore(xw, win, gcBoldReverse, bg);
   2576 	set_background(xw, -1);
   2577 	repaint = True;
   2578     }
   2579 
   2580     return repaint;
   2581 }
   2582 
   2583 void
   2584 ChangeColors(XtermWidget xw, ScrnColors * pNew)
   2585 {
   2586     Boolean repaint = False;
   2587     TScreen *screen = TScreenOf(xw);
   2588 
   2589     TRACE(("ChangeColors\n"));
   2590 
   2591     if (COLOR_DEFINED(pNew, TEXT_CURSOR)) {
   2592 	T_COLOR(screen, TEXT_CURSOR) = COLOR_VALUE(pNew, TEXT_CURSOR);
   2593 	TRACE(("... TEXT_CURSOR: %#lx\n", T_COLOR(screen, TEXT_CURSOR)));
   2594 	FreeMarkGCs(xw);
   2595 	/* no repaint needed */
   2596     } else if ((T_COLOR(screen, TEXT_CURSOR) == T_COLOR(screen, TEXT_FG)) &&
   2597 	       (COLOR_DEFINED(pNew, TEXT_FG))) {
   2598 	if (T_COLOR(screen, TEXT_CURSOR) != COLOR_VALUE(pNew, TEXT_FG)) {
   2599 	    T_COLOR(screen, TEXT_CURSOR) = COLOR_VALUE(pNew, TEXT_FG);
   2600 	    TRACE(("... TEXT_CURSOR: %#lx\n", T_COLOR(screen, TEXT_CURSOR)));
   2601 	    if (screen->Vshow)
   2602 		repaint = True;
   2603 	}
   2604 	FreeMarkGCs(xw);
   2605     }
   2606 
   2607     if (COLOR_DEFINED(pNew, TEXT_FG)
   2608 	&& AssignFgColor(xw, COLOR_VALUE(pNew, TEXT_FG))) {
   2609 	repaint = True;
   2610     }
   2611 
   2612     if (COLOR_DEFINED(pNew, TEXT_BG)
   2613 	&& AssignBgColor(xw, COLOR_VALUE(pNew, TEXT_BG))) {
   2614 	repaint = True;
   2615     }
   2616 #if OPT_HIGHLIGHT_COLOR
   2617     if (COLOR_DEFINED(pNew, HIGHLIGHT_BG)) {
   2618 	if (T_COLOR(screen, HIGHLIGHT_BG) != COLOR_VALUE(pNew, HIGHLIGHT_BG)) {
   2619 	    T_COLOR(screen, HIGHLIGHT_BG) = COLOR_VALUE(pNew, HIGHLIGHT_BG);
   2620 	    TRACE(("... HIGHLIGHT_BG: %#lx\n", T_COLOR(screen, HIGHLIGHT_BG)));
   2621 	    if (screen->Vshow)
   2622 		repaint = True;
   2623 	}
   2624     }
   2625     if (COLOR_DEFINED(pNew, HIGHLIGHT_FG)) {
   2626 	if (T_COLOR(screen, HIGHLIGHT_FG) != COLOR_VALUE(pNew, HIGHLIGHT_FG)) {
   2627 	    T_COLOR(screen, HIGHLIGHT_FG) = COLOR_VALUE(pNew, HIGHLIGHT_FG);
   2628 	    TRACE(("... HIGHLIGHT_FG: %#lx\n", T_COLOR(screen, HIGHLIGHT_FG)));
   2629 	    if (screen->Vshow)
   2630 		repaint = True;
   2631 	}
   2632     }
   2633 #endif
   2634 
   2635     if (COLOR_DEFINED(pNew, MOUSE_FG) || (COLOR_DEFINED(pNew, MOUSE_BG))) {
   2636 	if (COLOR_DEFINED(pNew, MOUSE_FG)) {
   2637 	    T_COLOR(screen, MOUSE_FG) = COLOR_VALUE(pNew, MOUSE_FG);
   2638 	    TRACE(("... MOUSE_FG: %#lx\n", T_COLOR(screen, MOUSE_FG)));
   2639 	}
   2640 	if (COLOR_DEFINED(pNew, MOUSE_BG)) {
   2641 	    T_COLOR(screen, MOUSE_BG) = COLOR_VALUE(pNew, MOUSE_BG);
   2642 	    TRACE(("... MOUSE_BG: %#lx\n", T_COLOR(screen, MOUSE_BG)));
   2643 	}
   2644 
   2645 	if (screen->Vshow) {
   2646 	    recolor_cursor(screen,
   2647 			   screen->pointer_cursor,
   2648 			   T_COLOR(screen, MOUSE_FG),
   2649 			   T_COLOR(screen, MOUSE_BG));
   2650 	    XDefineCursor(screen->display, VWindow(screen),
   2651 			  screen->pointer_cursor);
   2652 	}
   2653 #if OPT_TEK4014
   2654 	if (TEK4014_SHOWN(xw)) {
   2655 	    TekScreen *tekscr = TekScreenOf(tekWidget);
   2656 	    Window tekwin = TWindow(tekscr);
   2657 	    if (tekwin) {
   2658 		recolor_cursor(screen,
   2659 			       tekscr->arrow,
   2660 			       T_COLOR(screen, MOUSE_FG),
   2661 			       T_COLOR(screen, MOUSE_BG));
   2662 		XDefineCursor(screen->display, tekwin, tekscr->arrow);
   2663 	    }
   2664 	}
   2665 #endif
   2666 	/* no repaint needed */
   2667     }
   2668 
   2669     if (COLOR_DEFINED(pNew, TEXT_FG) ||
   2670 	COLOR_DEFINED(pNew, TEXT_BG) ||
   2671 	COLOR_DEFINED(pNew, TEXT_CURSOR)) {
   2672 	if (set_cursor_gcs(xw) && screen->Vshow) {
   2673 	    repaint = True;
   2674 	}
   2675     }
   2676 #if OPT_TEK4014
   2677     if (COLOR_DEFINED(pNew, TEK_FG) ||
   2678 	COLOR_DEFINED(pNew, TEK_BG)) {
   2679 	ChangeTekColors(tekWidget, screen, pNew);
   2680 	if (TEK4014_SHOWN(xw)) {
   2681 	    TekRepaint(tekWidget);
   2682 	}
   2683     } else if (COLOR_DEFINED(pNew, TEK_CURSOR)) {
   2684 	ChangeTekColors(tekWidget, screen, pNew);
   2685     }
   2686 #endif
   2687     if (repaint)
   2688 	xtermRepaint(xw);
   2689 }
   2690 
   2691 void
   2692 xtermClear(XtermWidget xw)
   2693 {
   2694     TScreen *screen = TScreenOf(xw);
   2695 
   2696     TRACE(("xtermClear\n"));
   2697     xtermClear2(xw, 0, 0, FullWidth(screen), FullHeight(screen));
   2698 }
   2699 
   2700 void
   2701 xtermRepaint(XtermWidget xw)
   2702 {
   2703     TScreen *screen = TScreenOf(xw);
   2704 
   2705     TRACE(("xtermRepaint\n"));
   2706     xtermClear(xw);
   2707     ScrnRefresh(xw, 0, 0, LastRowNumber(screen) + 1, MaxCols(screen), True);
   2708 }
   2709 
   2710 /***====================================================================***/
   2711 
   2712 Boolean
   2713 isDefaultForeground(const char *name)
   2714 {
   2715     return (Boolean) !x_strcasecmp(name, XtDefaultForeground);
   2716 }
   2717 
   2718 Boolean
   2719 isDefaultBackground(const char *name)
   2720 {
   2721     return (Boolean) !x_strcasecmp(name, XtDefaultBackground);
   2722 }
   2723 
   2724 /***====================================================================***/
   2725 
   2726 typedef struct {
   2727     Pixel fg;
   2728     Pixel bg;
   2729 } ToSwap;
   2730 
   2731 #if OPT_HIGHLIGHT_COLOR
   2732 #define hc_param ,Bool hilite_color
   2733 #define hc_value ,screen->hilite_color
   2734 #else
   2735 #define hc_param		/* nothing */
   2736 #define hc_value		/* nothing */
   2737 #endif
   2738 
   2739 /*
   2740  * Use this to swap the foreground/background color values in the resource
   2741  * data, and to build up a list of the pairs which must be swapped in the
   2742  * GC cache.
   2743  */
   2744 static void
   2745 swapLocally(ToSwap * list, int *count, ColorRes * fg, ColorRes * bg hc_param)
   2746 {
   2747     ColorRes tmp;
   2748     Boolean found = False;
   2749 
   2750     Pixel fg_color = fg->value;
   2751     Pixel bg_color = bg->value;
   2752 
   2753 #if OPT_HIGHLIGHT_COLOR
   2754     if ((fg_color != bg_color) || !hilite_color)
   2755 #endif
   2756     {
   2757 	int n;
   2758 
   2759 	EXCHANGE(*fg, *bg, tmp);
   2760 	for (n = 0; n < *count; ++n) {
   2761 	    if ((list[n].fg == fg_color && list[n].bg == bg_color)
   2762 		|| (list[n].fg == bg_color && list[n].bg == fg_color)) {
   2763 		found = True;
   2764 		break;
   2765 	    }
   2766 	}
   2767 	if (!found) {
   2768 	    list[*count].fg = fg_color;
   2769 	    list[*count].bg = bg_color;
   2770 	    *count = *count + 1;
   2771 	    TRACE(("swapLocally fg %#lx, bg %#lx ->%d\n",
   2772 		   fg_color, bg_color, *count));
   2773 	}
   2774     }
   2775 }
   2776 
   2777 static void
   2778 reallySwapColors(XtermWidget xw, ToSwap * list, int count)
   2779 {
   2780     int j, k;
   2781 
   2782     TRACE(("reallySwapColors\n"));
   2783     for (j = 0; j < count; ++j) {
   2784 	for_each_text_gc(k) {
   2785 	    redoCgs(xw, list[j].fg, list[j].bg, (CgsEnum) k);
   2786 	}
   2787     }
   2788     FreeMarkGCs(xw);
   2789 }
   2790 
   2791 static void
   2792 swapVTwinGCs(XtermWidget xw, VTwin *win)
   2793 {
   2794     swapCgs(xw, win, gcNorm, gcNormReverse);
   2795     swapCgs(xw, win, gcBold, gcBoldReverse);
   2796 }
   2797 
   2798 void
   2799 ReverseVideo(XtermWidget xw)
   2800 {
   2801     TScreen *screen = TScreenOf(xw);
   2802     ToSwap listToSwap[5];
   2803     int numToSwap = 0;
   2804 
   2805     TRACE(("ReverseVideo now %s\n", BtoS(xw->misc.re_verse)));
   2806 
   2807     /*
   2808      * Swap SGR foreground and background colors.  By convention, these are
   2809      * the colors assigned to "black" (SGR #0) and "white" (SGR #7).  Also,
   2810      * SGR #8 and SGR #15 are the bold (or bright) versions of SGR #0 and
   2811      * #7, respectively.
   2812      *
   2813      * We don't swap colors that happen to match the screen's foreground
   2814      * and background because that tends to produce bizarre effects.
   2815      */
   2816 #define swapAnyColor(name,a,b) swapLocally(listToSwap, &numToSwap, &(screen->name[a]), &(screen->name[b]) hc_value)
   2817 #define swapAColor(a,b) swapAnyColor(Acolors, a, b)
   2818     if_OPT_ISO_COLORS(screen, {
   2819 	swapAColor(0, 7);
   2820 	swapAColor(8, 15);
   2821     });
   2822 
   2823     if (T_COLOR(screen, TEXT_CURSOR) == T_COLOR(screen, TEXT_FG)) {
   2824 	T_COLOR(screen, TEXT_CURSOR) = T_COLOR(screen, TEXT_BG);
   2825     }
   2826 #define swapTColor(a,b) swapAnyColor(Tcolors, a, b)
   2827     swapTColor(TEXT_FG, TEXT_BG);
   2828     swapTColor(MOUSE_FG, MOUSE_BG);
   2829 
   2830     reallySwapColors(xw, listToSwap, numToSwap);
   2831 
   2832     swapVTwinGCs(xw, &(screen->fullVwin));
   2833 #ifndef NO_ACTIVE_ICON
   2834     swapVTwinGCs(xw, &(screen->iconVwin));
   2835 #endif /* NO_ACTIVE_ICON */
   2836 
   2837     xw->misc.re_verse = (Boolean) !xw->misc.re_verse;
   2838     TRACE(("...swapping done, set ReverseVideo %s\n", BtoS(xw->misc.re_verse)));
   2839 
   2840     if (XtIsRealized((Widget) xw)) {
   2841 	xtermDisplayPointer(xw);
   2842     }
   2843 #if OPT_TEK4014
   2844     if (TEK4014_SHOWN(xw)) {
   2845 	TekScreen *tekscr = TekScreenOf(tekWidget);
   2846 	Window tekwin = TWindow(tekscr);
   2847 	recolor_cursor(screen,
   2848 		       tekscr->arrow,
   2849 		       T_COLOR(screen, MOUSE_FG),
   2850 		       T_COLOR(screen, MOUSE_BG));
   2851 	XDefineCursor(screen->display, tekwin, tekscr->arrow);
   2852     }
   2853 #endif
   2854 
   2855     if (screen->scrollWidget)
   2856 	ScrollBarReverseVideo(screen->scrollWidget);
   2857 
   2858     if (XtIsRealized((Widget) xw)) {
   2859 	set_background(xw, -1);
   2860     }
   2861 #if OPT_TEK4014
   2862     TekReverseVideo(xw, tekWidget);
   2863 #endif
   2864     if (XtIsRealized((Widget) xw)) {
   2865 	xtermRepaint(xw);
   2866     }
   2867 #if OPT_TEK4014
   2868     if (TEK4014_SHOWN(xw)) {
   2869 	TekRepaint(tekWidget);
   2870     }
   2871 #endif
   2872     ReverseOldColors(xw);
   2873     set_cursor_gcs(xw);
   2874     update_reversevideo();
   2875     TRACE(("...ReverseVideo now %s\n", BtoS(xw->misc.re_verse)));
   2876 }
   2877 
   2878 void
   2879 recolor_cursor(TScreen *screen,
   2880 	       Cursor cursor,	/* X cursor ID to set */
   2881 	       unsigned long fg,	/* pixel indexes to look up */
   2882 	       unsigned long bg)	/* pixel indexes to look up */
   2883 {
   2884     Display *dpy = screen->display;
   2885     XColor colordefs[2];	/* 0 is foreground, 1 is background */
   2886 
   2887     colordefs[0].pixel = fg;
   2888     colordefs[1].pixel = bg;
   2889     XQueryColors(dpy, DefaultColormap(dpy, DefaultScreen(dpy)),
   2890 		 colordefs, 2);
   2891     XRecolorCursor(dpy, cursor, colordefs, colordefs + 1);
   2892     cleanup_colored_cursor();
   2893     return;
   2894 }
   2895 
   2896 #if OPT_RENDERFONT
   2897 #define XFT_CACHE_LIMIT ((unsigned)(~0) >> 1)
   2898 #define XFT_CACHE_SIZE  16
   2899 typedef struct {
   2900     XftColor color;
   2901     unsigned use;
   2902 } XftColorCache;
   2903 
   2904 static int
   2905 compare_xft_color_cache(const void *a, const void *b)
   2906 {
   2907     return (int) (((const XftColorCache *) a)->use -
   2908 		  ((const XftColorCache *) b)->use);
   2909 }
   2910 
   2911 static XftColor *
   2912 getXftColor(XtermWidget xw, Pixel pixel)
   2913 {
   2914     static XftColorCache cache[XFT_CACHE_SIZE + 1];
   2915     static unsigned latest_use;
   2916     int i;
   2917     int oldest;
   2918     unsigned oldest_use;
   2919     XColor color;
   2920     Boolean found = False;
   2921 
   2922     (void) xw;
   2923     oldest_use = XFT_CACHE_LIMIT;
   2924     oldest = 0;
   2925     if (latest_use == XFT_CACHE_LIMIT) {
   2926 	latest_use = 0;
   2927 	qsort(cache, (size_t) XFT_CACHE_SIZE, sizeof(XftColorCache), compare_xft_color_cache);
   2928 	for (i = 0; i < XFT_CACHE_SIZE; i++) {
   2929 	    if (cache[i].use) {
   2930 		cache[i].use = ++latest_use;
   2931 	    }
   2932 	}
   2933     }
   2934     for (i = 0; i < XFT_CACHE_SIZE; i++) {
   2935 	if (cache[i].use) {
   2936 	    if (cache[i].color.pixel == pixel) {
   2937 		found = True;
   2938 		break;
   2939 	    }
   2940 	}
   2941 	if (cache[i].use < oldest_use) {
   2942 	    oldest_use = cache[i].use;
   2943 	    oldest = i;
   2944 	}
   2945     }
   2946     if (!found) {
   2947 	i = oldest;
   2948 	color.pixel = pixel;
   2949 	(void) QueryOneColor(xw, &color);
   2950 	cache[i].color.color.red = color.red;
   2951 	cache[i].color.color.green = color.green;
   2952 	cache[i].color.color.blue = color.blue;
   2953 	cache[i].color.color.alpha = 0xffff;
   2954 	cache[i].color.pixel = pixel;
   2955     }
   2956     cache[i].use = ++latest_use;
   2957     return &cache[i].color;
   2958 }
   2959 
   2960 /*
   2961  * The cell-width is related to, but not the same as the wide-character width.
   2962  * We will only get useful values from wcwidth() for codes above 255.
   2963  * Otherwise, interpret according to internal data.
   2964  */
   2965 #if OPT_RENDERWIDE
   2966 
   2967 #if OPT_C1_PRINT
   2968 #define XtermCellWidth(xw, ch) \
   2969 	(((ch) == 0 || (ch) == 127) \
   2970 	  ? 0 \
   2971 	  : (((ch) < 256) \
   2972 	      ? (((ch) >= 128 && (ch) < 160) \
   2973 		  ? (TScreenOf(xw)->c1_printable ? 1 : 0) \
   2974 		  : 1) \
   2975 	      : CharWidth(TScreenOf(xw), ch)))
   2976 #else
   2977 #define XtermCellWidth(xw, ch) \
   2978 	(((ch) == 0 || (ch) == 127) \
   2979 	  ? 0 \
   2980 	  : (((ch) < 256) \
   2981 	      ? 1 \
   2982 	      : CharWidth(TScreenOf(xw), ch)))
   2983 #endif
   2984 
   2985 #endif /* OPT_RENDERWIDE */
   2986 
   2987 #define XFT_DATA(which) getMyXftFont(params->xw, which, fontnum)
   2988 
   2989 #if OPT_ISO_COLORS
   2990 #define UseBoldFont(screen) (!(screen)->colorBDMode || ((screen)->veryBoldColors & BOLD))
   2991 #else
   2992 #define UseBoldFont(screen) 1
   2993 #endif
   2994 
   2995 #if OPT_RENDERWIDE
   2996 /*
   2997  * Find Xft (truetype) double-width font for the given normal/bold attributes.
   2998  */
   2999 static XTermXftFonts *
   3000 getWideXftFont(XTermDraw * params,
   3001 	       unsigned attr_flags)
   3002 {
   3003     TScreen *screen = TScreenOf(params->xw);
   3004     int fontnum = screen->menu_font_number;
   3005     XTermXftFonts *result = NULL;
   3006 
   3007 #if OPT_WIDE_ATTRS
   3008     if ((attr_flags & ATR_ITALIC)
   3009 #if OPT_ISO_COLORS
   3010 	&& !screen->colorITMode
   3011 #endif
   3012 	) {
   3013 	if ((attr_flags & BOLDATTR(screen))
   3014 	    && UseBoldFont(screen)
   3015 	    && XFT_DATA(fWBtal)) {
   3016 	    result = XFT_DATA(fWBtal);
   3017 	} else if (XFT_DATA(fWItal)) {
   3018 	    result = XFT_DATA(fWItal);
   3019 	}
   3020     }
   3021     if (result != NULL) {
   3022 	;			/* skip the other tests */
   3023     } else
   3024 #endif
   3025 #if OPT_ISO_COLORS
   3026 	if ((attr_flags & UNDERLINE)
   3027 	    && !screen->colorULMode
   3028 	    && screen->italicULMode
   3029 	    && XFT_DATA(fWItal)) {
   3030 	result = XFT_DATA(fWItal);
   3031     } else
   3032 #endif
   3033 	if ((attr_flags & BOLDATTR(screen))
   3034 	    && UseBoldFont(screen)
   3035 	    && XFT_DATA(fWBold)) {
   3036 	result = XFT_DATA(fWBold);
   3037     } else {
   3038 	result = XFT_DATA(fWide);
   3039     }
   3040     return result;
   3041 }
   3042 #endif /* OPT_RENDERWIDE */
   3043 
   3044 /*
   3045  * Find Xft (truetype) single-width font for the given normal/bold attributes.
   3046  */
   3047 static XTermXftFonts *
   3048 getNormXftFont(XTermDraw * params,
   3049 	       unsigned attr_flags,
   3050 	       Bool *did_ul)
   3051 {
   3052     TScreen *screen = TScreenOf(params->xw);
   3053     int fontnum = screen->menu_font_number;
   3054     XTermXftFonts *result = NULL;
   3055 
   3056     (void) did_ul;
   3057 #if OPT_DEC_CHRSET
   3058     if (CSET_DOUBLE(params->real_chrset)) {
   3059 	result = xterm_DoubleFT(params, params->real_chrset, attr_flags);
   3060     }
   3061     if (result != NULL) {
   3062 	;			/* found a usable double-sized font */
   3063     } else
   3064 #endif
   3065 #if OPT_WIDE_ATTRS
   3066 	if ((attr_flags & ATR_ITALIC)
   3067 #if OPT_ISO_COLORS
   3068 	    && !screen->colorITMode
   3069 #endif
   3070 	) {
   3071 	if ((attr_flags & BOLDATTR(screen))
   3072 	    && UseBoldFont(screen)
   3073 	    && XFT_DATA(fBtal)) {
   3074 	    result = XFT_DATA(fBtal);
   3075 	} else if (XFT_DATA(fItal)) {
   3076 	    result = XFT_DATA(fItal);
   3077 	}
   3078     }
   3079     if (result != NULL) {
   3080 	;			/* skip the other tests */
   3081     } else
   3082 #endif
   3083 #if OPT_ISO_COLORS
   3084 	if ((attr_flags & UNDERLINE)
   3085 	    && !screen->colorULMode
   3086 	    && screen->italicULMode
   3087 	    && XFT_DATA(fItal)) {
   3088 	result = XFT_DATA(fItal);
   3089 	*did_ul = True;
   3090     } else
   3091 #endif
   3092 	if ((attr_flags & BOLDATTR(screen))
   3093 	    && UseBoldFont(screen)
   3094 	    && XFT_DATA(fBold)) {
   3095 	result = XFT_DATA(fBold);
   3096     } else {
   3097 	result = XFT_DATA(fNorm);
   3098     }
   3099     return result;
   3100 }
   3101 
   3102 #if OPT_RENDERWIDE
   3103 #define pickXftData(width, nf, wf) (((width == 2) && ((wf) != NULL) && XftFp(wf) != NULL) ? (wf) : (nf))
   3104 #define pickXftFont(width, nf, wf) (((width == 2) && ((wf) != NULL)) ? (wf) : (nf))
   3105 #else
   3106 #define pickXftData(width, nf, wf) (nf)
   3107 #define pickXftFont(width, nf, wf) (nf)
   3108 #endif
   3109 
   3110 /*
   3111  * fontconfig/Xft combination prior to 2.2 has a problem with
   3112  * CJK truetype 'double-width' (bi-width/monospace) fonts leading
   3113  * to the 's p a c e d o u t' rendering. Consequently, we can't
   3114  * rely on XftDrawString8/16 when one of those fonts is used.
   3115  * Instead, we need to roll out our own using XftDrawCharSpec.
   3116  * A patch in the same spirit (but in a rather different form)
   3117  * was applied to gnome vte and gtk2 port of vim.
   3118  * See http://bugzilla.mozilla.org/show_bug.cgi?id=196312
   3119  */
   3120 static int
   3121 xtermXftDrawString(XTermDraw * params,
   3122 		   unsigned attr_flags,
   3123 		   XftColor *color,
   3124 		   XftFont *font,
   3125 		   int x,
   3126 		   int y,
   3127 		   const IChar *text,
   3128 		   Cardinal len,
   3129 		   Bool really)
   3130 {
   3131     TScreen *screen = TScreenOf(params->xw);
   3132     int ncells = 0;
   3133 
   3134     (void) attr_flags;
   3135     if (len != 0) {
   3136 #if OPT_RENDERWIDE
   3137 	XftCharSpec *sbuf;
   3138 	XTermXftFonts *wdata = getWideXftFont(params, attr_flags);
   3139 	XftFont *wfont = XftFp(wdata);
   3140 	Cardinal src, dst;
   3141 	XftFont *lastFont = NULL;
   3142 	XftFont *currFont = NULL;
   3143 	Cardinal start = 0;
   3144 	int charWidth;
   3145 	int fwidth = FontWidth(screen);
   3146 #if OPT_DEC_CHRSET
   3147 	Boolean forceDbl = CSET_DOUBLE(params->real_chrset);
   3148 #else
   3149 	Boolean forceDbl = False;
   3150 #endif
   3151 
   3152 	BumpTypedBuffer(XftCharSpec, 2 * len);
   3153 	sbuf = BfBuf(XftCharSpec);
   3154 
   3155 	for (src = dst = 0; src < len; src++) {
   3156 	    FcChar32 wc = *text++;
   3157 
   3158 	    charWidth = XtermCellWidth(params->xw, (wchar_t) wc);
   3159 	    if (charWidth < 0)
   3160 		continue;
   3161 
   3162 	    sbuf[dst].ucs4 = wc;
   3163 	    sbuf[dst].x = (short) (x + fwidth * ncells);
   3164 	    sbuf[dst].y = (short) (y);
   3165 
   3166 	    currFont = pickXftFont(charWidth, font, wfont);
   3167 	    ncells += charWidth;
   3168 
   3169 	    if (lastFont != currFont) {
   3170 		if ((lastFont != NULL) && really) {
   3171 		    XftDrawCharSpec(screen->renderDraw,
   3172 				    color,
   3173 				    lastFont,
   3174 				    sbuf + start,
   3175 				    (int) (dst - start));
   3176 		}
   3177 		start = dst;
   3178 		lastFont = currFont;
   3179 	    }
   3180 	    ++dst;
   3181 
   3182 	    if (forceDbl && charWidth < 2) {
   3183 		sbuf[dst].ucs4 = ' ';
   3184 		sbuf[dst].x = (short) (x + fwidth * ncells);
   3185 		sbuf[dst].y = (short) (y);
   3186 		++dst;
   3187 		ncells += charWidth;
   3188 	    }
   3189 	}
   3190 	if ((dst != start) && really) {
   3191 	    XftDrawCharSpec(screen->renderDraw,
   3192 			    color,
   3193 			    lastFont,
   3194 			    sbuf + start,
   3195 			    (int) (dst - start));
   3196 	}
   3197 #else /* !OPT_RENDERWIDE */
   3198 	if (really) {
   3199 	    XftChar8 *buffer;
   3200 	    int dst;
   3201 
   3202 	    BumpTypedBuffer(XftChar8, len);
   3203 	    buffer = BfBuf(XftChar8);
   3204 
   3205 	    for (dst = 0; dst < (int) len; ++dst)
   3206 		buffer[dst] = CharOf(text[dst]);
   3207 
   3208 	    XftDrawString8(screen->renderDraw,
   3209 			   color,
   3210 			   font,
   3211 			   x, y, buffer, (int) len);
   3212 	}
   3213 	ncells = (int) len;
   3214 #endif
   3215 	xtermNeedSwap(params->xw, 1);
   3216     }
   3217     return ncells;
   3218 }
   3219 #define xtermXftWidth(params, attr_flags, color, font, x, y, chars, len) \
   3220    xtermXftDrawString(params, attr_flags, color, font, x, y, chars, len, False)
   3221 #endif /* OPT_RENDERFONT */
   3222 
   3223 #if OPT_WIDE_CHARS
   3224 /*
   3225  * Map characters commonly "fixed" by groff back to their ASCII equivalents.
   3226  * Also map other useful equivalents.
   3227  */
   3228 unsigned
   3229 AsciiEquivs(unsigned ch)
   3230 {
   3231     switch (ch) {
   3232     case 0x2010:		/* groff "-" */
   3233     case 0x2011:
   3234     case 0x2012:
   3235     case 0x2013:
   3236     case 0x2014:
   3237     case 0x2015:
   3238     case 0x2212:		/* groff "\-" */
   3239 	ch = '-';
   3240 	break;
   3241     case 0x2018:		/* groff "`" */
   3242 	ch = '`';
   3243 	break;
   3244     case 0x2019:		/* groff ' */
   3245 	ch = '\'';
   3246 	break;
   3247     case 0x201C:		/* groff lq */
   3248     case 0x201D:		/* groff rq */
   3249 	ch = '"';
   3250 	break;
   3251     case 0x2329:		/* groff ".URL" */
   3252 	ch = '<';
   3253 	break;
   3254     case 0x232a:		/* groff ".URL" */
   3255 	ch = '>';
   3256 	break;
   3257     default:
   3258 	if (ch >= 0xff01 && ch <= 0xff5e) {
   3259 	    /* "Fullwidth" codes (actually double-width) */
   3260 	    ch -= 0xff00;
   3261 	    ch += ANSI_SPA;
   3262 	    break;
   3263 	}
   3264     }
   3265     return ch;
   3266 }
   3267 
   3268 /*
   3269  * Actually this should be called "groff_workaround()" - for the places where
   3270  * groff stomps on compatibility.  Still, if enough people get used to it,
   3271  * this might someday become a quasi-standard.
   3272  */
   3273 #if OPT_BOX_CHARS
   3274 static int
   3275 ucs_workaround(XTermDraw * params,
   3276 	       unsigned ch,
   3277 	       GC gc,
   3278 	       int x,
   3279 	       int y)
   3280 {
   3281     TScreen *screen = TScreenOf(params->xw);
   3282     int fixed = False;
   3283 
   3284     if (screen->wide_chars && screen->utf8_mode && ch > 256) {
   3285 	IChar eqv = (IChar) AsciiEquivs(ch);
   3286 
   3287 	if (eqv != (IChar) ch) {
   3288 	    int width = CharWidth(screen, ch);
   3289 
   3290 	    do {
   3291 		drawXtermText(params,
   3292 			      gc,
   3293 			      x,
   3294 			      y,
   3295 			      &eqv,
   3296 			      1);
   3297 		x += FontWidth(screen);
   3298 		eqv = BAD_ASCII;
   3299 	    } while (width-- > 1);
   3300 
   3301 	    fixed = True;
   3302 	} else if (ch == HIDDEN_CHAR) {
   3303 	    fixed = True;
   3304 	}
   3305     }
   3306     return fixed;
   3307 }
   3308 #endif /* OPT_BOX_CHARS */
   3309 #endif /* OPT_WIDE_CHARS */
   3310 
   3311 /*
   3312  * Use this when the characters will not fill the cell area properly.  Fill the
   3313  * area where we'll write the characters, otherwise we'll get gaps between
   3314  * them, e.g., in the original background color.
   3315  *
   3316  * The cursor is a special case, because the XFillRectangle call only uses the
   3317  * foreground, while we've set the cursor color in the background.  So we need
   3318  * a special GC for that.
   3319  */
   3320 static void
   3321 xtermFillCells(XTermDraw * params,
   3322 	       GC gc,
   3323 	       int x,
   3324 	       int y,
   3325 	       Cardinal len)
   3326 {
   3327     TScreen *screen = TScreenOf(params->xw);
   3328     VTwin *currentWin = WhichVWin(screen);
   3329 
   3330     if (!(params->draw_flags & NOBACKGROUND)) {
   3331 	CgsEnum srcId = getCgsId(params->xw, currentWin, gc);
   3332 	CgsEnum dstId = gcMAX;
   3333 	Pixel fg = getCgsFore(params->xw, currentWin, gc);
   3334 	Pixel bg = getCgsBack(params->xw, currentWin, gc);
   3335 
   3336 	switch (srcId) {
   3337 	case gcVTcursNormal:
   3338 	case gcVTcursReverse:
   3339 	    dstId = gcVTcursOutline;
   3340 	    break;
   3341 	case gcVTcursFilled:
   3342 	case gcVTcursOutline:
   3343 	    break;
   3344 	case gcNorm:
   3345 	    dstId = gcNormReverse;
   3346 	    break;
   3347 	case gcNormReverse:
   3348 	    dstId = gcNorm;
   3349 	    break;
   3350 	case gcBold:
   3351 	    dstId = gcBoldReverse;
   3352 	    break;
   3353 	case gcBoldReverse:
   3354 	    dstId = gcBold;
   3355 	    break;
   3356 	case gcBorder:
   3357 	case gcFiller:
   3358 	    dstId = srcId;
   3359 	    break;
   3360 #if OPT_BOX_CHARS || OPT_WIDE_CHARS
   3361 	case gcLine:
   3362 	case gcDots:
   3363 	    break;
   3364 #endif
   3365 #if OPT_DEC_CHRSET
   3366 	case gcCNorm:
   3367 	case gcCBold:
   3368 	    break;
   3369 #endif
   3370 #if OPT_WIDE_CHARS
   3371 	case gcWide:
   3372 	    dstId = gcWideReverse;
   3373 	    break;
   3374 	case gcWBold:
   3375 	    dstId = gcBoldReverse;
   3376 	    break;
   3377 	case gcWideReverse:
   3378 	case gcWBoldReverse:
   3379 	    break;
   3380 #endif
   3381 #if OPT_TEK4014
   3382 	case gcTKcurs:
   3383 	    break;
   3384 #endif
   3385 	case gcMAX:
   3386 	    break;
   3387 	}
   3388 
   3389 	if (dstId != gcMAX) {
   3390 	    setCgsFore(params->xw, currentWin, dstId, bg);
   3391 	    setCgsBack(params->xw, currentWin, dstId, fg);
   3392 
   3393 	    XFillRectangle(screen->display, VDrawable(screen),
   3394 			   getCgsGC(params->xw, currentWin, dstId),
   3395 			   x, y,
   3396 			   len * (Cardinal) FontWidth(screen),
   3397 			   (unsigned) FontHeight(screen));
   3398 	}
   3399     }
   3400 }
   3401 
   3402 #if OPT_TRACE
   3403 static void
   3404 xtermSetClipRectangles(Display *dpy,
   3405 		       GC gc,
   3406 		       int x,
   3407 		       int y,
   3408 		       XRectangle * rp,
   3409 		       Cardinal nr,
   3410 		       int order)
   3411 {
   3412 #if 0
   3413     TScreen *screen = TScreenOf(term);
   3414     Drawable draw = VDrawable(screen);
   3415 
   3416     XSetClipMask(dpy, gc, None);
   3417     XDrawRectangle(screen->display, draw, gc,
   3418 		   x + rp->x - 1,
   3419 		   y + rp->y - 1,
   3420 		   rp->width,
   3421 		   rp->height);
   3422 #endif
   3423 
   3424     XSetClipRectangles(dpy, gc,
   3425 		       x, y, rp, (int) nr, order);
   3426     TRACE(("clipping @(%3d,%3d) (%3d,%3d)..(%3d,%3d)\n",
   3427 	   y, x,
   3428 	   rp->y, rp->x, rp->height, rp->width));
   3429 }
   3430 
   3431 #else
   3432 #define xtermSetClipRectangles(dpy, gc, x, y, rp, nr, order) \
   3433 	    XSetClipRectangles(dpy, gc, x, y, rp, (int) nr, order)
   3434 #endif
   3435 
   3436 #if OPT_CLIP_BOLD
   3437 /*
   3438  * This special case is a couple of percent slower, but avoids a lot of pixel
   3439  * trash in rxcurses' hanoi.cmd demo (e.g., 10x20 font).
   3440  */
   3441 #define beginClipping(screen,gc,pwidth,plength) \
   3442 	if (pwidth > 2) { \
   3443 	    if (screen->use_clipping) { \
   3444 		XRectangle clip; \
   3445 		int clip_x = x; \
   3446 		int clip_y = y - FontHeight(screen) + FontDescent(screen); \
   3447 		clip.x = 0; \
   3448 		clip.y = 0; \
   3449 		clip.height = (unsigned short) FontHeight(screen); \
   3450 		clip.width = (unsigned short) ((pwidth) * (plength)); \
   3451 		xtermSetClipRectangles(screen->display, gc, \
   3452 				       clip_x, clip_y, \
   3453 				       &clip, 1, Unsorted); \
   3454 	    } else if (screen->use_border_clipping) { \
   3455 		XRectangle clip; \
   3456 		clip.x = 0; \
   3457 		clip.y = 0; \
   3458 		clip.height = (unsigned short) Height(screen); \
   3459 		clip.width = (unsigned short) Width(screen); \
   3460 		xtermSetClipRectangles(screen->display, gc, \
   3461 				       0, 0, \
   3462 				       &clip, 1, Unsorted); \
   3463 	    } \
   3464 	}
   3465 #define endClipping(screen,gc) \
   3466 	    XSetClipMask(screen->display, gc, None)
   3467 #else
   3468 #define beginClipping(screen,gc,pwidth,plength)		/* nothing */
   3469 #define endClipping(screen,gc)	/* nothing */
   3470 #endif /* OPT_CLIP_BOLD */
   3471 
   3472 #if OPT_RENDERFONT
   3473 static int
   3474 drawClippedXftString(XTermDraw * params,
   3475 		     unsigned attr_flags,
   3476 		     XftFont *font,
   3477 		     XftColor *fg_color,
   3478 		     int x,
   3479 		     int y,
   3480 		     const IChar *text,
   3481 		     Cardinal len)
   3482 {
   3483     int ncells = xtermXftWidth(params, attr_flags,
   3484 			       fg_color,
   3485 			       font, x, y,
   3486 			       text,
   3487 			       len);
   3488     XtermWidget xw = params->xw;
   3489     TScreen *screen = TScreenOf(xw);
   3490     int fontHigh = FontHeight(screen);
   3491     int fontWide = FontWidth(screen);
   3492 
   3493     if (fontWide > 2) {
   3494 	int plength = (ncells ? ncells : 1);
   3495 	Boolean halfHigh = False;
   3496 #if OPT_DEC_CHRSET
   3497 	Boolean halfWide = False;
   3498 
   3499 	switch (params->real_chrset) {
   3500 	case CSET_SWL:
   3501 	    break;
   3502 	case CSET_DWL:
   3503 	    halfWide = True;
   3504 	    break;
   3505 	case CSET_DHL_TOP:
   3506 	    halfHigh = True;
   3507 	    halfWide = True;
   3508 	    break;
   3509 	case CSET_DHL_BOT:
   3510 	    halfHigh = True;
   3511 	    halfWide = True;
   3512 	    break;
   3513 	}
   3514 	if (CSET_DOUBLE(params->real_chrset)) {
   3515 	    fontHigh = font->height;
   3516 	    fontWide = font->max_advance_width;
   3517 	    /* check if this is really a double-height font */
   3518 #define DoubleScale(macro,field) \
   3519 	    (int) (macro(screen) * (1.0 + (Max(20, xw->misc.field) / 100.)))
   3520 	    if (halfHigh) {
   3521 		int wantHigh = DoubleScale(FontHeight, limit_fontheight);
   3522 		halfHigh = (fontHigh >= wantHigh);
   3523 		TRACE(("comparing fontHigh %d/%d vs %d:"
   3524 		       " double-height %s for %s\n",
   3525 		       fontHigh, FontHeight(screen),
   3526 		       wantHigh, BtoS(halfHigh),
   3527 		       visibleDblChrset(params->real_chrset)));
   3528 	    }
   3529 	    fontHigh = (halfHigh
   3530 			? (2 * FontHeight(screen))
   3531 			: FontHeight(screen));
   3532 	    /* check if this is really a double-width font */
   3533 	    if (halfWide) {
   3534 		int wantWide = DoubleScale(FontWidth, limit_fontwidth);
   3535 		halfWide = (fontWide >= wantWide);
   3536 		TRACE(("comparing fontWide %d/%d vs %d:"
   3537 		       " double-width %s for %s\n",
   3538 		       fontWide, FontWidth(screen),
   3539 		       wantWide, BtoS(halfWide),
   3540 		       visibleDblChrset(params->real_chrset)));
   3541 	    }
   3542 	    fontWide = (halfWide
   3543 			? (2 * FontWidth(screen))
   3544 			: FontWidth(screen));
   3545 	}
   3546 #endif
   3547 	if (screen->use_clipping || halfHigh) {
   3548 	    XRectangle clip;
   3549 	    double adds = ((double) screen->scale_height - 1.0f) * fontHigh;
   3550 	    int height = dimRound(adds + fontHigh);
   3551 	    int descnt = dimRound(adds / 2.0) + FontDescent(screen);
   3552 	    int clip_x = (x);
   3553 	    int clip_y = (y) - height + descnt;
   3554 
   3555 	    clip.x = 0;
   3556 	    clip.y = 0;
   3557 	    clip.height = (Dimension) height;
   3558 	    clip.width = (Dimension) (fontWide * (Dimension) (plength));
   3559 
   3560 #if OPT_DEC_CHRSET
   3561 	    if (halfHigh) {
   3562 		int adjust;
   3563 
   3564 		clip.height = (unsigned short) FontHeight(screen);
   3565 		clip_y += descnt;
   3566 		if (params->real_chrset == CSET_DHL_BOT) {
   3567 		    y -= clip.height;
   3568 		}
   3569 		adjust = ((clip_y - OriginY(screen)) % FontHeight(screen));
   3570 		if (adjust) {
   3571 		    if (adjust > FontHeight(screen) / 2)
   3572 			adjust -= FontHeight(screen);
   3573 		    clip_y -= adjust;
   3574 		}
   3575 	    }
   3576 #endif
   3577 	    XftDrawSetClipRectangles(screen->renderDraw,
   3578 				     clip_x, clip_y,
   3579 				     &clip, 1);
   3580 	} else if (screen->use_border_clipping) {
   3581 	    XRectangle clip;
   3582 
   3583 	    clip.x = (Position) OriginX(screen);
   3584 	    clip.y = (Position) OriginY(screen);
   3585 	    clip.height = (Dimension) Height(screen);
   3586 	    clip.width = (Dimension) Width(screen);
   3587 
   3588 	    XftDrawSetClipRectangles(screen->renderDraw,
   3589 				     0, 0,
   3590 				     &clip, 1);
   3591 	}
   3592     }
   3593 
   3594     xtermXftDrawString(params, attr_flags,
   3595 		       fg_color,
   3596 		       font, x, y + ScaleShift(screen),
   3597 		       text,
   3598 		       len,
   3599 		       True);
   3600     XftDrawSetClip(screen->renderDraw, NULL);
   3601     return ncells;
   3602 }
   3603 #endif
   3604 
   3605 #ifndef NO_ACTIVE_ICON
   3606 #define WhichVFontData(screen,name) \
   3607 		(IsIcon(screen) ? getIconicFont(screen) \
   3608 				: GetNormalFont(screen, name))
   3609 #else
   3610 #define WhichVFontData(screen,name) \
   3611 				GetNormalFont(screen, name)
   3612 #endif
   3613 
   3614 static void
   3615 drawUnderline(XtermWidget xw,
   3616 	      GC gc,
   3617 	      unsigned attr_flags,
   3618 	      unsigned underline_len,
   3619 	      int font_width,
   3620 	      int x,
   3621 	      int y,
   3622 	      Bool did_ul)
   3623 {
   3624     TScreen *screen = TScreenOf(xw);
   3625 
   3626     if (screen->underline && !did_ul) {
   3627 	int repeat = 0;
   3628 	int descent = FontDescent(screen);
   3629 	int length = x + (int) underline_len * font_width - 1;
   3630 
   3631 #if OPT_WIDE_ATTRS
   3632 	if ((attr_flags & ATR_STRIKEOUT)) {
   3633 	    int where = y - ((3 * FontAscent(screen)) / 8);
   3634 	    XDrawLine(screen->display, VDrawable(screen), gc,
   3635 		      x, where,
   3636 		      length,
   3637 		      where);
   3638 	}
   3639 	if ((attr_flags & ATR_DBL_UNDER)) {
   3640 	    repeat = 2;
   3641 	} else
   3642 #endif
   3643 	if ((attr_flags & UNDERLINE)) {
   3644 	    repeat = 1;
   3645 	}
   3646 	while (repeat-- > 0) {
   3647 	    if (descent-- > 1)
   3648 		y++;
   3649 	    XDrawLine(screen->display, VDrawable(screen), gc,
   3650 		      x, y,
   3651 		      length,
   3652 		      y);
   3653 	}
   3654     }
   3655 }
   3656 
   3657 #if OPT_WIDE_ATTRS
   3658 /*
   3659  * As a special case, we are currently allowing italic fonts to be inexact
   3660  * matches for the normal font's size.  That introduces a problem:  either the
   3661  * ascent or descent may be shorter, leaving a gap that has to be filled in.
   3662  * Or they may be larger, requiring clipping.  Check for both cases.
   3663  */
   3664 static int
   3665 fixupItalics(XTermDraw * params,
   3666 	     GC gc,
   3667 	     XTermFonts * curFont,
   3668 	     int y, int x,
   3669 	     int font_width,
   3670 	     Cardinal len)
   3671 {
   3672     TScreen *screen = TScreenOf(params->xw);
   3673     VTwin *cgsWin = WhichVWin(screen);
   3674     XFontStruct *realFp = curFont->fs;
   3675     XFontStruct *thisFp = getCgsFont(params->xw, cgsWin, gc)->fs;
   3676     int need_clipping = 0;
   3677     int need_filling = 0;
   3678 
   3679     if (thisFp->ascent > realFp->ascent)
   3680 	need_clipping = 1;
   3681     else if (thisFp->ascent < realFp->ascent)
   3682 	need_filling = 1;
   3683 
   3684     if (thisFp->descent > realFp->descent)
   3685 	need_clipping = 1;
   3686     else if (thisFp->descent < realFp->descent)
   3687 	need_filling = 1;
   3688 
   3689     if (need_clipping) {
   3690 	beginClipping(screen, gc, font_width, (int) len);
   3691     }
   3692     if (need_filling) {
   3693 	xtermFillCells(params,
   3694 		       gc,
   3695 		       x,
   3696 		       y - realFp->ascent,
   3697 		       len);
   3698     }
   3699     return need_clipping;
   3700 }
   3701 #endif
   3702 
   3703 #if OPT_DEC_CHRSET
   3704 static int
   3705 fakeDoubleChars(const XTermDraw * params,
   3706 		GC gc,
   3707 		int y,
   3708 		int x,
   3709 		const IChar *text,
   3710 		Cardinal len)
   3711 {
   3712     unsigned need = 2 * len;
   3713     IChar *temp = TypeMallocN(IChar, need);
   3714 
   3715     if (temp != NULL) {
   3716 	unsigned n = 0;
   3717 	XTermDraw recur = *params;
   3718 
   3719 	recur.this_chrset = CSET_SWL;
   3720 
   3721 	while (len--) {
   3722 	    temp[n++] = *text++;
   3723 	    temp[n++] = ' ';
   3724 	}
   3725 	x = drawXtermText(&recur,
   3726 			  gc,
   3727 			  x, y,
   3728 			  temp,
   3729 			  n);
   3730 	free(temp);
   3731     }
   3732     return x;
   3733 }
   3734 #endif /* OPT_DEC_CHRSET */
   3735 
   3736 #define SetMissing(tag) \
   3737 	TRACE(("%s %s: missing %d %04X\n", __FILE__, tag, missing, ch)); \
   3738 	missing = 1
   3739 
   3740 #define MaxImageString 255
   3741 
   3742 #define UCS2SBUF(value)	buffer2[dst].byte2 = LO_BYTE(value);\
   3743 			buffer2[dst].byte1 = HI_BYTE(value)
   3744 
   3745 #if OPT_WIDE_CHARS
   3746 static int
   3747 xtermDrawMissing(TScreen *screen, unsigned flags, GC gc,
   3748 		 int x, int y, int ncells, Bool fullWidth)
   3749 {
   3750     /* *INDENT-EQLS* */
   3751     int width   = FontWidth(screen) * ncells;
   3752     int height  = FontHeight(screen);
   3753     int descent = FontDescent(screen);
   3754     int thick   = Max((height / 16), 1);
   3755     int thick2  = 2 * thick;
   3756     int yhigh   = height - thick2;
   3757     int too_big = (flags & (DOUBLEWFONT | DOUBLEHFONT)) != 0;
   3758 
   3759     TRACE(("*missing [%4d,%4d] %dx%d (%d)/%d%s%s%s%s\n",
   3760 	   y, x,
   3761 	   height, width,
   3762 	   ncells,
   3763 	   descent,
   3764 	   flags ? " recur" : "",
   3765 	   flags & DOUBLEFIRST ? "*" : "",
   3766 	   flags & DOUBLEWFONT ? ":W" : "",
   3767 	   flags & DOUBLEHFONT ? ":H" : ""));
   3768     if (width > thick2 && height > thick2) {
   3769 	if (too_big) {
   3770 	    if (flags & DOUBLEWFONT) {
   3771 		width *= 2;
   3772 	    }
   3773 	    if (flags & DOUBLEHFONT) {
   3774 		if (flags & DOUBLEFIRST) {
   3775 		    height *= 2;
   3776 		    height -= thick;
   3777 		} else {
   3778 		    height += thick;
   3779 		}
   3780 		descent *= 2;
   3781 	    }
   3782 	} else {
   3783 	    beginClipping(screen, gc, (Cardinal) width, (Cardinal) ncells);
   3784 	}
   3785 #if OPT_BOX_CHARS
   3786 	if (screen->force_all_chars)
   3787 #endif
   3788 	{
   3789 	    int xpos = x;
   3790 	    int ypos = (y - height + descent + thick);
   3791 	    unsigned high = (unsigned) yhigh;
   3792 	    unsigned wide = (unsigned) (width - thick2);
   3793 
   3794 	    if (fullWidth)
   3795 		ncells /= 2;
   3796 	    setXtermLineAttributes(screen->display, gc, thick, LineOnOffDash);
   3797 	    XDrawImageString(screen->display, VDrawable(screen), gc,
   3798 			     x, y,
   3799 			     "    ",
   3800 			     Min(4, ncells));
   3801 	    XDrawRectangle(screen->display,
   3802 			   VDrawable(screen), gc,
   3803 			   xpos, ypos,
   3804 			   wide, high);
   3805 	    resetXtermLineAttributes(screen->display, gc);
   3806 	}
   3807 	if (!too_big) {
   3808 	    endClipping(screen, gc);
   3809 	}
   3810     }
   3811     return x + width;
   3812 }
   3813 
   3814 static int
   3815 xtermPartString16(TScreen *screen, unsigned flags, GC gc, int x, int y, int length)
   3816 {
   3817     if (length > 0) {
   3818 	IChar *mapped = BfBuf(IChar);
   3819 	XChar2b *buffer2 = BfBuf(XChar2b);
   3820 	Char *buffer1 = BfBuf(Char);
   3821 	int n;
   3822 	Bool eightBit = True;
   3823 	int ncells = 0;
   3824 
   3825 	for (n = 0; n < length; ++n) {
   3826 	    IChar ch = mapped[n];
   3827 	    int bump = 1;
   3828 	    if (buffer2[n].byte1 != 0) {
   3829 		bump = CharWidth(screen, ch);
   3830 		bump = Max(1, bump);
   3831 		eightBit = False;
   3832 	    } else {
   3833 		buffer1[n] = buffer2[n].byte2;
   3834 	    }
   3835 	    ncells += bump;
   3836 	}
   3837 
   3838 	if ((flags & NOBACKGROUND)) {
   3839 	    if (eightBit) {
   3840 		XDrawString(screen->display,
   3841 			    VDrawable(screen), gc,
   3842 			    x, y,
   3843 			    (char *) buffer1, length);
   3844 	    } else {
   3845 		XDrawString16(screen->display,
   3846 			      VDrawable(screen), gc,
   3847 			      x, y,
   3848 			      buffer2, length);
   3849 	    }
   3850 	} else {
   3851 	    int b_pos;
   3852 	    int b_max = MaxImageString;
   3853 	    for (b_pos = 0; b_pos < length; b_pos += b_max) {
   3854 		if (b_pos + b_max > length)
   3855 		    b_max = (length - b_pos);
   3856 		if (eightBit) {
   3857 		    XDrawImageString(screen->display,
   3858 				     VDrawable(screen), gc,
   3859 				     x + (b_pos * FontWidth(screen)),
   3860 				     y,
   3861 				     (char *) buffer1 + b_pos,
   3862 				     b_max);
   3863 		} else {
   3864 		    XDrawImageString16(screen->display,
   3865 				       VDrawable(screen), gc,
   3866 				       x + (b_pos * FontWidth(screen)),
   3867 				       y,
   3868 				       buffer2 + b_pos,
   3869 				       b_max);
   3870 		}
   3871 	    }
   3872 	}
   3873 	x += ncells * FontWidth(screen);
   3874     }
   3875     return x;
   3876 }
   3877 
   3878 static int
   3879 xtermFullString16(XTermDraw * params, unsigned flags, GC gc,
   3880 		  int x, int y, int length, Bool fullwidth)
   3881 {
   3882     XtermWidget xw = params->xw;
   3883     TScreen *screen = TScreenOf(xw);
   3884     int src, dst;
   3885     IChar *mapped = BfBuf(IChar);
   3886     XChar2b *buffer2 = BfBuf(XChar2b);
   3887     VTwin *currentWin = WhichVWin(screen);
   3888     XTermFonts *xf = getCgsFont(xw, currentWin, gc);
   3889     XTermFonts *fn = getNormalFont(screen, fNorm);
   3890     XTermFonts *fp = xf ? xf : fn;
   3891 
   3892     for (src = dst = 0; src < (int) length; src++) {
   3893 	IChar ch = mapped[src];
   3894 	int ch_width = CharWidth(screen, ch);
   3895 	/*
   3896 	 * X11 bitmap-fonts are limited to 16-bits.
   3897 	 */
   3898 	assert(ch != HIDDEN_CHAR);
   3899 	if (
   3900 #if OPT_WIDER_ICHAR
   3901 	       (ch > NARROW_ICHAR) ||
   3902 #endif
   3903 	       xtermMissingChar(ch,
   3904 				(((xf >= fn) && (xf - fn) < fMAX)
   3905 				 ? XTermFontsRef(screen->fnts,
   3906 						 (VTFontEnum) (xf - fn))
   3907 				 : fp))) {
   3908 	    unsigned part = ucs2dec(screen, ch);
   3909 	    if (xtermIsDecGraphic(part) && ch > 255)
   3910 		ch = (IChar) part;
   3911 	    x = xtermPartString16(screen, flags, gc, x, y, dst);
   3912 	    if (xtermIsInternalCs(ch)) {
   3913 		xtermDrawBoxChar(params, ch, gc,
   3914 				 x, y - FontAscent(screen), 1, False);
   3915 		x += FontWidth(screen);
   3916 	    } else {
   3917 		x = xtermDrawMissing(screen, flags, gc, x, y, ch_width, fullwidth);
   3918 	    }
   3919 	    dst = 0;
   3920 	} else {
   3921 	    UCS2SBUF(ch);
   3922 	    ++dst;
   3923 	}
   3924     }
   3925 
   3926     x = xtermPartString16(screen, flags, gc, x, y, dst);
   3927     return x;
   3928 }
   3929 #endif /* OPT_WIDE_CHARS */
   3930 
   3931 static void
   3932 xtermPartString(TScreen *screen, unsigned flags, GC gc, int x, int y, int length)
   3933 {
   3934     Char *buffer1 = BfBuf(Char);
   3935 
   3936     if ((flags & NOBACKGROUND)) {
   3937 	XDrawString(screen->display, VDrawable(screen), gc,
   3938 		    x, y, (char *) buffer1, length);
   3939     } else {
   3940 	int b_pos;
   3941 	int b_max = MaxImageString;
   3942 	for (b_pos = 0; b_pos < length; b_pos += b_max) {
   3943 	    if (b_pos + b_max > length)
   3944 		b_max = (length - b_pos);
   3945 	    XDrawImageString(screen->display,
   3946 			     VDrawable(screen), gc,
   3947 			     x + (b_pos * FontWidth(screen)),
   3948 			     y,
   3949 			     (char *) (buffer1 + b_pos),
   3950 			     b_max);
   3951 	}
   3952     }
   3953 }
   3954 
   3955 static void
   3956 xtermDrawString(TScreen *screen, unsigned flags, GC gc, int x, int y, int length)
   3957 {
   3958     IChar *mapped = BfBuf(IChar);
   3959     Char *buffer1 = BfBuf(Char);
   3960 
   3961     int dst;
   3962 
   3963     for (dst = 0; dst < length; ++dst)
   3964 	buffer1[dst] = (Char) LO_BYTE(mapped[dst]);
   3965 
   3966     xtermPartString(screen, flags, gc, x, y, length);
   3967 }
   3968 
   3969 /*
   3970  * Draws text with the specified combination of bold/underline.  The return
   3971  * value is the updated x position.
   3972  */
   3973 int
   3974 drawXtermText(const XTermDraw * params,
   3975 	      GC gc,
   3976 	      int start_x,
   3977 	      int start_y,
   3978 	      const IChar *text,
   3979 	      Cardinal len)
   3980 {
   3981     XTermDraw recur = *params;
   3982     TScreen *screen = TScreenOf(recur.xw);
   3983     int x = start_x;
   3984     int y = start_y;
   3985     int y_shift = ScaleShift(screen);
   3986     Cardinal real_length = len;
   3987     Cardinal underline_len = 0;
   3988     /* Intended width of the font to draw (as opposed to the actual width of
   3989        the X font, and the width of the default font) */
   3990     int font_width = (((recur.draw_flags & DOUBLEWFONT) ? 2 : 1)
   3991 		      * screen->fnt_wide);
   3992     Bool did_ul = False;
   3993     XTermFonts *curFont;
   3994 
   3995 #if OPT_WIDE_ATTRS
   3996     int need_clipping = 0;
   3997 #endif
   3998 
   3999 #if OPT_WIDE_CHARS
   4000     Bool need_wide;
   4001 #endif
   4002 
   4003 #if OPT_WIDE_CHARS
   4004     if (text == NULL)
   4005 	return 0;
   4006 #endif
   4007     TRACE(("DRAWTEXT%c[%4d,%4d] (%d)%3d:%s\n",
   4008 	   screen->cursor_state == OFF ? ' ' : '*',
   4009 	   y, x, recur.this_chrset, len,
   4010 	   visibleIChars(text, len)));
   4011 
   4012 #if OPT_DEC_CHRSET
   4013     if (CSET_DOUBLE(recur.this_chrset)) {
   4014 	/* We could try drawing double-size characters in the icon, but
   4015 	 * given that the icon font is usually nil or nil2, there
   4016 	 * doesn't seem to be much point.
   4017 	 */
   4018 	int inx = 0;
   4019 	GC gc2;
   4020 
   4021 #if OPT_RENDERFONT
   4022 	if (UsingRenderFont(recur.xw)) {
   4023 	    if (!IsIcon(screen) && screen->font_doublesize) {
   4024 		int next;
   4025 		TRACE(("drawing %s\n", visibleDblChrset((unsigned) recur.this_chrset)));
   4026 		recur.real_chrset = recur.this_chrset;
   4027 		recur.this_chrset = CSET_SWL;
   4028 		next = drawXtermText(&recur,
   4029 				     gc,
   4030 				     x, y,
   4031 				     text,
   4032 				     len);
   4033 		x += (next - x) * 2;
   4034 	    } else {
   4035 		x = fakeDoubleChars(&recur,
   4036 				    gc, y, x,
   4037 				    text, len);
   4038 	    }
   4039 	} else
   4040 #endif
   4041 	    if ((!IsIcon(screen) && screen->font_doublesize)
   4042 		&& (gc2 = xterm_DoubleGC(&recur, gc, &inx)) != NULL) {
   4043 	    /* draw actual double-sized characters */
   4044 	    XFontStruct *fs = getDoubleFont(screen, inx)->fs;
   4045 	    XRectangle rect, *rp = &rect;
   4046 	    Cardinal nr = 1;
   4047 	    Cardinal nlen;
   4048 	    int ncells = (int) len;
   4049 
   4050 	    font_width *= 2;
   4051 	    recur.draw_flags |= DOUBLEWFONT;
   4052 
   4053 	    for (nlen = 0; nlen < len; ++nlen) {
   4054 		int ch_width = CharWidth(screen, text[nlen]);
   4055 		if (ch_width > 1)
   4056 		    ncells += (ch_width - 1);
   4057 	    }
   4058 
   4059 	    rect.x = 0;
   4060 	    rect.y = 0;
   4061 	    rect.width = (unsigned short) (ncells * font_width);
   4062 	    rect.height = (unsigned short) (FontHeight(screen));
   4063 
   4064 	    TRACE(("drawing %s\n", visibleDblChrset((unsigned) recur.this_chrset)));
   4065 	    switch (recur.this_chrset) {
   4066 	    case CSET_DHL_TOP:
   4067 		rect.y = (short) -(fs->ascent / 2);
   4068 		y -= rect.y;
   4069 		recur.draw_flags |= (DOUBLEHFONT | DOUBLEFIRST);
   4070 		break;
   4071 	    case CSET_DHL_BOT:
   4072 		rect.y = (short) (rect.height - (fs->ascent / 2));
   4073 		y -= rect.y;
   4074 		recur.draw_flags |= DOUBLEHFONT;
   4075 		recur.draw_flags &= (unsigned) ~DOUBLEFIRST;
   4076 		break;
   4077 	    case CSET_DWL:
   4078 		break;
   4079 	    default:
   4080 		nr = 0;
   4081 		break;
   4082 	    }
   4083 
   4084 	    if (nr) {
   4085 		xtermSetClipRectangles(screen->display, gc2,
   4086 				       x, y, rp, nr, YXBanded);
   4087 		xtermFillCells(&recur, gc, x, y + rect.y,
   4088 			       (Cardinal) ncells * 2);
   4089 	    } else {
   4090 		XSetClipMask(screen->display, gc2, None);
   4091 	    }
   4092 
   4093 	    /* Call ourselves recursively with the new gc */
   4094 
   4095 	    /*
   4096 	     * If we're trying to use proportional font, or if the
   4097 	     * font server didn't give us what we asked for wrt
   4098 	     * width, position each character independently.
   4099 	     */
   4100 	    if (screen->fnt_prop
   4101 		|| (fs->min_bounds.width != fs->max_bounds.width)
   4102 		|| (fs->min_bounds.width != 2 * FontWidth(screen))) {
   4103 		/* It is hard to fall-through to the main
   4104 		   branch: in a lot of places the check
   4105 		   for the cached font info is for
   4106 		   normal/bold fonts only. */
   4107 		XTermDraw param2 = recur;
   4108 		param2.this_chrset = CSET_SWL;
   4109 		while (len--) {
   4110 		    int next = drawXtermText(&param2,
   4111 					     gc2,
   4112 					     x, y,
   4113 					     text++,
   4114 					     1);
   4115 		    x += (next - x) * 2;
   4116 		}
   4117 	    } else {
   4118 		int next;
   4119 		XTermDraw param2 = recur;
   4120 		param2.this_chrset = CSET_SWL;
   4121 		next = drawXtermText(&param2,
   4122 				     gc2,
   4123 				     x, y,
   4124 				     text,
   4125 				     len);
   4126 		x += (next - x) * 2;
   4127 	    }
   4128 
   4129 	} else {		/* simulate double-sized characters */
   4130 	    x = fakeDoubleChars(&recur,
   4131 				gc, y, x,
   4132 				text, len);
   4133 	}
   4134 	TRACE(("DrewText [%4d,%4d] @%d\n", y, x, __LINE__));
   4135 	return x;
   4136     }
   4137 #endif
   4138 #if OPT_RENDERFONT
   4139     if (UsingRenderFont(recur.xw)) {
   4140 	VTwin *currentWin = WhichVWin(screen);
   4141 	Display *dpy = screen->display;
   4142 	XTermXftFonts *ndata;
   4143 	XGCValues values;
   4144 #if OPT_WIDE_CHARS && OPT_BOX_CHARS
   4145 	XTermXftFonts *ndata0;
   4146 #endif
   4147 #if OPT_RENDERWIDE && OPT_BOX_CHARS
   4148 	XTermXftFonts *wdata;
   4149 	XTermXftFonts *wdata0;
   4150 #endif
   4151 	int ncells = 0;
   4152 
   4153 #if OPT_DEC_CHRSET
   4154 	/*
   4155 	 * If this is a VT100-style double-width font, ensure that everything
   4156 	 * is printed using two-columns per character.
   4157 	 */
   4158 	Boolean forceDbl = CSET_DOUBLE(recur.real_chrset);
   4159 #else
   4160 	Boolean forceDbl = False;
   4161 #endif
   4162 
   4163 	(void) forceDbl;
   4164 	/*
   4165 	 * Defer creating the drawable until we need it.
   4166 	 */
   4167 	if (!screen->renderDraw) {
   4168 	    int scr;
   4169 	    Drawable draw = VDrawable(screen);
   4170 	    Visual *visual;
   4171 
   4172 	    scr = DefaultScreen(dpy);
   4173 	    visual = DefaultVisual(dpy, scr);
   4174 	    screen->renderDraw = XftDrawCreate(dpy, draw, visual,
   4175 					       DefaultColormap(dpy, scr));
   4176 	}
   4177 
   4178 	/*
   4179 	 * ndata0/wdata0 provide fallback to non-bolded Xft font if a glyph is
   4180 	 * not found in the bold version.
   4181 	 */
   4182 #define IS_BOLD  (recur.attr_flags & BOLDATTR(screen))
   4183 #define NOT_BOLD (recur.attr_flags & ~BOLDATTR(screen))
   4184 	ndata = getNormXftFont(&recur, recur.attr_flags, &did_ul);
   4185 #if OPT_WIDE_CHARS && OPT_BOX_CHARS
   4186 	ndata0 = IS_BOLD ? getNormXftFont(&recur, NOT_BOLD, &did_ul) : ndata;
   4187 #endif
   4188 #if OPT_RENDERWIDE && OPT_BOX_CHARS
   4189 	wdata = getWideXftFont(&recur, recur.attr_flags);
   4190 	wdata0 = IS_BOLD ? getWideXftFont(&recur, NOT_BOLD) : wdata;
   4191 #endif
   4192 
   4193 #define GET_XFT_FG() getXftColor(recur.xw, values.foreground)
   4194 #define GET_XFT_BG() getXftColor(recur.xw, values.background)
   4195 
   4196 	values.foreground = getCgsFore(recur.xw, currentWin, gc);
   4197 	values.background = getCgsBack(recur.xw, currentWin, gc);
   4198 
   4199 	if (!(recur.draw_flags & NOBACKGROUND)) {
   4200 	    XftColor *bg_color = GET_XFT_BG();
   4201 	    int nc = xtermXftWidth(&recur, recur.attr_flags,
   4202 				   bg_color,
   4203 				   XftFp(ndata), x, y,
   4204 				   text,
   4205 				   len);
   4206 	    XftDrawRect(screen->renderDraw,
   4207 			bg_color,
   4208 			x, y,
   4209 			(unsigned) (nc * FontWidth(screen)),
   4210 			(unsigned) FontHeight(screen));
   4211 	}
   4212 
   4213 	y += XftFp(ndata)->ascent;
   4214 #if OPT_BOX_CHARS
   4215 	{
   4216 	    /* adding code to substitute simulated line-drawing characters */
   4217 	    int last, first = 0;
   4218 	    int curX = x;
   4219 
   4220 	    for (last = 0; last < (int) len; last++) {
   4221 		Boolean replace = False;
   4222 		Boolean missing = False;
   4223 		unsigned ch = (unsigned) text[last];
   4224 		int ch_width = CharWidth(screen, ch);
   4225 		int filler = 0;
   4226 #if OPT_WIDE_CHARS
   4227 		int needed = forceDbl ? 2 : ch_width;
   4228 		XTermXftFonts *currData = pickXftData(needed, ndata, wdata);
   4229 		XftFont *tempFont = NULL;
   4230 #define CURR_TEMP (tempFont ? tempFont : XftFp(currData))
   4231 
   4232 		if (xtermIsInternalCs(ch) || ch == 0) {
   4233 		    /*
   4234 		     * Xft generally does not have the line-drawing characters
   4235 		     * in cells 0-31.  Assume this (we cannot inspect the
   4236 		     * picture easily...), and attempt to fill in from real
   4237 		     * line-drawing character in the font at the Unicode
   4238 		     * position.  Failing that, use our own box-characters.
   4239 		     */
   4240 		    if (screen->force_box_chars
   4241 			|| screen->broken_box_chars
   4242 			|| xtermXftMissing(recur.xw,
   4243 					   currData, 0,
   4244 					   XftFp(currData),
   4245 					   dec2ucs(screen, ch))) {
   4246 			SetMissing("case 1");
   4247 		    } else {
   4248 			ch = dec2ucs(screen, ch);
   4249 			replace = True;
   4250 		    }
   4251 		} else if (ch >= 256) {
   4252 		    /*
   4253 		     * If we're reading UTF-8 from the client, we may have a
   4254 		     * line-drawing character.  Translate it back to our
   4255 		     * box-code if Xft tells us that the glyph is missing.
   4256 		     */
   4257 		    if_OPT_WIDE_CHARS(screen, {
   4258 			unsigned part = ucs2dec(screen, ch);
   4259 			if (xtermIsInternalCs(part)) {
   4260 			    if (screen->force_box_chars
   4261 				|| screen->broken_box_chars) {
   4262 				SetMissing("case 2");
   4263 				ch = part;
   4264 			    }
   4265 			}
   4266 			if (!missing && xtermXftMissing(recur.xw,
   4267 							currData, 0,
   4268 							XftFp(currData), ch)) {
   4269 			    int found = findXftGlyph(recur.xw, currData, ch);
   4270 			    XftFont *test;
   4271 			    if (found >= 0) {
   4272 				test = XftFpN(currData, found);
   4273 			    } else {
   4274 				test = pickXftFont(needed,
   4275 						   XftFp(ndata0),
   4276 						   XftFp(wdata0));
   4277 			    }
   4278 			    if (!xtermXftMissing(recur.xw,
   4279 						 currData, found,
   4280 						 test, ch)) {
   4281 				tempFont = test;
   4282 				replace = True;
   4283 				filler = 0;
   4284 			    } else if ((part = AsciiEquivs(ch)) != ch) {
   4285 				filler = needed - 1;
   4286 				ch = part;
   4287 				replace = True;
   4288 			    } else if (ch != HIDDEN_CHAR) {
   4289 				SetMissing("case 3");
   4290 			    }
   4291 			}
   4292 		    });
   4293 		}
   4294 #else
   4295 		XTermXftFonts *currData = ndata;
   4296 #define CURR_TEMP XftFp(currData)
   4297 		if (xtermIsInternalCs(ch)) {
   4298 		    /*
   4299 		     * Xft generally does not have the line-drawing characters
   4300 		     * in cells 1-31.  Check for this, and attempt to fill in
   4301 		     * from real line-drawing character in the font at the
   4302 		     * Unicode position.  Failing that, use our own
   4303 		     * box-characters.
   4304 		     */
   4305 		    if (xtermXftMissing(recur.xw,
   4306 					currData, 0,
   4307 					XftFp(currData), ch)) {
   4308 			SetMissing("case 4");
   4309 		    }
   4310 		}
   4311 #endif
   4312 
   4313 		/*
   4314 		 * If we now have one of our box-codes, draw it directly.
   4315 		 */
   4316 		if (missing || replace) {
   4317 		    /* line drawing character time */
   4318 		    if (last > first) {
   4319 			int nc = drawClippedXftString(&recur,
   4320 						      recur.attr_flags,
   4321 						      XftFp(currData),
   4322 						      GET_XFT_FG(),
   4323 						      curX,
   4324 						      y,
   4325 						      text + first,
   4326 						      (Cardinal) (last - first));
   4327 			curX += nc * FontWidth(screen);
   4328 			underline_len += (Cardinal) nc;
   4329 		    }
   4330 		    if (missing) {
   4331 			Dimension old_wide = screen->fnt_wide;
   4332 			Dimension old_high = screen->fnt_high;
   4333 			screen->fnt_wide = (Dimension) FontWidth(screen);
   4334 			screen->fnt_high = (Dimension) FontHeight(screen);
   4335 
   4336 			xtermDrawBoxChar(&recur, ch,
   4337 					 gc,
   4338 					 curX,
   4339 					 y - FontAscent(screen),
   4340 					 1,
   4341 					 True);
   4342 			curX += FontWidth(screen);
   4343 			underline_len += 1;
   4344 			screen->fnt_wide = old_wide;
   4345 			screen->fnt_high = old_high;
   4346 		    } else {
   4347 			IChar ch2 = (IChar) ch;
   4348 			int nc = drawClippedXftString(&recur,
   4349 						      recur.attr_flags,
   4350 						      CURR_TEMP,
   4351 						      GET_XFT_FG(),
   4352 						      curX,
   4353 						      y,
   4354 						      &ch2,
   4355 						      1);
   4356 			curX += nc * FontWidth(screen);
   4357 			underline_len += (Cardinal) nc;
   4358 			if (filler) {
   4359 			    ch2 = ' ';
   4360 			    nc = drawClippedXftString(&recur,
   4361 						      recur.attr_flags,
   4362 						      CURR_TEMP,
   4363 						      GET_XFT_FG(),
   4364 						      curX,
   4365 						      y,
   4366 						      &ch2,
   4367 						      1);
   4368 			    curX += nc * FontWidth(screen);
   4369 			    underline_len += (Cardinal) nc;
   4370 			}
   4371 		    }
   4372 		    first = last + 1;
   4373 		}
   4374 		if (ch_width > 0)
   4375 		    ncells += ch_width;
   4376 	    }
   4377 	    if (last > first) {
   4378 		int nc = drawClippedXftString(&recur,
   4379 					      recur.attr_flags,
   4380 					      XftFp(ndata),
   4381 					      GET_XFT_FG(),
   4382 					      curX,
   4383 					      y,
   4384 					      text + first,
   4385 					      (Cardinal) (last - first));
   4386 		underline_len += (Cardinal) nc;
   4387 	    }
   4388 	}
   4389 #else
   4390 	{
   4391 	    int nc = drawClippedXftString(&recur,
   4392 					  recur.attr_flags,
   4393 					  XftFp(ndata),
   4394 					  GET_XFT_FG(),
   4395 					  x,
   4396 					  y,
   4397 					  text,
   4398 					  len);
   4399 	    underline_len += (Cardinal) nc;
   4400 	    ncells = nc;
   4401 	}
   4402 #endif /* OPT_BOX_CHARS */
   4403 
   4404 	drawUnderline(recur.xw,
   4405 		      gc,
   4406 		      recur.attr_flags,
   4407 		      underline_len,
   4408 		      FontWidth(screen),
   4409 		      x,
   4410 		      y + y_shift,
   4411 		      did_ul);
   4412 
   4413 	x += (int) ncells *FontWidth(screen);
   4414 
   4415 	TRACE(("DrewText [%4d,%4d] @%d\n", y, x, __LINE__));
   4416 	return x;
   4417     }
   4418 #endif /* OPT_RENDERFONT */
   4419     curFont = ((recur.attr_flags & BOLDATTR(screen))
   4420 	       ? WhichVFontData(screen, fBold)
   4421 	       : WhichVFontData(screen, fNorm));
   4422     /*
   4423      * If we're asked to display a proportional font, do this with a fixed
   4424      * pitch.  Yes, it's ugly.  But we cannot distinguish the use of xterm
   4425      * as a dumb terminal vs its use as in fullscreen programs such as vi.
   4426      * Hint: do not try to use a proportional font in the icon.
   4427      */
   4428     if (!IsIcon(screen) && !(recur.draw_flags & CHARBYCHAR) && screen->fnt_prop) {
   4429 	int adj, width;
   4430 
   4431 	while (len--) {
   4432 	    int cells = WideCells(*text);
   4433 #if OPT_BOX_CHARS
   4434 #if OPT_WIDE_CHARS
   4435 	    if (*text == HIDDEN_CHAR) {
   4436 		++text;
   4437 		continue;
   4438 	    } else
   4439 #endif
   4440 	    if (IsXtermMissingChar(screen, *text, curFont)) {
   4441 		adj = 0;
   4442 	    } else
   4443 #endif
   4444 	    {
   4445 		if_WIDE_OR_NARROW(screen, {
   4446 		    XChar2b temp[1];
   4447 		    temp[0].byte2 = LO_BYTE(*text);
   4448 		    temp[0].byte1 = HI_BYTE(*text);
   4449 		    width = XTextWidth16(curFont->fs, temp, 1);
   4450 		}
   4451 		, {
   4452 		    char temp[1];
   4453 		    temp[0] = (char) LO_BYTE(*text);
   4454 		    width = XTextWidth(curFont->fs, temp, 1);
   4455 		});
   4456 		adj = (FontWidth(screen) - width) / 2;
   4457 		if (adj < 0)
   4458 		    adj = 0;
   4459 	    }
   4460 	    xtermFillCells(&recur, gc, x, y, (Cardinal) cells);
   4461 	    recur.draw_flags |= (NOBACKGROUND | CHARBYCHAR);
   4462 	    x = drawXtermText(&recur,
   4463 			      gc, x + adj, y,
   4464 			      text++, 1) - adj;
   4465 	}
   4466 
   4467 	TRACE(("DrewText [%4d,%4d] @%d\n", y, x, __LINE__));
   4468 	return x;
   4469     }
   4470 #if OPT_BOX_CHARS
   4471     /*
   4472      * Draw some substitutions, if needed.  The font may not include the
   4473      * line-drawing set, or it may be incomplete (in which case we'll draw an
   4474      * empty space via xtermDrawBoxChar), or we may be told to force our
   4475      * line-drawing.
   4476      *
   4477      * The empty space is a special case which can be overridden with the
   4478      * showMissingGlyphs resource to produce an outline.  Not all fonts in
   4479      * "modern" (sic) X provide an empty space; some use a thick outline or
   4480      * something like the replacement character.  If you would rather not see
   4481      * that, you can set assumeAllChars.
   4482      */
   4483     if (!IsIcon(screen)
   4484 	&& !(recur.draw_flags & NOTRANSLATION)
   4485 	&& (!screen->fnt_boxes
   4486 	    || (FontIsIncomplete(curFont) && !screen->assume_all_chars)
   4487 	    || recur.xw->work.force_wideFont
   4488 	    || screen->force_box_chars)) {
   4489 	/*
   4490 	 * Fill in missing box-characters.  Find regions without missing
   4491 	 * characters, and draw them calling ourselves recursively.  Draw
   4492 	 * missing characters via xtermDrawBoxChar().
   4493 	 */
   4494 	int last, first = 0;
   4495 	Bool drewBoxes = False;
   4496 
   4497 	for (last = 0; last < (int) len; last++) {
   4498 	    unsigned ch = (unsigned) text[last];
   4499 	    Bool isMissing;
   4500 	    int ch_width;
   4501 #if OPT_WIDE_CHARS
   4502 	    unsigned part;
   4503 
   4504 	    if (ch == HIDDEN_CHAR) {
   4505 		if (last > first) {
   4506 		    XTermDraw param2 = recur;
   4507 		    param2.draw_flags |= NOTRANSLATION;
   4508 		    x = drawXtermText(&param2,
   4509 				      gc,
   4510 				      x, y,
   4511 				      text + first,
   4512 				      (unsigned) (last - first));
   4513 		}
   4514 		first = last + 1;
   4515 		drewBoxes = True;
   4516 		continue;
   4517 	    }
   4518 	    ch_width = CharWidth(screen, ch);
   4519 	    isMissing =
   4520 		IsXtermMissingChar(screen, ch,
   4521 				   ((recur.on_wide || ch_width > 1)
   4522 				    && okFont(NormalWFont(screen)))
   4523 				   ? WhichVFontData(screen, fWide)
   4524 				   : curFont);
   4525 #else
   4526 	    isMissing = IsXtermMissingChar(screen, ch, curFont);
   4527 	    ch_width = 1;
   4528 #endif
   4529 	    /*
   4530 	     * If the character is not missing, but we're in wide-character
   4531 	     * mode and the character happens to be a wide-character that
   4532 	     * corresponds to the line-drawing set, allow the forceBoxChars
   4533 	     * resource (or menu entry) to force it to display using our
   4534 	     * tables.
   4535 	     */
   4536 	    if_OPT_WIDE_CHARS(screen, {
   4537 		if (!isMissing
   4538 		    && TScreenOf(recur.xw)->force_box_chars) {
   4539 		    if (ch > 255
   4540 			&& (part = ucs2dec(screen, ch)) < 32) {
   4541 			ch = part;
   4542 			isMissing = True;
   4543 		    } else if (ch < 32) {
   4544 			isMissing = True;
   4545 		    }
   4546 		}
   4547 	    });
   4548 
   4549 	    if (isMissing) {
   4550 		if (last > first) {
   4551 		    XTermDraw param2 = recur;
   4552 		    param2.draw_flags |= NOTRANSLATION;
   4553 		    x = drawXtermText(&recur,
   4554 				      gc,
   4555 				      x, y,
   4556 				      text + first,
   4557 				      (unsigned) (last - first));
   4558 		}
   4559 #if OPT_WIDE_CHARS
   4560 		if (ch_width <= 0 && ch < 32)
   4561 		    ch_width = 1;	/* special case for line-drawing */
   4562 		else if (ch_width < 0)
   4563 		    ch_width = 1;	/* special case for combining char */
   4564 		if (ch > 255 && (part = ucs2dec(screen, ch)) < 32) {
   4565 		    xtermDrawBoxChar(&recur, part, gc, x, y, 1, False);
   4566 		} else if (!ucs_workaround(&recur, ch, gc, x, y)) {
   4567 		    xtermDrawBoxChar(&recur, ch, gc, x, y, ch_width, False);
   4568 		}
   4569 #else
   4570 		xtermDrawBoxChar(&recur, ch, gc, x, y, ch_width, False);
   4571 #endif
   4572 		x += (ch_width * FontWidth(screen));
   4573 		first = last + 1;
   4574 		drewBoxes = True;
   4575 	    } else if (ch_width == 2
   4576 		       && recur.xw->work.force_wideFont
   4577 		       && !(recur.draw_flags & NOTRANSLATION)) {
   4578 		XTermDraw param2 = recur;
   4579 		param2.draw_flags |= NOTRANSLATION;
   4580 		/*
   4581 		 * Not a "box" character, but a special case treated similarly.
   4582 		 */
   4583 		(void) drawXtermText(&param2,
   4584 				     gc,
   4585 				     x, y,
   4586 				     text + first,
   4587 				     (unsigned) (1 + last - first));
   4588 		x += (ch_width * FontWidth(screen));
   4589 		first = last + 1;
   4590 		drewBoxes = True;
   4591 	    }
   4592 	}
   4593 	if (last <= first) {
   4594 	    TRACE(("DrewText [%4d,%4d] @%d\n", y, x, __LINE__));
   4595 	    return x;
   4596 	}
   4597 	text += first;
   4598 	len = (Cardinal) (last - first);
   4599 	recur.draw_flags |= NOTRANSLATION;
   4600 	if (drewBoxes) {
   4601 	    x = drawXtermText(&recur,
   4602 			      gc,
   4603 			      x,
   4604 			      y,
   4605 			      text,
   4606 			      len);
   4607 	    TRACE(("DrewText [%4d,%4d] @%d\n", y, x, __LINE__));
   4608 	    return x;
   4609 	}
   4610     }
   4611 #endif /* OPT_BOX_CHARS */
   4612     /*
   4613      * Behave as if the font has (maybe Unicode-replacements for) drawing
   4614      * characters in the range 1-31 (either we were not asked to ignore them,
   4615      * or the caller made sure that there is none).
   4616      */
   4617 #if OPT_WIDE_ATTRS
   4618 #define AttrFlags() recur.attr_flags
   4619 #define DrawFlags() recur.draw_flags
   4620 #else
   4621 #define AttrFlags() (recur.attr_flags & DRAWX_MASK)
   4622 #define DrawFlags() (recur.draw_flags & (unsigned)~DRAWX_MASK)
   4623 #endif
   4624     TRACE(("drawtext%c[%4d,%4d] {%#x,%#x} (%d) %d:%s\n",
   4625 	   screen->cursor_state == OFF ? ' ' : '*',
   4626 	   y, x,
   4627 	   AttrFlags(),
   4628 	   DrawFlags(),
   4629 	   recur.this_chrset, len,
   4630 	   visibleIChars(text, len)));
   4631     if (screen->scale_height != 1.0f) {
   4632 	xtermFillCells(&recur, gc, x, y, (Cardinal) len);
   4633     }
   4634     y += FontAscent(screen);
   4635 
   4636 #if OPT_WIDE_CHARS
   4637 
   4638     need_wide = False;
   4639     if (screen->wide_chars || screen->unicode_font) {
   4640 	int src;
   4641 	for (src = 0; src < (int) len; src++) {
   4642 	    IChar ch = text[src];
   4643 	    if (ch > 0xff) {
   4644 		need_wide = True;
   4645 		break;
   4646 	    }
   4647 	}
   4648     }
   4649 
   4650     if (need_wide) {
   4651 	IChar *mapped;
   4652 	Bool needWide = False;
   4653 	int src, dst;
   4654 	Bool useBoldFont;
   4655 	int ascent_adjust = 0;
   4656 
   4657 	BumpTypedBuffer(XChar2b, len);
   4658 	BumpTypedBuffer(Char, len);
   4659 	BumpTypedBuffer(IChar, len);
   4660 	mapped = BfBuf(IChar);
   4661 
   4662 	/* transform characters first, to decide how to draw them */
   4663 	for (src = 0; src < (int) len; src++) {
   4664 	    IChar ch = text[src];
   4665 	    if (ch != HIDDEN_CHAR) {
   4666 #if OPT_BOX_CHARS
   4667 		if ((screen->fnt_boxes == 1) && (ch >= 256)) {
   4668 		    unsigned part = ucs2dec(screen, ch);
   4669 		    if (part < 32)
   4670 			ch = (IChar) part;
   4671 		}
   4672 #endif
   4673 #if OPT_MINI_LUIT
   4674 #define Map2Sbuf(from,to) (ch == from) { ch = to; }
   4675 		if (screen->latin9_mode && !screen->utf8_mode && ch < 256) {
   4676 
   4677 		    /* see http://www.cs.tut.fi/~jkorpela/latin9.html */
   4678 		    /* *INDENT-OFF* */
   4679 		    if Map2Sbuf(0xa4, 0x20ac)
   4680 		    else if Map2Sbuf(0xa6, 0x0160)
   4681 		    else if Map2Sbuf(0xa8, 0x0161)
   4682 		    else if Map2Sbuf(0xb4, 0x017d)
   4683 		    else if Map2Sbuf(0xb8, 0x017e)
   4684 		    else if Map2Sbuf(0xbc, 0x0152)
   4685 		    else if Map2Sbuf(0xbd, 0x0153)
   4686 		    else if Map2Sbuf(0xbe, 0x0178)
   4687 		    /* *INDENT-ON* */
   4688 
   4689 		}
   4690 		if (screen->unicode_font
   4691 		    && (ch == ANSI_DEL ||
   4692 			ch < ANSI_SPA)) {
   4693 		    ch = (IChar) dec2ucs(screen,
   4694 					 (unsigned) ((ch == ANSI_DEL) ? 0 : ch));
   4695 		}
   4696 #endif /* OPT_MINI_LUIT */
   4697 	    }
   4698 	    mapped[src] = ch;
   4699 	}
   4700 
   4701 	for (src = dst = 0; src < (int) len; src++) {
   4702 	    IChar ch = mapped[src];
   4703 
   4704 	    if (ch != HIDDEN_CHAR) {
   4705 		int ch_width = CharWidth(screen, ch);
   4706 		if (!needWide
   4707 		    && !IsIcon(screen)
   4708 		    && ((recur.on_wide || ch_width > 1)
   4709 			&& okFont(NormalWFont(screen)))) {
   4710 		    needWide = True;
   4711 		}
   4712 		mapped[dst++] = ch;
   4713 	    }
   4714 	}
   4715 
   4716 	/*
   4717 	 * Check for special case where the bold font lacks glyphs found in the
   4718 	 * normal font, and drop down to normal fonts with overstriking to help
   4719 	 * show the actual characters.
   4720 	 */
   4721 	useBoldFont = ((recur.attr_flags & BOLDATTR(screen)) != 0);
   4722 	if (useBoldFont) {
   4723 	    XTermFonts *norm = NULL;
   4724 	    XTermFonts *bold = NULL;
   4725 	    Bool noBold, noNorm;
   4726 
   4727 	    (void) norm;
   4728 	    if (needWide && okFont(BoldWFont(screen))) {
   4729 		norm = WhichVFontData(screen, fWide);
   4730 		bold = WhichVFontData(screen, fWBold);
   4731 	    } else if (okFont(BoldFont(screen))) {
   4732 		norm = WhichVFontData(screen, fNorm);
   4733 		bold = WhichVFontData(screen, fBold);
   4734 	    } else {
   4735 		useBoldFont = False;
   4736 	    }
   4737 
   4738 	    if (useBoldFont && FontIsIncomplete(bold)) {
   4739 		for (src = 0; src < dst; src++) {
   4740 		    IChar ch = mapped[src];
   4741 
   4742 		    (void) ch;
   4743 		    noBold = IsXtermMissingChar(screen, ch, bold);
   4744 		    if (noBold) {
   4745 			noNorm = IsXtermMissingChar(screen, ch, norm);
   4746 			if (!noNorm) {
   4747 			    useBoldFont = False;
   4748 			    break;
   4749 			}
   4750 		    }
   4751 		}
   4752 	    }
   4753 	}
   4754 
   4755 	/* This is probably wrong. But it works. */
   4756 	underline_len = (Cardinal) dst;
   4757 
   4758 	/* Set the drawing font */
   4759 	if (!(recur.draw_flags & (DOUBLEHFONT | DOUBLEWFONT))) {
   4760 	    VTwin *currentWin = WhichVWin(screen);
   4761 	    VTFontEnum fntId;
   4762 	    CgsEnum cgsId;
   4763 	    Pixel fg = getCgsFore(recur.xw, currentWin, gc);
   4764 	    Pixel bg = getCgsBack(recur.xw, currentWin, gc);
   4765 
   4766 	    if (needWide
   4767 		&& useBoldFont
   4768 		&& okFont(BoldWFont(screen))) {
   4769 		fntId = fWBold;
   4770 		cgsId = gcWBold;
   4771 	    } else if (needWide) {
   4772 		fntId = fWide;
   4773 		cgsId = gcWide;
   4774 	    } else if (useBoldFont) {
   4775 		fntId = fBold;
   4776 		cgsId = gcBold;
   4777 	    } else {
   4778 		fntId = fNorm;
   4779 		cgsId = gcNorm;
   4780 	    }
   4781 
   4782 	    setCgsFore(recur.xw, currentWin, cgsId, fg);
   4783 	    setCgsBack(recur.xw, currentWin, cgsId, bg);
   4784 	    gc = getCgsGC(recur.xw, currentWin, cgsId);
   4785 
   4786 #if OPT_WIDE_ATTRS
   4787 #if OPT_DEC_CHRSET
   4788 	    if (!(CSET_DOUBLE(recur.this_chrset) || (recur.draw_flags & DOUBLEWFONT)))
   4789 #endif
   4790 	    {
   4791 		need_clipping = fixupItalics(&recur,
   4792 					     gc,
   4793 					     getCgsFont(recur.xw,
   4794 							currentWin, gc),
   4795 					     y, x, font_width, len);
   4796 	    }
   4797 #endif
   4798 	    if (fntId != fNorm) {
   4799 		XFontStruct *thisFp = WhichVFont(screen, fntId);
   4800 		ascent_adjust = (thisFp->ascent
   4801 				 - NormalFont(screen)->ascent);
   4802 		if (thisFp->max_bounds.width ==
   4803 		    NormalFont(screen)->max_bounds.width * 2) {
   4804 		    underline_len = real_length = (Cardinal) (dst * 2);
   4805 		} else if (cgsId == gcWide || cgsId == gcWBold) {
   4806 		    int ascent2 = Max(NormalFont(screen)->ascent,
   4807 				      thisFp->ascent);
   4808 		    underline_len = real_length = (Cardinal) (dst * 2);
   4809 		    xtermFillCells(&recur,
   4810 				   gc,
   4811 				   x,
   4812 				   y - ascent2,
   4813 				   real_length);
   4814 		}
   4815 	    }
   4816 	}
   4817 
   4818 	xtermFullString16(&recur, recur.draw_flags, gc,
   4819 			  x, y + y_shift + ascent_adjust,
   4820 			  dst, needWide);
   4821 #if OPT_WIDE_ATTRS
   4822 	if (need_clipping) {
   4823 	    endClipping(screen, gc);
   4824 	}
   4825 #endif
   4826 
   4827 	if ((recur.attr_flags & BOLDATTR(screen)) && (screen->enbolden || !useBoldFont)) {
   4828 	    if (!(recur.draw_flags & (DOUBLEWFONT | DOUBLEHFONT))) {
   4829 		beginClipping(screen, gc, (Cardinal) font_width, len);
   4830 	    }
   4831 	    xtermFullString16(&recur, recur.draw_flags | NOBACKGROUND,
   4832 			      gc, x + 1, y + y_shift + ascent_adjust,
   4833 			      dst, needWide);
   4834 	    if (!(recur.draw_flags & (DOUBLEWFONT | DOUBLEHFONT))) {
   4835 		endClipping(screen, gc);
   4836 	    }
   4837 	}
   4838 
   4839     } else
   4840 #endif /* OPT_WIDE_CHARS */
   4841     {
   4842 	IChar *mapped;
   4843 	Cardinal dst;
   4844 
   4845 	BumpTypedBuffer(IChar, len);
   4846 	BumpTypedBuffer(Char, len);
   4847 
   4848 	mapped = BfBuf(IChar);
   4849 	for (dst = 0; dst < len; ++dst)
   4850 	    mapped[dst] = text[dst];
   4851 
   4852 #if OPT_WIDE_ATTRS
   4853 #if OPT_DEC_CHRSET
   4854 	if (!(CSET_DOUBLE(recur.this_chrset) || (recur.draw_flags & DOUBLEWFONT)))
   4855 #endif
   4856 	{
   4857 	    need_clipping = fixupItalics(&recur, gc, curFont,
   4858 					 y, x, font_width, len);
   4859 	}
   4860 #endif
   4861 
   4862 	xtermDrawString(screen, recur.draw_flags, gc,
   4863 			x, y + y_shift, (int) len);
   4864 
   4865 #if OPT_WIDE_ATTRS
   4866 	if (need_clipping) {
   4867 	    endClipping(screen, gc);
   4868 	}
   4869 #endif
   4870 	underline_len = len;
   4871 	if ((recur.attr_flags & BOLDATTR(screen)) && screen->enbolden) {
   4872 	    if (!(recur.draw_flags & (DOUBLEWFONT | DOUBLEHFONT))) {
   4873 		beginClipping(screen, gc, font_width, (int) len);
   4874 	    }
   4875 	    xtermDrawString(screen, (recur.draw_flags | NOBACKGROUND),
   4876 			    gc, x + 1, y + y_shift, (int) len);
   4877 	    if (!(recur.draw_flags & (DOUBLEWFONT | DOUBLEHFONT))) {
   4878 		endClipping(screen, gc);
   4879 	    }
   4880 	}
   4881     }
   4882 
   4883     drawUnderline(recur.xw,
   4884 		  gc,
   4885 		  recur.attr_flags,
   4886 		  underline_len,
   4887 		  font_width,
   4888 		  x,
   4889 		  y + y_shift,
   4890 		  did_ul);
   4891 
   4892     x += ((int) real_length) * FontWidth(screen);
   4893     TRACE(("DrewText [%4d,%4d] @%d\n", y, x, __LINE__));
   4894     return x;
   4895 }
   4896 
   4897 #if OPT_WIDE_CHARS
   4898 /*
   4899  * Allocate buffer - workaround for wide-character interfaces.
   4900  */
   4901 void
   4902 allocXtermChars(ScrnPtr *buffer, Cardinal length)
   4903 {
   4904     if (*buffer == NULL) {
   4905 	*buffer = (ScrnPtr) XtMalloc(length);
   4906     } else {
   4907 	*buffer = (ScrnPtr) XtRealloc((char *) *buffer, length);
   4908     }
   4909 }
   4910 #endif
   4911 
   4912 /* set up size hints for window manager; min 1 char by 1 char */
   4913 void
   4914 xtermSizeHints(XtermWidget xw, int scrollbarWidth)
   4915 {
   4916     TScreen *screen = TScreenOf(xw);
   4917 
   4918     TRACE(("xtermSizeHints\n"));
   4919     TRACE(("   border    %d\n", xw->core.border_width));
   4920     TRACE(("   scrollbar %d\n", scrollbarWidth));
   4921 
   4922     xw->hints.base_width = 2 * screen->border + scrollbarWidth;
   4923     xw->hints.base_height = 2 * screen->border;
   4924 
   4925 #if OPT_TOOLBAR
   4926     TRACE(("   toolbar   %d\n", ToolbarHeight(xw)));
   4927 
   4928     xw->hints.base_height += ToolbarHeight(xw);
   4929     xw->hints.base_height += BorderWidth(xw) * 2;
   4930     xw->hints.base_width += BorderWidth(xw) * 2;
   4931 #endif
   4932 
   4933     if (xw->misc.resizeByPixel) {
   4934 	xw->hints.width_inc = 1;
   4935 	xw->hints.height_inc = 1;
   4936     } else {
   4937 	xw->hints.width_inc = FontWidth(screen);
   4938 	xw->hints.height_inc = FontHeight(screen);
   4939     }
   4940     xw->hints.min_width = xw->hints.base_width + xw->hints.width_inc;
   4941     xw->hints.min_height = xw->hints.base_height + xw->hints.height_inc;
   4942 
   4943     xw->hints.width = MaxCols(screen) * FontWidth(screen) + xw->hints.min_width;
   4944     xw->hints.height = MaxRows(screen) * FontHeight(screen) + xw->hints.min_height;
   4945 
   4946     xw->hints.flags |= (PSize | PBaseSize | PMinSize | PResizeInc);
   4947 
   4948     TRACE_HINTS(&(xw->hints));
   4949 }
   4950 
   4951 void
   4952 getXtermSizeHints(XtermWidget xw)
   4953 {
   4954     TScreen *screen = TScreenOf(xw);
   4955     long supp;
   4956 
   4957     if (!XGetWMNormalHints(screen->display, VShellWindow(xw),
   4958 			   &xw->hints, &supp))
   4959 	memset(&xw->hints, 0, sizeof(xw->hints));
   4960     TRACE_HINTS(&(xw->hints));
   4961 }
   4962 
   4963 CgsEnum
   4964 whichXtermCgs(XtermWidget xw, unsigned attr_flags, Bool hilite)
   4965 {
   4966     TScreen *screen = TScreenOf(xw);
   4967     CgsEnum cgsId = gcMAX;
   4968 
   4969     if (ReverseOrHilite(screen, attr_flags, hilite)) {
   4970 	if (attr_flags & BOLDATTR(screen)) {
   4971 	    cgsId = gcBoldReverse;
   4972 	} else {
   4973 	    cgsId = gcNormReverse;
   4974 	}
   4975     } else {
   4976 	if (attr_flags & BOLDATTR(screen)) {
   4977 	    cgsId = gcBold;
   4978 	} else {
   4979 	    cgsId = gcNorm;
   4980 	}
   4981     }
   4982     return cgsId;
   4983 }
   4984 
   4985 /*
   4986  * Returns a GC, selected according to the font (reverse/bold/normal) that is
   4987  * required for the current position (implied).  The GC is updated with the
   4988  * current screen foreground and background colors.
   4989  */
   4990 GC
   4991 updatedXtermGC(XtermWidget xw, unsigned attr_flags, CellColor fg_bg,
   4992 	       Bool hilite)
   4993 {
   4994     TScreen *screen = TScreenOf(xw);
   4995     VTwin *win = WhichVWin(screen);
   4996     CgsEnum cgsId = whichXtermCgs(xw, attr_flags, hilite);
   4997     Pixel my_fg = extract_fg(xw, fg_bg, attr_flags);
   4998     Pixel my_bg = extract_bg(xw, fg_bg, attr_flags);
   4999     Pixel fg_pix = getXtermFG(xw, attr_flags, (int) my_fg);
   5000     Pixel bg_pix = getXtermBG(xw, attr_flags, (int) my_bg);
   5001     Pixel xx_pix;
   5002 #if OPT_HIGHLIGHT_COLOR
   5003     Boolean reverse2 = ((attr_flags & INVERSE) && hilite);
   5004     Pixel selbg_pix = T_COLOR(screen, HIGHLIGHT_BG);
   5005     Pixel selfg_pix = T_COLOR(screen, HIGHLIGHT_FG);
   5006     Boolean always = screen->hilite_color;
   5007     Boolean use_selbg = (Boolean) (always &&
   5008 				   isNotForeground(xw, fg_pix, bg_pix, selbg_pix));
   5009     Boolean use_selfg = (Boolean) (always &&
   5010 				   isNotBackground(xw, fg_pix, bg_pix, selfg_pix));
   5011 #endif
   5012 
   5013     (void) fg_bg;
   5014     (void) my_bg;
   5015     (void) my_fg;
   5016 
   5017     /*
   5018      * Discard video attributes overridden by colorXXXMode's.
   5019      */
   5020     checkVeryBoldColors(attr_flags, my_fg);
   5021 
   5022     if (ReverseOrHilite(screen, attr_flags, hilite)) {
   5023 #if OPT_HIGHLIGHT_COLOR
   5024 	if (!screen->hilite_color) {
   5025 	    if (selbg_pix != T_COLOR(screen, TEXT_FG)
   5026 		&& selbg_pix != fg_pix
   5027 		&& selbg_pix != bg_pix
   5028 		&& selbg_pix != xw->dft_foreground) {
   5029 		bg_pix = fg_pix;
   5030 		fg_pix = selbg_pix;
   5031 	    }
   5032 	}
   5033 #endif
   5034 	EXCHANGE(fg_pix, bg_pix, xx_pix);
   5035 #if OPT_HIGHLIGHT_COLOR
   5036 	if (screen->hilite_color) {
   5037 	    if (screen->hilite_reverse) {
   5038 		if (use_selbg) {
   5039 		    if (use_selfg) {
   5040 			bg_pix = fg_pix;
   5041 		    } else {
   5042 			fg_pix = bg_pix;
   5043 			bg_pix = selbg_pix;
   5044 		    }
   5045 		}
   5046 		if (use_selfg)
   5047 		    fg_pix = selfg_pix;
   5048 	    }
   5049 	}
   5050 #endif
   5051     } else if ((attr_flags & INVERSE) && hilite) {
   5052 #if OPT_HIGHLIGHT_COLOR
   5053 	if (!screen->hilite_color) {
   5054 	    if (selbg_pix != T_COLOR(screen, TEXT_FG)
   5055 		&& selbg_pix != fg_pix
   5056 		&& selbg_pix != bg_pix
   5057 		&& selbg_pix != xw->dft_foreground) {
   5058 		bg_pix = fg_pix;
   5059 		fg_pix = selbg_pix;
   5060 	    }
   5061 	}
   5062 #endif
   5063 	/* double-reverse... EXCHANGE(fg_pix, bg_pix, xx_pix); */
   5064 #if OPT_HIGHLIGHT_COLOR
   5065 	if (screen->hilite_color) {
   5066 	    if (screen->hilite_reverse) {
   5067 		if (use_selbg) {
   5068 		    if (use_selfg ^ reverse2) {
   5069 			bg_pix = fg_pix;
   5070 		    } else {
   5071 			fg_pix = bg_pix;
   5072 		    }
   5073 		    if (reverse2) {
   5074 			fg_pix = selbg_pix;
   5075 		    } else {
   5076 			bg_pix = selbg_pix;
   5077 		    }
   5078 		}
   5079 		if (use_selfg) {
   5080 		    if (reverse2) {
   5081 			bg_pix = selfg_pix;
   5082 		    } else {
   5083 			fg_pix = selfg_pix;
   5084 		    }
   5085 		}
   5086 	    }
   5087 	}
   5088 #endif
   5089     }
   5090 #if OPT_HIGHLIGHT_COLOR
   5091     if (!screen->hilite_color || !screen->hilite_reverse) {
   5092 	if (hilite && !screen->hilite_reverse) {
   5093 	    if (use_selbg) {
   5094 		if (reverse2)
   5095 		    fg_pix = selbg_pix;
   5096 		else
   5097 		    bg_pix = selbg_pix;
   5098 	    }
   5099 	    if (use_selfg) {
   5100 		if (reverse2)
   5101 		    bg_pix = selfg_pix;
   5102 		else
   5103 		    fg_pix = selfg_pix;
   5104 	    }
   5105 	}
   5106     }
   5107 #endif
   5108 
   5109 #if OPT_BLINK_TEXT
   5110     if ((screen->blink_state == ON) &&
   5111 	(!screen->blink_as_bold) &&
   5112 	(attr_flags & BLINK)) {
   5113 	fg_pix = bg_pix;
   5114     }
   5115 #endif
   5116 
   5117     if (attr_flags & INVISIBLE) {
   5118 	fg_pix = bg_pix;
   5119     }
   5120     setCgsFore(xw, win, cgsId, fg_pix);
   5121     setCgsBack(xw, win, cgsId, bg_pix);
   5122     return getCgsGC(xw, win, cgsId);
   5123 }
   5124 
   5125 /*
   5126  * Resets the foreground/background of the GC returned by 'updatedXtermGC()'
   5127  * to the values that would be set in SGR_Foreground and SGR_Background. This
   5128  * duplicates some logic, but only modifies 1/4 as many GC's.
   5129  */
   5130 void
   5131 resetXtermGC(XtermWidget xw, unsigned attr_flags, Bool hilite)
   5132 {
   5133     TScreen *screen = TScreenOf(xw);
   5134     VTwin *win = WhichVWin(screen);
   5135     CgsEnum cgsId = whichXtermCgs(xw, attr_flags, hilite);
   5136     Pixel fg_pix = getXtermFG(xw, attr_flags, xw->cur_foreground);
   5137     Pixel bg_pix = getXtermBG(xw, attr_flags, xw->cur_background);
   5138 
   5139     checkVeryBoldColors(attr_flags, xw->cur_foreground);
   5140 
   5141     if (ReverseOrHilite(screen, attr_flags, hilite)) {
   5142 	setCgsFore(xw, win, cgsId, bg_pix);
   5143 	setCgsBack(xw, win, cgsId, fg_pix);
   5144     } else {
   5145 	setCgsFore(xw, win, cgsId, fg_pix);
   5146 	setCgsBack(xw, win, cgsId, bg_pix);
   5147     }
   5148 }
   5149 
   5150 #if OPT_ISO_COLORS
   5151 /*
   5152  * Extract the foreground-color index from a color pair.
   5153  * If we've got BOLD or UNDERLINE color-mode active, those will be used.
   5154  */
   5155 Pixel
   5156 extract_fg(XtermWidget xw, CellColor color, unsigned attr_flags)
   5157 {
   5158     unsigned fg = ExtractForeground(color);
   5159 
   5160     if (TScreenOf(xw)->colorAttrMode
   5161 	|| (fg == ExtractBackground(color))) {
   5162 	fg = MapToColorMode(fg, TScreenOf(xw), attr_flags);
   5163     }
   5164     return fg;
   5165 }
   5166 
   5167 /*
   5168  * Extract the background-color index from a color pair.
   5169  * If we've got INVERSE color-mode active, that will be used.
   5170  */
   5171 Pixel
   5172 extract_bg(XtermWidget xw, CellColor color, unsigned attr_flags)
   5173 {
   5174     unsigned bg = ExtractBackground(color);
   5175 
   5176     if (TScreenOf(xw)->colorAttrMode
   5177 	|| (bg == ExtractForeground(color))) {
   5178 	if (TScreenOf(xw)->colorRVMode && (attr_flags & INVERSE))
   5179 	    bg = COLOR_RV;
   5180     }
   5181     return bg;
   5182 }
   5183 
   5184 /*
   5185  * Combine the current foreground and background into a single 8-bit number.
   5186  * Note that we're storing the SGR foreground, since cur_foreground may be set
   5187  * to COLOR_UL, COLOR_BD or COLOR_BL, which would make the code larger than 8
   5188  * bits.
   5189  *
   5190  * This assumes that fg/bg are equal when we override with one of the special
   5191  * attribute colors.
   5192  */
   5193 CellColor
   5194 makeColorPair(XtermWidget xw)
   5195 {
   5196     CellColor result;
   5197 
   5198 #if OPT_DIRECT_COLOR
   5199     result.fg = xw->cur_foreground;
   5200     result.bg = xw->cur_background;
   5201 #else
   5202     int fg = xw->cur_foreground;
   5203     int bg = xw->cur_background;
   5204     unsigned my_bg = okIndexedColor(bg) ? (unsigned) bg : 0;
   5205     unsigned my_fg = okIndexedColor(fg) ? (unsigned) fg : my_bg;
   5206 
   5207     result = (CellColor) (my_fg | (my_bg << COLOR_BITS));
   5208 #endif
   5209 
   5210     return result;
   5211 }
   5212 
   5213 /*
   5214  * Using the "current" SGR background, clear a rectangle.
   5215  */
   5216 void
   5217 ClearCurBackground(XtermWidget xw,
   5218 		   int top,
   5219 		   int left,
   5220 		   unsigned height,
   5221 		   unsigned width,
   5222 		   unsigned fw)
   5223 {
   5224     TScreen *screen = TScreenOf(xw);
   5225     int actual_rows = PlusStatusLine(screen, screen->max_row + 1);
   5226     Boolean visible = (((int) width > 0)
   5227 		       && ((left + (int) width) <= screen->max_col + 1)
   5228 		       && (((int) height + top) <= actual_rows));
   5229 
   5230     TRACE(("ClearCurBackground %d,%d %dx%d%s with %d %s\n",
   5231 	   top, left, height, width,
   5232 	   IsStatusShown(screen) ? "*" : "",
   5233 	   xw->cur_background,
   5234 	   visible ? "(ok)" : "(err)"));
   5235 
   5236     if (VWindow(screen) && visible) {
   5237 	set_background(xw, xw->cur_background);
   5238 
   5239 	xtermClear2(xw,
   5240 		    CursorX2(screen, left, fw),
   5241 		    CursorY2(screen, top),
   5242 		    (width * fw),
   5243 		    (height * (unsigned) FontHeight(screen)));
   5244 
   5245 	set_background(xw, -1);
   5246     }
   5247 }
   5248 #endif /* OPT_ISO_COLORS */
   5249 
   5250 Pixel
   5251 getXtermBackground(XtermWidget xw, unsigned attr_flags, int color)
   5252 {
   5253     Pixel result = T_COLOR(TScreenOf(xw), TEXT_BG);
   5254 
   5255 #if OPT_ISO_COLORS
   5256     if (color >= 0) {
   5257 	if_OPT_DIRECT_COLOR2_else(TScreenOf(xw), (attr_flags & ATR_DIRECT_BG), {
   5258 	    result = (Pixel) color;
   5259 	}) if ((attr_flags & BG_COLOR) && (color < MAXCOLORS)) {
   5260 	    result = GET_COLOR_RES(xw, TScreenOf(xw)->Acolors[color]);
   5261 	}
   5262     }
   5263 #else
   5264     (void) attr_flags;
   5265     (void) color;
   5266 #endif
   5267     return result;
   5268 }
   5269 
   5270 #if OPT_ISO_COLORS && OPT_WIDE_ATTRS
   5271 #if OPT_SGR2_HASH
   5272 typedef struct _DimColorHT {
   5273     Pixel org;
   5274     Pixel dim;
   5275 } DimColorHT;
   5276 
   5277 static unsigned
   5278 jhash1(const unsigned char *key, size_t len)
   5279 {
   5280     unsigned hash;
   5281     size_t i;
   5282 
   5283     for (hash = 0, i = 0; i < len; ++i) {
   5284 	hash += key[i];
   5285 	hash += (hash << 10);
   5286 	hash ^= (hash >> 6);
   5287     }
   5288     hash += (hash << 3);
   5289     hash ^= (hash >> 11);
   5290     hash += (hash << 15);
   5291     return hash;
   5292 }
   5293 
   5294 static unsigned
   5295 computeFaint(XtermWidget xw, unsigned value, unsigned compare)
   5296 {
   5297     TScreen *screen = TScreenOf(xw);
   5298     if (screen->faint_relative) {
   5299 	value = (unsigned) ((value + compare) / 2);
   5300     } else {
   5301 	value = (unsigned) ((2 * value) / 3);
   5302     }
   5303     return value;
   5304 }
   5305 #endif /* OPT_SGR2_HASH */
   5306 #endif /* OPT_ISO_COLORS && OPT_WIDE_ATTRS */
   5307 
   5308 Pixel
   5309 getXtermForeground(XtermWidget xw, unsigned attr_flags, int color)
   5310 {
   5311     Pixel result = T_COLOR(TScreenOf(xw), TEXT_FG);
   5312 
   5313 #if OPT_ISO_COLORS
   5314     if_OPT_DIRECT_COLOR2_else(TScreenOf(xw), (attr_flags & ATR_DIRECT_FG), {
   5315 	result = (Pixel) color;
   5316     })
   5317 	if ((attr_flags & FG_COLOR) &&
   5318 	    (color >= 0 && color < MAXCOLORS)) {
   5319 	result = GET_COLOR_RES(xw, TScreenOf(xw)->Acolors[color]);
   5320     }
   5321 #else
   5322     (void) attr_flags;
   5323     (void) color;
   5324 #endif
   5325 
   5326 #if OPT_ISO_COLORS && OPT_WIDE_ATTRS
   5327     if ((attr_flags & ATR_FAINT)) {
   5328 #if OPT_SGR2_HASH
   5329 #define DIM_IT(n) work.n = (unsigned short) computeFaint(xw, work.n, bkg.n)
   5330 #define SizeOfHT ((unsigned) sizeof(unsigned long) * CHAR_BIT)
   5331 	static DimColorHT ht[SizeOfHT];
   5332 	Pixel bg = T_COLOR(TScreenOf(xw), TEXT_BG);
   5333 	XColor work;
   5334 	Pixel p;
   5335 
   5336 	if ((color >= 0)
   5337 	    || (result != (Pixel) color)) {
   5338 	    static unsigned long have = 0;
   5339 	    static Boolean have_bg = False;
   5340 	    static XColor bkg;
   5341 
   5342 	    /* cache bkg color in r/g/b */
   5343 	    if (!have_bg || bg != bkg.pixel) {
   5344 		bkg.pixel = bg;
   5345 		have_bg = QueryOneColor(xw, &bkg);
   5346 		have = 0;	/* invalidate color cache */
   5347 	    }
   5348 	    if (have_bg) {
   5349 		unsigned hv;
   5350 		hv = jhash1((unsigned char *) &result, sizeof(result));
   5351 		hv %= SizeOfHT;
   5352 
   5353 		if ((have & (1UL << hv))
   5354 		    && ht[hv].org == result) {
   5355 		    result = ht[hv].dim;	/* return cached color */
   5356 		} else {
   5357 		    work.pixel = result;
   5358 		    if (QueryOneColor(xw, &work)) {
   5359 			DIM_IT(red);
   5360 			DIM_IT(green);
   5361 			DIM_IT(blue);
   5362 			p = result;
   5363 			if (allocateBestRGB(xw, &work)) {
   5364 			    result = work.pixel;
   5365 			}
   5366 
   5367 			/* cache the result */
   5368 			have |= (1UL << hv);
   5369 			ht[hv].org = p;
   5370 			ht[hv].dim = result;
   5371 		    }
   5372 		}
   5373 	    }
   5374 	}
   5375 #else /* !OPT_SGR2_HASH */
   5376 #define DIM_IT(n) work.n = (unsigned short) ((2 * (unsigned)work.n) / 3)
   5377 	static Pixel last_in;
   5378 	static Pixel last_out;
   5379 	if ((result != last_in)
   5380 	    && ((color >= 0)
   5381 		|| (result != (Pixel) color))) {
   5382 	    XColor work;
   5383 	    last_in = result;
   5384 	    work.pixel = result;
   5385 	    if (QueryOneColor(xw, &work)) {
   5386 		DIM_IT(red);
   5387 		DIM_IT(green);
   5388 		DIM_IT(blue);
   5389 		if (allocateBestRGB(xw, &work)) {
   5390 		    result = work.pixel;
   5391 		}
   5392 	    }
   5393 	    last_out = result;
   5394 	} else {
   5395 	    result = last_out;
   5396 	}
   5397 #endif /* OPT_SGR2_HASH */
   5398     }
   5399 #endif
   5400     return result;
   5401 }
   5402 
   5403 /*
   5404  * Returns a single base character for the given cell.
   5405  */
   5406 unsigned
   5407 getXtermCell(TScreen *screen, int row, int col)
   5408 {
   5409     CLineData *ld = getLineData(screen, row);
   5410 
   5411     return ((ld && (col < (int) ld->lineSize))
   5412 	    ? ld->charData[col]
   5413 	    : (unsigned) ' ');
   5414 }
   5415 
   5416 /*
   5417  * Sets a single base character for the given cell.
   5418  */
   5419 void
   5420 putXtermCell(TScreen *screen, int row, int col, int ch)
   5421 {
   5422     LineData *ld = getLineData(screen, row);
   5423 
   5424     if (ld && (col < (int) ld->lineSize)) {
   5425 	ld->charData[col] = (CharData) ch;
   5426 	if_OPT_WIDE_CHARS(screen, {
   5427 	    size_t off;
   5428 	    for_each_combData(off, ld) {
   5429 		ld->combData[off][col] = 0;
   5430 	    }
   5431 	});
   5432     }
   5433 }
   5434 
   5435 #if OPT_WIDE_CHARS
   5436 /*
   5437  * Add a combining character for the given cell
   5438  */
   5439 void
   5440 addXtermCombining(TScreen *screen, int row, int col, unsigned ch)
   5441 {
   5442     if (ch != 0) {
   5443 	LineData *ld = getLineData(screen, row);
   5444 	size_t off;
   5445 
   5446 	TRACE(("addXtermCombining %d,%d U+%04X (%d)\n",
   5447 	       row, col, ch, CharWidth(screen, ch)));
   5448 
   5449 	for_each_combData(off, ld) {
   5450 	    if (!ld->combData[off][col]) {
   5451 		ld->combData[off][col] = (CharData) ch;
   5452 		break;
   5453 	    }
   5454 	}
   5455     }
   5456 }
   5457 
   5458 unsigned
   5459 getXtermCombining(TScreen *screen, int row, int col, int off)
   5460 {
   5461     CLineData *ld = getLineData(screen, row);
   5462     return (ld->combSize ? ld->combData[off][col] : 0U);
   5463 }
   5464 #endif
   5465 
   5466 void
   5467 update_keyboard_type(void)
   5468 {
   5469     update_delete_del();
   5470     update_tcap_fkeys();
   5471     update_old_fkeys();
   5472     update_hp_fkeys();
   5473     update_sco_fkeys();
   5474     update_sun_fkeys();
   5475     update_sun_kbd();
   5476 }
   5477 
   5478 void
   5479 set_keyboard_type(XtermWidget xw, xtermKeyboardType type, Bool set)
   5480 {
   5481     xtermKeyboardType save = xw->keyboard.type;
   5482 
   5483     TRACE(("set_keyboard_type(%s, %s) currently %s\n",
   5484 	   visibleKeyboardType(type),
   5485 	   BtoS(set),
   5486 	   visibleKeyboardType(xw->keyboard.type)));
   5487     if (set) {
   5488 	xw->keyboard.type = type;
   5489     } else {
   5490 	xw->keyboard.type = keyboardIsDefault;
   5491     }
   5492 
   5493     if (save != xw->keyboard.type) {
   5494 	update_keyboard_type();
   5495     }
   5496 }
   5497 
   5498 void
   5499 toggle_keyboard_type(XtermWidget xw, xtermKeyboardType type)
   5500 {
   5501     xtermKeyboardType save = xw->keyboard.type;
   5502 
   5503     TRACE(("toggle_keyboard_type(%s) currently %s\n",
   5504 	   visibleKeyboardType(type),
   5505 	   visibleKeyboardType(xw->keyboard.type)));
   5506     if (xw->keyboard.type == type) {
   5507 	xw->keyboard.type = keyboardIsDefault;
   5508     } else {
   5509 	xw->keyboard.type = type;
   5510     }
   5511 
   5512     if (save != xw->keyboard.type) {
   5513 	update_keyboard_type();
   5514     }
   5515 }
   5516 
   5517 const char *
   5518 visibleKeyboardType(xtermKeyboardType type)
   5519 {
   5520     const char *result = "?";
   5521     switch (type) {
   5522 	CASETYPE(keyboardIsLegacy);	/* bogus vt220 codes for F1-F4, etc. */
   5523 	CASETYPE(keyboardIsDefault);
   5524 	CASETYPE(keyboardIsHP);
   5525 	CASETYPE(keyboardIsSCO);
   5526 	CASETYPE(keyboardIsSun);
   5527 	CASETYPE(keyboardIsTermcap);
   5528 	CASETYPE(keyboardIsVT220);
   5529     }
   5530     return result;
   5531 }
   5532 
   5533 static void
   5534 init_keyboard_type(XtermWidget xw, xtermKeyboardType type, Bool set)
   5535 {
   5536     TRACE(("init_keyboard_type(%s, %s) currently %s\n",
   5537 	   visibleKeyboardType(type),
   5538 	   BtoS(set),
   5539 	   visibleKeyboardType(xw->keyboard.type)));
   5540     if (set) {
   5541 	/*
   5542 	 * Check for conflicts, e.g., if someone asked for both Sun and HP
   5543 	 * function keys.
   5544 	 */
   5545 	if (guard_keyboard_type) {
   5546 	    xtermWarning("Conflicting keyboard type option (%s/%s)\n",
   5547 			 visibleKeyboardType(xw->keyboard.type),
   5548 			 visibleKeyboardType(type));
   5549 	}
   5550 	xw->keyboard.type = type;
   5551 	guard_keyboard_type = True;
   5552 	update_keyboard_type();
   5553     }
   5554 }
   5555 
   5556 /*
   5557  * If the keyboardType resource is set, use that, overriding the individual
   5558  * boolean resources for different keyboard types.
   5559  */
   5560 void
   5561 decode_keyboard_type(XtermWidget xw, XTERM_RESOURCE * rp)
   5562 {
   5563 #define DATA(n, t, f) { n, t, XtOffsetOf(XTERM_RESOURCE, f) }
   5564 #define FLAG(n) *(Boolean *)(((char *)rp) + table[n].offset)
   5565     static struct {
   5566 	const char *name;
   5567 	xtermKeyboardType type;
   5568 	unsigned offset;
   5569     } table[] = {
   5570 	DATA(NAME_OLD_KT, keyboardIsLegacy, oldKeyboard),
   5571 #if OPT_HP_FUNC_KEYS
   5572 	    DATA(NAME_HP_KT, keyboardIsHP, hpFunctionKeys),
   5573 #endif
   5574 #if OPT_SCO_FUNC_KEYS
   5575 	    DATA(NAME_SCO_KT, keyboardIsSCO, scoFunctionKeys),
   5576 #endif
   5577 #if OPT_SUN_FUNC_KEYS
   5578 	    DATA(NAME_SUN_KT, keyboardIsSun, sunFunctionKeys),
   5579 #endif
   5580 #if OPT_SUNPC_KBD
   5581 	    DATA(NAME_VT220_KT, keyboardIsVT220, sunKeyboard),
   5582 #endif
   5583 #if OPT_TCAP_FKEYS
   5584 	    DATA(NAME_TCAP_KT, keyboardIsTermcap, termcapKeys),
   5585 #endif
   5586     };
   5587     Cardinal n;
   5588     TScreen *screen = TScreenOf(xw);
   5589 
   5590     TRACE(("decode_keyboard_type(%s)\n", rp->keyboardType));
   5591     if (!x_strcasecmp(rp->keyboardType, "unknown")) {
   5592 	/*
   5593 	 * Let the individual resources comprise the keyboard-type.
   5594 	 */
   5595 	for (n = 0; n < XtNumber(table); ++n)
   5596 	    init_keyboard_type(xw, table[n].type, FLAG(n));
   5597     } else if (!x_strcasecmp(rp->keyboardType, "default")) {
   5598 	/*
   5599 	 * Set the keyboard-type to the Sun/PC type, allowing modified
   5600 	 * function keys, etc.
   5601 	 */
   5602 	for (n = 0; n < XtNumber(table); ++n)
   5603 	    init_keyboard_type(xw, table[n].type, False);
   5604     } else {
   5605 	Bool found = False;
   5606 
   5607 	/*
   5608 	 * Special case: oldXtermFKeys should have been like the others.
   5609 	 */
   5610 	if (!x_strcasecmp(rp->keyboardType, NAME_OLD_KT)) {
   5611 	    TRACE(("special case, setting oldXtermFKeys\n"));
   5612 	    screen->old_fkeys = True;
   5613 	    screen->old_fkeys0 = True;
   5614 	}
   5615 
   5616 	/*
   5617 	 * Choose an individual keyboard type.
   5618 	 */
   5619 	for (n = 0; n < XtNumber(table); ++n) {
   5620 	    if (!x_strcasecmp(rp->keyboardType, table[n].name + 1)) {
   5621 		FLAG(n) = True;
   5622 		found = True;
   5623 	    } else {
   5624 		FLAG(n) = False;
   5625 	    }
   5626 	    init_keyboard_type(xw, table[n].type, FLAG(n));
   5627 	}
   5628 	if (!found) {
   5629 	    xtermWarning("KeyboardType resource \"%s\" not found\n",
   5630 			 rp->keyboardType);
   5631 	}
   5632     }
   5633 #undef DATA
   5634 #undef FLAG
   5635 }
   5636 
   5637 #if OPT_WIDE_CHARS
   5638 #if defined(HAVE_WCHAR_H) && defined(HAVE_WCWIDTH)
   5639 /*
   5640  * If xterm is running in a UTF-8 locale, it is still possible to encounter
   5641  * old runtime configurations which yield incomplete or inaccurate data.
   5642  */
   5643 static Bool
   5644 systemWcwidthOk(int samplesize, int samplepass)
   5645 {
   5646     wchar_t n;
   5647     int oops = 0;
   5648 
   5649     for (n = 21; n <= 25; ++n) {
   5650 	wchar_t code = (wchar_t) dec2ucs(NULL, (unsigned) n);
   5651 	int system_code = wcwidth(code);
   5652 	int intern_code = mk_wcwidth(code);
   5653 
   5654 	/*
   5655 	 * Solaris 10 wcwidth() returns "2" for all of the line-drawing (page
   5656 	 * 0x2500) and most of the geometric shapes (a few are excluded, just
   5657 	 * to make it more difficult to use).  Do a sanity check to avoid using
   5658 	 * it.
   5659 	 */
   5660 	if ((system_code < 0 && intern_code >= 1)
   5661 	    || (system_code >= 0 && intern_code != system_code)) {
   5662 	    TRACE(("systemWcwidthOk: broken system line-drawing wcwidth\n"));
   5663 	    oops += (samplepass + 1);
   5664 	    break;
   5665 	}
   5666     }
   5667 
   5668     for (n = 0; n < (wchar_t) samplesize; ++n) {
   5669 	int system_code = wcwidth(n);
   5670 	int intern_code = mk_wcwidth(n);
   5671 
   5672 	/*
   5673 	 * When this check was originally implemented, there were few if any
   5674 	 * libraries with full Unicode coverage.  Time passes, and it is
   5675 	 * possible to make a full comparison of the BMP.  There are some
   5676 	 * differences: mk_wcwidth() marks some codes as combining and some
   5677 	 * as single-width, differing from GNU libc.
   5678 	 */
   5679 	if ((system_code < 0 && intern_code >= 1)
   5680 	    || (system_code >= 0 && intern_code != system_code)) {
   5681 	    TRACE((".. width(U+%04X) = %d, expected %d\n",
   5682 		   (unsigned) n, system_code, intern_code));
   5683 	    if (++oops > samplepass)
   5684 		break;
   5685 	}
   5686     }
   5687     TRACE(("systemWcwidthOk: %d/%d mismatches, allowed %d\n",
   5688 	   oops, (int) n, samplepass));
   5689     return (oops <= samplepass);
   5690 }
   5691 #endif /* HAVE_WCWIDTH */
   5692 
   5693 void
   5694 decode_wcwidth(XtermWidget xw)
   5695 {
   5696     int mode = ((xw->misc.cjk_width ? 2 : 0)
   5697 		+ (xw->misc.mk_width ? 1 : 0)
   5698 		+ 1);
   5699 
   5700     switch (mode) {
   5701     default:
   5702 #if defined(HAVE_WCHAR_H) && defined(HAVE_WCWIDTH)
   5703 	if (xtermEnvUTF8() &&
   5704 	    systemWcwidthOk(xw->misc.mk_samplesize, xw->misc.mk_samplepass)) {
   5705 	    my_wcwidth = wcwidth;
   5706 	    TRACE(("using system wcwidth() function\n"));
   5707 	    break;
   5708 	}
   5709 #endif
   5710 	/* FALLTHRU */
   5711     case 2:
   5712 	my_wcwidth = &mk_wcwidth;
   5713 	TRACE(("using MK wcwidth() function\n"));
   5714 	break;
   5715     case 3:
   5716 	/* FALLTHRU */
   5717     case 4:
   5718 	my_wcwidth = &mk_wcwidth_cjk;
   5719 	TRACE(("using MK-CJK wcwidth() function\n"));
   5720 	break;
   5721     }
   5722 
   5723     for (first_widechar = 128; first_widechar < 4500; ++first_widechar) {
   5724 	if (my_wcwidth((wchar_t) first_widechar) > 1) {
   5725 	    TRACE(("first_widechar %#x\n", first_widechar));
   5726 	    break;
   5727 	}
   5728     }
   5729 }
   5730 #endif
   5731 
   5732 /*
   5733  * Extend a (normally) boolean resource value by checking for additional values
   5734  * which will be mapped into true/false.
   5735  */
   5736 int
   5737 extendedBoolean(const char *value, const FlagList * table, Cardinal limit)
   5738 {
   5739     int result = -1;
   5740     long check;
   5741     char *next;
   5742     Cardinal n;
   5743 
   5744     if ((x_strcasecmp(value, "true") == 0)
   5745 	|| (x_strcasecmp(value, "yes") == 0)
   5746 	|| (x_strcasecmp(value, "on") == 0)) {
   5747 	result = True;
   5748     } else if ((x_strcasecmp(value, "false") == 0)
   5749 	       || (x_strcasecmp(value, "no") == 0)
   5750 	       || (x_strcasecmp(value, "off") == 0)) {
   5751 	result = False;
   5752     } else if ((check = strtol(value, &next, 0)) >= 0 && FullS2L(value, next)) {
   5753 	if (check >= (long) limit)	/* i.e., past False=0, True=1 */
   5754 	    check = True;
   5755 	result = (int) check;
   5756     } else {
   5757 	for (n = 0; n < limit - 2; ++n) {
   5758 	    if (table[n].name == NULL) {
   5759 		break;
   5760 	    } else if (x_strcasecmp(value, table[n].name) == 0) {
   5761 		result = table[n].code;
   5762 		break;
   5763 	    }
   5764 	}
   5765     }
   5766 
   5767     if (result < 0) {
   5768 	xtermWarning("Unrecognized keyword: %s\n", value);
   5769 	result = False;
   5770     }
   5771 
   5772     TRACE(("extendedBoolean(%s) = %d\n", value, result));
   5773     return result;
   5774 }
   5775 
   5776 /*
   5777  * Something like round() from math library, but round() is less widely-used
   5778  * than xterm.  Also, there are no negative numbers to complicate this.
   5779  */
   5780 int
   5781 dimRound(double value)
   5782 {
   5783     int result = (int) value;
   5784     if (result < value)
   5785 	++result;
   5786     return result;
   5787 }
   5788 
   5789 /*
   5790  * Find the geometry of the specified Xinerama screen
   5791  */
   5792 static void
   5793 find_xinerama_screen(Display *display, int screen, struct Xinerama_geometry *ret)
   5794 {
   5795 #ifdef HAVE_X11_EXTENSIONS_XINERAMA_H
   5796     XineramaScreenInfo *screens;
   5797     int nb_screens;
   5798 
   5799     if (screen == -1)		/* already inited */
   5800 	return;
   5801     screens = XineramaQueryScreens(display, &nb_screens);
   5802     if (screen >= nb_screens) {
   5803 	xtermWarning("Xinerama screen %d does not exist\n", screen);
   5804 	return;
   5805     }
   5806     if (screen == -2) {
   5807 	int ptr_x, ptr_y;
   5808 	int dummy_int, i;
   5809 	unsigned dummy_uint;
   5810 	Window dummy_win;
   5811 	if (nb_screens == 0)
   5812 	    return;
   5813 	XQueryPointer(display, DefaultRootWindow(display),
   5814 		      &dummy_win, &dummy_win,
   5815 		      &ptr_x, &ptr_y,
   5816 		      &dummy_int, &dummy_int, &dummy_uint);
   5817 	for (i = 0; i < nb_screens; i++) {
   5818 	    if ((ptr_x - screens[i].x_org) < screens[i].width &&
   5819 		(ptr_y - screens[i].y_org) < screens[i].height) {
   5820 		screen = i;
   5821 		break;
   5822 	    }
   5823 	}
   5824 	if (screen < 0) {
   5825 	    xtermWarning("Mouse not in any Xinerama screen, using 0\n");
   5826 	    screen = 0;
   5827 	}
   5828     }
   5829     ret->scr_x = screens[screen].x_org;
   5830     ret->scr_y = screens[screen].y_org;
   5831     ret->scr_w = screens[screen].width;
   5832     ret->scr_h = screens[screen].height;
   5833 #else /* HAVE_X11_EXTENSIONS_XINERAMA_H */
   5834     (void) display;
   5835     (void) ret;
   5836     if (screen > 0)
   5837 	xtermWarning("Xinerama support not enabled\n");
   5838 #endif /* HAVE_X11_EXTENSIONS_XINERAMA_H */
   5839 }
   5840 
   5841 /*
   5842  * Parse the screen code after the @ in a geometry string.
   5843  */
   5844 static void
   5845 parse_xinerama_screen(Display *display, const char *str, struct Xinerama_geometry *ret)
   5846 {
   5847     int screen = -1;
   5848     char *end;
   5849 
   5850     if (*str == 'g') {
   5851 	screen = -1;
   5852 	str++;
   5853     } else if (*str == 'c') {
   5854 	screen = -2;
   5855 	str++;
   5856     } else {
   5857 	long s = strtol(str, &end, 0);
   5858 	if (FullS2L(str, end) && ((int) s >= 0)) {
   5859 	    screen = (int) s;
   5860 	    str = end;
   5861 	}
   5862     }
   5863     if (*str) {
   5864 	xtermWarning("invalid Xinerama specification '%s'\n", str);
   5865 	return;
   5866     }
   5867     if (screen == -1)		/* already done */
   5868 	return;
   5869     find_xinerama_screen(display, screen, ret);
   5870 }
   5871 
   5872 /*
   5873  * Parse a geometry string with extra Xinerama specification:
   5874  * <w>x<h>+<x>+<y>@<screen>.
   5875  */
   5876 int
   5877 XParseXineramaGeometry(Display *display, char *parsestring, struct Xinerama_geometry *ret)
   5878 {
   5879     char *at, buf[128];
   5880 
   5881     ret->scr_x = 0;
   5882     ret->scr_y = 0;
   5883     ret->scr_w = DisplayWidth(display, DefaultScreen(display));
   5884     ret->scr_h = DisplayHeight(display, DefaultScreen(display));
   5885     at = strchr(parsestring, '@');
   5886     if (at != NULL && (size_t) (at - parsestring) < sizeof(buf) - 1) {
   5887 	memcpy(buf, parsestring, (size_t) (at - parsestring));
   5888 	buf[at - parsestring] = 0;
   5889 	parsestring = buf;
   5890 	parse_xinerama_screen(display, at + 1, ret);
   5891     }
   5892     return ((strlen(parsestring) <= MAX_U_STRING)
   5893 	    ? XParseGeometry(parsestring, &ret->x, &ret->y, &ret->w, &ret->h)
   5894 	    : 0);
   5895 }
   5896 
   5897 #if USE_DOUBLE_BUFFER
   5898 Window
   5899 VDrawable(TScreen *screen)
   5900 {
   5901     screen->needSwap = 1;
   5902     return WhichVWin(screen)->drawable;
   5903 }
   5904 #endif
   5905 
   5906 #if OPT_RENDERFONT
   5907 #ifndef discardRenderDraw
   5908 void
   5909 discardRenderDraw(TScreen *screen)
   5910 {
   5911     if (screen->renderDraw) {
   5912 	XftDrawDestroy(screen->renderDraw);
   5913 	screen->renderDraw = NULL;
   5914     }
   5915 }
   5916 #endif
   5917 #endif /* OPT_RENDERFONT */
   5918 
   5919 char *
   5920 xtermSetLocale(int category, String after)
   5921 {
   5922     char *before = x_strdup(setlocale(category, NULL));
   5923 
   5924     (void) setlocale(category, after);
   5925     TRACE(("before setlocale :%s\n", NonNull(before)));
   5926     TRACE(("updated locale   :%s\n", NonNull(setlocale(category, NULL))));
   5927     return before;
   5928 }
   5929 
   5930 void
   5931 xtermResetLocale(int category, char *before)
   5932 {
   5933     (void) setlocale(category, before);
   5934     free(before);
   5935     TRACE(("restored locale  :%s\n", NonNull(setlocale(category, NULL))));
   5936 }
   5937