"Fossies" - the Fresh Open Source Software Archive

Member "xterm-379/button.c" (13 Feb 2023, 156745 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 "button.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 377_vs_379.

    1 /* $XTermId: button.c,v 1.653 2023/02/13 22:34:51 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 /*
   56 button.c    Handles button events in the terminal emulator.
   57         does cut/paste operations, change modes via menu,
   58         passes button events through to some applications.
   59                 J. Gettys.
   60 */
   61 
   62 #include <xterm.h>
   63 
   64 #include <stdio.h>
   65 #include <ctype.h>
   66 #include <assert.h>
   67 
   68 #include <X11/Xatom.h>
   69 #include <X11/Xmu/Atoms.h>
   70 #include <X11/Xmu/StdSel.h>
   71 
   72 #include <xutf8.h>
   73 #include <fontutils.h>
   74 
   75 #include <data.h>
   76 #include <error.h>
   77 #include <menu.h>
   78 #include <charclass.h>
   79 #include <xstrings.h>
   80 
   81 #if OPT_SELECT_REGEX
   82 #if defined(HAVE_PCRE2POSIX_H)
   83 #include <pcre2posix.h>
   84 
   85 /* pcre2 used to provide its "POSIX" entrypoints using the same names as the
   86  * standard ones in the C runtime, but that never worked because the linker
   87  * would use the C runtime.  Debian patched the library to fix this symbol
   88  * conflict, but overlooked the header file, and Debian's patch was made
   89  * obsolete when pcre2 was changed early in 2019 to provide different names.
   90  *
   91  * Here is a workaround to make the older version of Debian's package work.
   92  */
   93 #if !defined(PCRE2regcomp) && defined(HAVE_PCRE2REGCOMP)
   94 
   95 #undef regcomp
   96 #undef regexec
   97 #undef regfree
   98 
   99 #ifdef __cplusplus
  100 extern "C" {
  101 #endif
  102     PCRE2POSIX_EXP_DECL int PCRE2regcomp(regex_t *, const char *, int);
  103     PCRE2POSIX_EXP_DECL int PCRE2regexec(const regex_t *, const char *, size_t,
  104                      regmatch_t *, int);
  105     PCRE2POSIX_EXP_DECL void PCRE2regfree(regex_t *);
  106 #ifdef __cplusplus
  107 }               /* extern "C" */
  108 #endif
  109 #define regcomp(r,s,n)          PCRE2regcomp(r,s,n)
  110 #define regexec(r,s,n,m,x)      PCRE2regexec(r,s,n,m,x)
  111 #define regfree(r)              PCRE2regfree(r)
  112 #endif
  113 /* end workaround... */
  114 #elif defined(HAVE_PCREPOSIX_H)
  115 #include <pcreposix.h>
  116 #else /* POSIX regex.h */
  117 #include <sys/types.h>
  118 #include <regex.h>
  119 #endif
  120 #endif /* OPT_SELECT_REGEX */
  121 
  122 #ifdef HAVE_X11_TRANSLATEI_H
  123 #include <X11/ConvertI.h>
  124 #include <X11/TranslateI.h>
  125 #else
  126 extern String _XtPrintXlations(Widget w,
  127                    XtTranslations xlations,
  128                    Widget accelWidget,
  129                    _XtBoolean includeRHS);
  130 #endif
  131 
  132 #define PRIMARY_NAME    "PRIMARY"
  133 #define CLIPBOARD_NAME  "CLIPBOARD"
  134 #define SECONDARY_NAME  "SECONDARY"
  135 
  136 #define AtomToSelection(d,n) \
  137          (((n) == XA_CLIPBOARD(d)) \
  138           ? CLIPBOARD_CODE \
  139           : (((n) == XA_SECONDARY) \
  140              ? SECONDARY_CODE \
  141              : PRIMARY_CODE))
  142 
  143 #define isSelectionCode(n) ((n) >= PRIMARY_CODE)
  144 #define CutBufferToCode(n) ((n) +  MAX_SELECTION_CODES)
  145 #define okSelectionCode(n) (isSelectionCode(n) ? (n) : PRIMARY_CODE)
  146 
  147 #if OPT_WIDE_CHARS
  148 #include <ctype.h>
  149 #include <wcwidth.h>
  150 #else
  151 #define CharacterClass(value) \
  152     charClass[(value) & (int)((sizeof(charClass)/sizeof(charClass[0]))-1)]
  153 #endif
  154 
  155     /*
  156      * We'll generally map rows to indices when doing selection.
  157      * Simplify that with a macro.
  158      *
  159      * Note that ROW2INX() is safe to use with auto increment/decrement for
  160      * the row expression since that is evaluated once.
  161      */
  162 #define GET_LINEDATA(screen, row) \
  163     getLineData(screen, ROW2INX(screen, row))
  164 
  165 #define MaxMouseBtn  5
  166 
  167 #define IsBtnEvent(event) ((event)->type == ButtonPress || (event)->type == ButtonRelease)
  168 #define IsKeyEvent(event) ((event)->type == KeyPress    || (event)->type == KeyRelease)
  169 
  170 #define Coordinate(s,c) ((c)->row * MaxCols(s) + (c)->col)
  171 
  172 static const CELL zeroCELL =
  173 {0, 0};
  174 
  175 #if OPT_DEC_LOCATOR
  176 static Bool SendLocatorPosition(XtermWidget xw, XButtonEvent *event);
  177 static void CheckLocatorPosition(XtermWidget xw, XButtonEvent *event);
  178 #endif /* OPT_DEC_LOCATOR */
  179 
  180 /* Multi-click handling */
  181 #if OPT_READLINE
  182 static Time lastButtonDownTime = 0;
  183 static int ExtendingSelection = 0;
  184 static Time lastButton3UpTime = 0;
  185 static Time lastButton3DoubleDownTime = 0;
  186 static CELL lastButton3;    /* At the release time */
  187 #endif /* OPT_READLINE */
  188 
  189 static Char *SaveText(TScreen *screen, int row, int scol, int ecol,
  190               Char *lp, int *eol);
  191 static int Length(TScreen *screen, int row, int scol, int ecol);
  192 static void ComputeSelect(XtermWidget xw, CELL *startc, CELL *endc, Bool
  193               extend, Bool normal);
  194 static void EditorButton(XtermWidget xw, XButtonEvent *event);
  195 static void EndExtend(XtermWidget w, XEvent *event, String *params, Cardinal
  196               num_params, Bool use_cursor_loc);
  197 static void ExtendExtend(XtermWidget xw, const CELL *cell);
  198 static void PointToCELL(TScreen *screen, int y, int x, CELL *cell);
  199 static void ReHiliteText(XtermWidget xw, CELL *first, CELL *last);
  200 static void SaltTextAway(XtermWidget xw, int which, CELL *cellc, CELL *cell);
  201 static void SelectSet(XtermWidget xw, XEvent *event, String *params, Cardinal num_params);
  202 static void SelectionReceived PROTO_XT_SEL_CB_ARGS;
  203 static void StartSelect(XtermWidget xw, const CELL *cell);
  204 static void TrackDown(XtermWidget xw, XButtonEvent *event);
  205 static void TrackText(XtermWidget xw, const CELL *first, const CELL *last);
  206 static void UnHiliteText(XtermWidget xw);
  207 static void _OwnSelection(XtermWidget xw, String *selections, Cardinal count);
  208 static void do_select_end(XtermWidget xw, XEvent *event, String *params,
  209               Cardinal *num_params, Bool use_cursor_loc);
  210 
  211 #define MOUSE_LIMIT (255 - 32)
  212 
  213 /* Send SET_EXT_SIZE_MOUSE to enable offsets up to EXT_MOUSE_LIMIT */
  214 #define EXT_MOUSE_LIMIT (2047 - 32)
  215 #define EXT_MOUSE_START (127 - 32)
  216 
  217 static int
  218 MouseLimit(TScreen *screen)
  219 {
  220     int mouse_limit;
  221 
  222     switch (screen->extend_coords) {
  223     default:
  224     mouse_limit = MOUSE_LIMIT;
  225     break;
  226     case SET_EXT_MODE_MOUSE:
  227     mouse_limit = EXT_MOUSE_LIMIT;
  228     break;
  229     case SET_SGR_EXT_MODE_MOUSE:
  230     case SET_URXVT_EXT_MODE_MOUSE:
  231     case SET_PIXEL_POSITION_MOUSE:
  232     mouse_limit = -1;
  233     break;
  234     }
  235     return mouse_limit;
  236 }
  237 
  238 static unsigned
  239 EmitMousePosition(TScreen *screen, Char line[], unsigned count, int value)
  240 {
  241     int mouse_limit = MouseLimit(screen);
  242 
  243     /*
  244      * Add pointer position to key sequence
  245      *
  246      * In extended mode we encode large positions as two-byte UTF-8.
  247      *
  248      * NOTE: historically, it was possible to emit 256, which became
  249      * zero by truncation to 8 bits. While this was arguably a bug,
  250      * it's also somewhat useful as a past-end marker. We preserve
  251      * this behavior for both normal and extended mouse modes.
  252      */
  253     switch (screen->extend_coords) {
  254     default:
  255     if (value == mouse_limit) {
  256         line[count++] = CharOf(0);
  257     } else {
  258         line[count++] = CharOf(' ' + value + 1);
  259     }
  260     break;
  261     case SET_EXT_MODE_MOUSE:
  262     if (value == mouse_limit) {
  263         line[count++] = CharOf(0);
  264     } else if (value < EXT_MOUSE_START) {
  265         line[count++] = CharOf(' ' + value + 1);
  266     } else {
  267         value += ' ' + 1;
  268         line[count++] = CharOf(0xC0 + (value >> 6));
  269         line[count++] = CharOf(0x80 + (value & 0x3F));
  270     }
  271     break;
  272     case SET_SGR_EXT_MODE_MOUSE:
  273     case SET_URXVT_EXT_MODE_MOUSE:
  274     case SET_PIXEL_POSITION_MOUSE:
  275     count += (unsigned) sprintf((char *) line + count, "%d", value + 1);
  276     break;
  277     }
  278     return count;
  279 }
  280 
  281 static unsigned
  282 EmitMousePositionSeparator(TScreen *screen, Char line[], unsigned count)
  283 {
  284     switch (screen->extend_coords) {
  285     case SET_SGR_EXT_MODE_MOUSE:
  286     case SET_URXVT_EXT_MODE_MOUSE:
  287     case SET_PIXEL_POSITION_MOUSE:
  288     line[count++] = ';';
  289     break;
  290     }
  291     return count;
  292 }
  293 
  294 enum {
  295     scanMods,
  296     scanKey,
  297     scanColon,
  298     scanFunc,
  299     scanArgs
  300 };
  301 
  302 #if OPT_TRACE > 1
  303 static const char *
  304 visibleScan(int mode)
  305 {
  306     const char *result = "?";
  307 #define DATA(name) case name: result = #name; break
  308     switch (mode) {
  309     DATA(scanMods);
  310     DATA(scanKey);
  311     DATA(scanColon);
  312     DATA(scanFunc);
  313     DATA(scanArgs);
  314     }
  315 #undef DATA
  316     return result;
  317 }
  318 #endif
  319 
  320 #define L_BRACK '<'
  321 #define R_BRACK '>'
  322 #define L_PAREN '('
  323 #define R_PAREN ')'
  324 
  325 static char *
  326 scanTrans(char *source, int *this_is, int *next_is, unsigned *first, unsigned *last)
  327 {
  328     char *target = source;
  329 
  330     *first = *last = 0;
  331     if (IsEmpty(target)) {
  332     target = 0;
  333     } else {
  334     do {
  335         char ch;
  336         while (IsSpace(*target))
  337         target++;
  338         *first = (unsigned) (target - source);
  339         switch (*this_is = *next_is) {
  340         case scanMods:
  341         while ((ch = *target)) {
  342             if (IsSpace(ch)) {
  343             break;
  344             } else if (ch == L_BRACK) {
  345             *next_is = scanKey;
  346             break;
  347             } else if (ch == ':') {
  348             *next_is = scanColon;
  349             break;
  350             } else if (ch == '~' && target != source) {
  351             break;
  352             }
  353             target++;
  354         }
  355         break;
  356         case scanKey:
  357         while ((ch = *target)) {
  358             if (IsSpace(ch)) {
  359             break;
  360             } else if (ch == ':') {
  361             *next_is = scanColon;
  362             break;
  363             }
  364             target++;
  365             if (ch == R_BRACK)
  366             break;
  367         }
  368         break;
  369         case scanColon:
  370         *next_is = scanFunc;
  371         target++;
  372         break;
  373         case scanFunc:
  374         while ((ch = *target)) {
  375             if (IsSpace(ch)) {
  376             break;
  377             } else if (ch == L_PAREN) {
  378             *next_is = scanArgs;
  379             break;
  380             }
  381             target++;
  382         }
  383         break;
  384         case scanArgs:
  385         while ((ch = *target)) {
  386             if (ch == R_PAREN) {
  387             target++;
  388             *next_is = scanFunc;
  389             break;
  390             }
  391             target++;
  392         }
  393         break;
  394         }
  395         *last = (unsigned) (target - source);
  396         if (*target == '\n') {
  397         *next_is = scanMods;
  398         target++;
  399         }
  400     } while (*first == *last);
  401     }
  402     return target;
  403 }
  404 
  405 void
  406 xtermButtonInit(XtermWidget xw)
  407 {
  408     Widget w = (Widget) xw;
  409     XErrorHandler save = XSetErrorHandler(ignore_x11_error);
  410     XtTranslations xlations;
  411     Widget xcelerat;
  412     String result;
  413 
  414     XtVaGetValues(w,
  415           XtNtranslations, &xlations,
  416           XtNaccelerators, &xcelerat,
  417           (XtPointer) 0);
  418     result = _XtPrintXlations(w, xlations, xcelerat, True);
  419     if (result) {
  420     static const char *table[] =
  421     {
  422         "insert-selection",
  423         "select-end",
  424         "select-extend",
  425         "select-start",
  426         "start-extend",
  427     };
  428     char *data = x_strdup(result);
  429     char *next;
  430     int state = scanMods;
  431     int state2 = scanMods;
  432     unsigned first;
  433     unsigned last;
  434     int have_button = -1;
  435     Bool want_button = False;
  436     Bool have_shift = False;
  437     unsigned allowed = 0;
  438     unsigned disallow = 0;
  439 
  440     TRACE(("xtermButtonInit length %ld\n", (long) strlen(result)));
  441     xw->keyboard.print_translations = data;
  442     while ((next = scanTrans(data, &state, &state2, &first, &last)) != 0) {
  443         unsigned len = (last - first);
  444         TRACE2(("parse %s:%d..%d '%.*s'\n",
  445             visibleScan(state), first, last,
  446             len, data + first));
  447         if (state == scanMods) {
  448         if (len > 1 && data[first] == '~') {
  449             len--;
  450             first++;
  451         }
  452         if (len == 7 && !x_strncasecmp(data + first, "button", len - 1)) {
  453             have_button = data[first + 6] - '0';
  454         } else if (len == 5 && !x_strncasecmp(data + first, "shift", len)) {
  455             have_shift = True;
  456         }
  457         } else if (state == scanKey) {
  458         if (!x_strncasecmp(data + first, "<buttonpress>", len) ||
  459             !x_strncasecmp(data + first, "<buttonrelease>", len)) {
  460             want_button = True;
  461         } else if (want_button) {
  462             have_button = data[first] - '0';
  463             want_button = False;
  464         }
  465         } else if (state == scanFunc && have_button > 0) {
  466         Cardinal n;
  467         unsigned bmask = 1U << (have_button - 1);
  468         for (n = 0; n < XtNumber(table); ++n) {
  469             if (!x_strncasecmp(table[n], data + first, len)) {
  470             TRACE(("...button %d: %s%s\n",
  471                    have_button, table[n],
  472                    have_shift ? " (disallow)" : ""));
  473             if (have_shift)
  474                 disallow |= bmask;
  475             else
  476                 allowed |= bmask;
  477             break;
  478             }
  479         }
  480         }
  481         if (state2 == scanMods && state >= scanColon) {
  482         have_button = -1;
  483         want_button = False;
  484         have_shift = False;
  485         }
  486         state = state2;
  487         data = next;
  488     }
  489     XFree((char *) result);
  490     xw->keyboard.shift_buttons = allowed & ~disallow;
  491 #if OPT_TRACE
  492     if (xw->keyboard.shift_buttons) {
  493         int button = 0;
  494         unsigned mask = xw->keyboard.shift_buttons;
  495         TRACE(("...Buttons used for selection that can be overridden:"));
  496         while (mask != 0) {
  497         ++button;
  498         if ((mask & 1) != 0)
  499             TRACE((" %d", button));
  500         mask >>= 1;
  501         }
  502         TRACE(("\n"));
  503     } else {
  504         TRACE(("...No buttons used with selection can be overridden\n"));
  505     }
  506 #endif
  507     }
  508     XSetErrorHandler(save);
  509 }
  510 
  511 /*
  512  * Shift and control are regular X11 modifiers, but meta is not:
  513  * + X10 (which had no xmodmap utility) had a meta mask, but X11 did not.
  514  * + X11R1 introduced xmodmap, along with the current set of modifier masks.
  515  *   The meta key has been assumed to be mod1 since X11R1.
  516  *   The initial xterm logic in X11 was different, but gave the same result.
  517  * + X11R2 modified xterm was to eliminate the X10 table which provided part of
  518  *   the meta logic.
  519  * + X11R3 modified Xt, making Meta_L and Meta_R assignable via xmodmap, and
  520  *   equating Alt with Meta.  Neither Alt/Meta are modifiers, but Alt is more
  521  *   likely to be on the keyboard.  This release also added keymap tables for
  522  *   the server; Meta was used frequently in HP keymaps, which were the most
  523  *   extensive set of keymaps.
  524  * + X11R4 mentions Meta in the ICCCM, stating that if Meta_L or Meta_R are
  525  *   found in the keysyms for a given modifier, that the client should use
  526  *   that modifier.
  527  *
  528  * This function follows the ICCCM, picking the modifier which contains the
  529  * Meta_L/Meta_R keysyms (if available), falling back to the Alt_L/Alt_R
  530  * (as per X11R3), and ultimately to mod1 (per X11R1).
  531  */
  532 static unsigned
  533 MetaMask(XtermWidget xw)
  534 {
  535 #if OPT_NUM_LOCK
  536     unsigned meta = xw->work.meta_mods;
  537     if (meta == 0)
  538     meta = xw->work.alt_mods;
  539     if (meta == 0)
  540     meta = Mod1Mask;
  541 #else
  542     unsigned meta = Mod1Mask;
  543     (void) xw;
  544 #endif
  545     return meta;
  546 }
  547 
  548 /*
  549  * Returns a mask of the modifiers we may use for modifying the mouse protocol
  550  * response strings.
  551  */
  552 static unsigned
  553 OurModifiers(XtermWidget xw)
  554 {
  555     return (ShiftMask
  556         | ControlMask
  557         | MetaMask(xw));
  558 }
  559 
  560 /*
  561  * The actual check for the shift-mask, to see if it should tell xterm to
  562  * override mouse-protocol in favor of select/paste actions depends upon
  563  * whether the shiftEscape resource is set to true/always vs false/never.
  564  */
  565 static Boolean
  566 ShiftOverride(XtermWidget xw, unsigned state, int button)
  567 {
  568     unsigned check = (state & OurModifiers(xw));
  569     Boolean result = False;
  570 
  571     if (check & ShiftMask) {
  572     if (xw->keyboard.shift_escape == ssFalse ||
  573         xw->keyboard.shift_escape == ssNever) {
  574         result = True;
  575     } else if (xw->keyboard.shift_escape == ssTrue) {
  576         /*
  577          * Check if the button is one that we found does not directly use
  578          * the shift-modifier in its bindings to select/copy actions.
  579          */
  580         if (button > 0 && button <= MaxMouseBtn) {
  581         if (xw->keyboard.shift_buttons & (1U << (button - 1))) {
  582             result = True;
  583         }
  584         } else {
  585         result = True;  /* unlikely, and we don't care */
  586         }
  587     }
  588     }
  589     TRACE2(("ShiftOverride ( %#x -> %#x ) %d\n", state, check, result));
  590     return result;
  591 }
  592 
  593 /*
  594  * Normally xterm treats the shift-modifier specially when the mouse protocol
  595  * is active.  The translations resource binds otherwise unmodified button
  596  * for these mouse-related events:
  597  *
  598  *         ~Meta <Btn1Down>:select-start() \n\
  599  *       ~Meta <Btn1Motion>:select-extend() \n\
  600  *     ~Ctrl ~Meta <Btn2Up>:insert-selection(SELECT, CUT_BUFFER0) \n\
  601  *   ~Ctrl ~Meta <Btn3Down>:start-extend() \n\
  602  *       ~Meta <Btn3Motion>:select-extend() \n\
  603  *                  <BtnUp>:select-end(SELECT, CUT_BUFFER0) \n\
  604  *
  605  * There is no API in the X libraries which would tell us if a given mouse
  606  * button is bound to one of these actions.  These functions make the choice
  607  * configurable.
  608  */
  609 static Bool
  610 InterpretButton(XtermWidget xw, XButtonEvent *event)
  611 {
  612     Bool result = False;
  613 
  614     if (ShiftOverride(xw, event->state, (int) event->button)) {
  615     TRACE(("...shift-button #%d overrides mouse-protocol\n", event->button));
  616     result = True;
  617     }
  618     return result;
  619 }
  620 
  621 #define Button1Index 8      /* X.h should have done this */
  622 
  623 static int
  624 MotionButton(unsigned state)
  625 {
  626     unsigned bmask = state >> Button1Index;
  627     int result = 1;
  628 
  629     if (bmask != 0) {
  630     while (!(bmask & 1)) {
  631         ++result;
  632         bmask >>= 1;
  633     }
  634     }
  635     return result;
  636 }
  637 
  638 static Bool
  639 InterpretEvent(XtermWidget xw, XEvent *event)
  640 {
  641     Bool result = False;    /* if not a button, is motion */
  642 
  643     if (IsBtnEvent(event)) {
  644     result = InterpretButton(xw, (XButtonEvent *) event);
  645     } else if (event->type == MotionNotify) {
  646     unsigned state = event->xmotion.state;
  647     int button = MotionButton(state);
  648 
  649     if (ShiftOverride(xw, state, button)) {
  650         TRACE(("...shift-motion #%d (%d,%d) overrides mouse-protocol\n",
  651            button,
  652            event->xmotion.y,
  653            event->xmotion.x));
  654         result = True;
  655     }
  656     }
  657     return result;
  658 }
  659 
  660 #define OverrideEvent(event)  InterpretEvent(xw, event)
  661 #define OverrideButton(event) InterpretButton(xw, event)
  662 
  663 /*
  664  * Returns true if we handled the event here, and nothing more is needed.
  665  */
  666 Bool
  667 SendMousePosition(XtermWidget xw, XEvent *event)
  668 {
  669     XButtonEvent *my_event = (XButtonEvent *) event;
  670     Bool result = False;
  671 
  672     switch (okSendMousePos(xw)) {
  673     case MOUSE_OFF:
  674     /* If send_mouse_pos mode isn't on, we shouldn't be here */
  675     break;
  676 
  677     case BTN_EVENT_MOUSE:
  678     case ANY_EVENT_MOUSE:
  679     if (!OverrideEvent(event)) {
  680         /* xterm extension for motion reporting. June 1998 */
  681         /* EditorButton() will distinguish between the modes */
  682         switch (event->type) {
  683         case MotionNotify:
  684         my_event->button = 0;
  685         /* FALLTHRU */
  686         case ButtonPress:
  687         /* FALLTHRU */
  688         case ButtonRelease:
  689         EditorButton(xw, my_event);
  690         result = True;
  691         break;
  692         }
  693     }
  694     break;
  695 
  696     case X10_MOUSE:     /* X10 compatibility sequences */
  697     if (IsBtnEvent(event)) {
  698         if (!OverrideButton(my_event)) {
  699         if (my_event->type == ButtonPress)
  700             EditorButton(xw, my_event);
  701         result = True;
  702         }
  703     }
  704     break;
  705 
  706     case VT200_HIGHLIGHT_MOUSE: /* DEC vt200 hilite tracking */
  707     if (IsBtnEvent(event)) {
  708         if (!OverrideButton(my_event)) {
  709         if (my_event->type == ButtonPress &&
  710             my_event->button == Button1) {
  711             TrackDown(xw, my_event);
  712         } else {
  713             EditorButton(xw, my_event);
  714         }
  715         result = True;
  716         }
  717     }
  718     break;
  719 
  720     case VT200_MOUSE:       /* DEC vt200 compatible */
  721     if (IsBtnEvent(event)) {
  722         if (!OverrideButton(my_event)) {
  723         EditorButton(xw, my_event);
  724         result = True;
  725         }
  726     }
  727     break;
  728 
  729     case DEC_LOCATOR:
  730 #if OPT_DEC_LOCATOR
  731     if (IsBtnEvent(event) || event->type == MotionNotify) {
  732         result = SendLocatorPosition(xw, my_event);
  733     }
  734 #endif /* OPT_DEC_LOCATOR */
  735     break;
  736     }
  737     return result;
  738 }
  739 
  740 #if OPT_DEC_LOCATOR
  741 
  742 #define LocatorCoords( row, col, x, y, oor )            \
  743     if( screen->locator_pixels ) {              \
  744     (oor)=False; (row) = (y)+1; (col) = (x)+1;      \
  745     /* Limit to screen dimensions */            \
  746     if ((row) < 1) (row) = 1,(oor)=True;            \
  747     else if ((row) > screen->border*2+Height(screen))   \
  748         (row) = screen->border*2+Height(screen),(oor)=True; \
  749     if ((col) < 1) (col) = 1,(oor)=True;            \
  750     else if ((col) > OriginX(screen)*2+Width(screen))   \
  751         (col) = OriginX(screen)*2+Width(screen),(oor)=True; \
  752     } else {                            \
  753     (oor)=False;                        \
  754     /* Compute character position of mouse pointer */   \
  755     (row) = ((y) - screen->border) / FontHeight(screen);    \
  756     (col) = ((x) - OriginX(screen)) / FontWidth(screen);    \
  757     /* Limit to screen dimensions */            \
  758     if ((row) < 0) (row) = 0,(oor)=True;            \
  759     else if ((row) > screen->max_row)           \
  760         (row) = screen->max_row,(oor)=True;         \
  761     if ((col) < 0) (col) = 0,(oor)=True;            \
  762     else if ((col) > screen->max_col)           \
  763         (col) = screen->max_col,(oor)=True;         \
  764     (row)++; (col)++;                   \
  765     }
  766 
  767 static Bool
  768 SendLocatorPosition(XtermWidget xw, XButtonEvent *event)
  769 {
  770     ANSI reply;
  771     TScreen *screen = TScreenOf(xw);
  772     int row, col;
  773     Bool oor;
  774     int button;
  775     unsigned state;
  776 
  777     /* Make sure the event is an appropriate type */
  778     if (IsBtnEvent(event)) {
  779     if (OverrideButton(event))
  780         return (False);
  781     } else {
  782     if (!screen->loc_filter)
  783         return (False);
  784     }
  785 
  786     if ((event->type == ButtonPress &&
  787      !(screen->locator_events & LOC_BTNS_DN)) ||
  788     (event->type == ButtonRelease &&
  789      !(screen->locator_events & LOC_BTNS_UP)))
  790     return (True);
  791 
  792     if (event->type == MotionNotify) {
  793     CheckLocatorPosition(xw, event);
  794     return (True);
  795     }
  796 
  797     /* get button # */
  798     button = (int) event->button - 1;
  799 
  800     LocatorCoords(row, col, event->x, event->y, oor);
  801 
  802     /*
  803      * DECterm mouse:
  804      *
  805      * ESCAPE '[' event ; mask ; row ; column '&' 'w'
  806      */
  807     memset(&reply, 0, sizeof(reply));
  808     reply.a_type = ANSI_CSI;
  809 
  810     if (oor) {
  811     reply.a_nparam = 1;
  812     reply.a_param[0] = 0;   /* Event - 0 = locator unavailable */
  813     reply.a_inters = '&';
  814     reply.a_final = 'w';
  815     unparseseq(xw, &reply);
  816 
  817     if (screen->locator_reset) {
  818         MotionOff(screen, xw);
  819         screen->send_mouse_pos = MOUSE_OFF;
  820     }
  821     return (True);
  822     }
  823 
  824     /*
  825      * event:
  826      *        1       no buttons
  827      *        2       left button down
  828      *        3       left button up
  829      *        4       middle button down
  830      *        5       middle button up
  831      *        6       right button down
  832      *        7       right button up
  833      *        8       M4 down
  834      *        9       M4 up
  835      */
  836     reply.a_nparam = 4;
  837     switch (event->type) {
  838     case ButtonPress:
  839     reply.a_param[0] = (ParmType) (2 + (button << 1));
  840     break;
  841     case ButtonRelease:
  842     reply.a_param[0] = (ParmType) (3 + (button << 1));
  843     break;
  844     default:
  845     return (True);
  846     }
  847     /*
  848      * mask:
  849      * bit7   bit6   bit5   bit4   bit3     bit2       bit1         bit0
  850      *                             M4 down  left down  middle down  right down
  851      *
  852      * Notice that Button1 (left) and Button3 (right) are swapped in the mask.
  853      * Also, mask should be the state after the button press/release,
  854      * X provides the state not including the button press/release.
  855      */
  856     state = (event->state
  857          & (Button1Mask | Button2Mask | Button3Mask | Button4Mask)) >> 8;
  858     /* update mask to "after" state */
  859     state ^= ((unsigned) (1 << button));
  860     /* swap Button1 & Button3 */
  861     state = ((state & (unsigned) ~(4 | 1))
  862          | ((state & 1) ? 4 : 0)
  863          | ((state & 4) ? 1 : 0));
  864 
  865     reply.a_param[1] = (ParmType) state;
  866     reply.a_param[2] = (ParmType) row;
  867     reply.a_param[3] = (ParmType) col;
  868     reply.a_inters = '&';
  869     reply.a_final = 'w';
  870 
  871     unparseseq(xw, &reply);
  872 
  873     if (screen->locator_reset) {
  874     MotionOff(screen, xw);
  875     screen->send_mouse_pos = MOUSE_OFF;
  876     }
  877 
  878     /*
  879      * DECterm turns the Locator off if a button is pressed while a filter
  880      * rectangle is active.  This might be a bug, but I don't know, so I'll
  881      * emulate it anyway.
  882      */
  883     if (screen->loc_filter) {
  884     screen->send_mouse_pos = MOUSE_OFF;
  885     screen->loc_filter = False;
  886     screen->locator_events = 0;
  887     MotionOff(screen, xw);
  888     }
  889 
  890     return (True);
  891 }
  892 
  893 /*
  894  * mask:
  895  * bit 7   bit 6   bit 5   bit 4   bit 3   bit 2       bit 1         bit 0
  896  *                                 M4 down left down   middle down   right down
  897  *
  898  * Button1 (left) and Button3 (right) are swapped in the mask relative to X.
  899  */
  900 #define ButtonState(state, mask)    \
  901 { int stemp = (int) (((mask) & (Button1Mask | Button2Mask | Button3Mask | Button4Mask)) >> 8);  \
  902   /* swap Button1 & Button3 */                              \
  903   (state) = (stemp & ~(4|1)) | ((stemp & 1) ? 4 : 0) | ((stemp & 4) ? 1 : 0);           \
  904 }
  905 
  906 void
  907 GetLocatorPosition(XtermWidget xw)
  908 {
  909     ANSI reply;
  910     TScreen *screen = TScreenOf(xw);
  911     Window root, child;
  912     int rx, ry, x, y;
  913     unsigned int mask = 0;
  914     int row = 0, col = 0;
  915     Bool oor = False;
  916     Bool ret = False;
  917     int state;
  918 
  919     /*
  920      * DECterm turns the Locator off if the position is requested while a
  921      * filter rectangle is active.  This might be a bug, but I don't know, so
  922      * I'll emulate it anyways.
  923      */
  924     if (screen->loc_filter) {
  925     screen->send_mouse_pos = MOUSE_OFF;
  926     screen->loc_filter = False;
  927     screen->locator_events = 0;
  928     MotionOff(screen, xw);
  929     }
  930 
  931     memset(&reply, 0, sizeof(reply));
  932     reply.a_type = ANSI_CSI;
  933 
  934     if (okSendMousePos(xw) == DEC_LOCATOR) {
  935     ret = XQueryPointer(screen->display, VWindow(screen), &root,
  936                 &child, &rx, &ry, &x, &y, &mask);
  937     if (ret) {
  938         LocatorCoords(row, col, x, y, oor);
  939     }
  940     }
  941     if (ret == False || oor) {
  942     reply.a_nparam = 1;
  943     reply.a_param[0] = 0;   /* Event - 0 = locator unavailable */
  944     reply.a_inters = '&';
  945     reply.a_final = 'w';
  946     unparseseq(xw, &reply);
  947 
  948     if (screen->locator_reset) {
  949         MotionOff(screen, xw);
  950         screen->send_mouse_pos = MOUSE_OFF;
  951     }
  952     return;
  953     }
  954 
  955     ButtonState(state, mask);
  956 
  957     reply.a_nparam = 4;
  958     reply.a_param[0] = 1;   /* Event - 1 = response to locator request */
  959     reply.a_param[1] = (ParmType) state;
  960     reply.a_param[2] = (ParmType) row;
  961     reply.a_param[3] = (ParmType) col;
  962     reply.a_inters = '&';
  963     reply.a_final = 'w';
  964     unparseseq(xw, &reply);
  965 
  966     if (screen->locator_reset) {
  967     MotionOff(screen, xw);
  968     screen->send_mouse_pos = MOUSE_OFF;
  969     }
  970 }
  971 
  972 void
  973 InitLocatorFilter(XtermWidget xw)
  974 {
  975     ANSI reply;
  976     TScreen *screen = TScreenOf(xw);
  977     Window root, child;
  978     int rx, ry, x, y;
  979     unsigned int mask;
  980     int row = 0, col = 0;
  981     Bool oor = 0;
  982     Bool ret;
  983 
  984     ret = XQueryPointer(screen->display, VWindow(screen),
  985             &root, &child, &rx, &ry, &x, &y, &mask);
  986     if (ret) {
  987     LocatorCoords(row, col, x, y, oor);
  988     }
  989     if (ret == False || oor) {
  990     /* Locator is unavailable */
  991 
  992     if (screen->loc_filter_top != LOC_FILTER_POS ||
  993         screen->loc_filter_left != LOC_FILTER_POS ||
  994         screen->loc_filter_bottom != LOC_FILTER_POS ||
  995         screen->loc_filter_right != LOC_FILTER_POS) {
  996         /*
  997          * If any explicit coordinates were received,
  998          * report immediately with no coordinates.
  999          */
 1000         memset(&reply, 0, sizeof(reply));
 1001         reply.a_type = ANSI_CSI;
 1002         reply.a_nparam = 1;
 1003         reply.a_param[0] = 0;   /* Event - 0 = locator unavailable */
 1004         reply.a_inters = '&';
 1005         reply.a_final = 'w';
 1006         unparseseq(xw, &reply);
 1007 
 1008         if (screen->locator_reset) {
 1009         MotionOff(screen, xw);
 1010         screen->send_mouse_pos = MOUSE_OFF;
 1011         }
 1012     } else {
 1013         /*
 1014          * No explicit coordinates were received, and the pointer is
 1015          * unavailable.  Report when the pointer re-enters the window.
 1016          */
 1017         screen->loc_filter = True;
 1018         MotionOn(screen, xw);
 1019     }
 1020     return;
 1021     }
 1022 
 1023     /*
 1024      * Adjust rectangle coordinates:
 1025      *  1. Replace "LOC_FILTER_POS" with current coordinates
 1026      *  2. Limit coordinates to screen size
 1027      *  3. make sure top and left are less than bottom and right, resp.
 1028      */
 1029     if (screen->locator_pixels) {
 1030     rx = OriginX(screen) * 2 + Width(screen);
 1031     ry = screen->border * 2 + Height(screen);
 1032     } else {
 1033     rx = screen->max_col;
 1034     ry = screen->max_row;
 1035     }
 1036 
 1037 #define Adjust( coord, def, max )               \
 1038     if( (coord) == LOC_FILTER_POS ) (coord) = (def);    \
 1039     else if ((coord) < 1)       (coord) = 1;        \
 1040     else if ((coord) > (max))   (coord) = (max)
 1041 
 1042     Adjust(screen->loc_filter_top, row, ry);
 1043     Adjust(screen->loc_filter_left, col, rx);
 1044     Adjust(screen->loc_filter_bottom, row, ry);
 1045     Adjust(screen->loc_filter_right, col, rx);
 1046 
 1047     if (screen->loc_filter_top > screen->loc_filter_bottom) {
 1048     ry = screen->loc_filter_top;
 1049     screen->loc_filter_top = screen->loc_filter_bottom;
 1050     screen->loc_filter_bottom = ry;
 1051     }
 1052 
 1053     if (screen->loc_filter_left > screen->loc_filter_right) {
 1054     rx = screen->loc_filter_left;
 1055     screen->loc_filter_left = screen->loc_filter_right;
 1056     screen->loc_filter_right = rx;
 1057     }
 1058 
 1059     if ((col < screen->loc_filter_left) ||
 1060     (col > screen->loc_filter_right) ||
 1061     (row < screen->loc_filter_top) ||
 1062     (row > screen->loc_filter_bottom)) {
 1063     int state;
 1064 
 1065     /* Pointer is already outside the rectangle - report immediately */
 1066     ButtonState(state, mask);
 1067 
 1068     memset(&reply, 0, sizeof(reply));
 1069     reply.a_type = ANSI_CSI;
 1070     reply.a_nparam = 4;
 1071     reply.a_param[0] = 10;  /* Event - 10 = locator outside filter */
 1072     reply.a_param[1] = (ParmType) state;
 1073     reply.a_param[2] = (ParmType) row;
 1074     reply.a_param[3] = (ParmType) col;
 1075     reply.a_inters = '&';
 1076     reply.a_final = 'w';
 1077     unparseseq(xw, &reply);
 1078 
 1079     if (screen->locator_reset) {
 1080         MotionOff(screen, xw);
 1081         screen->send_mouse_pos = MOUSE_OFF;
 1082     }
 1083     return;
 1084     }
 1085 
 1086     /*
 1087      * Rectangle is set up.  Allow pointer tracking
 1088      * to detect if the mouse leaves the rectangle.
 1089      */
 1090     screen->loc_filter = True;
 1091     MotionOn(screen, xw);
 1092 }
 1093 
 1094 static void
 1095 CheckLocatorPosition(XtermWidget xw, XButtonEvent *event)
 1096 {
 1097     ANSI reply;
 1098     TScreen *screen = TScreenOf(xw);
 1099     int row, col;
 1100     Bool oor;
 1101 
 1102     LocatorCoords(row, col, event->x, event->y, oor);
 1103 
 1104     /*
 1105      * Send report if the pointer left the filter rectangle, if
 1106      * the pointer left the window, or if the filter rectangle
 1107      * had no coordinates and the pointer re-entered the window.
 1108      */
 1109     if (oor || (screen->loc_filter_top == LOC_FILTER_POS) ||
 1110     (col < screen->loc_filter_left) ||
 1111     (col > screen->loc_filter_right) ||
 1112     (row < screen->loc_filter_top) ||
 1113     (row > screen->loc_filter_bottom)) {
 1114     /* Filter triggered - disable it */
 1115     screen->loc_filter = False;
 1116     MotionOff(screen, xw);
 1117 
 1118     memset(&reply, 0, sizeof(reply));
 1119     reply.a_type = ANSI_CSI;
 1120     if (oor) {
 1121         reply.a_nparam = 1;
 1122         reply.a_param[0] = 0;   /* Event - 0 = locator unavailable */
 1123     } else {
 1124         int state;
 1125 
 1126         ButtonState(state, event->state);
 1127 
 1128         reply.a_nparam = 4;
 1129         reply.a_param[0] = 10;  /* Event - 10 = locator outside filter */
 1130         reply.a_param[1] = (ParmType) state;
 1131         reply.a_param[2] = (ParmType) row;
 1132         reply.a_param[3] = (ParmType) col;
 1133     }
 1134 
 1135     reply.a_inters = '&';
 1136     reply.a_final = 'w';
 1137     unparseseq(xw, &reply);
 1138 
 1139     if (screen->locator_reset) {
 1140         MotionOff(screen, xw);
 1141         screen->send_mouse_pos = MOUSE_OFF;
 1142     }
 1143     }
 1144 }
 1145 #endif /* OPT_DEC_LOCATOR */
 1146 
 1147 #if OPT_READLINE
 1148 static int
 1149 isClick1_clean(XtermWidget xw, XButtonEvent *event)
 1150 {
 1151     TScreen *screen = TScreenOf(xw);
 1152     int delta;
 1153 
 1154     /* Disable on Shift-Click-1, including the application-mouse modes */
 1155     if (OverrideButton(event)
 1156     || (okSendMousePos(xw) != MOUSE_OFF)
 1157     || ExtendingSelection)  /* Was moved */
 1158     return 0;
 1159 
 1160     if (event->type != ButtonRelease)
 1161     return 0;
 1162 
 1163     if (lastButtonDownTime == (Time) 0) {
 1164     /* first time or once in a blue moon */
 1165     delta = screen->multiClickTime + 1;
 1166     } else if (event->time > lastButtonDownTime) {
 1167     /* most of the time */
 1168     delta = (int) (event->time - lastButtonDownTime);
 1169     } else {
 1170     /* time has rolled over since lastButtonUpTime */
 1171     delta = (int) ((((Time) ~ 0) - lastButtonDownTime) + event->time);
 1172     }
 1173 
 1174     return delta <= screen->multiClickTime;
 1175 }
 1176 
 1177 static int
 1178 isDoubleClick3(XtermWidget xw, TScreen *screen, XButtonEvent *event)
 1179 {
 1180     int delta;
 1181 
 1182     if (event->type != ButtonRelease
 1183     || OverrideButton(event)
 1184     || event->button != Button3) {
 1185     lastButton3UpTime = 0;  /* Disable the cached info */
 1186     return 0;
 1187     }
 1188     /* Process Btn3Release. */
 1189     if (lastButton3DoubleDownTime == (Time) 0) {
 1190     /* No previous click or once in a blue moon */
 1191     delta = screen->multiClickTime + 1;
 1192     } else if (event->time > lastButton3DoubleDownTime) {
 1193     /* most of the time */
 1194     delta = (int) (event->time - lastButton3DoubleDownTime);
 1195     } else {
 1196     /* time has rolled over since lastButton3DoubleDownTime */
 1197     delta = (int) ((((Time) ~ 0) - lastButton3DoubleDownTime) + event->time);
 1198     }
 1199     if (delta <= screen->multiClickTime) {
 1200     /* Double click */
 1201     CELL cell;
 1202 
 1203     /* Cannot check ExtendingSelection, since mouse-3 always sets it */
 1204     PointToCELL(screen, event->y, event->x, &cell);
 1205     if (isSameCELL(&cell, &lastButton3)) {
 1206         lastButton3DoubleDownTime = 0;  /* Disable the third click */
 1207         return 1;
 1208     }
 1209     }
 1210     /* Not a double click, memorize for future check. */
 1211     lastButton3UpTime = event->time;
 1212     PointToCELL(screen, event->y, event->x, &lastButton3);
 1213     return 0;
 1214 }
 1215 
 1216 static int
 1217 CheckSecondPress3(XtermWidget xw, TScreen *screen, XEvent *event)
 1218 {
 1219     int delta;
 1220 
 1221     if (event->type != ButtonPress
 1222     || OverrideEvent(event)
 1223     || event->xbutton.button != Button3) {
 1224     lastButton3DoubleDownTime = 0;  /* Disable the cached info */
 1225     return 0;
 1226     }
 1227     /* Process Btn3Press. */
 1228     if (lastButton3UpTime == (Time) 0) {
 1229     /* No previous click or once in a blue moon */
 1230     delta = screen->multiClickTime + 1;
 1231     } else if (event->xbutton.time > lastButton3UpTime) {
 1232     /* most of the time */
 1233     delta = (int) (event->xbutton.time - lastButton3UpTime);
 1234     } else {
 1235     /* time has rolled over since lastButton3UpTime */
 1236     delta = (int) ((((Time) ~ 0) - lastButton3UpTime) + event->xbutton.time);
 1237     }
 1238     if (delta <= screen->multiClickTime) {
 1239     CELL cell;
 1240 
 1241     PointToCELL(screen, event->xbutton.y, event->xbutton.x, &cell);
 1242     if (isSameCELL(&cell, &lastButton3)) {
 1243         /* A candidate for a double-click */
 1244         lastButton3DoubleDownTime = event->xbutton.time;
 1245         PointToCELL(screen, event->xbutton.y, event->xbutton.x, &lastButton3);
 1246         return 1;
 1247     }
 1248     lastButton3UpTime = 0;  /* Disable the info about the previous click */
 1249     }
 1250     /* Either too long, or moved, disable. */
 1251     lastButton3DoubleDownTime = 0;
 1252     return 0;
 1253 }
 1254 
 1255 static int
 1256 rowOnCurrentLine(TScreen *screen,
 1257          int line,
 1258          int *deltap)   /* must be XButtonEvent */
 1259 {
 1260     int result = 1;
 1261 
 1262     *deltap = 0;
 1263 
 1264     if (line != screen->cur_row) {
 1265     int l1, l2;
 1266 
 1267     if (line < screen->cur_row) {
 1268         l1 = line;
 1269         l2 = screen->cur_row;
 1270     } else {
 1271         l2 = line;
 1272         l1 = screen->cur_row;
 1273     }
 1274     l1--;
 1275     while (++l1 < l2) {
 1276         LineData *ld = GET_LINEDATA(screen, l1);
 1277         if (!LineTstWrapped(ld)) {
 1278         result = 0;
 1279         break;
 1280         }
 1281     }
 1282     if (result) {
 1283         /* Everything is on one "wrapped line" now */
 1284         *deltap = line - screen->cur_row;
 1285     }
 1286     }
 1287     return result;
 1288 }
 1289 
 1290 static int
 1291 eventRow(TScreen *screen, XEvent *event)    /* must be XButtonEvent */
 1292 {
 1293     return (event->xbutton.y - screen->border) / FontHeight(screen);
 1294 }
 1295 
 1296 static int
 1297 eventColBetween(TScreen *screen, XEvent *event)     /* must be XButtonEvent */
 1298 {
 1299     /* Correct by half a width - we are acting on a boundary, not on a cell. */
 1300     return ((event->xbutton.x - OriginX(screen) + (FontWidth(screen) - 1) / 2)
 1301         / FontWidth(screen));
 1302 }
 1303 
 1304 static int
 1305 ReadLineMovePoint(XtermWidget xw, int col, int ldelta)
 1306 {
 1307     TScreen *screen = TScreenOf(xw);
 1308     Char line[6];
 1309     unsigned count = 0;
 1310 
 1311     col += ldelta * MaxCols(screen) - screen->cur_col;
 1312     if (col == 0)
 1313     return 0;
 1314     if (screen->control_eight_bits) {
 1315     line[count++] = ANSI_CSI;
 1316     } else {
 1317     line[count++] = ANSI_ESC;
 1318     line[count++] = (xw->keyboard.flags & MODE_DECCKM) ? 'O' : '[';
 1319     }
 1320     line[count] = CharOf(col > 0 ? 'C' : 'D');
 1321     if (col < 0)
 1322     col = -col;
 1323     while (col--)
 1324     v_write(screen->respond, line, (size_t) 3);
 1325     return 1;
 1326 }
 1327 
 1328 static int
 1329 ReadLineDelete(TScreen *screen, CELL *cell1, CELL *cell2)
 1330 {
 1331     int del;
 1332     Char erases[2];
 1333 
 1334     erases[0] = (Char) get_tty_erase(screen->respond, XTERM_ERASE, "pty");
 1335     erases[1] = 0;
 1336 
 1337     del = (cell2->col - cell1->col) + ((cell2->row - cell1->row) * MaxCols(screen));
 1338     if (del <= 0)       /* Just in case... */
 1339     return 0;
 1340     while (del--)
 1341     v_write(screen->respond, erases, (size_t) 1);
 1342     return 1;
 1343 }
 1344 
 1345 static void
 1346 readlineExtend(XtermWidget xw, XEvent *event)
 1347 {
 1348     TScreen *screen = TScreenOf(xw);
 1349     int ldelta1, ldelta2;
 1350 
 1351     if (IsBtnEvent(event)) {
 1352     XButtonEvent *my_event = (XButtonEvent *) event;
 1353     if (isClick1_clean(xw, my_event)
 1354         && SCREEN_FLAG(screen, click1_moves)
 1355         && rowOnCurrentLine(screen, eventRow(screen, event), &ldelta1)) {
 1356         ReadLineMovePoint(xw, eventColBetween(screen, event), ldelta1);
 1357     }
 1358     if (isDoubleClick3(xw, screen, my_event)
 1359         && SCREEN_FLAG(screen, dclick3_deletes)
 1360         && rowOnCurrentLine(screen, screen->startSel.row, &ldelta1)
 1361         && rowOnCurrentLine(screen, screen->endSel.row, &ldelta2)) {
 1362         ReadLineMovePoint(xw, screen->endSel.col, ldelta2);
 1363         ReadLineDelete(screen, &screen->startSel, &(screen->endSel));
 1364     }
 1365     }
 1366 }
 1367 #endif /* OPT_READLINE */
 1368 
 1369 /* ^XM-G<line+' '><col+' '> */
 1370 void
 1371 DiredButton(Widget w,
 1372         XEvent *event,  /* must be XButtonEvent */
 1373         String *params GCC_UNUSED,  /* selections */
 1374         Cardinal *num_params GCC_UNUSED)
 1375 {
 1376     XtermWidget xw;
 1377 
 1378     if ((xw = getXtermWidget(w)) != 0) {
 1379     TScreen *screen = TScreenOf(xw);
 1380 
 1381     if (IsBtnEvent(event)
 1382         && (event->xbutton.y >= screen->border)
 1383         && (event->xbutton.x >= OriginX(screen))) {
 1384         Char Line[6];
 1385         unsigned line, col;
 1386 
 1387         line = (unsigned) ((event->xbutton.y - screen->border)
 1388                    / FontHeight(screen));
 1389         col = (unsigned) ((event->xbutton.x - OriginX(screen))
 1390                   / FontWidth(screen));
 1391         Line[0] = CONTROL('X');
 1392         Line[1] = ANSI_ESC;
 1393         Line[2] = 'G';
 1394         Line[3] = CharOf(' ' + col);
 1395         Line[4] = CharOf(' ' + line);
 1396         v_write(screen->respond, Line, (size_t) 5);
 1397     }
 1398     }
 1399 }
 1400 
 1401 #if OPT_READLINE
 1402 void
 1403 ReadLineButton(Widget w,
 1404            XEvent *event,   /* must be XButtonEvent */
 1405            String *params,  /* selections */
 1406            Cardinal *num_params)
 1407 {
 1408     XtermWidget xw;
 1409 
 1410     if ((xw = getXtermWidget(w)) != 0) {
 1411     TScreen *screen = TScreenOf(xw);
 1412     Char Line[6];
 1413     int line, col, ldelta = 0;
 1414 
 1415     if (!IsBtnEvent(event)
 1416         || (okSendMousePos(xw) != MOUSE_OFF) || ExtendingSelection)
 1417         goto finish;
 1418     if (event->type == ButtonRelease) {
 1419         int delta;
 1420 
 1421         if (lastButtonDownTime == (Time) 0) {
 1422         /* first time and once in a blue moon */
 1423         delta = screen->multiClickTime + 1;
 1424         } else if (event->xbutton.time > lastButtonDownTime) {
 1425         /* most of the time */
 1426         delta = (int) (event->xbutton.time - lastButtonDownTime);
 1427         } else {
 1428         /* time has rolled over since lastButtonUpTime */
 1429         delta = (int) ((((Time) ~ 0) - lastButtonDownTime) + event->xbutton.time);
 1430         }
 1431         if (delta > screen->multiClickTime)
 1432         goto finish;    /* All this work for this... */
 1433     }
 1434     line = (event->xbutton.y - screen->border) / FontHeight(screen);
 1435     if (!rowOnCurrentLine(screen, line, &ldelta))
 1436         goto finish;
 1437     /* Correct by half a width - we are acting on a boundary, not on a cell. */
 1438     col = (event->xbutton.x - OriginX(screen) + (FontWidth(screen) - 1)
 1439            / 2)
 1440         / FontWidth(screen) - screen->cur_col + ldelta * MaxCols(screen);
 1441     if (col == 0)
 1442         goto finish;
 1443     Line[0] = ANSI_ESC;
 1444     Line[1] = (xw->keyboard.flags & MODE_DECCKM) ? 'O' : '[';
 1445     Line[2] = CharOf(col > 0 ? 'C' : 'D');
 1446     if (col < 0)
 1447         col = -col;
 1448     while (col--)
 1449         v_write(screen->respond, Line, (size_t) 3);
 1450       finish:
 1451     if (event->type == ButtonRelease)
 1452         do_select_end(xw, event, params, num_params, False);
 1453     }
 1454 }
 1455 #endif /* OPT_READLINE */
 1456 
 1457 /* repeats <ESC>n or <ESC>p */
 1458 void
 1459 ViButton(Widget w,
 1460      XEvent *event,     /* must be XButtonEvent */
 1461      String *params GCC_UNUSED, /* selections */
 1462      Cardinal *num_params GCC_UNUSED)
 1463 {
 1464     XtermWidget xw;
 1465 
 1466     if ((xw = getXtermWidget(w)) != 0) {
 1467     TScreen *screen = TScreenOf(xw);
 1468     int pty = screen->respond;
 1469 
 1470     if (IsBtnEvent(event)) {
 1471         int line;
 1472 
 1473         line = screen->cur_row -
 1474         ((event->xbutton.y - screen->border) / FontHeight(screen));
 1475 
 1476         if (line != 0) {
 1477         Char Line[6];
 1478 
 1479         Line[0] = ANSI_ESC; /* force an exit from insert-mode */
 1480         v_write(pty, Line, (size_t) 1);
 1481 
 1482         if (line < 0) {
 1483             line = -line;
 1484             Line[0] = CONTROL('n');
 1485         } else {
 1486             Line[0] = CONTROL('p');
 1487         }
 1488         while (--line >= 0)
 1489             v_write(pty, Line, (size_t) 1);
 1490         }
 1491     }
 1492     }
 1493 }
 1494 
 1495 /*
 1496  * This function handles button-motion events
 1497  */
 1498 /*ARGSUSED*/
 1499 void
 1500 HandleSelectExtend(Widget w,
 1501            XEvent *event,   /* must be XMotionEvent */
 1502            String *params GCC_UNUSED,
 1503            Cardinal *num_params GCC_UNUSED)
 1504 {
 1505     XtermWidget xw;
 1506 
 1507     if ((xw = getXtermWidget(w)) != 0) {
 1508     TScreen *screen = TScreenOf(xw);
 1509     CELL cell;
 1510 
 1511     TRACE_EVENT("HandleSelectExtend", event, params, num_params);
 1512 
 1513     screen->selection_time = event->xmotion.time;
 1514     switch (screen->eventMode) {
 1515         /* If not in one of the DEC mouse-reporting modes */
 1516     case LEFTEXTENSION:
 1517     case RIGHTEXTENSION:
 1518         PointToCELL(screen, event->xmotion.y, event->xmotion.x, &cell);
 1519         ExtendExtend(xw, &cell);
 1520         break;
 1521 
 1522         /* If in motion reporting mode, send mouse position to
 1523            character process as a key sequence \E[M... */
 1524     case NORMAL:
 1525         /* will get here if send_mouse_pos != MOUSE_OFF */
 1526         if (okSendMousePos(xw) == BTN_EVENT_MOUSE
 1527         || okSendMousePos(xw) == ANY_EVENT_MOUSE) {
 1528         (void) SendMousePosition(xw, event);
 1529         }
 1530         break;
 1531     }
 1532     }
 1533 }
 1534 
 1535 void
 1536 HandleKeyboardSelectExtend(Widget w,
 1537                XEvent *event GCC_UNUSED,    /* must be XButtonEvent */
 1538                String *params GCC_UNUSED,
 1539                Cardinal *num_params GCC_UNUSED)
 1540 {
 1541     XtermWidget xw;
 1542 
 1543     if ((xw = getXtermWidget(w)) != 0) {
 1544     TScreen *screen = TScreenOf(xw);
 1545 
 1546     TRACE_EVENT("HandleKeyboardSelectExtend", event, params, num_params);
 1547     ExtendExtend(xw, &screen->cursorp);
 1548     }
 1549 }
 1550 
 1551 static void
 1552 do_select_end(XtermWidget xw,
 1553           XEvent *event,    /* must be XButtonEvent */
 1554           String *params,   /* selections */
 1555           Cardinal *num_params,
 1556           Bool use_cursor_loc)
 1557 {
 1558     TScreen *screen = TScreenOf(xw);
 1559 
 1560     screen->selection_time = event->xbutton.time;
 1561 
 1562     TRACE(("do_select_end %s @%ld\n",
 1563        visibleEventMode(screen->eventMode),
 1564        screen->selection_time));
 1565 
 1566     switch (screen->eventMode) {
 1567     case NORMAL:
 1568     (void) SendMousePosition(xw, event);
 1569     break;
 1570     case LEFTEXTENSION:
 1571     case RIGHTEXTENSION:
 1572     EndExtend(xw, event, params, *num_params, use_cursor_loc);
 1573 #if OPT_READLINE
 1574     readlineExtend(xw, event);
 1575 #endif /* OPT_READLINE */
 1576     break;
 1577     }
 1578 }
 1579 
 1580 void
 1581 HandleSelectEnd(Widget w,
 1582         XEvent *event,  /* must be XButtonEvent */
 1583         String *params, /* selections */
 1584         Cardinal *num_params)
 1585 {
 1586     XtermWidget xw;
 1587 
 1588     if ((xw = getXtermWidget(w)) != 0) {
 1589     TRACE(("HandleSelectEnd\n"));
 1590     do_select_end(xw, event, params, num_params, False);
 1591     }
 1592 }
 1593 
 1594 void
 1595 HandleKeyboardSelectEnd(Widget w,
 1596             XEvent *event,  /* must be XButtonEvent */
 1597             String *params,     /* selections */
 1598             Cardinal *num_params)
 1599 {
 1600     XtermWidget xw;
 1601 
 1602     if ((xw = getXtermWidget(w)) != 0) {
 1603     TRACE(("HandleKeyboardSelectEnd\n"));
 1604     do_select_end(xw, event, params, num_params, True);
 1605     }
 1606 }
 1607 
 1608 void
 1609 HandlePointerMotion(Widget w,
 1610             XEvent *event,
 1611             String *params, /* selections */
 1612             Cardinal *num_params)
 1613 {
 1614     XtermWidget xw;
 1615 
 1616     (void) params;
 1617     (void) num_params;
 1618     if ((xw = getXtermWidget(w)) != 0) {
 1619     TRACE(("HandlePointerMotion\n"));
 1620     if (event->type == MotionNotify)
 1621         (void) SendMousePosition(xw, event);
 1622     }
 1623 }
 1624 
 1625 void
 1626 HandlePointerButton(Widget w,
 1627             XEvent *event,
 1628             String *params, /* selections */
 1629             Cardinal *num_params)
 1630 {
 1631     XtermWidget xw;
 1632 
 1633     (void) params;
 1634     (void) num_params;
 1635     if ((xw = getXtermWidget(w)) != 0) {
 1636     TRACE(("HandlePointerButton\n"));
 1637     if (IsBtnEvent(event))
 1638         (void) SendMousePosition(xw, event);
 1639     }
 1640 }
 1641 
 1642 /*
 1643  * Copy the selection data to the given target(s).
 1644  */
 1645 void
 1646 HandleCopySelection(Widget w,
 1647             XEvent *event,
 1648             String *params, /* list of targets */
 1649             Cardinal *num_params)
 1650 {
 1651     XtermWidget xw;
 1652 
 1653     if ((xw = getXtermWidget(w)) != 0) {
 1654     TRACE_EVENT("HandleCopySelection", event, params, num_params);
 1655     SelectSet(xw, event, params, *num_params);
 1656     }
 1657 }
 1658 
 1659 struct _SelectionList {
 1660     String *params;
 1661     Cardinal count;
 1662     Atom *targets;
 1663     Time time;
 1664 };
 1665 
 1666 static unsigned
 1667 DECtoASCII(unsigned ch)
 1668 {
 1669     if (xtermIsDecGraphic(ch)) {
 1670     ch = CharOf("###########+++++##-##++++|######"[ch]);
 1671     /*           01234567890123456789012345678901 */
 1672     }
 1673     return ch;
 1674 }
 1675 
 1676 #if OPT_WIDE_CHARS
 1677 static Cardinal
 1678 addXtermChar(Char **buffer, Cardinal *used, Cardinal offset, unsigned value)
 1679 {
 1680     if (offset + 1 >= *used) {
 1681     *used = 1 + (2 * (offset + 1));
 1682     allocXtermChars(buffer, *used);
 1683     }
 1684     (*buffer)[offset++] = (Char) value;
 1685     return offset;
 1686 }
 1687 #define AddChar(buffer, used, offset, value) \
 1688     offset = addXtermChar(buffer, used, offset, (unsigned) value)
 1689 
 1690 /*
 1691  * Convert a UTF-8 string to Latin-1, replacing non Latin-1 characters by `#',
 1692  * or ASCII/Latin-1 equivalents for special cases.
 1693  */
 1694 static Char *
 1695 UTF8toLatin1(TScreen *screen, Char *s, unsigned long len, unsigned long *result)
 1696 {
 1697     static Char *buffer;
 1698     static Cardinal used;
 1699 
 1700     Cardinal offset = 0;
 1701 
 1702     if (len != 0) {
 1703     PtyData data;
 1704 
 1705     fakePtyData(&data, s, s + len);
 1706     while (decodeUtf8(screen, &data)) {
 1707         Bool fails = False;
 1708         Bool extra = False;
 1709         IChar value;
 1710         skipPtyData(&data, value);
 1711         if (value == UCS_REPL) {
 1712         fails = True;
 1713         } else if (value < 256) {
 1714         AddChar(&buffer, &used, offset, CharOf(value));
 1715         } else {
 1716         unsigned eqv = ucs2dec(screen, value);
 1717         if (xtermIsDecGraphic(eqv)) {
 1718             AddChar(&buffer, &used, offset, DECtoASCII(eqv));
 1719         } else {
 1720             eqv = AsciiEquivs(value);
 1721             if (eqv == value) {
 1722             fails = True;
 1723             } else {
 1724             AddChar(&buffer, &used, offset, eqv);
 1725             }
 1726             if (isWide((wchar_t) value))
 1727             extra = True;
 1728         }
 1729         }
 1730 
 1731         /*
 1732          * If we're not able to plug in a single-byte result, insert the
 1733          * defaultString (which normally is a single "#", but could be
 1734          * whatever the user wants).
 1735          */
 1736         if (fails) {
 1737         const Char *p;
 1738 
 1739         for (p = (const Char *) screen->default_string; *p != '\0'; ++p) {
 1740             AddChar(&buffer, &used, offset, *p);
 1741         }
 1742         }
 1743         if (extra)
 1744         AddChar(&buffer, &used, offset, ' ');
 1745     }
 1746     AddChar(&buffer, &used, offset, '\0');
 1747     *result = (unsigned long) (offset - 1);
 1748     } else {
 1749     *result = 0;
 1750     }
 1751     return buffer;
 1752 }
 1753 
 1754 int
 1755 xtermUtf8ToTextList(XtermWidget xw,
 1756             XTextProperty * text_prop,
 1757             char ***text_list,
 1758             int *text_list_count)
 1759 {
 1760     TScreen *screen = TScreenOf(xw);
 1761     Display *dpy = screen->display;
 1762     int rc = -1;
 1763 
 1764     if (text_prop->format == 8
 1765     && (rc = Xutf8TextPropertyToTextList(dpy, text_prop,
 1766                          text_list,
 1767                          text_list_count)) >= 0) {
 1768     if (*text_list != NULL && *text_list_count != 0) {
 1769         int i;
 1770         Char *data;
 1771         char **new_text_list, *tmp;
 1772         unsigned long size, new_size;
 1773 
 1774         TRACE(("xtermUtf8ToTextList size %d\n", *text_list_count));
 1775 
 1776         /*
 1777          * XLib StringList actually uses only two pointers, one for the
 1778          * list itself, and one for the data.  Pointer to the data is the
 1779          * first element of the list, the rest (if any) list elements point
 1780          * to the same memory block as the first element
 1781          */
 1782         new_size = 0;
 1783         for (i = 0; i < *text_list_count; ++i) {
 1784         data = (Char *) (*text_list)[i];
 1785         size = strlen((*text_list)[i]) + 1;
 1786         (void) UTF8toLatin1(screen, data, size, &size);
 1787         new_size += size + 1;
 1788         }
 1789         new_text_list = TypeXtMallocN(char *, *text_list_count);
 1790         new_text_list[0] = tmp = XtMalloc((Cardinal) new_size);
 1791         for (i = 0; i < (*text_list_count); ++i) {
 1792         data = (Char *) (*text_list)[i];
 1793         size = strlen((*text_list)[i]) + 1;
 1794         if ((data = UTF8toLatin1(screen, data, size, &size)) != 0) {
 1795             memcpy(tmp, data, size + 1);
 1796             new_text_list[i] = tmp;
 1797             tmp += size + 1;
 1798         }
 1799         }
 1800         XFreeStringList((*text_list));
 1801         *text_list = new_text_list;
 1802     } else {
 1803         rc = -1;
 1804     }
 1805     }
 1806     return rc;
 1807 }
 1808 #endif /* OPT_WIDE_CHARS */
 1809 
 1810 static char *
 1811 parseItem(char *value, char *nextc)
 1812 {
 1813     char *nextp = value;
 1814     while (*nextp != '\0' && *nextp != ',') {
 1815     *nextp = x_toupper(*nextp);
 1816     ++nextp;
 1817     }
 1818     *nextc = *nextp;
 1819     *nextp = '\0';
 1820 
 1821     return nextp;
 1822 }
 1823 
 1824 /*
 1825  * All of the wanted strings are unique in the first character, so we can
 1826  * use simple abbreviations.
 1827  */
 1828 static Bool
 1829 sameItem(const char *actual, const char *wanted)
 1830 {
 1831     Bool result = False;
 1832     size_t have = strlen(actual);
 1833     size_t need = strlen(wanted);
 1834 
 1835     if (have != 0 && have <= need) {
 1836     if (!strncmp(actual, wanted, have)) {
 1837         TRACE(("...matched \"%s\"\n", wanted));
 1838         result = True;
 1839     }
 1840     }
 1841 
 1842     return result;
 1843 }
 1844 
 1845 /*
 1846  * Handle the eightBitSelectTypes or utf8SelectTypes resource values.
 1847  */
 1848 static Bool
 1849 overrideTargets(Widget w, String value, Atom **resultp)
 1850 {
 1851     Bool override = False;
 1852     XtermWidget xw;
 1853 
 1854     if ((xw = getXtermWidget(w)) != 0) {
 1855     TScreen *screen = TScreenOf(xw);
 1856 
 1857     if (!IsEmpty(value)) {
 1858         char *copied = x_strdup(value);
 1859         if (copied != 0) {
 1860         Atom *result = 0;
 1861         Cardinal count = 1;
 1862         int n;
 1863 
 1864         TRACE(("decoding SelectTypes \"%s\"\n", value));
 1865         for (n = 0; copied[n] != '\0'; ++n) {
 1866             if (copied[n] == ',')
 1867             ++count;
 1868         }
 1869         result = TypeXtMallocN(Atom, (2 * count) + 1);
 1870         if (result == NULL) {
 1871             TRACE(("Couldn't allocate selection types\n"));
 1872         } else {
 1873             char nextc = '?';
 1874             char *listp = (char *) copied;
 1875             count = 0;
 1876             do {
 1877             char *nextp = parseItem(listp, &nextc);
 1878             char *item = x_strtrim(listp);
 1879             size_t len = (item ? strlen(item) : 0);
 1880 
 1881             if (len == 0) {
 1882                 /* EMPTY */ ;
 1883             }
 1884 #if OPT_WIDE_CHARS
 1885             else if (sameItem(item, "UTF8")) {
 1886                 result[count++] = XA_UTF8_STRING(XtDisplay(w));
 1887             }
 1888 #endif
 1889             else if (sameItem(item, "I18N")) {
 1890                 if (screen->i18nSelections) {
 1891                 result[count++] = XA_TEXT(XtDisplay(w));
 1892                 result[count++] = XA_COMPOUND_TEXT(XtDisplay(w));
 1893                 }
 1894             } else if (sameItem(item, "TEXT")) {
 1895                 result[count++] = XA_TEXT(XtDisplay(w));
 1896             } else if (sameItem(item, "COMPOUND_TEXT")) {
 1897                 result[count++] = XA_COMPOUND_TEXT(XtDisplay(w));
 1898             } else if (sameItem(item, "STRING")) {
 1899                 result[count++] = XA_STRING;
 1900             }
 1901             *nextp++ = nextc;
 1902             listp = nextp;
 1903             free(item);
 1904             } while (nextc != '\0');
 1905             if (count) {
 1906             result[count] = None;
 1907             override = True;
 1908             *resultp = result;
 1909             } else {
 1910             XtFree((char *) result);
 1911             }
 1912         }
 1913         free(copied);
 1914         } else {
 1915         TRACE(("Couldn't allocate copy of selection types\n"));
 1916         }
 1917     }
 1918     }
 1919     return override;
 1920 }
 1921 
 1922 #if OPT_WIDE_CHARS
 1923 static Atom *
 1924 allocUtf8Targets(Widget w, TScreen *screen)
 1925 {
 1926     Atom **resultp = &(screen->selection_targets_utf8);
 1927 
 1928     if (*resultp == 0) {
 1929     Atom *result;
 1930 
 1931     if (!overrideTargets(w, screen->utf8_select_types, &result)) {
 1932         result = TypeXtMallocN(Atom, 5);
 1933         if (result == NULL) {
 1934         TRACE(("Couldn't allocate utf-8 selection targets\n"));
 1935         } else {
 1936         int n = 0;
 1937 
 1938         if (XSupportsLocale()) {
 1939             result[n++] = XA_UTF8_STRING(XtDisplay(w));
 1940 #ifdef X_HAVE_UTF8_STRING
 1941             if (screen->i18nSelections) {
 1942             result[n++] = XA_TEXT(XtDisplay(w));
 1943             result[n++] = XA_COMPOUND_TEXT(XtDisplay(w));
 1944             }
 1945 #endif
 1946         }
 1947         result[n++] = XA_STRING;
 1948         result[n] = None;
 1949         }
 1950     }
 1951 
 1952     *resultp = result;
 1953     }
 1954 
 1955     return *resultp;
 1956 }
 1957 #endif
 1958 
 1959 static Atom *
 1960 alloc8bitTargets(Widget w, TScreen *screen)
 1961 {
 1962     Atom **resultp = &(screen->selection_targets_8bit);
 1963 
 1964     if (*resultp == 0) {
 1965     Atom *result = 0;
 1966 
 1967     if (!overrideTargets(w, screen->eightbit_select_types, &result)) {
 1968         result = TypeXtMallocN(Atom, 5);
 1969         if (result == NULL) {
 1970         TRACE(("Couldn't allocate 8bit selection targets\n"));
 1971         } else {
 1972         int n = 0;
 1973 
 1974         if (XSupportsLocale()) {
 1975 #ifdef X_HAVE_UTF8_STRING
 1976             result[n++] = XA_UTF8_STRING(XtDisplay(w));
 1977 #endif
 1978             if (screen->i18nSelections) {
 1979             result[n++] = XA_TEXT(XtDisplay(w));
 1980             result[n++] = XA_COMPOUND_TEXT(XtDisplay(w));
 1981             }
 1982         }
 1983         result[n++] = XA_STRING;
 1984         result[n] = None;
 1985         }
 1986     }
 1987 
 1988     *resultp = result;
 1989     }
 1990 
 1991     return *resultp;
 1992 }
 1993 
 1994 static Atom *
 1995 _SelectionTargets(Widget w)
 1996 {
 1997     Atom *result;
 1998     XtermWidget xw;
 1999 
 2000     if ((xw = getXtermWidget(w)) == 0) {
 2001     result = NULL;
 2002     } else {
 2003     TScreen *screen = TScreenOf(xw);
 2004 
 2005 #if OPT_WIDE_CHARS
 2006     if (screen->wide_chars) {
 2007         result = allocUtf8Targets(w, screen);
 2008     } else
 2009 #endif
 2010     {
 2011         /* not screen->wide_chars */
 2012         result = alloc8bitTargets(w, screen);
 2013     }
 2014     }
 2015 
 2016     return result;
 2017 }
 2018 
 2019 #define isSELECT(value) (!strcmp(NonNull(value), "SELECT"))
 2020 
 2021 static int
 2022 DefaultSelection(TScreen *screen)
 2023 {
 2024     return (screen->selectToClipboard ? 1 : 0);
 2025 }
 2026 
 2027 static int
 2028 TargetToSelection(TScreen *screen, String name)
 2029 {
 2030     int result = -1;
 2031     int cutb;
 2032 
 2033     if (isSELECT(name)) {
 2034     result = DefaultSelection(screen);
 2035     } else if (!strcmp(name, PRIMARY_NAME)) {
 2036     result = PRIMARY_CODE;
 2037     } else if (!strcmp(name, CLIPBOARD_NAME)) {
 2038     result = CLIPBOARD_CODE;
 2039     } else if (!strcmp(name, SECONDARY_NAME)) {
 2040     result = SECONDARY_CODE;
 2041     } else if (sscanf(name, "CUT_BUFFER%d", &cutb) == 1) {
 2042     if (cutb >= 0 && cutb < MAX_CUT_BUFFER) {
 2043         result = CutBufferToCode(cutb);
 2044     } else {
 2045         xtermWarning("unexpected cut-buffer code: %d\n", cutb);
 2046     }
 2047     } else {
 2048     xtermWarning("unexpected selection target: %s\n", name);
 2049     }
 2050     TRACE2(("TargetToSelection(%s) ->%d\n", name, result));
 2051     return result;
 2052 }
 2053 
 2054 void
 2055 UnmapSelections(XtermWidget xw)
 2056 {
 2057     TScreen *screen = TScreenOf(xw);
 2058 
 2059     FreeAndNull(screen->mappedSelect);
 2060 }
 2061 
 2062 /*
 2063  * xterm generally uses the primary selection.  Some applications prefer
 2064  * (or are limited to) the clipboard.  Since the translations resource is
 2065  * complicated, users seldom change the way it affects selection.  But it
 2066  * is simple to remap the choice between primary and clipboard before the
 2067  * call to XmuInternStrings().
 2068  */
 2069 static String *
 2070 MapSelections(XtermWidget xw, String *params, Cardinal num_params)
 2071 {
 2072     String *result = params;
 2073 
 2074     if (params != 0 && num_params > 0) {
 2075     Cardinal j;
 2076     Boolean map = False;
 2077 
 2078     for (j = 0; j < num_params; ++j) {
 2079         TRACE(("param[%d]:%s\n", j, params[j]));
 2080         if (isSELECT(params[j])) {
 2081         map = True;
 2082         break;
 2083         }
 2084     }
 2085     if (map) {
 2086         TScreen *screen = TScreenOf(xw);
 2087         const char *mapTo = (screen->selectToClipboard
 2088                  ? CLIPBOARD_NAME
 2089                  : PRIMARY_NAME);
 2090 
 2091         UnmapSelections(xw);
 2092         if ((result = TypeMallocN(String, num_params + 1)) != 0) {
 2093         result[num_params] = 0;
 2094         for (j = 0; j < num_params; ++j) {
 2095             result[j] = (String) (isSELECT(params[j])
 2096                       ? mapTo
 2097                       : params[j]);
 2098             if (result[j] == 0) {
 2099             UnmapSelections(xw);
 2100             FreeAndNull(result);
 2101             break;
 2102             }
 2103         }
 2104         screen->mappedSelect = result;
 2105         }
 2106     }
 2107     }
 2108     return result;
 2109 }
 2110 
 2111 /*
 2112  * Lookup the cut-buffer number, which will be in the range 0-7.
 2113  * If it is not a cut-buffer, it is a type of selection, e.g., primary.
 2114  */
 2115 static int
 2116 CutBuffer(Atom code)
 2117 {
 2118     int cutbuffer;
 2119     switch ((unsigned) code) {
 2120     case XA_CUT_BUFFER0:
 2121     cutbuffer = 0;
 2122     break;
 2123     case XA_CUT_BUFFER1:
 2124     cutbuffer = 1;
 2125     break;
 2126     case XA_CUT_BUFFER2:
 2127     cutbuffer = 2;
 2128     break;
 2129     case XA_CUT_BUFFER3:
 2130     cutbuffer = 3;
 2131     break;
 2132     case XA_CUT_BUFFER4:
 2133     cutbuffer = 4;
 2134     break;
 2135     case XA_CUT_BUFFER5:
 2136     cutbuffer = 5;
 2137     break;
 2138     case XA_CUT_BUFFER6:
 2139     cutbuffer = 6;
 2140     break;
 2141     case XA_CUT_BUFFER7:
 2142     cutbuffer = 7;
 2143     break;
 2144     default:
 2145     cutbuffer = -1;
 2146     break;
 2147     }
 2148     TRACE2(("CutBuffer(%d) = %d\n", (int) code, cutbuffer));
 2149     return cutbuffer;
 2150 }
 2151 
 2152 #if OPT_PASTE64
 2153 static void
 2154 FinishPaste64(XtermWidget xw)
 2155 {
 2156     TScreen *screen = TScreenOf(xw);
 2157 
 2158     TRACE(("FinishPaste64(%d)\n", screen->base64_paste));
 2159     if (screen->base64_paste) {
 2160     screen->base64_paste = 0;
 2161     unparseputc1(xw, screen->base64_final);
 2162     unparse_end(xw);
 2163     }
 2164 }
 2165 #endif
 2166 
 2167 #if !OPT_PASTE64
 2168 static
 2169 #endif
 2170 void
 2171 xtermGetSelection(Widget w,
 2172           Time ev_time,
 2173           String *params,   /* selections in precedence order */
 2174           Cardinal num_params,
 2175           Atom *targets)
 2176 {
 2177     Atom selection;
 2178     int cutbuffer;
 2179     Atom target;
 2180 
 2181     XtermWidget xw;
 2182 
 2183     if (num_params == 0)
 2184     return;
 2185     if ((xw = getXtermWidget(w)) == 0)
 2186     return;
 2187 
 2188     TRACE(("xtermGetSelection num_params %d @%ld\n", num_params, ev_time));
 2189     params = MapSelections(xw, params, num_params);
 2190 
 2191     XmuInternStrings(XtDisplay(w), params, (Cardinal) 1, &selection);
 2192     cutbuffer = CutBuffer(selection);
 2193 
 2194     TRACE(("Cutbuffer: %d, target: %s\n", cutbuffer,
 2195        (targets
 2196         ? visibleSelectionTarget(XtDisplay(w), targets[0])
 2197         : "None")));
 2198 
 2199     if (cutbuffer >= 0) {
 2200     int inbytes;
 2201     unsigned long nbytes;
 2202     int fmt8 = 8;
 2203     Atom type = XA_STRING;
 2204     char *line;
 2205 
 2206     /* 'line' is freed in SelectionReceived */
 2207     line = XFetchBuffer(XtDisplay(w), &inbytes, cutbuffer);
 2208     nbytes = (unsigned long) inbytes;
 2209 
 2210     if (nbytes > 0) {
 2211         SelectionReceived(w, NULL, &selection, &type, (XtPointer) line,
 2212                   &nbytes, &fmt8);
 2213     } else if (num_params > 1) {
 2214         xtermGetSelection(w, ev_time, params + 1, num_params - 1, NULL);
 2215     }
 2216 #if OPT_PASTE64
 2217     else {
 2218         FinishPaste64(xw);
 2219     }
 2220 #endif
 2221     } else {
 2222 
 2223     if (targets == NULL || targets[0] == None) {
 2224         targets = _SelectionTargets(w);
 2225     }
 2226 
 2227     if (targets != 0) {
 2228         struct _SelectionList *list;
 2229 
 2230         target = targets[0];
 2231 
 2232         if (targets[1] == None) {   /* last target in list */
 2233         params++;
 2234         num_params--;
 2235         targets = _SelectionTargets(w);
 2236         } else {
 2237         targets = &(targets[1]);
 2238         }
 2239 
 2240         if (num_params) {
 2241         /* 'list' is freed in SelectionReceived */
 2242         list = TypeXtMalloc(struct _SelectionList);
 2243         if (list != 0) {
 2244             list->params = params;
 2245             list->count = num_params;
 2246             list->targets = targets;
 2247             list->time = ev_time;
 2248         }
 2249         } else {
 2250         list = NULL;
 2251         }
 2252 
 2253         XtGetSelectionValue(w, selection,
 2254                 target,
 2255                 SelectionReceived,
 2256                 (XtPointer) list, ev_time);
 2257     }
 2258     }
 2259 }
 2260 
 2261 #if OPT_TRACE && OPT_WIDE_CHARS
 2262 static void
 2263 GettingSelection(Display *dpy, Atom type, Char *line, unsigned long len)
 2264 {
 2265     Char *cp;
 2266     const char *name = TraceAtomName(dpy, type);
 2267 
 2268     TRACE(("Getting %s (type=%ld, length=%ld)\n", name, (long int) type, len));
 2269     for (cp = line; cp < line + len; cp++) {
 2270     TRACE(("[%d:%lu]", (int) (cp + 1 - line), len));
 2271     if (isprint(*cp)) {
 2272         TRACE(("%c\n", *cp));
 2273     } else {
 2274         TRACE(("\\x%02x\n", *cp));
 2275     }
 2276     }
 2277 }
 2278 #else
 2279 #define GettingSelection(dpy,type,line,len) /* nothing */
 2280 #endif
 2281 
 2282 #ifdef VMS
 2283 #  define tty_vwrite(pty,lag,l)     tt_write(lag,l)
 2284 #else /* !( VMS ) */
 2285 #  define tty_vwrite(pty,lag,l)     v_write(pty,lag,(size_t) l)
 2286 #endif /* defined VMS */
 2287 
 2288 #if OPT_PASTE64
 2289 /* Return base64 code character given 6-bit number */
 2290 static const char base64_code[] = "\
 2291 ABCDEFGHIJKLMNOPQRSTUVWXYZ\
 2292 abcdefghijklmnopqrstuvwxyz\
 2293 0123456789+/";
 2294 static void
 2295 base64_flush(TScreen *screen)
 2296 {
 2297     Char x;
 2298 
 2299     TRACE(("base64_flush count %d, pad %d (%d)\n",
 2300        screen->base64_count,
 2301        screen->base64_pad,
 2302        screen->base64_pad & 3));
 2303 
 2304     switch (screen->base64_count) {
 2305     case 0:
 2306     break;
 2307     case 2:
 2308     x = CharOf(base64_code[screen->base64_accu << 4]);
 2309     tty_vwrite(screen->respond, &x, 1);
 2310     break;
 2311     case 4:
 2312     x = CharOf(base64_code[screen->base64_accu << 2]);
 2313     tty_vwrite(screen->respond, &x, 1);
 2314     break;
 2315     }
 2316     if (screen->base64_pad & 3) {
 2317     tty_vwrite(screen->respond,
 2318            (const Char *) "===",
 2319            (unsigned) (3 - (screen->base64_pad & 3)));
 2320     }
 2321     screen->base64_count = 0;
 2322     screen->base64_accu = 0;
 2323     screen->base64_pad = 0;
 2324 }
 2325 #endif /* OPT_PASTE64 */
 2326 
 2327 /*
 2328  * Translate ISO-8859-1 or UTF-8 data to NRCS.
 2329  */
 2330 static void
 2331 ToNational(XtermWidget xw, Char *buffer, size_t *length)
 2332 {
 2333     TScreen *screen = TScreenOf(xw);
 2334     DECNRCM_codes gsetL = screen->gsets[screen->curgl];
 2335     DECNRCM_codes gsetR = screen->gsets[screen->curgr];
 2336 
 2337 #if OPT_WIDE_CHARS
 2338     if ((screen->utf8_nrc_mode | screen->utf8_mode) != uFalse) {
 2339     Char *p;
 2340     PtyData *data = TypeXtMallocX(PtyData, *length);
 2341 
 2342     memset(data, 0, sizeof(*data));
 2343     data->next = data->buffer;
 2344     data->last = data->buffer + *length;
 2345     memcpy(data->buffer, buffer, *length);
 2346     p = buffer;
 2347     while (data->next < data->last) {
 2348         unsigned chr, out, gl, gr;
 2349 
 2350         if (!decodeUtf8(screen, data)) {
 2351         data->utf_size = 1;
 2352         data->utf_data = data->next[0];
 2353         }
 2354         data->next += data->utf_size;
 2355         chr = data->utf_data;
 2356         out = chr;
 2357         if ((gl = xtermCharSetIn(xw, chr, gsetL)) != chr) {
 2358         out = gl;
 2359         } else if ((gr = xtermCharSetIn(xw, chr, gsetR)) != chr) {
 2360         out = gr;
 2361         }
 2362         *p++ = (Char) ((out < 256) ? out : ' ');
 2363     }
 2364     *length = (size_t) (p - buffer);
 2365     free(data);
 2366     } else
 2367 #endif
 2368     {
 2369     Char *p;
 2370 
 2371     for (p = buffer; (size_t) (p - buffer) < *length; ++p) {
 2372         unsigned gl, gr;
 2373         unsigned chr = *p;
 2374         unsigned out = chr;
 2375         if ((gl = xtermCharSetIn(xw, chr, gsetL)) != chr) {
 2376         out = gl;
 2377         } else if ((gr = xtermCharSetIn(xw, chr, gsetR)) != chr) {
 2378         out = gr;
 2379         }
 2380         *p = (Char) out;
 2381     }
 2382     }
 2383 }
 2384 
 2385 static void
 2386 _qWriteSelectionData(XtermWidget xw, Char *lag, size_t length)
 2387 {
 2388     TScreen *screen = TScreenOf(xw);
 2389 
 2390     /*
 2391      * If we are pasting into a window which is using NRCS, we want to map
 2392      * the text from the normal encoding (ISO-8859-1 or UTF-8) into the coding
 2393      * that an application would use to write characters with NRCS.
 2394      *
 2395      * TODO: handle conversion from UTF-8, and adjust length.  This can be done
 2396      * in the same buffer because the target is always 8-bit.
 2397      */
 2398     if ((xw->flags & NATIONAL) && (length != 0)) {
 2399     ToNational(xw, lag, &length);
 2400     }
 2401 #if OPT_PASTE64
 2402     if (screen->base64_paste) {
 2403     /* Send data as base64 */
 2404     Char *p = lag;
 2405     Char buf[64];
 2406     unsigned x = 0;
 2407 
 2408     TRACE(("convert to base64 %lu:%s\n",
 2409            (unsigned long) length,
 2410            visibleChars(p, length)));
 2411 
 2412     /*
 2413      * Handle the case where the selection is from _this_ xterm, which
 2414      * puts part of the reply in the buffer before the selection callback
 2415      * happens.
 2416      */
 2417     if (screen->base64_paste && screen->unparse_len) {
 2418         unparse_end(xw);
 2419     }
 2420     while (length--) {
 2421         switch (screen->base64_count) {
 2422         case 0:
 2423         buf[x++] = CharOf(base64_code[*p >> 2]);
 2424         screen->base64_accu = (unsigned) (*p & 0x3);
 2425         screen->base64_count = 2;
 2426         ++p;
 2427         break;
 2428         case 2:
 2429         buf[x++] = CharOf(base64_code[(screen->base64_accu << 4) +
 2430                           (*p >> 4)]);
 2431         screen->base64_accu = (unsigned) (*p & 0xF);
 2432         screen->base64_count = 4;
 2433         ++p;
 2434         break;
 2435         case 4:
 2436         buf[x++] = CharOf(base64_code[(screen->base64_accu << 2) +
 2437                           (*p >> 6)]);
 2438         buf[x++] = CharOf(base64_code[*p & 0x3F]);
 2439         screen->base64_accu = 0;
 2440         screen->base64_count = 0;
 2441         ++p;
 2442         break;
 2443         }
 2444         if (x >= 63) {
 2445         /* Write 63 or 64 characters */
 2446         screen->base64_pad += x;
 2447         TRACE(("writing base64 interim %s\n", visibleChars(buf, x)));
 2448         tty_vwrite(screen->respond, buf, x);
 2449         x = 0;
 2450         }
 2451     }
 2452     if (x != 0) {
 2453         screen->base64_pad += x;
 2454         TRACE(("writing base64 finish %s\n", visibleChars(buf, x)));
 2455         tty_vwrite(screen->respond, buf, x);
 2456     }
 2457     } else
 2458 #endif /* OPT_PASTE64 */
 2459 #if OPT_READLINE
 2460     if (SCREEN_FLAG(screen, paste_quotes)) {
 2461     Char quote[2];
 2462     quote[0] = (Char) get_tty_lnext(screen->respond, XTERM_LNEXT, "pty");
 2463     quote[1] = 0;
 2464     TRACE(("writing quoted selection data %s\n", visibleChars(lag, length)));
 2465     while (length--) {
 2466         tty_vwrite(screen->respond, quote, 1);
 2467         tty_vwrite(screen->respond, lag++, 1);
 2468     }
 2469     } else
 2470 #endif
 2471     {
 2472     TRACE(("writing selection data %s\n", visibleChars(lag, length)));
 2473     tty_vwrite(screen->respond, lag, length);
 2474     }
 2475 }
 2476 
 2477 static void
 2478 _WriteSelectionData(XtermWidget xw, Char *line, size_t length)
 2479 {
 2480 #if OPT_PASTE64 || OPT_READLINE
 2481     TScreen *screen = TScreenOf(xw);
 2482 #endif
 2483 
 2484     /* in the VMS version, if tt_pasting isn't set to True then qio
 2485        reads aren't blocked and an infinite loop is entered, where the
 2486        pasted text shows up as new input, goes in again, shows up
 2487        again, ad nauseum. */
 2488 #ifdef VMS
 2489     tt_pasting = True;
 2490 #endif
 2491 
 2492 #if OPT_PASTE64
 2493     if (screen->base64_paste) {
 2494     _qWriteSelectionData(xw, line, length);
 2495     base64_flush(screen);
 2496     } else
 2497 #endif
 2498     {
 2499     if (!SCREEN_FLAG(screen, paste_literal_nl)) {
 2500         size_t n;
 2501         for (n = 0; n < length; ++n) {
 2502         if (line[n] == '\n') {
 2503             line[n] = '\r';
 2504         }
 2505         }
 2506     }
 2507 
 2508     _qWriteSelectionData(xw, line, length);
 2509     }
 2510 #ifdef VMS
 2511     tt_pasting = False;
 2512     tt_start_read();        /* reenable reads or a character may be lost */
 2513 #endif
 2514 }
 2515 
 2516 #if OPT_PASTE64 || OPT_READLINE
 2517 static void
 2518 _WriteKey(TScreen *screen, const Char *in)
 2519 {
 2520     Char line[16];
 2521     unsigned count = 0;
 2522     size_t length = strlen((const char *) in);
 2523 
 2524     if (screen->control_eight_bits) {
 2525     line[count++] = ANSI_CSI;
 2526     } else {
 2527     line[count++] = ANSI_ESC;
 2528     line[count++] = '[';
 2529     }
 2530     while (length--)
 2531     line[count++] = *in++;
 2532     line[count++] = '~';
 2533     tty_vwrite(screen->respond, line, count);
 2534 }
 2535 #endif /* OPT_READLINE */
 2536 
 2537 /*
 2538  * Unless enabled by the user, strip control characters other than formatting.
 2539  */
 2540 static size_t
 2541 removeControls(XtermWidget xw, char *value)
 2542 {
 2543     TScreen *screen = TScreenOf(xw);
 2544     size_t dst = 0;
 2545 
 2546     if (screen->allowPasteControls) {
 2547     dst = strlen(value);
 2548     } else {
 2549     size_t src = 0;
 2550     while ((value[dst] = value[src]) != '\0') {
 2551         int ch = CharOf(value[src++]);
 2552 
 2553 #define ReplacePaste(n) \
 2554         if (screen->disallow_paste_ops[n]) \
 2555         value[dst] = ' '
 2556 
 2557         if (ch < 32) {
 2558         ReplacePaste(epC0);
 2559         ReplacePaste(ch);
 2560         ++dst;
 2561         } else if (ch == ANSI_DEL) {
 2562         ReplacePaste(epDEL);
 2563         ++dst;
 2564         }
 2565 #if OPT_WIDE_CHARS
 2566         else if (screen->utf8_inparse || screen->utf8_nrc_mode)
 2567         ++dst;
 2568 #endif
 2569 #if OPT_C1_PRINT || OPT_WIDE_CHARS
 2570         else if (screen->c1_printable)
 2571         ++dst;
 2572 #endif
 2573         else if (ch >= 128 && ch < 160)
 2574         continue;
 2575         else
 2576         ++dst;
 2577     }
 2578     }
 2579     return dst;
 2580 }
 2581 
 2582 #if OPT_SELECTION_OPS
 2583 static void
 2584 beginInternalSelect(XtermWidget xw)
 2585 {
 2586     TScreen *screen = TScreenOf(xw);
 2587     InternalSelect *mydata = &(screen->internal_select);
 2588 
 2589     (void) mydata;
 2590     /* override flags so that SelectionReceived only updates a buffer */
 2591 #if OPT_PASTE64
 2592     mydata->base64_paste = screen->base64_paste;
 2593     screen->base64_paste = 0;
 2594 #endif
 2595 #if OPT_PASTE64 || OPT_READLINE
 2596     mydata->paste_brackets = screen->paste_brackets;
 2597     SCREEN_FLAG_unset(screen, paste_brackets);
 2598 #endif
 2599 }
 2600 
 2601 static void
 2602 finishInternalSelect(XtermWidget xw)
 2603 {
 2604     TScreen *screen = TScreenOf(xw);
 2605     InternalSelect *mydata = &(screen->internal_select);
 2606 
 2607     (void) mydata;
 2608 #if OPT_PASTE64
 2609     screen->base64_paste = mydata->base64_paste;
 2610 #endif
 2611 #if OPT_PASTE64 || OPT_READLINE
 2612     screen->paste_brackets = mydata->paste_brackets;
 2613 #endif
 2614 }
 2615 
 2616 #else
 2617 #define finishInternalSelect(xw)    /* nothing */
 2618 #endif /* OPT_SELECTION_OPS */
 2619 
 2620 /* SelectionReceived: stuff received selection text into pty */
 2621 
 2622 /* ARGSUSED */
 2623 static void
 2624 SelectionReceived(Widget w,
 2625           XtPointer client_data,
 2626           Atom *selection GCC_UNUSED,
 2627           Atom *type,
 2628           XtPointer value,
 2629           unsigned long *length,
 2630           int *format)
 2631 {
 2632     char **text_list = NULL;
 2633     int text_list_count = 0;
 2634     XTextProperty text_prop;
 2635     TScreen *screen;
 2636     Display *dpy;
 2637 #if OPT_TRACE && OPT_WIDE_CHARS
 2638     Char *line = (Char *) value;
 2639 #endif
 2640 
 2641     XtermWidget xw;
 2642 
 2643     if ((xw = getXtermWidget(w)) == 0)
 2644     return;
 2645 
 2646     screen = TScreenOf(xw);
 2647     dpy = XtDisplay(w);
 2648 
 2649     if (*type == 0      /*XT_CONVERT_FAIL */
 2650     || *length == 0
 2651     || value == NULL) {
 2652     TRACE(("...no data to convert\n"));
 2653     goto fail;
 2654     }
 2655 
 2656     text_prop.value = (unsigned char *) value;
 2657     text_prop.encoding = *type;
 2658     text_prop.format = *format;
 2659     text_prop.nitems = *length;
 2660 
 2661     TRACE(("SelectionReceived %s %s format %d, nitems %ld\n",
 2662        TraceAtomName(screen->display, *selection),
 2663        visibleSelectionTarget(dpy, text_prop.encoding),
 2664        text_prop.format,
 2665        text_prop.nitems));
 2666 
 2667 #if OPT_WIDE_CHARS
 2668     if (XSupportsLocale() && screen->wide_chars) {
 2669     if (*type == XA_UTF8_STRING(dpy) ||
 2670         *type == XA_STRING ||
 2671         *type == XA_COMPOUND_TEXT(dpy)) {
 2672         GettingSelection(dpy, *type, line, *length);
 2673         if (Xutf8TextPropertyToTextList(dpy, &text_prop,
 2674                         &text_list,
 2675                         &text_list_count) < 0) {
 2676         TRACE(("default Xutf8 Conversion failed\n"));
 2677         text_list = NULL;
 2678         }
 2679     }
 2680     } else
 2681 #endif /* OPT_WIDE_CHARS */
 2682     {
 2683     /* Convert the selection to locale's multibyte encoding. */
 2684 
 2685     if (*type == XA_UTF8_STRING(dpy) ||
 2686         *type == XA_STRING ||
 2687         *type == XA_COMPOUND_TEXT(dpy)) {
 2688         Status rc;
 2689 
 2690         GettingSelection(dpy, *type, line, *length);
 2691 
 2692 #if OPT_WIDE_CHARS
 2693         if (*type == XA_UTF8_STRING(dpy) &&
 2694         !(screen->wide_chars || screen->c1_printable)) {
 2695         rc = xtermUtf8ToTextList(xw, &text_prop,
 2696                      &text_list, &text_list_count);
 2697         } else
 2698 #endif
 2699         if (*type == XA_STRING && (!XSupportsLocale() || screen->brokenSelections)) {
 2700         rc = XTextPropertyToStringList(&text_prop,
 2701                            &text_list, &text_list_count);
 2702         } else {
 2703         rc = XmbTextPropertyToTextList(dpy, &text_prop,
 2704                            &text_list,
 2705                            &text_list_count);
 2706         }
 2707         if (rc < 0) {
 2708         TRACE(("Conversion failed\n"));
 2709         text_list = NULL;
 2710         }
 2711     }
 2712     }
 2713 
 2714     if (text_list != NULL && text_list_count != 0) {
 2715     int i;
 2716 
 2717 #if OPT_PASTE64
 2718     if (screen->base64_paste) {
 2719         /* EMPTY */ ;
 2720     } else
 2721 #endif
 2722 #if OPT_PASTE64 || OPT_READLINE
 2723     if (SCREEN_FLAG(screen, paste_brackets) && !screen->selectToBuffer) {
 2724         _WriteKey(screen, (const Char *) "200");
 2725     }
 2726 #endif
 2727     for (i = 0; i < text_list_count; i++) {
 2728         size_t len = removeControls(xw, text_list[i]);
 2729 
 2730         if (screen->selectToBuffer) {
 2731         InternalSelect *mydata = &(screen->internal_select);
 2732         if (!mydata->done) {
 2733             size_t have = (mydata->buffer
 2734                    ? strlen(mydata->buffer)
 2735                    : 0);
 2736             size_t need = have + len + 1;
 2737             char *buffer = realloc(mydata->buffer, need);
 2738 
 2739             if (buffer != 0) {
 2740             strcpy(buffer + have, text_list[i]);
 2741             mydata->buffer = buffer;
 2742             }
 2743             TRACE(("FormatSelect %d.%d .. %d.%d %s\n",
 2744                screen->startSel.row,
 2745                screen->startSel.col,
 2746                screen->endSel.row,
 2747                screen->endSel.col,
 2748                mydata->buffer));
 2749             mydata->format_select(w, mydata->format, mydata->buffer,
 2750                       &(screen->startSel),
 2751                       &(screen->endSel));
 2752             mydata->done = True;
 2753         }
 2754 
 2755         } else {
 2756         _WriteSelectionData(xw, (Char *) text_list[i], len);
 2757         }
 2758     }
 2759 #if OPT_PASTE64
 2760     if (screen->base64_paste) {
 2761         FinishPaste64(xw);
 2762     } else
 2763 #endif
 2764 #if OPT_PASTE64 || OPT_READLINE
 2765     if (SCREEN_FLAG(screen, paste_brackets) && !screen->selectToBuffer) {
 2766         _WriteKey(screen, (const Char *) "201");
 2767     }
 2768 #endif
 2769     if (screen->selectToBuffer) {
 2770         InternalSelect *mydata = &(screen->internal_select);
 2771         finishInternalSelect(xw);
 2772         if (mydata->done) {
 2773         free(mydata->format);
 2774         free(mydata->buffer);
 2775         memset(mydata, 0, sizeof(*mydata));
 2776         }
 2777         screen->selectToBuffer = False;
 2778     }
 2779     XFreeStringList(text_list);
 2780     } else {
 2781     TRACE(("...empty text-list\n"));
 2782     goto fail;
 2783     }
 2784 
 2785     XtFree((char *) client_data);
 2786     XtFree((char *) value);
 2787 
 2788     return;
 2789 
 2790   fail:
 2791     if (client_data != 0) {
 2792     struct _SelectionList *list = (struct _SelectionList *) client_data;
 2793 
 2794     TRACE(("SelectionReceived ->xtermGetSelection\n"));
 2795     xtermGetSelection(w, list->time,
 2796               list->params, list->count, list->targets);
 2797     XtFree((char *) client_data);
 2798 #if OPT_PASTE64
 2799     } else {
 2800     FinishPaste64(xw);
 2801 #endif
 2802     }
 2803     return;
 2804 }
 2805 
 2806 void
 2807 HandleInsertSelection(Widget w,
 2808               XEvent *event,    /* assumed to be XButtonEvent* */
 2809               String *params,   /* selections in precedence order */
 2810               Cardinal *num_params)
 2811 {
 2812     XtermWidget xw;
 2813 
 2814     if ((xw = getXtermWidget(w)) != 0) {
 2815     TRACE_EVENT("HandleInsertSelection", event, params, num_params);
 2816     if (!SendMousePosition(xw, event)) {
 2817 #if OPT_READLINE
 2818         int ldelta;
 2819         TScreen *screen = TScreenOf(xw);
 2820         if (IsBtnEvent(event)
 2821         && !OverrideEvent(event)
 2822         && (okSendMousePos(xw) == MOUSE_OFF)
 2823         && SCREEN_FLAG(screen, paste_moves)
 2824         && rowOnCurrentLine(screen, eventRow(screen, event), &ldelta))
 2825         ReadLineMovePoint(xw, eventColBetween(screen, event), ldelta);
 2826 #endif /* OPT_READLINE */
 2827 
 2828         xtermGetSelection(w, event->xbutton.time, params, *num_params, NULL);
 2829     }
 2830     }
 2831 }
 2832 
 2833 static SelectUnit
 2834 EvalSelectUnit(XtermWidget xw,
 2835            Time buttonDownTime,
 2836            SelectUnit defaultUnit,
 2837            unsigned int button)
 2838 {
 2839     TScreen *screen = TScreenOf(xw);
 2840     SelectUnit result;
 2841     int delta;
 2842 
 2843     if (button != screen->lastButton) {
 2844     delta = screen->multiClickTime + 1;
 2845     } else if (screen->lastButtonUpTime == (Time) 0) {
 2846     /* first time and once in a blue moon */
 2847     delta = screen->multiClickTime + 1;
 2848     } else if (buttonDownTime > screen->lastButtonUpTime) {
 2849     /* most of the time */
 2850     delta = (int) (buttonDownTime - screen->lastButtonUpTime);
 2851     } else {
 2852     /* time has rolled over since lastButtonUpTime */
 2853     delta = (int) ((((Time) ~ 0) - screen->lastButtonUpTime) + buttonDownTime);
 2854     }
 2855 
 2856     if (delta > screen->multiClickTime) {
 2857     screen->numberOfClicks = 1;
 2858     result = defaultUnit;
 2859     } else {
 2860     result = screen->selectMap[screen->numberOfClicks % screen->maxClicks];
 2861     screen->numberOfClicks += 1;
 2862     }
 2863     TRACE(("EvalSelectUnit(%d) = %d\n", screen->numberOfClicks, result));
 2864     return result;
 2865 }
 2866 
 2867 static void
 2868 do_select_start(XtermWidget xw,
 2869         XEvent *event,  /* must be XButtonEvent* */
 2870         CELL *cell)
 2871 {
 2872     TScreen *screen = TScreenOf(xw);
 2873 
 2874     if (SendMousePosition(xw, event))
 2875     return;
 2876     screen->selectUnit = EvalSelectUnit(xw,
 2877                     event->xbutton.time,
 2878                     Select_CHAR,
 2879                     event->xbutton.button);
 2880     screen->replyToEmacs = False;
 2881 
 2882 #if OPT_READLINE
 2883     lastButtonDownTime = event->xbutton.time;
 2884 #endif
 2885 
 2886     StartSelect(xw, cell);
 2887 }
 2888 
 2889 /* ARGSUSED */
 2890 void
 2891 HandleSelectStart(Widget w,
 2892           XEvent *event,    /* must be XButtonEvent* */
 2893           String *params GCC_UNUSED,
 2894           Cardinal *num_params GCC_UNUSED)
 2895 {
 2896     XtermWidget xw;
 2897 
 2898     if ((xw = getXtermWidget(w)) != 0) {
 2899     TScreen *screen = TScreenOf(xw);
 2900     CELL cell;
 2901 
 2902     TRACE_EVENT("HandleSelectStart", event, params, num_params);
 2903     screen->firstValidRow = 0;
 2904     screen->lastValidRow = screen->max_row;
 2905     PointToCELL(screen, event->xbutton.y, event->xbutton.x, &cell);
 2906 
 2907 #if OPT_READLINE
 2908     ExtendingSelection = 0;
 2909 #endif
 2910 
 2911     do_select_start(xw, event, &cell);
 2912     }
 2913 }
 2914 
 2915 /* ARGSUSED */
 2916 void
 2917 HandleKeyboardSelectStart(Widget w,
 2918               XEvent *event,    /* must be XButtonEvent* */
 2919               String *params GCC_UNUSED,
 2920               Cardinal *num_params GCC_UNUSED)
 2921 {
 2922     XtermWidget xw;
 2923 
 2924     if ((xw = getXtermWidget(w)) != 0) {
 2925     TScreen *screen = TScreenOf(xw);
 2926 
 2927     TRACE_EVENT("HandleKeyboardSelectStart", event, params, num_params);
 2928     do_select_start(xw, event, &screen->cursorp);
 2929     }
 2930 }
 2931 
 2932 static void
 2933 TrackDown(XtermWidget xw, XButtonEvent *event)
 2934 {
 2935     TScreen *screen = TScreenOf(xw);
 2936     CELL cell;
 2937 
 2938     screen->selectUnit = EvalSelectUnit(xw,
 2939                     event->time,
 2940                     Select_CHAR,
 2941                     event->button);
 2942     if (screen->numberOfClicks > 1) {
 2943     PointToCELL(screen, event->y, event->x, &cell);
 2944     screen->replyToEmacs = True;
 2945     StartSelect(xw, &cell);
 2946     } else {
 2947     screen->waitingForTrackInfo = True;
 2948     EditorButton(xw, event);
 2949     }
 2950 }
 2951 
 2952 #define boundsCheck(x)  if (x < 0) \
 2953                 x = 0; \
 2954             else if (x >= screen->max_row) \
 2955                 x = screen->max_row
 2956 
 2957 void
 2958 TrackMouse(XtermWidget xw,
 2959        int func,
 2960        CELL *start,
 2961        int firstrow,
 2962        int lastrow)
 2963 {
 2964     TScreen *screen = TScreenOf(xw);
 2965 
 2966     if (screen->waitingForTrackInfo) {  /* if Timed, ignore */
 2967     screen->waitingForTrackInfo = False;
 2968 
 2969     if (func != 0) {
 2970         CELL first = *start;
 2971 
 2972         boundsCheck(first.row);
 2973         boundsCheck(firstrow);
 2974         boundsCheck(lastrow);
 2975         screen->firstValidRow = firstrow;
 2976         screen->lastValidRow = lastrow;
 2977         screen->replyToEmacs = True;
 2978         StartSelect(xw, &first);
 2979     }
 2980     }
 2981 }
 2982 
 2983 static void
 2984 StartSelect(XtermWidget xw, const CELL *cell)
 2985 {
 2986     TScreen *screen = TScreenOf(xw);
 2987 
 2988     TRACE(("StartSelect row=%d, col=%d\n", cell->row, cell->col));
 2989     if (screen->cursor_state)
 2990     HideCursor(xw);
 2991     if (screen->numberOfClicks == 1) {
 2992     /* set start of selection */
 2993     screen->rawPos = *cell;
 2994     }
 2995     /* else use old values in rawPos */
 2996     screen->saveStartR = screen->startExt = screen->rawPos;
 2997     screen->saveEndR = screen->endExt = screen->rawPos;
 2998     if (Coordinate(screen, cell) < Coordinate(screen, &(screen->rawPos))) {
 2999     screen->eventMode = LEFTEXTENSION;
 3000     screen->startExt = *cell;
 3001     } else {
 3002     screen->eventMode = RIGHTEXTENSION;
 3003     screen->endExt = *cell;
 3004     }
 3005     ComputeSelect(xw, &(screen->startExt), &(screen->endExt), False, True);
 3006 }
 3007 
 3008 static void
 3009 EndExtend(XtermWidget xw,
 3010       XEvent *event,    /* must be XButtonEvent */
 3011       String *params,   /* selections */
 3012       Cardinal num_params,
 3013       Bool use_cursor_loc)
 3014 {
 3015     CELL cell;
 3016     TScreen *screen = TScreenOf(xw);
 3017 
 3018     TRACE_EVENT("EndExtend", event, params, &num_params);
 3019     if (use_cursor_loc) {
 3020     cell = screen->cursorp;
 3021     } else {
 3022     PointToCELL(screen, event->xbutton.y, event->xbutton.x, &cell);
 3023     }
 3024     ExtendExtend(xw, &cell);
 3025 
 3026     screen->lastButtonUpTime = event->xbutton.time;
 3027     screen->lastButton = event->xbutton.button;
 3028 
 3029     if (!isSameCELL(&(screen->startSel), &(screen->endSel))) {
 3030     if (screen->replyToEmacs) {
 3031         Char line[64];
 3032         unsigned count = 0;
 3033 
 3034         if (screen->control_eight_bits) {
 3035         line[count++] = ANSI_CSI;
 3036         } else {
 3037         line[count++] = ANSI_ESC;
 3038         line[count++] = '[';
 3039         }
 3040         if (isSameCELL(&(screen->rawPos), &(screen->startSel))
 3041         && isSameCELL(&cell, &(screen->endSel))) {
 3042         /* Use short-form emacs select */
 3043 
 3044         switch (screen->extend_coords) {
 3045         case 0:
 3046         case SET_EXT_MODE_MOUSE:
 3047             line[count++] = 't';
 3048             break;
 3049         case SET_SGR_EXT_MODE_MOUSE:
 3050         case SET_PIXEL_POSITION_MOUSE:
 3051             line[count++] = '<';
 3052             break;
 3053         }
 3054 
 3055         count = EmitMousePosition(screen, line, count, screen->endSel.col);
 3056         count = EmitMousePositionSeparator(screen, line, count);
 3057         count = EmitMousePosition(screen, line, count, screen->endSel.row);
 3058 
 3059         switch (screen->extend_coords) {
 3060         case SET_SGR_EXT_MODE_MOUSE:
 3061         case SET_URXVT_EXT_MODE_MOUSE:
 3062         case SET_PIXEL_POSITION_MOUSE:
 3063             line[count++] = 't';
 3064             break;
 3065         }
 3066         } else {
 3067         /* long-form, specify everything */
 3068 
 3069         switch (screen->extend_coords) {
 3070         case 0:
 3071         case SET_EXT_MODE_MOUSE:
 3072             line[count++] = 'T';
 3073             break;
 3074         case SET_SGR_EXT_MODE_MOUSE:
 3075         case SET_PIXEL_POSITION_MOUSE:
 3076             line[count++] = '<';
 3077             break;
 3078         }
 3079 
 3080         count = EmitMousePosition(screen, line, count, screen->startSel.col);
 3081         count = EmitMousePositionSeparator(screen, line, count);
 3082         count = EmitMousePosition(screen, line, count, screen->startSel.row);
 3083         count = EmitMousePositionSeparator(screen, line, count);
 3084         count = EmitMousePosition(screen, line, count, screen->endSel.col);
 3085         count = EmitMousePositionSeparator(screen, line, count);
 3086         count = EmitMousePosition(screen, line, count, screen->endSel.row);
 3087         count = EmitMousePositionSeparator(screen, line, count);
 3088         count = EmitMousePosition(screen, line, count, cell.col);
 3089         count = EmitMousePositionSeparator(screen, line, count);
 3090         count = EmitMousePosition(screen, line, count, cell.row);
 3091 
 3092         switch (screen->extend_coords) {
 3093         case SET_SGR_EXT_MODE_MOUSE:
 3094         case SET_URXVT_EXT_MODE_MOUSE:
 3095         case SET_PIXEL_POSITION_MOUSE:
 3096             line[count++] = 'T';
 3097             break;
 3098         }
 3099         }
 3100         v_write(screen->respond, line, (size_t) count);
 3101         UnHiliteText(xw);
 3102     }
 3103     }
 3104     SelectSet(xw, event, params, num_params);
 3105     screen->eventMode = NORMAL;
 3106 }
 3107 
 3108 void
 3109 HandleSelectSet(Widget w,
 3110         XEvent *event,
 3111         String *params,
 3112         Cardinal *num_params)
 3113 {
 3114     XtermWidget xw;
 3115 
 3116     if ((xw = getXtermWidget(w)) != 0) {
 3117     TRACE_EVENT("HandleSelectSet", event, params, num_params);
 3118     SelectSet(xw, event, params, *num_params);
 3119     }
 3120 }
 3121 
 3122 /* ARGSUSED */
 3123 static void
 3124 SelectSet(XtermWidget xw,
 3125       XEvent *event GCC_UNUSED,
 3126       String *params,
 3127       Cardinal num_params)
 3128 {
 3129     TScreen *screen = TScreenOf(xw);
 3130 
 3131     TRACE(("SelectSet\n"));
 3132     /* Only do select stuff if non-null select */
 3133     if (!isSameCELL(&(screen->startSel), &(screen->endSel))) {
 3134     Cardinal n;
 3135     for (n = 0; n < num_params; ++n) {
 3136         SaltTextAway(xw,
 3137              TargetToSelection(screen, params[n]),
 3138              &(screen->startSel), &(screen->endSel));
 3139     }
 3140     _OwnSelection(xw, params, num_params);
 3141     } else {
 3142     ScrnDisownSelection(xw);
 3143     }
 3144 }
 3145 
 3146 #define Abs(x)      ((x) < 0 ? -(x) : (x))
 3147 
 3148 /* ARGSUSED */
 3149 static void
 3150 do_start_extend(XtermWidget xw,
 3151         XEvent *event,  /* must be XButtonEvent* */
 3152         String *params GCC_UNUSED,
 3153         Cardinal *num_params GCC_UNUSED,
 3154         Bool use_cursor_loc)
 3155 {
 3156     TScreen *screen = TScreenOf(xw);
 3157     int coord;
 3158     CELL cell;
 3159 
 3160     if (SendMousePosition(xw, event))
 3161     return;
 3162 
 3163     screen->firstValidRow = 0;
 3164     screen->lastValidRow = screen->max_row;
 3165 #if OPT_READLINE
 3166     if (OverrideEvent(event)
 3167     || event->xbutton.button != Button3
 3168     || !(SCREEN_FLAG(screen, dclick3_deletes)))
 3169 #endif
 3170     screen->selectUnit = EvalSelectUnit(xw,
 3171                         event->xbutton.time,
 3172                         screen->selectUnit,
 3173                         event->xbutton.button);
 3174     screen->replyToEmacs = False;
 3175 
 3176 #if OPT_READLINE
 3177     CheckSecondPress3(xw, screen, event);
 3178 #endif
 3179 
 3180     if (screen->numberOfClicks == 1
 3181     || (SCREEN_FLAG(screen, dclick3_deletes)
 3182         && !OverrideEvent(event))) {
 3183     /* Save existing selection so we can reestablish it if the guy
 3184        extends past the other end of the selection */
 3185     screen->saveStartR = screen->startExt = screen->startRaw;
 3186     screen->saveEndR = screen->endExt = screen->endRaw;
 3187     } else {
 3188     /* He just needed the selection mode changed, use old values. */
 3189     screen->startExt = screen->startRaw = screen->saveStartR;
 3190     screen->endExt = screen->endRaw = screen->saveEndR;
 3191     }
 3192     if (use_cursor_loc) {
 3193     cell = screen->cursorp;
 3194     } else {
 3195     PointToCELL(screen, event->xbutton.y, event->xbutton.x, &cell);
 3196     }
 3197     coord = Coordinate(screen, &cell);
 3198 
 3199     if (Abs(coord - Coordinate(screen, &(screen->startSel)))
 3200     < Abs(coord - Coordinate(screen, &(screen->endSel)))
 3201     || coord < Coordinate(screen, &(screen->startSel))) {
 3202     /* point is close to left side of selection */
 3203     screen->eventMode = LEFTEXTENSION;
 3204     screen->startExt = cell;
 3205     } else {
 3206     /* point is close to left side of selection */
 3207     screen->eventMode = RIGHTEXTENSION;
 3208     screen->endExt = cell;
 3209     }
 3210     ComputeSelect(xw, &(screen->startExt), &(screen->endExt), True, True);
 3211 
 3212 #if OPT_READLINE
 3213     if (!isSameCELL(&(screen->startSel), &(screen->endSel)))
 3214     ExtendingSelection = 1;
 3215 #endif
 3216 }
 3217 
 3218 static void
 3219 ExtendExtend(XtermWidget xw, const CELL *cell)
 3220 {
 3221     TScreen *screen = TScreenOf(xw);
 3222     int coord = Coordinate(screen, cell);
 3223 
 3224     TRACE(("ExtendExtend row=%d, col=%d\n", cell->row, cell->col));
 3225     if (screen->eventMode == LEFTEXTENSION
 3226     && ((coord + (screen->selectUnit != Select_CHAR))
 3227         > Coordinate(screen, &(screen->endSel)))) {
 3228     /* Whoops, he's changed his mind.  Do RIGHTEXTENSION */
 3229     screen->eventMode = RIGHTEXTENSION;
 3230     screen->startExt = screen->saveStartR;
 3231     } else if (screen->eventMode == RIGHTEXTENSION
 3232            && coord < Coordinate(screen, &(screen->startSel))) {
 3233     /* Whoops, he's changed his mind.  Do LEFTEXTENSION */
 3234     screen->eventMode = LEFTEXTENSION;
 3235     screen->endExt = screen->saveEndR;
 3236     }
 3237     if (screen->eventMode == LEFTEXTENSION) {
 3238     screen->startExt = *cell;
 3239     } else {
 3240     screen->endExt = *cell;
 3241     }
 3242     ComputeSelect(xw, &(screen->startExt), &(screen->endExt), False, True);
 3243 
 3244 #if OPT_READLINE
 3245     if (!isSameCELL(&(screen->startSel), &(screen->endSel)))
 3246     ExtendingSelection = 1;
 3247 #endif
 3248 }
 3249 
 3250 void
 3251 HandleStartExtend(Widget w,
 3252           XEvent *event,    /* must be XButtonEvent* */
 3253           String *params,   /* unused */
 3254           Cardinal *num_params)     /* unused */
 3255 {
 3256     XtermWidget xw;
 3257 
 3258     if ((xw = getXtermWidget(w)) != 0) {
 3259     TRACE_EVENT("HandleStartExtend", event, params, num_params);
 3260     do_start_extend(xw, event, params, num_params, False);
 3261     }
 3262 }
 3263 
 3264 void
 3265 HandleKeyboardStartExtend(Widget w,
 3266               XEvent *event,    /* must be XButtonEvent* */
 3267               String *params,   /* unused */
 3268               Cardinal *num_params)     /* unused */
 3269 {
 3270     XtermWidget xw;
 3271 
 3272     if ((xw = getXtermWidget(w)) != 0) {
 3273     TRACE_EVENT("HandleKeyboardStartExtend", event, params, num_params);
 3274     do_start_extend(xw, event, params, num_params, True);
 3275     }
 3276 }
 3277 
 3278 void
 3279 ScrollSelection(TScreen *screen, int amount, Bool always)
 3280 {
 3281     int minrow = INX2ROW(screen, -screen->savedlines);
 3282     int maxrow = INX2ROW(screen, screen->max_row);
 3283     int maxcol = screen->max_col;
 3284 
 3285 #define scroll_update_one(cell) \
 3286     (cell)->row += amount; \
 3287     if ((cell)->row < minrow) { \
 3288         (cell)->row = minrow; \
 3289         (cell)->col = 0; \
 3290     } \
 3291     if ((cell)->row > maxrow) { \
 3292         (cell)->row = maxrow; \
 3293         (cell)->col = maxcol; \
 3294     }
 3295 
 3296     scroll_update_one(&(screen->startRaw));
 3297     scroll_update_one(&(screen->endRaw));
 3298     scroll_update_one(&(screen->startSel));
 3299     scroll_update_one(&(screen->endSel));
 3300 
 3301     scroll_update_one(&(screen->rawPos));
 3302 
 3303     /*
 3304      * If we are told to scroll the selection but it lies outside the scrolling
 3305      * margins, then that could cause the selection to move (bad).  It is not
 3306      * simple to fix, because this function is called both for the scrollbar
 3307      * actions as well as application scrolling.  The 'always' flag is set in
 3308      * the former case.  The rest of the logic handles the latter.
 3309      */
 3310     if (ScrnHaveSelection(screen)) {
 3311     int adjust;
 3312 
 3313     adjust = ROW2INX(screen, screen->startH.row);
 3314     if (always
 3315         || !ScrnHaveRowMargins(screen)
 3316         || ScrnIsRowInMargins(screen, adjust)) {
 3317         scroll_update_one(&screen->startH);
 3318     }
 3319     adjust = ROW2INX(screen, screen->endH.row);
 3320     if (always
 3321         || !ScrnHaveRowMargins(screen)
 3322         || ScrnIsRowInMargins(screen, adjust)) {
 3323         scroll_update_one(&screen->endH);
 3324     }
 3325     }
 3326 
 3327     screen->startHCoord = Coordinate(screen, &screen->startH);
 3328     screen->endHCoord = Coordinate(screen, &screen->endH);
 3329 }
 3330 
 3331 /*ARGSUSED*/
 3332 void
 3333 ResizeSelection(TScreen *screen, int rows, int cols)
 3334 {
 3335     rows--;         /* decr to get 0-max */
 3336     cols--;
 3337 
 3338     if (screen->startRaw.row > rows)
 3339     screen->startRaw.row = rows;
 3340     if (screen->startSel.row > rows)
 3341     screen->startSel.row = rows;
 3342     if (screen->endRaw.row > rows)
 3343     screen->endRaw.row = rows;
 3344     if (screen->endSel.row > rows)
 3345     screen->endSel.row = rows;
 3346     if (screen->rawPos.row > rows)
 3347     screen->rawPos.row = rows;
 3348 
 3349     if (screen->startRaw.col > cols)
 3350     screen->startRaw.col = cols;
 3351     if (screen->startSel.col > cols)
 3352     screen->startSel.col = cols;
 3353     if (screen->endRaw.col > cols)
 3354     screen->endRaw.col = cols;
 3355     if (screen->endSel.col > cols)
 3356     screen->endSel.col = cols;
 3357     if (screen->rawPos.col > cols)
 3358     screen->rawPos.col = cols;
 3359 }
 3360 
 3361 #if OPT_WIDE_CHARS
 3362 #define isWideCell(row, col) isWideFrg((int)XTERM_CELL(row, col))
 3363 #endif
 3364 
 3365 static void
 3366 PointToCELL(TScreen *screen,
 3367         int y,
 3368         int x,
 3369         CELL *cell)
 3370 /* Convert pixel coordinates to character coordinates.
 3371    Rows are clipped between firstValidRow and lastValidRow.
 3372    Columns are clipped between to be 0 or greater, but are not clipped to some
 3373        maximum value. */
 3374 {
 3375     cell->row = (y - screen->border) / FontHeight(screen);
 3376     if (cell->row < screen->firstValidRow)
 3377     cell->row = screen->firstValidRow;
 3378     else if (cell->row > screen->lastValidRow)
 3379     cell->row = screen->lastValidRow;
 3380     cell->col = (x - OriginX(screen)) / FontWidth(screen);
 3381     if (cell->col < 0)
 3382     cell->col = 0;
 3383     else if (cell->col > MaxCols(screen)) {
 3384     cell->col = MaxCols(screen);
 3385     }
 3386 #if OPT_WIDE_CHARS
 3387     /*
 3388      * If we got a click on the right half of a doublewidth character,
 3389      * pretend it happened on the left half.
 3390      */
 3391     if (cell->col > 0
 3392     && isWideCell(cell->row, cell->col - 1)
 3393     && (XTERM_CELL(cell->row, cell->col) == HIDDEN_CHAR)) {
 3394     cell->col -= 1;
 3395     }
 3396 #endif
 3397 }
 3398 
 3399 /*
 3400  * Find the last column at which text was drawn on the given row.
 3401  */
 3402 static int
 3403 LastTextCol(TScreen *screen, CLineData *ld, int row)
 3404 {
 3405     int i = -1;
 3406 
 3407     if (ld != 0) {
 3408     if (okScrnRow(screen, row)) {
 3409         const IAttr *ch;
 3410         for (i = screen->max_col,
 3411          ch = ld->attribs + i;
 3412          i >= 0 && !(*ch & CHARDRAWN);
 3413          ch--, i--) {
 3414         ;
 3415         }
 3416 #if OPT_DEC_CHRSET
 3417         if (CSET_DOUBLE(GetLineDblCS(ld))) {
 3418         i *= 2;
 3419         }
 3420 #endif
 3421     }
 3422     }
 3423     return (i);
 3424 }
 3425 
 3426 #if !OPT_WIDE_CHARS
 3427 /*
 3428 ** double click table for cut and paste in 8 bits
 3429 **
 3430 ** This table is divided in four parts :
 3431 **
 3432 **  - control characters    [0,0x1f] U [0x80,0x9f]
 3433 **  - separators        [0x20,0x3f] U [0xa0,0xb9]
 3434 **  - binding characters    [0x40,0x7f] U [0xc0,0xff]
 3435 **  - exceptions
 3436 */
 3437 /* *INDENT-OFF* */
 3438 static int charClass[256] =
 3439 {
 3440 /* NUL  SOH  STX  ETX  EOT  ENQ  ACK  BEL */
 3441     32,  1,    1,   1,   1,   1,   1,   1,
 3442 /*  BS   HT   NL   VT   FF   CR   SO   SI */
 3443      1,  32,   1,   1,   1,   1,   1,   1,
 3444 /* DLE  DC1  DC2  DC3  DC4  NAK  SYN  ETB */
 3445      1,   1,   1,   1,   1,   1,   1,   1,
 3446 /* CAN   EM  SUB  ESC   FS   GS   RS   US */
 3447      1,   1,   1,   1,   1,   1,   1,   1,
 3448 /*  SP    !    "    #    $    %    &    ' */
 3449     32,  33,  34,  35,  36,  37,  38,  39,
 3450 /*   (    )    *    +    ,    -    .    / */
 3451     40,  41,  42,  43,  44,  45,  46,  47,
 3452 /*   0    1    2    3    4    5    6    7 */
 3453     48,  48,  48,  48,  48,  48,  48,  48,
 3454 /*   8    9    :    ;    <    =    >    ? */
 3455     48,  48,  58,  59,  60,  61,  62,  63,
 3456 /*   @    A    B    C    D    E    F    G */
 3457     64,  48,  48,  48,  48,  48,  48,  48,
 3458 /*   H    I    J    K    L    M    N    O */
 3459     48,  48,  48,  48,  48,  48,  48,  48,
 3460 /*   P    Q    R    S    T    U    V    W */
 3461     48,  48,  48,  48,  48,  48,  48,  48,
 3462 /*   X    Y    Z    [    \    ]    ^    _ */
 3463     48,  48,  48,  91,  92,  93,  94,  48,
 3464 /*   `    a    b    c    d    e    f    g */
 3465     96,  48,  48,  48,  48,  48,  48,  48,
 3466 /*   h    i    j    k    l    m    n    o */
 3467     48,  48,  48,  48,  48,  48,  48,  48,
 3468 /*   p    q    r    s    t    u    v    w */
 3469     48,  48,  48,  48,  48,  48,  48,  48,
 3470 /*   x    y    z    {    |    }    ~  DEL */
 3471     48,  48,  48, 123, 124, 125, 126,   1,
 3472 /* x80  x81  x82  x83  IND  NEL  SSA  ESA */
 3473     1,    1,   1,   1,   1,   1,   1,   1,
 3474 /* HTS  HTJ  VTS  PLD  PLU   RI  SS2  SS3 */
 3475     1,    1,   1,   1,   1,   1,   1,   1,
 3476 /* DCS  PU1  PU2  STS  CCH   MW  SPA  EPA */
 3477     1,    1,   1,   1,   1,   1,   1,   1,
 3478 /* x98  x99  x9A  CSI   ST  OSC   PM  APC */
 3479     1,    1,   1,   1,   1,   1,   1,   1,
 3480 /*   -    i   c/    L   ox   Y-    |   So */
 3481     160, 161, 162, 163, 164, 165, 166, 167,
 3482 /*  ..   c0   ip   <<    _        R0    - */
 3483     168, 169, 170, 171, 172, 173, 174, 175,
 3484 /*   o   +-    2    3    '    u   q|    . */
 3485     176, 177, 178, 179, 180, 181, 182, 183,
 3486 /*   ,    1    2   >>  1/4  1/2  3/4    ? */
 3487     184, 185, 186, 187, 188, 189, 190, 191,
 3488 /*  A`   A'   A^   A~   A:   Ao   AE   C, */
 3489      48,  48,  48,  48,  48,  48,  48,  48,
 3490 /*  E`   E'   E^   E:   I`   I'   I^   I: */
 3491      48,  48,  48,  48,  48,  48,  48,  48,
 3492 /*  D-   N~   O`   O'   O^   O~   O:    X */
 3493      48,  48,  48,  48,  48,  48,  48, 215,
 3494 /*  O/   U`   U'   U^   U:   Y'    P    B */
 3495      48,  48,  48,  48,  48,  48,  48,  48,
 3496 /*  a`   a'   a^   a~   a:   ao   ae   c, */
 3497      48,  48,  48,  48,  48,  48,  48,  48,
 3498 /*  e`   e'   e^   e:    i`  i'   i^   i: */
 3499      48,  48,  48,  48,  48,  48,  48,  48,
 3500 /*   d   n~   o`   o'   o^   o~   o:   -: */
 3501      48,  48,  48,  48,  48,  48,  48, 247,
 3502 /*  o/   u`   u'   u^   u:   y'    P   y: */
 3503      48,  48,  48,  48,  48,  48,  48,  48};
 3504 /* *INDENT-ON* */
 3505 
 3506 int
 3507 SetCharacterClassRange(int low, /* in range of [0..255] */
 3508                int high,
 3509                int value)   /* arbitrary */
 3510 {
 3511 
 3512     if (low < 0 || high > 255 || high < low)
 3513     return (-1);
 3514 
 3515     for (; low <= high; low++)
 3516     charClass[low] = value;
 3517 
 3518     return (0);
 3519 }
 3520 #endif
 3521 
 3522 static int
 3523 class_of(LineData *ld, CELL *cell)
 3524 {
 3525     CELL temp = *cell;
 3526     int result = 0;
 3527 
 3528 #if OPT_DEC_CHRSET
 3529     if (CSET_DOUBLE(GetLineDblCS(ld))) {
 3530     temp.col /= 2;
 3531     }
 3532 #endif
 3533     if (temp.col < (int) ld->lineSize)
 3534     result = CharacterClass((int) (ld->charData[temp.col]));
 3535     return result;
 3536 }
 3537 
 3538 #if OPT_WIDE_CHARS
 3539 #define CClassSelects(name, cclass) \
 3540      (CClassOf(name) == cclass \
 3541      || XTERM_CELL(screen->name.row, screen->name.col) == HIDDEN_CHAR)
 3542 #else
 3543 #define CClassSelects(name, cclass) \
 3544      (class_of(ld.name, &((screen->name))) == cclass)
 3545 #endif
 3546 
 3547 #define CClassOf(name) class_of(ld.name, &((screen->name)))
 3548 
 3549 #if OPT_REPORT_CCLASS
 3550 static int
 3551 show_cclass_range(int lo, int hi)
 3552 {
 3553     int cclass = CharacterClass(lo);
 3554     int ident = (cclass == lo);
 3555     int more = 0;
 3556     if (ident) {
 3557     int ch;
 3558     for (ch = lo + 1; ch <= hi; ch++) {
 3559         if (CharacterClass(ch) != ch) {
 3560         ident = 0;
 3561         break;
 3562         }
 3563     }
 3564     if (ident && (hi < 255)) {
 3565         ch = hi + 1;
 3566         if (CharacterClass(ch) == ch) {
 3567         if (ch >= 255 || CharacterClass(ch + 1) != ch) {
 3568             more = 1;
 3569         }
 3570         }
 3571     }
 3572     }
 3573     if (!more) {
 3574     if (lo == hi) {
 3575         printf("\t%d", lo);
 3576     } else {
 3577         printf("\t%d-%d", lo, hi);
 3578     }
 3579     if (!ident)
 3580         printf(":%d", cclass);
 3581     if (hi < 255)
 3582         printf(", \\");
 3583     printf("\n");
 3584     }
 3585     return !more;
 3586 }
 3587 
 3588 void
 3589 report_char_class(XtermWidget xw)
 3590 {
 3591     /* simple table, to match documentation */
 3592     static const char charnames[] =
 3593     "NUL\0" "SOH\0" "STX\0" "ETX\0" "EOT\0" "ENQ\0" "ACK\0" "BEL\0"
 3594     " BS\0" " HT\0" " NL\0" " VT\0" " NP\0" " CR\0" " SO\0" " SI\0"
 3595     "DLE\0" "DC1\0" "DC2\0" "DC3\0" "DC4\0" "NAK\0" "SYN\0" "ETB\0"
 3596     "CAN\0" " EM\0" "SUB\0" "ESC\0" " FS\0" " GS\0" " RS\0" " US\0"
 3597     " SP\0" "  !\0" "  \"\0" "  #\0" "  $\0" "  %\0" "  &\0" "  '\0"
 3598     "  (\0" "  )\0" "  *\0" "  +\0" "  ,\0" "  -\0" "  .\0" "  /\0"
 3599     "  0\0" "  1\0" "  2\0" "  3\0" "  4\0" "  5\0" "  6\0" "  7\0"
 3600     "  8\0" "  9\0" "  :\0" "  ;\0" "  <\0" "  =\0" "  >\0" "  ?\0"
 3601     "  @\0" "  A\0" "  B\0" "  C\0" "  D\0" "  E\0" "  F\0" "  G\0"
 3602     "  H\0" "  I\0" "  J\0" "  K\0" "  L\0" "  M\0" "  N\0" "  O\0"
 3603     "  P\0" "  Q\0" "  R\0" "  S\0" "  T\0" "  U\0" "  V\0" "  W\0"
 3604     "  X\0" "  Y\0" "  Z\0" "  [\0" "  \\\0" "  ]\0" "  ^\0" "  _\0"
 3605     "  `\0" "  a\0" "  b\0" "  c\0" "  d\0" "  e\0" "  f\0" "  g\0"
 3606     "  h\0" "  i\0" "  j\0" "  k\0" "  l\0" "  m\0" "  n\0" "  o\0"
 3607     "  p\0" "  q\0" "  r\0" "  s\0" "  t\0" "  u\0" "  v\0" "  w\0"
 3608     "  x\0" "  y\0" "  z\0" "  {\0" "  |\0" "  }\0" "  ~\0" "DEL\0"
 3609     "x80\0" "x81\0" "x82\0" "x83\0" "IND\0" "NEL\0" "SSA\0" "ESA\0"
 3610     "HTS\0" "HTJ\0" "VTS\0" "PLD\0" "PLU\0" " RI\0" "SS2\0" "SS3\0"
 3611     "DCS\0" "PU1\0" "PU2\0" "STS\0" "CCH\0" " MW\0" "SPA\0" "EPA\0"
 3612     "x98\0" "x99\0" "x9A\0" "CSI\0" " ST\0" "OSC\0" " PM\0" "APC\0"
 3613     "  -\0" "  i\0" " c/\0" "  L\0" " ox\0" " Y-\0" "  |\0" " So\0"
 3614     " ..\0" " c0\0" " ip\0" " <<\0" "  _\0" "   \0" " R0\0" "  -\0"
 3615     "  o\0" " +-\0" "  2\0" "  3\0" "  '\0" "  u\0" " q|\0" "  .\0"
 3616     "  ,\0" "  1\0" "  2\0" " >>\0" "1/4\0" "1/2\0" "3/4\0" "  ?\0"
 3617     " A`\0" " A'\0" " A^\0" " A~\0" " A:\0" " Ao\0" " AE\0" " C,\0"
 3618     " E`\0" " E'\0" " E^\0" " E:\0" " I`\0" " I'\0" " I^\0" " I:\0"
 3619     " D-\0" " N~\0" " O`\0" " O'\0" " O^\0" " O~\0" " O:\0" "  X\0"
 3620     " O/\0" " U`\0" " U'\0" " U^\0" " U:\0" " Y'\0" "  P\0" "  B\0"
 3621     " a`\0" " a'\0" " a^\0" " a~\0" " a:\0" " ao\0" " ae\0" " c,\0"
 3622     " e`\0" " e'\0" " e^\0" " e:\0" " i`\0" " i'\0" " i^\0" " i:\0"
 3623     "  d\0" " n~\0" " o`\0" " o'\0" " o^\0" " o~\0" " o:\0" " -:\0"
 3624     " o/\0" " u`\0" " u'\0" " u^\0" " u:\0" " y'\0" "  P\0" " y:\0";
 3625     int ch, dh;
 3626     int class_p;
 3627 
 3628     (void) xw;
 3629 
 3630     printf("static int charClass[256] = {\n");
 3631     for (ch = 0; ch < 256; ++ch) {
 3632     const char *s = charnames + (ch * 4);
 3633     if ((ch & 7) == 0)
 3634         printf("/*");
 3635     printf(" %s ", s);
 3636     if (((ch + 1) & 7) == 0) {
 3637         printf("*/\n  ");
 3638         for (dh = ch - 7; dh <= ch; ++dh) {
 3639         printf(" %3d%s", CharacterClass(dh), dh == 255 ? "};" : ",");
 3640         }
 3641         printf("\n");
 3642     }
 3643     }
 3644 
 3645     /* print the table as if it were the charClass resource */
 3646     printf("\n");
 3647     printf("The table is equivalent to this \"charClass\" resource:\n");
 3648     class_p = CharacterClass(dh = 0);
 3649     for (ch = 0; ch < 256; ++ch) {
 3650     int class_c = CharacterClass(ch);
 3651     if (class_c != class_p) {
 3652         if (show_cclass_range(dh, ch - 1)) {
 3653         dh = ch;
 3654         class_p = class_c;
 3655         }
 3656     }
 3657     }
 3658     if (dh < 255) {
 3659     show_cclass_range(dh, 255);
 3660     }
 3661 
 3662     if_OPT_WIDE_CHARS(TScreenOf(xw), {
 3663     /* if this is a wide-character configuration, print all intervals */
 3664     report_wide_char_class();
 3665     });
 3666 }
 3667 #endif
 3668 
 3669 /*
 3670  * If the given column is past the end of text on the given row, bump to the
 3671  * beginning of the next line.
 3672  */
 3673 static Boolean
 3674 okPosition(TScreen *screen,
 3675        LineData **ld,
 3676        CELL *cell)
 3677 {
 3678     Boolean result = True;
 3679 
 3680     if (cell->row > screen->max_row) {
 3681     result = False;
 3682     TRACE(("okPosition cell row %d > screen max %d\n", cell->row, screen->max_row));
 3683     } else if (cell->col > (LastTextCol(screen, *ld, cell->row) + 1)) {
 3684     TRACE(("okPosition cell col %d > screen max %d\n", cell->col,
 3685            (LastTextCol(screen, *ld, cell->row) + 1)));
 3686     if (cell->row < screen->max_row) {
 3687         TRACE(("okPosition cell row %d < screen max %d\n", cell->row, screen->max_row));
 3688         cell->col = 0;
 3689         *ld = GET_LINEDATA(screen, ++cell->row);
 3690         result = False;
 3691     }
 3692     }
 3693     return result;
 3694 }
 3695 
 3696 static void
 3697 trimLastLine(TScreen *screen,
 3698          LineData **ld,
 3699          CELL *last)
 3700 {
 3701     if (screen->cutNewline && last->row < screen->max_row) {
 3702     last->col = 0;
 3703     *ld = GET_LINEDATA(screen, ++last->row);
 3704     } else {
 3705     last->col = LastTextCol(screen, *ld, last->row) + 1;
 3706     }
 3707 }
 3708 
 3709 #if OPT_SELECT_REGEX
 3710 /*
 3711  * Returns the first row of a wrapped line.
 3712  */
 3713 static int
 3714 firstRowOfLine(TScreen *screen, int row, Bool visible)
 3715 {
 3716     LineData *ld = 0;
 3717     int limit = visible ? 0 : -screen->savedlines;
 3718 
 3719     while (row > limit &&
 3720        (ld = GET_LINEDATA(screen, row - 1)) != 0 &&
 3721        LineTstWrapped(ld)) {
 3722     --row;
 3723     }
 3724     return row;
 3725 }
 3726 
 3727 /*
 3728  * Returns the last row of a wrapped line.
 3729  */
 3730 static int
 3731 lastRowOfLine(TScreen *screen, int row)
 3732 {
 3733     LineData *ld;
 3734 
 3735     while (row < screen->max_row &&
 3736        (ld = GET_LINEDATA(screen, row)) != 0 &&
 3737        LineTstWrapped(ld)) {
 3738     ++row;
 3739     }
 3740     return row;
 3741 }
 3742 
 3743 /*
 3744  * Returns the number of cells on the range of rows.
 3745  */
 3746 static unsigned
 3747 lengthOfLines(TScreen *screen, int firstRow, int lastRow)
 3748 {
 3749     unsigned length = 0;
 3750     int n;
 3751 
 3752     for (n = firstRow; n <= lastRow; ++n) {
 3753     LineData *ld = GET_LINEDATA(screen, n);
 3754     int value = LastTextCol(screen, ld, n);
 3755     if (value >= 0)
 3756         length += (unsigned) (value + 1);
 3757     }
 3758     return length;
 3759 }
 3760 
 3761 /*
 3762  * Make a copy of the wrapped-line which corresponds to the given row as a
 3763  * string of bytes.  Construct an index for the columns from the beginning of
 3764  * the line.
 3765  */
 3766 static char *
 3767 make_indexed_text(TScreen *screen, int row, unsigned length, int *indexed)
 3768 {
 3769     Char *result = 0;
 3770     size_t need = (length + 1);
 3771 
 3772     /*
 3773      * Get a quick upper bound to the number of bytes needed, if the whole
 3774      * string were UTF-8.
 3775      */
 3776     if_OPT_WIDE_CHARS(screen, {
 3777     need *= ((screen->lineExtra + 1) * 6);
 3778     });
 3779 
 3780     if ((result = TypeCallocN(Char, need + 1)) != 0) {
 3781     LineData *ld = GET_LINEDATA(screen, row);
 3782     unsigned used = 0;
 3783     Char *last = result;
 3784 
 3785     do {
 3786         int col = 0;
 3787         int limit = LastTextCol(screen, ld, row);
 3788 
 3789         while (col <= limit) {
 3790         Char *next = last;
 3791         unsigned data = ld->charData[col];
 3792 
 3793         assert(col < (int) ld->lineSize);
 3794         /* some internal points may not be drawn */
 3795         if (data == 0)
 3796             data = ' ';
 3797 
 3798         if_WIDE_OR_NARROW(screen, {
 3799             next = convertToUTF8(last, data);
 3800         }
 3801         , {
 3802             *next++ = CharOf(data);
 3803         });
 3804 
 3805         if_OPT_WIDE_CHARS(screen, {
 3806             size_t off;
 3807             for_each_combData(off, ld) {
 3808             data = ld->combData[off][col];
 3809             if (data == 0)
 3810                 break;
 3811             next = convertToUTF8(next, data);
 3812             }
 3813         });
 3814 
 3815         indexed[used] = (int) (last - result);
 3816         *next = 0;
 3817         /* TRACE(("index[%d.%d] %d:%s\n", row, used, indexed[used], last)); */
 3818         last = next;
 3819         ++used;
 3820         ++col;
 3821         indexed[used] = (int) (next - result);
 3822         }
 3823     } while (used < length &&
 3824          LineTstWrapped(ld) &&
 3825          (ld = GET_LINEDATA(screen, ++row)) != 0 &&
 3826          row < screen->max_row);
 3827     }
 3828     /* TRACE(("result:%s\n", result)); */
 3829     return (char *) result;
 3830 }
 3831 
 3832 /*
 3833  * Find the column given an offset into the character string by using the
 3834  * index constructed in make_indexed_text().
 3835  */
 3836 static int
 3837 indexToCol(int *indexed, int len, int off)
 3838 {
 3839     int col = 0;
 3840     while (indexed[col] < len) {
 3841     if (indexed[col] >= off)
 3842         break;
 3843     ++col;
 3844     }
 3845     return col;
 3846 }
 3847 
 3848 /*
 3849  * Given a row number, and a column offset from that (which may be wrapped),
 3850  * set the cell to the actual row/column values.
 3851  */
 3852 static void
 3853 columnToCell(TScreen *screen, int row, int col, CELL *cell)
 3854 {
 3855     while (row < screen->max_row) {
 3856     CLineData *ld = GET_LINEDATA(screen, row);
 3857     int last = LastTextCol(screen, ld, row);
 3858 
 3859     /* TRACE(("last(%d) = %d, have %d\n", row, last, col)); */
 3860     if (col <= last) {
 3861         break;
 3862     }
 3863     /*
 3864      * Stop if the current row does not wrap (does not continue the current
 3865      * line).
 3866      */
 3867     if (!LineTstWrapped(ld)) {
 3868         col = last + 1;
 3869         break;
 3870     }
 3871     col -= (last + 1);
 3872     ++row;
 3873     }
 3874     if (col < 0)
 3875     col = 0;
 3876     cell->row = row;
 3877     cell->col = col;
 3878 }
 3879 
 3880 /*
 3881  * Given a cell, find the corresponding column offset.
 3882  */
 3883 static int
 3884 cellToColumn(TScreen *screen, CELL *cell)
 3885 {
 3886     CLineData *ld = 0;
 3887     int col = cell->col;
 3888     int row = firstRowOfLine(screen, cell->row, False);
 3889     while (row < cell->row) {
 3890     ld = GET_LINEDATA(screen, row);
 3891     col += LastTextCol(screen, ld, row++);
 3892     }
 3893 #if OPT_DEC_CHRSET
 3894     if (ld == 0)
 3895     ld = GET_LINEDATA(screen, row);
 3896     if (CSET_DOUBLE(GetLineDblCS(ld)))
 3897     col /= 2;
 3898 #endif
 3899     return col;
 3900 }
 3901 
 3902 static void
 3903 do_select_regex(TScreen *screen, CELL *startc, CELL *endc)
 3904 {
 3905     LineData *ld = GET_LINEDATA(screen, startc->row);
 3906     int inx = ((screen->numberOfClicks - 1) % screen->maxClicks);
 3907     char *expr = screen->selectExpr[inx];
 3908     regex_t preg;
 3909     regmatch_t match;
 3910 
 3911     TRACE(("Select_REGEX[%d]:%s\n", inx, NonNull(expr)));
 3912     if (okPosition(screen, &ld, startc) && expr != 0) {
 3913     if (regcomp(&preg, expr, REG_EXTENDED) == 0) {
 3914         int firstRow = firstRowOfLine(screen, startc->row, True);
 3915         int lastRow = lastRowOfLine(screen, firstRow);
 3916         unsigned size = lengthOfLines(screen, firstRow, lastRow);
 3917         int actual = cellToColumn(screen, startc);
 3918         int *indexed;
 3919 
 3920         TRACE(("regcomp ok rows %d..%d bytes %d\n",
 3921            firstRow, lastRow, size));
 3922 
 3923         if ((indexed = TypeCallocN(int, size + 1)) != 0) {
 3924         char *search;
 3925         if ((search = make_indexed_text(screen,
 3926                         firstRow,
 3927                         size,
 3928                         indexed)) != 0) {
 3929             int len = (int) strlen(search);
 3930             int col;
 3931             int best_col = -1;
 3932             int best_len = -1;
 3933 
 3934             startc->row = 0;
 3935             startc->col = 0;
 3936             endc->row = 0;
 3937             endc->col = 0;
 3938 
 3939             for (col = 0; indexed[col] < len; ++col) {
 3940             if (regexec(&preg,
 3941                     search + indexed[col],
 3942                     (size_t) 1, &match, 0) == 0) {
 3943                 int start_inx = (int) (match.rm_so + indexed[col]);
 3944                 int finis_inx = (int) (match.rm_eo + indexed[col]);
 3945                 int start_col = indexToCol(indexed, len, start_inx);
 3946                 int finis_col = indexToCol(indexed, len, finis_inx);
 3947 
 3948                 if (start_col <= actual &&
 3949                 actual <= finis_col) {
 3950                 int test = finis_col - start_col;
 3951                 if (best_len < test) {
 3952                     best_len = test;
 3953                     best_col = start_col;
 3954                     TRACE(("match column %d len %d\n",
 3955                        best_col,
 3956                        best_len));
 3957                 }
 3958                 }
 3959             }
 3960             }
 3961             if (best_col >= 0) {
 3962             int best_nxt = best_col + best_len;
 3963             columnToCell(screen, firstRow, best_col, startc);
 3964             columnToCell(screen, firstRow, best_nxt, endc);
 3965             TRACE(("search::%s\n", search));
 3966             TRACE(("indexed:%d..%d -> %d..%d\n",
 3967                    best_col, best_nxt,
 3968                    indexed[best_col],
 3969                    indexed[best_nxt]));
 3970             TRACE(("matched:%d:%s\n",
 3971                    indexed[best_nxt] + 1 -
 3972                    indexed[best_col],
 3973                    visibleChars((Char *) (search + indexed[best_col]),
 3974                         (unsigned) (indexed[best_nxt] +
 3975                             1 -
 3976                             indexed[best_col]))));
 3977             }
 3978             free(search);
 3979         }
 3980         free(indexed);
 3981 #if OPT_DEC_CHRSET
 3982         if ((ld = GET_LINEDATA(screen, startc->row)) != 0) {
 3983             if (CSET_DOUBLE(GetLineDblCS(ld)))
 3984             startc->col *= 2;
 3985         }
 3986         if ((ld = GET_LINEDATA(screen, endc->row)) != 0) {
 3987             if (CSET_DOUBLE(GetLineDblCS(ld)))
 3988             endc->col *= 2;
 3989         }
 3990 #endif
 3991         }
 3992         regfree(&preg);
 3993     }
 3994     }
 3995 }
 3996 #endif /* OPT_SELECT_REGEX */
 3997 
 3998 #define InitRow(name) \
 3999     ld.name = GET_LINEDATA(screen, screen->name.row)
 4000 
 4001 #define NextRow(name) \
 4002     ld.name = GET_LINEDATA(screen, ++screen->name.row)
 4003 
 4004 #define PrevRow(name) \
 4005     ld.name = GET_LINEDATA(screen, --screen->name.row)
 4006 
 4007 #define MoreRows(name) \
 4008     (screen->name.row < screen->max_row)
 4009 
 4010 #define isPrevWrapped(name) \
 4011     (screen->name.row > 0 \
 4012        && (ltmp = GET_LINEDATA(screen, screen->name.row - 1)) != 0 \
 4013        && LineTstWrapped(ltmp))
 4014 
 4015 /*
 4016  * sets startSel endSel
 4017  * ensuring that they have legal values
 4018  */
 4019 static void
 4020 ComputeSelect(XtermWidget xw,
 4021           CELL *startc,
 4022           CELL *endc,
 4023           Bool extend,
 4024           Bool normal)
 4025 {
 4026     TScreen *screen = TScreenOf(xw);
 4027 
 4028     int cclass;
 4029     CELL first = *startc;
 4030     CELL last = *endc;
 4031     Boolean ignored = False;
 4032 
 4033     struct {
 4034     LineData *startSel;
 4035     LineData *endSel;
 4036     } ld;
 4037     LineData *ltmp;
 4038 
 4039     TRACE(("ComputeSelect(startRow=%d, startCol=%d, endRow=%d, endCol=%d, %sextend)\n",
 4040        first.row, first.col,
 4041        last.row, last.col,
 4042        extend ? "" : "no"));
 4043 
 4044 #if OPT_WIDE_CHARS
 4045     if (first.col > 1
 4046     && isWideCell(first.row, first.col - 1)
 4047     && XTERM_CELL(first.row, first.col - 0) == HIDDEN_CHAR) {
 4048     TRACE(("Adjusting start. Changing downwards from %i.\n", first.col));
 4049     first.col -= 1;
 4050     if (last.col == (first.col + 1))
 4051         last.col--;
 4052     }
 4053 
 4054     if (last.col > 1
 4055     && isWideCell(last.row, last.col - 1)
 4056     && XTERM_CELL(last.row, last.col) == HIDDEN_CHAR) {
 4057     last.col += 1;
 4058     }
 4059 #endif
 4060 
 4061     if (Coordinate(screen, &first) <= Coordinate(screen, &last)) {
 4062     screen->startSel = screen->startRaw = first;
 4063     screen->endSel = screen->endRaw = last;
 4064     } else {            /* Swap them */
 4065     screen->startSel = screen->startRaw = last;
 4066     screen->endSel = screen->endRaw = first;
 4067     }
 4068 
 4069     InitRow(startSel);
 4070     InitRow(endSel);
 4071 
 4072     switch (screen->selectUnit) {
 4073     case Select_CHAR:
 4074     (void) okPosition(screen, &(ld.startSel), &(screen->startSel));
 4075     (void) okPosition(screen, &(ld.endSel), &(screen->endSel));
 4076     break;
 4077 
 4078     case Select_WORD:
 4079     TRACE(("Select_WORD\n"));
 4080     if (okPosition(screen, &(ld.startSel), &(screen->startSel))) {
 4081         CELL mark;
 4082         cclass = CClassOf(startSel);
 4083         TRACE(("...starting with class %d\n", cclass));
 4084         do {
 4085         mark = screen->startSel;
 4086         --screen->startSel.col;
 4087         if (screen->startSel.col < 0
 4088             && isPrevWrapped(startSel)) {
 4089             PrevRow(startSel);
 4090             screen->startSel.col = LastTextCol(screen, ld.startSel, screen->startSel.row);
 4091         }
 4092         } while (screen->startSel.col >= 0
 4093              && CClassSelects(startSel, cclass));
 4094         if (normal)
 4095         ++screen->startSel.col;
 4096         else
 4097         screen->startSel = mark;
 4098     }
 4099 #if OPT_WIDE_CHARS
 4100 #define SkipHiddenCell(mark) \
 4101     if (mark.col && XTERM_CELL(mark.row, mark.col) == HIDDEN_CHAR) \
 4102         mark.col++
 4103 #else
 4104 #define SkipHiddenCell(mark)    /* nothing */
 4105 #endif
 4106     SkipHiddenCell(screen->startSel);
 4107 
 4108     if (!normal) {
 4109         screen->endSel = screen->startSel;
 4110         ld.endSel = ld.startSel;
 4111     }
 4112 
 4113     if (okPosition(screen, &(ld.endSel), &(screen->endSel))) {
 4114         int length = LastTextCol(screen, ld.endSel, screen->endSel.row);
 4115         cclass = CClassOf(endSel);
 4116         TRACE(("...ending with class %d\n", cclass));
 4117         do {
 4118         ++screen->endSel.col;
 4119         if (screen->endSel.col > length
 4120             && LineTstWrapped(ld.endSel)) {
 4121             if (!MoreRows(endSel))
 4122             break;
 4123             screen->endSel.col = 0;
 4124             NextRow(endSel);
 4125             length = LastTextCol(screen, ld.endSel, screen->endSel.row);
 4126         }
 4127         } while (screen->endSel.col <= length
 4128              && CClassSelects(endSel, cclass));
 4129         if (normal
 4130         && screen->endSel.col > length + 1
 4131         && MoreRows(endSel)) {
 4132         screen->endSel.col = 0;
 4133         NextRow(endSel);
 4134         }
 4135     }
 4136     SkipHiddenCell(screen->endSel);
 4137 
 4138     screen->saveStartW = screen->startSel;
 4139     break;
 4140 
 4141     case Select_LINE:
 4142     TRACE(("Select_LINE\n"));
 4143     while (LineTstWrapped(ld.endSel)
 4144            && MoreRows(endSel)) {
 4145         NextRow(endSel);
 4146     }
 4147     if (screen->cutToBeginningOfLine
 4148         || screen->startSel.row < screen->saveStartW.row) {
 4149         screen->startSel.col = 0;
 4150         while (isPrevWrapped(startSel)) {
 4151         PrevRow(startSel);
 4152         }
 4153     } else if (!extend) {
 4154         if ((first.row < screen->saveStartW.row)
 4155         || (isSameRow(&first, &(screen->saveStartW))
 4156             && first.col < screen->saveStartW.col)) {
 4157         screen->startSel.col = 0;
 4158         while (isPrevWrapped(startSel)) {
 4159             PrevRow(startSel);
 4160         }
 4161         } else {
 4162         screen->startSel = screen->saveStartW;
 4163         }
 4164     }
 4165     trimLastLine(screen, &(ld.endSel), &(screen->endSel));
 4166     break;
 4167 
 4168     case Select_GROUP:      /* paragraph */
 4169     TRACE(("Select_GROUP\n"));
 4170     if (okPosition(screen, &(ld.startSel), &(screen->startSel))) {
 4171         /* scan backward for beginning of group */
 4172         while (screen->startSel.row > 0 &&
 4173            (LastTextCol(screen, ld.startSel, screen->startSel.row -
 4174                 1) > 0 ||
 4175             isPrevWrapped(startSel))) {
 4176         PrevRow(startSel);
 4177         }
 4178         screen->startSel.col = 0;
 4179         /* scan forward for end of group */
 4180         while (MoreRows(endSel) &&
 4181            (LastTextCol(screen, ld.endSel, screen->endSel.row + 1) >
 4182             0 ||
 4183             LineTstWrapped(ld.endSel))) {
 4184         NextRow(endSel);
 4185         }
 4186         trimLastLine(screen, &(ld.endSel), &(screen->endSel));
 4187     }
 4188     break;
 4189 
 4190     case Select_PAGE:       /* everything one can see */
 4191     TRACE(("Select_PAGE\n"));
 4192     screen->startSel.row = 0;
 4193     screen->startSel.col = 0;
 4194     screen->endSel.row = MaxRows(screen);
 4195     screen->endSel.col = 0;
 4196     break;
 4197 
 4198     case Select_ALL:        /* counts scrollback if in normal screen */
 4199     TRACE(("Select_ALL\n"));
 4200     screen->startSel.row = -screen->savedlines;
 4201     screen->startSel.col = 0;
 4202     screen->endSel.row = MaxRows(screen);
 4203     screen->endSel.col = 0;
 4204     break;
 4205 
 4206 #if OPT_SELECT_REGEX
 4207     case Select_REGEX:
 4208     do_select_regex(screen, &(screen->startSel), &(screen->endSel));
 4209     break;
 4210 #endif
 4211 
 4212     case NSELECTUNITS:      /* always ignore */
 4213     ignored = True;
 4214     break;
 4215     }
 4216 
 4217     if (!ignored) {
 4218     /* check boundaries */
 4219     ScrollSelection(screen, 0, False);
 4220     TrackText(xw, &(screen->startSel), &(screen->endSel));
 4221     }
 4222 
 4223     return;
 4224 }
 4225 
 4226 /* Guaranteed (first.row, first.col) <= (last.row, last.col) */
 4227 static void
 4228 TrackText(XtermWidget xw,
 4229       const CELL *firstp,
 4230       const CELL *lastp)
 4231 {
 4232     TScreen *screen = TScreenOf(xw);
 4233     int from, to;
 4234     CELL old_start, old_end;
 4235     CELL first = *firstp;
 4236     CELL last = *lastp;
 4237 
 4238     TRACE(("TrackText(first=%d,%d, last=%d,%d)\n",
 4239        first.row, first.col, last.row, last.col));
 4240 
 4241     old_start = screen->startH;
 4242     old_end = screen->endH;
 4243     TRACE(("...previous(first=%d,%d, last=%d,%d)\n",
 4244        old_start.row, old_start.col,
 4245        old_end.row, old_end.col));
 4246     if (isSameCELL(&first, &old_start) &&
 4247     isSameCELL(&last, &old_end)) {
 4248     return;
 4249     }
 4250 
 4251     screen->startH = first;
 4252     screen->endH = last;
 4253     from = Coordinate(screen, &screen->startH);
 4254     to = Coordinate(screen, &screen->endH);
 4255     if (to <= screen->startHCoord || from > screen->endHCoord) {
 4256     /* No overlap whatsoever between old and new hilite */
 4257     ReHiliteText(xw, &old_start, &old_end);
 4258     ReHiliteText(xw, &first, &last);
 4259     } else {
 4260     if (from < screen->startHCoord) {
 4261         /* Extend left end */
 4262         ReHiliteText(xw, &first, &old_start);
 4263     } else if (from > screen->startHCoord) {
 4264         /* Shorten left end */
 4265         ReHiliteText(xw, &old_start, &first);
 4266     }
 4267     if (to > screen->endHCoord) {
 4268         /* Extend right end */
 4269         ReHiliteText(xw, &old_end, &last);
 4270     } else if (to < screen->endHCoord) {
 4271         /* Shorten right end */
 4272         ReHiliteText(xw, &last, &old_end);
 4273     }
 4274     }
 4275     screen->startHCoord = from;
 4276     screen->endHCoord = to;
 4277 }
 4278 
 4279 static void
 4280 UnHiliteText(XtermWidget xw)
 4281 {
 4282     TrackText(xw, &zeroCELL, &zeroCELL);
 4283 }
 4284 
 4285 /* Guaranteed that (first->row, first->col) <= (last->row, last->col) */
 4286 static void
 4287 ReHiliteText(XtermWidget xw,
 4288          CELL *firstp,
 4289          CELL *lastp)
 4290 {
 4291     TScreen *screen = TScreenOf(xw);
 4292     CELL first = *firstp;
 4293     CELL last = *lastp;
 4294 
 4295     TRACE(("ReHiliteText from %d.%d to %d.%d\n",
 4296        first.row, first.col, last.row, last.col));
 4297 
 4298     if (first.row < 0)
 4299     first.row = first.col = 0;
 4300     else if (first.row > screen->max_row)
 4301     return;         /* nothing to do, since last.row >= first.row */
 4302 
 4303     if (last.row < 0)
 4304     return;         /* nothing to do, since first.row <= last.row */
 4305     else if (last.row > screen->max_row) {
 4306     last.row = screen->max_row;
 4307     last.col = MaxCols(screen);
 4308     }
 4309     if (isSameCELL(&first, &last))
 4310     return;
 4311 
 4312     if (!isSameRow(&first, &last)) {    /* do multiple rows */
 4313     int i;
 4314     if ((i = screen->max_col - first.col + 1) > 0) {    /* first row */
 4315         ScrnRefresh(xw, first.row, first.col, 1, i, True);
 4316     }
 4317     if ((i = last.row - first.row - 1) > 0) {   /* middle rows */
 4318         ScrnRefresh(xw, first.row + 1, 0, i, MaxCols(screen), True);
 4319     }
 4320     if (last.col > 0 && last.row <= screen->max_row) {  /* last row */
 4321         ScrnRefresh(xw, last.row, 0, 1, last.col, True);
 4322     }
 4323     } else {            /* do single row */
 4324     ScrnRefresh(xw, first.row, first.col, 1, last.col - first.col, True);
 4325     }
 4326 }
 4327 
 4328 /*
 4329  * Guaranteed that (cellc->row, cellc->col) <= (cell->row, cell->col),
 4330  * and that both points are valid
 4331  * (may have cell->row = screen->max_row+1, cell->col = 0).
 4332  */
 4333 static void
 4334 SaltTextAway(XtermWidget xw,
 4335          int which,
 4336          CELL *cellc,
 4337          CELL *cell)
 4338 {
 4339     TScreen *screen = TScreenOf(xw);
 4340     SelectedCells *scp;
 4341     int i;
 4342     int eol;
 4343     int need = 0;
 4344     size_t have = 0;
 4345     Char *line;
 4346     Char *lp;
 4347     CELL first = *cellc;
 4348     CELL last = *cell;
 4349 
 4350     if (which < 0 || which >= MAX_SELECTIONS) {
 4351     TRACE(("SaltTextAway - which selection?\n"));
 4352     return;
 4353     }
 4354     scp = &(screen->selected_cells[which]);
 4355 
 4356     TRACE(("SaltTextAway which=%d, first=%d,%d, last=%d,%d\n",
 4357        which, first.row, first.col, last.row, last.col));
 4358 
 4359     if (isSameRow(&first, &last) && first.col > last.col) {
 4360     int tmp;
 4361     EXCHANGE(first.col, last.col, tmp);
 4362     }
 4363 
 4364     --last.col;
 4365     /* first we need to know how long the string is before we can save it */
 4366 
 4367     if (isSameRow(&last, &first)) {
 4368     need = Length(screen, first.row, first.col, last.col);
 4369     } else {            /* two cases, cut is on same line, cut spans multiple lines */
 4370     need += Length(screen, first.row, first.col, screen->max_col) + 1;
 4371     for (i = first.row + 1; i < last.row; i++)
 4372         need += Length(screen, i, 0, screen->max_col) + 1;
 4373     if (last.col >= 0)
 4374         need += Length(screen, last.row, 0, last.col);
 4375     }
 4376 
 4377     /* UTF-8 may require more space */
 4378     if_OPT_WIDE_CHARS(screen, {
 4379     if (need > 0) {
 4380         if (screen->max_combining > 0)
 4381         need += screen->max_combining;
 4382         need *= 6;
 4383     }
 4384     });
 4385 
 4386     /* now get some memory to save it in */
 4387     if (need < 0)
 4388     return;
 4389 
 4390     if (scp->data_limit <= (unsigned) need) {
 4391     if ((line = (Char *) malloc((size_t) need + 1)) == 0)
 4392         SysError(ERROR_BMALLOC2);
 4393     free(scp->data_buffer);
 4394     scp->data_buffer = line;
 4395     scp->data_limit = (size_t) (need + 1);
 4396     } else {
 4397     line = scp->data_buffer;
 4398     }
 4399 
 4400     if (line == 0)
 4401     return;
 4402 
 4403     line[need] = '\0';      /* make sure it is null terminated */
 4404     lp = line;          /* lp points to where to save the text */
 4405     if (isSameRow(&last, &first)) {
 4406     lp = SaveText(screen, last.row, first.col, last.col, lp, &eol);
 4407     } else {
 4408     lp = SaveText(screen, first.row, first.col, screen->max_col, lp, &eol);
 4409     if (eol)
 4410         *lp++ = '\n';   /* put in newline at end of line */
 4411     for (i = first.row + 1; i < last.row; i++) {
 4412         lp = SaveText(screen, i, 0, screen->max_col, lp, &eol);
 4413         if (eol)
 4414         *lp++ = '\n';
 4415     }
 4416     if (last.col >= 0)
 4417         lp = SaveText(screen, last.row, 0, last.col, lp, &eol);
 4418     }
 4419     *lp = '\0';         /* make sure we have end marked */
 4420 
 4421     have = (size_t) (lp - line);
 4422     /*
 4423      * Scanning the buffer twice is unnecessary.  Discard unwanted memory if
 4424      * the estimate is too-far off.
 4425      */
 4426     if ((have * 2) < (size_t) need) {
 4427     Char *next;
 4428     scp->data_limit = have + 1;
 4429     next = realloc(line, scp->data_limit);
 4430     if (next == NULL) {
 4431         free(line);
 4432         scp->data_length = 0;
 4433         scp->data_limit = 0;
 4434     }
 4435     scp->data_buffer = next;
 4436     }
 4437     scp->data_length = have;
 4438 
 4439     TRACE(("Salted TEXT:%u:%s\n", (unsigned) have,
 4440        visibleChars(scp->data_buffer, (unsigned) have)));
 4441 }
 4442 
 4443 #if OPT_PASTE64
 4444 void
 4445 ClearSelectionBuffer(TScreen *screen, String selection)
 4446 {
 4447     int which = TargetToSelection(screen, selection);
 4448     SelectedCells *scp = &(screen->selected_cells[okSelectionCode(which)]);
 4449     FreeAndNull(scp->data_buffer);
 4450     scp->data_limit = 0;
 4451     scp->data_length = 0;
 4452     screen->base64_count = 0;
 4453 }
 4454 
 4455 static void
 4456 AppendStrToSelectionBuffer(SelectedCells * scp, Char *text, size_t len)
 4457 {
 4458     if (len != 0) {
 4459     size_t j = (scp->data_length + len);
 4460     size_t k = j + (j >> 2) + 80;
 4461     if (j + 1 >= scp->data_limit) {
 4462         Char *line;
 4463         if (!scp->data_length) {
 4464         line = (Char *) malloc(k);
 4465         } else {
 4466         line = (Char *) realloc(scp->data_buffer, k);
 4467         }
 4468         if (line == 0)
 4469         SysError(ERROR_BMALLOC2);
 4470         scp->data_buffer = line;
 4471         scp->data_limit = k;
 4472     }
 4473     if (scp->data_buffer != 0) {
 4474         memcpy(scp->data_buffer + scp->data_length, text, len);
 4475         scp->data_length += len;
 4476         scp->data_buffer[scp->data_length] = 0;
 4477     }
 4478     }
 4479 }
 4480 
 4481 void
 4482 AppendToSelectionBuffer(TScreen *screen, unsigned c, String selection)
 4483 {
 4484     int which = TargetToSelection(screen, selection);
 4485     SelectedCells *scp = &(screen->selected_cells[okSelectionCode(which)]);
 4486     unsigned six;
 4487     Char ch;
 4488 
 4489     /* Decode base64 character */
 4490     if (c >= 'A' && c <= 'Z')
 4491     six = c - 'A';
 4492     else if (c >= 'a' && c <= 'z')
 4493     six = c - 'a' + 26;
 4494     else if (c >= '0' && c <= '9')
 4495     six = c - '0' + 52;
 4496     else if (c == '+')
 4497     six = 62;
 4498     else if (c == '/')
 4499     six = 63;
 4500     else
 4501     return;
 4502 
 4503     /* Accumulate bytes */
 4504     switch (screen->base64_count) {
 4505     case 0:
 4506     screen->base64_accu = six;
 4507     screen->base64_count = 6;
 4508     break;
 4509 
 4510     case 2:
 4511     ch = CharOf((screen->base64_accu << 6) + six);
 4512     screen->base64_count = 0;
 4513     AppendStrToSelectionBuffer(scp, &ch, (size_t) 1);
 4514     break;
 4515 
 4516     case 4:
 4517     ch = CharOf((screen->base64_accu << 4) + (six >> 2));
 4518     screen->base64_accu = (six & 0x3);
 4519     screen->base64_count = 2;
 4520     AppendStrToSelectionBuffer(scp, &ch, (size_t) 1);
 4521     break;
 4522 
 4523     case 6:
 4524     ch = CharOf((screen->base64_accu << 2) + (six >> 4));
 4525     screen->base64_accu = (six & 0xF);
 4526     screen->base64_count = 4;
 4527     AppendStrToSelectionBuffer(scp, &ch, (size_t) 1);
 4528     break;
 4529     }
 4530 }
 4531 
 4532 void
 4533 CompleteSelection(XtermWidget xw, String *args, Cardinal len)
 4534 {
 4535     TScreen *screen = TScreenOf(xw);
 4536 
 4537     screen->base64_count = 0;
 4538     screen->base64_accu = 0;
 4539     _OwnSelection(xw, args, len);
 4540 }
 4541 #endif /* OPT_PASTE64 */
 4542 
 4543 static Bool
 4544 _ConvertSelectionHelper(Widget w,
 4545             SelectedCells * scp,
 4546             Atom *type,
 4547             XtPointer *value,
 4548             unsigned long *length,
 4549             int *format,
 4550             int (*conversion_function) (Display *,
 4551                             char **, int,
 4552                             XICCEncodingStyle,
 4553                             XTextProperty *),
 4554             XICCEncodingStyle conversion_style)
 4555 {
 4556     *value = 0;
 4557     *length = 0;
 4558     *type = 0;
 4559     *format = 0;
 4560 
 4561     if (getXtermWidget(w) != 0) {
 4562     Display *dpy = XtDisplay(w);
 4563     XTextProperty textprop;
 4564     int out_n = 0;
 4565     char *result = 0;
 4566     char *the_data = (char *) scp->data_buffer;
 4567     char *the_next;
 4568     unsigned long remaining = scp->data_length;
 4569 
 4570     TRACE(("converting %ld:'%s'\n",
 4571            (long) scp->data_length,
 4572            visibleChars(scp->data_buffer, (unsigned) scp->data_length)));
 4573     /*
 4574      * For most selections, we can convert in one pass.  It is possible
 4575      * that some applications contain embedded nulls, e.g., using xterm's
 4576      * paste64 feature.  For those cases, we will build up the result in
 4577      * parts.
 4578      */
 4579     if (memchr(the_data, 0, scp->data_length) != 0) {
 4580         TRACE(("selection contains embedded nulls\n"));
 4581         result = calloc(scp->data_length + 1, sizeof(char));
 4582     }
 4583 
 4584       next_try:
 4585     memset(&textprop, 0, sizeof(textprop));
 4586     if (conversion_function(dpy, &the_data, 1,
 4587                 conversion_style,
 4588                 &textprop) >= Success) {
 4589         if ((result != 0)
 4590         && (textprop.value != 0)
 4591         && (textprop.format == 8)) {
 4592         char *text_values = (char *) textprop.value;
 4593         unsigned long in_n;
 4594 
 4595         if (out_n == 0) {
 4596             *value = result;
 4597             *type = textprop.encoding;
 4598             *format = textprop.format;
 4599         }
 4600         for (in_n = 0; in_n < textprop.nitems; ++in_n) {
 4601             result[out_n++] = text_values[in_n];
 4602         }
 4603         *length += textprop.nitems;
 4604         if ((the_next = memchr(the_data, 0, remaining)) != 0) {
 4605             unsigned long this_was = (unsigned long) (the_next - the_data);
 4606             this_was++;
 4607             the_data += this_was;
 4608             remaining -= this_was;
 4609             result[out_n++] = 0;
 4610             *length += 1;
 4611             if (remaining)
 4612             goto next_try;
 4613         }
 4614         return True;
 4615         } else {
 4616         free(result);
 4617         *value = (XtPointer) textprop.value;
 4618         *length = textprop.nitems;
 4619         *type = textprop.encoding;
 4620         *format = textprop.format;
 4621         return True;
 4622         }
 4623     }
 4624     free(result);
 4625     }
 4626     return False;
 4627 }
 4628 
 4629 static Boolean
 4630 SaveConvertedLength(XtPointer *target, unsigned long source)
 4631 {
 4632     Boolean result = False;
 4633 
 4634     *target = XtMalloc(4);
 4635     if (*target != 0) {
 4636     result = True;
 4637     if (sizeof(unsigned long) == 4) {
 4638         *(unsigned long *) *target = source;
 4639     } else if (sizeof(unsigned) == 4) {
 4640         *(unsigned *) *target = (unsigned) source;
 4641     } else if (sizeof(unsigned short) == 4) {
 4642         *(unsigned short *) *target = (unsigned short) source;
 4643     } else {
 4644         /* FIXME - does this depend on byte-order? */
 4645         unsigned long temp = source;
 4646         memcpy((char *) *target,
 4647            ((char *) &temp) + sizeof(temp) - 4,
 4648            (size_t) 4);
 4649     }
 4650     }
 4651     return result;
 4652 }
 4653 
 4654 #define keepClipboard(d,atom) ((screen->keepClipboard) && \
 4655      (atom == XA_CLIPBOARD(d)))
 4656 
 4657 static Boolean
 4658 ConvertSelection(Widget w,
 4659          Atom *selection,
 4660          Atom *target,
 4661          Atom *type,
 4662          XtPointer *value,
 4663          unsigned long *length,
 4664          int *format)
 4665 {
 4666     Display *dpy = XtDisplay(w);
 4667     TScreen *screen;
 4668     SelectedCells *scp;
 4669     Bool result = False;
 4670 
 4671     Char *data;
 4672     unsigned long data_length;
 4673 
 4674     XtermWidget xw;
 4675 
 4676     if ((xw = getXtermWidget(w)) == 0)
 4677     return False;
 4678 
 4679     screen = TScreenOf(xw);
 4680 
 4681     TRACE(("ConvertSelection %s -> %s\n",
 4682        TraceAtomName(screen->display, *selection),
 4683        visibleSelectionTarget(dpy, *target)));
 4684 
 4685     if (keepClipboard(dpy, *selection)) {
 4686     TRACE(("asked for clipboard\n"));
 4687     scp = &(screen->clipboard_data);
 4688     } else {
 4689     TRACE(("asked for selection\n"));
 4690     scp = &(screen->selected_cells[AtomToSelection(dpy, *selection)]);
 4691     }
 4692 
 4693     data = scp->data_buffer;
 4694     data_length = scp->data_length;
 4695     if (data == NULL) {
 4696     TRACE(("...no selection-data\n"));
 4697     return False;
 4698     }
 4699 
 4700     if (*target == XA_TARGETS(dpy)) {
 4701     Atom *targetP;
 4702     XPointer std_return = 0;
 4703     unsigned long std_length;
 4704 
 4705     if (XmuConvertStandardSelection(w, screen->selection_time, selection,
 4706                     target, type, &std_return,
 4707                     &std_length, format)) {
 4708         Atom *my_targets = _SelectionTargets(w);
 4709         Atom *allocP;
 4710         Atom *std_targets;
 4711 
 4712         TRACE(("XmuConvertStandardSelection - success\n"));
 4713         std_targets = (Atom *) (void *) (std_return);
 4714         *length = std_length + 6;
 4715 
 4716         targetP = TypeXtMallocN(Atom, *length);
 4717         allocP = targetP;
 4718 
 4719         *value = (XtPointer) targetP;
 4720 
 4721         if (my_targets != 0) {
 4722         while (*my_targets != None) {
 4723             *targetP++ = *my_targets++;
 4724         }
 4725         }
 4726         *targetP++ = XA_LENGTH(dpy);
 4727         *targetP++ = XA_LIST_LENGTH(dpy);
 4728 
 4729         *length = std_length + (unsigned long) (targetP - allocP);
 4730 
 4731         memcpy(targetP, std_targets, sizeof(Atom) * std_length);
 4732         XtFree((char *) std_targets);
 4733         *type = XA_ATOM;
 4734         *format = 32;
 4735         result = True;
 4736     } else {
 4737         TRACE(("XmuConvertStandardSelection - failed\n"));
 4738     }
 4739     }
 4740 #if OPT_WIDE_CHARS
 4741     else if (screen->wide_chars && *target == XA_STRING) {
 4742     result =
 4743         _ConvertSelectionHelper(w, scp,
 4744                     type, value, length, format,
 4745                     Xutf8TextListToTextProperty,
 4746                     XStringStyle);
 4747     TRACE(("...Xutf8TextListToTextProperty:%d\n", result));
 4748     } else if (screen->wide_chars && *target == XA_UTF8_STRING(dpy)) {
 4749     result =
 4750         _ConvertSelectionHelper(w, scp,
 4751                     type, value, length, format,
 4752                     Xutf8TextListToTextProperty,
 4753                     XUTF8StringStyle);
 4754     TRACE(("...Xutf8TextListToTextProperty:%d\n", result));
 4755     } else if (screen->wide_chars && *target == XA_TEXT(dpy)) {
 4756     result =
 4757         _ConvertSelectionHelper(w, scp,
 4758                     type, value, length, format,
 4759                     Xutf8TextListToTextProperty,
 4760                     XStdICCTextStyle);
 4761     TRACE(("...Xutf8TextListToTextProperty:%d\n", result));
 4762     } else if (screen->wide_chars && *target == XA_COMPOUND_TEXT(dpy)) {
 4763     result =
 4764         _ConvertSelectionHelper(w, scp,
 4765                     type, value, length, format,
 4766                     Xutf8TextListToTextProperty,
 4767                     XCompoundTextStyle);
 4768     TRACE(("...Xutf8TextListToTextProperty:%d\n", result));
 4769     }
 4770 #endif
 4771 
 4772     else if (*target == XA_STRING) {    /* not wide_chars */
 4773     /* We can only reach this point if the selection requestor
 4774        requested STRING before any of TEXT, COMPOUND_TEXT or
 4775        UTF8_STRING.  We therefore assume that the requestor is not
 4776        properly internationalised, and dump raw eight-bit data
 4777        with no conversion into the selection.  Yes, this breaks
 4778        the ICCCM in non-Latin-1 locales. */
 4779     *type = XA_STRING;
 4780     *value = (XtPointer) data;
 4781     *length = data_length;
 4782     *format = 8;
 4783     result = True;
 4784     TRACE(("...raw 8-bit data:%d\n", result));
 4785     } else if (*target == XA_TEXT(dpy)) {   /* not wide_chars */
 4786     result =
 4787         _ConvertSelectionHelper(w, scp,
 4788                     type, value, length, format,
 4789                     XmbTextListToTextProperty,
 4790                     XStdICCTextStyle);
 4791     TRACE(("...XmbTextListToTextProperty(StdICC):%d\n", result));
 4792     } else if (*target == XA_COMPOUND_TEXT(dpy)) {  /* not wide_chars */
 4793     result =
 4794         _ConvertSelectionHelper(w, scp,
 4795                     type, value, length, format,
 4796                     XmbTextListToTextProperty,
 4797                     XCompoundTextStyle);
 4798     TRACE(("...XmbTextListToTextProperty(Compound):%d\n", result));
 4799     }
 4800 #ifdef X_HAVE_UTF8_STRING
 4801     else if (*target == XA_UTF8_STRING(dpy)) {  /* not wide_chars */
 4802     result =
 4803         _ConvertSelectionHelper(w, scp,
 4804                     type, value, length, format,
 4805                     XmbTextListToTextProperty,
 4806                     XUTF8StringStyle);
 4807     TRACE(("...XmbTextListToTextProperty(UTF8):%d\n", result));
 4808     }
 4809 #endif
 4810     else if (*target == XA_LIST_LENGTH(dpy)) {
 4811     result = SaveConvertedLength(value, (unsigned long) 1);
 4812     *type = XA_INTEGER;
 4813     *length = 1;
 4814     *format = 32;
 4815     TRACE(("...list of values:%d\n", result));
 4816     } else if (*target == XA_LENGTH(dpy)) {
 4817     /* This value is wrong if we have UTF-8 text */
 4818     result = SaveConvertedLength(value, scp->data_length);
 4819     *type = XA_INTEGER;
 4820     *length = 1;
 4821     *format = 32;
 4822     TRACE(("...list of values:%d\n", result));
 4823     } else if (XmuConvertStandardSelection(w,
 4824                        screen->selection_time, selection,
 4825                        target, type, (XPointer *) value,
 4826                        length, format)) {
 4827     result = True;
 4828     TRACE(("...XmuConvertStandardSelection:%d\n", result));
 4829     }
 4830 
 4831     /* else */
 4832     return (Boolean) result;
 4833 }
 4834 
 4835 static void
 4836 LoseSelection(Widget w, Atom *selection)
 4837 {
 4838     TScreen *screen;
 4839     Atom *atomP;
 4840     Cardinal i;
 4841 
 4842     XtermWidget xw;
 4843 
 4844     if ((xw = getXtermWidget(w)) == 0)
 4845     return;
 4846 
 4847     screen = TScreenOf(xw);
 4848     TRACE(("LoseSelection %s\n", TraceAtomName(screen->display, *selection)));
 4849 
 4850     for (i = 0, atomP = screen->selection_atoms;
 4851      i < screen->selection_count; i++, atomP++) {
 4852     if (*selection == *atomP)
 4853         *atomP = (Atom) 0;
 4854     if (CutBuffer(*atomP) >= 0) {
 4855         *atomP = (Atom) 0;
 4856     }
 4857     }
 4858 
 4859     for (i = screen->selection_count; i; i--) {
 4860     if (screen->selection_atoms[i - 1] != 0)
 4861         break;
 4862     }
 4863     screen->selection_count = i;
 4864 
 4865     for (i = 0, atomP = screen->selection_atoms;
 4866      i < screen->selection_count; i++, atomP++) {
 4867     if (*atomP == (Atom) 0) {
 4868         *atomP = screen->selection_atoms[--screen->selection_count];
 4869     }
 4870     }
 4871 
 4872     if (screen->selection_count == 0)
 4873     UnHiliteText(xw);
 4874 }
 4875 
 4876 /* ARGSUSED */
 4877 static void
 4878 SelectionDone(Widget w GCC_UNUSED,
 4879           Atom *selection GCC_UNUSED,
 4880           Atom *target GCC_UNUSED)
 4881 {
 4882     /* empty proc so Intrinsics know we want to keep storage */
 4883     TRACE(("SelectionDone\n"));
 4884 }
 4885 
 4886 static void
 4887 _OwnSelection(XtermWidget xw,
 4888           String *selections,
 4889           Cardinal count)
 4890 {
 4891     TScreen *screen = TScreenOf(xw);
 4892     Display *dpy = screen->display;
 4893     Atom *atoms = screen->selection_atoms;
 4894     Cardinal i;
 4895     Bool have_selection = False;
 4896     SelectedCells *scp;
 4897 
 4898     if (count == 0)
 4899     return;
 4900 
 4901     TRACE(("_OwnSelection count %d\n", count));
 4902     selections = MapSelections(xw, selections, count);
 4903 
 4904     if (count > screen->sel_atoms_size) {
 4905     XtFree((char *) atoms);
 4906     atoms = TypeXtMallocN(Atom, count);
 4907     screen->selection_atoms = atoms;
 4908     screen->sel_atoms_size = count;
 4909     }
 4910     XmuInternStrings(dpy, selections, count, atoms);
 4911     for (i = 0; i < count; i++) {
 4912     int cutbuffer = CutBuffer(atoms[i]);
 4913     if (cutbuffer >= 0) {
 4914         unsigned long limit =
 4915         (unsigned long) (4 * XMaxRequestSize(dpy) - 32);
 4916         scp = &(screen->selected_cells[CutBufferToCode(cutbuffer)]);
 4917         if (scp->data_length > limit) {
 4918         TRACE(("selection too big (%lu bytes), not storing in CUT_BUFFER%d\n",
 4919                (unsigned long) scp->data_length, cutbuffer));
 4920         xtermWarning("selection too big (%lu bytes), not storing in CUT_BUFFER%d\n",
 4921                  (unsigned long) scp->data_length, cutbuffer);
 4922         } else {
 4923         /* This used to just use the UTF-8 data, which was totally
 4924          * broken as not even the corresponding paste code in xterm
 4925          * understood this!  So now it converts to Latin1 first.
 4926          *   Robert Brady, 2000-09-05
 4927          */
 4928         unsigned long length = scp->data_length;
 4929         Char *data = scp->data_buffer;
 4930         if_OPT_WIDE_CHARS((screen), {
 4931             data = UTF8toLatin1(screen, data, length, &length);
 4932         });
 4933         TRACE(("XStoreBuffer(%d)\n", cutbuffer));
 4934         XStoreBuffer(dpy,
 4935                  (char *) data,
 4936                  (int) length,
 4937                  cutbuffer);
 4938         }
 4939     } else {
 4940         int which = AtomToSelection(dpy, atoms[i]);
 4941         if (keepClipboard(dpy, atoms[i])) {
 4942         Char *buf;
 4943         SelectedCells *tcp = &(screen->clipboard_data);
 4944         TRACE(("saving selection to clipboard buffer\n"));
 4945         scp = &(screen->selected_cells[CLIPBOARD_CODE]);
 4946         if ((buf = (Char *) malloc((size_t) scp->data_length)) == 0)
 4947             SysError(ERROR_BMALLOC2);
 4948 
 4949         free(tcp->data_buffer);
 4950         memcpy(buf, scp->data_buffer, scp->data_length);
 4951         tcp->data_buffer = buf;
 4952         tcp->data_limit = scp->data_length;
 4953         tcp->data_length = scp->data_length;
 4954         }
 4955         scp = &(screen->selected_cells[which]);
 4956         if (scp->data_length == 0) {
 4957         TRACE(("XtDisownSelection(%s, @%ld)\n",
 4958                TraceAtomName(screen->display, atoms[i]),
 4959                (long) screen->selection_time));
 4960         XtDisownSelection((Widget) xw,
 4961                   atoms[i],
 4962                   screen->selection_time);
 4963         } else if (!screen->replyToEmacs && atoms[i] != 0) {
 4964         TRACE(("XtOwnSelection(%s, @%ld)\n",
 4965                TraceAtomName(screen->display, atoms[i]),
 4966                (long) screen->selection_time));
 4967         have_selection |=
 4968             XtOwnSelection((Widget) xw, atoms[i],
 4969                    screen->selection_time,
 4970                    ConvertSelection,
 4971                    LoseSelection,
 4972                    SelectionDone);
 4973         }
 4974     }
 4975     TRACE(("... _OwnSelection used length %lu value %s\n",
 4976            (unsigned long) scp->data_length,
 4977            visibleChars(scp->data_buffer,
 4978                 (unsigned) scp->data_length)));
 4979     }
 4980     if (!screen->replyToEmacs)
 4981     screen->selection_count = count;
 4982     if (!have_selection)
 4983     UnHiliteText(xw);
 4984 }
 4985 
 4986 static void
 4987 ResetSelectionState(TScreen *screen)
 4988 {
 4989     screen->selection_count = 0;
 4990     screen->startH = zeroCELL;
 4991     screen->endH = zeroCELL;
 4992 }
 4993 
 4994 void
 4995 DisownSelection(XtermWidget xw)
 4996 {
 4997     TScreen *screen = TScreenOf(xw);
 4998     Atom *atoms = screen->selection_atoms;
 4999     Cardinal count = screen->selection_count;
 5000     Cardinal i;
 5001 
 5002     TRACE(("DisownSelection count %d, start %d.%d, end %d.%d\n",
 5003        count,
 5004        screen->startH.row,
 5005        screen->startH.col,
 5006        screen->endH.row,
 5007        screen->endH.col));
 5008 
 5009     for (i = 0; i < count; i++) {
 5010     int cutbuffer = CutBuffer(atoms[i]);
 5011     if (cutbuffer < 0) {
 5012         XtDisownSelection((Widget) xw, atoms[i],
 5013                   screen->selection_time);
 5014     }
 5015     }
 5016     /*
 5017      * If none of the callbacks via XtDisownSelection() reset highlighting
 5018      * do it now.
 5019      */
 5020     if (ScrnHaveSelection(screen)) {
 5021     /* save data which will be reset */
 5022     CELL first = screen->startH;
 5023     CELL last = screen->endH;
 5024 
 5025     ResetSelectionState(screen);
 5026     ReHiliteText(xw, &first, &last);
 5027     } else {
 5028     ResetSelectionState(screen);
 5029     }
 5030 }
 5031 
 5032 void
 5033 UnhiliteSelection(XtermWidget xw)
 5034 {
 5035     TScreen *screen = TScreenOf(xw);
 5036 
 5037     if (ScrnHaveSelection(screen)) {
 5038     CELL first = screen->startH;
 5039     CELL last = screen->endH;
 5040 
 5041     screen->startH = zeroCELL;
 5042     screen->endH = zeroCELL;
 5043     ReHiliteText(xw, &first, &last);
 5044     }
 5045 }
 5046 
 5047 /* returns number of chars in line from scol to ecol out */
 5048 /* ARGSUSED */
 5049 static int
 5050 Length(TScreen *screen,
 5051        int row,
 5052        int scol,
 5053        int ecol)
 5054 {
 5055     CLineData *ld = GET_LINEDATA(screen, row);
 5056     const int lastcol = LastTextCol(screen, ld, row);
 5057 
 5058     if (ecol > lastcol)
 5059     ecol = lastcol;
 5060     return (ecol - scol + 1);
 5061 }
 5062 
 5063 /* copies text into line, preallocated */
 5064 static Char *
 5065 SaveText(TScreen *screen,
 5066      int row,
 5067      int scol,
 5068      int ecol,
 5069      Char *lp,      /* pointer to where to put the text */
 5070      int *eol)
 5071 {
 5072     LineData *ld;
 5073     int i = 0;
 5074     Char *result = lp;
 5075 #if OPT_WIDE_CHARS
 5076     unsigned previous = 0;
 5077 #endif
 5078 
 5079     ld = GET_LINEDATA(screen, row);
 5080     i = Length(screen, row, scol, ecol);
 5081     ecol = scol + i;
 5082 #if OPT_DEC_CHRSET
 5083     if (CSET_DOUBLE(GetLineDblCS(ld))) {
 5084     scol = (scol + 0) / 2;
 5085     ecol = (ecol + 1) / 2;
 5086     }
 5087 #endif
 5088     *eol = !LineTstWrapped(ld);
 5089     for (i = scol; i < ecol; i++) {
 5090     unsigned c;
 5091     assert(i < (int) ld->lineSize);
 5092     c = E2A(ld->charData[i]);
 5093 #if OPT_WIDE_CHARS
 5094     /* We want to strip out every occurrence of HIDDEN_CHAR AFTER a
 5095      * wide character.
 5096      */
 5097     if (c == HIDDEN_CHAR) {
 5098         if (isWide((int) previous)) {
 5099         previous = c;
 5100         /* Combining characters attached to double-width characters
 5101            are in memory attached to the HIDDEN_CHAR */
 5102         if_OPT_WIDE_CHARS(screen, {
 5103             if ((screen->utf8_nrc_mode | screen->utf8_mode) != uFalse) {
 5104             size_t off;
 5105             for_each_combData(off, ld) {
 5106                 unsigned ch = ld->combData[off][i];
 5107                 if (ch == 0)
 5108                 break;
 5109                 lp = convertToUTF8(lp, ch);
 5110             }
 5111             }
 5112         });
 5113         continue;
 5114         } else {
 5115         c = ' ';    /* should not happen, but just in case... */
 5116         }
 5117     }
 5118     previous = c;
 5119     if ((screen->utf8_nrc_mode | screen->utf8_mode) != uFalse) {
 5120         lp = convertToUTF8(lp, (c != 0) ? c : ' ');
 5121         if_OPT_WIDE_CHARS(screen, {
 5122         size_t off;
 5123         for_each_combData(off, ld) {
 5124             unsigned ch = ld->combData[off][i];
 5125             if (ch == 0)
 5126             break;
 5127             lp = convertToUTF8(lp, ch);
 5128         }
 5129         });
 5130     } else
 5131 #endif
 5132     {
 5133         if (c == 0) {
 5134         c = E2A(' ');
 5135         } else if (c < E2A(' ')) {
 5136         c = DECtoASCII(c);
 5137         } else if (c == 0x7f) {
 5138         c = 0x5f;
 5139         }
 5140         *lp++ = CharOf(A2E(c));
 5141     }
 5142     if (c != E2A(' '))
 5143         result = lp;
 5144     }
 5145 
 5146     /*
 5147      * If requested, trim trailing blanks from selected lines.  Do not do this
 5148      * if the line is wrapped.
 5149      */
 5150     if (!*eol || !screen->trim_selection)
 5151     result = lp;
 5152 
 5153     return (result);
 5154 }
 5155 
 5156 /*
 5157  * This adds together the bits:
 5158  *   shift key   -> 1
 5159  *   meta key    -> 2
 5160  *   control key -> 4
 5161  */
 5162 static unsigned
 5163 KeyState(XtermWidget xw, unsigned x)
 5164 {
 5165     return ((((x) & (ShiftMask | ControlMask)))
 5166         + (((x) & MetaMask(xw)) ? 2 : 0));
 5167 }
 5168 
 5169 /* 32 + following 8-bit word:
 5170 
 5171    1:0  Button no: 0, 1, 2.  3=release.
 5172      2  shift
 5173      3  meta
 5174      4  ctrl
 5175      5  set for motion notify
 5176      6  set for wheel (and button 6 and 7)
 5177      7  set for buttons 8 to 11
 5178 */
 5179 
 5180 /* Position: 32 - 255. */
 5181 static int
 5182 BtnCode(XtermWidget xw, XButtonEvent *event, int button)
 5183 {
 5184     int result = (int) (32 + (KeyState(xw, event->state) << 2));
 5185 
 5186     if (event->type == MotionNotify)
 5187     result += 32;
 5188 
 5189     if (button < 0) {
 5190     result += 3;
 5191     } else {
 5192     result += button & 3;
 5193     if (button & 4)
 5194         result += 64;
 5195     if (button & 8)
 5196         result += 128;
 5197     }
 5198     TRACE(("BtnCode button %d, %s state " FMT_MODIFIER_NAMES " ->%#x\n",
 5199        button,
 5200        visibleEventType(event->type),
 5201        ARG_MODIFIER_NAMES(event->state),
 5202        result));
 5203     return result;
 5204 }
 5205 
 5206 static unsigned
 5207 EmitButtonCode(XtermWidget xw,
 5208            Char *line,
 5209            unsigned count,
 5210            XButtonEvent *event,
 5211            int button)
 5212 {
 5213     TScreen *screen = TScreenOf(xw);
 5214     int value;
 5215 
 5216     if (okSendMousePos(xw) == X10_MOUSE) {
 5217     value = CharOf(' ' + button);
 5218     } else {
 5219     value = BtnCode(xw, event, button);
 5220     }
 5221 
 5222     switch (screen->extend_coords) {
 5223     default:
 5224     line[count++] = CharOf(value);
 5225     break;
 5226     case SET_SGR_EXT_MODE_MOUSE:
 5227     case SET_PIXEL_POSITION_MOUSE:
 5228     value -= 32;        /* encoding starts at zero */
 5229     /* FALLTHRU */
 5230     case SET_URXVT_EXT_MODE_MOUSE:
 5231     count += (unsigned) sprintf((char *) line + count, "%d", value);
 5232     break;
 5233     case SET_EXT_MODE_MOUSE:
 5234     if (value < 128