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