button.c revision a5ae21e4
1/* $XTermId: button.c,v 1.637 2021/08/12 00:33:06 tom Exp $ */
2
3/*
4 * Copyright 1999-2020,2021 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/*
56button.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
100extern "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
126extern 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
172static const CELL zeroCELL =
173{0, 0};
174
175#if OPT_DEC_LOCATOR
176static Bool SendLocatorPosition(XtermWidget xw, XButtonEvent *event);
177static void CheckLocatorPosition(XtermWidget xw, XButtonEvent *event);
178#endif /* OPT_DEC_LOCATOR */
179
180/* Multi-click handling */
181#if OPT_READLINE
182static Time lastButtonDownTime = 0;
183static int ExtendingSelection = 0;
184static Time lastButton3UpTime = 0;
185static Time lastButton3DoubleDownTime = 0;
186static CELL lastButton3;	/* At the release time */
187#endif /* OPT_READLINE */
188
189static Char *SaveText(TScreen *screen, int row, int scol, int ecol,
190		      Char *lp, int *eol);
191static int Length(TScreen *screen, int row, int scol, int ecol);
192static void ComputeSelect(XtermWidget xw, CELL *startc, CELL *endc, Bool
193			  extend, Bool normal);
194static void EditorButton(XtermWidget xw, XButtonEvent *event);
195static void EndExtend(XtermWidget w, XEvent *event, String *params, Cardinal
196		      num_params, Bool use_cursor_loc);
197static void ExtendExtend(XtermWidget xw, const CELL *cell);
198static void PointToCELL(TScreen *screen, int y, int x, CELL *cell);
199static void ReHiliteText(XtermWidget xw, CELL *first, CELL *last);
200static void SaltTextAway(XtermWidget xw, int which, CELL *cellc, CELL *cell);
201static void SelectSet(XtermWidget xw, XEvent *event, String *params, Cardinal num_params);
202static void SelectionReceived PROTO_XT_SEL_CB_ARGS;
203static void StartSelect(XtermWidget xw, const CELL *cell);
204static void TrackDown(XtermWidget xw, XButtonEvent *event);
205static void TrackText(XtermWidget xw, const CELL *first, const CELL *last);
206static void UnHiliteText(XtermWidget xw);
207static void _OwnSelection(XtermWidget xw, String *selections, Cardinal count);
208static 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
217static int
218MouseLimit(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
238static unsigned
239EmitMousePosition(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
281static unsigned
282EmitMousePositionSeparator(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
294enum {
295    scanMods,
296    scanKey,
297    scanColon,
298    scanFunc,
299    scanArgs
300};
301
302#if OPT_TRACE > 1
303static const char *
304visibleScan(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
325static char *
326scanTrans(char *source, int *this_is, int *next_is, unsigned *first, unsigned *last)
327{
328    char ch;
329    char *target = source;
330
331    *first = *last = 0;
332    if (IsEmpty(target)) {
333	target = 0;
334    } else {
335	do {
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
405void
406xtermButtonInit(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 */
532static unsigned
533MetaMask(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 */
552static unsigned
553OurModifiers(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 */
565static Boolean
566ShiftOverride(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 */
609static Bool
610InterpretButton(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
623static int
624MotionButton(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
638static Bool
639InterpretEvent(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 */
666Bool
667SendMousePosition(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
767static Bool
768SendLocatorPosition(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
906void
907GetLocatorPosition(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
972void
973InitLocatorFilter(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
1094static void
1095CheckLocatorPosition(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
1148static int
1149isClick1_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
1177static int
1178isDoubleClick3(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
1216static int
1217CheckSecondPress3(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
1255static int
1256rowOnCurrentLine(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
1290static int
1291eventRow(TScreen *screen, XEvent *event)	/* must be XButtonEvent */
1292{
1293    return (event->xbutton.y - screen->border) / FontHeight(screen);
1294}
1295
1296static int
1297eventColBetween(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
1304static int
1305ReadLineMovePoint(TScreen *screen, int col, int ldelta)
1306{
1307    Char line[6];
1308    unsigned count = 0;
1309
1310    col += ldelta * MaxCols(screen) - screen->cur_col;
1311    if (col == 0)
1312	return 0;
1313    if (screen->control_eight_bits) {
1314	line[count++] = ANSI_CSI;
1315    } else {
1316	line[count++] = ANSI_ESC;
1317	line[count++] = '[';	/* XXX maybe sometimes O is better? */
1318    }
1319    line[count] = CharOf(col > 0 ? 'C' : 'D');
1320    if (col < 0)
1321	col = -col;
1322    while (col--)
1323	v_write(screen->respond, line, 3);
1324    return 1;
1325}
1326
1327static int
1328ReadLineDelete(TScreen *screen, CELL *cell1, CELL *cell2)
1329{
1330    int del;
1331
1332    del = (cell2->col - cell1->col) + ((cell2->row - cell1->row) * MaxCols(screen));
1333    if (del <= 0)		/* Just in case... */
1334	return 0;
1335    while (del--)
1336	v_write(screen->respond, (const Char *) "\177", 1);
1337    return 1;
1338}
1339
1340static void
1341readlineExtend(XtermWidget xw, XEvent *event)
1342{
1343    TScreen *screen = TScreenOf(xw);
1344    int ldelta1, ldelta2;
1345
1346    if (IsBtnEvent(event)) {
1347	XButtonEvent *my_event = (XButtonEvent *) event;
1348	if (isClick1_clean(xw, my_event)
1349	    && SCREEN_FLAG(screen, click1_moves)
1350	    && rowOnCurrentLine(screen, eventRow(screen, event), &ldelta1)) {
1351	    ReadLineMovePoint(screen, eventColBetween(screen, event), ldelta1);
1352	}
1353	if (isDoubleClick3(xw, screen, my_event)
1354	    && SCREEN_FLAG(screen, dclick3_deletes)
1355	    && rowOnCurrentLine(screen, screen->startSel.row, &ldelta1)
1356	    && rowOnCurrentLine(screen, screen->endSel.row, &ldelta2)) {
1357	    ReadLineMovePoint(screen, screen->endSel.col, ldelta2);
1358	    ReadLineDelete(screen, &screen->startSel, &(screen->endSel));
1359	}
1360    }
1361}
1362#endif /* OPT_READLINE */
1363
1364/* ^XM-G<line+' '><col+' '> */
1365void
1366DiredButton(Widget w,
1367	    XEvent *event,	/* must be XButtonEvent */
1368	    String *params GCC_UNUSED,	/* selections */
1369	    Cardinal *num_params GCC_UNUSED)
1370{
1371    XtermWidget xw;
1372
1373    if ((xw = getXtermWidget(w)) != 0) {
1374	TScreen *screen = TScreenOf(xw);
1375
1376	if (IsBtnEvent(event)
1377	    && (event->xbutton.y >= screen->border)
1378	    && (event->xbutton.x >= OriginX(screen))) {
1379	    Char Line[6];
1380	    unsigned line, col;
1381
1382	    line = (unsigned) ((event->xbutton.y - screen->border)
1383			       / FontHeight(screen));
1384	    col = (unsigned) ((event->xbutton.x - OriginX(screen))
1385			      / FontWidth(screen));
1386	    Line[0] = CONTROL('X');
1387	    Line[1] = ANSI_ESC;
1388	    Line[2] = 'G';
1389	    Line[3] = CharOf(' ' + col);
1390	    Line[4] = CharOf(' ' + line);
1391	    v_write(screen->respond, Line, 5);
1392	}
1393    }
1394}
1395
1396#if OPT_READLINE
1397void
1398ReadLineButton(Widget w,
1399	       XEvent *event,	/* must be XButtonEvent */
1400	       String *params,	/* selections */
1401	       Cardinal *num_params)
1402{
1403    XtermWidget xw;
1404
1405    if ((xw = getXtermWidget(w)) != 0) {
1406	TScreen *screen = TScreenOf(xw);
1407	Char Line[6];
1408	int line, col, ldelta = 0;
1409
1410	if (!IsBtnEvent(event)
1411	    || (okSendMousePos(xw) != MOUSE_OFF) || ExtendingSelection)
1412	    goto finish;
1413	if (event->type == ButtonRelease) {
1414	    int delta;
1415
1416	    if (lastButtonDownTime == (Time) 0) {
1417		/* first time and once in a blue moon */
1418		delta = screen->multiClickTime + 1;
1419	    } else if (event->xbutton.time > lastButtonDownTime) {
1420		/* most of the time */
1421		delta = (int) (event->xbutton.time - lastButtonDownTime);
1422	    } else {
1423		/* time has rolled over since lastButtonUpTime */
1424		delta = (int) ((((Time) ~ 0) - lastButtonDownTime) + event->xbutton.time);
1425	    }
1426	    if (delta > screen->multiClickTime)
1427		goto finish;	/* All this work for this... */
1428	}
1429	line = (event->xbutton.y - screen->border) / FontHeight(screen);
1430	if (!rowOnCurrentLine(screen, line, &ldelta))
1431	    goto finish;
1432	/* Correct by half a width - we are acting on a boundary, not on a cell. */
1433	col = (event->xbutton.x - OriginX(screen) + (FontWidth(screen) - 1)
1434	       / 2)
1435	    / FontWidth(screen) - screen->cur_col + ldelta * MaxCols(screen);
1436	if (col == 0)
1437	    goto finish;
1438	Line[0] = ANSI_ESC;
1439	/* XXX: sometimes it is better to send '['? */
1440	Line[1] = 'O';
1441	Line[2] = CharOf(col > 0 ? 'C' : 'D');
1442	if (col < 0)
1443	    col = -col;
1444	while (col--)
1445	    v_write(screen->respond, Line, 3);
1446      finish:
1447	if (event->type == ButtonRelease)
1448	    do_select_end(xw, event, params, num_params, False);
1449    }
1450}
1451#endif /* OPT_READLINE */
1452
1453/* repeats <ESC>n or <ESC>p */
1454void
1455ViButton(Widget w,
1456	 XEvent *event,		/* must be XButtonEvent */
1457	 String *params GCC_UNUSED,	/* selections */
1458	 Cardinal *num_params GCC_UNUSED)
1459{
1460    XtermWidget xw;
1461
1462    if ((xw = getXtermWidget(w)) != 0) {
1463	TScreen *screen = TScreenOf(xw);
1464	int pty = screen->respond;
1465
1466	if (IsBtnEvent(event)) {
1467	    int line;
1468
1469	    line = screen->cur_row -
1470		((event->xbutton.y - screen->border) / FontHeight(screen));
1471
1472	    if (line != 0) {
1473		Char Line[6];
1474
1475		Line[0] = ANSI_ESC;	/* force an exit from insert-mode */
1476		v_write(pty, Line, 1);
1477
1478		if (line < 0) {
1479		    line = -line;
1480		    Line[0] = CONTROL('n');
1481		} else {
1482		    Line[0] = CONTROL('p');
1483		}
1484		while (--line >= 0)
1485		    v_write(pty, Line, 1);
1486	    }
1487	}
1488    }
1489}
1490
1491/*
1492 * This function handles button-motion events
1493 */
1494/*ARGSUSED*/
1495void
1496HandleSelectExtend(Widget w,
1497		   XEvent *event,	/* must be XMotionEvent */
1498		   String *params GCC_UNUSED,
1499		   Cardinal *num_params GCC_UNUSED)
1500{
1501    XtermWidget xw;
1502
1503    if ((xw = getXtermWidget(w)) != 0) {
1504	TScreen *screen = TScreenOf(xw);
1505	CELL cell;
1506
1507	TRACE_EVENT("HandleSelectExtend", event, params, num_params);
1508
1509	screen->selection_time = event->xmotion.time;
1510	switch (screen->eventMode) {
1511	    /* If not in one of the DEC mouse-reporting modes */
1512	case LEFTEXTENSION:
1513	case RIGHTEXTENSION:
1514	    PointToCELL(screen, event->xmotion.y, event->xmotion.x, &cell);
1515	    ExtendExtend(xw, &cell);
1516	    break;
1517
1518	    /* If in motion reporting mode, send mouse position to
1519	       character process as a key sequence \E[M... */
1520	case NORMAL:
1521	    /* will get here if send_mouse_pos != MOUSE_OFF */
1522	    if (okSendMousePos(xw) == BTN_EVENT_MOUSE
1523		|| okSendMousePos(xw) == ANY_EVENT_MOUSE) {
1524		(void) SendMousePosition(xw, event);
1525	    }
1526	    break;
1527	}
1528    }
1529}
1530
1531void
1532HandleKeyboardSelectExtend(Widget w,
1533			   XEvent *event GCC_UNUSED,	/* must be XButtonEvent */
1534			   String *params GCC_UNUSED,
1535			   Cardinal *num_params GCC_UNUSED)
1536{
1537    XtermWidget xw;
1538
1539    if ((xw = getXtermWidget(w)) != 0) {
1540	TScreen *screen = TScreenOf(xw);
1541
1542	TRACE_EVENT("HandleKeyboardSelectExtend", event, params, num_params);
1543	ExtendExtend(xw, &screen->cursorp);
1544    }
1545}
1546
1547static void
1548do_select_end(XtermWidget xw,
1549	      XEvent *event,	/* must be XButtonEvent */
1550	      String *params,	/* selections */
1551	      Cardinal *num_params,
1552	      Bool use_cursor_loc)
1553{
1554    TScreen *screen = TScreenOf(xw);
1555
1556    screen->selection_time = event->xbutton.time;
1557
1558    TRACE(("do_select_end %s @%ld\n",
1559	   visibleEventMode(screen->eventMode),
1560	   screen->selection_time));
1561
1562    switch (screen->eventMode) {
1563    case NORMAL:
1564	(void) SendMousePosition(xw, event);
1565	break;
1566    case LEFTEXTENSION:
1567    case RIGHTEXTENSION:
1568	EndExtend(xw, event, params, *num_params, use_cursor_loc);
1569#if OPT_READLINE
1570	readlineExtend(xw, event);
1571#endif /* OPT_READLINE */
1572	break;
1573    }
1574}
1575
1576void
1577HandleSelectEnd(Widget w,
1578		XEvent *event,	/* must be XButtonEvent */
1579		String *params,	/* selections */
1580		Cardinal *num_params)
1581{
1582    XtermWidget xw;
1583
1584    if ((xw = getXtermWidget(w)) != 0) {
1585	TRACE(("HandleSelectEnd\n"));
1586	do_select_end(xw, event, params, num_params, False);
1587    }
1588}
1589
1590void
1591HandleKeyboardSelectEnd(Widget w,
1592			XEvent *event,	/* must be XButtonEvent */
1593			String *params,		/* selections */
1594			Cardinal *num_params)
1595{
1596    XtermWidget xw;
1597
1598    if ((xw = getXtermWidget(w)) != 0) {
1599	TRACE(("HandleKeyboardSelectEnd\n"));
1600	do_select_end(xw, event, params, num_params, True);
1601    }
1602}
1603
1604void
1605HandlePointerMotion(Widget w,
1606		    XEvent *event,
1607		    String *params,	/* selections */
1608		    Cardinal *num_params)
1609{
1610    XtermWidget xw;
1611
1612    (void) params;
1613    (void) num_params;
1614    if ((xw = getXtermWidget(w)) != 0) {
1615	TRACE(("HandlePointerMotion\n"));
1616	if (event->type == MotionNotify)
1617	    (void) SendMousePosition(xw, event);
1618    }
1619}
1620
1621void
1622HandlePointerButton(Widget w,
1623		    XEvent *event,
1624		    String *params,	/* selections */
1625		    Cardinal *num_params)
1626{
1627    XtermWidget xw;
1628
1629    (void) params;
1630    (void) num_params;
1631    if ((xw = getXtermWidget(w)) != 0) {
1632	TRACE(("HandlePointerButton\n"));
1633	if (IsBtnEvent(event))
1634	    (void) SendMousePosition(xw, event);
1635    }
1636}
1637
1638/*
1639 * Copy the selection data to the given target(s).
1640 */
1641void
1642HandleCopySelection(Widget w,
1643		    XEvent *event,
1644		    String *params,	/* list of targets */
1645		    Cardinal *num_params)
1646{
1647    XtermWidget xw;
1648
1649    if ((xw = getXtermWidget(w)) != 0) {
1650	TRACE_EVENT("HandleCopySelection", event, params, num_params);
1651	SelectSet(xw, event, params, *num_params);
1652    }
1653}
1654
1655struct _SelectionList {
1656    String *params;
1657    Cardinal count;
1658    Atom *targets;
1659    Time time;
1660};
1661
1662static unsigned
1663DECtoASCII(unsigned ch)
1664{
1665    if (xtermIsDecGraphic(ch)) {
1666	ch = CharOf("###########+++++##-##++++|######"[ch]);
1667	/*           01234567890123456789012345678901 */
1668    }
1669    return ch;
1670}
1671
1672#if OPT_WIDE_CHARS
1673static Cardinal
1674addXtermChar(Char **buffer, Cardinal *used, Cardinal offset, unsigned value)
1675{
1676    if (offset + 1 >= *used) {
1677	*used = 1 + (2 * (offset + 1));
1678	allocXtermChars(buffer, *used);
1679    }
1680    (*buffer)[offset++] = (Char) value;
1681    return offset;
1682}
1683#define AddChar(buffer, used, offset, value) \
1684	offset = addXtermChar(buffer, used, offset, (unsigned) value)
1685
1686/*
1687 * Convert a UTF-8 string to Latin-1, replacing non Latin-1 characters by `#',
1688 * or ASCII/Latin-1 equivalents for special cases.
1689 */
1690static Char *
1691UTF8toLatin1(TScreen *screen, Char *s, unsigned long len, unsigned long *result)
1692{
1693    static Char *buffer;
1694    static Cardinal used;
1695
1696    Cardinal offset = 0;
1697
1698    if (len != 0) {
1699	PtyData data;
1700
1701	fakePtyData(&data, s, s + len);
1702	while (decodeUtf8(screen, &data)) {
1703	    Bool fails = False;
1704	    Bool extra = False;
1705	    IChar value;
1706	    skipPtyData(&data, value);
1707	    if (value == UCS_REPL) {
1708		fails = True;
1709	    } else if (value < 256) {
1710		AddChar(&buffer, &used, offset, CharOf(value));
1711	    } else {
1712		unsigned eqv = ucs2dec(screen, value);
1713		if (xtermIsDecGraphic(eqv)) {
1714		    AddChar(&buffer, &used, offset, DECtoASCII(eqv));
1715		} else {
1716		    eqv = AsciiEquivs(value);
1717		    if (eqv == value) {
1718			fails = True;
1719		    } else {
1720			AddChar(&buffer, &used, offset, eqv);
1721		    }
1722		    if (isWide((wchar_t) value))
1723			extra = True;
1724		}
1725	    }
1726
1727	    /*
1728	     * If we're not able to plug in a single-byte result, insert the
1729	     * defaultString (which normally is a single "#", but could be
1730	     * whatever the user wants).
1731	     */
1732	    if (fails) {
1733		const Char *p;
1734
1735		for (p = (const Char *) screen->default_string; *p != '\0'; ++p) {
1736		    AddChar(&buffer, &used, offset, *p);
1737		}
1738	    }
1739	    if (extra)
1740		AddChar(&buffer, &used, offset, ' ');
1741	}
1742	AddChar(&buffer, &used, offset, '\0');
1743	*result = (unsigned long) (offset - 1);
1744    } else {
1745	*result = 0;
1746    }
1747    return buffer;
1748}
1749
1750int
1751xtermUtf8ToTextList(XtermWidget xw,
1752		    XTextProperty * text_prop,
1753		    char ***text_list,
1754		    int *text_list_count)
1755{
1756    TScreen *screen = TScreenOf(xw);
1757    Display *dpy = screen->display;
1758    int rc = -1;
1759
1760    if (text_prop->format == 8
1761	&& (rc = Xutf8TextPropertyToTextList(dpy, text_prop,
1762					     text_list,
1763					     text_list_count)) >= 0) {
1764	if (*text_list != NULL && *text_list_count != 0) {
1765	    int i;
1766	    Char *data;
1767	    char **new_text_list, *tmp;
1768	    unsigned long size, new_size;
1769
1770	    TRACE(("xtermUtf8ToTextList size %d\n", *text_list_count));
1771
1772	    /*
1773	     * XLib StringList actually uses only two pointers, one for the
1774	     * list itself, and one for the data.  Pointer to the data is the
1775	     * first element of the list, the rest (if any) list elements point
1776	     * to the same memory block as the first element
1777	     */
1778	    new_size = 0;
1779	    for (i = 0; i < *text_list_count; ++i) {
1780		data = (Char *) (*text_list)[i];
1781		size = strlen((*text_list)[i]) + 1;
1782		(void) UTF8toLatin1(screen, data, size, &size);
1783		new_size += size + 1;
1784	    }
1785	    new_text_list = TypeXtMallocN(char *, *text_list_count);
1786	    new_text_list[0] = tmp = XtMalloc((Cardinal) new_size);
1787	    for (i = 0; i < (*text_list_count); ++i) {
1788		data = (Char *) (*text_list)[i];
1789		size = strlen((*text_list)[i]) + 1;
1790		if ((data = UTF8toLatin1(screen, data, size, &size)) != 0) {
1791		    memcpy(tmp, data, size + 1);
1792		    new_text_list[i] = tmp;
1793		    tmp += size + 1;
1794		}
1795	    }
1796	    XFreeStringList((*text_list));
1797	    *text_list = new_text_list;
1798	} else {
1799	    rc = -1;
1800	}
1801    }
1802    return rc;
1803}
1804#endif /* OPT_WIDE_CHARS */
1805
1806static char *
1807parseItem(char *value, char *nextc)
1808{
1809    char *nextp = value;
1810    while (*nextp != '\0' && *nextp != ',') {
1811	*nextp = x_toupper(*nextp);
1812	++nextp;
1813    }
1814    *nextc = *nextp;
1815    *nextp = '\0';
1816
1817    return nextp;
1818}
1819
1820/*
1821 * All of the wanted strings are unique in the first character, so we can
1822 * use simple abbreviations.
1823 */
1824static Bool
1825sameItem(const char *actual, const char *wanted)
1826{
1827    Bool result = False;
1828    size_t have = strlen(actual);
1829    size_t need = strlen(wanted);
1830
1831    if (have != 0 && have <= need) {
1832	if (!strncmp(actual, wanted, have)) {
1833	    TRACE(("...matched \"%s\"\n", wanted));
1834	    result = True;
1835	}
1836    }
1837
1838    return result;
1839}
1840
1841/*
1842 * Handle the eightBitSelectTypes or utf8SelectTypes resource values.
1843 */
1844static Bool
1845overrideTargets(Widget w, String value, Atom **resultp)
1846{
1847    Bool override = False;
1848    XtermWidget xw;
1849
1850    if ((xw = getXtermWidget(w)) != 0) {
1851	TScreen *screen = TScreenOf(xw);
1852
1853	if (!IsEmpty(value)) {
1854	    char *copied = x_strdup(value);
1855	    if (copied != 0) {
1856		Atom *result = 0;
1857		Cardinal count = 1;
1858		int n;
1859
1860		TRACE(("decoding SelectTypes \"%s\"\n", value));
1861		for (n = 0; copied[n] != '\0'; ++n) {
1862		    if (copied[n] == ',')
1863			++count;
1864		}
1865		result = TypeXtMallocN(Atom, (2 * count) + 1);
1866		if (result == NULL) {
1867		    TRACE(("Couldn't allocate selection types\n"));
1868		} else {
1869		    char nextc = '?';
1870		    char *listp = (char *) copied;
1871		    count = 0;
1872		    do {
1873			char *nextp = parseItem(listp, &nextc);
1874			char *item = x_strtrim(listp);
1875			size_t len = (item ? strlen(item) : 0);
1876
1877			if (len == 0) {
1878			    /* EMPTY */ ;
1879			}
1880#if OPT_WIDE_CHARS
1881			else if (sameItem(item, "UTF8")) {
1882			    result[count++] = XA_UTF8_STRING(XtDisplay(w));
1883			}
1884#endif
1885			else if (sameItem(item, "I18N")) {
1886			    if (screen->i18nSelections) {
1887				result[count++] = XA_TEXT(XtDisplay(w));
1888				result[count++] = XA_COMPOUND_TEXT(XtDisplay(w));
1889			    }
1890			} else if (sameItem(item, "TEXT")) {
1891			    result[count++] = XA_TEXT(XtDisplay(w));
1892			} else if (sameItem(item, "COMPOUND_TEXT")) {
1893			    result[count++] = XA_COMPOUND_TEXT(XtDisplay(w));
1894			} else if (sameItem(item, "STRING")) {
1895			    result[count++] = XA_STRING;
1896			}
1897			*nextp++ = nextc;
1898			listp = nextp;
1899			free(item);
1900		    } while (nextc != '\0');
1901		    if (count) {
1902			result[count] = None;
1903			override = True;
1904			*resultp = result;
1905		    } else {
1906			XtFree((char *) result);
1907		    }
1908		}
1909		free(copied);
1910	    } else {
1911		TRACE(("Couldn't allocate copy of selection types\n"));
1912	    }
1913	}
1914    }
1915    return override;
1916}
1917
1918#if OPT_WIDE_CHARS
1919static Atom *
1920allocUtf8Targets(Widget w, TScreen *screen)
1921{
1922    Atom **resultp = &(screen->selection_targets_utf8);
1923
1924    if (*resultp == 0) {
1925	Atom *result;
1926
1927	if (!overrideTargets(w, screen->utf8_select_types, &result)) {
1928	    result = TypeXtMallocN(Atom, 5);
1929	    if (result == NULL) {
1930		TRACE(("Couldn't allocate utf-8 selection targets\n"));
1931	    } else {
1932		int n = 0;
1933
1934		if (XSupportsLocale()) {
1935		    result[n++] = XA_UTF8_STRING(XtDisplay(w));
1936#ifdef X_HAVE_UTF8_STRING
1937		    if (screen->i18nSelections) {
1938			result[n++] = XA_TEXT(XtDisplay(w));
1939			result[n++] = XA_COMPOUND_TEXT(XtDisplay(w));
1940		    }
1941#endif
1942		}
1943		result[n++] = XA_STRING;
1944		result[n] = None;
1945	    }
1946	}
1947
1948	*resultp = result;
1949    }
1950
1951    return *resultp;
1952}
1953#endif
1954
1955static Atom *
1956alloc8bitTargets(Widget w, TScreen *screen)
1957{
1958    Atom **resultp = &(screen->selection_targets_8bit);
1959
1960    if (*resultp == 0) {
1961	Atom *result = 0;
1962
1963	if (!overrideTargets(w, screen->eightbit_select_types, &result)) {
1964	    result = TypeXtMallocN(Atom, 5);
1965	    if (result == NULL) {
1966		TRACE(("Couldn't allocate 8bit selection targets\n"));
1967	    } else {
1968		int n = 0;
1969
1970		if (XSupportsLocale()) {
1971#ifdef X_HAVE_UTF8_STRING
1972		    result[n++] = XA_UTF8_STRING(XtDisplay(w));
1973#endif
1974		    if (screen->i18nSelections) {
1975			result[n++] = XA_TEXT(XtDisplay(w));
1976			result[n++] = XA_COMPOUND_TEXT(XtDisplay(w));
1977		    }
1978		}
1979		result[n++] = XA_STRING;
1980		result[n] = None;
1981	    }
1982	}
1983
1984	*resultp = result;
1985    }
1986
1987    return *resultp;
1988}
1989
1990static Atom *
1991_SelectionTargets(Widget w)
1992{
1993    Atom *result;
1994    XtermWidget xw;
1995
1996    if ((xw = getXtermWidget(w)) == 0) {
1997	result = NULL;
1998    } else {
1999	TScreen *screen = TScreenOf(xw);
2000
2001#if OPT_WIDE_CHARS
2002	if (screen->wide_chars) {
2003	    result = allocUtf8Targets(w, screen);
2004	} else
2005#endif
2006	{
2007	    /* not screen->wide_chars */
2008	    result = alloc8bitTargets(w, screen);
2009	}
2010    }
2011
2012    return result;
2013}
2014
2015#define isSELECT(value) (!strcmp(value, "SELECT"))
2016
2017static int
2018DefaultSelection(TScreen *screen)
2019{
2020    return (screen->selectToClipboard ? 1 : 0);
2021}
2022
2023static int
2024TargetToSelection(TScreen *screen, String name)
2025{
2026    int result = -1;
2027    int cutb;
2028
2029    if (isSELECT(name)) {
2030	result = DefaultSelection(screen);
2031    } else if (!strcmp(name, PRIMARY_NAME)) {
2032	result = PRIMARY_CODE;
2033    } else if (!strcmp(name, CLIPBOARD_NAME)) {
2034	result = CLIPBOARD_CODE;
2035    } else if (!strcmp(name, SECONDARY_NAME)) {
2036	result = SECONDARY_CODE;
2037    } else if (sscanf(name, "CUT_BUFFER%d", &cutb) == 1) {
2038	if (cutb >= 0 && cutb < MAX_CUT_BUFFER) {
2039	    result = CutBufferToCode(cutb);
2040	} else {
2041	    xtermWarning("unexpected cut-buffer code: %d\n", cutb);
2042	}
2043    } else {
2044	xtermWarning("unexpected selection target: %s\n", name);
2045    }
2046    TRACE2(("TargetToSelection(%s) ->%d\n", name, result));
2047    return result;
2048}
2049
2050void
2051UnmapSelections(XtermWidget xw)
2052{
2053    TScreen *screen = TScreenOf(xw);
2054    Cardinal n;
2055
2056    if (screen->mappedSelect) {
2057	for (n = 0; screen->mappedSelect[n] != 0; ++n)
2058	    free((void *) screen->mappedSelect[n]);
2059	FreeAndNull(screen->mappedSelect);
2060    }
2061}
2062
2063/*
2064 * xterm generally uses the primary selection.  Some applications prefer
2065 * (or are limited to) the clipboard.  Since the translations resource is
2066 * complicated, users seldom change the way it affects selection.  But it
2067 * is simple to remap the choice between primary and clipboard before the
2068 * call to XmuInternStrings().
2069 */
2070static String *
2071MapSelections(XtermWidget xw, String *params, Cardinal num_params)
2072{
2073    String *result = params;
2074
2075    if (params != 0 && num_params > 0) {
2076	Cardinal j;
2077	Boolean map = False;
2078
2079	for (j = 0; j < num_params; ++j) {
2080	    TRACE(("param[%d]:%s\n", j, params[j]));
2081	    if (isSELECT(params[j])) {
2082		map = True;
2083		break;
2084	    }
2085	}
2086	if (map) {
2087	    TScreen *screen = TScreenOf(xw);
2088	    const char *mapTo = (screen->selectToClipboard
2089				 ? CLIPBOARD_NAME
2090				 : PRIMARY_NAME);
2091
2092	    UnmapSelections(xw);
2093	    if ((result = TypeMallocN(String, num_params + 1)) != 0) {
2094		result[num_params] = 0;
2095		for (j = 0; j < num_params; ++j) {
2096		    result[j] = x_strdup((isSELECT(params[j])
2097					  ? mapTo
2098					  : params[j]));
2099		    if (result[j] == 0) {
2100			UnmapSelections(xw);
2101			while (j != 0) {
2102			    free((void *) result[--j]);
2103			}
2104			FreeAndNull(result);
2105			break;
2106		    }
2107		}
2108		screen->mappedSelect = result;
2109	    }
2110	}
2111    }
2112    return result;
2113}
2114
2115/*
2116 * Lookup the cut-buffer number, which will be in the range 0-7.
2117 * If it is not a cut-buffer, it is a type of selection, e.g., primary.
2118 */
2119static int
2120CutBuffer(Atom code)
2121{
2122    int cutbuffer;
2123    switch ((unsigned) code) {
2124    case XA_CUT_BUFFER0:
2125	cutbuffer = 0;
2126	break;
2127    case XA_CUT_BUFFER1:
2128	cutbuffer = 1;
2129	break;
2130    case XA_CUT_BUFFER2:
2131	cutbuffer = 2;
2132	break;
2133    case XA_CUT_BUFFER3:
2134	cutbuffer = 3;
2135	break;
2136    case XA_CUT_BUFFER4:
2137	cutbuffer = 4;
2138	break;
2139    case XA_CUT_BUFFER5:
2140	cutbuffer = 5;
2141	break;
2142    case XA_CUT_BUFFER6:
2143	cutbuffer = 6;
2144	break;
2145    case XA_CUT_BUFFER7:
2146	cutbuffer = 7;
2147	break;
2148    default:
2149	cutbuffer = -1;
2150	break;
2151    }
2152    TRACE2(("CutBuffer(%d) = %d\n", (int) code, cutbuffer));
2153    return cutbuffer;
2154}
2155
2156#if OPT_PASTE64
2157static void
2158FinishPaste64(XtermWidget xw)
2159{
2160    TScreen *screen = TScreenOf(xw);
2161
2162    TRACE(("FinishPaste64(%d)\n", screen->base64_paste));
2163    if (screen->base64_paste) {
2164	screen->base64_paste = 0;
2165	unparseputc1(xw, screen->base64_final);
2166	unparse_end(xw);
2167    }
2168}
2169#endif
2170
2171#if !OPT_PASTE64
2172static
2173#endif
2174void
2175xtermGetSelection(Widget w,
2176		  Time ev_time,
2177		  String *params,	/* selections in precedence order */
2178		  Cardinal num_params,
2179		  Atom *targets)
2180{
2181    Atom selection;
2182    int cutbuffer;
2183    Atom target;
2184
2185    XtermWidget xw;
2186
2187    if (num_params == 0)
2188	return;
2189    if ((xw = getXtermWidget(w)) == 0)
2190	return;
2191
2192    TRACE(("xtermGetSelection num_params %d @%ld\n", num_params, ev_time));
2193    params = MapSelections(xw, params, num_params);
2194
2195    XmuInternStrings(XtDisplay(w), params, (Cardinal) 1, &selection);
2196    cutbuffer = CutBuffer(selection);
2197
2198    TRACE(("Cutbuffer: %d, target: %s\n", cutbuffer,
2199	   (targets
2200	    ? visibleSelectionTarget(XtDisplay(w), targets[0])
2201	    : "None")));
2202
2203    if (cutbuffer >= 0) {
2204	int inbytes;
2205	unsigned long nbytes;
2206	int fmt8 = 8;
2207	Atom type = XA_STRING;
2208	char *line;
2209
2210	/* 'line' is freed in SelectionReceived */
2211	line = XFetchBuffer(XtDisplay(w), &inbytes, cutbuffer);
2212	nbytes = (unsigned long) inbytes;
2213
2214	if (nbytes > 0) {
2215	    SelectionReceived(w, NULL, &selection, &type, (XtPointer) line,
2216			      &nbytes, &fmt8);
2217	} else if (num_params > 1) {
2218	    xtermGetSelection(w, ev_time, params + 1, num_params - 1, NULL);
2219	}
2220#if OPT_PASTE64
2221	else {
2222	    FinishPaste64(xw);
2223	}
2224#endif
2225    } else {
2226
2227	if (targets == NULL || targets[0] == None) {
2228	    targets = _SelectionTargets(w);
2229	}
2230
2231	if (targets != 0) {
2232	    struct _SelectionList *list;
2233
2234	    target = targets[0];
2235
2236	    if (targets[1] == None) {	/* last target in list */
2237		params++;
2238		num_params--;
2239		targets = _SelectionTargets(w);
2240	    } else {
2241		targets = &(targets[1]);
2242	    }
2243
2244	    if (num_params) {
2245		/* 'list' is freed in SelectionReceived */
2246		list = TypeXtMalloc(struct _SelectionList);
2247		if (list != 0) {
2248		    list->params = params;
2249		    list->count = num_params;
2250		    list->targets = targets;
2251		    list->time = ev_time;
2252		}
2253	    } else {
2254		list = NULL;
2255	    }
2256
2257	    XtGetSelectionValue(w, selection,
2258				target,
2259				SelectionReceived,
2260				(XtPointer) list, ev_time);
2261	}
2262    }
2263}
2264
2265#if OPT_TRACE && OPT_WIDE_CHARS
2266static void
2267GettingSelection(Display *dpy, Atom type, Char *line, unsigned long len)
2268{
2269    Char *cp;
2270    const char *name = TraceAtomName(dpy, type);
2271
2272    TRACE(("Getting %s (type=%ld, length=%ld)\n", name, (long int) type, len));
2273    for (cp = line; cp < line + len; cp++) {
2274	TRACE(("[%d:%lu]", (int) (cp + 1 - line), len));
2275	if (isprint(*cp)) {
2276	    TRACE(("%c\n", *cp));
2277	} else {
2278	    TRACE(("\\x%02x\n", *cp));
2279	}
2280    }
2281}
2282#else
2283#define GettingSelection(dpy,type,line,len)	/* nothing */
2284#endif
2285
2286#ifdef VMS
2287#  define tty_vwrite(pty,lag,l)		tt_write(lag,l)
2288#else /* !( VMS ) */
2289#  define tty_vwrite(pty,lag,l)		v_write(pty,lag,l)
2290#endif /* defined VMS */
2291
2292#if OPT_PASTE64
2293/* Return base64 code character given 6-bit number */
2294static const char base64_code[] = "\
2295ABCDEFGHIJKLMNOPQRSTUVWXYZ\
2296abcdefghijklmnopqrstuvwxyz\
22970123456789+/";
2298static void
2299base64_flush(TScreen *screen)
2300{
2301    Char x;
2302
2303    TRACE(("base64_flush count %d, pad %d (%d)\n",
2304	   screen->base64_count,
2305	   screen->base64_pad,
2306	   screen->base64_pad & 3));
2307
2308    switch (screen->base64_count) {
2309    case 0:
2310	break;
2311    case 2:
2312	x = CharOf(base64_code[screen->base64_accu << 4]);
2313	tty_vwrite(screen->respond, &x, 1);
2314	break;
2315    case 4:
2316	x = CharOf(base64_code[screen->base64_accu << 2]);
2317	tty_vwrite(screen->respond, &x, 1);
2318	break;
2319    }
2320    if (screen->base64_pad & 3) {
2321	tty_vwrite(screen->respond,
2322		   (const Char *) "===",
2323		   (unsigned) (3 - (screen->base64_pad & 3)));
2324    }
2325    screen->base64_count = 0;
2326    screen->base64_accu = 0;
2327    screen->base64_pad = 0;
2328}
2329#endif /* OPT_PASTE64 */
2330
2331/*
2332 * Translate ISO-8859-1 or UTF-8 data to NRCS.
2333 */
2334static void
2335ToNational(XtermWidget xw, Char *buffer, unsigned *length)
2336{
2337    TScreen *screen = TScreenOf(xw);
2338    DECNRCM_codes gsetL = screen->gsets[screen->curgl];
2339    DECNRCM_codes gsetR = screen->gsets[screen->curgr];
2340
2341#if OPT_WIDE_CHARS
2342    if ((screen->utf8_nrc_mode | screen->utf8_mode) != uFalse) {
2343	Char *p;
2344	PtyData *data = TypeXtMallocX(PtyData, *length);
2345
2346	memset(data, 0, sizeof(*data));
2347	data->next = data->buffer;
2348	data->last = data->buffer + *length;
2349	memcpy(data->buffer, buffer, (size_t) *length);
2350	p = buffer;
2351	while (data->next < data->last) {
2352	    unsigned chr, out, gl, gr;
2353
2354	    if (!decodeUtf8(screen, data)) {
2355		data->utf_size = 1;
2356		data->utf_data = data->next[0];
2357	    }
2358	    data->next += data->utf_size;
2359	    chr = data->utf_data;
2360	    out = chr;
2361	    if ((gl = xtermCharSetIn(xw, chr, gsetL)) != chr) {
2362		out = gl;
2363	    } else if ((gr = xtermCharSetIn(xw, chr, gsetR)) != chr) {
2364		out = gr;
2365	    }
2366	    *p++ = (Char) ((out < 256) ? out : ' ');
2367	}
2368	*length = (unsigned) (p - buffer);
2369	free(data);
2370    } else
2371#endif
2372    {
2373	Char *p;
2374
2375	for (p = buffer; (int) (p - buffer) < (int) *length; ++p) {
2376	    unsigned gl, gr;
2377	    unsigned chr = *p;
2378	    unsigned out = chr;
2379	    if ((gl = xtermCharSetIn(xw, chr, gsetL)) != chr) {
2380		out = gl;
2381	    } else if ((gr = xtermCharSetIn(xw, chr, gsetR)) != chr) {
2382		out = gr;
2383	    }
2384	    *p = (Char) out;
2385	}
2386    }
2387}
2388
2389static void
2390_qWriteSelectionData(XtermWidget xw, Char *lag, unsigned length)
2391{
2392    TScreen *screen = TScreenOf(xw);
2393
2394    /*
2395     * If we are pasting into a window which is using NRCS, we want to map
2396     * the text from the normal encoding (ISO-8859-1 or UTF-8) into the coding
2397     * that an application would use to write characters with NRCS.
2398     *
2399     * TODO: handle conversion from UTF-8, and adjust length.  This can be done
2400     * in the same buffer because the target is always 8-bit.
2401     */
2402    if ((xw->flags & NATIONAL) && (length != 0)) {
2403	ToNational(xw, lag, &length);
2404    }
2405#if OPT_PASTE64
2406    if (screen->base64_paste) {
2407	/* Send data as base64 */
2408	Char *p = lag;
2409	Char buf[64];
2410	unsigned x = 0;
2411
2412	TRACE(("convert to base64 %d:%s\n", length, visibleChars(p, length)));
2413
2414	/*
2415	 * Handle the case where the selection is from _this_ xterm, which
2416	 * puts part of the reply in the buffer before the selection callback
2417	 * happens.
2418	 */
2419	if (screen->base64_paste && screen->unparse_len) {
2420	    unparse_end(xw);
2421	}
2422	while (length--) {
2423	    switch (screen->base64_count) {
2424	    case 0:
2425		buf[x++] = CharOf(base64_code[*p >> 2]);
2426		screen->base64_accu = (unsigned) (*p & 0x3);
2427		screen->base64_count = 2;
2428		++p;
2429		break;
2430	    case 2:
2431		buf[x++] = CharOf(base64_code[(screen->base64_accu << 4) +
2432					      (*p >> 4)]);
2433		screen->base64_accu = (unsigned) (*p & 0xF);
2434		screen->base64_count = 4;
2435		++p;
2436		break;
2437	    case 4:
2438		buf[x++] = CharOf(base64_code[(screen->base64_accu << 2) +
2439					      (*p >> 6)]);
2440		buf[x++] = CharOf(base64_code[*p & 0x3F]);
2441		screen->base64_accu = 0;
2442		screen->base64_count = 0;
2443		++p;
2444		break;
2445	    }
2446	    if (x >= 63) {
2447		/* Write 63 or 64 characters */
2448		screen->base64_pad += x;
2449		TRACE(("writing base64 interim %s\n", visibleChars(buf, x)));
2450		tty_vwrite(screen->respond, buf, x);
2451		x = 0;
2452	    }
2453	}
2454	if (x != 0) {
2455	    screen->base64_pad += x;
2456	    TRACE(("writing base64 finish %s\n", visibleChars(buf, x)));
2457	    tty_vwrite(screen->respond, buf, x);
2458	}
2459    } else
2460#endif /* OPT_PASTE64 */
2461#if OPT_READLINE
2462    if (SCREEN_FLAG(screen, paste_quotes)) {
2463	while (length--) {
2464	    tty_vwrite(screen->respond, (const Char *) "\026", 1);	/* Control-V */
2465	    tty_vwrite(screen->respond, lag++, 1);
2466	}
2467    } else
2468#endif
2469    {
2470	TRACE(("writing base64 padding %s\n", visibleChars(lag, length)));
2471	tty_vwrite(screen->respond, lag, length);
2472    }
2473}
2474
2475static void
2476_WriteSelectionData(XtermWidget xw, Char *line, size_t length)
2477{
2478    /* Write data to pty a line at a time. */
2479    /* Doing this one line at a time may no longer be necessary
2480       because v_write has been re-written. */
2481
2482#if OPT_PASTE64
2483    TScreen *screen = TScreenOf(xw);
2484#endif
2485    Char *lag, *end;
2486
2487    /* in the VMS version, if tt_pasting isn't set to True then qio
2488       reads aren't blocked and an infinite loop is entered, where the
2489       pasted text shows up as new input, goes in again, shows up
2490       again, ad nauseum. */
2491#ifdef VMS
2492    tt_pasting = True;
2493#endif
2494
2495    end = &line[length];
2496    lag = line;
2497
2498#if OPT_PASTE64
2499    if (screen->base64_paste) {
2500	_qWriteSelectionData(xw, lag, (unsigned) (end - lag));
2501	base64_flush(screen);
2502    } else
2503#endif
2504    {
2505	if (!SCREEN_FLAG(screen, paste_literal_nl)) {
2506	    Char *cp;
2507	    for (cp = line; cp != end; cp++) {
2508		if (*cp == '\n') {
2509		    *cp = '\r';
2510		    _qWriteSelectionData(xw, lag, (unsigned) (cp - lag + 1));
2511		    lag = cp + 1;
2512		}
2513	    }
2514	}
2515
2516	if (lag != end) {
2517	    _qWriteSelectionData(xw, lag, (unsigned) (end - lag));
2518	}
2519    }
2520#ifdef VMS
2521    tt_pasting = False;
2522    tt_start_read();		/* reenable reads or a character may be lost */
2523#endif
2524}
2525
2526#if OPT_PASTE64 || OPT_READLINE
2527static void
2528_WriteKey(TScreen *screen, const Char *in)
2529{
2530    Char line[16];
2531    unsigned count = 0;
2532    size_t length = strlen((const char *) in);
2533
2534    if (screen->control_eight_bits) {
2535	line[count++] = ANSI_CSI;
2536    } else {
2537	line[count++] = ANSI_ESC;
2538	line[count++] = '[';
2539    }
2540    while (length--)
2541	line[count++] = *in++;
2542    line[count++] = '~';
2543    tty_vwrite(screen->respond, line, count);
2544}
2545#endif /* OPT_READLINE */
2546
2547/*
2548 * Unless enabled by the user, strip control characters other than formatting.
2549 */
2550static size_t
2551removeControls(XtermWidget xw, char *value)
2552{
2553    TScreen *screen = TScreenOf(xw);
2554    size_t dst = 0;
2555
2556    if (screen->allowPasteControls) {
2557	dst = strlen(value);
2558    } else {
2559	size_t src = 0;
2560	while ((value[dst] = value[src]) != '\0') {
2561	    int ch = CharOf(value[src++]);
2562
2563#define ReplacePaste(n) \
2564	    if (screen->disallow_paste_controls[n]) \
2565		value[dst] = ' '
2566
2567	    if (ch < 32) {
2568		ReplacePaste(epC0);
2569		ReplacePaste(ch);
2570		++dst;
2571	    } else if (ch == ANSI_DEL) {
2572		ReplacePaste(epDEL);
2573		++dst;
2574	    }
2575#if OPT_WIDE_CHARS
2576	    else if (screen->utf8_inparse || screen->utf8_nrc_mode)
2577		++dst;
2578#endif
2579#if OPT_C1_PRINT || OPT_WIDE_CHARS
2580	    else if (screen->c1_printable)
2581		++dst;
2582#endif
2583	    else if (ch >= 128 && ch < 160)
2584		continue;
2585	    else
2586		++dst;
2587	}
2588    }
2589    return dst;
2590}
2591
2592#if OPT_SELECTION_OPS
2593static void
2594beginInternalSelect(XtermWidget xw)
2595{
2596    TScreen *screen = TScreenOf(xw);
2597    InternalSelect *mydata = &(screen->internal_select);
2598
2599    (void) mydata;
2600    /* override flags so that SelectionReceived only updates a buffer */
2601#if OPT_PASTE64
2602    mydata->base64_paste = screen->base64_paste;
2603    screen->base64_paste = 0;
2604#endif
2605#if OPT_PASTE64 || OPT_READLINE
2606    mydata->paste_brackets = screen->paste_brackets;
2607    SCREEN_FLAG_unset(screen, paste_brackets);
2608#endif
2609}
2610
2611static void
2612finishInternalSelect(XtermWidget xw)
2613{
2614    TScreen *screen = TScreenOf(xw);
2615    InternalSelect *mydata = &(screen->internal_select);
2616
2617    (void) mydata;
2618#if OPT_PASTE64
2619    screen->base64_paste = mydata->base64_paste;
2620#endif
2621#if OPT_PASTE64 || OPT_READLINE
2622    screen->paste_brackets = mydata->paste_brackets;
2623#endif
2624}
2625
2626#else
2627#define finishInternalSelect(xw)	/* nothing */
2628#endif /* OPT_SELECTION_OPS */
2629
2630/* SelectionReceived: stuff received selection text into pty */
2631
2632/* ARGSUSED */
2633static void
2634SelectionReceived(Widget w,
2635		  XtPointer client_data,
2636		  Atom *selection GCC_UNUSED,
2637		  Atom *type,
2638		  XtPointer value,
2639		  unsigned long *length,
2640		  int *format)
2641{
2642    char **text_list = NULL;
2643    int text_list_count = 0;
2644    XTextProperty text_prop;
2645    TScreen *screen;
2646    Display *dpy;
2647#if OPT_TRACE && OPT_WIDE_CHARS
2648    Char *line = (Char *) value;
2649#endif
2650
2651    XtermWidget xw;
2652
2653    if ((xw = getXtermWidget(w)) == 0)
2654	return;
2655
2656    screen = TScreenOf(xw);
2657    dpy = XtDisplay(w);
2658
2659    if (*type == 0		/*XT_CONVERT_FAIL */
2660	|| *length == 0
2661	|| value == NULL) {
2662	TRACE(("...no data to convert\n"));
2663	goto fail;
2664    }
2665
2666    text_prop.value = (unsigned char *) value;
2667    text_prop.encoding = *type;
2668    text_prop.format = *format;
2669    text_prop.nitems = *length;
2670
2671    TRACE(("SelectionReceived %s %s format %d, nitems %ld\n",
2672	   TraceAtomName(screen->display, *selection),
2673	   visibleSelectionTarget(dpy, text_prop.encoding),
2674	   text_prop.format,
2675	   text_prop.nitems));
2676
2677#if OPT_WIDE_CHARS
2678    if (XSupportsLocale() && screen->wide_chars) {
2679	if (*type == XA_UTF8_STRING(dpy) ||
2680	    *type == XA_STRING ||
2681	    *type == XA_COMPOUND_TEXT(dpy)) {
2682	    GettingSelection(dpy, *type, line, *length);
2683	    if (Xutf8TextPropertyToTextList(dpy, &text_prop,
2684					    &text_list,
2685					    &text_list_count) < 0) {
2686		TRACE(("default Xutf8 Conversion failed\n"));
2687		text_list = NULL;
2688	    }
2689	}
2690    } else
2691#endif /* OPT_WIDE_CHARS */
2692    {
2693	/* Convert the selection to locale's multibyte encoding. */
2694
2695	if (*type == XA_UTF8_STRING(dpy) ||
2696	    *type == XA_STRING ||
2697	    *type == XA_COMPOUND_TEXT(dpy)) {
2698	    Status rc;
2699
2700	    GettingSelection(dpy, *type, line, *length);
2701
2702#if OPT_WIDE_CHARS
2703	    if (*type == XA_UTF8_STRING(dpy) &&
2704		!(screen->wide_chars || screen->c1_printable)) {
2705		rc = xtermUtf8ToTextList(xw, &text_prop,
2706					 &text_list, &text_list_count);
2707	    } else
2708#endif
2709	    if (*type == XA_STRING && (!XSupportsLocale() || screen->brokenSelections)) {
2710		rc = XTextPropertyToStringList(&text_prop,
2711					       &text_list, &text_list_count);
2712	    } else {
2713		rc = XmbTextPropertyToTextList(dpy, &text_prop,
2714					       &text_list,
2715					       &text_list_count);
2716	    }
2717	    if (rc < 0) {
2718		TRACE(("Conversion failed\n"));
2719		text_list = NULL;
2720	    }
2721	}
2722    }
2723
2724    if (text_list != NULL && text_list_count != 0) {
2725	int i;
2726
2727#if OPT_PASTE64
2728	if (screen->base64_paste) {
2729	    /* EMPTY */ ;
2730	} else
2731#endif
2732#if OPT_PASTE64 || OPT_READLINE
2733	if (SCREEN_FLAG(screen, paste_brackets) && !screen->selectToBuffer) {
2734	    _WriteKey(screen, (const Char *) "200");
2735	}
2736#endif
2737	for (i = 0; i < text_list_count; i++) {
2738	    size_t len = removeControls(xw, text_list[i]);
2739
2740	    if (screen->selectToBuffer) {
2741		InternalSelect *mydata = &(screen->internal_select);
2742		if (!mydata->done) {
2743		    size_t have = (mydata->buffer
2744				   ? strlen(mydata->buffer)
2745				   : 0);
2746		    size_t need = have + len + 1;
2747		    char *buffer = realloc(mydata->buffer, need);
2748
2749		    if (buffer != 0) {
2750			strcpy(buffer + have, text_list[i]);
2751			mydata->buffer = buffer;
2752		    }
2753		    TRACE(("FormatSelect %d.%d .. %d.%d %s\n",
2754			   screen->startSel.row,
2755			   screen->startSel.col,
2756			   screen->endSel.row,
2757			   screen->endSel.col,
2758			   mydata->buffer));
2759		    mydata->format_select(w, mydata->format, mydata->buffer,
2760					  &(screen->startSel),
2761					  &(screen->endSel));
2762		    mydata->done = True;
2763		}
2764
2765	    } else {
2766		_WriteSelectionData(xw, (Char *) text_list[i], len);
2767	    }
2768	}
2769#if OPT_PASTE64
2770	if (screen->base64_paste) {
2771	    FinishPaste64(xw);
2772	} else
2773#endif
2774#if OPT_PASTE64 || OPT_READLINE
2775	if (SCREEN_FLAG(screen, paste_brackets) && !screen->selectToBuffer) {
2776	    _WriteKey(screen, (const Char *) "201");
2777	}
2778#endif
2779	if (screen->selectToBuffer) {
2780	    InternalSelect *mydata = &(screen->internal_select);
2781	    finishInternalSelect(xw);
2782	    if (mydata->done) {
2783		free(mydata->format);
2784		free(mydata->buffer);
2785		memset(mydata, 0, sizeof(*mydata));
2786	    }
2787	    screen->selectToBuffer = False;
2788	}
2789	XFreeStringList(text_list);
2790    } else {
2791	TRACE(("...empty text-list\n"));
2792	goto fail;
2793    }
2794
2795    XtFree((char *) client_data);
2796    XtFree((char *) value);
2797
2798    return;
2799
2800  fail:
2801    if (client_data != 0) {
2802	struct _SelectionList *list = (struct _SelectionList *) client_data;
2803
2804	TRACE(("SelectionReceived ->xtermGetSelection\n"));
2805	xtermGetSelection(w, list->time,
2806			  list->params, list->count, list->targets);
2807	XtFree((char *) client_data);
2808#if OPT_PASTE64
2809    } else {
2810	FinishPaste64(xw);
2811#endif
2812    }
2813    return;
2814}
2815
2816void
2817HandleInsertSelection(Widget w,
2818		      XEvent *event,	/* assumed to be XButtonEvent* */
2819		      String *params,	/* selections in precedence order */
2820		      Cardinal *num_params)
2821{
2822    XtermWidget xw;
2823
2824    if ((xw = getXtermWidget(w)) != 0) {
2825	TRACE_EVENT("HandleInsertSelection", event, params, num_params);
2826	if (!SendMousePosition(xw, event)) {
2827#if OPT_READLINE
2828	    int ldelta;
2829	    TScreen *screen = TScreenOf(xw);
2830	    if (IsBtnEvent(event)
2831		&& !OverrideEvent(event)
2832		&& (okSendMousePos(xw) == MOUSE_OFF)
2833		&& SCREEN_FLAG(screen, paste_moves)
2834		&& rowOnCurrentLine(screen, eventRow(screen, event), &ldelta))
2835		ReadLineMovePoint(screen, eventColBetween(screen, event), ldelta);
2836#endif /* OPT_READLINE */
2837
2838	    xtermGetSelection(w, event->xbutton.time, params, *num_params, NULL);
2839	}
2840    }
2841}
2842
2843static SelectUnit
2844EvalSelectUnit(XtermWidget xw,
2845	       Time buttonDownTime,
2846	       SelectUnit defaultUnit,
2847	       unsigned int button)
2848{
2849    TScreen *screen = TScreenOf(xw);
2850    SelectUnit result;
2851    int delta;
2852
2853    if (button != screen->lastButton) {
2854	delta = screen->multiClickTime + 1;
2855    } else if (screen->lastButtonUpTime == (Time) 0) {
2856	/* first time and once in a blue moon */
2857	delta = screen->multiClickTime + 1;
2858    } else if (buttonDownTime > screen->lastButtonUpTime) {
2859	/* most of the time */
2860	delta = (int) (buttonDownTime - screen->lastButtonUpTime);
2861    } else {
2862	/* time has rolled over since lastButtonUpTime */
2863	delta = (int) ((((Time) ~ 0) - screen->lastButtonUpTime) + buttonDownTime);
2864    }
2865
2866    if (delta > screen->multiClickTime) {
2867	screen->numberOfClicks = 1;
2868	result = defaultUnit;
2869    } else {
2870	result = screen->selectMap[screen->numberOfClicks % screen->maxClicks];
2871	screen->numberOfClicks += 1;
2872    }
2873    TRACE(("EvalSelectUnit(%d) = %d\n", screen->numberOfClicks, result));
2874    return result;
2875}
2876
2877static void
2878do_select_start(XtermWidget xw,
2879		XEvent *event,	/* must be XButtonEvent* */
2880		CELL *cell)
2881{
2882    TScreen *screen = TScreenOf(xw);
2883
2884    if (SendMousePosition(xw, event))
2885	return;
2886    screen->selectUnit = EvalSelectUnit(xw,
2887					event->xbutton.time,
2888					Select_CHAR,
2889					event->xbutton.button);
2890    screen->replyToEmacs = False;
2891
2892#if OPT_READLINE
2893    lastButtonDownTime = event->xbutton.time;
2894#endif
2895
2896    StartSelect(xw, cell);
2897}
2898
2899/* ARGSUSED */
2900void
2901HandleSelectStart(Widget w,
2902		  XEvent *event,	/* must be XButtonEvent* */
2903		  String *params GCC_UNUSED,
2904		  Cardinal *num_params GCC_UNUSED)
2905{
2906    XtermWidget xw;
2907
2908    if ((xw = getXtermWidget(w)) != 0) {
2909	TScreen *screen = TScreenOf(xw);
2910	CELL cell;
2911
2912	TRACE_EVENT("HandleSelectStart", event, params, num_params);
2913	screen->firstValidRow = 0;
2914	screen->lastValidRow = screen->max_row;
2915	PointToCELL(screen, event->xbutton.y, event->xbutton.x, &cell);
2916
2917#if OPT_READLINE
2918	ExtendingSelection = 0;
2919#endif
2920
2921	do_select_start(xw, event, &cell);
2922    }
2923}
2924
2925/* ARGSUSED */
2926void
2927HandleKeyboardSelectStart(Widget w,
2928			  XEvent *event,	/* must be XButtonEvent* */
2929			  String *params GCC_UNUSED,
2930			  Cardinal *num_params GCC_UNUSED)
2931{
2932    XtermWidget xw;
2933
2934    if ((xw = getXtermWidget(w)) != 0) {
2935	TScreen *screen = TScreenOf(xw);
2936
2937	TRACE_EVENT("HandleKeyboardSelectStart", event, params, num_params);
2938	do_select_start(xw, event, &screen->cursorp);
2939    }
2940}
2941
2942static void
2943TrackDown(XtermWidget xw, XButtonEvent *event)
2944{
2945    TScreen *screen = TScreenOf(xw);
2946    CELL cell;
2947
2948    screen->selectUnit = EvalSelectUnit(xw,
2949					event->time,
2950					Select_CHAR,
2951					event->button);
2952    if (screen->numberOfClicks > 1) {
2953	PointToCELL(screen, event->y, event->x, &cell);
2954	screen->replyToEmacs = True;
2955	StartSelect(xw, &cell);
2956    } else {
2957	screen->waitingForTrackInfo = True;
2958	EditorButton(xw, event);
2959    }
2960}
2961
2962#define boundsCheck(x)	if (x < 0) \
2963			    x = 0; \
2964			else if (x >= screen->max_row) \
2965			    x = screen->max_row
2966
2967void
2968TrackMouse(XtermWidget xw,
2969	   int func,
2970	   CELL *start,
2971	   int firstrow,
2972	   int lastrow)
2973{
2974    TScreen *screen = TScreenOf(xw);
2975
2976    if (screen->waitingForTrackInfo) {	/* if Timed, ignore */
2977	screen->waitingForTrackInfo = False;
2978
2979	if (func != 0) {
2980	    CELL first = *start;
2981
2982	    boundsCheck(first.row);
2983	    boundsCheck(firstrow);
2984	    boundsCheck(lastrow);
2985	    screen->firstValidRow = firstrow;
2986	    screen->lastValidRow = lastrow;
2987	    screen->replyToEmacs = True;
2988	    StartSelect(xw, &first);
2989	}
2990    }
2991}
2992
2993static void
2994StartSelect(XtermWidget xw, const CELL *cell)
2995{
2996    TScreen *screen = TScreenOf(xw);
2997
2998    TRACE(("StartSelect row=%d, col=%d\n", cell->row, cell->col));
2999    if (screen->cursor_state)
3000	HideCursor(xw);
3001    if (screen->numberOfClicks == 1) {
3002	/* set start of selection */
3003	screen->rawPos = *cell;
3004    }
3005    /* else use old values in rawPos */
3006    screen->saveStartR = screen->startExt = screen->rawPos;
3007    screen->saveEndR = screen->endExt = screen->rawPos;
3008    if (Coordinate(screen, cell) < Coordinate(screen, &(screen->rawPos))) {
3009	screen->eventMode = LEFTEXTENSION;
3010	screen->startExt = *cell;
3011    } else {
3012	screen->eventMode = RIGHTEXTENSION;
3013	screen->endExt = *cell;
3014    }
3015    ComputeSelect(xw, &(screen->startExt), &(screen->endExt), False, True);
3016}
3017
3018static void
3019EndExtend(XtermWidget xw,
3020	  XEvent *event,	/* must be XButtonEvent */
3021	  String *params,	/* selections */
3022	  Cardinal num_params,
3023	  Bool use_cursor_loc)
3024{
3025    CELL cell;
3026    TScreen *screen = TScreenOf(xw);
3027
3028    TRACE_EVENT("EndExtend", event, params, &num_params);
3029    if (use_cursor_loc) {
3030	cell = screen->cursorp;
3031    } else {
3032	PointToCELL(screen, event->xbutton.y, event->xbutton.x, &cell);
3033    }
3034    ExtendExtend(xw, &cell);
3035
3036    screen->lastButtonUpTime = event->xbutton.time;
3037    screen->lastButton = event->xbutton.button;
3038
3039    if (!isSameCELL(&(screen->startSel), &(screen->endSel))) {
3040	if (screen->replyToEmacs) {
3041	    Char line[64];
3042	    unsigned count = 0;
3043
3044	    if (screen->control_eight_bits) {
3045		line[count++] = ANSI_CSI;
3046	    } else {
3047		line[count++] = ANSI_ESC;
3048		line[count++] = '[';
3049	    }
3050	    if (isSameCELL(&(screen->rawPos), &(screen->startSel))
3051		&& isSameCELL(&cell, &(screen->endSel))) {
3052		/* Use short-form emacs select */
3053
3054		switch (screen->extend_coords) {
3055		case 0:
3056		case SET_EXT_MODE_MOUSE:
3057		    line[count++] = 't';
3058		    break;
3059		case SET_SGR_EXT_MODE_MOUSE:
3060		case SET_PIXEL_POSITION_MOUSE:
3061		    line[count++] = '<';
3062		    break;
3063		}
3064
3065		count = EmitMousePosition(screen, line, count, screen->endSel.col);
3066		count = EmitMousePositionSeparator(screen, line, count);
3067		count = EmitMousePosition(screen, line, count, screen->endSel.row);
3068
3069		switch (screen->extend_coords) {
3070		case SET_SGR_EXT_MODE_MOUSE:
3071		case SET_URXVT_EXT_MODE_MOUSE:
3072		case SET_PIXEL_POSITION_MOUSE:
3073		    line[count++] = 't';
3074		    break;
3075		}
3076	    } else {
3077		/* long-form, specify everything */
3078
3079		switch (screen->extend_coords) {
3080		case 0:
3081		case SET_EXT_MODE_MOUSE:
3082		    line[count++] = 'T';
3083		    break;
3084		case SET_SGR_EXT_MODE_MOUSE:
3085		case SET_PIXEL_POSITION_MOUSE:
3086		    line[count++] = '<';
3087		    break;
3088		}
3089
3090		count = EmitMousePosition(screen, line, count, screen->startSel.col);
3091		count = EmitMousePositionSeparator(screen, line, count);
3092		count = EmitMousePosition(screen, line, count, screen->startSel.row);
3093		count = EmitMousePositionSeparator(screen, line, count);
3094		count = EmitMousePosition(screen, line, count, screen->endSel.col);
3095		count = EmitMousePositionSeparator(screen, line, count);
3096		count = EmitMousePosition(screen, line, count, screen->endSel.row);
3097		count = EmitMousePositionSeparator(screen, line, count);
3098		count = EmitMousePosition(screen, line, count, cell.col);
3099		count = EmitMousePositionSeparator(screen, line, count);
3100		count = EmitMousePosition(screen, line, count, cell.row);
3101
3102		switch (screen->extend_coords) {
3103		case SET_SGR_EXT_MODE_MOUSE:
3104		case SET_URXVT_EXT_MODE_MOUSE:
3105		case SET_PIXEL_POSITION_MOUSE:
3106		    line[count++] = 'T';
3107		    break;
3108		}
3109	    }
3110	    v_write(screen->respond, line, count);
3111	    UnHiliteText(xw);
3112	}
3113    }
3114    SelectSet(xw, event, params, num_params);
3115    screen->eventMode = NORMAL;
3116}
3117
3118void
3119HandleSelectSet(Widget w,
3120		XEvent *event,
3121		String *params,
3122		Cardinal *num_params)
3123{
3124    XtermWidget xw;
3125
3126    if ((xw = getXtermWidget(w)) != 0) {
3127	TRACE_EVENT("HandleSelectSet", event, params, num_params);
3128	SelectSet(xw, event, params, *num_params);
3129    }
3130}
3131
3132/* ARGSUSED */
3133static void
3134SelectSet(XtermWidget xw,
3135	  XEvent *event GCC_UNUSED,
3136	  String *params,
3137	  Cardinal num_params)
3138{
3139    TScreen *screen = TScreenOf(xw);
3140
3141    TRACE(("SelectSet\n"));
3142    /* Only do select stuff if non-null select */
3143    if (!isSameCELL(&(screen->startSel), &(screen->endSel))) {
3144	Cardinal n;
3145	for (n = 0; n < num_params; ++n) {
3146	    SaltTextAway(xw,
3147			 TargetToSelection(screen, params[n]),
3148			 &(screen->startSel), &(screen->endSel));
3149	}
3150	_OwnSelection(xw, params, num_params);
3151    } else {
3152	ScrnDisownSelection(xw);
3153    }
3154}
3155
3156#define Abs(x)		((x) < 0 ? -(x) : (x))
3157
3158/* ARGSUSED */
3159static void
3160do_start_extend(XtermWidget xw,
3161		XEvent *event,	/* must be XButtonEvent* */
3162		String *params GCC_UNUSED,
3163		Cardinal *num_params GCC_UNUSED,
3164		Bool use_cursor_loc)
3165{
3166    TScreen *screen = TScreenOf(xw);
3167    int coord;
3168    CELL cell;
3169
3170    if (SendMousePosition(xw, event))
3171	return;
3172
3173    screen->firstValidRow = 0;
3174    screen->lastValidRow = screen->max_row;
3175#if OPT_READLINE
3176    if (OverrideEvent(event)
3177	|| event->xbutton.button != Button3
3178	|| !(SCREEN_FLAG(screen, dclick3_deletes)))
3179#endif
3180	screen->selectUnit = EvalSelectUnit(xw,
3181					    event->xbutton.time,
3182					    screen->selectUnit,
3183					    event->xbutton.button);
3184    screen->replyToEmacs = False;
3185
3186#if OPT_READLINE
3187    CheckSecondPress3(xw, screen, event);
3188#endif
3189
3190    if (screen->numberOfClicks == 1
3191	|| (SCREEN_FLAG(screen, dclick3_deletes)
3192	    && !OverrideEvent(event))) {
3193	/* Save existing selection so we can reestablish it if the guy
3194	   extends past the other end of the selection */
3195	screen->saveStartR = screen->startExt = screen->startRaw;
3196	screen->saveEndR = screen->endExt = screen->endRaw;
3197    } else {
3198	/* He just needed the selection mode changed, use old values. */
3199	screen->startExt = screen->startRaw = screen->saveStartR;
3200	screen->endExt = screen->endRaw = screen->saveEndR;
3201    }
3202    if (use_cursor_loc) {
3203	cell = screen->cursorp;
3204    } else {
3205	PointToCELL(screen, event->xbutton.y, event->xbutton.x, &cell);
3206    }
3207    coord = Coordinate(screen, &cell);
3208
3209    if (Abs(coord - Coordinate(screen, &(screen->startSel)))
3210	< Abs(coord - Coordinate(screen, &(screen->endSel)))
3211	|| coord < Coordinate(screen, &(screen->startSel))) {
3212	/* point is close to left side of selection */
3213	screen->eventMode = LEFTEXTENSION;
3214	screen->startExt = cell;
3215    } else {
3216	/* point is close to left side of selection */
3217	screen->eventMode = RIGHTEXTENSION;
3218	screen->endExt = cell;
3219    }
3220    ComputeSelect(xw, &(screen->startExt), &(screen->endExt), True, True);
3221
3222#if OPT_READLINE
3223    if (!isSameCELL(&(screen->startSel), &(screen->endSel)))
3224	ExtendingSelection = 1;
3225#endif
3226}
3227
3228static void
3229ExtendExtend(XtermWidget xw, const CELL *cell)
3230{
3231    TScreen *screen = TScreenOf(xw);
3232    int coord = Coordinate(screen, cell);
3233
3234    TRACE(("ExtendExtend row=%d, col=%d\n", cell->row, cell->col));
3235    if (screen->eventMode == LEFTEXTENSION
3236	&& ((coord + (screen->selectUnit != Select_CHAR))
3237	    > Coordinate(screen, &(screen->endSel)))) {
3238	/* Whoops, he's changed his mind.  Do RIGHTEXTENSION */
3239	screen->eventMode = RIGHTEXTENSION;
3240	screen->startExt = screen->saveStartR;
3241    } else if (screen->eventMode == RIGHTEXTENSION
3242	       && coord < Coordinate(screen, &(screen->startSel))) {
3243	/* Whoops, he's changed his mind.  Do LEFTEXTENSION */
3244	screen->eventMode = LEFTEXTENSION;
3245	screen->endExt = screen->saveEndR;
3246    }
3247    if (screen->eventMode == LEFTEXTENSION) {
3248	screen->startExt = *cell;
3249    } else {
3250	screen->endExt = *cell;
3251    }
3252    ComputeSelect(xw, &(screen->startExt), &(screen->endExt), False, True);
3253
3254#if OPT_READLINE
3255    if (!isSameCELL(&(screen->startSel), &(screen->endSel)))
3256	ExtendingSelection = 1;
3257#endif
3258}
3259
3260void
3261HandleStartExtend(Widget w,
3262		  XEvent *event,	/* must be XButtonEvent* */
3263		  String *params,	/* unused */
3264		  Cardinal *num_params)		/* unused */
3265{
3266    XtermWidget xw;
3267
3268    if ((xw = getXtermWidget(w)) != 0) {
3269	TRACE_EVENT("HandleStartExtend", event, params, num_params);
3270	do_start_extend(xw, event, params, num_params, False);
3271    }
3272}
3273
3274void
3275HandleKeyboardStartExtend(Widget w,
3276			  XEvent *event,	/* must be XButtonEvent* */
3277			  String *params,	/* unused */
3278			  Cardinal *num_params)		/* unused */
3279{
3280    XtermWidget xw;
3281
3282    if ((xw = getXtermWidget(w)) != 0) {
3283	TRACE_EVENT("HandleKeyboardStartExtend", event, params, num_params);
3284	do_start_extend(xw, event, params, num_params, True);
3285    }
3286}
3287
3288void
3289ScrollSelection(TScreen *screen, int amount, Bool always)
3290{
3291    int minrow = INX2ROW(screen, -screen->savedlines);
3292    int maxrow = INX2ROW(screen, screen->max_row);
3293    int maxcol = screen->max_col;
3294
3295#define scroll_update_one(cell) \
3296	(cell)->row += amount; \
3297	if ((cell)->row < minrow) { \
3298	    (cell)->row = minrow; \
3299	    (cell)->col = 0; \
3300	} \
3301	if ((cell)->row > maxrow) { \
3302	    (cell)->row = maxrow; \
3303	    (cell)->col = maxcol; \
3304	}
3305
3306    scroll_update_one(&(screen->startRaw));
3307    scroll_update_one(&(screen->endRaw));
3308    scroll_update_one(&(screen->startSel));
3309    scroll_update_one(&(screen->endSel));
3310
3311    scroll_update_one(&(screen->rawPos));
3312
3313    /*
3314     * If we are told to scroll the selection but it lies outside the scrolling
3315     * margins, then that could cause the selection to move (bad).  It is not
3316     * simple to fix, because this function is called both for the scrollbar
3317     * actions as well as application scrolling.  The 'always' flag is set in
3318     * the former case.  The rest of the logic handles the latter.
3319     */
3320    if (ScrnHaveSelection(screen)) {
3321	int adjust;
3322
3323	adjust = ROW2INX(screen, screen->startH.row);
3324	if (always
3325	    || !ScrnHaveRowMargins(screen)
3326	    || ScrnIsRowInMargins(screen, adjust)) {
3327	    scroll_update_one(&screen->startH);
3328	}
3329	adjust = ROW2INX(screen, screen->endH.row);
3330	if (always
3331	    || !ScrnHaveRowMargins(screen)
3332	    || ScrnIsRowInMargins(screen, adjust)) {
3333	    scroll_update_one(&screen->endH);
3334	}
3335    }
3336
3337    screen->startHCoord = Coordinate(screen, &screen->startH);
3338    screen->endHCoord = Coordinate(screen, &screen->endH);
3339}
3340
3341/*ARGSUSED*/
3342void
3343ResizeSelection(TScreen *screen, int rows, int cols)
3344{
3345    rows--;			/* decr to get 0-max */
3346    cols--;
3347
3348    if (screen->startRaw.row > rows)
3349	screen->startRaw.row = rows;
3350    if (screen->startSel.row > rows)
3351	screen->startSel.row = rows;
3352    if (screen->endRaw.row > rows)
3353	screen->endRaw.row = rows;
3354    if (screen->endSel.row > rows)
3355	screen->endSel.row = rows;
3356    if (screen->rawPos.row > rows)
3357	screen->rawPos.row = rows;
3358
3359    if (screen->startRaw.col > cols)
3360	screen->startRaw.col = cols;
3361    if (screen->startSel.col > cols)
3362	screen->startSel.col = cols;
3363    if (screen->endRaw.col > cols)
3364	screen->endRaw.col = cols;
3365    if (screen->endSel.col > cols)
3366	screen->endSel.col = cols;
3367    if (screen->rawPos.col > cols)
3368	screen->rawPos.col = cols;
3369}
3370
3371#if OPT_WIDE_CHARS
3372#define isWideCell(row, col) isWideFrg((int)XTERM_CELL(row, col))
3373#endif
3374
3375static void
3376PointToCELL(TScreen *screen,
3377	    int y,
3378	    int x,
3379	    CELL *cell)
3380/* Convert pixel coordinates to character coordinates.
3381   Rows are clipped between firstValidRow and lastValidRow.
3382   Columns are clipped between to be 0 or greater, but are not clipped to some
3383       maximum value. */
3384{
3385    cell->row = (y - screen->border) / FontHeight(screen);
3386    if (cell->row < screen->firstValidRow)
3387	cell->row = screen->firstValidRow;
3388    else if (cell->row > screen->lastValidRow)
3389	cell->row = screen->lastValidRow;
3390    cell->col = (x - OriginX(screen)) / FontWidth(screen);
3391    if (cell->col < 0)
3392	cell->col = 0;
3393    else if (cell->col > MaxCols(screen)) {
3394	cell->col = MaxCols(screen);
3395    }
3396#if OPT_WIDE_CHARS
3397    /*
3398     * If we got a click on the right half of a doublewidth character,
3399     * pretend it happened on the left half.
3400     */
3401    if (cell->col > 0
3402	&& isWideCell(cell->row, cell->col - 1)
3403	&& (XTERM_CELL(cell->row, cell->col) == HIDDEN_CHAR)) {
3404	cell->col -= 1;
3405    }
3406#endif
3407}
3408
3409/*
3410 * Find the last column at which text was drawn on the given row.
3411 */
3412static int
3413LastTextCol(TScreen *screen, CLineData *ld, int row)
3414{
3415    int i = -1;
3416
3417    if (ld != 0) {
3418	if (okScrnRow(screen, row)) {
3419	    const IAttr *ch;
3420	    for (i = screen->max_col,
3421		 ch = ld->attribs + i;
3422		 i >= 0 && !(*ch & CHARDRAWN);
3423		 ch--, i--) {
3424		;
3425	    }
3426#if OPT_DEC_CHRSET
3427	    if (CSET_DOUBLE(GetLineDblCS(ld))) {
3428		i *= 2;
3429	    }
3430#endif
3431	}
3432    }
3433    return (i);
3434}
3435
3436#if !OPT_WIDE_CHARS
3437/*
3438** double click table for cut and paste in 8 bits
3439**
3440** This table is divided in four parts :
3441**
3442**	- control characters	[0,0x1f] U [0x80,0x9f]
3443**	- separators		[0x20,0x3f] U [0xa0,0xb9]
3444**	- binding characters	[0x40,0x7f] U [0xc0,0xff]
3445**	- exceptions
3446*/
3447/* *INDENT-OFF* */
3448static int charClass[256] =
3449{
3450/* NUL  SOH  STX  ETX  EOT  ENQ  ACK  BEL */
3451    32,  1,    1,   1,   1,   1,   1,   1,
3452/*  BS   HT   NL   VT   FF   CR   SO   SI */
3453     1,  32,   1,   1,   1,   1,   1,   1,
3454/* DLE  DC1  DC2  DC3  DC4  NAK  SYN  ETB */
3455     1,   1,   1,   1,   1,   1,   1,   1,
3456/* CAN   EM  SUB  ESC   FS   GS   RS   US */
3457     1,   1,   1,   1,   1,   1,   1,   1,
3458/*  SP    !    "    #    $    %    &    ' */
3459    32,  33,  34,  35,  36,  37,  38,  39,
3460/*   (    )    *    +    ,    -    .    / */
3461    40,  41,  42,  43,  44,  45,  46,  47,
3462/*   0    1    2    3    4    5    6    7 */
3463    48,  48,  48,  48,  48,  48,  48,  48,
3464/*   8    9    :    ;    <    =    >    ? */
3465    48,  48,  58,  59,  60,  61,  62,  63,
3466/*   @    A    B    C    D    E    F    G */
3467    64,  48,  48,  48,  48,  48,  48,  48,
3468/*   H    I    J    K    L    M    N    O */
3469    48,  48,  48,  48,  48,  48,  48,  48,
3470/*   P    Q    R    S    T    U    V    W */
3471    48,  48,  48,  48,  48,  48,  48,  48,
3472/*   X    Y    Z    [    \    ]    ^    _ */
3473    48,  48,  48,  91,  92,  93,  94,  48,
3474/*   `    a    b    c    d    e    f    g */
3475    96,  48,  48,  48,  48,  48,  48,  48,
3476/*   h    i    j    k    l    m    n    o */
3477    48,  48,  48,  48,  48,  48,  48,  48,
3478/*   p    q    r    s    t    u    v    w */
3479    48,  48,  48,  48,  48,  48,  48,  48,
3480/*   x    y    z    {    |    }    ~  DEL */
3481    48,  48,  48, 123, 124, 125, 126,   1,
3482/* x80  x81  x82  x83  IND  NEL  SSA  ESA */
3483    1,    1,   1,   1,   1,   1,   1,   1,
3484/* HTS  HTJ  VTS  PLD  PLU   RI  SS2  SS3 */
3485    1,    1,   1,   1,   1,   1,   1,   1,
3486/* DCS  PU1  PU2  STS  CCH   MW  SPA  EPA */
3487    1,    1,   1,   1,   1,   1,   1,   1,
3488/* x98  x99  x9A  CSI   ST  OSC   PM  APC */
3489    1,    1,   1,   1,   1,   1,   1,   1,
3490/*   -    i   c/    L   ox   Y-    |   So */
3491    160, 161, 162, 163, 164, 165, 166, 167,
3492/*  ..   c0   ip   <<    _        R0    - */
3493    168, 169, 170, 171, 172, 173, 174, 175,
3494/*   o   +-    2    3    '    u   q|    . */
3495    176, 177, 178, 179, 180, 181, 182, 183,
3496/*   ,    1    2   >>  1/4  1/2  3/4    ? */
3497    184, 185, 186, 187, 188, 189, 190, 191,
3498/*  A`   A'   A^   A~   A:   Ao   AE   C, */
3499     48,  48,  48,  48,  48,  48,  48,  48,
3500/*  E`   E'   E^   E:   I`   I'   I^   I: */
3501     48,  48,  48,  48,  48,  48,  48,  48,
3502/*  D-   N~   O`   O'   O^   O~   O:    X */
3503     48,  48,  48,  48,  48,  48,  48, 215,
3504/*  O/   U`   U'   U^   U:   Y'    P    B */
3505     48,  48,  48,  48,  48,  48,  48,  48,
3506/*  a`   a'   a^   a~   a:   ao   ae   c, */
3507     48,  48,  48,  48,  48,  48,  48,  48,
3508/*  e`   e'   e^   e:    i`  i'   i^   i: */
3509     48,  48,  48,  48,  48,  48,  48,  48,
3510/*   d   n~   o`   o'   o^   o~   o:   -: */
3511     48,  48,  48,  48,  48,  48,  48, 247,
3512/*  o/   u`   u'   u^   u:   y'    P   y: */
3513     48,  48,  48,  48,  48,  48,  48,  48};
3514/* *INDENT-ON* */
3515
3516int
3517SetCharacterClassRange(int low,	/* in range of [0..255] */
3518		       int high,
3519		       int value)	/* arbitrary */
3520{
3521
3522    if (low < 0 || high > 255 || high < low)
3523	return (-1);
3524
3525    for (; low <= high; low++)
3526	charClass[low] = value;
3527
3528    return (0);
3529}
3530#endif
3531
3532static int
3533class_of(LineData *ld, CELL *cell)
3534{
3535    CELL temp = *cell;
3536    int result = 0;
3537
3538#if OPT_DEC_CHRSET
3539    if (CSET_DOUBLE(GetLineDblCS(ld))) {
3540	temp.col /= 2;
3541    }
3542#endif
3543    if (temp.col < (int) ld->lineSize)
3544	result = CharacterClass((int) (ld->charData[temp.col]));
3545    return result;
3546}
3547
3548#if OPT_WIDE_CHARS
3549#define CClassSelects(name, cclass) \
3550	 (CClassOf(name) == cclass \
3551	 || XTERM_CELL(screen->name.row, screen->name.col) == HIDDEN_CHAR)
3552#else
3553#define CClassSelects(name, cclass) \
3554	 (class_of(ld.name, &((screen->name))) == cclass)
3555#endif
3556
3557#define CClassOf(name) class_of(ld.name, &((screen->name)))
3558
3559#if OPT_REPORT_CCLASS
3560static int
3561show_cclass_range(int lo, int hi)
3562{
3563    int cclass = CharacterClass(lo);
3564    int ident = (cclass == lo);
3565    int more = 0;
3566    if (ident) {
3567	int ch;
3568	for (ch = lo + 1; ch <= hi; ch++) {
3569	    if (CharacterClass(ch) != ch) {
3570		ident = 0;
3571		break;
3572	    }
3573	}
3574	if (ident && (hi < 255)) {
3575	    ch = hi + 1;
3576	    if (CharacterClass(ch) == ch) {
3577		if (ch >= 255 || CharacterClass(ch + 1) != ch) {
3578		    more = 1;
3579		}
3580	    }
3581	}
3582    }
3583    if (!more) {
3584	if (lo == hi) {
3585	    printf("\t%d", lo);
3586	} else {
3587	    printf("\t%d-%d", lo, hi);
3588	}
3589	if (!ident)
3590	    printf(":%d", cclass);
3591	if (hi < 255)
3592	    printf(", \\");
3593	printf("\n");
3594    }
3595    return !more;
3596}
3597
3598void
3599report_char_class(XtermWidget xw)
3600{
3601    /* simple table, to match documentation */
3602    static const char charnames[] =
3603    "NUL\0" "SOH\0" "STX\0" "ETX\0" "EOT\0" "ENQ\0" "ACK\0" "BEL\0"
3604    " BS\0" " HT\0" " NL\0" " VT\0" " NP\0" " CR\0" " SO\0" " SI\0"
3605    "DLE\0" "DC1\0" "DC2\0" "DC3\0" "DC4\0" "NAK\0" "SYN\0" "ETB\0"
3606    "CAN\0" " EM\0" "SUB\0" "ESC\0" " FS\0" " GS\0" " RS\0" " US\0"
3607    " SP\0" "  !\0" "  \"\0" "  #\0" "  $\0" "  %\0" "  &\0" "  '\0"
3608    "  (\0" "  )\0" "  *\0" "  +\0" "  ,\0" "  -\0" "  .\0" "  /\0"
3609    "  0\0" "  1\0" "  2\0" "  3\0" "  4\0" "  5\0" "  6\0" "  7\0"
3610    "  8\0" "  9\0" "  :\0" "  ;\0" "  <\0" "  =\0" "  >\0" "  ?\0"
3611    "  @\0" "  A\0" "  B\0" "  C\0" "  D\0" "  E\0" "  F\0" "  G\0"
3612    "  H\0" "  I\0" "  J\0" "  K\0" "  L\0" "  M\0" "  N\0" "  O\0"
3613    "  P\0" "  Q\0" "  R\0" "  S\0" "  T\0" "  U\0" "  V\0" "  W\0"
3614    "  X\0" "  Y\0" "  Z\0" "  [\0" "  \\\0" "  ]\0" "  ^\0" "  _\0"
3615    "  `\0" "  a\0" "  b\0" "  c\0" "  d\0" "  e\0" "  f\0" "  g\0"
3616    "  h\0" "  i\0" "  j\0" "  k\0" "  l\0" "  m\0" "  n\0" "  o\0"
3617    "  p\0" "  q\0" "  r\0" "  s\0" "  t\0" "  u\0" "  v\0" "  w\0"
3618    "  x\0" "  y\0" "  z\0" "  {\0" "  |\0" "  }\0" "  ~\0" "DEL\0"
3619    "x80\0" "x81\0" "x82\0" "x83\0" "IND\0" "NEL\0" "SSA\0" "ESA\0"
3620    "HTS\0" "HTJ\0" "VTS\0" "PLD\0" "PLU\0" " RI\0" "SS2\0" "SS3\0"
3621    "DCS\0" "PU1\0" "PU2\0" "STS\0" "CCH\0" " MW\0" "SPA\0" "EPA\0"
3622    "x98\0" "x99\0" "x9A\0" "CSI\0" " ST\0" "OSC\0" " PM\0" "APC\0"
3623    "  -\0" "  i\0" " c/\0" "  L\0" " ox\0" " Y-\0" "  |\0" " So\0"
3624    " ..\0" " c0\0" " ip\0" " <<\0" "  _\0" "   \0" " R0\0" "  -\0"
3625    "  o\0" " +-\0" "  2\0" "  3\0" "  '\0" "  u\0" " q|\0" "  .\0"
3626    "  ,\0" "  1\0" "  2\0" " >>\0" "1/4\0" "1/2\0" "3/4\0" "  ?\0"
3627    " A`\0" " A'\0" " A^\0" " A~\0" " A:\0" " Ao\0" " AE\0" " C,\0"
3628    " E`\0" " E'\0" " E^\0" " E:\0" " I`\0" " I'\0" " I^\0" " I:\0"
3629    " D-\0" " N~\0" " O`\0" " O'\0" " O^\0" " O~\0" " O:\0" "  X\0"
3630    " O/\0" " U`\0" " U'\0" " U^\0" " U:\0" " Y'\0" "  P\0" "  B\0"
3631    " a`\0" " a'\0" " a^\0" " a~\0" " a:\0" " ao\0" " ae\0" " c,\0"
3632    " e`\0" " e'\0" " e^\0" " e:\0" " i`\0" " i'\0" " i^\0" " i:\0"
3633    "  d\0" " n~\0" " o`\0" " o'\0" " o^\0" " o~\0" " o:\0" " -:\0"
3634    " o/\0" " u`\0" " u'\0" " u^\0" " u:\0" " y'\0" "  P\0" " y:\0";
3635    int ch, dh;
3636    int class_p;
3637
3638    (void) xw;
3639
3640    printf("static int charClass[256] = {\n");
3641    for (ch = 0; ch < 256; ++ch) {
3642	const char *s = charnames + (ch * 4);
3643	if ((ch & 7) == 0)
3644	    printf("/*");
3645	printf(" %s ", s);
3646	if (((ch + 1) & 7) == 0) {
3647	    printf("*/\n  ");
3648	    for (dh = ch - 7; dh <= ch; ++dh) {
3649		printf(" %3d%s", CharacterClass(dh), dh == 255 ? "};" : ",");
3650	    }
3651	    printf("\n");
3652	}
3653    }
3654
3655    /* print the table as if it were the charClass resource */
3656    printf("\n");
3657    printf("The table is equivalent to this \"charClass\" resource:\n");
3658    class_p = CharacterClass(dh = 0);
3659    for (ch = 0; ch < 256; ++ch) {
3660	int class_c = CharacterClass(ch);
3661	if (class_c != class_p) {
3662	    if (show_cclass_range(dh, ch - 1)) {
3663		dh = ch;
3664		class_p = class_c;
3665	    }
3666	}
3667    }
3668    if (dh < 255) {
3669	show_cclass_range(dh, 255);
3670    }
3671
3672    if_OPT_WIDE_CHARS(TScreenOf(xw), {
3673	/* if this is a wide-character configuration, print all intervals */
3674	report_wide_char_class();
3675    });
3676}
3677#endif
3678
3679/*
3680 * If the given column is past the end of text on the given row, bump to the
3681 * beginning of the next line.
3682 */
3683static Boolean
3684okPosition(TScreen *screen,
3685	   LineData **ld,
3686	   CELL *cell)
3687{
3688    Boolean result = True;
3689
3690    if (cell->row > screen->max_row) {
3691	result = False;
3692	TRACE(("okPosition cell row %d > screen max %d\n", cell->row, screen->max_row));
3693    } else if (cell->col > (LastTextCol(screen, *ld, cell->row) + 1)) {
3694	TRACE(("okPosition cell col %d > screen max %d\n", cell->col,
3695	       (LastTextCol(screen, *ld, cell->row) + 1)));
3696	if (cell->row < screen->max_row) {
3697	    TRACE(("okPosition cell row %d < screen max %d\n", cell->row, screen->max_row));
3698	    cell->col = 0;
3699	    *ld = GET_LINEDATA(screen, ++cell->row);
3700	    result = False;
3701	}
3702    }
3703    return result;
3704}
3705
3706static void
3707trimLastLine(TScreen *screen,
3708	     LineData **ld,
3709	     CELL *last)
3710{
3711    if (screen->cutNewline && last->row < screen->max_row) {
3712	last->col = 0;
3713	*ld = GET_LINEDATA(screen, ++last->row);
3714    } else {
3715	last->col = LastTextCol(screen, *ld, last->row) + 1;
3716    }
3717}
3718
3719#if OPT_SELECT_REGEX
3720/*
3721 * Returns the first row of a wrapped line.
3722 */
3723static int
3724firstRowOfLine(TScreen *screen, int row, Bool visible)
3725{
3726    LineData *ld = 0;
3727    int limit = visible ? 0 : -screen->savedlines;
3728
3729    while (row > limit &&
3730	   (ld = GET_LINEDATA(screen, row - 1)) != 0 &&
3731	   LineTstWrapped(ld)) {
3732	--row;
3733    }
3734    return row;
3735}
3736
3737/*
3738 * Returns the last row of a wrapped line.
3739 */
3740static int
3741lastRowOfLine(TScreen *screen, int row)
3742{
3743    LineData *ld;
3744
3745    while (row < screen->max_row &&
3746	   (ld = GET_LINEDATA(screen, row)) != 0 &&
3747	   LineTstWrapped(ld)) {
3748	++row;
3749    }
3750    return row;
3751}
3752
3753/*
3754 * Returns the number of cells on the range of rows.
3755 */
3756static unsigned
3757lengthOfLines(TScreen *screen, int firstRow, int lastRow)
3758{
3759    unsigned length = 0;
3760    int n;
3761
3762    for (n = firstRow; n <= lastRow; ++n) {
3763	LineData *ld = GET_LINEDATA(screen, n);
3764	int value = LastTextCol(screen, ld, n);
3765	if (value >= 0)
3766	    length += (unsigned) (value + 1);
3767    }
3768    return length;
3769}
3770
3771/*
3772 * Make a copy of the wrapped-line which corresponds to the given row as a
3773 * string of bytes.  Construct an index for the columns from the beginning of
3774 * the line.
3775 */
3776static char *
3777make_indexed_text(TScreen *screen, int row, unsigned length, int *indexed)
3778{
3779    Char *result = 0;
3780    size_t need = (length + 1);
3781
3782    /*
3783     * Get a quick upper bound to the number of bytes needed, if the whole
3784     * string were UTF-8.
3785     */
3786    if_OPT_WIDE_CHARS(screen, {
3787	need *= ((screen->lineExtra + 1) * 6);
3788    });
3789
3790    if ((result = TypeCallocN(Char, need + 1)) != 0) {
3791	LineData *ld = GET_LINEDATA(screen, row);
3792	unsigned used = 0;
3793	Char *last = result;
3794
3795	do {
3796	    int col = 0;
3797	    int limit = LastTextCol(screen, ld, row);
3798
3799	    while (col <= limit) {
3800		Char *next = last;
3801		unsigned data = ld->charData[col];
3802
3803		assert(col < (int) ld->lineSize);
3804		/* some internal points may not be drawn */
3805		if (data == 0)
3806		    data = ' ';
3807
3808		if_WIDE_OR_NARROW(screen, {
3809		    next = convertToUTF8(last, data);
3810		}
3811		, {
3812		    *next++ = CharOf(data);
3813		});
3814
3815		if_OPT_WIDE_CHARS(screen, {
3816		    size_t off;
3817		    for_each_combData(off, ld) {
3818			data = ld->combData[off][col];
3819			if (data == 0)
3820			    break;
3821			next = convertToUTF8(next, data);
3822		    }
3823		});
3824
3825		indexed[used] = (int) (last - result);
3826		*next = 0;
3827		/* TRACE(("index[%d.%d] %d:%s\n", row, used, indexed[used], last)); */
3828		last = next;
3829		++used;
3830		++col;
3831		indexed[used] = (int) (next - result);
3832	    }
3833	} while (used < length &&
3834		 LineTstWrapped(ld) &&
3835		 (ld = GET_LINEDATA(screen, ++row)) != 0 &&
3836		 row < screen->max_row);
3837    }
3838    /* TRACE(("result:%s\n", result)); */
3839    return (char *) result;
3840}
3841
3842/*
3843 * Find the column given an offset into the character string by using the
3844 * index constructed in make_indexed_text().
3845 */
3846static int
3847indexToCol(int *indexed, int len, int off)
3848{
3849    int col = 0;
3850    while (indexed[col] < len) {
3851	if (indexed[col] >= off)
3852	    break;
3853	++col;
3854    }
3855    return col;
3856}
3857
3858/*
3859 * Given a row number, and a column offset from that (which may be wrapped),
3860 * set the cell to the actual row/column values.
3861 */
3862static void
3863columnToCell(TScreen *screen, int row, int col, CELL *cell)
3864{
3865    while (row < screen->max_row) {
3866	CLineData *ld = GET_LINEDATA(screen, row);
3867	int last = LastTextCol(screen, ld, row);
3868
3869	/* TRACE(("last(%d) = %d, have %d\n", row, last, col)); */
3870	if (col <= last) {
3871	    break;
3872	}
3873	/*
3874	 * Stop if the current row does not wrap (does not continue the current
3875	 * line).
3876	 */
3877	if (!LineTstWrapped(ld)) {
3878	    col = last + 1;
3879	    break;
3880	}
3881	col -= (last + 1);
3882	++row;
3883    }
3884    if (col < 0)
3885	col = 0;
3886    cell->row = row;
3887    cell->col = col;
3888}
3889
3890/*
3891 * Given a cell, find the corresponding column offset.
3892 */
3893static int
3894cellToColumn(TScreen *screen, CELL *cell)
3895{
3896    CLineData *ld = 0;
3897    int col = cell->col;
3898    int row = firstRowOfLine(screen, cell->row, False);
3899    while (row < cell->row) {
3900	ld = GET_LINEDATA(screen, row);
3901	col += LastTextCol(screen, ld, row++);
3902    }
3903#if OPT_DEC_CHRSET
3904    if (ld == 0)
3905	ld = GET_LINEDATA(screen, row);
3906    if (CSET_DOUBLE(GetLineDblCS(ld)))
3907	col /= 2;
3908#endif
3909    return col;
3910}
3911
3912static void
3913do_select_regex(TScreen *screen, CELL *startc, CELL *endc)
3914{
3915    LineData *ld = GET_LINEDATA(screen, startc->row);
3916    int inx = ((screen->numberOfClicks - 1) % screen->maxClicks);
3917    char *expr = screen->selectExpr[inx];
3918    regex_t preg;
3919    regmatch_t match;
3920
3921    TRACE(("Select_REGEX[%d]:%s\n", inx, NonNull(expr)));
3922    if (okPosition(screen, &ld, startc) && expr != 0) {
3923	if (regcomp(&preg, expr, REG_EXTENDED) == 0) {
3924	    int firstRow = firstRowOfLine(screen, startc->row, True);
3925	    int lastRow = lastRowOfLine(screen, firstRow);
3926	    unsigned size = lengthOfLines(screen, firstRow, lastRow);
3927	    int actual = cellToColumn(screen, startc);
3928	    int *indexed;
3929
3930	    TRACE(("regcomp ok rows %d..%d bytes %d\n",
3931		   firstRow, lastRow, size));
3932
3933	    if ((indexed = TypeCallocN(int, size + 1)) != 0) {
3934		char *search;
3935		if ((search = make_indexed_text(screen,
3936						firstRow,
3937						size,
3938						indexed)) != 0) {
3939		    int len = (int) strlen(search);
3940		    int col;
3941		    int best_col = -1;
3942		    int best_len = -1;
3943
3944		    startc->row = 0;
3945		    startc->col = 0;
3946		    endc->row = 0;
3947		    endc->col = 0;
3948
3949		    for (col = 0; indexed[col] < len; ++col) {
3950			if (regexec(&preg,
3951				    search + indexed[col],
3952				    (size_t) 1, &match, 0) == 0) {
3953			    int start_inx = (int) (match.rm_so + indexed[col]);
3954			    int finis_inx = (int) (match.rm_eo + indexed[col]);
3955			    int start_col = indexToCol(indexed, len, start_inx);
3956			    int finis_col = indexToCol(indexed, len, finis_inx);
3957
3958			    if (start_col <= actual &&
3959				actual <= finis_col) {
3960				int test = finis_col - start_col;
3961				if (best_len < test) {
3962				    best_len = test;
3963				    best_col = start_col;
3964				    TRACE(("match column %d len %d\n",
3965					   best_col,
3966					   best_len));
3967				}
3968			    }
3969			}
3970		    }
3971		    if (best_col >= 0) {
3972			int best_nxt = best_col + best_len;
3973			columnToCell(screen, firstRow, best_col, startc);
3974			columnToCell(screen, firstRow, best_nxt, endc);
3975			TRACE(("search::%s\n", search));
3976			TRACE(("indexed:%d..%d -> %d..%d\n",
3977			       best_col, best_nxt,
3978			       indexed[best_col],
3979			       indexed[best_nxt]));
3980			TRACE(("matched:%d:%s\n",
3981			       indexed[best_nxt] + 1 -
3982			       indexed[best_col],
3983			       visibleChars((Char *) (search + indexed[best_col]),
3984					    (unsigned) (indexed[best_nxt] +
3985							1 -
3986							indexed[best_col]))));
3987		    }
3988		    free(search);
3989		}
3990		free(indexed);
3991#if OPT_DEC_CHRSET
3992		if ((ld = GET_LINEDATA(screen, startc->row)) != 0) {
3993		    if (CSET_DOUBLE(GetLineDblCS(ld)))
3994			startc->col *= 2;
3995		}
3996		if ((ld = GET_LINEDATA(screen, endc->row)) != 0) {
3997		    if (CSET_DOUBLE(GetLineDblCS(ld)))
3998			endc->col *= 2;
3999		}
4000#endif
4001	    }
4002	    regfree(&preg);
4003	}
4004    }
4005}
4006#endif /* OPT_SELECT_REGEX */
4007
4008#define InitRow(name) \
4009	ld.name = GET_LINEDATA(screen, screen->name.row)
4010
4011#define NextRow(name) \
4012	ld.name = GET_LINEDATA(screen, ++screen->name.row)
4013
4014#define PrevRow(name) \
4015	ld.name = GET_LINEDATA(screen, --screen->name.row)
4016
4017#define MoreRows(name) \
4018	(screen->name.row < screen->max_row)
4019
4020#define isPrevWrapped(name) \
4021	(screen->name.row > 0 \
4022	   && (ltmp = GET_LINEDATA(screen, screen->name.row - 1)) != 0 \
4023	   && LineTstWrapped(ltmp))
4024
4025/*
4026 * sets startSel endSel
4027 * ensuring that they have legal values
4028 */
4029static void
4030ComputeSelect(XtermWidget xw,
4031	      CELL *startc,
4032	      CELL *endc,
4033	      Bool extend,
4034	      Bool normal)
4035{
4036    TScreen *screen = TScreenOf(xw);
4037
4038    int cclass;
4039    CELL first = *startc;
4040    CELL last = *endc;
4041    Boolean ignored = False;
4042
4043    struct {
4044	LineData *startSel;
4045	LineData *endSel;
4046    } ld;
4047    LineData *ltmp;
4048
4049    TRACE(("ComputeSelect(startRow=%d, startCol=%d, endRow=%d, endCol=%d, %sextend)\n",
4050	   first.row, first.col,
4051	   last.row, last.col,
4052	   extend ? "" : "no"));
4053
4054#if OPT_WIDE_CHARS
4055    if (first.col > 1
4056	&& isWideCell(first.row, first.col - 1)
4057	&& XTERM_CELL(first.row, first.col - 0) == HIDDEN_CHAR) {
4058	TRACE(("Adjusting start. Changing downwards from %i.\n", first.col));
4059	first.col -= 1;
4060	if (last.col == (first.col + 1))
4061	    last.col--;
4062    }
4063
4064    if (last.col > 1
4065	&& isWideCell(last.row, last.col - 1)
4066	&& XTERM_CELL(last.row, last.col) == HIDDEN_CHAR) {
4067	last.col += 1;
4068    }
4069#endif
4070
4071    if (Coordinate(screen, &first) <= Coordinate(screen, &last)) {
4072	screen->startSel = screen->startRaw = first;
4073	screen->endSel = screen->endRaw = last;
4074    } else {			/* Swap them */
4075	screen->startSel = screen->startRaw = last;
4076	screen->endSel = screen->endRaw = first;
4077    }
4078
4079    InitRow(startSel);
4080    InitRow(endSel);
4081
4082    switch (screen->selectUnit) {
4083    case Select_CHAR:
4084	(void) okPosition(screen, &(ld.startSel), &(screen->startSel));
4085	(void) okPosition(screen, &(ld.endSel), &(screen->endSel));
4086	break;
4087
4088    case Select_WORD:
4089	TRACE(("Select_WORD\n"));
4090	if (okPosition(screen, &(ld.startSel), &(screen->startSel))) {
4091	    CELL mark;
4092	    cclass = CClassOf(startSel);
4093	    TRACE(("...starting with class %d\n", cclass));
4094	    do {
4095		mark = screen->startSel;
4096		--screen->startSel.col;
4097		if (screen->startSel.col < 0
4098		    && isPrevWrapped(startSel)) {
4099		    PrevRow(startSel);
4100		    screen->startSel.col = LastTextCol(screen, ld.startSel, screen->startSel.row);
4101		}
4102	    } while (screen->startSel.col >= 0
4103		     && CClassSelects(startSel, cclass));
4104	    if (normal)
4105		++screen->startSel.col;
4106	    else
4107		screen->startSel = mark;
4108	}
4109#if OPT_WIDE_CHARS
4110#define SkipHiddenCell(mark) \
4111	if (mark.col && XTERM_CELL(mark.row, mark.col) == HIDDEN_CHAR) \
4112	    mark.col++
4113#else
4114#define SkipHiddenCell(mark)	/* nothing */
4115#endif
4116	SkipHiddenCell(screen->startSel);
4117
4118	if (!normal) {
4119	    screen->endSel = screen->startSel;
4120	    ld.endSel = ld.startSel;
4121	}
4122
4123	if (okPosition(screen, &(ld.endSel), &(screen->endSel))) {
4124	    int length = LastTextCol(screen, ld.endSel, screen->endSel.row);
4125	    cclass = CClassOf(endSel);
4126	    TRACE(("...ending with class %d\n", cclass));
4127	    do {
4128		++screen->endSel.col;
4129		if (screen->endSel.col > length
4130		    && LineTstWrapped(ld.endSel)) {
4131		    if (!MoreRows(endSel))
4132			break;
4133		    screen->endSel.col = 0;
4134		    NextRow(endSel);
4135		    length = LastTextCol(screen, ld.endSel, screen->endSel.row);
4136		}
4137	    } while (screen->endSel.col <= length
4138		     && CClassSelects(endSel, cclass));
4139	    if (normal
4140		&& screen->endSel.col > length + 1
4141		&& MoreRows(endSel)) {
4142		screen->endSel.col = 0;
4143		NextRow(endSel);
4144	    }
4145	}
4146	SkipHiddenCell(screen->endSel);
4147
4148	screen->saveStartW = screen->startSel;
4149	break;
4150
4151    case Select_LINE:
4152	TRACE(("Select_LINE\n"));
4153	while (LineTstWrapped(ld.endSel)
4154	       && MoreRows(endSel)) {
4155	    NextRow(endSel);
4156	}
4157	if (screen->cutToBeginningOfLine
4158	    || screen->startSel.row < screen->saveStartW.row) {
4159	    screen->startSel.col = 0;
4160	    while (isPrevWrapped(startSel)) {
4161		PrevRow(startSel);
4162	    }
4163	} else if (!extend) {
4164	    if ((first.row < screen->saveStartW.row)
4165		|| (isSameRow(&first, &(screen->saveStartW))
4166		    && first.col < screen->saveStartW.col)) {
4167		screen->startSel.col = 0;
4168		while (isPrevWrapped(startSel)) {
4169		    PrevRow(startSel);
4170		}
4171	    } else {
4172		screen->startSel = screen->saveStartW;
4173	    }
4174	}
4175	trimLastLine(screen, &(ld.endSel), &(screen->endSel));
4176	break;
4177
4178    case Select_GROUP:		/* paragraph */
4179	TRACE(("Select_GROUP\n"));
4180	if (okPosition(screen, &(ld.startSel), &(screen->startSel))) {
4181	    /* scan backward for beginning of group */
4182	    while (screen->startSel.row > 0 &&
4183		   (LastTextCol(screen, ld.startSel, screen->startSel.row -
4184				1) > 0 ||
4185		    isPrevWrapped(startSel))) {
4186		PrevRow(startSel);
4187	    }
4188	    screen->startSel.col = 0;
4189	    /* scan forward for end of group */
4190	    while (MoreRows(endSel) &&
4191		   (LastTextCol(screen, ld.endSel, screen->endSel.row + 1) >
4192		    0 ||
4193		    LineTstWrapped(ld.endSel))) {
4194		NextRow(endSel);
4195	    }
4196	    trimLastLine(screen, &(ld.endSel), &(screen->endSel));
4197	}
4198	break;
4199
4200    case Select_PAGE:		/* everything one can see */
4201	TRACE(("Select_PAGE\n"));
4202	screen->startSel.row = 0;
4203	screen->startSel.col = 0;
4204	screen->endSel.row = MaxRows(screen);
4205	screen->endSel.col = 0;
4206	break;
4207
4208    case Select_ALL:		/* counts scrollback if in normal screen */
4209	TRACE(("Select_ALL\n"));
4210	screen->startSel.row = -screen->savedlines;
4211	screen->startSel.col = 0;
4212	screen->endSel.row = MaxRows(screen);
4213	screen->endSel.col = 0;
4214	break;
4215
4216#if OPT_SELECT_REGEX
4217    case Select_REGEX:
4218	do_select_regex(screen, &(screen->startSel), &(screen->endSel));
4219	break;
4220#endif
4221
4222    case NSELECTUNITS:		/* always ignore */
4223	ignored = True;
4224	break;
4225    }
4226
4227    if (!ignored) {
4228	/* check boundaries */
4229	ScrollSelection(screen, 0, False);
4230	TrackText(xw, &(screen->startSel), &(screen->endSel));
4231    }
4232
4233    return;
4234}
4235
4236/* Guaranteed (first.row, first.col) <= (last.row, last.col) */
4237static void
4238TrackText(XtermWidget xw,
4239	  const CELL *firstp,
4240	  const CELL *lastp)
4241{
4242    TScreen *screen = TScreenOf(xw);
4243    int from, to;
4244    CELL old_start, old_end;
4245    CELL first = *firstp;
4246    CELL last = *lastp;
4247
4248    TRACE(("TrackText(first=%d,%d, last=%d,%d)\n",
4249	   first.row, first.col, last.row, last.col));
4250
4251    old_start = screen->startH;
4252    old_end = screen->endH;
4253    TRACE(("...previous(first=%d,%d, last=%d,%d)\n",
4254	   old_start.row, old_start.col,
4255	   old_end.row, old_end.col));
4256    if (isSameCELL(&first, &old_start) &&
4257	isSameCELL(&last, &old_end)) {
4258	return;
4259    }
4260
4261    screen->startH = first;
4262    screen->endH = last;
4263    from = Coordinate(screen, &screen->startH);
4264    to = Coordinate(screen, &screen->endH);
4265    if (to <= screen->startHCoord || from > screen->endHCoord) {
4266	/* No overlap whatsoever between old and new hilite */
4267	ReHiliteText(xw, &old_start, &old_end);
4268	ReHiliteText(xw, &first, &last);
4269    } else {
4270	if (from < screen->startHCoord) {
4271	    /* Extend left end */
4272	    ReHiliteText(xw, &first, &old_start);
4273	} else if (from > screen->startHCoord) {
4274	    /* Shorten left end */
4275	    ReHiliteText(xw, &old_start, &first);
4276	}
4277	if (to > screen->endHCoord) {
4278	    /* Extend right end */
4279	    ReHiliteText(xw, &old_end, &last);
4280	} else if (to < screen->endHCoord) {
4281	    /* Shorten right end */
4282	    ReHiliteText(xw, &last, &old_end);
4283	}
4284    }
4285    screen->startHCoord = from;
4286    screen->endHCoord = to;
4287}
4288
4289static void
4290UnHiliteText(XtermWidget xw)
4291{
4292    TrackText(xw, &zeroCELL, &zeroCELL);
4293}
4294
4295/* Guaranteed that (first->row, first->col) <= (last->row, last->col) */
4296static void
4297ReHiliteText(XtermWidget xw,
4298	     CELL *firstp,
4299	     CELL *lastp)
4300{
4301    TScreen *screen = TScreenOf(xw);
4302    CELL first = *firstp;
4303    CELL last = *lastp;
4304
4305    TRACE(("ReHiliteText from %d.%d to %d.%d\n",
4306	   first.row, first.col, last.row, last.col));
4307
4308    if (first.row < 0)
4309	first.row = first.col = 0;
4310    else if (first.row > screen->max_row)
4311	return;			/* nothing to do, since last.row >= first.row */
4312
4313    if (last.row < 0)
4314	return;			/* nothing to do, since first.row <= last.row */
4315    else if (last.row > screen->max_row) {
4316	last.row = screen->max_row;
4317	last.col = MaxCols(screen);
4318    }
4319    if (isSameCELL(&first, &last))
4320	return;
4321
4322    if (!isSameRow(&first, &last)) {	/* do multiple rows */
4323	int i;
4324	if ((i = screen->max_col - first.col + 1) > 0) {	/* first row */
4325	    ScrnRefresh(xw, first.row, first.col, 1, i, True);
4326	}
4327	if ((i = last.row - first.row - 1) > 0) {	/* middle rows */
4328	    ScrnRefresh(xw, first.row + 1, 0, i, MaxCols(screen), True);
4329	}
4330	if (last.col > 0 && last.row <= screen->max_row) {	/* last row */
4331	    ScrnRefresh(xw, last.row, 0, 1, last.col, True);
4332	}
4333    } else {			/* do single row */
4334	ScrnRefresh(xw, first.row, first.col, 1, last.col - first.col, True);
4335    }
4336}
4337
4338/*
4339 * Guaranteed that (cellc->row, cellc->col) <= (cell->row, cell->col),
4340 * and that both points are valid
4341 * (may have cell->row = screen->max_row+1, cell->col = 0).
4342 */
4343static void
4344SaltTextAway(XtermWidget xw,
4345	     int which,
4346	     CELL *cellc,
4347	     CELL *cell)
4348{
4349    TScreen *screen = TScreenOf(xw);
4350    SelectedCells *scp;
4351    int i;
4352    int eol;
4353    int need = 0;
4354    size_t have = 0;
4355    Char *line;
4356    Char *lp;
4357    CELL first = *cellc;
4358    CELL last = *cell;
4359
4360    if (which < 0 || which >= MAX_SELECTIONS) {
4361	TRACE(("SaltTextAway - which selection?\n"));
4362	return;
4363    }
4364    scp = &(screen->selected_cells[which]);
4365
4366    TRACE(("SaltTextAway which=%d, first=%d,%d, last=%d,%d\n",
4367	   which, first.row, first.col, last.row, last.col));
4368
4369    if (isSameRow(&first, &last) && first.col > last.col) {
4370	int tmp;
4371	EXCHANGE(first.col, last.col, tmp);
4372    }
4373
4374    --last.col;
4375    /* first we need to know how long the string is before we can save it */
4376
4377    if (isSameRow(&last, &first)) {
4378	need = Length(screen, first.row, first.col, last.col);
4379    } else {			/* two cases, cut is on same line, cut spans multiple lines */
4380	need += Length(screen, first.row, first.col, screen->max_col) + 1;
4381	for (i = first.row + 1; i < last.row; i++)
4382	    need += Length(screen, i, 0, screen->max_col) + 1;
4383	if (last.col >= 0)
4384	    need += Length(screen, last.row, 0, last.col);
4385    }
4386
4387    /* UTF-8 may require more space */
4388    if_OPT_WIDE_CHARS(screen, {
4389	if (need > 0) {
4390	    if (screen->max_combining > 0)
4391		need += screen->max_combining;
4392	    need *= 6;
4393	}
4394    });
4395
4396    /* now get some memory to save it in */
4397    if (need < 0)
4398	return;
4399
4400    if (scp->data_limit <= (unsigned) need) {
4401	if ((line = (Char *) malloc((size_t) need + 1)) == 0)
4402	    SysError(ERROR_BMALLOC2);
4403	free(scp->data_buffer);
4404	scp->data_buffer = line;
4405	scp->data_limit = (size_t) (need + 1);
4406    } else {
4407	line = scp->data_buffer;
4408    }
4409
4410    if (line == 0)
4411	return;
4412
4413    line[need] = '\0';		/* make sure it is null terminated */
4414    lp = line;			/* lp points to where to save the text */
4415    if (isSameRow(&last, &first)) {
4416	lp = SaveText(screen, last.row, first.col, last.col, lp, &eol);
4417    } else {
4418	lp = SaveText(screen, first.row, first.col, screen->max_col, lp, &eol);
4419	if (eol)
4420	    *lp++ = '\n';	/* put in newline at end of line */
4421	for (i = first.row + 1; i < last.row; i++) {
4422	    lp = SaveText(screen, i, 0, screen->max_col, lp, &eol);
4423	    if (eol)
4424		*lp++ = '\n';
4425	}
4426	if (last.col >= 0)
4427	    lp = SaveText(screen, last.row, 0, last.col, lp, &eol);
4428    }
4429    *lp = '\0';			/* make sure we have end marked */
4430
4431    have = (size_t) (lp - line);
4432    /*
4433     * Scanning the buffer twice is unnecessary.  Discard unwanted memory if
4434     * the estimate is too-far off.
4435     */
4436    if ((have * 2) < (size_t) need) {
4437	Char *next;
4438	scp->data_limit = have + 1;
4439	next = realloc(line, scp->data_limit);
4440	if (next == NULL) {
4441	    free(line);
4442	    scp->data_length = 0;
4443	    scp->data_limit = 0;
4444	}
4445	scp->data_buffer = next;
4446    }
4447    scp->data_length = have;
4448
4449    TRACE(("Salted TEXT:%u:%s\n", (unsigned) have,
4450	   visibleChars(scp->data_buffer, (unsigned) have)));
4451}
4452
4453#if OPT_PASTE64
4454void
4455ClearSelectionBuffer(TScreen *screen, String selection)
4456{
4457    int which = TargetToSelection(screen, selection);
4458    SelectedCells *scp = &(screen->selected_cells[okSelectionCode(which)]);
4459    FreeAndNull(scp->data_buffer);
4460    scp->data_limit = 0;
4461    scp->data_length = 0;
4462    screen->base64_count = 0;
4463}
4464
4465static void
4466AppendStrToSelectionBuffer(SelectedCells * scp, Char *text, size_t len)
4467{
4468    if (len != 0) {
4469	size_t j = (scp->data_length + len);
4470	size_t k = j + (j >> 2) + 80;
4471	if (j + 1 >= scp->data_limit) {
4472	    Char *line;
4473	    if (!scp->data_length) {
4474		line = (Char *) malloc(k);
4475	    } else {
4476		line = (Char *) realloc(scp->data_buffer, k);
4477	    }
4478	    if (line == 0)
4479		SysError(ERROR_BMALLOC2);
4480	    scp->data_buffer = line;
4481	    scp->data_limit = k;
4482	}
4483	if (scp->data_buffer != 0) {
4484	    memcpy(scp->data_buffer + scp->data_length, text, len);
4485	    scp->data_length += len;
4486	    scp->data_buffer[scp->data_length] = 0;
4487	}
4488    }
4489}
4490
4491void
4492AppendToSelectionBuffer(TScreen *screen, unsigned c, String selection)
4493{
4494    int which = TargetToSelection(screen, selection);
4495    SelectedCells *scp = &(screen->selected_cells[okSelectionCode(which)]);
4496    unsigned six;
4497    Char ch;
4498
4499    /* Decode base64 character */
4500    if (c >= 'A' && c <= 'Z')
4501	six = c - 'A';
4502    else if (c >= 'a' && c <= 'z')
4503	six = c - 'a' + 26;
4504    else if (c >= '0' && c <= '9')
4505	six = c - '0' + 52;
4506    else if (c == '+')
4507	six = 62;
4508    else if (c == '/')
4509	six = 63;
4510    else
4511	return;
4512
4513    /* Accumulate bytes */
4514    switch (screen->base64_count) {
4515    case 0:
4516	screen->base64_accu = six;
4517	screen->base64_count = 6;
4518	break;
4519
4520    case 2:
4521	ch = CharOf((screen->base64_accu << 6) + six);
4522	screen->base64_count = 0;
4523	AppendStrToSelectionBuffer(scp, &ch, (size_t) 1);
4524	break;
4525
4526    case 4:
4527	ch = CharOf((screen->base64_accu << 4) + (six >> 2));
4528	screen->base64_accu = (six & 0x3);
4529	screen->base64_count = 2;
4530	AppendStrToSelectionBuffer(scp, &ch, (size_t) 1);
4531	break;
4532
4533    case 6:
4534	ch = CharOf((screen->base64_accu << 2) + (six >> 4));
4535	screen->base64_accu = (six & 0xF);
4536	screen->base64_count = 4;
4537	AppendStrToSelectionBuffer(scp, &ch, (size_t) 1);
4538	break;
4539    }
4540}
4541
4542void
4543CompleteSelection(XtermWidget xw, String *args, Cardinal len)
4544{
4545    TScreen *screen = TScreenOf(xw);
4546
4547    screen->base64_count = 0;
4548    screen->base64_accu = 0;
4549    _OwnSelection(xw, args, len);
4550}
4551#endif /* OPT_PASTE64 */
4552
4553static Bool
4554_ConvertSelectionHelper(Widget w,
4555			SelectedCells * scp,
4556			Atom *type,
4557			XtPointer *value,
4558			unsigned long *length,
4559			int *format,
4560			int (*conversion_function) (Display *,
4561						    char **, int,
4562						    XICCEncodingStyle,
4563						    XTextProperty *),
4564			XICCEncodingStyle conversion_style)
4565{
4566    *value = 0;
4567    *length = 0;
4568    *type = 0;
4569    *format = 0;
4570
4571    if (getXtermWidget(w) != 0) {
4572	Display *dpy = XtDisplay(w);
4573	XTextProperty textprop;
4574	int out_n = 0;
4575	char *result = 0;
4576	char *the_data = (char *) scp->data_buffer;
4577	char *the_next;
4578	unsigned long remaining = scp->data_length;
4579
4580	TRACE(("converting %ld:'%s'\n",
4581	       (long) scp->data_length,
4582	       visibleChars(scp->data_buffer, (unsigned) scp->data_length)));
4583	/*
4584	 * For most selections, we can convert in one pass.  It is possible
4585	 * that some applications contain embedded nulls, e.g., using xterm's
4586	 * paste64 feature.  For those cases, we will build up the result in
4587	 * parts.
4588	 */
4589	if (memchr(the_data, 0, scp->data_length) != 0) {
4590	    TRACE(("selection contains embedded nulls\n"));
4591	    result = calloc(scp->data_length + 1, sizeof(char));
4592	}
4593
4594      next_try:
4595	memset(&textprop, 0, sizeof(textprop));
4596	if (conversion_function(dpy, &the_data, 1,
4597				conversion_style,
4598				&textprop) >= Success) {
4599	    if ((result != 0)
4600		&& (textprop.value != 0)
4601		&& (textprop.format == 8)) {
4602		char *text_values = (char *) textprop.value;
4603		unsigned long in_n;
4604
4605		if (out_n == 0) {
4606		    *value = result;
4607		    *type = textprop.encoding;
4608		    *format = textprop.format;
4609		}
4610		for (in_n = 0; in_n < textprop.nitems; ++in_n) {
4611		    result[out_n++] = text_values[in_n];
4612		}
4613		*length += textprop.nitems;
4614		if ((the_next = memchr(the_data, 0, remaining)) != 0) {
4615		    unsigned long this_was = (unsigned long) (the_next - the_data);
4616		    this_was++;
4617		    the_data += this_was;
4618		    remaining -= this_was;
4619		    result[out_n++] = 0;
4620		    *length += 1;
4621		    if (remaining)
4622			goto next_try;
4623		}
4624		return True;
4625	    } else {
4626		free(result);
4627		*value = (XtPointer) textprop.value;
4628		*length = textprop.nitems;
4629		*type = textprop.encoding;
4630		*format = textprop.format;
4631		return True;
4632	    }
4633	}
4634	free(result);
4635    }
4636    return False;
4637}
4638
4639static Boolean
4640SaveConvertedLength(XtPointer *target, unsigned long source)
4641{
4642    Boolean result = False;
4643
4644    *target = XtMalloc(4);
4645    if (*target != 0) {
4646	result = True;
4647	if (sizeof(unsigned long) == 4) {
4648	    *(unsigned long *) *target = source;
4649	} else if (sizeof(unsigned) == 4) {
4650	    *(unsigned *) *target = (unsigned) source;
4651	} else if (sizeof(unsigned short) == 4) {
4652	    *(unsigned short *) *target = (unsigned short) source;
4653	} else {
4654	    /* FIXME - does this depend on byte-order? */
4655	    unsigned long temp = source;
4656	    memcpy((char *) *target,
4657		   ((char *) &temp) + sizeof(temp) - 4,
4658		   (size_t) 4);
4659	}
4660    }
4661    return result;
4662}
4663
4664#define keepClipboard(d,atom) ((screen->keepClipboard) && \
4665	 (atom == XA_CLIPBOARD(d)))
4666
4667static Boolean
4668ConvertSelection(Widget w,
4669		 Atom *selection,
4670		 Atom *target,
4671		 Atom *type,
4672		 XtPointer *value,
4673		 unsigned long *length,
4674		 int *format)
4675{
4676    Display *dpy = XtDisplay(w);
4677    TScreen *screen;
4678    SelectedCells *scp;
4679    Bool result = False;
4680
4681    Char *data;
4682    unsigned long data_length;
4683
4684    XtermWidget xw;
4685
4686    if ((xw = getXtermWidget(w)) == 0)
4687	return False;
4688
4689    screen = TScreenOf(xw);
4690
4691    TRACE(("ConvertSelection %s -> %s\n",
4692	   TraceAtomName(screen->display, *selection),
4693	   visibleSelectionTarget(dpy, *target)));
4694
4695    if (keepClipboard(dpy, *selection)) {
4696	TRACE(("asked for clipboard\n"));
4697	scp = &(screen->clipboard_data);
4698    } else {
4699	TRACE(("asked for selection\n"));
4700	scp = &(screen->selected_cells[AtomToSelection(dpy, *selection)]);
4701    }
4702
4703    data = scp->data_buffer;
4704    data_length = scp->data_length;
4705    if (data == NULL) {
4706	TRACE(("...no selection-data\n"));
4707	return False;
4708    }
4709
4710    if (*target == XA_TARGETS(dpy)) {
4711	Atom *targetP;
4712	XPointer std_return = 0;
4713	unsigned long std_length;
4714
4715	if (XmuConvertStandardSelection(w, screen->selection_time, selection,
4716					target, type, &std_return,
4717					&std_length, format)) {
4718	    Atom *my_targets = _SelectionTargets(w);
4719	    Atom *allocP;
4720	    Atom *std_targets;
4721
4722	    TRACE(("XmuConvertStandardSelection - success\n"));
4723	    std_targets = (Atom *) (void *) (std_return);
4724	    *length = std_length + 6;
4725
4726	    targetP = TypeXtMallocN(Atom, *length);
4727	    allocP = targetP;
4728
4729	    *value = (XtPointer) targetP;
4730
4731	    if (my_targets != 0) {
4732		while (*my_targets != None) {
4733		    *targetP++ = *my_targets++;
4734		}
4735	    }
4736	    *targetP++ = XA_LENGTH(dpy);
4737	    *targetP++ = XA_LIST_LENGTH(dpy);
4738
4739	    *length = std_length + (unsigned long) (targetP - allocP);
4740
4741	    memcpy(targetP, std_targets, sizeof(Atom) * std_length);
4742	    XtFree((char *) std_targets);
4743	    *type = XA_ATOM;
4744	    *format = 32;
4745	    result = True;
4746	} else {
4747	    TRACE(("XmuConvertStandardSelection - failed\n"));
4748	}
4749    }
4750#if OPT_WIDE_CHARS
4751    else if (screen->wide_chars && *target == XA_STRING) {
4752	result =
4753	    _ConvertSelectionHelper(w, scp,
4754				    type, value, length, format,
4755				    Xutf8TextListToTextProperty,
4756				    XStringStyle);
4757	TRACE(("...Xutf8TextListToTextProperty:%d\n", result));
4758    } else if (screen->wide_chars && *target == XA_UTF8_STRING(dpy)) {
4759	result =
4760	    _ConvertSelectionHelper(w, scp,
4761				    type, value, length, format,
4762				    Xutf8TextListToTextProperty,
4763				    XUTF8StringStyle);
4764	TRACE(("...Xutf8TextListToTextProperty:%d\n", result));
4765    } else if (screen->wide_chars && *target == XA_TEXT(dpy)) {
4766	result =
4767	    _ConvertSelectionHelper(w, scp,
4768				    type, value, length, format,
4769				    Xutf8TextListToTextProperty,
4770				    XStdICCTextStyle);
4771	TRACE(("...Xutf8TextListToTextProperty:%d\n", result));
4772    } else if (screen->wide_chars && *target == XA_COMPOUND_TEXT(dpy)) {
4773	result =
4774	    _ConvertSelectionHelper(w, scp,
4775				    type, value, length, format,
4776				    Xutf8TextListToTextProperty,
4777				    XCompoundTextStyle);
4778	TRACE(("...Xutf8TextListToTextProperty:%d\n", result));
4779    }
4780#endif
4781
4782    else if (*target == XA_STRING) {	/* not wide_chars */
4783	/* We can only reach this point if the selection requestor
4784	   requested STRING before any of TEXT, COMPOUND_TEXT or
4785	   UTF8_STRING.  We therefore assume that the requestor is not
4786	   properly internationalised, and dump raw eight-bit data
4787	   with no conversion into the selection.  Yes, this breaks
4788	   the ICCCM in non-Latin-1 locales. */
4789	*type = XA_STRING;
4790	*value = (XtPointer) data;
4791	*length = data_length;
4792	*format = 8;
4793	result = True;
4794	TRACE(("...raw 8-bit data:%d\n", result));
4795    } else if (*target == XA_TEXT(dpy)) {	/* not wide_chars */
4796	result =
4797	    _ConvertSelectionHelper(w, scp,
4798				    type, value, length, format,
4799				    XmbTextListToTextProperty,
4800				    XStdICCTextStyle);
4801	TRACE(("...XmbTextListToTextProperty(StdICC):%d\n", result));
4802    } else if (*target == XA_COMPOUND_TEXT(dpy)) {	/* not wide_chars */
4803	result =
4804	    _ConvertSelectionHelper(w, scp,
4805				    type, value, length, format,
4806				    XmbTextListToTextProperty,
4807				    XCompoundTextStyle);
4808	TRACE(("...XmbTextListToTextProperty(Compound):%d\n", result));
4809    }
4810#ifdef X_HAVE_UTF8_STRING
4811    else if (*target == XA_UTF8_STRING(dpy)) {	/* not wide_chars */
4812	result =
4813	    _ConvertSelectionHelper(w, scp,
4814				    type, value, length, format,
4815				    XmbTextListToTextProperty,
4816				    XUTF8StringStyle);
4817	TRACE(("...XmbTextListToTextProperty(UTF8):%d\n", result));
4818    }
4819#endif
4820    else if (*target == XA_LIST_LENGTH(dpy)) {
4821	result = SaveConvertedLength(value, (unsigned long) 1);
4822	*type = XA_INTEGER;
4823	*length = 1;
4824	*format = 32;
4825	TRACE(("...list of values:%d\n", result));
4826    } else if (*target == XA_LENGTH(dpy)) {
4827	/* This value is wrong if we have UTF-8 text */
4828	result = SaveConvertedLength(value, scp->data_length);
4829	*type = XA_INTEGER;
4830	*length = 1;
4831	*format = 32;
4832	TRACE(("...list of values:%d\n", result));
4833    } else if (XmuConvertStandardSelection(w,
4834					   screen->selection_time, selection,
4835					   target, type, (XPointer *) value,
4836					   length, format)) {
4837	result = True;
4838	TRACE(("...XmuConvertStandardSelection:%d\n", result));
4839    }
4840
4841    /* else */
4842    return (Boolean) result;
4843}
4844
4845static void
4846LoseSelection(Widget w, Atom *selection)
4847{
4848    TScreen *screen;
4849    Atom *atomP;
4850    Cardinal i;
4851
4852    XtermWidget xw;
4853
4854    if ((xw = getXtermWidget(w)) == 0)
4855	return;
4856
4857    screen = TScreenOf(xw);
4858    TRACE(("LoseSelection %s\n", TraceAtomName(screen->display, *selection)));
4859
4860    for (i = 0, atomP = screen->selection_atoms;
4861	 i < screen->selection_count; i++, atomP++) {
4862	if (*selection == *atomP)
4863	    *atomP = (Atom) 0;
4864	if (CutBuffer(*atomP) >= 0) {
4865	    *atomP = (Atom) 0;
4866	}
4867    }
4868
4869    for (i = screen->selection_count; i; i--) {
4870	if (screen->selection_atoms[i - 1] != 0)
4871	    break;
4872    }
4873    screen->selection_count = i;
4874
4875    for (i = 0, atomP = screen->selection_atoms;
4876	 i < screen->selection_count; i++, atomP++) {
4877	if (*atomP == (Atom) 0) {
4878	    *atomP = screen->selection_atoms[--screen->selection_count];
4879	}
4880    }
4881
4882    if (screen->selection_count == 0)
4883	UnHiliteText(xw);
4884}
4885
4886/* ARGSUSED */
4887static void
4888SelectionDone(Widget w GCC_UNUSED,
4889	      Atom *selection GCC_UNUSED,
4890	      Atom *target GCC_UNUSED)
4891{
4892    /* empty proc so Intrinsics know we want to keep storage */
4893    TRACE(("SelectionDone\n"));
4894}
4895
4896static void
4897_OwnSelection(XtermWidget xw,
4898	      String *selections,
4899	      Cardinal count)
4900{
4901    TScreen *screen = TScreenOf(xw);
4902    Display *dpy = screen->display;
4903    Atom *atoms = screen->selection_atoms;
4904    Cardinal i;
4905    Bool have_selection = False;
4906    SelectedCells *scp;
4907
4908    if (count == 0)
4909	return;
4910
4911    TRACE(("_OwnSelection count %d\n", count));
4912    selections = MapSelections(xw, selections, count);
4913
4914    if (count > screen->sel_atoms_size) {
4915	XtFree((char *) atoms);
4916	atoms = TypeXtMallocN(Atom, count);
4917	screen->selection_atoms = atoms;
4918	screen->sel_atoms_size = count;
4919    }
4920    XmuInternStrings(dpy, selections, count, atoms);
4921    for (i = 0; i < count; i++) {
4922	int cutbuffer = CutBuffer(atoms[i]);
4923	if (cutbuffer >= 0) {
4924	    unsigned long limit =
4925	    (unsigned long) (4 * XMaxRequestSize(dpy) - 32);
4926	    scp = &(screen->selected_cells[CutBufferToCode(cutbuffer)]);
4927	    if (scp->data_length > limit) {
4928		TRACE(("selection too big (%lu bytes), not storing in CUT_BUFFER%d\n",
4929		       (unsigned long) scp->data_length, cutbuffer));
4930		xtermWarning("selection too big (%lu bytes), not storing in CUT_BUFFER%d\n",
4931			     (unsigned long) scp->data_length, cutbuffer);
4932	    } else {
4933		/* This used to just use the UTF-8 data, which was totally
4934		 * broken as not even the corresponding paste code in xterm
4935		 * understood this!  So now it converts to Latin1 first.
4936		 *   Robert Brady, 2000-09-05
4937		 */
4938		unsigned long length = scp->data_length;
4939		Char *data = scp->data_buffer;
4940		if_OPT_WIDE_CHARS((screen), {
4941		    data = UTF8toLatin1(screen, data, length, &length);
4942		});
4943		TRACE(("XStoreBuffer(%d)\n", cutbuffer));
4944		XStoreBuffer(dpy,
4945			     (char *) data,
4946			     (int) length,
4947			     cutbuffer);
4948	    }
4949	} else {
4950	    int which = AtomToSelection(dpy, atoms[i]);
4951	    if (keepClipboard(dpy, atoms[i])) {
4952		Char *buf;
4953		SelectedCells *tcp = &(screen->clipboard_data);
4954		TRACE(("saving selection to clipboard buffer\n"));
4955		scp = &(screen->selected_cells[CLIPBOARD_CODE]);
4956		if ((buf = (Char *) malloc((size_t) scp->data_length)) == 0)
4957		    SysError(ERROR_BMALLOC2);
4958
4959		free(tcp->data_buffer);
4960		memcpy(buf, scp->data_buffer, scp->data_length);
4961		tcp->data_buffer = buf;
4962		tcp->data_limit = scp->data_length;
4963		tcp->data_length = scp->data_length;
4964	    }
4965	    scp = &(screen->selected_cells[which]);
4966	    if (scp->data_length == 0) {
4967		TRACE(("XtDisownSelection(%s, @%ld)\n",
4968		       TraceAtomName(screen->display, atoms[i]),
4969		       (long) screen->selection_time));
4970		XtDisownSelection((Widget) xw,
4971				  atoms[i],
4972				  screen->selection_time);
4973	    } else if (!screen->replyToEmacs && atoms[i] != 0) {
4974		TRACE(("XtOwnSelection(%s, @%ld)\n",
4975		       TraceAtomName(screen->display, atoms[i]),
4976		       (long) screen->selection_time));
4977		have_selection |=
4978		    XtOwnSelection((Widget) xw, atoms[i],
4979				   screen->selection_time,
4980				   ConvertSelection,
4981				   LoseSelection,
4982				   SelectionDone);
4983	    }
4984	}
4985	TRACE(("... _OwnSelection used length %lu value %s\n",
4986	       (unsigned long) scp->data_length,
4987	       visibleChars(scp->data_buffer,
4988			    (unsigned) scp->data_length)));
4989    }
4990    if (!screen->replyToEmacs)
4991	screen->selection_count = count;
4992    if (!have_selection)
4993	UnHiliteText(xw);
4994}
4995
4996static void
4997ResetSelectionState(TScreen *screen)
4998{
4999    screen->selection_count = 0;
5000    screen->startH = zeroCELL;
5001    screen->endH = zeroCELL;
5002}
5003
5004void
5005DisownSelection(XtermWidget xw)
5006{
5007    TScreen *screen = TScreenOf(xw);
5008    Atom *atoms = screen->selection_atoms;
5009    Cardinal count = screen->selection_count;
5010    Cardinal i;
5011
5012    TRACE(("DisownSelection count %d, start %d.%d, end %d.%d\n",
5013	   count,
5014	   screen->startH.row,
5015	   screen->startH.col,
5016	   screen->endH.row,
5017	   screen->endH.col));
5018
5019    for (i = 0; i < count; i++) {
5020	int cutbuffer = CutBuffer(atoms[i]);
5021	if (cutbuffer < 0) {
5022	    XtDisownSelection((Widget) xw, atoms[i],
5023			      screen->selection_time);
5024	}
5025    }
5026    /*
5027     * If none of the callbacks via XtDisownSelection() reset highlighting
5028     * do it now.
5029     */
5030    if (ScrnHaveSelection(screen)) {
5031	/* save data which will be reset */
5032	CELL first = screen->startH;
5033	CELL last = screen->endH;
5034
5035	ResetSelectionState(screen);
5036	ReHiliteText(xw, &first, &last);
5037    } else {
5038	ResetSelectionState(screen);
5039    }
5040}
5041
5042void
5043UnhiliteSelection(XtermWidget xw)
5044{
5045    TScreen *screen = TScreenOf(xw);
5046
5047    if (ScrnHaveSelection(screen)) {
5048	CELL first = screen->startH;
5049	CELL last = screen->endH;
5050
5051	screen->startH = zeroCELL;
5052	screen->endH = zeroCELL;
5053	ReHiliteText(xw, &first, &last);
5054    }
5055}
5056
5057/* returns number of chars in line from scol to ecol out */
5058/* ARGSUSED */
5059static int
5060Length(TScreen *screen,
5061       int row,
5062       int scol,
5063       int ecol)
5064{
5065    CLineData *ld = GET_LINEDATA(screen, row);
5066    const int lastcol = LastTextCol(screen, ld, row);
5067
5068    if (ecol > lastcol)
5069	ecol = lastcol;
5070    return (ecol - scol + 1);
5071}
5072
5073/* copies text into line, preallocated */
5074static Char *
5075SaveText(TScreen *screen,
5076	 int row,
5077	 int scol,
5078	 int ecol,
5079	 Char *lp,		/* pointer to where to put the text */
5080	 int *eol)
5081{
5082    LineData *ld;
5083    int i = 0;
5084    Char *result = lp;
5085#if OPT_WIDE_CHARS
5086    unsigned previous = 0;
5087#endif
5088
5089    ld = GET_LINEDATA(screen, row);
5090    i = Length(screen, row, scol, ecol);
5091    ecol = scol + i;
5092#if OPT_DEC_CHRSET
5093    if (CSET_DOUBLE(GetLineDblCS(ld))) {
5094	scol = (scol + 0) / 2;
5095	ecol = (ecol + 1) / 2;
5096    }
5097#endif
5098    *eol = !LineTstWrapped(ld);
5099    for (i = scol; i < ecol; i++) {
5100	unsigned c;
5101	assert(i < (int) ld->lineSize);
5102	c = E2A(ld->charData[i]);
5103#if OPT_WIDE_CHARS
5104	/* We want to strip out every occurrence of HIDDEN_CHAR AFTER a
5105	 * wide character.
5106	 */
5107	if (c == HIDDEN_CHAR) {
5108	    if (isWide((int) previous)) {
5109		previous = c;
5110		/* Combining characters attached to double-width characters
5111		   are in memory attached to the HIDDEN_CHAR */
5112		if_OPT_WIDE_CHARS(screen, {
5113		    if ((screen->utf8_nrc_mode | screen->utf8_mode) != uFalse) {
5114			size_t off;
5115			for_each_combData(off, ld) {
5116			    unsigned ch = ld->combData[off][i];
5117			    if (ch == 0)
5118				break;
5119			    lp = convertToUTF8(lp, ch);
5120			}
5121		    }
5122		});
5123		continue;
5124	    } else {
5125		c = ' ';	/* should not happen, but just in case... */
5126	    }
5127	}
5128	previous = c;
5129	if ((screen->utf8_nrc_mode | screen->utf8_mode) != uFalse) {
5130	    lp = convertToUTF8(lp, (c != 0) ? c : ' ');
5131	    if_OPT_WIDE_CHARS(screen, {
5132		size_t off;
5133		for_each_combData(off, ld) {
5134		    unsigned ch = ld->combData[off][i];
5135		    if (ch == 0)
5136			break;
5137		    lp = convertToUTF8(lp, ch);
5138		}
5139	    });
5140	} else
5141#endif
5142	{
5143	    if (c == 0) {
5144		c = E2A(' ');
5145	    } else if (c < E2A(' ')) {
5146		c = DECtoASCII(c);
5147	    } else if (c == 0x7f) {
5148		c = 0x5f;
5149	    }
5150	    *lp++ = CharOf(A2E(c));
5151	}
5152	if (c != E2A(' '))
5153	    result = lp;
5154    }
5155
5156    /*
5157     * If requested, trim trailing blanks from selected lines.  Do not do this
5158     * if the line is wrapped.
5159     */
5160    if (!*eol || !screen->trim_selection)
5161	result = lp;
5162
5163    return (result);
5164}
5165
5166/*
5167 * This adds together the bits:
5168 *   shift key   -> 1
5169 *   meta key    -> 2
5170 *   control key -> 4
5171 */
5172static unsigned
5173KeyState(XtermWidget xw, unsigned x)
5174{
5175    return ((((x) & (ShiftMask | ControlMask)))
5176	    + (((x) & MetaMask(xw)) ? 2 : 0));
5177}
5178
5179/* 32 + following 8-bit word:
5180
5181   1:0  Button no: 0, 1, 2.  3=release.
5182     2  shift
5183     3  meta
5184     4  ctrl
5185     5  set for motion notify
5186     6  set for wheel (and button 6 and 7)
5187     7  set for buttons 8 to 11
5188*/
5189
5190/* Position: 32 - 255. */
5191static int
5192BtnCode(XtermWidget xw, XButtonEvent *event, int button)
5193{
5194    int result = (int) (32 + (KeyState(xw, event->state) << 2));
5195
5196    if (event->type == MotionNotify)
5197	result += 32;
5198
5199    if (button < 0) {
5200	result += 3;
5201    } else {
5202	result += button & 3;
5203	if (button & 4)
5204	    result += 64;
5205	if (button & 8)
5206	    result += 128;
5207    }
5208    TRACE(("BtnCode button %d, %s state " FMT_MODIFIER_NAMES " ->%#x\n",
5209	   button,
5210	   visibleEventType(event->type),
5211	   ARG_MODIFIER_NAMES(event->state),
5212	   result));
5213    return result;
5214}
5215
5216static unsigned
5217EmitButtonCode(XtermWidget xw,
5218	       Char *line,
5219	       unsigned count,
5220	       XButtonEvent *event,
5221	       int button)
5222{
5223    TScreen *screen = TScreenOf(xw);
5224    int value;
5225
5226    if (okSendMousePos(xw) == X10_MOUSE) {
5227	value = CharOf(' ' + button);
5228    } else {
5229	value = BtnCode(xw, event, button);
5230    }
5231
5232    switch (screen->extend_coords) {
5233    default:
5234	line[count++] = CharOf(value);
5235	break;
5236    case SET_SGR_EXT_MODE_MOUSE:
5237    case SET_PIXEL_POSITION_MOUSE:
5238	value -= 32;		/* encoding starts at zero */
5239	/* FALLTHRU */
5240    case SET_URXVT_EXT_MODE_MOUSE:
5241	count += (unsigned) sprintf((char *) line + count, "%d", value);
5242	break;
5243    case SET_EXT_MODE_MOUSE:
5244	if (value < 128) {
5245	    line[count++] = CharOf(value);
5246	} else {
5247	    line[count++] = CharOf(0xC0 + (value >> 6));
5248	    line[count++] = CharOf(0x80 + (value & 0x3F));
5249	}
5250	break;
5251    }
5252    return count;
5253}
5254
5255static int
5256FirstBitN(int bits)
5257{
5258    int result = -1;
5259    if (bits > 0) {
5260	result = 0;
5261	while (!(bits & 1)) {
5262	    bits /= 2;
5263	    ++result;
5264	}
5265    }
5266    return result;
5267}
5268
5269#define ButtonBit(button) ((button >= 0) ? (1 << (button)) : 0)
5270
5271#define EMIT_BUTTON(button) EmitButtonCode(xw, line, count, event, button)
5272
5273static void
5274EditorButton(XtermWidget xw, XButtonEvent *event)
5275{
5276    TScreen *screen = TScreenOf(xw);
5277    int pty = screen->respond;
5278    int mouse_limit = MouseLimit(screen);
5279    Char line[32];
5280    Char final = 'M';
5281    int row, col;
5282    int button;
5283    unsigned count = 0;
5284    Boolean changed = True;
5285
5286    /* If button event, get button # adjusted for DEC compatibility */
5287    button = (int) (event->button - 1);
5288    if (button >= 3)
5289	button++;
5290
5291    /* Ignore buttons that cannot be encoded */
5292    if (screen->send_mouse_pos == X10_MOUSE) {
5293	if (button > 3)
5294	    return;
5295    } else if (screen->extend_coords == SET_SGR_EXT_MODE_MOUSE
5296	       || screen->extend_coords == SET_URXVT_EXT_MODE_MOUSE
5297	       || screen->extend_coords == SET_PIXEL_POSITION_MOUSE) {
5298	if (button > 15) {
5299	    return;
5300	}
5301    } else {
5302	if (button > 11) {
5303	    return;
5304	}
5305    }
5306
5307    if (screen->extend_coords == SET_PIXEL_POSITION_MOUSE) {
5308	row = event->y - OriginY(screen);
5309	col = event->x - OriginX(screen);
5310    } else {
5311	/* Compute character position of mouse pointer */
5312	row = (event->y - screen->border) / FontHeight(screen);
5313	col = (event->x - OriginX(screen)) / FontWidth(screen);
5314
5315	/* Limit to screen dimensions */
5316	if (row < 0)
5317	    row = 0;
5318	else if (row > screen->max_row)
5319	    row = screen->max_row;
5320
5321	if (col < 0)
5322	    col = 0;
5323	else if (col > screen->max_col)
5324	    col = screen->max_col;
5325
5326	if (mouse_limit > 0) {
5327	    /* Limit to representable mouse dimensions */
5328	    if (row > mouse_limit)
5329		row = mouse_limit;
5330	    if (col > mouse_limit)
5331		col = mouse_limit;
5332	}
5333    }
5334
5335    /* Build key sequence starting with \E[M */
5336    if (screen->control_eight_bits) {
5337	line[count++] = ANSI_CSI;
5338    } else {
5339	line[count++] = ANSI_ESC;
5340	line[count++] = '[';
5341    }
5342    switch (screen->extend_coords) {
5343    case 0:
5344    case SET_EXT_MODE_MOUSE:
5345#if OPT_SCO_FUNC_KEYS
5346	if (xw->keyboard.type == keyboardIsSCO) {
5347	    /*
5348	     * SCO function key F1 is \E[M, which would conflict with xterm's
5349	     * normal kmous.
5350	     */
5351	    line[count++] = '>';
5352	}
5353#endif
5354	line[count++] = final;
5355	break;
5356    case SET_SGR_EXT_MODE_MOUSE:
5357    case SET_PIXEL_POSITION_MOUSE:
5358	line[count++] = '<';
5359	break;
5360    }
5361
5362    /* Add event code to key sequence */
5363    if (okSendMousePos(xw) == X10_MOUSE) {
5364	count = EMIT_BUTTON(button);
5365    } else {
5366	/* Button-Motion events */
5367	switch (event->type) {
5368	case ButtonPress:
5369	    screen->mouse_button |= ButtonBit(button);
5370	    count = EMIT_BUTTON(button);
5371	    break;
5372	case ButtonRelease:
5373	    /*
5374	     * The (vertical) wheel mouse interface generates release-events
5375	     * for buttons 4 and 5.
5376	     *
5377	     * The X10/X11 xterm protocol maps the release for buttons 1..3 to
5378	     * a -1, which will be later mapped into a "0" (some button was
5379	     * released),  At this point, buttons 1..3 are encoded 0..2 (the
5380	     * code 3 is unused).
5381	     *
5382	     * The SGR (extended) xterm mouse protocol keeps the button number
5383	     * and uses a "m" to indicate button release.
5384	     *
5385	     * The behavior for mice with more buttons is unclear, and may be
5386	     * revised -TD
5387	     */
5388	    screen->mouse_button &= ~ButtonBit(button);
5389	    if (button < 3 || button > 5) {
5390		switch (screen->extend_coords) {
5391		case SET_SGR_EXT_MODE_MOUSE:
5392		case SET_PIXEL_POSITION_MOUSE:
5393		    final = 'm';
5394		    break;
5395		default:
5396		    button = -1;
5397		    break;
5398		}
5399	    }
5400	    count = EMIT_BUTTON(button);
5401	    break;
5402	case MotionNotify:
5403	    /* BTN_EVENT_MOUSE and ANY_EVENT_MOUSE modes send motion
5404	     * events only if character cell has changed.
5405	     */
5406	    if ((row == screen->mouse_row)
5407		&& (col == screen->mouse_col)) {
5408		changed = False;
5409	    } else {
5410		count = EMIT_BUTTON(FirstBitN(screen->mouse_button));
5411	    }
5412	    break;
5413	default:
5414	    changed = False;
5415	    break;
5416	}
5417    }
5418
5419    if (changed) {
5420	screen->mouse_row = row;
5421	screen->mouse_col = col;
5422
5423	TRACE(("mouse at %d,%d button+mask = %#x\n", row, col, line[count - 1]));
5424
5425	/* Add pointer position to key sequence */
5426	count = EmitMousePositionSeparator(screen, line, count);
5427	count = EmitMousePosition(screen, line, count, col);
5428	count = EmitMousePositionSeparator(screen, line, count);
5429	count = EmitMousePosition(screen, line, count, row);
5430
5431	switch (screen->extend_coords) {
5432	case SET_SGR_EXT_MODE_MOUSE:
5433	case SET_URXVT_EXT_MODE_MOUSE:
5434	case SET_PIXEL_POSITION_MOUSE:
5435	    line[count++] = final;
5436	    break;
5437	}
5438
5439	/* Transmit key sequence to process running under xterm */
5440	TRACE(("EditorButton -> %s\n", visibleChars(line, count)));
5441	v_write(pty, line, count);
5442    }
5443    return;
5444}
5445
5446/*
5447 * Check the current send_mouse_pos against allowed mouse-operations, returning
5448 * none if it is disallowed.
5449 */
5450XtermMouseModes
5451okSendMousePos(XtermWidget xw)
5452{
5453    TScreen *screen = TScreenOf(xw);
5454    XtermMouseModes result = (XtermMouseModes) screen->send_mouse_pos;
5455
5456    switch ((int) result) {
5457    case MOUSE_OFF:
5458	break;
5459    case X10_MOUSE:
5460	if (!AllowMouseOps(xw, emX10))
5461	    result = MOUSE_OFF;
5462	break;
5463    case VT200_MOUSE:
5464	if (!AllowMouseOps(xw, emVT200Click))
5465	    result = MOUSE_OFF;
5466	break;
5467    case VT200_HIGHLIGHT_MOUSE:
5468	if (!AllowMouseOps(xw, emVT200Hilite))
5469	    result = MOUSE_OFF;
5470	break;
5471    case BTN_EVENT_MOUSE:
5472	if (!AllowMouseOps(xw, emAnyButton))
5473	    result = MOUSE_OFF;
5474	break;
5475    case ANY_EVENT_MOUSE:
5476	if (!AllowMouseOps(xw, emAnyEvent))
5477	    result = MOUSE_OFF;
5478	break;
5479    case DEC_LOCATOR:
5480	if (!AllowMouseOps(xw, emLocator))
5481	    result = MOUSE_OFF;
5482	break;
5483    }
5484    return result;
5485}
5486
5487#if OPT_FOCUS_EVENT
5488/*
5489 * Check the current send_focus_pos against allowed mouse-operations, returning
5490 * none if it is disallowed.
5491 */
5492static int
5493okSendFocusPos(XtermWidget xw)
5494{
5495    TScreen *screen = TScreenOf(xw);
5496    int result = screen->send_focus_pos;
5497
5498    if (!AllowMouseOps(xw, emFocusEvent)) {
5499	result = False;
5500    }
5501    return result;
5502}
5503
5504void
5505SendFocusButton(XtermWidget xw, XFocusChangeEvent *event)
5506{
5507    if (okSendFocusPos(xw)) {
5508	ANSI reply;
5509
5510	memset(&reply, 0, sizeof(reply));
5511	reply.a_type = ANSI_CSI;
5512
5513#if OPT_SCO_FUNC_KEYS
5514	if (xw->keyboard.type == keyboardIsSCO) {
5515	    reply.a_pintro = '>';
5516	}
5517#endif
5518	reply.a_final = CharOf((event->type == FocusIn) ? 'I' : 'O');
5519	unparseseq(xw, &reply);
5520    }
5521    return;
5522}
5523#endif /* OPT_FOCUS_EVENT */
5524
5525#if OPT_SELECTION_OPS
5526/*
5527 * Get the event-time, needed to process selections.
5528 */
5529static Time
5530getEventTime(XEvent *event)
5531{
5532    Time result;
5533
5534    if (IsBtnEvent(event)) {
5535	result = ((XButtonEvent *) event)->time;
5536    } else if (IsKeyEvent(event)) {
5537	result = ((XKeyEvent *) event)->time;
5538    } else {
5539	result = 0;
5540    }
5541
5542    return result;
5543}
5544
5545/* obtain the selection string, passing the endpoints to caller's parameters */
5546static void
5547doSelectionFormat(XtermWidget xw,
5548		  Widget w,
5549		  XEvent *event,
5550		  String *params,
5551		  Cardinal *num_params,
5552		  FormatSelect format_select)
5553{
5554    TScreen *screen = TScreenOf(xw);
5555    InternalSelect *mydata = &(screen->internal_select);
5556
5557    memset(mydata, 0, sizeof(*mydata));
5558    mydata->format = x_strdup(params[0]);
5559    mydata->format_select = format_select;
5560
5561    screen->selectToBuffer = True;
5562    beginInternalSelect(xw);
5563
5564    xtermGetSelection(w, getEventTime(event), params + 1, *num_params - 1, NULL);
5565
5566    if (screen->selectToBuffer)
5567	finishInternalSelect(xw);
5568}
5569
5570/* obtain data from the screen, passing the endpoints to caller's parameters */
5571static char *
5572getDataFromScreen(XtermWidget xw, XEvent *event, String method, CELL *start, CELL *finish)
5573{
5574    TScreen *screen = TScreenOf(xw);
5575
5576    CELL save_old_start = screen->startH;
5577    CELL save_old_end = screen->endH;
5578
5579    CELL save_startSel = screen->startSel;
5580    CELL save_startRaw = screen->startRaw;
5581    CELL save_finishSel = screen->endSel;
5582    CELL save_finishRaw = screen->endRaw;
5583
5584    int save_firstValidRow = screen->firstValidRow;
5585    int save_lastValidRow = screen->lastValidRow;
5586
5587    const Cardinal noClick = 0;
5588    int save_numberOfClicks = screen->numberOfClicks;
5589
5590    SelectUnit saveUnits = screen->selectUnit;
5591    SelectUnit saveMap = screen->selectMap[noClick];
5592#if OPT_SELECT_REGEX
5593    char *saveExpr = screen->selectExpr[noClick];
5594#endif
5595    SelectedCells *scp = &(screen->selected_cells[PRIMARY_CODE]);
5596    SelectedCells save_selection = *scp;
5597
5598    char *result = 0;
5599
5600    TRACE(("getDataFromScreen %s\n", method));
5601
5602    memset(scp, 0, sizeof(*scp));
5603
5604    screen->numberOfClicks = 1;
5605    lookupSelectUnit(xw, noClick, method);
5606    screen->selectUnit = screen->selectMap[noClick];
5607
5608    memset(start, 0, sizeof(*start));
5609    if (IsBtnEvent(event)) {
5610	XButtonEvent *btn_event = (XButtonEvent *) event;
5611	CELL cell;
5612	screen->firstValidRow = 0;
5613	screen->lastValidRow = screen->max_row;
5614	PointToCELL(screen, btn_event->y, btn_event->x, &cell);
5615	start->row = cell.row;
5616	start->col = cell.col;
5617	finish->row = cell.row;
5618	finish->col = screen->max_col;
5619    } else {
5620	start->row = screen->cur_row;
5621	start->col = screen->cur_col;
5622	finish->row = screen->cur_row;
5623	finish->col = screen->max_col;
5624    }
5625
5626    ComputeSelect(xw, start, finish, False, False);
5627    SaltTextAway(xw,
5628		 TargetToSelection(screen, PRIMARY_NAME),
5629		 &(screen->startSel), &(screen->endSel));
5630
5631    if (scp->data_limit && scp->data_buffer) {
5632	TRACE(("...getDataFromScreen selection-data %.*s\n",
5633	       (int) scp->data_limit,
5634	       scp->data_buffer));
5635	result = malloc(scp->data_limit + 1);
5636	if (result) {
5637	    memcpy(result, scp->data_buffer, scp->data_limit);
5638	    result[scp->data_limit] = 0;
5639	}
5640	free(scp->data_buffer);
5641	scp->data_limit = 0;
5642    }
5643
5644    TRACE(("...getDataFromScreen restoring previous selection\n"));
5645
5646    screen->startSel = save_startSel;
5647    screen->startRaw = save_startRaw;
5648    screen->endSel = save_finishSel;
5649    screen->endRaw = save_finishRaw;
5650
5651    screen->firstValidRow = save_firstValidRow;
5652    screen->lastValidRow = save_lastValidRow;
5653
5654    screen->numberOfClicks = save_numberOfClicks;
5655    screen->selectUnit = saveUnits;
5656    screen->selectMap[noClick] = saveMap;
5657#if OPT_SELECT_REGEX
5658    screen->selectExpr[noClick] = saveExpr;
5659#endif
5660
5661    screen->selected_cells[0] = save_selection;
5662
5663    TrackText(xw, &save_old_start, &save_old_end);
5664
5665    TRACE(("...getDataFromScreen done\n"));
5666    return result;
5667}
5668
5669/*
5670 * Split-up the format before substituting data, to avoid quoting issues.
5671 * The resource mechanism has a limited ability to handle escapes.  We take
5672 * the result as if it were an sh-type string and parse it into a regular
5673 * argv array.
5674 */
5675static char **
5676tokenizeFormat(String format)
5677{
5678    char **result = 0;
5679
5680    format = x_skip_blanks(format);
5681    if (*format != '\0') {
5682	char *blob = x_strdup(format);
5683	int pass;
5684
5685	for (pass = 0; pass < 2; ++pass) {
5686	    int used = 0;
5687	    int first = 1;
5688	    int escaped = 0;
5689	    int squoted = 0;
5690	    int dquoted = 0;
5691	    int n;
5692	    int argc = 0;
5693
5694	    for (n = 0; format[n] != '\0'; ++n) {
5695		if (escaped) {
5696		    blob[used++] = format[n];
5697		    escaped = 0;
5698		} else if (format[n] == '"') {
5699		    if (!squoted) {
5700			if (!dquoted)
5701			    blob[used++] = format[n];
5702			dquoted = !dquoted;
5703		    }
5704		} else if (format[n] == '\'') {
5705		    if (!dquoted) {
5706			if (!squoted)
5707			    blob[used++] = format[n];
5708			squoted = !squoted;
5709		    }
5710		} else if (format[n] == '\\') {
5711		    blob[used++] = format[n];
5712		    escaped = 1;
5713		} else {
5714		    if (first) {
5715			first = 0;
5716			if (pass) {
5717			    result[argc] = &blob[n];
5718			}
5719			++argc;
5720		    }
5721		    if (isspace((Char) format[n])) {
5722			first = !isspace((Char) format[n + 1]);
5723			if (squoted || dquoted) {
5724			    blob[used++] = format[n];
5725			} else if (first) {
5726			    blob[used++] = '\0';
5727			}
5728		    } else {
5729			blob[used++] = format[n];
5730		    }
5731		}
5732	    }
5733	    blob[used] = '\0';
5734	    assert(strlen(blob) <= strlen(format));
5735	    if (!pass) {
5736		result = TypeCallocN(char *, argc + 1);
5737		if (result == 0) {
5738		    free(blob);
5739		    break;
5740		}
5741	    }
5742	}
5743    }
5744#if OPT_TRACE
5745    if (result) {
5746	int n;
5747	TRACE(("tokenizeFormat %s\n", format));
5748	for (n = 0; result[n]; ++n) {
5749	    TRACE(("argv[%d] = %s\n", n, result[n]));
5750	}
5751    }
5752#endif
5753
5754    return result;
5755}
5756
5757static void
5758formatVideoAttrs(XtermWidget xw, char *buffer, CELL *cell)
5759{
5760    TScreen *screen = TScreenOf(xw);
5761    LineData *ld = GET_LINEDATA(screen, cell->row);
5762
5763    *buffer = '\0';
5764    if (ld != 0 && cell->col < (int) ld->lineSize) {
5765	IAttr attribs = ld->attribs[cell->col];
5766	const char *delim = "";
5767
5768	if (attribs & INVERSE) {
5769	    buffer += sprintf(buffer, "7");
5770	    delim = ";";
5771	}
5772	if (attribs & UNDERLINE) {
5773	    buffer += sprintf(buffer, "%s4", delim);
5774	    delim = ";";
5775	}
5776	if (attribs & BOLD) {
5777	    buffer += sprintf(buffer, "%s1", delim);
5778	    delim = ";";
5779	}
5780	if (attribs & BLINK) {
5781	    buffer += sprintf(buffer, "%s5", delim);
5782	    delim = ";";
5783	}
5784#if OPT_ISO_COLORS
5785	if (attribs & FG_COLOR) {
5786	    Pixel fg = extract_fg(xw, ld->color[cell->col], attribs);
5787	    if (fg < 8) {
5788		fg += 30;
5789	    } else if (fg < 16) {
5790		fg += 90;
5791	    } else {
5792		buffer += sprintf(buffer, "%s38;5", delim);
5793		delim = ";";
5794	    }
5795	    buffer += sprintf(buffer, "%s%lu", delim, fg);
5796	    delim = ";";
5797	}
5798	if (attribs & BG_COLOR) {
5799	    Pixel bg = extract_bg(xw, ld->color[cell->col], attribs);
5800	    if (bg < 8) {
5801		bg += 40;
5802	    } else if (bg < 16) {
5803		bg += 100;
5804	    } else {
5805		buffer += sprintf(buffer, "%s48;5", delim);
5806		delim = ";";
5807	    }
5808	    (void) sprintf(buffer, "%s%lu", delim, bg);
5809	}
5810#endif
5811    }
5812}
5813
5814static char *
5815formatStrlen(char *target, char *source, int freeit)
5816{
5817    if (source != 0) {
5818	sprintf(target, "%u", (unsigned) strlen(source));
5819	if (freeit) {
5820	    free(source);
5821	}
5822    } else {
5823	strcpy(target, "0");
5824    }
5825    return target;
5826}
5827
5828/* substitute data into format, reallocating the result */
5829static char *
5830expandFormat(XtermWidget xw,
5831	     const char *format,
5832	     char *data,
5833	     CELL *start,
5834	     CELL *finish)
5835{
5836    char *result = 0;
5837    if (!IsEmpty(format)) {
5838	static char empty[1];
5839	int pass;
5840	int n;
5841	char numbers[80];
5842
5843	if (data == 0)
5844	    data = empty;
5845
5846	for (pass = 0; pass < 2; ++pass) {
5847	    size_t need = 0;
5848
5849	    for (n = 0; format[n] != '\0'; ++n) {
5850
5851		if (format[n] == '%') {
5852		    char *value = 0;
5853
5854		    switch (format[++n]) {
5855		    case '%':
5856			if (pass) {
5857			    result[need] = format[n];
5858			}
5859			++need;
5860			break;
5861		    case 'P':
5862			sprintf(numbers, "%d;%d",
5863				TScreenOf(xw)->topline + start->row + 1,
5864				start->col + 1);
5865			value = numbers;
5866			break;
5867		    case 'p':
5868			sprintf(numbers, "%d;%d",
5869				TScreenOf(xw)->topline + finish->row + 1,
5870				finish->col + 1);
5871			value = numbers;
5872			break;
5873		    case 'R':
5874			value = formatStrlen(numbers, x_strrtrim(data), 1);
5875			break;
5876		    case 'r':
5877			value = x_strrtrim(data);
5878			break;
5879		    case 'S':
5880			value = formatStrlen(numbers, data, 0);
5881			break;
5882		    case 's':
5883			value = data;
5884			break;
5885		    case 'T':
5886			value = formatStrlen(numbers, x_strtrim(data), 1);
5887			break;
5888		    case 't':
5889			value = x_strtrim(data);
5890			break;
5891		    case 'V':
5892			formatVideoAttrs(xw, numbers, start);
5893			value = numbers;
5894			break;
5895		    case 'v':
5896			formatVideoAttrs(xw, numbers, finish);
5897			value = numbers;
5898			break;
5899		    default:
5900			if (pass) {
5901			    result[need] = format[n];
5902			}
5903			--n;
5904			++need;
5905			break;
5906		    }
5907		    if (value != 0) {
5908			if (pass) {
5909			    strcpy(result + need, value);
5910			}
5911			need += strlen(value);
5912			if (value != numbers && value != data) {
5913			    free(value);
5914			}
5915		    }
5916		} else {
5917		    if (pass) {
5918			result[need] = format[n];
5919		    }
5920		    ++need;
5921		}
5922	    }
5923	    if (pass) {
5924		result[need] = '\0';
5925	    } else {
5926		++need;
5927		result = malloc(need);
5928		if (result == 0) {
5929		    break;
5930		}
5931	    }
5932	}
5933    }
5934    TRACE(("expandFormat(%s) = %s\n", NonNull(format), NonNull(result)));
5935    return result;
5936}
5937
5938/* execute the command after forking.  The main process frees its data */
5939static void
5940executeCommand(pid_t pid, char **argv)
5941{
5942    (void) pid;
5943    if (argv != 0 && argv[0] != 0) {
5944	char *child_cwd = ProcGetCWD(pid);
5945
5946	if (fork() == 0) {
5947	    if (child_cwd) {
5948		IGNORE_RC(chdir(child_cwd));	/* We don't care if this fails */
5949	    }
5950	    execvp(argv[0], argv);
5951	    exit(EXIT_FAILURE);
5952	}
5953	free(child_cwd);
5954    }
5955}
5956
5957static void
5958freeArgv(char *blob, char **argv)
5959{
5960    if (blob) {
5961	free(blob);
5962	if (argv) {
5963	    int n;
5964	    for (n = 0; argv[n]; ++n)
5965		free(argv[n]);
5966	    free(argv);
5967	}
5968    }
5969}
5970
5971static void
5972reallyExecFormatted(Widget w, char *format, char *data, CELL *start, CELL *finish)
5973{
5974    XtermWidget xw;
5975
5976    if ((xw = getXtermWidget(w)) != 0) {
5977	char **argv;
5978
5979	if ((argv = tokenizeFormat(format)) != 0) {
5980	    char *blob = argv[0];
5981	    int argc;
5982
5983	    for (argc = 0; argv[argc] != 0; ++argc) {
5984		argv[argc] = expandFormat(xw, argv[argc], data, start, finish);
5985	    }
5986	    executeCommand(TScreenOf(xw)->pid, argv);
5987	    freeArgv(blob, argv);
5988	}
5989    }
5990}
5991
5992void
5993HandleExecFormatted(Widget w,
5994		    XEvent *event,
5995		    String *params,	/* selections */
5996		    Cardinal *num_params)
5997{
5998    XtermWidget xw;
5999
6000    TRACE_EVENT("HandleExecFormatted", event, params, num_params);
6001    if ((xw = getXtermWidget(w)) != 0 &&
6002	(*num_params > 1)) {
6003	doSelectionFormat(xw, w, event, params, num_params, reallyExecFormatted);
6004    }
6005}
6006
6007void
6008HandleExecSelectable(Widget w,
6009		     XEvent *event,
6010		     String *params,	/* selections */
6011		     Cardinal *num_params)
6012{
6013    XtermWidget xw;
6014
6015    if ((xw = getXtermWidget(w)) != 0) {
6016	TRACE_EVENT("HandleExecSelectable", event, params, num_params);
6017
6018	if (*num_params == 2) {
6019	    CELL start, finish;
6020	    char *data;
6021	    char **argv;
6022
6023	    data = getDataFromScreen(xw, event, params[1], &start, &finish);
6024	    if (data != 0) {
6025		if ((argv = tokenizeFormat(params[0])) != 0) {
6026		    char *blob = argv[0];
6027		    int argc;
6028
6029		    for (argc = 0; argv[argc] != 0; ++argc) {
6030			argv[argc] = expandFormat(xw, argv[argc], data,
6031						  &start, &finish);
6032		    }
6033		    executeCommand(TScreenOf(xw)->pid, argv);
6034		    freeArgv(blob, argv);
6035		}
6036		free(data);
6037	    }
6038	}
6039    }
6040}
6041
6042static void
6043reallyInsertFormatted(Widget w, char *format, char *data, CELL *start, CELL *finish)
6044{
6045    XtermWidget xw;
6046
6047    if ((xw = getXtermWidget(w)) != 0) {
6048	char *exps;
6049
6050	if ((exps = expandFormat(xw, format, data, start, finish)) != 0) {
6051	    unparseputs(xw, exps);
6052	    unparse_end(xw);
6053	    free(exps);
6054	}
6055    }
6056}
6057
6058void
6059HandleInsertFormatted(Widget w,
6060		      XEvent *event,
6061		      String *params,	/* selections */
6062		      Cardinal *num_params)
6063{
6064    XtermWidget xw;
6065
6066    TRACE_EVENT("HandleInsertFormatted", event, params, num_params);
6067    if ((xw = getXtermWidget(w)) != 0 &&
6068	(*num_params > 1)) {
6069	doSelectionFormat(xw, w, event, params, num_params, reallyInsertFormatted);
6070    }
6071}
6072
6073void
6074HandleInsertSelectable(Widget w,
6075		       XEvent *event,
6076		       String *params,	/* selections */
6077		       Cardinal *num_params)
6078{
6079    XtermWidget xw;
6080
6081    if ((xw = getXtermWidget(w)) != 0) {
6082	TRACE_EVENT("HandleInsertSelectable", event, params, num_params);
6083
6084	if (*num_params == 2) {
6085	    CELL start, finish;
6086	    char *data;
6087	    char *temp = x_strdup(params[0]);
6088
6089	    data = getDataFromScreen(xw, event, params[1], &start, &finish);
6090	    if (data != 0) {
6091		char *exps = expandFormat(xw, temp, data, &start, &finish);
6092		if (exps != 0) {
6093		    unparseputs(xw, exps);
6094		    unparse_end(xw);
6095		    free(exps);
6096		}
6097		free(data);
6098	    }
6099	    free(temp);
6100	}
6101    }
6102}
6103#endif /* OPT_SELECTION_OPS */
6104