"Fossies" - the Fresh Open Source Software Archive

Member "xterm-379/util.c" (4 Jan 2023, 144274 Bytes) of package /linux/misc/xterm-379.tgz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "util.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 377_vs_379.

    1 /* $XTermId: util.c,v 1.914 2023/01/04 09:21:31 tom Exp $ */
    2 
    3 /*
    4  * Copyright 1999-2022,2023 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)) != 0) {
  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)) != 0
  541         && (dst = getLineData(screen, row)) != 0) {
  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)) != 0
  552         && (dst = getLineData(screen, row)) != 0) {
  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, char *name)
 1011 {
 1012     Boolean code = False;
 1013 
 1014     if (resource.zIconBeep && TScreenOf(xw)->zIconBeep_flagged) {
 1015     char *format = resource.zIconFormat;
 1016     char *newname = malloc(strlen(name) + strlen(format) + 2);
 1017     if (!newname) {
 1018         xtermWarning("malloc failed in showZIconBeep\n");
 1019     } else {
 1020         char *marker = strstr(format, "%s");
 1021         char *result = newname;
 1022         if (marker != 0) {
 1023         size_t skip = (size_t) (marker - format);
 1024         if (skip) {
 1025             strncpy(result, format, skip);
 1026             result += skip;
 1027         }
 1028         strcpy(result, name);
 1029         strcat(result, marker + 2);
 1030         } else {
 1031         strcpy(result, format);
 1032         strcat(result, name);
 1033         }
 1034         ChangeGroup(xw, XtNiconName, newname);
 1035         free(newname);
 1036     }
 1037     code = True;
 1038     }
 1039     return code;
 1040 }
 1041 
 1042 /*
 1043  * Restore the icon name, resetting the state for zIconBeep.
 1044  */
 1045 void
 1046 resetZIconBeep(XtermWidget xw)
 1047 {
 1048     TScreen *screen = TScreenOf(xw);
 1049 
 1050     if (screen->zIconBeep_flagged) {
 1051     char *icon_name = getIconName();
 1052     screen->zIconBeep_flagged = False;
 1053     if (icon_name != NULL) {
 1054         char *buf = malloc(strlen(icon_name) + 1);
 1055         if (buf == NULL) {
 1056         screen->zIconBeep_flagged = True;
 1057         } else {
 1058         char *format = resource.zIconFormat;
 1059         char *marker = strstr(format, "%s");
 1060         Boolean found = False;
 1061 
 1062         if (marker != 0) {
 1063             if (marker == format
 1064             || !strncmp(icon_name, format, (size_t) (marker - format))) {
 1065             found = True;
 1066             strcpy(buf, icon_name + (marker - format));
 1067             marker += 2;
 1068             if (*marker != '\0') {
 1069                 size_t len_m = strlen(marker);
 1070                 size_t len_b = strlen(buf);
 1071                 if (len_m < len_b
 1072                 && !strcmp(buf + len_b - len_m, marker)) {
 1073                 buf[len_b - len_m] = '\0';
 1074                 }
 1075             }
 1076             }
 1077         } else if (!strncmp(icon_name, format, strlen(format))) {
 1078             strcpy(buf, icon_name + strlen(format));
 1079             found = True;
 1080         }
 1081         if (found)
 1082             ChangeIconName(xw, buf);
 1083         free(buf);
 1084         }
 1085     }
 1086     }
 1087 }
 1088 #else
 1089 #define setZIconBeep(xw)    /* nothing */
 1090 #endif /* OPT_ZICONBEEP */
 1091 
 1092 /*
 1093  * write a string str of length len onto the screen at
 1094  * the current cursor position.  update cursor position.
 1095  */
 1096 void
 1097 WriteText(XtermWidget xw, IChar *str, Cardinal len)
 1098 {
 1099     TScreen *screen = TScreenOf(xw);
 1100     XTermDraw params;
 1101     CLineData *ld = 0;
 1102     unsigned attr_flags = xw->flags;
 1103     CellColor fg_bg = xtermColorPair(xw);
 1104     unsigned cells = visual_width(str, len);
 1105     GC currentGC;
 1106 
 1107     TRACE(("WriteText %d (%2d,%2d) %3d:%s\n",
 1108        screen->topline,
 1109        screen->cur_row,
 1110        screen->cur_col,
 1111        len, visibleIChars(str, len)));
 1112 
 1113     if (cells + (unsigned) screen->cur_col > (unsigned) MaxCols(screen)) {
 1114     cells = (unsigned) (MaxCols(screen) - screen->cur_col);
 1115     }
 1116 
 1117     if (screen->cur_row <= screen->max_row
 1118     && ScrnHaveSelection(screen)
 1119     && ScrnIsRowInSelection(screen, INX2ROW(screen, screen->cur_row))) {
 1120     ScrnDisownSelection(xw);
 1121     }
 1122 #if OPT_ISO_COLORS
 1123     /* if colorBDMode is set, and enabled */
 1124     if (screen->colorBDMode &&
 1125     screen->boldColors &&
 1126     !hasDirectFG(attr_flags) &&
 1127     /* and bold foreground color on bold background color */
 1128     GetCellColorFG(fg_bg) > COLOR_7 &&
 1129     GetCellColorFG(fg_bg) < MIN_ANSI_COLORS &&
 1130     /* and both colors are the same */
 1131     GetCellColorFG(fg_bg) == GetCellColorBG(fg_bg))
 1132     /* clear BOLD flag, else it will be colorBD on bold background color */
 1133     UIntClr(attr_flags, BOLD);
 1134 #endif
 1135 
 1136     /* if we are in insert-mode, reserve space for the new cells */
 1137     if (attr_flags & INSERT) {
 1138     InsertChar(xw, cells);
 1139     }
 1140 
 1141     if (AddToVisible(xw)
 1142     && ((ld = getLineData(screen, screen->cur_row))) != 0) {
 1143     unsigned test;
 1144 
 1145     if (screen->cursor_state)
 1146         HideCursor(xw);
 1147 
 1148     /*
 1149      * If we overwrite part of a multi-column character, fill the rest
 1150      * of it with blanks.
 1151      */
 1152     if_OPT_WIDE_CHARS(screen, {
 1153         int kl;
 1154         int kr;
 1155         if (DamagedCurCells(screen, cells, &kl, &kr))
 1156         ClearInLine(xw, screen->cur_row, kl, (unsigned) (kr - kl + 1));
 1157     });
 1158 
 1159     if (attr_flags & INVISIBLE) {
 1160         Cardinal n;
 1161         for (n = 0; n < cells; ++n)
 1162         str[n] = ' ';
 1163     }
 1164 
 1165     TRACE(("WriteText calling drawXtermText (%d) (%d,%d)\n",
 1166            LineCharSet(screen, ld),
 1167            screen->cur_row,
 1168            screen->cur_col));
 1169 
 1170     test = attr_flags;
 1171 #if OPT_ISO_COLORS
 1172     {
 1173         int fg;
 1174         if (screen->colorAttrMode) {
 1175         fg = MapToColorMode(xw->cur_foreground, screen, attr_flags);
 1176         } else {
 1177         fg = xw->cur_foreground;
 1178         }
 1179         checkVeryBoldColors(test, fg);
 1180     }
 1181 #endif
 1182 
 1183     /* make sure that the correct GC is current */
 1184     currentGC = updatedXtermGC(xw, attr_flags, fg_bg, False);
 1185 
 1186     /* *INDENT-EQLS* */
 1187     params.xw          = xw;
 1188     params.attr_flags  = (test & DRAWX_MASK);
 1189     params.draw_flags  = 0;
 1190     params.this_chrset = LineCharSet(screen, ld);
 1191     params.real_chrset = CSET_SWL;
 1192     params.on_wide     = 0;
 1193 
 1194     drawXtermText(&params,
 1195               currentGC,
 1196               LineCursorX(screen, ld, screen->cur_col),
 1197               CursorY(screen, screen->cur_row),
 1198               str, len);
 1199 
 1200     resetXtermGC(xw, attr_flags, False);
 1201     }
 1202 
 1203     ScrnWriteText(xw, str, attr_flags, fg_bg, len);
 1204     CursorForward(xw, (int) cells);
 1205 
 1206     if (screen->cur_row <= screen->max_row) {
 1207     setZIconBeep(xw);
 1208     }
 1209     return;
 1210 }
 1211 
 1212 /*
 1213  * If cursor not in scrolling region, returns.  Else,
 1214  * inserts n blank lines at the cursor's position.  Lines above the
 1215  * bottom margin are lost.
 1216  */
 1217 void
 1218 InsertLine(XtermWidget xw, int n)
 1219 {
 1220     TScreen *screen = TScreenOf(xw);
 1221     int i;
 1222     int left = ScrnLeftMargin(xw);
 1223     int right = ScrnRightMargin(xw);
 1224     Boolean scroll_full_line = ((left == 0) && (right == screen->max_col));
 1225 
 1226     if (!ScrnIsRowInMargins(screen, screen->cur_row)
 1227     || screen->cur_col < left
 1228     || screen->cur_col > right)
 1229     return;
 1230 
 1231     TRACE(("InsertLine count=%d\n", n));
 1232 
 1233     set_cur_col(screen, ScrnLeftMargin(xw));
 1234     if (screen->cursor_state)
 1235     HideCursor(xw);
 1236 
 1237     if (ScrnHaveSelection(screen)
 1238     && ScrnAreRowsInSelection(screen,
 1239                   INX2ROW(screen, screen->top_marg),
 1240                   INX2ROW(screen, screen->cur_row - 1))
 1241     && ScrnAreRowsInSelection(screen,
 1242                   INX2ROW(screen, screen->cur_row),
 1243                   INX2ROW(screen, screen->bot_marg))) {
 1244     ScrnDisownSelection(xw);
 1245     }
 1246 
 1247     ResetWrap(screen);
 1248     if (n > (i = screen->bot_marg - screen->cur_row + 1))
 1249     n = i;
 1250     if (screen->jumpscroll && scroll_full_line) {
 1251     if (screen->scroll_amt <= 0 &&
 1252         screen->cur_row <= -screen->refresh_amt) {
 1253         if (-screen->refresh_amt + n > MaxRows(screen))
 1254         FlushScroll(xw);
 1255         screen->scroll_amt -= n;
 1256         screen->refresh_amt -= n;
 1257     } else {
 1258         if (screen->scroll_amt)
 1259         FlushScroll(xw);
 1260     }
 1261     }
 1262     if (!screen->scroll_amt && scroll_full_line) {
 1263     int shift = INX2ROW(screen, 0);
 1264     int bot = screen->max_row - shift;
 1265     int refreshheight = n;
 1266     int refreshtop = screen->cur_row + shift;
 1267     int scrolltop = refreshtop + refreshheight;
 1268     int scrollheight = (screen->bot_marg
 1269                 - screen->cur_row - refreshheight + 1);
 1270 
 1271     if ((i = screen->bot_marg - bot) > 0)
 1272         scrollheight -= i;
 1273     if ((i = screen->cur_row + refreshheight - 1 - bot) > 0)
 1274         refreshheight -= i;
 1275     vertical_copy_area(xw, scrolltop - n, scrollheight, -n, left, right);
 1276     if (refreshheight > 0) {
 1277         ClearCurBackground(xw,
 1278                    refreshtop,
 1279                    left,
 1280                    (unsigned) refreshheight,
 1281                    (unsigned) (right + 1 - left),
 1282                    (unsigned) FontWidth(screen));
 1283     }
 1284     }
 1285     if (n > 0) {
 1286     if (scroll_full_line) {
 1287         ScrnInsertLine(xw,
 1288                screen->visbuf,
 1289                screen->bot_marg,
 1290                screen->cur_row,
 1291                (unsigned) n);
 1292     } else {
 1293         scrollInMargins(xw, -n, screen->cur_row);
 1294         ScrnUpdate2(xw,
 1295             screen->cur_row,
 1296             left,
 1297             screen->bot_marg + 1 - screen->cur_row,
 1298             right + 1 - left,
 1299             True);
 1300     }
 1301     }
 1302 }
 1303 
 1304 /*
 1305  * If cursor not in scrolling region, returns.  Else, deletes n lines
 1306  * at the cursor's position, lines added at bottom margin are blank.
 1307  */
 1308 void
 1309 DeleteLine(XtermWidget xw, int n, Bool canSave)
 1310 {
 1311     TScreen *screen = TScreenOf(xw);
 1312     int i;
 1313     int left = ScrnLeftMargin(xw);
 1314     int right = ScrnRightMargin(xw);
 1315     Boolean scroll_all_lines = (Boolean) (screen->scrollWidget
 1316                       && !screen->whichBuf
 1317                       && screen->cur_row == 0);
 1318     Boolean scroll_full_line = ((left == 0) && (right == screen->max_col));
 1319 
 1320     if (!ScrnIsRowInMargins(screen, screen->cur_row) ||
 1321     !ScrnIsColInMargins(screen, screen->cur_col))
 1322     return;
 1323 
 1324     TRACE(("DeleteLine count=%d\n", n));
 1325 
 1326     set_cur_col(screen, ScrnLeftMargin(xw));
 1327     if (screen->cursor_state)
 1328     HideCursor(xw);
 1329 
 1330     if (n > (i = screen->bot_marg - screen->cur_row + 1)) {
 1331     n = i;
 1332     }
 1333     if (ScrnHaveSelection(screen)
 1334     && ScrnAreRowsInSelection(screen,
 1335                   INX2ROW(screen, screen->cur_row),
 1336                   INX2ROW(screen, screen->cur_row + n - 1))) {
 1337     ScrnDisownSelection(xw);
 1338     }
 1339 
 1340     ResetWrap(screen);
 1341     if (screen->jumpscroll && scroll_full_line) {
 1342     if (screen->scroll_amt >= 0 && screen->cur_row == screen->top_marg) {
 1343         if (screen->refresh_amt + n > MaxRows(screen))
 1344         FlushScroll(xw);
 1345         if (canSave) {
 1346         screen->scroll_amt += n;
 1347         screen->refresh_amt += n;
 1348         }
 1349     } else {
 1350         if (screen->scroll_amt)
 1351         FlushScroll(xw);
 1352     }
 1353     }
 1354 
 1355     /* adjust screen->buf */
 1356     if (n > 0) {
 1357     if (left > 0 || right < screen->max_col) {
 1358         scrollInMargins(xw, n, screen->cur_row);
 1359     } else if (canSave && scroll_all_lines) {
 1360         ScrnDeleteLine(xw,
 1361                screen->saveBuf_index,
 1362                screen->bot_marg + screen->savelines,
 1363                0,
 1364                (unsigned) n);
 1365     } else {
 1366         ScrnDeleteLine(xw,
 1367                screen->visbuf,
 1368                screen->bot_marg,
 1369                screen->cur_row,
 1370                (unsigned) n);
 1371     }
 1372     }
 1373 
 1374     /* repaint the screen, as needed */
 1375     if (!scroll_full_line) {
 1376     ScrnUpdate2(xw,
 1377             screen->cur_row,
 1378             left,
 1379             screen->bot_marg + 1 - screen->cur_row,
 1380             right + 1 - left,
 1381             True);
 1382     } else if (!screen->scroll_amt) {
 1383     int shift = INX2ROW(screen, 0);
 1384     int bot = screen->max_row - shift;
 1385     int refreshtop;
 1386     int refreshheight = n;
 1387     int scrolltop;
 1388     int scrollheight = i - n;
 1389 
 1390     if ((refreshtop = screen->bot_marg - refreshheight + 1 + shift) >
 1391         (i = screen->max_row - refreshheight + 1))
 1392         refreshtop = i;
 1393     if (canSave && scroll_all_lines) {
 1394         scrolltop = 0;
 1395         if ((scrollheight += shift) > i)
 1396         scrollheight = i;
 1397         IncrementSavedLines(n);
 1398     } else {
 1399         scrolltop = screen->cur_row + shift;
 1400         if ((i = screen->bot_marg - bot) > 0) {
 1401         scrollheight -= i;
 1402         if ((i = screen->cur_row + n - 1 - bot) >= 0) {
 1403             refreshheight -= i;
 1404         }
 1405         }
 1406     }
 1407     vertical_copy_area(xw, scrolltop + n, scrollheight, n, left, right);
 1408     if (shift > 0 && refreshheight > 0) {
 1409         int rows = refreshheight;
 1410         if (rows > shift)
 1411         rows = shift;
 1412         ScrnUpdate(xw, refreshtop, 0, rows, MaxCols(screen), True);
 1413         refreshtop += shift;
 1414         refreshheight -= shift;
 1415     }
 1416     if (refreshheight > 0) {
 1417         ClearCurBackground(xw,
 1418                    refreshtop,
 1419                    left,
 1420                    (unsigned) refreshheight,
 1421                    (unsigned) (right + 1 - left),
 1422                    (unsigned) FontWidth(screen));
 1423     }
 1424     }
 1425 }
 1426 
 1427 /*
 1428  * Insert n blanks at the cursor's position, no wraparound
 1429  */
 1430 void
 1431 InsertChar(XtermWidget xw, unsigned n)
 1432 {
 1433     TScreen *screen = TScreenOf(xw);
 1434     CLineData *ld;
 1435     unsigned limit;
 1436     int row = INX2ROW(screen, screen->cur_row);
 1437     int left = ScrnLeftMargin(xw);
 1438     int right = ScrnRightMargin(xw);
 1439 
 1440     if (screen->cursor_state)
 1441     HideCursor(xw);
 1442 
 1443     TRACE(("InsertChar count=%d\n", n));
 1444 
 1445     if (ScrnHaveSelection(screen)
 1446     && ScrnIsRowInSelection(screen, row)) {
 1447     ScrnDisownSelection(xw);
 1448     }
 1449     ResetWrap(screen);
 1450 
 1451     limit = (unsigned) (right + 1 - screen->cur_col);
 1452 
 1453     if (n > limit)
 1454     n = limit;
 1455 
 1456     if (screen->cur_col < left || screen->cur_col > right) {
 1457     n = 0;
 1458     } else if (AddToVisible(xw)
 1459            && (ld = getLineData(screen, screen->cur_row)) != 0) {
 1460     int col = right + 1 - (int) n;
 1461 
 1462     /*
 1463      * If we shift part of a multi-column character, fill the rest
 1464      * of it with blanks.  Do similar repair for the text which will
 1465      * be shifted into the right-margin.
 1466      */
 1467     if_OPT_WIDE_CHARS(screen, {
 1468         int kl;
 1469         int kr = screen->cur_col;
 1470         if (DamagedCurCells(screen, n, &kl, (int *) 0) && kr > kl) {
 1471         ClearInLine(xw, screen->cur_row, kl, (unsigned) (kr - kl + 1));
 1472         }
 1473         kr = screen->max_col - (int) n + 1;
 1474         if (DamagedCells(screen, n, &kl, (int *) 0,
 1475                  screen->cur_row,
 1476                  kr) && kr > kl) {
 1477         ClearInLine(xw, screen->cur_row, kl, (unsigned) (kr - kl + 1));
 1478         }
 1479     });
 1480 
 1481 #if OPT_DEC_CHRSET
 1482     if (CSET_DOUBLE(GetLineDblCS(ld))) {
 1483         col = MaxCols(screen) / 2 - (int) n;
 1484     }
 1485 #endif
 1486     /*
 1487      * prevent InsertChar from shifting the end of a line over
 1488      * if it is being appended to
 1489      */
 1490     if (non_blank_line(screen, screen->cur_row,
 1491                screen->cur_col, MaxCols(screen))) {
 1492         horizontal_copy_area(xw, screen->cur_col,
 1493                  col - screen->cur_col,
 1494                  (int) n);
 1495     }
 1496 
 1497     ClearCurBackground(xw,
 1498                INX2ROW(screen, screen->cur_row),
 1499                screen->cur_col,
 1500                1U,
 1501                n,
 1502                (unsigned) LineFontWidth(screen, ld));
 1503     }
 1504     if (n != 0) {
 1505     /* adjust screen->buf */
 1506     ScrnInsertChar(xw, n);
 1507     }
 1508 }
 1509 
 1510 /*
 1511  * Deletes n chars at the cursor's position, no wraparound.
 1512  */
 1513 void
 1514 DeleteChar(XtermWidget xw, unsigned n)
 1515 {
 1516     TScreen *screen = TScreenOf(xw);
 1517     CLineData *ld;
 1518     unsigned limit;
 1519     int row = INX2ROW(screen, screen->cur_row);
 1520     int right = ScrnRightMargin(xw);
 1521 
 1522     if (screen->cursor_state)
 1523     HideCursor(xw);
 1524 
 1525     if (!ScrnIsColInMargins(screen, screen->cur_col))
 1526     return;
 1527 
 1528     TRACE(("DeleteChar count=%d\n", n));
 1529 
 1530     if (ScrnHaveSelection(screen)
 1531     && ScrnIsRowInSelection(screen, row)) {
 1532     ScrnDisownSelection(xw);
 1533     }
 1534     ResetWrap(screen);
 1535 
 1536     limit = (unsigned) (right + 1 - screen->cur_col);
 1537 
 1538     if (n > limit)
 1539     n = limit;
 1540 
 1541     if (AddToVisible(xw)
 1542     && (ld = getLineData(screen, screen->cur_row)) != 0) {
 1543     int col = right + 1 - (int) n;
 1544 
 1545     /*
 1546      * If we delete part of a multi-column character, fill the rest
 1547      * of it with blanks.
 1548      */
 1549     if_OPT_WIDE_CHARS(screen, {
 1550         int kl;
 1551         int kr;
 1552         if (DamagedCurCells(screen, n, &kl, &kr))
 1553         ClearInLine(xw, screen->cur_row, kl, (unsigned) (kr - kl + 1));
 1554     });
 1555 
 1556 #if OPT_DEC_CHRSET
 1557     if (CSET_DOUBLE(GetLineDblCS(ld))) {
 1558         col = MaxCols(screen) / 2 - (int) n;
 1559     }
 1560 #endif
 1561     horizontal_copy_area(xw,
 1562                  (screen->cur_col + (int) n),
 1563                  col - screen->cur_col,
 1564                  -((int) n));
 1565 
 1566     ClearCurBackground(xw,
 1567                INX2ROW(screen, screen->cur_row),
 1568                col,
 1569                1U,
 1570                n,
 1571                (unsigned) LineFontWidth(screen, ld));
 1572     }
 1573     if (n != 0) {
 1574     /* adjust screen->buf */
 1575     ScrnDeleteChar(xw, n);
 1576     }
 1577 }
 1578 
 1579 /*
 1580  * Clear from cursor position to beginning of display, inclusive.
 1581  */
 1582 static void
 1583 ClearAbove(XtermWidget xw)
 1584 {
 1585     TScreen *screen = TScreenOf(xw);
 1586 
 1587     if (screen->protected_mode != OFF_PROTECT) {
 1588     int row;
 1589     unsigned len = (unsigned) MaxCols(screen);
 1590 
 1591     assert(screen->max_col >= 0);
 1592     for (row = 0; row < screen->cur_row; row++)
 1593         ClearInLine(xw, row, 0, len);
 1594     ClearInLine(xw, screen->cur_row, 0, (unsigned) screen->cur_col);
 1595     } else {
 1596     int top;
 1597 
 1598     if (screen->cursor_state)
 1599         HideCursor(xw);
 1600     if ((top = INX2ROW(screen, 0)) <= screen->max_row) {
 1601         int height;
 1602 
 1603         if (screen->scroll_amt)
 1604         FlushScroll(xw);
 1605         if ((height = screen->cur_row + top) > screen->max_row)
 1606         height = screen->max_row + 1;
 1607         if ((height -= top) > 0) {
 1608         chararea_clear_displayed_graphics(screen,
 1609                           0,
 1610                           top,
 1611                           MaxCols(screen),
 1612                           height);
 1613 
 1614         ClearCurBackground(xw,
 1615                    top,
 1616                    0,
 1617                    (unsigned) height,
 1618                    (unsigned) MaxCols(screen),
 1619                    (unsigned) FontWidth(screen));
 1620         }
 1621     }
 1622     ClearBufRows(xw, 0, screen->cur_row - 1);
 1623     }
 1624 
 1625     ClearLeft(xw);
 1626 }
 1627 
 1628 /*
 1629  * Clear from cursor position to end of display, inclusive.
 1630  */
 1631 static void
 1632 ClearBelow(XtermWidget xw)
 1633 {
 1634     TScreen *screen = TScreenOf(xw);
 1635 
 1636     ClearRight(xw, -1);
 1637 
 1638     if (screen->protected_mode != OFF_PROTECT) {
 1639     int row;
 1640     unsigned len = (unsigned) MaxCols(screen);
 1641 
 1642     assert(screen->max_col >= 0);
 1643     for (row = screen->cur_row + 1; row <= screen->max_row; row++)
 1644         ClearInLine(xw, row, 0, len);
 1645     } else {
 1646     int top;
 1647 
 1648     if ((top = INX2ROW(screen, screen->cur_row)) <= screen->max_row) {
 1649         if (screen->scroll_amt)
 1650         FlushScroll(xw);
 1651         if (++top <= screen->max_row) {
 1652         chararea_clear_displayed_graphics(screen,
 1653                           0,
 1654                           top,
 1655                           MaxCols(screen),
 1656                           (screen->max_row - top + 1));
 1657         ClearCurBackground(xw,
 1658                    top,
 1659                    0,
 1660                    (unsigned) (screen->max_row - top + 1),
 1661                    (unsigned) MaxCols(screen),
 1662                    (unsigned) FontWidth(screen));
 1663         }
 1664     }
 1665     ClearBufRows(xw, screen->cur_row + 1, screen->max_row);
 1666     }
 1667 }
 1668 
 1669 /*
 1670  * Clear the given row, for the given range of columns, returning 1 if no
 1671  * protected characters were found, 0 otherwise.
 1672  */
 1673 static int
 1674 ClearInLine2(XtermWidget xw, int flags, int row, int col, unsigned len)
 1675 {
 1676     TScreen *screen = TScreenOf(xw);
 1677     CLineData *ld;
 1678     int rc = 1;
 1679 
 1680     TRACE(("ClearInLine(row=%d, col=%d, len=%d) vs %d..%d\n",
 1681        row, col, len,
 1682        screen->startH.row,
 1683        screen->startH.col));
 1684 
 1685     if (ScrnHaveSelection(screen)
 1686     && ScrnIsRowInSelection(screen, row)) {
 1687     ScrnDisownSelection(xw);
 1688     }
 1689 
 1690     if (col + (int) len >= MaxCols(screen)) {
 1691     len = (unsigned) (MaxCols(screen) - col);
 1692     }
 1693 
 1694     /* If we've marked protected text on the screen, we'll have to
 1695      * check each time we do an erase.
 1696      */
 1697     if (screen->protected_mode != OFF_PROTECT) {
 1698     unsigned n;
 1699     IAttr *attrs = getLineData(screen, row)->attribs + col;
 1700     int saved_mode = screen->protected_mode;
 1701     Bool done;
 1702 
 1703     /* disable this branch during recursion */
 1704     screen->protected_mode = OFF_PROTECT;
 1705 
 1706     do {
 1707         done = True;
 1708         for (n = 0; n < len; n++) {
 1709         if (attrs[n] & PROTECTED) {
 1710             rc = 0; /* found a protected segment */
 1711             if (n != 0) {
 1712             ClearInLine(xw, row, col, n);
 1713             }
 1714             while ((n < len)
 1715                && (attrs[n] & PROTECTED)) {
 1716             n++;
 1717             }
 1718             done = False;
 1719             break;
 1720         }
 1721         }
 1722         /* setup for another segment, past the protected text */
 1723         if (!done) {
 1724         attrs += n;
 1725         col += (int) n;
 1726         len -= n;
 1727         }
 1728     } while (!done);
 1729 
 1730     screen->protected_mode = saved_mode;
 1731     if ((int) len <= 0) {
 1732         return 0;
 1733     }
 1734     }
 1735     /* fall through to the final non-protected segment */
 1736 
 1737     if (screen->cursor_state)
 1738     HideCursor(xw);
 1739     ResetWrap(screen);
 1740 
 1741     if (AddToVisible(xw)
 1742     && (ld = getLineData(screen, row)) != 0) {
 1743 
 1744     ClearCurBackground(xw,
 1745                INX2ROW(screen, row),
 1746                col,
 1747                1U,
 1748                len,
 1749                (unsigned) LineFontWidth(screen, ld));
 1750     }
 1751 
 1752     if (len != 0) {
 1753     ClearCells(xw, flags, len, row, col);
 1754     }
 1755 
 1756     return rc;
 1757 }
 1758 
 1759 int
 1760 ClearInLine(XtermWidget xw, int row, int col, unsigned len)
 1761 {
 1762     TScreen *screen = TScreenOf(xw);
 1763     int flags = 0;
 1764 
 1765     /*
 1766      * If we're clearing to the end of the line, we won't count this as
 1767      * "drawn" characters.  We'll only do cut/paste on "drawn" characters,
 1768      * so this has the effect of suppressing trailing blanks from a
 1769      * selection.
 1770      */
 1771     if (col + (int) len < MaxCols(screen)) {
 1772     flags |= CHARDRAWN;
 1773     }
 1774     return ClearInLine2(xw, flags, row, col, len);
 1775 }
 1776 
 1777 /*
 1778  * Clear the next n characters on the cursor's line, including the cursor's
 1779  * position.
 1780  */
 1781 void
 1782 ClearRight(XtermWidget xw, int n)
 1783 {
 1784     TScreen *screen = TScreenOf(xw);
 1785     LineData *ld;
 1786     unsigned len = (unsigned) (MaxCols(screen) - screen->cur_col);
 1787 
 1788     assert(screen->max_col >= 0);
 1789     assert(screen->max_col >= screen->cur_col);
 1790 
 1791     if (n < 0)          /* the remainder of the line */
 1792     n = MaxCols(screen);
 1793     if (n == 0)         /* default for 'ECH' */
 1794     n = 1;
 1795 
 1796     if (len > (unsigned) n)
 1797     len = (unsigned) n;
 1798 
 1799     ld = getLineData(screen, screen->cur_row);
 1800     if (AddToVisible(xw)) {
 1801     if_OPT_WIDE_CHARS(screen, {
 1802         int col = screen->cur_col;
 1803         int row = screen->cur_row;
 1804         int kl;
 1805         int kr;
 1806         if (DamagedCurCells(screen, len, &kl, &kr) && kr >= kl) {
 1807         int xx = col;
 1808         if (kl < xx) {
 1809             ClearInLine2(xw, 0, row, kl, (unsigned) (xx - kl));
 1810         }
 1811         xx = col + (int) len - 1;
 1812         if (kr > xx) {
 1813             ClearInLine2(xw, 0, row, xx + 1, (unsigned) (kr - xx));
 1814         }
 1815         }
 1816     });
 1817     (void) ClearInLine(xw, screen->cur_row, screen->cur_col, len);
 1818     } else {
 1819     ScrnClearCells(xw, screen->cur_row, screen->cur_col, len);
 1820     }
 1821 
 1822     /* with the right part cleared, we can't be wrapping */
 1823     LineClrWrapped(ld);
 1824     ShowWrapMarks(xw, screen->cur_row, ld);
 1825     ResetWrap(screen);
 1826 }
 1827 
 1828 /*
 1829  * Clear first part of cursor's line, inclusive.
 1830  */
 1831 static void
 1832 ClearLeft(XtermWidget xw)
 1833 {
 1834     TScreen *screen = TScreenOf(xw);
 1835     unsigned len = (unsigned) screen->cur_col + 1;
 1836 
 1837     assert(screen->cur_col >= 0);
 1838     if (AddToVisible(xw)) {
 1839     if_OPT_WIDE_CHARS(screen, {
 1840         int row = screen->cur_row;
 1841         int kl;
 1842         int kr;
 1843         if (DamagedCurCells(screen, 1, &kl, &kr) && kr >= kl) {
 1844         ClearInLine2(xw, 0, row, kl, (unsigned) (kr - kl + 1));
 1845         }
 1846     });
 1847     (void) ClearInLine(xw, screen->cur_row, 0, len);
 1848     } else {
 1849     ScrnClearCells(xw, screen->cur_row, 0, len);
 1850     }
 1851 }
 1852 
 1853 /*
 1854  * Erase the cursor's line.
 1855  */
 1856 void
 1857 ClearLine(XtermWidget xw)
 1858 {
 1859     TScreen *screen = TScreenOf(xw);
 1860     unsigned len = (unsigned) MaxCols(screen);
 1861 
 1862     assert(screen->max_col >= 0);
 1863     (void) ClearInLine(xw, screen->cur_row, 0, len);
 1864 }
 1865 
 1866 void
 1867 ClearScreen(XtermWidget xw)
 1868 {
 1869     TScreen *screen = TScreenOf(xw);
 1870     int top;
 1871 
 1872     TRACE(("ClearScreen\n"));
 1873 
 1874     if (screen->cursor_state)
 1875     HideCursor(xw);
 1876 
 1877     ScrnDisownSelection(xw);
 1878     ResetWrap(screen);
 1879     if ((top = INX2ROW(screen, 0)) <= screen->max_row) {
 1880     if (screen->scroll_amt)
 1881         FlushScroll(xw);
 1882     chararea_clear_displayed_graphics(screen,
 1883                       0,
 1884                       top,
 1885                       MaxCols(screen),
 1886                       (screen->max_row - top + 1));
 1887     ClearCurBackground(xw,
 1888                top,
 1889                0,
 1890                (unsigned) (screen->max_row - top + 1),
 1891                (unsigned) MaxCols(screen),
 1892                (unsigned) FontWidth(screen));
 1893     }
 1894     ClearBufRows(xw, 0, screen->max_row);
 1895 }
 1896 
 1897 /*
 1898  * If we've written protected text DEC-style, and are issuing a non-DEC
 1899  * erase, temporarily reset the protected_mode flag so that the erase will
 1900  * ignore the protected flags.
 1901  */
 1902 void
 1903 do_erase_char(XtermWidget xw, int param, int mode)
 1904 {
 1905     TScreen *screen = TScreenOf(xw);
 1906     int saved_mode = screen->protected_mode;
 1907 
 1908     if (saved_mode == DEC_PROTECT
 1909     && saved_mode != mode) {
 1910     screen->protected_mode = OFF_PROTECT;
 1911     }
 1912 
 1913     ClearRight(xw, param);
 1914     screen->protected_mode = saved_mode;
 1915 }
 1916 
 1917 void
 1918 do_erase_line(XtermWidget xw, int param, int mode)
 1919 {
 1920     TScreen *screen = TScreenOf(xw);
 1921     int saved_mode = screen->protected_mode;
 1922 
 1923     if (saved_mode == DEC_PROTECT
 1924     && saved_mode != mode) {
 1925     screen->protected_mode = OFF_PROTECT;
 1926     }
 1927 
 1928     switch (param) {
 1929     case -1:            /* DEFAULT */
 1930     case 0:
 1931     ClearRight(xw, -1);
 1932     break;
 1933     case 1:
 1934     ClearLeft(xw);
 1935     break;
 1936     case 2:
 1937     ClearLine(xw);
 1938     break;
 1939     }
 1940     screen->protected_mode = saved_mode;
 1941 }
 1942 
 1943 /*
 1944  * Just like 'do_erase_line()', except that this intercepts ED controls.  If we
 1945  * clear the whole screen, we'll get the return-value from ClearInLine, and
 1946  * find if there were any protected characters left.  If not, reset the
 1947  * protected mode flag in the screen data (it's slower).
 1948  */
 1949 void
 1950 do_erase_display(XtermWidget xw, int param, int mode)
 1951 {
 1952     TScreen *screen = TScreenOf(xw);
 1953     int saved_mode = screen->protected_mode;
 1954 
 1955     if (saved_mode == DEC_PROTECT
 1956     && saved_mode != mode)
 1957     screen->protected_mode = OFF_PROTECT;
 1958 
 1959     switch (param) {
 1960     case -1:            /* DEFAULT */
 1961     case 0:
 1962     if (screen->cur_row == 0
 1963         && screen->cur_col == 0) {
 1964         screen->protected_mode = saved_mode;
 1965         do_erase_display(xw, 2, mode);
 1966         saved_mode = screen->protected_mode;
 1967     } else
 1968         ClearBelow(xw);
 1969     break;
 1970 
 1971     case 1:
 1972     if (screen->cur_row == screen->max_row
 1973         && screen->cur_col == screen->max_col) {
 1974         screen->protected_mode = saved_mode;
 1975         do_erase_display(xw, 2, mode);
 1976         saved_mode = screen->protected_mode;
 1977     } else
 1978         ClearAbove(xw);
 1979     break;
 1980 
 1981     case 2:
 1982     /*
 1983      * We use 'ClearScreen()' throughout the remainder of the
 1984      * program for places where we don't care if the characters are
 1985      * protected or not.  So we modify the logic around this call
 1986      * on 'ClearScreen()' to handle protected characters.
 1987      */
 1988     if (screen->protected_mode != OFF_PROTECT) {
 1989         int row;
 1990         int rc = 1;
 1991         unsigned len = (unsigned) MaxCols(screen);
 1992 
 1993         assert(screen->max_col >= 0);
 1994         for (row = 0; row <= screen->max_row; row++)
 1995         rc &= ClearInLine(xw, row, 0, len);
 1996         if (rc != 0)
 1997         saved_mode = OFF_PROTECT;
 1998     } else {
 1999         ClearScreen(xw);
 2000     }
 2001     break;
 2002 
 2003     case 3:
 2004     /* xterm addition - erase saved lines. */
 2005     if (screen->eraseSavedLines) {
 2006         screen->savedlines = 0;
 2007         ScrollBarDrawThumb(xw, 1);
 2008     }
 2009     break;
 2010     }
 2011     screen->protected_mode = saved_mode;
 2012 }
 2013 
 2014 static Boolean
 2015 row_has_data(TScreen *screen, int row)
 2016 {
 2017     Boolean result = False;
 2018     CLineData *ld;
 2019 
 2020     if ((ld = getLineData(screen, row)) != 0) {
 2021     int col;
 2022 
 2023     for (col = 0; col < screen->max_col; ++col) {
 2024         if (ld->attribs[col] & CHARDRAWN && ld->charData[col] != ' ') {
 2025         result = True;
 2026         break;
 2027         }
 2028     }
 2029     }
 2030     return result;
 2031 }
 2032 
 2033 static Boolean
 2034 screen_has_data(XtermWidget xw)
 2035 {
 2036     TScreen *screen = TScreenOf(xw);
 2037     Boolean result = False;
 2038     int row;
 2039 
 2040     for (row = 0; row < screen->max_row; ++row) {
 2041     if (row_has_data(screen, row)) {
 2042         result = True;
 2043         break;
 2044     }
 2045     }
 2046     return result;
 2047 }
 2048 
 2049 static void
 2050 do_extra_scroll(XtermWidget xw, Bool trimmed)
 2051 {
 2052     TScreen *screen = TScreenOf(xw);
 2053 
 2054     if (screen_has_data(xw)) {
 2055     TRACE(("do_extra_scroll buffer=%d, trimmed=%s\n", screen->whichBuf,
 2056            BtoS(trimmed)));
 2057     if (trimmed) {
 2058         int row;
 2059         Boolean hadData = (Boolean) ((screen->saved_fifo > 0)
 2060                      ? row_has_data(screen, -1)
 2061                      : False);
 2062 
 2063         for (row = 0; row < screen->max_row; ++row) {
 2064         Boolean hasData = row_has_data(screen, row);
 2065         if (hasData || hadData) {
 2066             LineData *dst = addScrollback(screen);
 2067             LineData *src = getLineData(screen, row);
 2068             copyLineData(dst, src);
 2069             IncrementSavedLines(1);
 2070         }
 2071         hadData = hasData;
 2072         }
 2073     } else {
 2074         xtermScroll(xw, screen->max_row);
 2075         FlushScroll(xw);
 2076     }
 2077     xtermRepaint(xw);
 2078     }
 2079 }
 2080 
 2081 /*
 2082  * Like tiXtraScroll, perform a scroll up of the page contents.
 2083  *
 2084  * In this case, it happens for the special case when erasing the whole
 2085  * display, e.g., an erase-below starting from the upper-left corner of the
 2086  * screen, or if the erasure applies to the whole screen.
 2087  */
 2088 void
 2089 do_cd_xtra_scroll(XtermWidget xw, int param)
 2090 {
 2091     TScreen *screen = TScreenOf(xw);
 2092 
 2093     TRACE(("do_cd_xtra_scroll param %d, @%d,%d vs %d,%d\n", param,
 2094        screen->cur_row,
 2095        screen->cur_col,
 2096        ScrnTopMargin(xw),
 2097        ScrnLeftMargin(xw)));
 2098     if (xw->misc.cdXtraScroll
 2099     && (param == 2 ||
 2100         (param == 0
 2101          && screen->cur_col <= ScrnLeftMargin(xw)
 2102          && screen->cur_row <= ScrnTopMargin(xw)))) {
 2103     do_extra_scroll(xw, (xw->misc.cdXtraScroll == edTrim));
 2104     }
 2105 }
 2106 
 2107 /*
 2108  * Scroll the page up (saving it).  This is called when doing terminal
 2109  * initialization (ti) or exiting from that (te).
 2110  */
 2111 void
 2112 do_ti_xtra_scroll(XtermWidget xw)
 2113 {
 2114     if (xw->misc.tiXtraScroll) {
 2115     do_extra_scroll(xw, False);
 2116     }
 2117 }
 2118 
 2119 static void
 2120 CopyWait(XtermWidget xw)
 2121 {
 2122     TScreen *screen = TScreenOf(xw);
 2123     XEvent reply;
 2124     XEvent *rep = &reply;
 2125 #ifndef NO_ACTIVE_ICON
 2126     int retries = 0;
 2127 #endif
 2128 
 2129 #if USE_DOUBLE_BUFFER
 2130     if (resource.buffered)
 2131     return;
 2132 #endif
 2133 
 2134     for (;;) {
 2135 #ifndef NO_ACTIVE_ICON
 2136     if (xw->work.active_icon != eiFalse) {
 2137         /*
 2138          * The XWindowEvent call blocks until an event is available.  That
 2139          * can hang when using active-icon and iconifying/deiconifying
 2140          * while the terminal is receiving lots of output.  Checking with
 2141          * this call on the other hand may lose exposure events which
 2142          * arrive too late.  As a compromise, try several times with a
 2143          * time-delay before assuming no more events are available.
 2144          */
 2145         if (XCheckWindowEvent(screen->display,
 2146                   VWindow(screen),
 2147                   ExposureMask,
 2148                   &reply)) {
 2149         retries = 0;
 2150         } else {
 2151         if (++retries >= 1000)
 2152             return;
 2153         usleep(100U);   /* wait 0.1msec */
 2154         continue;
 2155         }
 2156     } else
 2157 #endif
 2158         XWindowEvent(screen->display, VWindow(screen), ExposureMask, &reply);
 2159     switch (reply.type) {
 2160     case Expose:
 2161         HandleExposure(xw, &reply);
 2162         break;
 2163     case NoExpose:
 2164     case GraphicsExpose:
 2165         if (screen->incopy <= 0) {
 2166         screen->incopy = 1;
 2167         if (screen->scrolls > 0)
 2168             screen->scrolls--;
 2169         }
 2170         if (reply.type == GraphicsExpose)
 2171         HandleExposure(xw, &reply);
 2172 
 2173         if ((reply.type == NoExpose) ||
 2174         ((XExposeEvent *) rep)->count == 0) {
 2175         if (screen->incopy <= 0 && screen->scrolls > 0)
 2176             screen->scrolls--;
 2177         if (screen->scrolls == 0) {
 2178             screen->incopy = 0;
 2179             return;
 2180         }
 2181         screen->incopy = -1;
 2182         }
 2183         break;
 2184     }
 2185     }
 2186 }
 2187 
 2188 /*
 2189  * used by vertical_copy_area and and horizontal_copy_area
 2190  */
 2191 static void
 2192 copy_area(XtermWidget xw,
 2193       int src_x,
 2194       int src_y,
 2195       unsigned width,
 2196       unsigned height,
 2197       int dest_x,
 2198       int dest_y)
 2199 {
 2200     TScreen *screen = TScreenOf(xw);
 2201 
 2202     if (width != 0 && height != 0) {
 2203     /* wait for previous CopyArea to complete unless
 2204        multiscroll is enabled and active */
 2205     if (screen->incopy && screen->scrolls == 0)
 2206         CopyWait(xw);
 2207     screen->incopy = -1;
 2208 
 2209     /* save for translating Expose events */
 2210     screen->copy_src_x = src_x;
 2211     screen->copy_src_y = src_y;
 2212     screen->copy_width = width;
 2213     screen->copy_height = height;
 2214     screen->copy_dest_x = dest_x;
 2215     screen->copy_dest_y = dest_y;
 2216 
 2217     XCopyArea(screen->display,
 2218           VDrawable(screen), VDrawable(screen),
 2219           NormalGC(xw, screen),
 2220           src_x, src_y, width, height, dest_x, dest_y);
 2221     }
 2222 }
 2223 
 2224 /*
 2225  * use when inserting or deleting characters on the current line
 2226  */
 2227 static void
 2228 horizontal_copy_area(XtermWidget xw,
 2229              int firstchar, /* char pos on screen to start copying at */
 2230              int nchars,
 2231              int amount)    /* number of characters to move right */
 2232 {
 2233     TScreen *screen = TScreenOf(xw);
 2234     CLineData *ld;
 2235 
 2236     if ((ld = getLineData(screen, screen->cur_row)) != 0) {
 2237     int src_x = LineCursorX(screen, ld, firstchar);
 2238     int src_y = CursorY(screen, screen->cur_row);
 2239 
 2240     copy_area(xw, src_x, src_y,
 2241           (unsigned) (nchars * LineFontWidth(screen, ld)),
 2242           (unsigned) FontHeight(screen),
 2243           src_x + amount * LineFontWidth(screen, ld), src_y);
 2244     }
 2245 }
 2246 
 2247 /*
 2248  * use when inserting or deleting lines from the screen
 2249  */
 2250 static void
 2251 vertical_copy_area(XtermWidget xw,
 2252            int firstline,   /* line on screen to start copying at */
 2253            int nlines,
 2254            int amount,  /* number of lines to move up (neg=down) */
 2255            int left,
 2256            int right)
 2257 {
 2258     TScreen *screen = TScreenOf(xw);
 2259 
 2260     TRACE(("vertical_copy_area - firstline=%d nlines=%d left=%d right=%d amount=%d\n",
 2261        firstline, nlines, left, right, amount));
 2262 
 2263     if (nlines > 0) {
 2264     int src_x = CursorX(screen, left);
 2265     int src_y = firstline * FontHeight(screen) + screen->border;
 2266     unsigned int w = (unsigned) ((right + 1 - left) * FontWidth(screen));
 2267     unsigned int h = (unsigned) (nlines * FontHeight(screen));
 2268     int dst_x = src_x;
 2269     int dst_y = src_y - amount * FontHeight(screen);
 2270 
 2271     copy_area(xw, src_x, src_y, w, h, dst_x, dst_y);
 2272 
 2273     if (screen->show_wrap_marks) {
 2274         int row;
 2275         int first = firstline - amount;
 2276         int last = firstline + nlines + amount;
 2277 
 2278         for (row = first; row < last; ++row) {
 2279         CLineData *ld;
 2280         int mapped = amount + row + screen->topline;
 2281 
 2282         if ((ld = getLineData(screen, mapped)) != 0) {
 2283             ShowWrapMarks(xw, row, ld);
 2284         }
 2285         }
 2286     }
 2287     }
 2288 }
 2289 
 2290 /*
 2291  * use when scrolling the entire screen
 2292  */
 2293 void
 2294 scrolling_copy_area(XtermWidget xw,
 2295             int firstline,  /* line on screen to start copying at */
 2296             int nlines,
 2297             int amount) /* number of lines to move up (neg=down) */
 2298 {
 2299 
 2300     if (nlines > 0) {
 2301     vertical_copy_area(xw, firstline, nlines, amount, 0, TScreenOf(xw)->max_col);
 2302     }
 2303 }
 2304 
 2305 /*
 2306  * Handler for Expose events on the VT widget.
 2307  * Returns 1 iff the area where the cursor was got refreshed.
 2308  */
 2309 int
 2310 HandleExposure(XtermWidget xw, XEvent *event)
 2311 {
 2312     TScreen *screen = TScreenOf(xw);
 2313     XExposeEvent *reply = (XExposeEvent *) event;
 2314 
 2315 #ifndef NO_ACTIVE_ICON
 2316     if (reply->window == screen->iconVwin.window) {
 2317     WhichVWin(screen) = &screen->iconVwin;
 2318     TRACE(("HandleExposure - icon\n"));
 2319     } else {
 2320     WhichVWin(screen) = &screen->fullVwin;
 2321     TRACE(("HandleExposure - normal\n"));
 2322     }
 2323     TRACE((" event %d,%d %dx%d\n",
 2324        reply->y,
 2325        reply->x,
 2326        reply->height,
 2327        reply->width));
 2328 #endif /* NO_ACTIVE_ICON */
 2329 
 2330     /* if not doing CopyArea or if this is a GraphicsExpose, don't translate */
 2331     if (!screen->incopy || event->type != Expose) {
 2332     return handle_translated_exposure(xw, reply->x, reply->y,
 2333                       reply->width,
 2334                       reply->height);
 2335     } else {
 2336     /* compute intersection of area being copied with
 2337        area being exposed. */
 2338     int both_x1 = Max(screen->copy_src_x, reply->x);
 2339     int both_y1 = Max(screen->copy_src_y, reply->y);
 2340     int both_x2 = Min(screen->copy_src_x + (int) screen->copy_width,
 2341               (reply->x + (int) reply->width));
 2342     int both_y2 = Min(screen->copy_src_y + (int) screen->copy_height,
 2343               (reply->y + (int) reply->height));
 2344     int value = 0;
 2345 
 2346     /* was anything copied affected? */
 2347     if (both_x2 > both_x1 && both_y2 > both_y1) {
 2348         /* do the copied area */
 2349         value = handle_translated_exposure
 2350         (xw, reply->x + screen->copy_dest_x - screen->copy_src_x,
 2351          reply->y + screen->copy_dest_y - screen->copy_src_y,
 2352          reply->width, reply->height);
 2353     }
 2354     /* was anything not copied affected? */
 2355     if (reply->x < both_x1 || reply->y < both_y1
 2356         || reply->x + reply->width > both_x2
 2357         || reply->y + reply->height > both_y2)
 2358         value = handle_translated_exposure(xw, reply->x, reply->y,
 2359                            reply->width, reply->height);
 2360 
 2361     return value;
 2362     }
 2363 }
 2364 
 2365 static void
 2366 set_background(XtermWidget xw, int color)
 2367 {
 2368     TScreen *screen = TScreenOf(xw);
 2369     Pixel c = getXtermBG(xw, xw->flags, color);
 2370 
 2371 #if OPT_WIDE_ATTRS
 2372     TRACE(("set_background(%d) %#lx %s\n", color, c,
 2373        ((xw->flags & ATR_DIRECT_BG)
 2374         ? "direct"
 2375         : "indexed")));
 2376 #else
 2377     TRACE(("set_background(%d) %#lx\n", color, c));
 2378 #endif
 2379     XSetWindowBackground(screen->display, VShellWindow(xw), c);
 2380     XSetWindowBackground(screen->display, VWindow(screen), c);
 2381     initBorderGC(xw, WhichVWin(screen));
 2382 }
 2383 
 2384 void
 2385 xtermClear2(XtermWidget xw, int x, int y, unsigned width, unsigned height)
 2386 {
 2387     TScreen *screen = TScreenOf(xw);
 2388     VTwin *vwin = WhichVWin(screen);
 2389     Drawable draw = VDrawable(screen);
 2390     GC gc;
 2391 
 2392     if ((gc = vwin->border_gc) != 0) {
 2393     int vmark1 = screen->border;
 2394     int vmark2 = vwin->height + vmark1;
 2395     int hmark1 = OriginX(screen);
 2396     int hmark2 = vwin->width + hmark1;
 2397     if (y < vmark1) {
 2398         int yy = y + (int) height;
 2399         int h1 = (yy <= vmark1) ? (yy - y) : (vmark1 - y);
 2400         XFillRectangle(screen->display, draw, gc,
 2401                x, y, width, (unsigned) h1);
 2402         if (yy > vmark1) {
 2403         xtermClear2(xw, x, vmark1, width, (unsigned) (yy - vmark1));
 2404         }
 2405     } else if (y < vmark2) {
 2406         int yy = y + (int) height;
 2407         int h2 = (yy <= vmark2) ? (yy - y) : (vmark2 - y);
 2408         int xb = x;
 2409         int xx = x + (int) width;
 2410         int ww = (int) width;
 2411         if (x < hmark1) {
 2412         int w1 = (xx <= hmark1) ? (xx - x) : (hmark1 - x);
 2413         XFillRectangle(screen->display, draw, gc,
 2414                    x, y, (unsigned) w1, (unsigned) h2);
 2415         x += w1;
 2416         ww -= w1;
 2417         }
 2418         if ((ww > 0) && (x < hmark2)) {
 2419         int w2 = (xx <= hmark2) ? (xx - x) : (hmark2 - x);
 2420 #if USE_DOUBLE_BUFFER
 2421         if (resource.buffered) {
 2422             XFillRectangle(screen->display, draw,
 2423                    FillerGC(xw, screen),
 2424                    x, y, (unsigned) w2, (unsigned) h2);
 2425         } else
 2426 #endif
 2427             XClearArea(screen->display, VWindow(screen),
 2428                    x, y, (unsigned) w2, (unsigned) h2, False);
 2429         x += w2;
 2430         ww -= w2;
 2431         }
 2432         if (ww > 0) {
 2433         XFillRectangle(screen->display, draw, gc,
 2434                    x, y, (unsigned) ww, (unsigned) h2);
 2435         }
 2436         if (yy > vmark2) {
 2437         xtermClear2(xw, xb, vmark2, width, (unsigned) (yy - vmark2));
 2438         }
 2439     } else {
 2440         XFillRectangle(screen->display, draw, gc, x, y, width, height);
 2441     }
 2442     } else {
 2443 #if USE_DOUBLE_BUFFER
 2444     if (resource.buffered) {
 2445         gc = FillerGC(xw, screen);
 2446         XFillRectangle(screen->display, draw, gc,
 2447                x, y, width, height);
 2448     } else
 2449 #endif
 2450         XClearArea(screen->display,
 2451                VWindow(screen),
 2452                x, y, width, height, False);
 2453     }
 2454 }
 2455 
 2456 /*
 2457  * Called by the ExposeHandler to do the actual repaint after the coordinates
 2458  * have been translated to allow for any CopyArea in progress.
 2459  * The rectangle passed in is pixel coordinates.
 2460  */
 2461 static int
 2462 handle_translated_exposure(XtermWidget xw,
 2463                int rect_x,
 2464                int rect_y,
 2465                int rect_width,
 2466                int rect_height)
 2467 {
 2468     TScreen *screen = TScreenOf(xw);
 2469     int toprow, leftcol, nrows, ncols;
 2470     int x0, x1;
 2471     int y0, y1;
 2472     int result = 0;
 2473 
 2474     TRACE(("handle_translated_exposure at %d,%d size %dx%d\n",
 2475        rect_y, rect_x, rect_height, rect_width));
 2476 
 2477     x0 = (rect_x - OriginX(screen));
 2478     x1 = (x0 + rect_width);
 2479 
 2480     y0 = (rect_y - OriginY(screen));
 2481     y1 = (y0 + rect_height);
 2482 
 2483     if (x0 < 0 ||
 2484     y0 < 0 ||
 2485     x1 > Width(screen) ||
 2486     y1 > PlusStatusLine(screen, Height(screen))) {
 2487     set_background(xw, -1);
 2488     xtermClear2(xw,
 2489             rect_x,
 2490             rect_y,
 2491             (unsigned) rect_width,
 2492             (unsigned) rect_height);
 2493     }
 2494     toprow = y0 / FontHeight(screen);
 2495     if (toprow < 0)
 2496     toprow = 0;
 2497 
 2498     leftcol = x0 / FontWidth(screen);
 2499     if (leftcol < 0)
 2500     leftcol = 0;
 2501 
 2502     nrows = (y1 - 1) / FontHeight(screen) - toprow + 1;
 2503     ncols = (x1 - 1) / FontWidth(screen) - leftcol + 1;
 2504     toprow -= screen->scrolls;
 2505     if (toprow < 0) {
 2506     nrows += toprow;
 2507     toprow = 0;
 2508     }
 2509     if (toprow + nrows > PlusStatusLine(screen, MaxRows(screen)))
 2510     nrows = PlusStatusLine(screen, MaxRows(screen)) - toprow;
 2511     if (leftcol + ncols > MaxCols(screen))
 2512     ncols = MaxCols(screen) - leftcol;
 2513 
 2514     if (nrows > 0 && ncols > 0) {
 2515     ScrnRefresh(xw, toprow, leftcol, nrows, ncols, True);
 2516     first_map_occurred();
 2517     if (screen->cur_row >= toprow &&
 2518         screen->cur_row < toprow + nrows &&
 2519         screen->cur_col >= leftcol &&
 2520         screen->cur_col < leftcol + ncols) {
 2521         result = 1;
 2522     }
 2523 
 2524     }
 2525     TRACE(("...handle_translated_exposure %d\n", result));
 2526     return (result);
 2527 }
 2528 
 2529 /***====================================================================***/
 2530 
 2531 void
 2532 GetColors(XtermWidget xw, ScrnColors * pColors)
 2533 {
 2534     TScreen *screen = TScreenOf(xw);
 2535     int n;
 2536 
 2537     pColors->which = 0;
 2538     for (n = 0; n < NCOLORS; ++n) {
 2539     SET_COLOR_VALUE(pColors, n, T_COLOR(screen, n));
 2540     }
 2541 }
 2542 
 2543 void
 2544 ChangeColors(XtermWidget xw, ScrnColors * pNew)
 2545 {
 2546     Bool repaint = False;
 2547     TScreen *screen = TScreenOf(xw);
 2548     VTwin *win = WhichVWin(screen);
 2549 
 2550     TRACE(("ChangeColors\n"));
 2551 
 2552     if (COLOR_DEFINED(pNew, TEXT_CURSOR)) {
 2553     T_COLOR(screen, TEXT_CURSOR) = COLOR_VALUE(pNew, TEXT_CURSOR);
 2554     TRACE(("... TEXT_CURSOR: %#lx\n", T_COLOR(screen, TEXT_CURSOR)));
 2555     FreeMarkGCs(xw);
 2556     /* no repaint needed */
 2557     } else if ((T_COLOR(screen, TEXT_CURSOR) == T_COLOR(screen, TEXT_FG)) &&
 2558            (COLOR_DEFINED(pNew, TEXT_FG))) {
 2559     if (T_COLOR(screen, TEXT_CURSOR) != COLOR_VALUE(pNew, TEXT_FG)) {
 2560         T_COLOR(screen, TEXT_CURSOR) = COLOR_VALUE(pNew, TEXT_FG);
 2561         TRACE(("... TEXT_CURSOR: %#lx\n", T_COLOR(screen, TEXT_CURSOR)));
 2562         if (screen->Vshow)
 2563         repaint = True;
 2564     }
 2565     FreeMarkGCs(xw);
 2566     }
 2567 
 2568     if (COLOR_DEFINED(pNew, TEXT_FG)) {
 2569     Pixel fg = COLOR_VALUE(pNew, TEXT_FG);
 2570     T_COLOR(screen, TEXT_FG) = fg;
 2571     TRACE(("... TEXT_FG: %#lx\n", T_COLOR(screen, TEXT_FG)));
 2572     if (screen->Vshow) {
 2573         setCgsFore(xw, win, gcNorm, fg);
 2574         setCgsBack(xw, win, gcNormReverse, fg);
 2575         setCgsFore(xw, win, gcBold, fg);
 2576         setCgsBack(xw, win, gcBoldReverse, fg);
 2577         repaint = True;
 2578     }
 2579     FreeMarkGCs(xw);
 2580     }
 2581 
 2582     if (COLOR_DEFINED(pNew, TEXT_BG)) {
 2583     Pixel bg = COLOR_VALUE(pNew, TEXT_BG);
 2584     T_COLOR(screen, TEXT_BG) = bg;
 2585     TRACE(("... TEXT_BG: %#lx\n", T_COLOR(screen, TEXT_BG)));
 2586     if (screen->Vshow) {
 2587         setCgsBack(xw, win, gcNorm, bg);
 2588         setCgsFore(xw, win, gcNormReverse, bg);
 2589         setCgsBack(xw, win, gcBold, bg);
 2590         setCgsFore(xw, win, gcBoldReverse, bg);
 2591         set_background(xw, -1);
 2592         repaint = True;
 2593     }
 2594     }
 2595 #if OPT_HIGHLIGHT_COLOR
 2596     if (COLOR_DEFINED(pNew, HIGHLIGHT_BG)) {
 2597     if (T_COLOR(screen, HIGHLIGHT_BG) != COLOR_VALUE(pNew, HIGHLIGHT_BG)) {
 2598         T_COLOR(screen, HIGHLIGHT_BG) = COLOR_VALUE(pNew, HIGHLIGHT_BG);
 2599         TRACE(("... HIGHLIGHT_BG: %#lx\n", T_COLOR(screen, HIGHLIGHT_BG)));
 2600         if (screen->Vshow)
 2601         repaint = True;
 2602     }
 2603     }
 2604     if (COLOR_DEFINED(pNew, HIGHLIGHT_FG)) {
 2605     if (T_COLOR(screen, HIGHLIGHT_FG) != COLOR_VALUE(pNew, HIGHLIGHT_FG)) {
 2606         T_COLOR(screen, HIGHLIGHT_FG) = COLOR_VALUE(pNew, HIGHLIGHT_FG);
 2607         TRACE(("... HIGHLIGHT_FG: %#lx\n", T_COLOR(screen, HIGHLIGHT_FG)));
 2608         if (screen->Vshow)
 2609         repaint = True;
 2610     }
 2611     }
 2612 #endif
 2613 
 2614     if (COLOR_DEFINED(pNew, MOUSE_FG) || (COLOR_DEFINED(pNew, MOUSE_BG))) {
 2615     if (COLOR_DEFINED(pNew, MOUSE_FG)) {
 2616         T_COLOR(screen, MOUSE_FG) = COLOR_VALUE(pNew, MOUSE_FG);
 2617         TRACE(("... MOUSE_FG: %#lx\n", T_COLOR(screen, MOUSE_FG)));
 2618     }
 2619     if (COLOR_DEFINED(pNew, MOUSE_BG)) {
 2620         T_COLOR(screen, MOUSE_BG) = COLOR_VALUE(pNew, MOUSE_BG);
 2621         TRACE(("... MOUSE_BG: %#lx\n", T_COLOR(screen, MOUSE_BG)));
 2622     }
 2623 
 2624     if (screen->Vshow) {
 2625         recolor_cursor(screen,
 2626                screen->pointer_cursor,
 2627                T_COLOR(screen, MOUSE_FG),
 2628                T_COLOR(screen, MOUSE_BG));
 2629         XDefineCursor(screen->display, VWindow(screen),
 2630               screen->pointer_cursor);
 2631     }
 2632 #if OPT_TEK4014
 2633     if (TEK4014_SHOWN(xw)) {
 2634         TekScreen *tekscr = TekScreenOf(tekWidget);
 2635         Window tekwin = TWindow(tekscr);
 2636         if (tekwin) {
 2637         recolor_cursor(screen,
 2638                    tekscr->arrow,
 2639                    T_COLOR(screen, MOUSE_FG),
 2640                    T_COLOR(screen, MOUSE_BG));
 2641         XDefineCursor(screen->display, tekwin, tekscr->arrow);
 2642         }
 2643     }
 2644 #endif
 2645     /* no repaint needed */
 2646     }
 2647 
 2648     if (COLOR_DEFINED(pNew, TEXT_FG) ||
 2649     COLOR_DEFINED(pNew, TEXT_BG) ||
 2650     COLOR_DEFINED(pNew, TEXT_CURSOR)) {
 2651     if (set_cursor_gcs(xw) && screen->Vshow) {
 2652         repaint = True;
 2653     }
 2654     }
 2655 #if OPT_TEK4014
 2656     if (COLOR_DEFINED(pNew, TEK_FG) ||
 2657     COLOR_DEFINED(pNew, TEK_BG)) {
 2658     ChangeTekColors(tekWidget, screen, pNew);
 2659     if (TEK4014_SHOWN(xw)) {
 2660         TekRepaint(tekWidget);
 2661     }
 2662     } else if (COLOR_DEFINED(pNew, TEK_CURSOR)) {
 2663     ChangeTekColors(tekWidget, screen, pNew);
 2664     }
 2665 #endif
 2666     if (repaint)
 2667     xtermRepaint(xw);
 2668 }
 2669 
 2670 void
 2671 xtermClear(XtermWidget xw)
 2672 {
 2673     TScreen *screen = TScreenOf(xw);
 2674 
 2675     TRACE(("xtermClear\n"));
 2676     xtermClear2(xw, 0, 0, FullWidth(screen), FullHeight(screen));
 2677 }
 2678 
 2679 void
 2680 xtermRepaint(XtermWidget xw)
 2681 {
 2682     TScreen *screen = TScreenOf(xw);
 2683 
 2684     TRACE(("xtermRepaint\n"));
 2685     xtermClear(xw);
 2686     ScrnRefresh(xw, 0, 0, LastRowNumber(screen) + 1, MaxCols(screen), True);
 2687 }
 2688 
 2689 /***====================================================================***/
 2690 
 2691 Boolean
 2692 isDefaultForeground(const char *name)
 2693 {
 2694     return (Boolean) !x_strcasecmp(name, XtDefaultForeground);
 2695 }
 2696 
 2697 Boolean
 2698 isDefaultBackground(const char *name)
 2699 {
 2700     return (Boolean) !x_strcasecmp(name, XtDefaultBackground);
 2701 }
 2702 
 2703 /***====================================================================***/
 2704 
 2705 typedef struct {
 2706     Pixel fg;
 2707     Pixel bg;
 2708 } ToSwap;
 2709 
 2710 #if OPT_HIGHLIGHT_COLOR
 2711 #define hc_param ,Bool hilite_color
 2712 #define hc_value ,screen->hilite_color
 2713 #else
 2714 #define hc_param        /* nothing */
 2715 #define hc_value        /* nothing */
 2716 #endif
 2717 
 2718 /*
 2719  * Use this to swap the foreground/background color values in the resource
 2720  * data, and to build up a list of the pairs which must be swapped in the
 2721  * GC cache.
 2722  */
 2723 static void
 2724 swapLocally(ToSwap * list, int *count, ColorRes * fg, ColorRes * bg hc_param)
 2725 {
 2726     ColorRes tmp;
 2727     Boolean found = False;
 2728 
 2729     Pixel fg_color = fg->value;
 2730     Pixel bg_color = bg->value;
 2731 
 2732 #if OPT_HIGHLIGHT_COLOR
 2733     if ((fg_color != bg_color) || !hilite_color)
 2734 #endif
 2735     {
 2736     int n;
 2737 
 2738     EXCHANGE(*fg, *bg, tmp);
 2739     for (n = 0; n < *count; ++n) {
 2740         if ((list[n].fg == fg_color && list[n].bg == bg_color)
 2741         || (list[n].fg == bg_color && list[n].bg == fg_color)) {
 2742         found = True;
 2743         break;
 2744         }
 2745     }
 2746     if (!found) {
 2747         list[*count].fg = fg_color;
 2748         list[*count].bg = bg_color;
 2749         *count = *count + 1;
 2750         TRACE(("swapLocally fg %#lx, bg %#lx ->%d\n",
 2751            fg_color, bg_color, *count));
 2752     }
 2753     }
 2754 }
 2755 
 2756 static void
 2757 reallySwapColors(XtermWidget xw, ToSwap * list, int count)
 2758 {
 2759     int j, k;
 2760 
 2761     TRACE(("reallySwapColors\n"));
 2762     for (j = 0; j < count; ++j) {
 2763     for_each_text_gc(k) {
 2764         redoCgs(xw, list[j].fg, list[j].bg, (CgsEnum) k);
 2765     }
 2766     }
 2767     FreeMarkGCs(xw);
 2768 }
 2769 
 2770 static void
 2771 swapVTwinGCs(XtermWidget xw, VTwin *win)
 2772 {
 2773     swapCgs(xw, win, gcNorm, gcNormReverse);
 2774     swapCgs(xw, win, gcBold, gcBoldReverse);
 2775 }
 2776 
 2777 void
 2778 ReverseVideo(XtermWidget xw)
 2779 {
 2780     TScreen *screen = TScreenOf(xw);
 2781     ToSwap listToSwap[5];
 2782     int numToSwap = 0;
 2783 
 2784     TRACE(("ReverseVideo now %s\n", BtoS(xw->misc.re_verse)));
 2785 
 2786     /*
 2787      * Swap SGR foreground and background colors.  By convention, these are
 2788      * the colors assigned to "black" (SGR #0) and "white" (SGR #7).  Also,
 2789      * SGR #8 and SGR #15 are the bold (or bright) versions of SGR #0 and
 2790      * #7, respectively.
 2791      *
 2792      * We don't swap colors that happen to match the screen's foreground
 2793      * and background because that tends to produce bizarre effects.
 2794      */
 2795 #define swapAnyColor(name,a,b) swapLocally(listToSwap, &numToSwap, &(screen->name[a]), &(screen->name[b]) hc_value)
 2796 #define swapAColor(a,b) swapAnyColor(Acolors, a, b)
 2797     if_OPT_ISO_COLORS(screen, {
 2798     swapAColor(0, 7);
 2799     swapAColor(8, 15);
 2800     });
 2801 
 2802     if (T_COLOR(screen, TEXT_CURSOR) == T_COLOR(screen, TEXT_FG)) {
 2803     T_COLOR(screen, TEXT_CURSOR) = T_COLOR(screen, TEXT_BG);
 2804     }
 2805 #define swapTColor(a,b) swapAnyColor(Tcolors, a, b)
 2806     swapTColor(TEXT_FG, TEXT_BG);
 2807     swapTColor(MOUSE_FG, MOUSE_BG);
 2808 
 2809     reallySwapColors(xw, listToSwap, numToSwap);
 2810 
 2811     swapVTwinGCs(xw, &(screen->fullVwin));
 2812 #ifndef NO_ACTIVE_ICON
 2813     swapVTwinGCs(xw, &(screen->iconVwin));
 2814 #endif /* NO_ACTIVE_ICON */
 2815 
 2816     xw->misc.re_verse = (Boolean) !xw->misc.re_verse;
 2817     TRACE(("...swapping done, set ReverseVideo %s\n", BtoS(xw->misc.re_verse)));
 2818 
 2819     if (XtIsRealized((Widget) xw)) {
 2820     xtermDisplayPointer(xw);
 2821     }
 2822 #if OPT_TEK4014
 2823     if (TEK4014_SHOWN(xw)) {
 2824     TekScreen *tekscr = TekScreenOf(tekWidget);
 2825     Window tekwin = TWindow(tekscr);
 2826     recolor_cursor(screen,
 2827                tekscr->arrow,
 2828                T_COLOR(screen, MOUSE_FG),
 2829                T_COLOR(screen, MOUSE_BG));
 2830     XDefineCursor(screen->display, tekwin, tekscr->arrow);
 2831     }
 2832 #endif
 2833 
 2834     if (screen->scrollWidget)
 2835     ScrollBarReverseVideo(screen->scrollWidget);
 2836 
 2837     if (XtIsRealized((Widget) xw)) {
 2838     set_background(xw, -1);
 2839     }
 2840 #if OPT_TEK4014
 2841     TekReverseVideo(xw, tekWidget);
 2842 #endif
 2843     if (XtIsRealized((Widget) xw)) {
 2844     xtermRepaint(xw);
 2845     }
 2846 #if OPT_TEK4014
 2847     if (TEK4014_SHOWN(xw)) {
 2848     TekRepaint(tekWidget);
 2849     }
 2850 #endif
 2851     ReverseOldColors(xw);
 2852     set_cursor_gcs(xw);
 2853     update_reversevideo();
 2854     TRACE(("...ReverseVideo now %s\n", BtoS(xw->misc.re_verse)));
 2855 }
 2856 
 2857 void
 2858 recolor_cursor(TScreen *screen,
 2859            Cursor cursor,   /* X cursor ID to set */
 2860            unsigned long fg,    /* pixel indexes to look up */
 2861            unsigned long bg)    /* pixel indexes to look up */
 2862 {
 2863     Display *dpy = screen->display;
 2864     XColor colordefs[2];    /* 0 is foreground, 1 is background */
 2865 
 2866     colordefs[0].pixel = fg;
 2867     colordefs[1].pixel = bg;
 2868     XQueryColors(dpy, DefaultColormap(dpy, DefaultScreen(dpy)),
 2869          colordefs, 2);
 2870     XRecolorCursor(dpy, cursor, colordefs, colordefs + 1);
 2871     cleanup_colored_cursor();
 2872     return;
 2873 }
 2874 
 2875 #if OPT_RENDERFONT
 2876 #define XFT_CACHE_LIMIT ((unsigned)(~0) >> 1)
 2877 #define XFT_CACHE_SIZE  16
 2878 typedef struct {
 2879     XftColor color;
 2880     unsigned use;
 2881 } XftColorCache;
 2882 
 2883 static int
 2884 compare_xft_color_cache(const void *a, const void *b)
 2885 {
 2886     return (int) (((const XftColorCache *) a)->use -
 2887           ((const XftColorCache *) b)->use);
 2888 }
 2889 
 2890 static XftColor *
 2891 getXftColor(XtermWidget xw, Pixel pixel)
 2892 {
 2893     static XftColorCache cache[XFT_CACHE_SIZE + 1];
 2894     static unsigned latest_use;
 2895     int i;
 2896     int oldest;
 2897     unsigned oldest_use;
 2898     XColor color;
 2899     Boolean found = False;
 2900 
 2901     (void) xw;
 2902     oldest_use = XFT_CACHE_LIMIT;
 2903     oldest = 0;
 2904     if (latest_use == XFT_CACHE_LIMIT) {
 2905     latest_use = 0;
 2906     qsort(cache, (size_t) XFT_CACHE_SIZE, sizeof(XftColorCache), compare_xft_color_cache);
 2907     for (i = 0; i < XFT_CACHE_SIZE; i++) {
 2908         if (cache[i].use) {
 2909         cache[i].use = ++latest_use;
 2910         }
 2911     }
 2912     }
 2913     for (i = 0; i < XFT_CACHE_SIZE; i++) {
 2914     if (cache[i].use) {
 2915         if (cache[i].color.pixel == pixel) {
 2916         found = True;
 2917         break;
 2918         }
 2919     }
 2920     if (cache[i].use < oldest_use) {
 2921         oldest_use = cache[i].use;
 2922         oldest = i;
 2923     }
 2924     }
 2925     if (!found) {
 2926     i = oldest;
 2927     color.pixel = pixel;
 2928     (void) QueryOneColor(xw, &color);
 2929     cache[i].color.color.red = color.red;
 2930     cache[i].color.color.green = color.green;
 2931     cache[i].color.color.blue = color.blue;
 2932     cache[i].color.color.alpha = 0xffff;
 2933     cache[i].color.pixel = pixel;
 2934     }
 2935     cache[i].use = ++latest_use;
 2936     return &cache[i].color;
 2937 }
 2938 
 2939 /*
 2940  * The cell-width is related to, but not the same as the wide-character width.
 2941  * We will only get useful values from wcwidth() for codes above 255.
 2942  * Otherwise, interpret according to internal data.
 2943  */
 2944 #if OPT_RENDERWIDE
 2945 
 2946 #if OPT_C1_PRINT
 2947 #define XtermCellWidth(xw, ch) \
 2948     (((ch) == 0 || (ch) == 127) \
 2949       ? 0 \
 2950       : (((ch) < 256) \
 2951           ? (((ch) >= 128 && (ch) < 160) \
 2952           ? (TScreenOf(xw)->c1_printable ? 1 : 0) \
 2953           : 1) \
 2954           : CharWidth(TScreenOf(xw), ch)))
 2955 #else
 2956 #define XtermCellWidth(xw, ch) \
 2957     (((ch) == 0 || (ch) == 127) \
 2958       ? 0 \
 2959       : (((ch) < 256) \
 2960           ? 1 \
 2961           : CharWidth(TScreenOf(xw), ch)))
 2962 #endif
 2963 
 2964 #endif /* OPT_RENDERWIDE */
 2965 
 2966 #define XFT_DATA(which) getMyXftFont(params->xw, which, fontnum)
 2967 
 2968 #if OPT_ISO_COLORS
 2969 #define UseBoldFont(screen) (!(screen)->colorBDMode || ((screen)->veryBoldColors & BOLD))
 2970 #else
 2971 #define UseBoldFont(screen) 1
 2972 #endif
 2973 
 2974 #if OPT_RENDERWIDE
 2975 /*
 2976  * Find Xft (truetype) double-width font for the given normal/bold attributes.
 2977  */
 2978 static XTermXftFonts *
 2979 getWideXftFont(XTermDraw * params,
 2980            unsigned attr_flags)
 2981 {
 2982     TScreen *screen = TScreenOf(params->xw);
 2983     int fontnum = screen->menu_font_number;
 2984     XTermXftFonts *result = 0;
 2985 
 2986 #if OPT_WIDE_ATTRS
 2987     if ((attr_flags & ATR_ITALIC)
 2988 #if OPT_ISO_COLORS
 2989     && !screen->colorITMode
 2990 #endif
 2991     ) {
 2992     if ((attr_flags & BOLDATTR(screen))
 2993         && UseBoldFont(screen)
 2994         && XFT_DATA(fWBtal)) {
 2995         result = XFT_DATA(fWBtal);
 2996     } else if (XFT_DATA(fWItal)) {
 2997         result = XFT_DATA(fWItal);
 2998     }
 2999     }
 3000     if (result != 0) {
 3001     ;           /* skip the other tests */
 3002     } else
 3003 #endif
 3004 #if OPT_ISO_COLORS
 3005     if ((attr_flags & UNDERLINE)
 3006         && !screen->colorULMode
 3007         && screen->italicULMode
 3008         && XFT_DATA(fWItal)) {
 3009     result = XFT_DATA(fWItal);
 3010     } else
 3011 #endif
 3012     if ((attr_flags & BOLDATTR(screen))
 3013         && UseBoldFont(screen)
 3014         && XFT_DATA(fWBold)) {
 3015     result = XFT_DATA(fWBold);
 3016     } else {
 3017     result = XFT_DATA(fWide);
 3018     }
 3019     return result;
 3020 }
 3021 #endif /* OPT_RENDERWIDE */
 3022 
 3023 /*
 3024  * Find Xft (truetype) single-width font for the given normal/bold attributes.
 3025  */
 3026 static XTermXftFonts *
 3027 getNormXftFont(XTermDraw * params,
 3028            unsigned attr_flags,
 3029            Bool *did_ul)
 3030 {
 3031     TScreen *screen = TScreenOf(params->xw);
 3032     int fontnum = screen->menu_font_number;
 3033     XTermXftFonts *result = NULL;
 3034 
 3035     (void) did_ul;
 3036 #if OPT_DEC_CHRSET
 3037     if (CSET_DOUBLE(params->real_chrset)) {
 3038     result = xterm_DoubleFT(params, params->real_chrset, attr_flags);
 3039     }
 3040     if (result != 0) {
 3041     ;           /* found a usable double-sized font */
 3042     } else
 3043 #endif
 3044 #if OPT_WIDE_ATTRS
 3045     if ((attr_flags & ATR_ITALIC)
 3046 #if OPT_ISO_COLORS
 3047         && !screen->colorITMode
 3048 #endif
 3049     ) {
 3050     if ((attr_flags & BOLDATTR(screen))
 3051         && UseBoldFont(screen)
 3052         && XFT_DATA(fBtal)) {
 3053         result = XFT_DATA(fBtal);
 3054     } else if (XFT_DATA(fItal)) {
 3055         result = XFT_DATA(fItal);
 3056     }
 3057     }
 3058     if (result != NULL) {
 3059     ;           /* skip the other tests */
 3060     } else
 3061 #endif
 3062 #if OPT_ISO_COLORS
 3063     if ((attr_flags & UNDERLINE)
 3064         && !screen->colorULMode
 3065         && screen->italicULMode
 3066         && XFT_DATA(fItal)) {
 3067     result = XFT_DATA(fItal);
 3068     *did_ul = True;
 3069     } else
 3070 #endif
 3071     if ((attr_flags & BOLDATTR(screen))
 3072         && UseBoldFont(screen)
 3073         && XFT_DATA(fBold)) {
 3074     result = XFT_DATA(fBold);
 3075     } else {
 3076     result = XFT_DATA(fNorm);
 3077     }
 3078     return result;
 3079 }
 3080 
 3081 #if OPT_RENDERWIDE
 3082 #define pickXftData(width, nf, wf) (((width == 2) && ((wf) != NULL) && XftFp(wf) != NULL) ? (wf) : (nf))
 3083 #define pickXftFont(width, nf, wf) (((width == 2) && ((wf) != NULL)) ? (wf) : (nf))
 3084 #else
 3085 #define pickXftData(width, nf, wf) (nf)
 3086 #define pickXftFont(width, nf, wf) (nf)
 3087 #endif
 3088 
 3089 /*
 3090  * fontconfig/Xft combination prior to 2.2 has a problem with
 3091  * CJK truetype 'double-width' (bi-width/monospace) fonts leading
 3092  * to the 's p a c e d o u t' rendering. Consequently, we can't
 3093  * rely on XftDrawString8/16 when one of those fonts is used.
 3094  * Instead, we need to roll out our own using XftDrawCharSpec.
 3095  * A patch in the same spirit (but in a rather different form)
 3096  * was applied to gnome vte and gtk2 port of vim.
 3097  * See http://bugzilla.mozilla.org/show_bug.cgi?id=196312
 3098  */
 3099 static int
 3100 xtermXftDrawString(XTermDraw * params,
 3101            unsigned attr_flags,
 3102            XftColor *color,
 3103            XftFont *font,
 3104            int x,
 3105            int y,
 3106            const IChar *text,
 3107            Cardinal len,
 3108            Bool really)
 3109 {
 3110     TScreen *screen = TScreenOf(params->xw);
 3111     int ncells = 0;
 3112 
 3113     (void) attr_flags;
 3114     if (len != 0) {
 3115 #if OPT_RENDERWIDE
 3116     XftCharSpec *sbuf;
 3117     XTermXftFonts *wdata = getWideXftFont(params, attr_flags);
 3118     XftFont *wfont = XftFp(wdata);
 3119     Cardinal src, dst;
 3120     XftFont *lastFont = 0;
 3121     XftFont *currFont = 0;
 3122     Cardinal start = 0;
 3123     int charWidth;
 3124     int fwidth = FontWidth(screen);
 3125 #if OPT_DEC_CHRSET
 3126     Boolean forceDbl = CSET_DOUBLE(params->real_chrset);
 3127 #else
 3128     Boolean forceDbl = False;
 3129 #endif
 3130 
 3131     BumpTypedBuffer(XftCharSpec, 2 * len);
 3132     sbuf = BfBuf(XftCharSpec);
 3133 
 3134     for (src = dst = 0; src < len; src++) {
 3135         FcChar32 wc = *text++;
 3136 
 3137         charWidth = XtermCellWidth(params->xw, (wchar_t) wc);
 3138         if (charWidth < 0)
 3139         continue;
 3140 
 3141         sbuf[dst].ucs4 = wc;
 3142         sbuf[dst].x = (short) (x + fwidth * ncells);
 3143         sbuf[dst].y = (short) (y);
 3144 
 3145         currFont = pickXftFont(charWidth, font, wfont);
 3146         ncells += charWidth;
 3147 
 3148         if (lastFont != currFont) {
 3149         if ((lastFont != 0) && really) {
 3150             XftDrawCharSpec(screen->renderDraw,
 3151                     color,
 3152                     lastFont,
 3153                     sbuf + start,
 3154                     (int) (dst - start));
 3155         }
 3156         start = dst;
 3157         lastFont = currFont;
 3158         }
 3159         ++dst;
 3160 
 3161         if (forceDbl && charWidth < 2) {
 3162         sbuf[dst].ucs4 = ' ';
 3163         sbuf[dst].x = (short) (x + fwidth * ncells);
 3164         sbuf[dst].y = (short) (y);
 3165         ++dst;
 3166         ncells += charWidth;
 3167         }
 3168     }
 3169     if ((dst != start) && really) {
 3170         XftDrawCharSpec(screen->renderDraw,
 3171                 color,
 3172                 lastFont,
 3173                 sbuf + start,
 3174                 (int) (dst - start));
 3175     }
 3176 #else /* !OPT_RENDERWIDE */
 3177     if (really) {
 3178         XftChar8 *buffer;
 3179         int dst;
 3180 
 3181         BumpTypedBuffer(XftChar8, len);
 3182         buffer = BfBuf(XftChar8);
 3183 
 3184         for (dst = 0; dst < (int) len; ++dst)
 3185         buffer[dst] = CharOf(text[dst]);
 3186 
 3187         XftDrawString8(screen->renderDraw,
 3188                color,
 3189                font,
 3190                x, y, buffer, (int) len);
 3191     }
 3192     ncells = (int) len;
 3193 #endif
 3194     xtermNeedSwap(params->xw, 1);
 3195     }
 3196     return ncells;
 3197 }
 3198 #define xtermXftWidth(params, attr_flags, color, font, x, y, chars, len) \
 3199    xtermXftDrawString(params, attr_flags, color, font, x, y, chars, len, False)
 3200 #endif /* OPT_RENDERFONT */
 3201 
 3202 #if OPT_WIDE_CHARS
 3203 /*
 3204  * Map characters commonly "fixed" by groff back to their ASCII equivalents.
 3205  * Also map other useful equivalents.
 3206  */
 3207 unsigned
 3208 AsciiEquivs(unsigned ch)
 3209 {
 3210     switch (ch) {
 3211     case 0x2010:        /* groff "-" */
 3212     case 0x2011:
 3213     case 0x2012:
 3214     case 0x2013:
 3215     case 0x2014:
 3216     case 0x2015:
 3217     case 0x2212:        /* groff "\-" */
 3218     ch = '-';
 3219     break;
 3220     case 0x2018:        /* groff "`" */
 3221     ch = '`';
 3222     break;
 3223     case 0x2019:        /* groff ' */
 3224     ch = '\'';
 3225     break;
 3226     case 0x201C:        /* groff lq */
 3227     case 0x201D:        /* groff rq */
 3228     ch = '"';
 3229     break;
 3230     case 0x2329:        /* groff ".URL" */
 3231     ch = '<';
 3232     break;
 3233     case 0x232a:        /* groff ".URL" */
 3234     ch = '>';
 3235     break;
 3236     default:
 3237     if (ch >= 0xff01 && ch <= 0xff5e) {
 3238         /* "Fullwidth" codes (actually double-width) */
 3239         ch -= 0xff00;
 3240         ch += ANSI_SPA;
 3241         break;
 3242     }
 3243     }
 3244     return ch;
 3245 }
 3246 
 3247 /*
 3248  * Actually this should be called "groff_workaround()" - for the places where
 3249  * groff stomps on compatibility.  Still, if enough people get used to it,
 3250  * this might someday become a quasi-standard.
 3251  */
 3252 #if OPT_BOX_CHARS
 3253 static int
 3254 ucs_workaround(XTermDraw * params,
 3255            unsigned ch,
 3256            GC gc,
 3257            int x,
 3258            int y)
 3259 {
 3260     TScreen *screen = TScreenOf(params->xw);
 3261     int fixed = False;
 3262 
 3263     if (screen->wide_chars && screen->utf8_mode && ch > 256) {
 3264     IChar eqv = (IChar) AsciiEquivs(ch);
 3265 
 3266     if (eqv != (IChar) ch) {
 3267         int width = CharWidth(screen, ch);
 3268 
 3269         do {
 3270         drawXtermText(params,
 3271                   gc,
 3272                   x,
 3273                   y,
 3274                   &eqv,
 3275                   1);
 3276         x += FontWidth(screen);
 3277         eqv = BAD_ASCII;
 3278         } while (width-- > 1);
 3279 
 3280         fixed = True;
 3281     } else if (ch == HIDDEN_CHAR) {
 3282         fixed = True;
 3283     }
 3284     }
 3285     return fixed;
 3286 }
 3287 #endif /* OPT_BOX_CHARS */
 3288 #endif /* OPT_WIDE_CHARS */
 3289 
 3290 /*
 3291  * Use this when the characters will not fill the cell area properly.  Fill the
 3292  * area where we'll write the characters, otherwise we'll get gaps between
 3293  * them, e.g., in the original background color.
 3294  *
 3295  * The cursor is a special case, because the XFillRectangle call only uses the
 3296  * foreground, while we've set the cursor color in the background.  So we need
 3297  * a special GC for that.
 3298  */
 3299 static void
 3300 xtermFillCells(XTermDraw * params,
 3301            GC gc,
 3302            int x,
 3303            int y,
 3304            Cardinal len)
 3305 {
 3306     TScreen *screen = TScreenOf(params->xw);
 3307     VTwin *currentWin = WhichVWin(screen);
 3308 
 3309     if (!(params->draw_flags & NOBACKGROUND)) {
 3310     CgsEnum srcId = getCgsId(params->xw, currentWin, gc);
 3311     CgsEnum dstId = gcMAX;
 3312     Pixel fg = getCgsFore(params->xw, currentWin, gc);
 3313     Pixel bg = getCgsBack(params->xw, currentWin, gc);
 3314 
 3315     switch (srcId) {
 3316     case gcVTcursNormal:
 3317     case gcVTcursReverse:
 3318         dstId = gcVTcursOutline;
 3319         break;
 3320     case gcVTcursFilled:
 3321     case gcVTcursOutline:
 3322         /* FIXME */
 3323         break;
 3324     case gcNorm:
 3325         dstId = gcNormReverse;
 3326         break;
 3327     case gcNormReverse:
 3328         dstId = gcNorm;
 3329         break;
 3330     case gcBold:
 3331         dstId = gcBoldReverse;
 3332         break;
 3333     case gcBoldReverse:
 3334         dstId = gcBold;
 3335         break;
 3336     case gcBorder:
 3337     case gcFiller:
 3338         dstId = srcId;
 3339         break;
 3340 #if OPT_BOX_CHARS
 3341     case gcLine:
 3342     case gcDots:
 3343         /* FIXME */
 3344         break;
 3345 #endif
 3346 #if OPT_DEC_CHRSET
 3347     case gcCNorm:
 3348     case gcCBold:
 3349         /* FIXME */
 3350         break;
 3351 #endif
 3352 #if OPT_WIDE_CHARS
 3353     case gcWide:
 3354         dstId = gcWideReverse;
 3355         break;
 3356     case gcWBold:
 3357         dstId = gcBoldReverse;
 3358         break;
 3359     case gcWideReverse:
 3360     case gcWBoldReverse:
 3361         /* FIXME */
 3362         break;
 3363 #endif
 3364 #if OPT_TEK4014
 3365     case gcTKcurs:
 3366         /* FIXME */
 3367         break;
 3368 #endif
 3369     case gcMAX:
 3370         break;
 3371     }
 3372 
 3373     if (dstId != gcMAX) {
 3374         setCgsFore(params->xw, currentWin, dstId, bg);
 3375         setCgsBack(params->xw, currentWin, dstId, fg);
 3376 
 3377         XFillRectangle(screen->display, VDrawable(screen),
 3378                getCgsGC(params->xw, currentWin, dstId),
 3379                x, y,
 3380                len * (Cardinal) FontWidth(screen),
 3381                (unsigned) FontHeight(screen));
 3382     }
 3383     }
 3384 }
 3385 
 3386 #if OPT_TRACE
 3387 static void
 3388 xtermSetClipRectangles(Display *dpy,
 3389                GC gc,
 3390                int x,
 3391                int y,
 3392                XRectangle * rp,
 3393                Cardinal nr,
 3394                int order)
 3395 {
 3396 #if 0
 3397     TScreen *screen = TScreenOf(term);
 3398     Drawable draw = VDrawable(screen);
 3399 
 3400     XSetClipMask(dpy, gc, None);
 3401     XDrawRectangle(screen->display, draw, gc,
 3402            x + rp->x - 1,
 3403            y + rp->y - 1,
 3404            rp->width,
 3405            rp->height);
 3406 #endif
 3407 
 3408     XSetClipRectangles(dpy, gc,
 3409                x, y, rp, (int) nr, order);
 3410     TRACE(("clipping @(%3d,%3d) (%3d,%3d)..(%3d,%3d)\n",
 3411        y, x,
 3412        rp->y, rp->x, rp->height, rp->width));
 3413 }
 3414 
 3415 #else
 3416 #define xtermSetClipRectangles(dpy, gc, x, y, rp, nr, order) \
 3417         XSetClipRectangles(dpy, gc, x, y, rp, (int) nr, order)
 3418 #endif
 3419 
 3420 #if OPT_CLIP_BOLD
 3421 /*
 3422  * This special case is a couple of percent slower, but avoids a lot of pixel
 3423  * trash in rxcurses' hanoi.cmd demo (e.g., 10x20 font).
 3424  */
 3425 #define beginClipping(screen,gc,pwidth,plength) \
 3426     if (pwidth > 2) { \
 3427         if (screen->use_clipping) { \
 3428         XRectangle clip; \
 3429         int clip_x = x; \
 3430         int clip_y = y - FontHeight(screen) + FontDescent(screen); \
 3431         clip.x = 0; \
 3432         clip.y = 0; \
 3433         clip.height = (unsigned short) FontHeight(screen); \
 3434         clip.width = (unsigned short) ((pwidth) * (plength)); \
 3435         xtermSetClipRectangles(screen->display, gc, \
 3436                        clip_x, clip_y, \
 3437                        &clip, 1, Unsorted); \
 3438         } else if (screen->use_border_clipping) { \
 3439         XRectangle clip; \
 3440         clip.x = 0; \
 3441         clip.y = 0; \
 3442         clip.height = (unsigned short) Height(screen); \
 3443         clip.width = (unsigned short) Width(screen); \
 3444         xtermSetClipRectangles(screen->display, gc, \
 3445                        0, 0, \
 3446                        &clip, 1, Unsorted); \
 3447         } \
 3448     }
 3449 #define endClipping(screen,gc) \
 3450         XSetClipMask(screen->display, gc, None)
 3451 #else
 3452 #define beginClipping(screen,gc,pwidth,plength)     /* nothing */
 3453 #define endClipping(screen,gc)  /* nothing */
 3454 #endif /* OPT_CLIP_BOLD */
 3455 
 3456 #if OPT_RENDERFONT
 3457 static int
 3458 drawClippedXftString(XTermDraw * params,
 3459              unsigned attr_flags,
 3460              XftFont *font,
 3461              XftColor *fg_color,
 3462              int x,
 3463              int y,
 3464              const IChar *text,
 3465              Cardinal len)
 3466 {
 3467     int ncells = xtermXftWidth(params, attr_flags,
 3468                    fg_color,
 3469                    font, x, y,
 3470                    text,
 3471                    len);
 3472     TScreen *screen = TScreenOf(params->xw);
 3473     int fontHigh = FontHeight(screen);
 3474     int fontWide = FontWidth(screen);
 3475 
 3476     if (fontWide > 2) {
 3477     int plength = (ncells ? ncells : 1);
 3478     Boolean halfHigh = False;
 3479 #if OPT_DEC_CHRSET
 3480     Boolean halfWide = False;
 3481 
 3482     switch (params->real_chrset) {
 3483     case CSET_SWL:
 3484         break;
 3485     case CSET_DWL:
 3486         halfWide = True;
 3487         break;
 3488     case CSET_DHL_TOP:
 3489         halfHigh = True;
 3490         halfWide = True;
 3491         break;
 3492     case CSET_DHL_BOT:
 3493         halfHigh = True;
 3494         halfWide = True;
 3495         break;
 3496     }
 3497     if (CSET_DOUBLE(params->real_chrset)) {
 3498         fontHigh = font->height;
 3499         fontWide = font->max_advance_width;
 3500         /* check if this is really a double-height font */
 3501         if (halfHigh) {
 3502         int wantHigh = (int) (FontHeight(screen) * 1.8);
 3503         halfHigh = (fontHigh >= wantHigh);
 3504         TRACE(("comparing fontHigh %d/%d vs %d:"
 3505                " double-height %s for %s\n",
 3506                fontHigh, FontHeight(screen),
 3507                wantHigh, BtoS(halfHigh),
 3508                visibleDblChrset(params->real_chrset)));
 3509         }
 3510         fontHigh = (halfHigh
 3511             ? (2 * FontHeight(screen))
 3512             : FontHeight(screen));
 3513         /* check if this is really a double-width font */
 3514         if (halfWide) {
 3515         int wantWide = (int) (FontWidth(screen) * 1.8);
 3516         halfWide = (fontWide >= wantWide);
 3517         TRACE(("comparing fontWide %d/%d vs %d:"
 3518                " double-width %s for %s\n",
 3519                fontWide, FontWidth(screen),
 3520                wantWide, BtoS(halfWide),
 3521                visibleDblChrset(params->real_chrset)));
 3522         }
 3523         fontWide = (halfWide
 3524             ? (2 * FontWidth(screen))
 3525             : FontWidth(screen));
 3526     }
 3527 #endif
 3528     if (screen->use_clipping || halfHigh) {
 3529         XRectangle clip;
 3530         double adds = ((double) screen->scale_height - 1.0f) * fontHigh;
 3531         int height = dimRound(adds + fontHigh);
 3532         int descnt = dimRound(adds / 2.0) + FontDescent(screen);
 3533         int clip_x = (x);
 3534         int clip_y = (y) - height + descnt;
 3535 
 3536         clip.x = 0;
 3537         clip.y = 0;
 3538         clip.height = (Dimension) height;
 3539         clip.width = (Dimension) (fontWide * (Dimension) (plength));
 3540 
 3541 #if OPT_DEC_CHRSET
 3542         if (halfHigh) {
 3543         int adjust;
 3544 
 3545         clip.height = (unsigned short) FontHeight(screen);
 3546         clip_y += descnt;
 3547         if (params->real_chrset == CSET_DHL_BOT) {
 3548             y -= clip.height;
 3549         }
 3550         adjust = ((clip_y - OriginY(screen)) % FontHeight(screen));
 3551         if (adjust) {
 3552             if (adjust > FontHeight(screen) / 2)
 3553             adjust -= FontHeight(screen);
 3554             clip_y -= adjust;
 3555         }
 3556         }
 3557 #endif
 3558         XftDrawSetClipRectangles(screen->renderDraw,
 3559                      clip_x, clip_y,
 3560                      &clip, 1);
 3561     } else if (screen->use_border_clipping) {
 3562         XRectangle clip;
 3563 
 3564         clip.x = (Position) OriginX(screen);
 3565         clip.y = (Position) OriginY(screen);
 3566         clip.height = (Dimension) Height(screen);
 3567         clip.width = (Dimension) Width(screen);
 3568 
 3569         XftDrawSetClipRectangles(screen->renderDraw,
 3570                      0, 0,
 3571                      &clip, 1);
 3572     }
 3573     }
 3574 
 3575     xtermXftDrawString(params, attr_flags,
 3576                fg_color,
 3577                font, x, y + ScaleShift(screen),
 3578                text,
 3579                len,
 3580                True);
 3581     XftDrawSetClip(screen->renderDraw, 0);
 3582     return ncells;
 3583 }
 3584 #endif
 3585 
 3586 #ifndef NO_ACTIVE_ICON
 3587 #define WhichVFontData(screen,name) \
 3588         (IsIcon(screen) ? getIconicFont(screen) \
 3589                 : GetNormalFont(screen, name))
 3590 #else
 3591 #define WhichVFontData(screen,name) \
 3592                 GetNormalFont(screen, name)
 3593 #endif
 3594 
 3595 static void
 3596 drawUnderline(XtermWidget xw,
 3597           GC gc,
 3598           unsigned attr_flags,
 3599           unsigned underline_len,
 3600           int font_width,
 3601           int x,
 3602           int y,
 3603           Bool did_ul)
 3604 {
 3605     TScreen *screen = TScreenOf(xw);
 3606 
 3607     if (screen->underline && !did_ul) {
 3608     int repeat = 0;
 3609     int descent = FontDescent(screen);
 3610     int length = x + (int) underline_len * font_width - 1;
 3611 
 3612 #if OPT_WIDE_ATTRS
 3613     if ((attr_flags & ATR_STRIKEOUT)) {
 3614         int where = y - ((3 * FontAscent(screen)) / 8);
 3615         XDrawLine(screen->display, VDrawable(screen), gc,
 3616               x, where,
 3617               length,
 3618               where);
 3619     }
 3620     if ((attr_flags & ATR_DBL_UNDER)) {
 3621         repeat = 2;
 3622     } else
 3623 #endif
 3624     if ((attr_flags & UNDERLINE)) {
 3625         repeat = 1;
 3626     }
 3627     while (repeat-- > 0) {
 3628         if (descent-- > 1)
 3629         y++;
 3630         XDrawLine(screen->display, VDrawable(screen), gc,
 3631               x, y,
 3632               length,
 3633               y);
 3634     }
 3635     }
 3636 }
 3637 
 3638 #if OPT_WIDE_ATTRS
 3639 /*
 3640  * As a special case, we are currently allowing italic fonts to be inexact
 3641  * matches for the normal font's size.  That introduces a problem:  either the
 3642  * ascent or descent may be shorter, leaving a gap that has to be filled in.
 3643  * Or they may be larger, requiring clipping.  Check for both cases.
 3644  */
 3645 static int
 3646 fixupItalics(XTermDraw * params,
 3647          GC gc,
 3648          XTermFonts * curFont,
 3649          int y, int x,
 3650          int font_width,
 3651          Cardinal len)
 3652 {
 3653     TScreen *screen = TScreenOf(params->xw);
 3654     VTwin *cgsWin = WhichVWin(screen);
 3655     XFontStruct *realFp = curFont->fs;
 3656     XFontStruct *thisFp = getCgsFont(params->xw, cgsWin, gc)->fs;
 3657     int need_clipping = 0;
 3658     int need_filling = 0;
 3659 
 3660     if (thisFp->ascent > realFp->ascent)
 3661     need_clipping = 1;
 3662     else if (thisFp->ascent < realFp->ascent)
 3663     need_filling = 1;
 3664 
 3665     if (thisFp->descent > realFp->descent)
 3666     need_clipping = 1;
 3667     else if (thisFp->descent < realFp->descent)
 3668     need_filling = 1;
 3669 
 3670     if (need_clipping) {
 3671     beginClipping(screen, gc, font_width, (int) len);
 3672     }
 3673     if (need_filling) {
 3674     xtermFillCells(params,
 3675                gc,
 3676                x,
 3677                y - realFp->ascent,
 3678                len);
 3679     }
 3680     return need_clipping;
 3681 }
 3682 #endif
 3683 
 3684 #if OPT_DEC_CHRSET
 3685 static int
 3686 fakeDoubleChars(XTermDraw * params,
 3687         GC gc,
 3688         int y,
 3689         int x,
 3690         const IChar *text,
 3691         Cardinal len)
 3692 {
 3693     unsigned need = 2 * len;
 3694     IChar *temp = TypeMallocN(IChar, need);
 3695 
 3696     if (temp != 0) {
 3697     unsigned n = 0;
 3698     XTermDraw recur = *params;
 3699 
 3700     recur.this_chrset = CSET_SWL;
 3701 
 3702     while (len--) {
 3703         temp[n++] = *text++;
 3704         temp[n++] = ' ';
 3705     }
 3706     x = drawXtermText(&recur,
 3707               gc,
 3708               x, y,
 3709               temp,
 3710               n);
 3711     free(temp);
 3712     }
 3713     return x;
 3714 }
 3715 #endif /* OPT_DEC_CHRSET */
 3716 
 3717 #define SetMissing(tag) \
 3718     TRACE(("%s %s: missing %d %04X\n", __FILE__, tag, missing, ch)); \
 3719     missing = 1
 3720 
 3721 #define MaxImageString 255
 3722 
 3723 /*
 3724  * Draws text with the specified combination of bold/underline.  The return
 3725  * value is the updated x position.
 3726  */
 3727 int
 3728 drawXtermText(XTermDraw * params,
 3729           GC gc,
 3730           int start_x,
 3731           int start_y,
 3732           const IChar *text,
 3733           Cardinal len)
 3734 {
 3735     XTermDraw recur = *params;
 3736     TScreen *screen = TScreenOf(recur.xw);
 3737     int x = start_x;
 3738     int y = start_y;
 3739     int y_shift = ScaleShift(screen);
 3740     Cardinal real_length = len;
 3741     Cardinal underline_len = 0;
 3742     /* Intended width of the font to draw (as opposed to the actual width of
 3743        the X font, and the width of the default font) */
 3744     int font_width = (((recur.draw_flags & DOUBLEWFONT) ? 2 : 1)
 3745               * screen->fnt_wide);
 3746     Bool did_ul = False;
 3747     XTermFonts *curFont;
 3748 
 3749 #if OPT_WIDE_ATTRS
 3750     int need_clipping = 0;
 3751 #endif
 3752 
 3753 #if OPT_WIDE_CHARS
 3754     Bool need_wide;
 3755 #endif
 3756 
 3757 #if OPT_WIDE_CHARS
 3758     if (text == 0)
 3759     return 0;
 3760 #endif
 3761     TRACE(("DRAWTEXT%c[%4d,%4d] (%d)%3d:%s\n",
 3762        screen->cursor_state == OFF ? ' ' : '*',
 3763        y, x, recur.this_chrset, len,
 3764        visibleIChars(text, len)));
 3765 
 3766 #if OPT_DEC_CHRSET
 3767     if (CSET_DOUBLE(recur.this_chrset)) {
 3768     /* We could try drawing double-size characters in the icon, but
 3769      * given that the icon font is usually nil or nil2, there
 3770      * doesn't seem to be much point.
 3771      */
 3772     int inx = 0;
 3773     GC gc2;
 3774 
 3775 #if OPT_RENDERFONT
 3776     if (UsingRenderFont(recur.xw)) {
 3777         if (!IsIcon(screen) && screen->font_doublesize) {
 3778         TRACE(("drawing %s\n", visibleDblChrset((unsigned) recur.this_chrset)));
 3779         recur.real_chrset = recur.this_chrset;
 3780         recur.this_chrset = CSET_SWL;
 3781         x = drawXtermText(&recur,
 3782                   gc,
 3783                   x, y,
 3784                   text,
 3785                   len);
 3786         x += ((int) len) * FontWidth(screen);
 3787         } else {
 3788         x = fakeDoubleChars(&recur,
 3789                     gc, y, x,
 3790                     text, len);
 3791         }
 3792     } else
 3793 #endif
 3794         if ((!IsIcon(screen) && screen->font_doublesize)
 3795         && (gc2 = xterm_DoubleGC(&recur, gc, &inx)) != 0) {
 3796         /* draw actual double-sized characters */
 3797         XFontStruct *fs = getDoubleFont(screen, inx)->fs;
 3798         XRectangle rect, *rp = &rect;
 3799         Cardinal nr = 1;
 3800 
 3801         font_width *= 2;
 3802         recur.draw_flags |= DOUBLEWFONT;
 3803 
 3804         rect.x = 0;
 3805         rect.y = 0;
 3806         rect.width = (unsigned short) ((int) len * font_width);
 3807         rect.height = (unsigned short) (FontHeight(screen));
 3808 
 3809         TRACE(("drawing %s\n", visibleDblChrset((unsigned) recur.this_chrset)));
 3810         switch (recur.this_chrset) {
 3811         case CSET_DHL_TOP:
 3812         rect.y = (short) -(fs->ascent / 2);
 3813         y -= rect.y;
 3814         recur.draw_flags |= DOUBLEHFONT;
 3815         break;
 3816         case CSET_DHL_BOT:
 3817         rect.y = (short) (rect.height - (fs->ascent / 2));
 3818         y -= rect.y;
 3819         recur.draw_flags |= DOUBLEHFONT;
 3820         break;
 3821         case CSET_DWL:
 3822         break;
 3823         default:
 3824         nr = 0;
 3825         break;
 3826         }
 3827 
 3828         if (nr) {
 3829         xtermSetClipRectangles(screen->display, gc2,
 3830                        x, y, rp, nr, YXBanded);
 3831         xtermFillCells(&recur, gc, x, y + rect.y, len * 2);
 3832         } else {
 3833         XSetClipMask(screen->display, gc2, None);
 3834         }
 3835 
 3836         /* Call ourselves recursively with the new gc */
 3837 
 3838         /*
 3839          * If we're trying to use proportional font, or if the
 3840          * font server didn't give us what we asked for wrt
 3841          * width, position each character independently.
 3842          */
 3843         if (screen->fnt_prop
 3844         || (fs->min_bounds.width != fs->max_bounds.width)
 3845         || (fs->min_bounds.width != 2 * FontWidth(screen))) {
 3846         /* It is hard to fall-through to the main
 3847            branch: in a lot of places the check
 3848            for the cached font info is for
 3849            normal/bold fonts only. */
 3850         XTermDraw param2 = recur;
 3851         param2.this_chrset = CSET_SWL;
 3852         while (len--) {
 3853             x = drawXtermText(&param2,
 3854                       gc2,
 3855                       x, y,
 3856                       text++,
 3857                       1);
 3858             x += FontWidth(screen);
 3859         }
 3860         } else {
 3861         XTermDraw param2 = recur;
 3862         param2.this_chrset = CSET_SWL;
 3863         x = drawXtermText(&param2,
 3864                   gc2,
 3865                   x, y,
 3866                   text,
 3867                   len);
 3868         x += (int) len *FontWidth(screen);
 3869         }
 3870 
 3871     } else {        /* simulate double-sized characters */
 3872         x = fakeDoubleChars(&recur,
 3873                 gc, y, x,
 3874                 text, len);
 3875     }
 3876     TRACE(("DrewText [%4d,%4d]\n", y, x));
 3877     return x;
 3878     }
 3879 #endif
 3880 #if OPT_RENDERFONT
 3881     if (UsingRenderFont(recur.xw)) {
 3882     VTwin *currentWin = WhichVWin(screen);
 3883     Display *dpy = screen->display;
 3884     XTermXftFonts *ndata;
 3885     XGCValues values;
 3886 #if OPT_WIDE_CHARS
 3887     XTermXftFonts *ndata0;
 3888 #endif
 3889 #if OPT_RENDERWIDE
 3890     XTermXftFonts *wdata;
 3891     XTermXftFonts *wdata0;
 3892 #endif
 3893 
 3894 #if OPT_DEC_CHRSET
 3895     /*
 3896      * If this is a VT100-style double-width font, ensure that everything
 3897      * is printed using two-columns per character.
 3898      */
 3899     Boolean forceDbl = CSET_DOUBLE(recur.real_chrset);
 3900 #else
 3901     Boolean forceDbl = False;
 3902 #endif
 3903 
 3904     (void) forceDbl;
 3905     /*
 3906      * Defer creating the drawable until we need it.
 3907      */
 3908     if (!screen->renderDraw) {
 3909         int scr;
 3910         Drawable draw = VDrawable(screen);
 3911         Visual *visual;
 3912 
 3913         scr = DefaultScreen(dpy);
 3914         visual = DefaultVisual(dpy, scr);
 3915         screen->renderDraw = XftDrawCreate(dpy, draw, visual,
 3916                            DefaultColormap(dpy, scr));
 3917     }
 3918 
 3919     /*
 3920      * ndata0/wdata0 provide fallback to non-bolded Xft font if a glyph is
 3921      * not found in the bold version.
 3922      */
 3923 #define IS_BOLD  (recur.attr_flags & BOLDATTR(screen))
 3924 #define NOT_BOLD (recur.attr_flags & ~BOLDATTR(screen))
 3925     ndata = getNormXftFont(&recur, recur.attr_flags, &did_ul);
 3926 #if OPT_WIDE_CHARS
 3927     ndata0 = IS_BOLD ? getNormXftFont(&recur, NOT_BOLD, &did_ul) : ndata;
 3928 #endif
 3929 #if OPT_RENDERWIDE
 3930     wdata = getWideXftFont(&recur, recur.attr_flags);
 3931     wdata0 = IS_BOLD ? getWideXftFont(&recur, NOT_BOLD) : wdata;
 3932 #endif
 3933 
 3934 #define GET_XFT_FG() getXftColor(recur.xw, values.foreground)
 3935 #define GET_XFT_BG() getXftColor(recur.xw, values.background)
 3936 
 3937     values.foreground = getCgsFore(recur.xw, currentWin, gc);
 3938     values.background = getCgsBack(recur.xw, currentWin, gc);
 3939 
 3940     if (!(recur.draw_flags & NOBACKGROUND)) {
 3941         XftColor *bg_color = GET_XFT_BG();
 3942         int ncells = xtermXftWidth(&recur, recur.attr_flags,
 3943                        bg_color,
 3944                        XftFp(ndata), x, y,
 3945                        text,
 3946                        len);
 3947         XftDrawRect(screen->renderDraw,
 3948             bg_color,
 3949             x, y,
 3950             (unsigned) (ncells * FontWidth(screen)),
 3951             (unsigned) FontHeight(screen));
 3952     }
 3953 
 3954     y += XftFp(ndata)->ascent;
 3955 #if OPT_BOX_CHARS
 3956     {
 3957         /* adding code to substitute simulated line-drawing characters */
 3958         int last, first = 0;
 3959         int curX = x;
 3960 
 3961         for (last = 0; last < (int) len; last++) {
 3962         Boolean replace = False;
 3963         Boolean missing = False;
 3964         unsigned ch = (unsigned) text[last];
 3965         int filler = 0;
 3966 #if OPT_WIDE_CHARS
 3967         int needed = forceDbl ? 2 : CharWidth(screen, ch);
 3968         XTermXftFonts *currData = pickXftData(needed, ndata, wdata);
 3969         XftFont *tempFont = 0;
 3970 #define CURR_TEMP (tempFont ? tempFont : XftFp(currData))
 3971 
 3972         if (xtermIsDecGraphic(ch) || ch == 0) {
 3973             /*
 3974              * Xft generally does not have the line-drawing characters
 3975              * in cells 0-31.  Assume this (we cannot inspect the
 3976              * picture easily...), and attempt to fill in from real
 3977              * line-drawing character in the font at the Unicode
 3978              * position.  Failing that, use our own box-characters.
 3979              */
 3980             if (screen->force_box_chars
 3981             || screen->broken_box_chars
 3982             || xtermXftMissing(recur.xw,
 3983                        currData, 0,
 3984                        XftFp(currData),
 3985                        dec2ucs(screen, ch))) {
 3986             SetMissing("case 1");
 3987             } else {
 3988             ch = dec2ucs(screen, ch);
 3989             replace = True;
 3990             }
 3991         } else if (ch >= 256) {
 3992             /*
 3993              * If we're reading UTF-8 from the client, we may have a
 3994              * line-drawing character.  Translate it back to our
 3995              * box-code if Xft tells us that the glyph is missing.
 3996              */
 3997             if_OPT_WIDE_CHARS(screen, {
 3998             unsigned part = ucs2dec(screen, ch);
 3999             if (xtermIsDecGraphic(part)) {
 4000                 if (screen->force_box_chars
 4001                 || screen->broken_box_chars) {
 4002                 SetMissing("case 2");
 4003                 ch = part;
 4004                 }
 4005             }
 4006             if (!missing && xtermXftMissing(recur.xw,
 4007                             currData, 0,
 4008                             XftFp(currData), ch)) {
 4009                 int found = findXftGlyph(recur.xw, currData, ch);
 4010                 XftFont *test;
 4011                 if (found >= 0) {
 4012                 test = XftFpN(currData, found);
 4013                 } else {
 4014                 test = pickXftFont(needed,
 4015                            XftFp(ndata0),
 4016                            XftFp(wdata0));
 4017                 }
 4018                 if (!xtermXftMissing(recur.xw,
 4019                          currData, found,
 4020                          test, ch)) {
 4021                 tempFont = test;
 4022                 replace = True;
 4023                 filler = 0;
 4024                 } else if ((part = AsciiEquivs(ch)) != ch) {
 4025                 filler = needed - 1;
 4026                 ch = part;
 4027                 replace = True;
 4028                 } else if (ch != HIDDEN_CHAR) {
 4029                 SetMissing("case 3");
 4030                 }
 4031             }
 4032             });
 4033         }
 4034 #else
 4035         XTermXftFonts *currData = ndata;
 4036 #define CURR_TEMP XftFp(currData)
 4037         if (xtermIsDecGraphic(ch)) {
 4038             /*
 4039              * Xft generally does not have the line-drawing characters
 4040              * in cells 1-31.  Check for this, and attempt to fill in
 4041              * from real line-drawing character in the font at the
 4042              * Unicode position.  Failing that, use our own
 4043              * box-characters.
 4044              */
 4045             if (xtermXftMissing(recur.xw,
 4046                     currData, 0,
 4047                     XftFp(currData), ch)) {
 4048             SetMissing("case 4");
 4049             }
 4050         }
 4051 #endif
 4052 
 4053         /*
 4054          * If we now have one of our box-codes, draw it directly.
 4055          */
 4056         if (missing || replace) {
 4057             /* line drawing character time */
 4058             if (last > first) {
 4059             int nc = drawClippedXftString(&recur,
 4060                               recur.attr_flags,
 4061                               XftFp(currData),
 4062                               GET_XFT_FG(),
 4063                               curX,
 4064                               y,
 4065                               text + first,
 4066                               (Cardinal) (last - first));
 4067             curX += nc * FontWidth(screen);
 4068             underline_len += (Cardinal) nc;
 4069             }
 4070             if (missing) {
 4071             Dimension old_wide = screen->fnt_wide;
 4072             Dimension old_high = screen->fnt_high;
 4073             screen->fnt_wide = (Dimension) FontWidth(screen);
 4074             screen->fnt_high = (Dimension) FontHeight(screen);
 4075 
 4076             xtermDrawBoxChar(&recur, ch,
 4077                      gc,
 4078                      curX,
 4079                      y - FontAscent(screen),
 4080                      1,
 4081                      True);
 4082             curX += FontWidth(screen);
 4083             underline_len += 1;
 4084             screen->fnt_wide = old_wide;
 4085             screen->fnt_high = old_high;
 4086             } else {
 4087             IChar ch2 = (IChar) ch;
 4088             int nc = drawClippedXftString(&recur,
 4089                               recur.attr_flags,
 4090                               CURR_TEMP,
 4091                               GET_XFT_FG(),
 4092                               curX,
 4093                               y,
 4094                               &ch2,
 4095                               1);
 4096             curX += nc * FontWidth(screen);
 4097             underline_len += (Cardinal) nc;
 4098             if (filler) {
 4099                 ch2 = ' ';
 4100                 nc = drawClippedXftString(&recur,
 4101                               recur.attr_flags,
 4102                               CURR_TEMP,
 4103                               GET_XFT_FG(),
 4104                               curX,
 4105                               y,
 4106                               &ch2,
 4107                               1);
 4108                 curX += nc * FontWidth(screen);
 4109                 underline_len += (Cardinal) nc;
 4110             }
 4111             }
 4112             first = last + 1;
 4113         }
 4114         }
 4115         if (last > first) {
 4116         underline_len += (Cardinal)
 4117             drawClippedXftString(&recur,
 4118                      recur.attr_flags,
 4119                      XftFp(ndata),
 4120                      GET_XFT_FG(),
 4121                      curX,
 4122                      y,
 4123                      text + first,
 4124                      (Cardinal) (last - first));
 4125         }
 4126     }
 4127 #else
 4128     {
 4129         underline_len += (Cardinal)
 4130         drawClippedXftString(&recur,
 4131                      recur.attr_flags,
 4132                      XftFp(ndata),
 4133                      GET_XFT_FG(),
 4134                      x,
 4135                      y,
 4136                      text,
 4137                      len);
 4138     }
 4139 #endif /* OPT_BOX_CHARS */
 4140 
 4141     drawUnderline(recur.xw,
 4142               gc,
 4143               recur.attr_flags,
 4144               underline_len,
 4145               FontWidth(screen),
 4146               x,
 4147               y + y_shift,
 4148               did_ul);
 4149 
 4150     x += (int) len *FontWidth(screen);
 4151 
 4152     return x;
 4153     }
 4154 #endif /* OPT_RENDERFONT */
 4155     curFont = ((recur.attr_flags & BOLDATTR(screen))
 4156            ? WhichVFontData(screen, fBold)
 4157            : WhichVFontData(screen, fNorm));
 4158     /*
 4159      * If we're asked to display a proportional font, do this with a fixed
 4160      * pitch.  Yes, it's ugly.  But we cannot distinguish the use of xterm
 4161      * as a dumb terminal vs its use as in fullscreen programs such as vi.
 4162      * Hint: do not try to use a proportional font in the icon.
 4163      */
 4164     if (!IsIcon(screen) && !(recur.draw_flags & CHARBYCHAR) && screen->fnt_prop) {
 4165     int adj, width;
 4166 
 4167     while (len--) {
 4168         int cells = WideCells(*text);
 4169 #if OPT_BOX_CHARS
 4170 #if OPT_WIDE_CHARS
 4171         if (*text == HIDDEN_CHAR) {
 4172         ++text;
 4173         continue;
 4174         } else
 4175 #endif
 4176         if (IsXtermMissingChar(screen, *text, curFont)) {
 4177         adj = 0;
 4178         } else
 4179 #endif
 4180         {
 4181         if_WIDE_OR_NARROW(screen, {
 4182             XChar2b temp[1];
 4183             temp[0].byte2 = LO_BYTE(*text);
 4184             temp[0].byte1 = HI_BYTE(*text);
 4185             width = XTextWidth16(curFont->fs, temp, 1);
 4186         }
 4187         , {
 4188             char temp[1];
 4189             temp[0] = (char) LO_BYTE(*text);
 4190             width = XTextWidth(curFont->fs, temp, 1);
 4191         });
 4192         adj = (FontWidth(screen) - width) / 2;
 4193         if (adj < 0)
 4194             adj = 0;
 4195         }
 4196         xtermFillCells(&recur, gc, x, y, (Cardinal) cells);
 4197         recur.draw_flags |= (NOBACKGROUND | CHARBYCHAR);
 4198         x = drawXtermText(&recur,
 4199                   gc, x + adj, y,
 4200                   text++, 1) - adj;
 4201     }
 4202 
 4203     return x;
 4204     }
 4205 #if OPT_BOX_CHARS
 4206     /*
 4207      * Draw some substitutions, if needed.  The font may not include the
 4208      * line-drawing set, or it may be incomplete (in which case we'll draw an
 4209      * empty space via xtermDrawBoxChar), or we may be told to force our
 4210      * line-drawing.
 4211      *
 4212      * The empty space is a special case which can be overridden with the
 4213      * showMissingGlyphs resource to produce an outline.  Not all fonts in
 4214      * "modern" (sic) X provide an empty space; some use a thick outline or
 4215      * something like the replacement character.  If you would rather not see
 4216      * that, you can set assumeAllChars.
 4217      */
 4218     if (!IsIcon(screen)
 4219     && !(recur.draw_flags & NOTRANSLATION)
 4220     && (!screen->fnt_boxes
 4221         || (FontIsIncomplete(curFont) && !screen->assume_all_chars)
 4222         || recur.xw->work.force_wideFont
 4223         || screen->force_box_chars)) {
 4224     /*
 4225      * Fill in missing box-characters.  Find regions without missing
 4226      * characters, and draw them calling ourselves recursively.  Draw
 4227      * missing characters via xtermDrawBoxChar().
 4228      */
 4229     int last, first = 0;
 4230     Bool drewBoxes = False;
 4231 
 4232     for (last = 0; last < (int) len; last++) {
 4233         unsigned ch = (unsigned) text[last];
 4234         Bool isMissing;
 4235         int ch_width;
 4236 #if OPT_WIDE_CHARS
 4237 
 4238         if (ch == HIDDEN_CHAR) {
 4239         if (last > first) {
 4240             XTermDraw param2 = recur;
 4241             param2.draw_flags |= NOTRANSLATION;
 4242             x = drawXtermText(&param2,
 4243                       gc,
 4244                       x, y,
 4245                       text + first,
 4246                       (unsigned) (last - first));
 4247         }
 4248         first = last + 1;
 4249         drewBoxes = True;
 4250         continue;
 4251         }
 4252         ch_width = CharWidth(screen, ch);
 4253         isMissing =
 4254         IsXtermMissingChar(screen, ch,
 4255                    ((recur.on_wide || ch_width > 1)
 4256                     && okFont(NormalWFont(screen)))
 4257                    ? WhichVFontData(screen, fWide)
 4258                    : curFont);
 4259 #else
 4260         isMissing = IsXtermMissingChar(screen, ch, curFont);
 4261         ch_width = 1;
 4262 #endif
 4263         /*
 4264          * If the character is not missing, but we're in wide-character
 4265          * mode and the character happens to be a wide-character that
 4266          * corresponds to the line-drawing set, allow the forceBoxChars
 4267          * resource (or menu entry) to force it to display using our
 4268          * tables.
 4269          */
 4270         if_OPT_WIDE_CHARS(screen, {
 4271         if (!isMissing
 4272             && TScreenOf(recur.xw)->force_box_chars) {
 4273             if (ch > 255
 4274             && ucs2dec(screen, ch) < 32) {
 4275             ch = ucs2dec(screen, ch);
 4276             isMissing = True;
 4277             } else if (ch < 32) {
 4278             isMissing = True;
 4279             }
 4280         }
 4281         });
 4282 
 4283         if (isMissing) {
 4284         if (last > first) {
 4285             XTermDraw param2 = recur;
 4286             param2.draw_flags |= NOTRANSLATION;
 4287             x = drawXtermText(&recur,
 4288                       gc,
 4289                       x, y,
 4290                       text + first,
 4291                       (unsigned) (last - first));
 4292         }
 4293 #if OPT_WIDE_CHARS
 4294         if (ch_width <= 0 && ch < 32)
 4295             ch_width = 1;   /* special case for line-drawing */
 4296         else if (ch_width < 0)
 4297             ch_width = 1;   /* special case for combining char */
 4298         if (!ucs_workaround(&recur, ch, gc, x, y)) {
 4299             xtermDrawBoxChar(&recur, ch, gc, x, y, ch_width, False);
 4300         }
 4301 #else
 4302         xtermDrawBoxChar(&recur, ch, gc, x, y, ch_width, False);
 4303 #endif
 4304         x += (ch_width * FontWidth(screen));
 4305         first = last + 1;
 4306         drewBoxes = True;
 4307         } else if (ch_width == 2
 4308                && recur.xw->work.force_wideFont
 4309                && !(recur.draw_flags & NOTRANSLATION)) {
 4310         XTermDraw param2 = recur;
 4311         param2.draw_flags |= NOTRANSLATION;
 4312         /*
 4313          * Not a "box" character, but a special case treated similarly.
 4314          */
 4315         (void) drawXtermText(&param2,
 4316                      gc,
 4317                      x, y,
 4318                      text + first,
 4319                      (unsigned) (1 + last - first));
 4320         x += (ch_width * FontWidth(screen));
 4321         first = last + 1;
 4322         drewBoxes = True;
 4323         }
 4324     }
 4325     if (last <= first) {
 4326         return x;
 4327     }
 4328     text += first;
 4329     len = (Cardinal) (last - first);
 4330     recur.draw_flags |= NOTRANSLATION;
 4331     if (drewBoxes) {
 4332         return drawXtermText(&recur,
 4333                  gc,
 4334                  x,
 4335                  y,
 4336                  text,
 4337                  len);
 4338     }
 4339     }
 4340 #endif /* OPT_BOX_CHARS */
 4341     /*
 4342      * Behave as if the font has (maybe Unicode-replacements for) drawing
 4343      * characters in the range 1-31 (either we were not asked to ignore them,
 4344      * or the caller made sure that there is none).
 4345      */
 4346 #if OPT_WIDE_ATTRS
 4347 #define AttrFlags() recur.attr_flags
 4348 #define DrawFlags() recur.draw_flags
 4349 #else
 4350 #define AttrFlags() (recur.attr_flags & DRAWX_MASK)
 4351 #define DrawFlags() (recur.draw_flags & (unsigned)~DRAWX_MASK)
 4352 #endif
 4353     TRACE(("drawtext%c[%4d,%4d] {%#x,%#x} (%d) %d:%s\n",
 4354        screen->cursor_state == OFF ? ' ' : '*',
 4355        y, x,
 4356        AttrFlags(),
 4357        DrawFlags(),
 4358        recur.this_chrset, len,
 4359        visibleIChars(text, len)));
 4360     if (screen->scale_height != 1.0f) {
 4361     xtermFillCells(&recur, gc, x, y, (Cardinal) len);
 4362     }
 4363     y += FontAscent(screen);
 4364 
 4365 #if OPT_WIDE_CHARS
 4366 
 4367     need_wide = False;
 4368     if (screen->wide_chars || screen->unicode_font) {
 4369     int src;
 4370     for (src = 0; src < (int) len; src++) {
 4371         IChar ch = text[src];
 4372         if (ch > 0xff) {
 4373         need_wide = True;
 4374         break;
 4375         }
 4376     }
 4377     }
 4378 
 4379     if (need_wide) {
 4380     XChar2b *buffer;
 4381     Bool needWide = False;
 4382     int src, dst;
 4383     Bool useBoldFont;
 4384     int ascent_adjust = 0;
 4385 
 4386     BumpTypedBuffer(XChar2b, len);
 4387     buffer = BfBuf(XChar2b);
 4388 
 4389     for (src = dst = 0; src < (int) len; src++) {
 4390         IChar ch = text[src];
 4391 
 4392         if (ch == HIDDEN_CHAR)
 4393         continue;
 4394 
 4395 #if OPT_BOX_CHARS
 4396         if ((screen->fnt_boxes == 1) && (ch >= 256)) {
 4397         unsigned part = ucs2dec(screen, ch);
 4398         if (part < 32)
 4399             ch = (IChar) part;
 4400         }
 4401 #endif
 4402 
 4403         if (!needWide
 4404         && !IsIcon(screen)
 4405         && ((recur.on_wide || CharWidth(screen, ch) > 1)
 4406             && okFont(NormalWFont(screen)))) {
 4407         needWide = True;
 4408         }
 4409 #if OPT_WIDER_ICHAR
 4410         /*
 4411          * bitmap-fonts are limited to 16-bits.
 4412          */
 4413         if (ch > NARROW_ICHAR) {
 4414         ch = 0;
 4415         }
 4416 #endif
 4417         buffer[dst].byte2 = LO_BYTE(ch);
 4418         buffer[dst].byte1 = HI_BYTE(ch);
 4419 #if OPT_MINI_LUIT
 4420 #define UCS2SBUF(value) buffer[dst].byte2 = LO_BYTE(value);\
 4421             buffer[dst].byte1 = HI_BYTE(value)
 4422 
 4423 #define Map2Sbuf(from,to) (text[src] == from) { UCS2SBUF(to); }
 4424 
 4425         if (screen->latin9_mode && !screen->utf8_mode && text[src] < 256) {
 4426 
 4427         /* see http://www.cs.tut.fi/~jkorpela/latin9.html */
 4428         /* *INDENT-OFF* */
 4429         if Map2Sbuf(0xa4, 0x20ac)
 4430         else if Map2Sbuf(0xa6, 0x0160)
 4431         else if Map2Sbuf(0xa8, 0x0161)
 4432         else if Map2Sbuf(0xb4, 0x017d)
 4433         else if Map2Sbuf(0xb8, 0x017e)
 4434         else if Map2Sbuf(0xbc, 0x0152)
 4435         else if Map2Sbuf(0xbd, 0x0153)
 4436         else if Map2Sbuf(0xbe, 0x0178)
 4437         /* *INDENT-ON* */
 4438 
 4439         }
 4440         if (screen->unicode_font
 4441         && (text[src] == ANSI_DEL ||
 4442             text[src] < ANSI_SPA)) {
 4443         unsigned ni = dec2ucs(screen,
 4444                       (unsigned) ((text[src] == ANSI_DEL)
 4445                           ? 0
 4446                           : text[src]));
 4447         UCS2SBUF(ni);
 4448         }
 4449 #endif /* OPT_MINI_LUIT */
 4450         ++dst;
 4451     }
 4452 
 4453     /*
 4454      * Check for special case where the bold font lacks glyphs found in the
 4455      * normal font, and drop down to normal fonts with overstriking to help
 4456      * show the actual characters.
 4457      */
 4458     useBoldFont = ((recur.attr_flags & BOLDATTR(screen)) != 0);
 4459     if (useBoldFont) {
 4460         XTermFonts *norm = 0;
 4461         XTermFonts *bold = 0;
 4462         Bool noBold, noNorm;
 4463 
 4464         (void) norm;
 4465         if (needWide && okFont(BoldWFont(screen))) {
 4466         norm = WhichVFontData(screen, fWide);
 4467         bold = WhichVFontData(screen, fWBold);
 4468         } else if (okFont(BoldFont(screen))) {
 4469         norm = WhichVFontData(screen, fNorm);
 4470         bold = WhichVFontData(screen, fBold);
 4471         } else {
 4472         useBoldFont = False;
 4473         }
 4474 
 4475         if (useBoldFont && FontIsIncomplete(bold)) {
 4476         for (src = 0; src < (int) len; src++) {
 4477             IChar ch = text[src];
 4478 
 4479             if (ch == HIDDEN_CHAR)
 4480             continue;
 4481 
 4482             noBold = IsXtermMissingChar(screen, ch, bold);
 4483             if (noBold) {
 4484             noNorm = IsXtermMissingChar(screen, ch, norm);
 4485             if (!noNorm) {
 4486                 useBoldFont = False;
 4487                 break;
 4488             }
 4489             }
 4490         }
 4491         }
 4492     }
 4493 
 4494     /* FIXME This is probably wrong. But it works. */
 4495     underline_len = len;
 4496 
 4497     /* Set the drawing font */
 4498     if (!(recur.draw_flags & (DOUBLEHFONT | DOUBLEWFONT))) {
 4499         VTwin *currentWin = WhichVWin(screen);
 4500         VTFontEnum fntId;
 4501         CgsEnum cgsId;
 4502         Pixel fg = getCgsFore(recur.xw, currentWin, gc);
 4503         Pixel bg = getCgsBack(recur.xw, currentWin, gc);
 4504 
 4505         if (needWide
 4506         && useBoldFont
 4507         && okFont(BoldWFont(screen))) {
 4508         fntId = fWBold;
 4509         cgsId = gcWBold;
 4510         } else if (needWide) {
 4511         fntId = fWide;
 4512         cgsId = gcWide;
 4513         } else if (useBoldFont) {
 4514         fntId = fBold;
 4515         cgsId = gcBold;
 4516         } else {
 4517         fntId = fNorm;
 4518         cgsId = gcNorm;
 4519         }
 4520 
 4521         setCgsFore(recur.xw, currentWin, cgsId, fg);
 4522         setCgsBack(recur.xw, currentWin, cgsId, bg);
 4523         gc = getCgsGC(recur.xw, currentWin, cgsId);
 4524 
 4525 #if OPT_WIDE_ATTRS
 4526 #if OPT_DEC_CHRSET
 4527         if (!(CSET_DOUBLE(recur.this_chrset) || (recur.draw_flags & DOUBLEWFONT)))
 4528 #endif
 4529         need_clipping = fixupItalics(&recur,
 4530                          gc,
 4531                          getCgsFont(recur.xw,
 4532                             currentWin, gc),
 4533                          y, x, font_width, len);
 4534 #endif
 4535         if (fntId != fNorm) {
 4536         XFontStruct *thisFp = WhichVFont(screen, fntId);
 4537         ascent_adjust = (thisFp->ascent
 4538                  - NormalFont(screen)->ascent);
 4539         if (thisFp->max_bounds.width ==
 4540             NormalFont(screen)->max_bounds.width * 2) {
 4541             underline_len = real_length = (Cardinal) (dst * 2);
 4542         } else if (cgsId == gcWide || cgsId == gcWBold) {
 4543             underline_len = real_length = (Cardinal) (dst * 2);
 4544             xtermFillCells(&recur,
 4545                    gc,
 4546                    x,
 4547                    y - thisFp->ascent,
 4548                    real_length);
 4549         }
 4550         }
 4551     }
 4552 
 4553     if (recur.draw_flags & NOBACKGROUND) {
 4554         XDrawString16(screen->display,
 4555               VDrawable(screen), gc,
 4556               x, y + y_shift + ascent_adjust,
 4557               buffer, dst);
 4558     } else if (dst <= MaxImageString) {
 4559         XDrawImageString16(screen->display,
 4560                    VDrawable(screen), gc,
 4561                    x, y + y_shift + ascent_adjust,
 4562                    buffer, dst);
 4563     } else {
 4564         int b_pos;
 4565         int b_max = MaxImageString;
 4566         for (b_pos = 0; b_pos < dst; b_pos += b_max) {
 4567         if (b_pos + b_max > dst)
 4568             b_max = (dst - b_pos);
 4569         XDrawImageString16(screen->display,
 4570                    VDrawable(screen), gc,
 4571                    x + (b_pos * FontWidth(screen)),
 4572                    y + y_shift + ascent_adjust,
 4573                    buffer + b_pos,
 4574                    b_max);
 4575         }
 4576     }
 4577 #if OPT_WIDE_ATTRS
 4578     if (need_clipping) {
 4579         endClipping(screen, gc);
 4580     }
 4581 #endif
 4582 
 4583     if ((recur.attr_flags & BOLDATTR(screen)) && (screen->enbolden || !useBoldFont)) {
 4584         if (!(recur.draw_flags & (DOUBLEWFONT | DOUBLEHFONT))) {
 4585         beginClipping(screen, gc, (Cardinal) font_width, len);
 4586         }
 4587         XDrawString16(screen->display, VDrawable(screen), gc,
 4588               x + 1,
 4589               y + y_shift + ascent_adjust,
 4590               buffer, dst);
 4591         if (!(recur.draw_flags & (DOUBLEWFONT | DOUBLEHFONT))) {
 4592         endClipping(screen, gc);
 4593         }
 4594     }
 4595 
 4596     } else
 4597 #endif /* OPT_WIDE_CHARS */
 4598     {
 4599     int length = (int) len; /* X should have used unsigned */
 4600 #if OPT_WIDE_CHARS
 4601     char *buffer;
 4602     int dst;
 4603 
 4604     BumpTypedBuffer(char, len);
 4605     buffer = BfBuf(char);
 4606 
 4607     for (dst = 0; dst < length; ++dst)
 4608         buffer[dst] = (char) LO_BYTE(text[dst]);
 4609 #else
 4610     char *buffer = (char *) text;
 4611 #endif
 4612 
 4613 #if OPT_WIDE_ATTRS
 4614 #if OPT_DEC_CHRSET
 4615     if (!(CSET_DOUBLE(recur.this_chrset) || (recur.draw_flags & DOUBLEWFONT)))
 4616 #endif
 4617         need_clipping = fixupItalics(&recur, gc, curFont,
 4618                      y, x, font_width, len);
 4619 #endif
 4620 
 4621     if (recur.draw_flags & NOBACKGROUND) {
 4622         XDrawString(screen->display, VDrawable(screen), gc,
 4623             x, y + y_shift, buffer, length);
 4624     } else if (length <= MaxImageString) {
 4625         XDrawImageString(screen->display, VDrawable(screen), gc,
 4626                  x, y + y_shift, buffer, length);
 4627     } else {
 4628         int b_pos;
 4629         int b_max = MaxImageString;
 4630         for (b_pos = 0; b_pos < length; b_pos += b_max) {
 4631         if (b_pos + b_max > length)
 4632             b_max = (length - b_pos);
 4633         XDrawImageString(screen->display,
 4634                  VDrawable(screen), gc,
 4635                  x + (b_pos * FontWidth(screen)),
 4636                  y + y_shift,
 4637                  buffer + b_pos,
 4638                  b_max);
 4639         }
 4640     }
 4641 
 4642 #if OPT_WIDE_ATTRS
 4643     if (need_clipping) {
 4644         endClipping(screen, gc);
 4645     }
 4646 #endif
 4647     underline_len = (Cardinal) length;
 4648     if ((recur.attr_flags & BOLDATTR(screen)) && screen->enbolden) {
 4649         if (!(recur.draw_flags & (DOUBLEWFONT | DOUBLEHFONT))) {
 4650         beginClipping(screen, gc, font_width, length);
 4651         }
 4652         XDrawString(screen->display, VDrawable(screen), gc,
 4653             x + 1, y + y_shift, buffer, length);
 4654         if (!(recur.draw_flags & (DOUBLEWFONT | DOUBLEHFONT))) {
 4655         endClipping(screen, gc);
 4656         }
 4657     }
 4658     }
 4659 
 4660     drawUnderline(recur.xw,
 4661           gc,
 4662           recur.attr_flags,
 4663           underline_len,
 4664           font_width,
 4665           x,
 4666           y + y_shift,
 4667           did_ul);
 4668 
 4669     x += ((int) real_length) * FontWidth(screen);
 4670     return x;
 4671 }
 4672 
 4673 #if OPT_WIDE_CHARS
 4674 /*
 4675  * Allocate buffer - workaround for wide-character interfaces.
 4676  */
 4677 void
 4678 allocXtermChars(ScrnPtr *buffer, Cardinal length)
 4679 {
 4680     if (*buffer == 0) {
 4681     *buffer = (ScrnPtr) XtMalloc(length);
 4682     } else {
 4683     *buffer = (ScrnPtr) XtRealloc((char *) *buffer, length);
 4684     }
 4685 }
 4686 #endif
 4687 
 4688 /* set up size hints for window manager; min 1 char by 1 char */
 4689 void
 4690 xtermSizeHints(XtermWidget xw, int scrollbarWidth)
 4691 {
 4692     TScreen *screen = TScreenOf(xw);
 4693 
 4694     TRACE(("xtermSizeHints\n"));
 4695     TRACE(("   border    %d\n", xw->core.border_width));
 4696     TRACE(("   scrollbar %d\n", scrollbarWidth));
 4697 
 4698     xw->hints.base_width = 2 * screen->border + scrollbarWidth;
 4699     xw->hints.base_height = 2 * screen->border;
 4700 
 4701 #if OPT_TOOLBAR
 4702     TRACE(("   toolbar   %d\n", ToolbarHeight(xw)));
 4703 
 4704     xw->hints.base_height += ToolbarHeight(xw);
 4705     xw->hints.base_height += BorderWidth(xw) * 2;
 4706     xw->hints.base_width += BorderWidth(xw) * 2;
 4707 #endif
 4708 
 4709     if (xw->misc.resizeByPixel) {
 4710     xw->hints.width_inc = 1;
 4711     xw->hints.height_inc = 1;
 4712     } else {
 4713     xw->hints.width_inc = FontWidth(screen);
 4714     xw->hints.height_inc = FontHeight(screen);
 4715     }
 4716     xw->hints.min_width = xw->hints.base_width + xw->hints.width_inc;
 4717     xw->hints.min_height = xw->hints.base_height + xw->hints.height_inc;
 4718 
 4719     xw->hints.width = MaxCols(screen) * FontWidth(screen) + xw->hints.min_width;
 4720     xw->hints.height = MaxRows(screen) * FontHeight(screen) + xw->hints.min_height;
 4721 
 4722     xw->hints.flags |= (PSize | PBaseSize | PMinSize | PResizeInc);
 4723 
 4724     TRACE_HINTS(&(xw->hints));
 4725 }
 4726 
 4727 void
 4728 getXtermSizeHints(XtermWidget xw)
 4729 {
 4730     TScreen *screen = TScreenOf(xw);
 4731     long supp;
 4732 
 4733     if (!XGetWMNormalHints(screen->display, VShellWindow(xw),
 4734                &xw->hints, &supp))
 4735     memset(&xw->hints, 0, sizeof(xw->hints));
 4736     TRACE_HINTS(&(xw->hints));
 4737 }
 4738 
 4739 CgsEnum
 4740 whichXtermCgs(XtermWidget xw, unsigned attr_flags, Bool hilite)
 4741 {
 4742     TScreen *screen = TScreenOf(xw);
 4743     CgsEnum cgsId = gcMAX;
 4744 
 4745     if (ReverseOrHilite(screen, attr_flags, hilite)) {
 4746     if (attr_flags & BOLDATTR(screen)) {
 4747         cgsId = gcBoldReverse;
 4748     } else {
 4749         cgsId = gcNormReverse;
 4750     }
 4751     } else {
 4752     if (attr_flags & BOLDATTR(screen)) {
 4753         cgsId = gcBold;
 4754     } else {
 4755         cgsId = gcNorm;
 4756     }
 4757     }
 4758     return cgsId;
 4759 }
 4760 
 4761 /*
 4762  * Returns a GC, selected according to the font (reverse/bold/normal) that is
 4763  * required for the current position (implied).  The GC is updated with the
 4764  * current screen foreground and background colors.
 4765  */
 4766 GC
 4767 updatedXtermGC(XtermWidget xw, unsigned attr_flags, CellColor fg_bg,
 4768            Bool hilite)
 4769 {
 4770     TScreen *screen = TScreenOf(xw);
 4771     VTwin *win = WhichVWin(screen);
 4772     CgsEnum cgsId = whichXtermCgs(xw, attr_flags, hilite);
 4773     Pixel my_fg = extract_fg(xw, fg_bg, attr_flags);
 4774     Pixel my_bg = extract_bg(xw, fg_bg, attr_flags);
 4775     Pixel fg_pix = getXtermFG(xw, attr_flags, (int) my_fg);
 4776     Pixel bg_pix = getXtermBG(xw, attr_flags, (int) my_bg);
 4777     Pixel xx_pix;
 4778 #if OPT_HIGHLIGHT_COLOR
 4779     Boolean reverse2 = ((attr_flags & INVERSE) && hilite);
 4780     Pixel selbg_pix = T_COLOR(screen, HIGHLIGHT_BG);
 4781     Pixel selfg_pix = T_COLOR(screen, HIGHLIGHT_FG);
 4782     Boolean always = screen->hilite_color;
 4783     Boolean use_selbg = (Boolean) (always &&
 4784                    isNotForeground(xw, fg_pix, bg_pix, selbg_pix));
 4785     Boolean use_selfg = (Boolean) (always &&
 4786                    isNotBackground(xw, fg_pix, bg_pix, selfg_pix));
 4787 #endif
 4788 
 4789     (void) fg_bg;
 4790     (void) my_bg;
 4791     (void) my_fg;
 4792 
 4793     /*
 4794      * Discard video attributes overridden by colorXXXMode's.
 4795      */
 4796     checkVeryBoldColors(attr_flags, my_fg);
 4797 
 4798     if (ReverseOrHilite(screen, attr_flags, hilite)) {
 4799 #if OPT_HIGHLIGHT_COLOR
 4800     if (!screen->hilite_color) {
 4801         if (selbg_pix != T_COLOR(screen, TEXT_FG)
 4802         && selbg_pix != fg_pix
 4803         && selbg_pix != bg_pix
 4804         && selbg_pix != xw->dft_foreground) {
 4805         bg_pix = fg_pix;
 4806         fg_pix = selbg_pix;
 4807         }
 4808     }
 4809 #endif
 4810     EXCHANGE(fg_pix, bg_pix, xx_pix);
 4811 #if OPT_HIGHLIGHT_COLOR
 4812     if (screen->hilite_color) {
 4813         if (screen->hilite_reverse) {
 4814         if (use_selbg) {
 4815             if (use_selfg) {
 4816             bg_pix = fg_pix;
 4817             } else {
 4818             fg_pix = bg_pix;
 4819             bg_pix = selbg_pix;
 4820             }
 4821         }
 4822         if (use_selfg)
 4823             fg_pix = selfg_pix;
 4824         }
 4825     }
 4826 #endif
 4827     } else if ((attr_flags & INVERSE) && hilite) {
 4828 #if OPT_HIGHLIGHT_COLOR
 4829     if (!screen->hilite_color) {
 4830         if (selbg_pix != T_COLOR(screen, TEXT_FG)
 4831         && selbg_pix != fg_pix
 4832         && selbg_pix != bg_pix
 4833         && selbg_pix != xw->dft_foreground) {
 4834         bg_pix = fg_pix;
 4835         fg_pix = selbg_pix;
 4836         }
 4837     }
 4838 #endif
 4839     /* double-reverse... EXCHANGE(fg_pix, bg_pix, xx_pix); */
 4840 #if OPT_HIGHLIGHT_COLOR
 4841     if (screen->hilite_color) {
 4842         if (screen->hilite_reverse) {
 4843         if (use_selbg) {
 4844             if (use_selfg ^ reverse2) {
 4845             bg_pix = fg_pix;
 4846             } else {
 4847             fg_pix = bg_pix;
 4848             }
 4849             if (reverse2) {
 4850             fg_pix = selbg_pix;
 4851             } else {
 4852             bg_pix = selbg_pix;
 4853             }
 4854         }
 4855         if (use_selfg) {
 4856             if (reverse2) {
 4857             bg_pix = selfg_pix;
 4858             } else {
 4859             fg_pix = selfg_pix;
 4860             }
 4861         }
 4862         }
 4863     }
 4864 #endif
 4865     }
 4866 #if OPT_HIGHLIGHT_COLOR
 4867     if (!screen->hilite_color || !screen->hilite_reverse) {
 4868     if (hilite && !screen->hilite_reverse) {
 4869         if (use_selbg) {
 4870         if (reverse2)
 4871             fg_pix = selbg_pix;
 4872         else
 4873             bg_pix = selbg_pix;
 4874         }
 4875         if (use_selfg) {
 4876         if (reverse2)
 4877             bg_pix = selfg_pix;
 4878         else
 4879             fg_pix = selfg_pix;
 4880         }
 4881     }
 4882     }
 4883 #endif
 4884 
 4885 #if OPT_BLINK_TEXT
 4886     if ((screen->blink_state == ON) &&
 4887     (!screen->blink_as_bold) &&
 4888     (attr_flags & BLINK)) {
 4889     fg_pix = bg_pix;
 4890     }
 4891 #endif
 4892 
 4893     setCgsFore(xw, win, cgsId, fg_pix);
 4894     setCgsBack(xw, win, cgsId, bg_pix);
 4895     return getCgsGC(xw, win, cgsId);
 4896 }
 4897 
 4898 /*
 4899  * Resets the foreground/background of the GC returned by 'updatedXtermGC()'
 4900  * to the values that would be set in SGR_Foreground and SGR_Background. This
 4901  * duplicates some logic, but only modifies 1/4 as many GC's.
 4902  */
 4903 void
 4904 resetXtermGC(XtermWidget xw, unsigned attr_flags, Bool hilite)
 4905 {
 4906     TScreen *screen = TScreenOf(xw);
 4907     VTwin *win = WhichVWin(screen);
 4908     CgsEnum cgsId = whichXtermCgs(xw, attr_flags, hilite);
 4909     Pixel fg_pix = getXtermFG(xw, attr_flags, xw->cur_foreground);
 4910     Pixel bg_pix = getXtermBG(xw, attr_flags, xw->cur_background);
 4911 
 4912     checkVeryBoldColors(attr_flags, xw->cur_foreground);
 4913 
 4914     if (ReverseOrHilite(screen, attr_flags, hilite)) {
 4915     setCgsFore(xw, win, cgsId, bg_pix);
 4916     setCgsBack(xw, win, cgsId, fg_pix);
 4917     } else {
 4918     setCgsFore(xw, win, cgsId, fg_pix);
 4919     setCgsBack(xw, win, cgsId, bg_pix);
 4920     }
 4921 }
 4922 
 4923 #if OPT_ISO_COLORS
 4924 /*
 4925  * Extract the foreground-color index from a color pair.
 4926  * If we've got BOLD or UNDERLINE color-mode active, those will be used.
 4927  */
 4928 Pixel
 4929 extract_fg(XtermWidget xw, CellColor color, unsigned attr_flags)
 4930 {
 4931     unsigned fg = ExtractForeground(color);
 4932 
 4933     if (TScreenOf(xw)->colorAttrMode
 4934     || (fg == ExtractBackground(color))) {
 4935     fg = MapToColorMode(fg, TScreenOf(xw), attr_flags);
 4936     }
 4937     return fg;
 4938 }
 4939 
 4940 /*
 4941  * Extract the background-color index from a color pair.
 4942  * If we've got INVERSE color-mode active, that will be used.
 4943  */
 4944 Pixel
 4945 extract_bg(XtermWidget xw, CellColor color, unsigned attr_flags)
 4946 {
 4947     unsigned bg = ExtractBackground(color);
 4948 
 4949     if (TScreenOf(xw)->colorAttrMode
 4950     || (bg == ExtractForeground(color))) {
 4951     if (TScreenOf(xw)->colorRVMode && (attr_flags & INVERSE))
 4952         bg = COLOR_RV;
 4953     }
 4954     return bg;
 4955 }
 4956 
 4957 /*
 4958  * Combine the current foreground and background into a single 8-bit number.
 4959  * Note that we're storing the SGR foreground, since cur_foreground may be set
 4960  * to COLOR_UL, COLOR_BD or COLOR_BL, which would make the code larger than 8
 4961  * bits.
 4962  *
 4963  * This assumes that fg/bg are equal when we override with one of the special
 4964  * attribute colors.
 4965  */
 4966 CellColor
 4967 makeColorPair(XtermWidget xw)
 4968 {
 4969     CellColor result;
 4970 
 4971 #if OPT_DIRECT_COLOR
 4972     result.fg = xw->cur_foreground;
 4973     result.bg = xw->cur_background;
 4974 #else
 4975     int fg = xw->cur_foreground;
 4976     int bg = xw->cur_background;
 4977     unsigned my_bg = okIndexedColor(bg) ? (unsigned) bg : 0;
 4978     unsigned my_fg = okIndexedColor(fg) ? (unsigned) fg : my_bg;
 4979 
 4980     result = (CellColor) (my_fg | (my_bg << COLOR_BITS));
 4981 #endif
 4982 
 4983     return result;
 4984 }
 4985 
 4986 /*
 4987  * Using the "current" SGR background, clear a rectangle.
 4988  */
 4989 void
 4990 ClearCurBackground(XtermWidget xw,
 4991            int top,
 4992            int left,
 4993            unsigned height,
 4994            unsigned width,
 4995            unsigned fw)
 4996 {
 4997     TScreen *screen = TScreenOf(xw);
 4998     int actual_rows = PlusStatusLine(screen, screen->max_row + 1);
 4999     Boolean visible = (((int) width > 0)
 5000                && ((left + (int) width) <= screen->max_col + 1)
 5001                && (((int) height + top) <= actual_rows));
 5002 
 5003     TRACE(("ClearCurBackground %d,%d %dx%d%s with %d %s\n",
 5004        top, left, height, width,
 5005        IsStatusShown(screen) ? "*" : "",
 5006        xw->cur_background,
 5007        visible ? "(ok)" : "(err)"));
 5008 
 5009     if (VWindow(screen) && visible) {
 5010     set_background(xw, xw->cur_background);
 5011 
 5012     xtermClear2(xw,
 5013             CursorX2(screen, left, fw),
 5014             CursorY2(screen, top),
 5015             (width * fw),
 5016             (height * (unsigned) FontHeight(screen)));
 5017 
 5018     set_background(xw, -1);
 5019     }
 5020 }
 5021 #endif /* OPT_ISO_COLORS */
 5022 
 5023 Pixel
 5024 getXtermBackground(XtermWidget xw, unsigned attr_flags, int color)
 5025 {
 5026     Pixel result = T_COLOR(TScreenOf(xw), TEXT_BG);
 5027 
 5028 #if OPT_ISO_COLORS
 5029     if (color >= 0) {
 5030     if_OPT_DIRECT_COLOR2_else(TScreenOf(xw), (attr_flags & ATR_DIRECT_BG), {
 5031         result = (Pixel) color;
 5032     }) if ((attr_flags & BG_COLOR) && (color < MAXCOLORS)) {
 5033         result = GET_COLOR_RES(xw, TScreenOf(xw)->Acolors[color]);
 5034     }
 5035     }
 5036 #else
 5037     (void) attr_flags;
 5038     (void) color;
 5039 #endif
 5040     return result;
 5041 }
 5042 
 5043 #if OPT_ISO_COLORS && OPT_WIDE_ATTRS
 5044 #if OPT_SGR2_HASH
 5045 typedef struct _DimColorHT {
 5046     Pixel org;
 5047     Pixel dim;
 5048 } DimColorHT;
 5049 
 5050 static unsigned
 5051 jhash1(unsigned char *key, size_t len)
 5052 {
 5053     unsigned hash;
 5054     size_t i;
 5055 
 5056     for (hash = 0, i = 0; i < len; ++i) {
 5057     hash += key[i];
 5058     hash += (hash << 10);
 5059     hash ^= (hash >> 6);
 5060     }
 5061     hash += (hash << 3);
 5062     hash ^= (hash >> 11);
 5063     hash += (hash << 15);
 5064     return hash;
 5065 }
 5066 
 5067 static unsigned
 5068 computeFaint(XtermWidget xw, unsigned value, unsigned compare)
 5069 {
 5070     TScreen *screen = TScreenOf(xw);
 5071     if (screen->faint_relative) {
 5072     value = (unsigned) ((value + compare) / 2);
 5073     } else {
 5074     value = (unsigned) ((2 * value) / 3);
 5075     }
 5076     return value;
 5077 }
 5078 #endif /* OPT_SGR2_HASH */
 5079 #endif /* OPT_ISO_COLORS && OPT_WIDE_ATTRS */
 5080 
 5081 Pixel
 5082 getXtermForeground(XtermWidget xw, unsigned attr_flags, int color)
 5083 {
 5084     Pixel result = T_COLOR(TScreenOf(xw), TEXT_FG);
 5085 
 5086 #if OPT_ISO_COLORS
 5087     if_OPT_DIRECT_COLOR2_else(TScreenOf(xw), (attr_flags & ATR_DIRECT_FG), {
 5088     result = (Pixel) color;
 5089     })
 5090     if ((attr_flags & FG_COLOR) &&
 5091         (color >= 0 && color < MAXCOLORS)) {
 5092     result = GET_COLOR_RES(xw, TScreenOf(xw)->Acolors[color]);
 5093     }
 5094 #else
 5095     (void) attr_flags;
 5096     (void) color;
 5097 #endif
 5098 
 5099 #if OPT_ISO_COLORS && OPT_WIDE_ATTRS
 5100     if ((attr_flags & ATR_FAINT)) {
 5101 #if OPT_SGR2_HASH
 5102 #define DIM_IT(n) work.n = (unsigned short) computeFaint(xw, work.n, bkg.n)
 5103 #define SizeOfHT ((unsigned) sizeof(unsigned long) * CHAR_BIT)
 5104     static DimColorHT ht[SizeOfHT];
 5105     Pixel bg = T_COLOR(TScreenOf(xw), TEXT_BG);
 5106     XColor work;
 5107     Pixel p;
 5108 
 5109     if ((color >= 0)
 5110         || (result != (Pixel) color)) {
 5111         static unsigned long have = 0;
 5112         static Boolean have_bg = False;
 5113         static XColor bkg;
 5114 
 5115         /* cache bkg color in r/g/b */
 5116         if (!have_bg || bg != bkg.pixel) {
 5117         bkg.pixel = bg;
 5118         have_bg = QueryOneColor(xw, &bkg);
 5119         have = 0;   /* invalidate color cache */
 5120         }
 5121         if (have_bg) {
 5122         unsigned hv;
 5123         hv = jhash1((unsigned char *) &result, sizeof(result));
 5124         hv %= SizeOfHT;
 5125 
 5126         if ((have & (1UL << hv))
 5127             && ht[hv].org == result) {
 5128             result = ht[hv].dim;    /* return cached color */
 5129         } else {
 5130             work.pixel = result;
 5131             if (QueryOneColor(xw, &work)) {
 5132             DIM_IT(red);
 5133             DIM_IT(green);
 5134             DIM_IT(blue);
 5135             p = result;
 5136             if (allocateBestRGB(xw, &work)) {
 5137                 result = work.pixel;
 5138             }
 5139 
 5140             /* cache the result */
 5141             have |= (1UL << hv);
 5142             ht[hv].org = p;
 5143             ht[hv].dim = result;
 5144             }
 5145         }
 5146         }
 5147     }
 5148 #else /* !OPT_SGR2_HASH */
 5149 #define DIM_IT(n) work.n = (unsigned short) ((2 * (unsigned)work.n) / 3)
 5150     static Pixel last_in;
 5151     static Pixel last_out;
 5152     if ((result != last_in)
 5153         && ((color >= 0)
 5154         || (result != (Pixel) color))) {
 5155         XColor work;
 5156         last_in = result;
 5157         work.pixel = result;
 5158         if (QueryOneColor(xw, &work)) {
 5159         DIM_IT(red);
 5160         DIM_IT(green);
 5161         DIM_IT(blue);
 5162         if (allocateBestRGB(xw, &work)) {
 5163             result = work.pixel;
 5164         }
 5165         }
 5166         last_out = result;
 5167     } else {
 5168         result = last_out;
 5169     }
 5170 #endif /* OPT_SGR2_HASH */
 5171     }
 5172 #endif
 5173     return result;
 5174 }
 5175 
 5176 /*
 5177  * Returns a single base character for the given cell.
 5178  */
 5179 unsigned
 5180 getXtermCell(TScreen *screen, int row, int col)
 5181 {
 5182     CLineData *ld = getLineData(screen, row);
 5183 
 5184     return ((ld && (col < (int) ld->lineSize))
 5185         ? ld->charData[col]
 5186         : (unsigned) ' ');
 5187 }
 5188 
 5189 /*
 5190  * Sets a single base character for the given cell.
 5191  */
 5192 void
 5193 putXtermCell(TScreen *screen, int row, int col, int ch)
 5194 {
 5195     LineData *ld = getLineData(screen, row);
 5196 
 5197     if (ld && (col < (int) ld->lineSize)) {
 5198     ld->charData[col] = (CharData) ch;
 5199     if_OPT_WIDE_CHARS(screen, {
 5200         size_t off;
 5201         for_each_combData(off, ld) {
 5202         ld->combData[off][col] = 0;
 5203         }
 5204     });
 5205     }
 5206 }
 5207 
 5208 #if OPT_WIDE_CHARS
 5209 /*
 5210  * Add a combining character for the given cell
 5211  */
 5212 void
 5213 addXtermCombining(TScreen *screen, int row, int col, unsigned ch)
 5214 {
 5215     if (ch != 0) {
 5216     LineData *ld = getLineData(screen, row);
 5217     size_t off;
 5218 
 5219     TRACE(("addXtermCombining %d,%d U+%04X (%d)\n",
 5220            row, col, ch, CharWidth(screen, ch)));
 5221 
 5222     for_each_combData(off, ld) {
 5223         if (!ld->combData[off][col]) {
 5224         ld->combData[off][col] = (CharData) ch;
 5225         break;
 5226         }
 5227     }
 5228     }
 5229 }
 5230 
 5231 unsigned
 5232 getXtermCombining(TScreen *screen, int row, int col, int off)
 5233 {
 5234     CLineData *ld = getLineData(screen, row);
 5235     return (ld->combSize ? ld->combData[off][col] : 0U);
 5236 }
 5237 #endif
 5238 
 5239 void
 5240 update_keyboard_type(void)
 5241 {
 5242     update_delete_del();
 5243     update_tcap_fkeys();
 5244     update_old_fkeys();
 5245     update_hp_fkeys();
 5246     update_sco_fkeys();
 5247     update_sun_fkeys();
 5248     update_sun_kbd();
 5249 }
 5250 
 5251 void
 5252 set_keyboard_type(XtermWidget xw, xtermKeyboardType type, Bool set)
 5253 {
 5254     xtermKeyboardType save = xw->keyboard.type;
 5255 
 5256     TRACE(("set_keyboard_type(%s, %s) currently %s\n",
 5257        visibleKeyboardType(type),
 5258        BtoS(set),
 5259        visibleKeyboardType(xw->keyboard.type)));
 5260     if (set) {
 5261     xw->keyboard.type = type;
 5262     } else {
 5263     xw->keyboard.type = keyboardIsDefault;
 5264     }
 5265 
 5266     if (save != xw->keyboard.type) {
 5267     update_keyboard_type();
 5268     }
 5269 }
 5270 
 5271 void
 5272 toggle_keyboard_type(XtermWidget xw, xtermKeyboardType type)
 5273 {
 5274     xtermKeyboardType save = xw->keyboard.type;
 5275 
 5276     TRACE(("toggle_keyboard_type(%s) currently %s\n",
 5277        visibleKeyboardType(type),
 5278        visibleKeyboardType(xw->keyboard.type)));
 5279     if (xw->keyboard.type == type) {
 5280     xw->keyboard.type = keyboardIsDefault;
 5281     } else {
 5282     xw->keyboard.type = type;
 5283     }
 5284 
 5285     if (save != xw->keyboard.type) {
 5286     update_keyboard_type();
 5287     }
 5288 }
 5289 
 5290 const char *
 5291 visibleKeyboardType(xtermKeyboardType type)
 5292 {
 5293     const char *result = "?";
 5294     switch (type) {
 5295     CASETYPE(keyboardIsLegacy); /* bogus vt220 codes for F1-F4, etc. */
 5296     CASETYPE(keyboardIsDefault);
 5297     CASETYPE(keyboardIsHP);
 5298     CASETYPE(keyboardIsSCO);
 5299     CASETYPE(keyboardIsSun);
 5300     CASETYPE(keyboardIsTermcap);
 5301     CASETYPE(keyboardIsVT220);
 5302     }
 5303     return result;
 5304 }
 5305 
 5306 static void
 5307 init_keyboard_type(XtermWidget xw, xtermKeyboardType type, Bool set)
 5308 {
 5309     TRACE(("init_keyboard_type(%s, %s) currently %s\n",
 5310        visibleKeyboardType(type),
 5311        BtoS(set),
 5312        visibleKeyboardType(xw->keyboard.type)));
 5313     if (set) {
 5314     /*
 5315      * Check for conflicts, e.g., if someone asked for both Sun and HP
 5316      * function keys.
 5317      */
 5318     if (guard_keyboard_type) {
 5319         xtermWarning("Conflicting keyboard type option (%s/%s)\n",
 5320              visibleKeyboardType(xw->keyboard.type),
 5321              visibleKeyboardType(type));
 5322     }
 5323     xw->keyboard.type = type;
 5324     guard_keyboard_type = True;
 5325     update_keyboard_type();
 5326     }
 5327 }
 5328 
 5329 /*
 5330  * If the keyboardType resource is set, use that, overriding the individual
 5331  * boolean resources for different keyboard types.
 5332  */
 5333 void
 5334 decode_keyboard_type(XtermWidget xw, XTERM_RESOURCE * rp)
 5335 {
 5336 #define DATA(n, t, f) { n, t, XtOffsetOf(XTERM_RESOURCE, f) }
 5337 #define FLAG(n) *(Boolean *)(((char *)rp) + table[n].offset)
 5338     static struct {
 5339     const char *name;
 5340     xtermKeyboardType type;
 5341     unsigned offset;
 5342     } table[] = {
 5343     DATA(NAME_OLD_KT, keyboardIsLegacy, oldKeyboard),
 5344 #if OPT_HP_FUNC_KEYS
 5345         DATA(NAME_HP_KT, keyboardIsHP, hpFunctionKeys),
 5346 #endif
 5347 #if OPT_SCO_FUNC_KEYS
 5348         DATA(NAME_SCO_KT, keyboardIsSCO, scoFunctionKeys),
 5349 #endif
 5350 #if OPT_SUN_FUNC_KEYS
 5351         DATA(NAME_SUN_KT, keyboardIsSun, sunFunctionKeys),
 5352 #endif
 5353 #if OPT_SUNPC_KBD
 5354         DATA(NAME_VT220_KT, keyboardIsVT220, sunKeyboard),
 5355 #endif
 5356 #if OPT_TCAP_FKEYS
 5357         DATA(NAME_TCAP_KT, keyboardIsTermcap, termcapKeys),
 5358 #endif
 5359     };
 5360     Cardinal n;
 5361     TScreen *screen = TScreenOf(xw);
 5362 
 5363     TRACE(("decode_keyboard_type(%s)\n", rp->keyboardType));
 5364     if (!x_strcasecmp(rp->keyboardType, "unknown")) {
 5365     /*
 5366      * Let the individual resources comprise the keyboard-type.
 5367      */
 5368     for (n = 0; n < XtNumber(table); ++n)
 5369         init_keyboard_type(xw, table[n].type, FLAG(n));
 5370     } else if (!x_strcasecmp(rp->keyboardType, "default")) {
 5371     /*
 5372      * Set the keyboard-type to the Sun/PC type, allowing modified
 5373      * function keys, etc.
 5374      */
 5375     for (n = 0; n < XtNumber(table); ++n)
 5376         init_keyboard_type(xw, table[n].type, False);
 5377     } else {
 5378     Bool found = False;
 5379 
 5380     /*
 5381      * Special case: oldXtermFKeys should have been like the others.
 5382      */
 5383     if (!x_strcasecmp(rp->keyboardType, NAME_OLD_KT)) {
 5384         TRACE(("special case, setting oldXtermFKeys\n"));
 5385         screen->old_fkeys = True;
 5386         screen->old_fkeys0 = True;
 5387     }
 5388 
 5389     /*
 5390      * Choose an individual keyboard type.
 5391      */
 5392     for (n = 0; n < XtNumber(table); ++n) {
 5393         if (!x_strcasecmp(rp->keyboardType, table[n].name + 1))