button.c revision f2e35a3a
1f2e35a3aSmrg/* $XTermId: button.c,v 1.636 2021/02/10 01:14:51 tom Exp $ */
2d522f475Smrg
3d522f475Smrg/*
4f2e35a3aSmrg * Copyright 1999-2020,2021 by Thomas E. Dickey
5d522f475Smrg *
6d522f475Smrg *                         All Rights Reserved
7d522f475Smrg *
8d522f475Smrg * Permission is hereby granted, free of charge, to any person obtaining a
9d522f475Smrg * copy of this software and associated documentation files (the
10d522f475Smrg * "Software"), to deal in the Software without restriction, including
11d522f475Smrg * without limitation the rights to use, copy, modify, merge, publish,
12d522f475Smrg * distribute, sublicense, and/or sell copies of the Software, and to
13d522f475Smrg * permit persons to whom the Software is furnished to do so, subject to
14d522f475Smrg * the following conditions:
15d522f475Smrg *
16d522f475Smrg * The above copyright notice and this permission notice shall be included
17d522f475Smrg * in all copies or substantial portions of the Software.
18d522f475Smrg *
19d522f475Smrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
20d522f475Smrg * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
21d522f475Smrg * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
22d522f475Smrg * IN NO EVENT SHALL THE ABOVE LISTED COPYRIGHT HOLDER(S) BE LIABLE FOR ANY
23d522f475Smrg * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
24d522f475Smrg * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
25d522f475Smrg * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
26d522f475Smrg *
27d522f475Smrg * Except as contained in this notice, the name(s) of the above copyright
28d522f475Smrg * holders shall not be used in advertising or otherwise to promote the
29d522f475Smrg * sale, use or other dealings in this Software without prior written
30d522f475Smrg * authorization.
31d522f475Smrg *
32d522f475Smrg *
33d522f475Smrg * Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts.
34d522f475Smrg *
35d522f475Smrg *                         All Rights Reserved
36d522f475Smrg *
37d522f475Smrg * Permission to use, copy, modify, and distribute this software and its
38d522f475Smrg * documentation for any purpose and without fee is hereby granted,
39d522f475Smrg * provided that the above copyright notice appear in all copies and that
40d522f475Smrg * both that copyright notice and this permission notice appear in
41d522f475Smrg * supporting documentation, and that the name of Digital Equipment
42d522f475Smrg * Corporation not be used in advertising or publicity pertaining to
43d522f475Smrg * distribution of the software without specific, written prior permission.
44d522f475Smrg *
45d522f475Smrg *
46d522f475Smrg * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
47d522f475Smrg * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
48d522f475Smrg * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
49d522f475Smrg * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
50d522f475Smrg * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
51d522f475Smrg * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
52d522f475Smrg * SOFTWARE.
53d522f475Smrg */
54d522f475Smrg
55d522f475Smrg/*
56d522f475Smrgbutton.c	Handles button events in the terminal emulator.
57d522f475Smrg		does cut/paste operations, change modes via menu,
58d522f475Smrg		passes button events through to some applications.
59d522f475Smrg				J. Gettys.
60d522f475Smrg*/
61d522f475Smrg
62d522f475Smrg#include <xterm.h>
63d522f475Smrg
64d522f475Smrg#include <stdio.h>
650bd37d32Smrg#include <ctype.h>
6620d2c4d2Smrg#include <assert.h>
67d522f475Smrg
68d522f475Smrg#include <X11/Xatom.h>
69d522f475Smrg#include <X11/Xmu/Atoms.h>
70d522f475Smrg#include <X11/Xmu/StdSel.h>
71d522f475Smrg
72d522f475Smrg#include <xutf8.h>
73d522f475Smrg#include <fontutils.h>
74d522f475Smrg
75d522f475Smrg#include <data.h>
76d522f475Smrg#include <error.h>
77d522f475Smrg#include <menu.h>
78d522f475Smrg#include <charclass.h>
79d522f475Smrg#include <xstrings.h>
80d522f475Smrg
81d522f475Smrg#if OPT_SELECT_REGEX
82f2e35a3aSmrg#ifdef HAVE_PCRE2POSIX_H
83f2e35a3aSmrg#include <pcre2posix.h>
84f2e35a3aSmrg#else
85d522f475Smrg#ifdef HAVE_PCREPOSIX_H
86d522f475Smrg#include <pcreposix.h>
87d522f475Smrg#else /* POSIX regex.h */
88d522f475Smrg#include <sys/types.h>
89d522f475Smrg#include <regex.h>
90d522f475Smrg#endif
91d522f475Smrg#endif
92f2e35a3aSmrg#endif
93f2e35a3aSmrg
94f2e35a3aSmrg#ifdef HAVE_X11_TRANSLATEI_H
95f2e35a3aSmrg#include <X11/ConvertI.h>
96f2e35a3aSmrg#include <X11/TranslateI.h>
97f2e35a3aSmrg#else
98f2e35a3aSmrgextern String _XtPrintXlations(Widget w,
99f2e35a3aSmrg			       XtTranslations xlations,
100f2e35a3aSmrg			       Widget accelWidget,
101f2e35a3aSmrg			       _XtBoolean includeRHS);
102f2e35a3aSmrg#endif
103f2e35a3aSmrg
104f2e35a3aSmrg#define PRIMARY_NAME    "PRIMARY"
105f2e35a3aSmrg#define CLIPBOARD_NAME  "CLIPBOARD"
106f2e35a3aSmrg#define SECONDARY_NAME  "SECONDARY"
107f2e35a3aSmrg
108f2e35a3aSmrg#define AtomToSelection(d,n) \
109f2e35a3aSmrg		 (((n) == XA_CLIPBOARD(d)) \
110f2e35a3aSmrg		  ? CLIPBOARD_CODE \
111f2e35a3aSmrg		  : (((n) == XA_SECONDARY) \
112f2e35a3aSmrg		     ? SECONDARY_CODE \
113f2e35a3aSmrg		     : PRIMARY_CODE))
114f2e35a3aSmrg
115f2e35a3aSmrg#define isSelectionCode(n) ((n) >= PRIMARY_CODE)
116f2e35a3aSmrg#define CutBufferToCode(n) ((n) +  MAX_SELECTION_CODES)
117f2e35a3aSmrg#define okSelectionCode(n) (isSelectionCode(n) ? (n) : PRIMARY_CODE)
118d522f475Smrg
119d522f475Smrg#if OPT_WIDE_CHARS
120d522f475Smrg#include <ctype.h>
121d522f475Smrg#include <wcwidth.h>
122d522f475Smrg#else
123d522f475Smrg#define CharacterClass(value) \
124f2e35a3aSmrg	charClass[(value) & (int)((sizeof(charClass)/sizeof(charClass[0]))-1)]
125d522f475Smrg#endif
126d522f475Smrg
127956cc18dSsnj    /*
128956cc18dSsnj     * We'll generally map rows to indices when doing selection.
129956cc18dSsnj     * Simplify that with a macro.
130956cc18dSsnj     *
131956cc18dSsnj     * Note that ROW2INX() is safe to use with auto increment/decrement for
132956cc18dSsnj     * the row expression since that is evaluated once.
133956cc18dSsnj     */
134956cc18dSsnj#define GET_LINEDATA(screen, row) \
135956cc18dSsnj	getLineData(screen, ROW2INX(screen, row))
136956cc18dSsnj
137f2e35a3aSmrg#define MaxMouseBtn  5
138492d43a5Smrg
139492d43a5Smrg#define IsBtnEvent(event) ((event)->type == ButtonPress || (event)->type == ButtonRelease)
1400bd37d32Smrg#define IsKeyEvent(event) ((event)->type == KeyPress    || (event)->type == KeyRelease)
141d522f475Smrg
142d522f475Smrg#define	Coordinate(s,c)	((c)->row * MaxCols(s) + (c)->col)
143d522f475Smrg
144d522f475Smrgstatic const CELL zeroCELL =
145d522f475Smrg{0, 0};
146d522f475Smrg
147d522f475Smrg#if OPT_DEC_LOCATOR
148894e0ac8Smrgstatic Bool SendLocatorPosition(XtermWidget xw, XButtonEvent *event);
149894e0ac8Smrgstatic void CheckLocatorPosition(XtermWidget xw, XButtonEvent *event);
150d522f475Smrg#endif /* OPT_DEC_LOCATOR */
151d522f475Smrg
152d522f475Smrg/* Multi-click handling */
153d522f475Smrg#if OPT_READLINE
154d522f475Smrgstatic Time lastButtonDownTime = 0;
155d522f475Smrgstatic int ExtendingSelection = 0;
156d522f475Smrgstatic Time lastButton3UpTime = 0;
157d522f475Smrgstatic Time lastButton3DoubleDownTime = 0;
158d522f475Smrgstatic CELL lastButton3;	/* At the release time */
159d522f475Smrg#endif /* OPT_READLINE */
160d522f475Smrg
161e0a2b6dfSmrgstatic Char *SaveText(TScreen *screen, int row, int scol, int ecol,
162e0a2b6dfSmrg		      Char *lp, int *eol);
163e0a2b6dfSmrgstatic int Length(TScreen *screen, int row, int scol, int ecol);
164f2e35a3aSmrgstatic void ComputeSelect(XtermWidget xw, CELL *startc, CELL *endc, Bool
165f2e35a3aSmrg			  extend, Bool normal);
166894e0ac8Smrgstatic void EditorButton(XtermWidget xw, XButtonEvent *event);
167894e0ac8Smrgstatic void EndExtend(XtermWidget w, XEvent *event, String *params, Cardinal
168d522f475Smrg		      num_params, Bool use_cursor_loc);
169e0a2b6dfSmrgstatic void ExtendExtend(XtermWidget xw, const CELL *cell);
170e0a2b6dfSmrgstatic void PointToCELL(TScreen *screen, int y, int x, CELL *cell);
171e0a2b6dfSmrgstatic void ReHiliteText(XtermWidget xw, CELL *first, CELL *last);
172f2e35a3aSmrgstatic void SaltTextAway(XtermWidget xw, int which, CELL *cellc, CELL *cell);
173894e0ac8Smrgstatic void SelectSet(XtermWidget xw, XEvent *event, String *params, Cardinal num_params);
174d522f475Smrgstatic void SelectionReceived PROTO_XT_SEL_CB_ARGS;
175e0a2b6dfSmrgstatic void StartSelect(XtermWidget xw, const CELL *cell);
176894e0ac8Smrgstatic void TrackDown(XtermWidget xw, XButtonEvent *event);
177e0a2b6dfSmrgstatic void TrackText(XtermWidget xw, const CELL *first, const CELL *last);
178f2e35a3aSmrgstatic void UnHiliteText(XtermWidget xw);
179e0a2b6dfSmrgstatic void _OwnSelection(XtermWidget xw, String *selections, Cardinal count);
180894e0ac8Smrgstatic void do_select_end(XtermWidget xw, XEvent *event, String *params,
181d522f475Smrg			  Cardinal *num_params, Bool use_cursor_loc);
182d522f475Smrg
183492d43a5Smrg#define MOUSE_LIMIT (255 - 32)
184d522f475Smrg
185492d43a5Smrg/* Send SET_EXT_SIZE_MOUSE to enable offsets up to EXT_MOUSE_LIMIT */
186492d43a5Smrg#define EXT_MOUSE_LIMIT (2047 - 32)
187492d43a5Smrg#define EXT_MOUSE_START (127 - 32)
188d522f475Smrg
1890bd37d32Smrgstatic int
190e0a2b6dfSmrgMouseLimit(TScreen *screen)
1910bd37d32Smrg{
1920bd37d32Smrg    int mouse_limit;
1930bd37d32Smrg
1940bd37d32Smrg    switch (screen->extend_coords) {
1950bd37d32Smrg    default:
1960bd37d32Smrg	mouse_limit = MOUSE_LIMIT;
1970bd37d32Smrg	break;
1980bd37d32Smrg    case SET_EXT_MODE_MOUSE:
1990bd37d32Smrg	mouse_limit = EXT_MOUSE_LIMIT;
2000bd37d32Smrg	break;
2010bd37d32Smrg    case SET_SGR_EXT_MODE_MOUSE:
2020bd37d32Smrg    case SET_URXVT_EXT_MODE_MOUSE:
203f2e35a3aSmrg    case SET_PIXEL_POSITION_MOUSE:
2040bd37d32Smrg	mouse_limit = -1;
2050bd37d32Smrg	break;
2060bd37d32Smrg    }
2070bd37d32Smrg    return mouse_limit;
2080bd37d32Smrg}
2090bd37d32Smrg
210492d43a5Smrgstatic unsigned
211e0a2b6dfSmrgEmitMousePosition(TScreen *screen, Char line[], unsigned count, int value)
212492d43a5Smrg{
2130bd37d32Smrg    int mouse_limit = MouseLimit(screen);
214492d43a5Smrg
215492d43a5Smrg    /*
216492d43a5Smrg     * Add pointer position to key sequence
217492d43a5Smrg     *
218492d43a5Smrg     * In extended mode we encode large positions as two-byte UTF-8.
219492d43a5Smrg     *
220492d43a5Smrg     * NOTE: historically, it was possible to emit 256, which became
221492d43a5Smrg     * zero by truncation to 8 bits. While this was arguably a bug,
222492d43a5Smrg     * it's also somewhat useful as a past-end marker. We preserve
223492d43a5Smrg     * this behavior for both normal and extended mouse modes.
224492d43a5Smrg     */
2250bd37d32Smrg    switch (screen->extend_coords) {
2260bd37d32Smrg    default:
2270bd37d32Smrg	if (value == mouse_limit) {
2280bd37d32Smrg	    line[count++] = CharOf(0);
2290bd37d32Smrg	} else {
2300bd37d32Smrg	    line[count++] = CharOf(' ' + value + 1);
2310bd37d32Smrg	}
2320bd37d32Smrg	break;
2330bd37d32Smrg    case SET_EXT_MODE_MOUSE:
2340bd37d32Smrg	if (value == mouse_limit) {
2350bd37d32Smrg	    line[count++] = CharOf(0);
2360bd37d32Smrg	} else if (value < EXT_MOUSE_START) {
2370bd37d32Smrg	    line[count++] = CharOf(' ' + value + 1);
2380bd37d32Smrg	} else {
2390bd37d32Smrg	    value += ' ' + 1;
2400bd37d32Smrg	    line[count++] = CharOf(0xC0 + (value >> 6));
2410bd37d32Smrg	    line[count++] = CharOf(0x80 + (value & 0x3F));
2420bd37d32Smrg	}
2430bd37d32Smrg	break;
2440bd37d32Smrg    case SET_SGR_EXT_MODE_MOUSE:
2450bd37d32Smrg    case SET_URXVT_EXT_MODE_MOUSE:
246f2e35a3aSmrg    case SET_PIXEL_POSITION_MOUSE:
2470bd37d32Smrg	count += (unsigned) sprintf((char *) line + count, "%d", value + 1);
2480bd37d32Smrg	break;
2490bd37d32Smrg    }
2500bd37d32Smrg    return count;
2510bd37d32Smrg}
2520bd37d32Smrg
2530bd37d32Smrgstatic unsigned
254e0a2b6dfSmrgEmitMousePositionSeparator(TScreen *screen, Char line[], unsigned count)
2550bd37d32Smrg{
2560bd37d32Smrg    switch (screen->extend_coords) {
2570bd37d32Smrg    case SET_SGR_EXT_MODE_MOUSE:
2580bd37d32Smrg    case SET_URXVT_EXT_MODE_MOUSE:
259f2e35a3aSmrg    case SET_PIXEL_POSITION_MOUSE:
2600bd37d32Smrg	line[count++] = ';';
2610bd37d32Smrg	break;
262d522f475Smrg    }
263492d43a5Smrg    return count;
264492d43a5Smrg}
265d522f475Smrg
266f2e35a3aSmrgenum {
267f2e35a3aSmrg    scanMods,
268f2e35a3aSmrg    scanKey,
269f2e35a3aSmrg    scanColon,
270f2e35a3aSmrg    scanFunc,
271f2e35a3aSmrg    scanArgs
272f2e35a3aSmrg};
273f2e35a3aSmrg
274f2e35a3aSmrg#if OPT_TRACE > 1
275f2e35a3aSmrgstatic const char *
276f2e35a3aSmrgvisibleScan(int mode)
277f2e35a3aSmrg{
278f2e35a3aSmrg    const char *result = "?";
279f2e35a3aSmrg#define DATA(name) case name: result = #name; break
280f2e35a3aSmrg    switch (mode) {
281f2e35a3aSmrg	DATA(scanMods);
282f2e35a3aSmrg	DATA(scanKey);
283f2e35a3aSmrg	DATA(scanColon);
284f2e35a3aSmrg	DATA(scanFunc);
285f2e35a3aSmrg	DATA(scanArgs);
286f2e35a3aSmrg    }
287f2e35a3aSmrg#undef DATA
288f2e35a3aSmrg    return result;
289f2e35a3aSmrg}
290f2e35a3aSmrg#endif
291f2e35a3aSmrg
292f2e35a3aSmrg#define L_BRACK '<'
293f2e35a3aSmrg#define R_BRACK '>'
294f2e35a3aSmrg#define L_PAREN '('
295f2e35a3aSmrg#define R_PAREN ')'
296f2e35a3aSmrg
297f2e35a3aSmrgstatic char *
298f2e35a3aSmrgscanTrans(char *source, int *this_is, int *next_is, unsigned *first, unsigned *last)
299f2e35a3aSmrg{
300f2e35a3aSmrg    char ch;
301f2e35a3aSmrg    char *target = source;
302f2e35a3aSmrg
303f2e35a3aSmrg    *first = *last = 0;
304f2e35a3aSmrg    if (IsEmpty(target)) {
305f2e35a3aSmrg	target = 0;
306f2e35a3aSmrg    } else {
307f2e35a3aSmrg	do {
308f2e35a3aSmrg	    while (IsSpace(*target))
309f2e35a3aSmrg		target++;
310f2e35a3aSmrg	    *first = (unsigned) (target - source);
311f2e35a3aSmrg	    switch (*this_is = *next_is) {
312f2e35a3aSmrg	    case scanMods:
313f2e35a3aSmrg		while ((ch = *target)) {
314f2e35a3aSmrg		    if (IsSpace(ch)) {
315f2e35a3aSmrg			break;
316f2e35a3aSmrg		    } else if (ch == L_BRACK) {
317f2e35a3aSmrg			*next_is = scanKey;
318f2e35a3aSmrg			break;
319f2e35a3aSmrg		    } else if (ch == ':') {
320f2e35a3aSmrg			*next_is = scanColon;
321f2e35a3aSmrg			break;
322f2e35a3aSmrg		    } else if (ch == '~' && target != source) {
323f2e35a3aSmrg			break;
324f2e35a3aSmrg		    }
325f2e35a3aSmrg		    target++;
326f2e35a3aSmrg		}
327f2e35a3aSmrg		break;
328f2e35a3aSmrg	    case scanKey:
329f2e35a3aSmrg		while ((ch = *target)) {
330f2e35a3aSmrg		    if (IsSpace(ch)) {
331f2e35a3aSmrg			break;
332f2e35a3aSmrg		    } else if (ch == ':') {
333f2e35a3aSmrg			*next_is = scanColon;
334f2e35a3aSmrg			break;
335f2e35a3aSmrg		    }
336f2e35a3aSmrg		    target++;
337f2e35a3aSmrg		    if (ch == R_BRACK)
338f2e35a3aSmrg			break;
339f2e35a3aSmrg		}
340f2e35a3aSmrg		break;
341f2e35a3aSmrg	    case scanColon:
342f2e35a3aSmrg		*next_is = scanFunc;
343f2e35a3aSmrg		target++;
344f2e35a3aSmrg		break;
345f2e35a3aSmrg	    case scanFunc:
346f2e35a3aSmrg		while ((ch = *target)) {
347f2e35a3aSmrg		    if (IsSpace(ch)) {
348f2e35a3aSmrg			break;
349f2e35a3aSmrg		    } else if (ch == L_PAREN) {
350f2e35a3aSmrg			*next_is = scanArgs;
351f2e35a3aSmrg			break;
352f2e35a3aSmrg		    }
353f2e35a3aSmrg		    target++;
354f2e35a3aSmrg		}
355f2e35a3aSmrg		break;
356f2e35a3aSmrg	    case scanArgs:
357f2e35a3aSmrg		while ((ch = *target)) {
358f2e35a3aSmrg		    if (ch == R_PAREN) {
359f2e35a3aSmrg			target++;
360f2e35a3aSmrg			*next_is = scanFunc;
361f2e35a3aSmrg			break;
362f2e35a3aSmrg		    }
363f2e35a3aSmrg		    target++;
364f2e35a3aSmrg		}
365f2e35a3aSmrg		break;
366f2e35a3aSmrg	    }
367f2e35a3aSmrg	    *last = (unsigned) (target - source);
368f2e35a3aSmrg	    if (*target == '\n') {
369f2e35a3aSmrg		*next_is = scanMods;
370f2e35a3aSmrg		target++;
371f2e35a3aSmrg	    }
372f2e35a3aSmrg	} while (*first == *last);
373f2e35a3aSmrg    }
374f2e35a3aSmrg    return target;
375f2e35a3aSmrg}
376f2e35a3aSmrg
377f2e35a3aSmrgvoid
378f2e35a3aSmrgxtermButtonInit(XtermWidget xw)
379f2e35a3aSmrg{
380f2e35a3aSmrg    Widget w = (Widget) xw;
381f2e35a3aSmrg    XErrorHandler save = XSetErrorHandler(ignore_x11_error);
382f2e35a3aSmrg    XtTranslations xlations;
383f2e35a3aSmrg    Widget xcelerat;
384f2e35a3aSmrg    String result;
385f2e35a3aSmrg
386f2e35a3aSmrg    XtVaGetValues(w,
387f2e35a3aSmrg		  XtNtranslations, &xlations,
388f2e35a3aSmrg		  XtNaccelerators, &xcelerat,
389f2e35a3aSmrg		  (XtPointer) 0);
390f2e35a3aSmrg    result = _XtPrintXlations(w, xlations, xcelerat, True);
391f2e35a3aSmrg    if (result) {
392f2e35a3aSmrg	static const char *table[] =
393f2e35a3aSmrg	{
394f2e35a3aSmrg	    "insert-selection",
395f2e35a3aSmrg	    "select-end",
396f2e35a3aSmrg	    "select-extend",
397f2e35a3aSmrg	    "select-start",
398f2e35a3aSmrg	    "start-extend",
399f2e35a3aSmrg	};
400f2e35a3aSmrg	char *data = x_strdup(result);
401f2e35a3aSmrg	char *next;
402f2e35a3aSmrg	int state = scanMods;
403f2e35a3aSmrg	int state2 = scanMods;
404f2e35a3aSmrg	unsigned first;
405f2e35a3aSmrg	unsigned last;
406f2e35a3aSmrg	int have_button = -1;
407f2e35a3aSmrg	Bool want_button = False;
408f2e35a3aSmrg	Bool have_shift = False;
409f2e35a3aSmrg	unsigned allowed = 0;
410f2e35a3aSmrg	unsigned disallow = 0;
411f2e35a3aSmrg
412f2e35a3aSmrg	TRACE(("xtermButtonInit length %ld\n", (long) strlen(result)));
413f2e35a3aSmrg	xw->keyboard.print_translations = data;
414f2e35a3aSmrg	while ((next = scanTrans(data, &state, &state2, &first, &last)) != 0) {
415f2e35a3aSmrg	    unsigned len = (last - first);
416f2e35a3aSmrg	    TRACE2(("parse %s:%d..%d '%.*s'\n",
417f2e35a3aSmrg		    visibleScan(state), first, last,
418f2e35a3aSmrg		    len, data + first));
419f2e35a3aSmrg	    if (state == scanMods) {
420f2e35a3aSmrg		if (len > 1 && data[first] == '~') {
421f2e35a3aSmrg		    len--;
422f2e35a3aSmrg		    first++;
423f2e35a3aSmrg		}
424f2e35a3aSmrg		if (len == 7 && !x_strncasecmp(data + first, "button", len - 1)) {
425f2e35a3aSmrg		    have_button = data[first + 6] - '0';
426f2e35a3aSmrg		} else if (len == 5 && !x_strncasecmp(data + first, "shift", len)) {
427f2e35a3aSmrg		    have_shift = True;
428f2e35a3aSmrg		}
429f2e35a3aSmrg	    } else if (state == scanKey) {
430f2e35a3aSmrg		if (!x_strncasecmp(data + first, "<buttonpress>", len) ||
431f2e35a3aSmrg		    !x_strncasecmp(data + first, "<buttonrelease>", len)) {
432f2e35a3aSmrg		    want_button = True;
433f2e35a3aSmrg		} else if (want_button) {
434f2e35a3aSmrg		    have_button = data[first] - '0';
435f2e35a3aSmrg		    want_button = False;
436f2e35a3aSmrg		}
437f2e35a3aSmrg	    } else if (state == scanFunc && have_button > 0) {
438f2e35a3aSmrg		Cardinal n;
439f2e35a3aSmrg		unsigned bmask = 1U << (have_button - 1);
440f2e35a3aSmrg		for (n = 0; n < XtNumber(table); ++n) {
441f2e35a3aSmrg		    if (!x_strncasecmp(table[n], data + first, len)) {
442f2e35a3aSmrg			TRACE(("...button %d: %s%s\n",
443f2e35a3aSmrg			       have_button, table[n],
444f2e35a3aSmrg			       have_shift ? " (disallow)" : ""));
445f2e35a3aSmrg			if (have_shift)
446f2e35a3aSmrg			    disallow |= bmask;
447f2e35a3aSmrg			else
448f2e35a3aSmrg			    allowed |= bmask;
449f2e35a3aSmrg			break;
450f2e35a3aSmrg		    }
451f2e35a3aSmrg		}
452f2e35a3aSmrg	    }
453f2e35a3aSmrg	    if (state2 == scanMods && state >= scanColon) {
454f2e35a3aSmrg		have_button = -1;
455f2e35a3aSmrg		want_button = False;
456f2e35a3aSmrg		have_shift = False;
457f2e35a3aSmrg	    }
458f2e35a3aSmrg	    state = state2;
459f2e35a3aSmrg	    data = next;
460f2e35a3aSmrg	}
461f2e35a3aSmrg	XFree((char *) result);
462f2e35a3aSmrg	xw->keyboard.shift_buttons = allowed & ~disallow;
463f2e35a3aSmrg#if OPT_TRACE
464f2e35a3aSmrg	if (xw->keyboard.shift_buttons) {
465f2e35a3aSmrg	    int button = 0;
466f2e35a3aSmrg	    unsigned mask = xw->keyboard.shift_buttons;
467f2e35a3aSmrg	    TRACE(("...Buttons used for selection that can be overridden:"));
468f2e35a3aSmrg	    while (mask != 0) {
469f2e35a3aSmrg		++button;
470f2e35a3aSmrg		if ((mask & 1) != 0)
471f2e35a3aSmrg		    TRACE((" %d", button));
472f2e35a3aSmrg		mask >>= 1;
473f2e35a3aSmrg	    }
474f2e35a3aSmrg	    TRACE(("\n"));
475f2e35a3aSmrg	} else {
476f2e35a3aSmrg	    TRACE(("...No buttons used with selection can be overridden\n"));
477f2e35a3aSmrg	}
478f2e35a3aSmrg#endif
479f2e35a3aSmrg    }
480f2e35a3aSmrg    XSetErrorHandler(save);
481f2e35a3aSmrg}
482f2e35a3aSmrg
483f2e35a3aSmrg/*
484f2e35a3aSmrg * Shift and control are regular X11 modifiers, but meta is not:
485f2e35a3aSmrg * + X10 (which had no xmodmap utility) had a meta mask, but X11 did not.
486f2e35a3aSmrg * + X11R1 introduced xmodmap, along with the current set of modifier masks.
487f2e35a3aSmrg *   The meta key has been assumed to be mod1 since X11R1.
488f2e35a3aSmrg *   The initial xterm logic in X11 was different, but gave the same result.
489f2e35a3aSmrg * + X11R2 modified xterm was to eliminate the X10 table which provided part of
490f2e35a3aSmrg *   the meta logic.
491f2e35a3aSmrg * + X11R3 modified Xt, making Meta_L and Meta_R assignable via xmodmap, and
492f2e35a3aSmrg *   equating Alt with Meta.  Neither Alt/Meta are modifiers, but Alt is more
493f2e35a3aSmrg *   likely to be on the keyboard.  This release also added keymap tables for
494f2e35a3aSmrg *   the server; Meta was used frequently in HP keymaps, which were the most
495f2e35a3aSmrg *   extensive set of keymaps.
496f2e35a3aSmrg * + X11R4 mentions Meta in the ICCCM, stating that if Meta_L or Meta_R are
497f2e35a3aSmrg *   found in the keysyms for a given modifier, that the client should use
498f2e35a3aSmrg *   that modifier.
499f2e35a3aSmrg *
500f2e35a3aSmrg * This function follows the ICCCM, picking the modifier which contains the
501f2e35a3aSmrg * Meta_L/Meta_R keysyms (if available), falling back to the Alt_L/Alt_R
502f2e35a3aSmrg * (as per X11R3), and ultimately to mod1 (per X11R1).
503f2e35a3aSmrg */
504f2e35a3aSmrgstatic unsigned
505f2e35a3aSmrgMetaMask(XtermWidget xw)
506f2e35a3aSmrg{
507f2e35a3aSmrg#if OPT_NUM_LOCK
508f2e35a3aSmrg    unsigned meta = xw->work.meta_mods;
509f2e35a3aSmrg    if (meta == 0)
510f2e35a3aSmrg	meta = xw->work.alt_mods;
511f2e35a3aSmrg    if (meta == 0)
512f2e35a3aSmrg	meta = Mod1Mask;
513f2e35a3aSmrg#else
514f2e35a3aSmrg    unsigned meta = Mod1Mask;
515f2e35a3aSmrg    (void) xw;
516f2e35a3aSmrg#endif
517f2e35a3aSmrg    return meta;
518f2e35a3aSmrg}
519f2e35a3aSmrg
520f2e35a3aSmrg/*
521f2e35a3aSmrg * Returns a mask of the modifiers we may use for modifying the mouse protocol
522f2e35a3aSmrg * response strings.
523f2e35a3aSmrg */
524f2e35a3aSmrgstatic unsigned
525f2e35a3aSmrgOurModifiers(XtermWidget xw)
526f2e35a3aSmrg{
527f2e35a3aSmrg    return (ShiftMask
528f2e35a3aSmrg	    | ControlMask
529f2e35a3aSmrg	    | MetaMask(xw));
530f2e35a3aSmrg}
531f2e35a3aSmrg
532f2e35a3aSmrg/*
533f2e35a3aSmrg * The actual check for the shift-mask, to see if it should tell xterm to
534f2e35a3aSmrg * override mouse-protocol in favor of select/paste actions depends upon
535f2e35a3aSmrg * whether the shiftEscape resource is set to true/always vs false/never.
536f2e35a3aSmrg */
537f2e35a3aSmrgstatic Boolean
538f2e35a3aSmrgShiftOverride(XtermWidget xw, unsigned state, int button)
539f2e35a3aSmrg{
540f2e35a3aSmrg    unsigned check = (state & OurModifiers(xw));
541f2e35a3aSmrg    Boolean result = False;
542f2e35a3aSmrg
543f2e35a3aSmrg    if (check & ShiftMask) {
544f2e35a3aSmrg	if (xw->keyboard.shift_escape == ssFalse ||
545f2e35a3aSmrg	    xw->keyboard.shift_escape == ssNever) {
546f2e35a3aSmrg	    result = True;
547f2e35a3aSmrg	} else if (xw->keyboard.shift_escape == ssTrue) {
548f2e35a3aSmrg	    /*
549f2e35a3aSmrg	     * Check if the button is one that we found does not directly use
550f2e35a3aSmrg	     * the shift-modifier in its bindings to select/copy actions.
551f2e35a3aSmrg	     */
552f2e35a3aSmrg	    if (button > 0 && button <= MaxMouseBtn) {
553f2e35a3aSmrg		if (xw->keyboard.shift_buttons & (1U << (button - 1))) {
554f2e35a3aSmrg		    result = True;
555f2e35a3aSmrg		}
556f2e35a3aSmrg	    } else {
557f2e35a3aSmrg		result = True;	/* unlikely, and we don't care */
558f2e35a3aSmrg	    }
559f2e35a3aSmrg	}
560f2e35a3aSmrg    }
561f2e35a3aSmrg    TRACE2(("ShiftOverride ( %#x -> %#x ) %d\n", state, check, result));
562f2e35a3aSmrg    return result;
563f2e35a3aSmrg}
564f2e35a3aSmrg
565f2e35a3aSmrg/*
566f2e35a3aSmrg * Normally xterm treats the shift-modifier specially when the mouse protocol
567f2e35a3aSmrg * is active.  The translations resource binds otherwise unmodified button
568f2e35a3aSmrg * for these mouse-related events:
569f2e35a3aSmrg *
570f2e35a3aSmrg *         ~Meta <Btn1Down>:select-start() \n\
571f2e35a3aSmrg *       ~Meta <Btn1Motion>:select-extend() \n\
572f2e35a3aSmrg *     ~Ctrl ~Meta <Btn2Up>:insert-selection(SELECT, CUT_BUFFER0) \n\
573f2e35a3aSmrg *   ~Ctrl ~Meta <Btn3Down>:start-extend() \n\
574f2e35a3aSmrg *       ~Meta <Btn3Motion>:select-extend() \n\
575f2e35a3aSmrg *                  <BtnUp>:select-end(SELECT, CUT_BUFFER0) \n\
576f2e35a3aSmrg *
577f2e35a3aSmrg * There is no API in the X libraries which would tell us if a given mouse
578f2e35a3aSmrg * button is bound to one of these actions.  These functions make the choice
579f2e35a3aSmrg * configurable.
580f2e35a3aSmrg */
581f2e35a3aSmrgstatic Bool
582f2e35a3aSmrgInterpretButton(XtermWidget xw, XButtonEvent *event)
583f2e35a3aSmrg{
584f2e35a3aSmrg    Bool result = False;
585f2e35a3aSmrg
586f2e35a3aSmrg    if (ShiftOverride(xw, event->state, (int) event->button)) {
587f2e35a3aSmrg	TRACE(("...shift-button #%d overrides mouse-protocol\n", event->button));
588f2e35a3aSmrg	result = True;
589f2e35a3aSmrg    }
590f2e35a3aSmrg    return result;
591f2e35a3aSmrg}
592f2e35a3aSmrg
593f2e35a3aSmrg#define Button1Index 8		/* X.h should have done this */
594f2e35a3aSmrg
595f2e35a3aSmrgstatic int
596f2e35a3aSmrgMotionButton(unsigned state)
597f2e35a3aSmrg{
598f2e35a3aSmrg    unsigned bmask = state >> Button1Index;
599f2e35a3aSmrg    int result = 1;
600f2e35a3aSmrg
601f2e35a3aSmrg    if (bmask != 0) {
602f2e35a3aSmrg	while (!(bmask & 1)) {
603f2e35a3aSmrg	    ++result;
604f2e35a3aSmrg	    bmask >>= 1;
605f2e35a3aSmrg	}
606f2e35a3aSmrg    }
607f2e35a3aSmrg    return result;
608f2e35a3aSmrg}
609f2e35a3aSmrg
610f2e35a3aSmrgstatic Bool
611f2e35a3aSmrgInterpretEvent(XtermWidget xw, XEvent *event)
612f2e35a3aSmrg{
613f2e35a3aSmrg    Bool result = False;	/* if not a button, is motion */
614f2e35a3aSmrg
615f2e35a3aSmrg    if (IsBtnEvent(event)) {
616f2e35a3aSmrg	result = InterpretButton(xw, (XButtonEvent *) event);
617f2e35a3aSmrg    } else if (event->type == MotionNotify) {
618f2e35a3aSmrg	unsigned state = event->xmotion.state;
619f2e35a3aSmrg	int button = MotionButton(state);
620f2e35a3aSmrg
621f2e35a3aSmrg	if (ShiftOverride(xw, state, button)) {
622f2e35a3aSmrg	    TRACE(("...shift-motion #%d (%d,%d) overrides mouse-protocol\n",
623f2e35a3aSmrg		   button,
624f2e35a3aSmrg		   event->xmotion.y,
625f2e35a3aSmrg		   event->xmotion.x));
626f2e35a3aSmrg	    result = True;
627f2e35a3aSmrg	}
628f2e35a3aSmrg    }
629f2e35a3aSmrg    return result;
630f2e35a3aSmrg}
631f2e35a3aSmrg
632f2e35a3aSmrg#define OverrideEvent(event)  InterpretEvent(xw, event)
633f2e35a3aSmrg#define OverrideButton(event) InterpretButton(xw, event)
634f2e35a3aSmrg
635f2e35a3aSmrg/*
636f2e35a3aSmrg * Returns true if we handled the event here, and nothing more is needed.
637f2e35a3aSmrg */
638492d43a5SmrgBool
639894e0ac8SmrgSendMousePosition(XtermWidget xw, XEvent *event)
640492d43a5Smrg{
641492d43a5Smrg    XButtonEvent *my_event = (XButtonEvent *) event;
642492d43a5Smrg    Bool result = False;
643d522f475Smrg
644913cc679Smrg    switch (okSendMousePos(xw)) {
645492d43a5Smrg    case MOUSE_OFF:
646492d43a5Smrg	/* If send_mouse_pos mode isn't on, we shouldn't be here */
647492d43a5Smrg	break;
648d522f475Smrg
649d522f475Smrg    case BTN_EVENT_MOUSE:
650d522f475Smrg    case ANY_EVENT_MOUSE:
651f2e35a3aSmrg	if (!OverrideEvent(event)) {
652492d43a5Smrg	    /* xterm extension for motion reporting. June 1998 */
653492d43a5Smrg	    /* EditorButton() will distinguish between the modes */
654492d43a5Smrg	    switch (event->type) {
655492d43a5Smrg	    case MotionNotify:
656492d43a5Smrg		my_event->button = 0;
657492d43a5Smrg		/* FALLTHRU */
658492d43a5Smrg	    case ButtonPress:
659492d43a5Smrg		/* FALLTHRU */
660492d43a5Smrg	    case ButtonRelease:
661492d43a5Smrg		EditorButton(xw, my_event);
662492d43a5Smrg		result = True;
663492d43a5Smrg		break;
6642eaa94a1Schristos	    }
665d522f475Smrg	}
666492d43a5Smrg	break;
667d522f475Smrg
668913cc679Smrg    case X10_MOUSE:		/* X10 compatibility sequences */
669492d43a5Smrg	if (IsBtnEvent(event)) {
670f2e35a3aSmrg	    if (!OverrideButton(my_event)) {
671913cc679Smrg		if (my_event->type == ButtonPress)
672492d43a5Smrg		    EditorButton(xw, my_event);
673913cc679Smrg		result = True;
674913cc679Smrg	    }
675913cc679Smrg	}
676913cc679Smrg	break;
677492d43a5Smrg
678913cc679Smrg    case VT200_HIGHLIGHT_MOUSE:	/* DEC vt200 hilite tracking */
679913cc679Smrg	if (IsBtnEvent(event)) {
680f2e35a3aSmrg	    if (!OverrideButton(my_event)) {
681f2e35a3aSmrg		if (my_event->type == ButtonPress &&
682f2e35a3aSmrg		    my_event->button == Button1) {
683f2e35a3aSmrg		    TrackDown(xw, my_event);
684f2e35a3aSmrg		} else {
685f2e35a3aSmrg		    EditorButton(xw, my_event);
686f2e35a3aSmrg		}
687913cc679Smrg		result = True;
688913cc679Smrg	    }
689913cc679Smrg	}
690913cc679Smrg	break;
691492d43a5Smrg
692913cc679Smrg    case VT200_MOUSE:		/* DEC vt200 compatible */
693913cc679Smrg	if (IsBtnEvent(event)) {
694f2e35a3aSmrg	    if (!OverrideButton(my_event)) {
695913cc679Smrg		EditorButton(xw, my_event);
696913cc679Smrg		result = True;
697913cc679Smrg	    }
698913cc679Smrg	}
699913cc679Smrg	break;
700913cc679Smrg
701913cc679Smrg    case DEC_LOCATOR:
702492d43a5Smrg#if OPT_DEC_LOCATOR
703f2e35a3aSmrg	if (IsBtnEvent(event) || event->type == MotionNotify) {
704913cc679Smrg	    result = SendLocatorPosition(xw, my_event);
705492d43a5Smrg	}
706f2e35a3aSmrg#endif /* OPT_DEC_LOCATOR */
707913cc679Smrg	break;
708d522f475Smrg    }
709492d43a5Smrg    return result;
710d522f475Smrg}
711d522f475Smrg
712d522f475Smrg#if OPT_DEC_LOCATOR
713d522f475Smrg
714d522f475Smrg#define	LocatorCoords( row, col, x, y, oor )			\
715d522f475Smrg    if( screen->locator_pixels ) {				\
716d522f475Smrg	(oor)=False; (row) = (y)+1; (col) = (x)+1;		\
717d522f475Smrg	/* Limit to screen dimensions */			\
718d522f475Smrg	if ((row) < 1) (row) = 1,(oor)=True;			\
719d522f475Smrg	else if ((row) > screen->border*2+Height(screen))	\
720d522f475Smrg	    (row) = screen->border*2+Height(screen),(oor)=True;	\
721d522f475Smrg	if ((col) < 1) (col) = 1,(oor)=True;			\
722d522f475Smrg	else if ((col) > OriginX(screen)*2+Width(screen))	\
723d522f475Smrg	    (col) = OriginX(screen)*2+Width(screen),(oor)=True;	\
724d522f475Smrg    } else {							\
725d522f475Smrg	(oor)=False;						\
726d522f475Smrg	/* Compute character position of mouse pointer */	\
727d522f475Smrg	(row) = ((y) - screen->border) / FontHeight(screen);	\
728d522f475Smrg	(col) = ((x) - OriginX(screen)) / FontWidth(screen);	\
729d522f475Smrg	/* Limit to screen dimensions */			\
730d522f475Smrg	if ((row) < 0) (row) = 0,(oor)=True;			\
731d522f475Smrg	else if ((row) > screen->max_row)			\
732d522f475Smrg	    (row) = screen->max_row,(oor)=True;			\
733d522f475Smrg	if ((col) < 0) (col) = 0,(oor)=True;			\
734d522f475Smrg	else if ((col) > screen->max_col)			\
735d522f475Smrg	    (col) = screen->max_col,(oor)=True;			\
736d522f475Smrg	(row)++; (col)++;					\
737d522f475Smrg    }
738d522f475Smrg
739d522f475Smrgstatic Bool
740894e0ac8SmrgSendLocatorPosition(XtermWidget xw, XButtonEvent *event)
741d522f475Smrg{
742d522f475Smrg    ANSI reply;
743956cc18dSsnj    TScreen *screen = TScreenOf(xw);
744d522f475Smrg    int row, col;
745d522f475Smrg    Bool oor;
746d522f475Smrg    int button;
7472eaa94a1Schristos    unsigned state;
748d522f475Smrg
749d522f475Smrg    /* Make sure the event is an appropriate type */
750f2e35a3aSmrg    if (IsBtnEvent(event)) {
751f2e35a3aSmrg	if (OverrideButton(event))
752f2e35a3aSmrg	    return (False);
753f2e35a3aSmrg    } else {
754f2e35a3aSmrg	if (!screen->loc_filter)
755f2e35a3aSmrg	    return (False);
756f2e35a3aSmrg    }
757d522f475Smrg
758d522f475Smrg    if ((event->type == ButtonPress &&
759d522f475Smrg	 !(screen->locator_events & LOC_BTNS_DN)) ||
760d522f475Smrg	(event->type == ButtonRelease &&
761d522f475Smrg	 !(screen->locator_events & LOC_BTNS_UP)))
762d522f475Smrg	return (True);
763d522f475Smrg
764d522f475Smrg    if (event->type == MotionNotify) {
765d522f475Smrg	CheckLocatorPosition(xw, event);
766d522f475Smrg	return (True);
767d522f475Smrg    }
768d522f475Smrg
769d522f475Smrg    /* get button # */
770492d43a5Smrg    button = (int) event->button - 1;
771d522f475Smrg
772492d43a5Smrg    LocatorCoords(row, col, event->x, event->y, oor);
773d522f475Smrg
774d522f475Smrg    /*
775d522f475Smrg     * DECterm mouse:
776d522f475Smrg     *
777d522f475Smrg     * ESCAPE '[' event ; mask ; row ; column '&' 'w'
778d522f475Smrg     */
779d522f475Smrg    memset(&reply, 0, sizeof(reply));
780d522f475Smrg    reply.a_type = ANSI_CSI;
781d522f475Smrg
782d522f475Smrg    if (oor) {
783d522f475Smrg	reply.a_nparam = 1;
784d522f475Smrg	reply.a_param[0] = 0;	/* Event - 0 = locator unavailable */
785d522f475Smrg	reply.a_inters = '&';
786d522f475Smrg	reply.a_final = 'w';
787d522f475Smrg	unparseseq(xw, &reply);
788d522f475Smrg
789d522f475Smrg	if (screen->locator_reset) {
790d522f475Smrg	    MotionOff(screen, xw);
791d522f475Smrg	    screen->send_mouse_pos = MOUSE_OFF;
792d522f475Smrg	}
793d522f475Smrg	return (True);
794d522f475Smrg    }
795d522f475Smrg
796d522f475Smrg    /*
797d522f475Smrg     * event:
798d522f475Smrg     *        1       no buttons
799d522f475Smrg     *        2       left button down
800d522f475Smrg     *        3       left button up
801d522f475Smrg     *        4       middle button down
802d522f475Smrg     *        5       middle button up
803d522f475Smrg     *        6       right button down
804d522f475Smrg     *        7       right button up
805d522f475Smrg     *        8       M4 down
806d522f475Smrg     *        9       M4 up
807d522f475Smrg     */
808d522f475Smrg    reply.a_nparam = 4;
809d522f475Smrg    switch (event->type) {
810d522f475Smrg    case ButtonPress:
8112eaa94a1Schristos	reply.a_param[0] = (ParmType) (2 + (button << 1));
812d522f475Smrg	break;
813d522f475Smrg    case ButtonRelease:
8142eaa94a1Schristos	reply.a_param[0] = (ParmType) (3 + (button << 1));
815d522f475Smrg	break;
816d522f475Smrg    default:
817d522f475Smrg	return (True);
818d522f475Smrg    }
819d522f475Smrg    /*
820d522f475Smrg     * mask:
821913cc679Smrg     * bit7   bit6   bit5   bit4   bit3     bit2       bit1         bit0
822913cc679Smrg     *                             M4 down  left down  middle down  right down
823d522f475Smrg     *
824d522f475Smrg     * Notice that Button1 (left) and Button3 (right) are swapped in the mask.
825d522f475Smrg     * Also, mask should be the state after the button press/release,
826d522f475Smrg     * X provides the state not including the button press/release.
827d522f475Smrg     */
828492d43a5Smrg    state = (event->state
829d522f475Smrg	     & (Button1Mask | Button2Mask | Button3Mask | Button4Mask)) >> 8;
8304e40088cSchristos    /* update mask to "after" state */
83120d2c4d2Smrg    state ^= ((unsigned) (1 << button));
8324e40088cSchristos    /* swap Button1 & Button3 */
833956cc18dSsnj    state = ((state & (unsigned) ~(4 | 1))
834956cc18dSsnj	     | ((state & 1) ? 4 : 0)
835956cc18dSsnj	     | ((state & 4) ? 1 : 0));
836d522f475Smrg
8372eaa94a1Schristos    reply.a_param[1] = (ParmType) state;
8382eaa94a1Schristos    reply.a_param[2] = (ParmType) row;
8392eaa94a1Schristos    reply.a_param[3] = (ParmType) col;
840d522f475Smrg    reply.a_inters = '&';
841d522f475Smrg    reply.a_final = 'w';
842d522f475Smrg
843d522f475Smrg    unparseseq(xw, &reply);
844d522f475Smrg
845d522f475Smrg    if (screen->locator_reset) {
846d522f475Smrg	MotionOff(screen, xw);
847d522f475Smrg	screen->send_mouse_pos = MOUSE_OFF;
848d522f475Smrg    }
849d522f475Smrg
850d522f475Smrg    /*
851913cc679Smrg     * DECterm turns the Locator off if a button is pressed while a filter
852913cc679Smrg     * rectangle is active.  This might be a bug, but I don't know, so I'll
853913cc679Smrg     * emulate it anyway.
854d522f475Smrg     */
855d522f475Smrg    if (screen->loc_filter) {
856d522f475Smrg	screen->send_mouse_pos = MOUSE_OFF;
857d522f475Smrg	screen->loc_filter = False;
858d522f475Smrg	screen->locator_events = 0;
859d522f475Smrg	MotionOff(screen, xw);
860d522f475Smrg    }
861d522f475Smrg
862d522f475Smrg    return (True);
863d522f475Smrg}
864d522f475Smrg
865d522f475Smrg/*
866d522f475Smrg * mask:
867d522f475Smrg * bit 7   bit 6   bit 5   bit 4   bit 3   bit 2       bit 1         bit 0
868d522f475Smrg *                                 M4 down left down   middle down   right down
869d522f475Smrg *
870d522f475Smrg * Button1 (left) and Button3 (right) are swapped in the mask relative to X.
871d522f475Smrg */
872d522f475Smrg#define	ButtonState(state, mask)	\
873913cc679Smrg{ int stemp = (int) (((mask) & (Button1Mask | Button2Mask | Button3Mask | Button4Mask)) >> 8);	\
874d522f475Smrg  /* swap Button1 & Button3 */								\
875913cc679Smrg  (state) = (stemp & ~(4|1)) | ((stemp & 1) ? 4 : 0) | ((stemp & 4) ? 1 : 0);			\
876d522f475Smrg}
877d522f475Smrg
878d522f475Smrgvoid
879d522f475SmrgGetLocatorPosition(XtermWidget xw)
880d522f475Smrg{
881d522f475Smrg    ANSI reply;
882956cc18dSsnj    TScreen *screen = TScreenOf(xw);
883d522f475Smrg    Window root, child;
884d522f475Smrg    int rx, ry, x, y;
885f2e35a3aSmrg    unsigned int mask = 0;
886d522f475Smrg    int row = 0, col = 0;
887d522f475Smrg    Bool oor = False;
888d522f475Smrg    Bool ret = False;
889d522f475Smrg    int state;
890d522f475Smrg
891d522f475Smrg    /*
892913cc679Smrg     * DECterm turns the Locator off if the position is requested while a
893913cc679Smrg     * filter rectangle is active.  This might be a bug, but I don't know, so
894913cc679Smrg     * I'll emulate it anyways.
895d522f475Smrg     */
896d522f475Smrg    if (screen->loc_filter) {
897d522f475Smrg	screen->send_mouse_pos = MOUSE_OFF;
898d522f475Smrg	screen->loc_filter = False;
899d522f475Smrg	screen->locator_events = 0;
900d522f475Smrg	MotionOff(screen, xw);
901d522f475Smrg    }
902d522f475Smrg
903d522f475Smrg    memset(&reply, 0, sizeof(reply));
904d522f475Smrg    reply.a_type = ANSI_CSI;
905d522f475Smrg
906913cc679Smrg    if (okSendMousePos(xw) == DEC_LOCATOR) {
907d522f475Smrg	ret = XQueryPointer(screen->display, VWindow(screen), &root,
908d522f475Smrg			    &child, &rx, &ry, &x, &y, &mask);
909d522f475Smrg	if (ret) {
910d522f475Smrg	    LocatorCoords(row, col, x, y, oor);
911d522f475Smrg	}
912d522f475Smrg    }
913d522f475Smrg    if (ret == False || oor) {
914d522f475Smrg	reply.a_nparam = 1;
915d522f475Smrg	reply.a_param[0] = 0;	/* Event - 0 = locator unavailable */
916d522f475Smrg	reply.a_inters = '&';
917d522f475Smrg	reply.a_final = 'w';
918d522f475Smrg	unparseseq(xw, &reply);
919d522f475Smrg
920d522f475Smrg	if (screen->locator_reset) {
921d522f475Smrg	    MotionOff(screen, xw);
922d522f475Smrg	    screen->send_mouse_pos = MOUSE_OFF;
923d522f475Smrg	}
924d522f475Smrg	return;
925d522f475Smrg    }
926d522f475Smrg
927d522f475Smrg    ButtonState(state, mask);
928d522f475Smrg
929d522f475Smrg    reply.a_nparam = 4;
930d522f475Smrg    reply.a_param[0] = 1;	/* Event - 1 = response to locator request */
9312eaa94a1Schristos    reply.a_param[1] = (ParmType) state;
9322eaa94a1Schristos    reply.a_param[2] = (ParmType) row;
9332eaa94a1Schristos    reply.a_param[3] = (ParmType) col;
934d522f475Smrg    reply.a_inters = '&';
935d522f475Smrg    reply.a_final = 'w';
936d522f475Smrg    unparseseq(xw, &reply);
937d522f475Smrg
938d522f475Smrg    if (screen->locator_reset) {
939d522f475Smrg	MotionOff(screen, xw);
940d522f475Smrg	screen->send_mouse_pos = MOUSE_OFF;
941d522f475Smrg    }
942d522f475Smrg}
943d522f475Smrg
944d522f475Smrgvoid
945d522f475SmrgInitLocatorFilter(XtermWidget xw)
946d522f475Smrg{
947d522f475Smrg    ANSI reply;
948956cc18dSsnj    TScreen *screen = TScreenOf(xw);
949d522f475Smrg    Window root, child;
950d522f475Smrg    int rx, ry, x, y;
951d522f475Smrg    unsigned int mask;
952d522f475Smrg    int row = 0, col = 0;
953d522f475Smrg    Bool oor = 0;
954d522f475Smrg    Bool ret;
955d522f475Smrg
956d522f475Smrg    ret = XQueryPointer(screen->display, VWindow(screen),
957d522f475Smrg			&root, &child, &rx, &ry, &x, &y, &mask);
958d522f475Smrg    if (ret) {
959d522f475Smrg	LocatorCoords(row, col, x, y, oor);
960d522f475Smrg    }
961d522f475Smrg    if (ret == False || oor) {
962d522f475Smrg	/* Locator is unavailable */
963d522f475Smrg
964d522f475Smrg	if (screen->loc_filter_top != LOC_FILTER_POS ||
965d522f475Smrg	    screen->loc_filter_left != LOC_FILTER_POS ||
966d522f475Smrg	    screen->loc_filter_bottom != LOC_FILTER_POS ||
967d522f475Smrg	    screen->loc_filter_right != LOC_FILTER_POS) {
968d522f475Smrg	    /*
969d522f475Smrg	     * If any explicit coordinates were received,
970d522f475Smrg	     * report immediately with no coordinates.
971d522f475Smrg	     */
972d522f475Smrg	    memset(&reply, 0, sizeof(reply));
973d522f475Smrg	    reply.a_type = ANSI_CSI;
974d522f475Smrg	    reply.a_nparam = 1;
975d522f475Smrg	    reply.a_param[0] = 0;	/* Event - 0 = locator unavailable */
976d522f475Smrg	    reply.a_inters = '&';
977d522f475Smrg	    reply.a_final = 'w';
978d522f475Smrg	    unparseseq(xw, &reply);
979d522f475Smrg
980d522f475Smrg	    if (screen->locator_reset) {
981d522f475Smrg		MotionOff(screen, xw);
982d522f475Smrg		screen->send_mouse_pos = MOUSE_OFF;
983d522f475Smrg	    }
984d522f475Smrg	} else {
985d522f475Smrg	    /*
986d522f475Smrg	     * No explicit coordinates were received, and the pointer is
987d522f475Smrg	     * unavailable.  Report when the pointer re-enters the window.
988d522f475Smrg	     */
989d522f475Smrg	    screen->loc_filter = True;
990d522f475Smrg	    MotionOn(screen, xw);
991d522f475Smrg	}
992d522f475Smrg	return;
993d522f475Smrg    }
994d522f475Smrg
995d522f475Smrg    /*
996d522f475Smrg     * Adjust rectangle coordinates:
997d522f475Smrg     *  1. Replace "LOC_FILTER_POS" with current coordinates
998d522f475Smrg     *  2. Limit coordinates to screen size
999d522f475Smrg     *  3. make sure top and left are less than bottom and right, resp.
1000d522f475Smrg     */
1001d522f475Smrg    if (screen->locator_pixels) {
1002d522f475Smrg	rx = OriginX(screen) * 2 + Width(screen);
1003d522f475Smrg	ry = screen->border * 2 + Height(screen);
1004d522f475Smrg    } else {
1005d522f475Smrg	rx = screen->max_col;
1006d522f475Smrg	ry = screen->max_row;
1007d522f475Smrg    }
1008d522f475Smrg
1009d522f475Smrg#define	Adjust( coord, def, max )				\
1010d522f475Smrg	if( (coord) == LOC_FILTER_POS )	(coord) = (def);	\
1011d522f475Smrg	else if ((coord) < 1)		(coord) = 1;		\
1012d522f475Smrg	else if ((coord) > (max))	(coord) = (max)
1013d522f475Smrg
1014d522f475Smrg    Adjust(screen->loc_filter_top, row, ry);
1015d522f475Smrg    Adjust(screen->loc_filter_left, col, rx);
1016d522f475Smrg    Adjust(screen->loc_filter_bottom, row, ry);
1017d522f475Smrg    Adjust(screen->loc_filter_right, col, rx);
1018d522f475Smrg
1019d522f475Smrg    if (screen->loc_filter_top > screen->loc_filter_bottom) {
1020d522f475Smrg	ry = screen->loc_filter_top;
1021d522f475Smrg	screen->loc_filter_top = screen->loc_filter_bottom;
1022d522f475Smrg	screen->loc_filter_bottom = ry;
1023d522f475Smrg    }
1024d522f475Smrg
1025d522f475Smrg    if (screen->loc_filter_left > screen->loc_filter_right) {
1026d522f475Smrg	rx = screen->loc_filter_left;
1027d522f475Smrg	screen->loc_filter_left = screen->loc_filter_right;
1028d522f475Smrg	screen->loc_filter_right = rx;
1029d522f475Smrg    }
1030d522f475Smrg
1031d522f475Smrg    if ((col < screen->loc_filter_left) ||
1032d522f475Smrg	(col > screen->loc_filter_right) ||
1033d522f475Smrg	(row < screen->loc_filter_top) ||
1034d522f475Smrg	(row > screen->loc_filter_bottom)) {
10352e4f8982Smrg	int state;
10362e4f8982Smrg
1037d522f475Smrg	/* Pointer is already outside the rectangle - report immediately */
1038d522f475Smrg	ButtonState(state, mask);
1039d522f475Smrg
1040d522f475Smrg	memset(&reply, 0, sizeof(reply));
1041d522f475Smrg	reply.a_type = ANSI_CSI;
1042d522f475Smrg	reply.a_nparam = 4;
1043d522f475Smrg	reply.a_param[0] = 10;	/* Event - 10 = locator outside filter */
10442eaa94a1Schristos	reply.a_param[1] = (ParmType) state;
10452eaa94a1Schristos	reply.a_param[2] = (ParmType) row;
10462eaa94a1Schristos	reply.a_param[3] = (ParmType) col;
1047d522f475Smrg	reply.a_inters = '&';
1048d522f475Smrg	reply.a_final = 'w';
1049d522f475Smrg	unparseseq(xw, &reply);
1050d522f475Smrg
1051d522f475Smrg	if (screen->locator_reset) {
1052d522f475Smrg	    MotionOff(screen, xw);
1053d522f475Smrg	    screen->send_mouse_pos = MOUSE_OFF;
1054d522f475Smrg	}
1055d522f475Smrg	return;
1056d522f475Smrg    }
1057d522f475Smrg
1058d522f475Smrg    /*
1059d522f475Smrg     * Rectangle is set up.  Allow pointer tracking
1060d522f475Smrg     * to detect if the mouse leaves the rectangle.
1061d522f475Smrg     */
1062d522f475Smrg    screen->loc_filter = True;
1063d522f475Smrg    MotionOn(screen, xw);
1064d522f475Smrg}
1065d522f475Smrg
1066d522f475Smrgstatic void
1067894e0ac8SmrgCheckLocatorPosition(XtermWidget xw, XButtonEvent *event)
1068d522f475Smrg{
1069d522f475Smrg    ANSI reply;
1070956cc18dSsnj    TScreen *screen = TScreenOf(xw);
1071d522f475Smrg    int row, col;
1072d522f475Smrg    Bool oor;
1073d522f475Smrg
1074492d43a5Smrg    LocatorCoords(row, col, event->x, event->y, oor);
1075d522f475Smrg
1076d522f475Smrg    /*
1077d522f475Smrg     * Send report if the pointer left the filter rectangle, if
1078d522f475Smrg     * the pointer left the window, or if the filter rectangle
1079d522f475Smrg     * had no coordinates and the pointer re-entered the window.
1080d522f475Smrg     */
1081d522f475Smrg    if (oor || (screen->loc_filter_top == LOC_FILTER_POS) ||
1082d522f475Smrg	(col < screen->loc_filter_left) ||
1083d522f475Smrg	(col > screen->loc_filter_right) ||
1084d522f475Smrg	(row < screen->loc_filter_top) ||
1085d522f475Smrg	(row > screen->loc_filter_bottom)) {
1086d522f475Smrg	/* Filter triggered - disable it */
1087d522f475Smrg	screen->loc_filter = False;
1088d522f475Smrg	MotionOff(screen, xw);
1089d522f475Smrg
1090d522f475Smrg	memset(&reply, 0, sizeof(reply));
1091d522f475Smrg	reply.a_type = ANSI_CSI;
1092d522f475Smrg	if (oor) {
1093d522f475Smrg	    reply.a_nparam = 1;
1094d522f475Smrg	    reply.a_param[0] = 0;	/* Event - 0 = locator unavailable */
1095d522f475Smrg	} else {
10962e4f8982Smrg	    int state;
10972e4f8982Smrg
1098492d43a5Smrg	    ButtonState(state, event->state);
1099d522f475Smrg
1100d522f475Smrg	    reply.a_nparam = 4;
1101d522f475Smrg	    reply.a_param[0] = 10;	/* Event - 10 = locator outside filter */
11022eaa94a1Schristos	    reply.a_param[1] = (ParmType) state;
11032eaa94a1Schristos	    reply.a_param[2] = (ParmType) row;
11042eaa94a1Schristos	    reply.a_param[3] = (ParmType) col;
1105d522f475Smrg	}
1106d522f475Smrg
1107d522f475Smrg	reply.a_inters = '&';
1108d522f475Smrg	reply.a_final = 'w';
1109d522f475Smrg	unparseseq(xw, &reply);
1110d522f475Smrg
1111d522f475Smrg	if (screen->locator_reset) {
1112d522f475Smrg	    MotionOff(screen, xw);
1113d522f475Smrg	    screen->send_mouse_pos = MOUSE_OFF;
1114d522f475Smrg	}
1115d522f475Smrg    }
1116d522f475Smrg}
1117d522f475Smrg#endif /* OPT_DEC_LOCATOR */
1118d522f475Smrg
1119d522f475Smrg#if OPT_READLINE
1120d522f475Smrgstatic int
1121913cc679SmrgisClick1_clean(XtermWidget xw, XButtonEvent *event)
1122d522f475Smrg{
1123913cc679Smrg    TScreen *screen = TScreenOf(xw);
1124d522f475Smrg    int delta;
1125d522f475Smrg
1126d522f475Smrg    /* Disable on Shift-Click-1, including the application-mouse modes */
1127f2e35a3aSmrg    if (OverrideButton(event)
1128f2e35a3aSmrg	|| (okSendMousePos(xw) != MOUSE_OFF)
1129f2e35a3aSmrg	|| ExtendingSelection)	/* Was moved */
1130d522f475Smrg	return 0;
1131d522f475Smrg
1132d522f475Smrg    if (event->type != ButtonRelease)
1133d522f475Smrg	return 0;
1134d522f475Smrg
1135d522f475Smrg    if (lastButtonDownTime == (Time) 0) {
1136d522f475Smrg	/* first time or once in a blue moon */
1137d522f475Smrg	delta = screen->multiClickTime + 1;
1138492d43a5Smrg    } else if (event->time > lastButtonDownTime) {
1139d522f475Smrg	/* most of the time */
1140492d43a5Smrg	delta = (int) (event->time - lastButtonDownTime);
1141d522f475Smrg    } else {
1142d522f475Smrg	/* time has rolled over since lastButtonUpTime */
1143492d43a5Smrg	delta = (int) ((((Time) ~ 0) - lastButtonDownTime) + event->time);
1144d522f475Smrg    }
1145d522f475Smrg
1146d522f475Smrg    return delta <= screen->multiClickTime;
1147d522f475Smrg}
1148d522f475Smrg
1149d522f475Smrgstatic int
1150f2e35a3aSmrgisDoubleClick3(XtermWidget xw, TScreen *screen, XButtonEvent *event)
1151d522f475Smrg{
1152d522f475Smrg    int delta;
1153d522f475Smrg
1154d522f475Smrg    if (event->type != ButtonRelease
1155f2e35a3aSmrg	|| OverrideButton(event)
1156492d43a5Smrg	|| event->button != Button3) {
1157d522f475Smrg	lastButton3UpTime = 0;	/* Disable the cached info */
1158d522f475Smrg	return 0;
1159d522f475Smrg    }
1160d522f475Smrg    /* Process Btn3Release. */
1161d522f475Smrg    if (lastButton3DoubleDownTime == (Time) 0) {
1162d522f475Smrg	/* No previous click or once in a blue moon */
1163d522f475Smrg	delta = screen->multiClickTime + 1;
1164492d43a5Smrg    } else if (event->time > lastButton3DoubleDownTime) {
1165d522f475Smrg	/* most of the time */
1166492d43a5Smrg	delta = (int) (event->time - lastButton3DoubleDownTime);
1167d522f475Smrg    } else {
1168d522f475Smrg	/* time has rolled over since lastButton3DoubleDownTime */
1169492d43a5Smrg	delta = (int) ((((Time) ~ 0) - lastButton3DoubleDownTime) + event->time);
1170d522f475Smrg    }
1171d522f475Smrg    if (delta <= screen->multiClickTime) {
1172d522f475Smrg	/* Double click */
1173d522f475Smrg	CELL cell;
1174d522f475Smrg
1175d522f475Smrg	/* Cannot check ExtendingSelection, since mouse-3 always sets it */
1176492d43a5Smrg	PointToCELL(screen, event->y, event->x, &cell);
1177d522f475Smrg	if (isSameCELL(&cell, &lastButton3)) {
1178d522f475Smrg	    lastButton3DoubleDownTime = 0;	/* Disable the third click */
1179d522f475Smrg	    return 1;
1180d522f475Smrg	}
1181d522f475Smrg    }
1182d522f475Smrg    /* Not a double click, memorize for future check. */
1183492d43a5Smrg    lastButton3UpTime = event->time;
1184492d43a5Smrg    PointToCELL(screen, event->y, event->x, &lastButton3);
1185d522f475Smrg    return 0;
1186d522f475Smrg}
1187d522f475Smrg
1188d522f475Smrgstatic int
1189f2e35a3aSmrgCheckSecondPress3(XtermWidget xw, TScreen *screen, XEvent *event)
1190d522f475Smrg{
1191d522f475Smrg    int delta;
1192d522f475Smrg
1193d522f475Smrg    if (event->type != ButtonPress
1194f2e35a3aSmrg	|| OverrideEvent(event)
1195d522f475Smrg	|| event->xbutton.button != Button3) {
1196d522f475Smrg	lastButton3DoubleDownTime = 0;	/* Disable the cached info */
1197d522f475Smrg	return 0;
1198d522f475Smrg    }
1199d522f475Smrg    /* Process Btn3Press. */
1200d522f475Smrg    if (lastButton3UpTime == (Time) 0) {
1201d522f475Smrg	/* No previous click or once in a blue moon */
1202d522f475Smrg	delta = screen->multiClickTime + 1;
1203d522f475Smrg    } else if (event->xbutton.time > lastButton3UpTime) {
1204d522f475Smrg	/* most of the time */
12052eaa94a1Schristos	delta = (int) (event->xbutton.time - lastButton3UpTime);
1206d522f475Smrg    } else {
1207d522f475Smrg	/* time has rolled over since lastButton3UpTime */
12082eaa94a1Schristos	delta = (int) ((((Time) ~ 0) - lastButton3UpTime) + event->xbutton.time);
1209d522f475Smrg    }
1210d522f475Smrg    if (delta <= screen->multiClickTime) {
1211d522f475Smrg	CELL cell;
1212d522f475Smrg
1213d522f475Smrg	PointToCELL(screen, event->xbutton.y, event->xbutton.x, &cell);
1214d522f475Smrg	if (isSameCELL(&cell, &lastButton3)) {
1215d522f475Smrg	    /* A candidate for a double-click */
1216d522f475Smrg	    lastButton3DoubleDownTime = event->xbutton.time;
1217d522f475Smrg	    PointToCELL(screen, event->xbutton.y, event->xbutton.x, &lastButton3);
1218d522f475Smrg	    return 1;
1219d522f475Smrg	}
1220d522f475Smrg	lastButton3UpTime = 0;	/* Disable the info about the previous click */
1221d522f475Smrg    }
1222d522f475Smrg    /* Either too long, or moved, disable. */
1223d522f475Smrg    lastButton3DoubleDownTime = 0;
1224d522f475Smrg    return 0;
1225d522f475Smrg}
1226d522f475Smrg
1227d522f475Smrgstatic int
1228e0a2b6dfSmrgrowOnCurrentLine(TScreen *screen,
1229d522f475Smrg		 int line,
1230d522f475Smrg		 int *deltap)	/* must be XButtonEvent */
1231d522f475Smrg{
1232956cc18dSsnj    int result = 1;
1233d522f475Smrg
1234d522f475Smrg    *deltap = 0;
12352e4f8982Smrg
1236956cc18dSsnj    if (line != screen->cur_row) {
12372e4f8982Smrg	int l1, l2;
12382e4f8982Smrg
1239f2e35a3aSmrg	if (line < screen->cur_row) {
1240f2e35a3aSmrg	    l1 = line;
1241f2e35a3aSmrg	    l2 = screen->cur_row;
1242f2e35a3aSmrg	} else {
1243f2e35a3aSmrg	    l2 = line;
1244f2e35a3aSmrg	    l1 = screen->cur_row;
1245f2e35a3aSmrg	}
1246956cc18dSsnj	l1--;
1247956cc18dSsnj	while (++l1 < l2) {
1248956cc18dSsnj	    LineData *ld = GET_LINEDATA(screen, l1);
1249956cc18dSsnj	    if (!LineTstWrapped(ld)) {
1250956cc18dSsnj		result = 0;
1251956cc18dSsnj		break;
1252956cc18dSsnj	    }
1253956cc18dSsnj	}
1254956cc18dSsnj	if (result) {
1255956cc18dSsnj	    /* Everything is on one "wrapped line" now */
1256956cc18dSsnj	    *deltap = line - screen->cur_row;
1257956cc18dSsnj	}
1258956cc18dSsnj    }
1259956cc18dSsnj    return result;
1260d522f475Smrg}
1261d522f475Smrg
1262d522f475Smrgstatic int
1263894e0ac8SmrgeventRow(TScreen *screen, XEvent *event)	/* must be XButtonEvent */
1264d522f475Smrg{
1265d522f475Smrg    return (event->xbutton.y - screen->border) / FontHeight(screen);
1266d522f475Smrg}
1267d522f475Smrg
1268d522f475Smrgstatic int
1269894e0ac8SmrgeventColBetween(TScreen *screen, XEvent *event)		/* must be XButtonEvent */
1270d522f475Smrg{
1271d522f475Smrg    /* Correct by half a width - we are acting on a boundary, not on a cell. */
1272d522f475Smrg    return ((event->xbutton.x - OriginX(screen) + (FontWidth(screen) - 1) / 2)
1273d522f475Smrg	    / FontWidth(screen));
1274d522f475Smrg}
1275d522f475Smrg
1276d522f475Smrgstatic int
1277e0a2b6dfSmrgReadLineMovePoint(TScreen *screen, int col, int ldelta)
1278d522f475Smrg{
1279d522f475Smrg    Char line[6];
1280d522f475Smrg    unsigned count = 0;
1281d522f475Smrg
1282d522f475Smrg    col += ldelta * MaxCols(screen) - screen->cur_col;
1283d522f475Smrg    if (col == 0)
1284d522f475Smrg	return 0;
1285d522f475Smrg    if (screen->control_eight_bits) {
1286d522f475Smrg	line[count++] = ANSI_CSI;
1287d522f475Smrg    } else {
1288d522f475Smrg	line[count++] = ANSI_ESC;
1289d522f475Smrg	line[count++] = '[';	/* XXX maybe sometimes O is better? */
1290d522f475Smrg    }
129120d2c4d2Smrg    line[count] = CharOf(col > 0 ? 'C' : 'D');
1292d522f475Smrg    if (col < 0)
1293d522f475Smrg	col = -col;
1294d522f475Smrg    while (col--)
1295d522f475Smrg	v_write(screen->respond, line, 3);
1296d522f475Smrg    return 1;
1297d522f475Smrg}
1298d522f475Smrg
1299d522f475Smrgstatic int
1300e0a2b6dfSmrgReadLineDelete(TScreen *screen, CELL *cell1, CELL *cell2)
1301d522f475Smrg{
1302d522f475Smrg    int del;
1303d522f475Smrg
1304d522f475Smrg    del = (cell2->col - cell1->col) + ((cell2->row - cell1->row) * MaxCols(screen));
1305d522f475Smrg    if (del <= 0)		/* Just in case... */
1306d522f475Smrg	return 0;
1307d522f475Smrg    while (del--)
1308492d43a5Smrg	v_write(screen->respond, (const Char *) "\177", 1);
1309d522f475Smrg    return 1;
1310d522f475Smrg}
1311492d43a5Smrg
1312492d43a5Smrgstatic void
1313913cc679SmrgreadlineExtend(XtermWidget xw, XEvent *event)
1314492d43a5Smrg{
1315913cc679Smrg    TScreen *screen = TScreenOf(xw);
1316492d43a5Smrg    int ldelta1, ldelta2;
1317492d43a5Smrg
1318492d43a5Smrg    if (IsBtnEvent(event)) {
1319492d43a5Smrg	XButtonEvent *my_event = (XButtonEvent *) event;
1320913cc679Smrg	if (isClick1_clean(xw, my_event)
1321492d43a5Smrg	    && SCREEN_FLAG(screen, click1_moves)
1322492d43a5Smrg	    && rowOnCurrentLine(screen, eventRow(screen, event), &ldelta1)) {
1323492d43a5Smrg	    ReadLineMovePoint(screen, eventColBetween(screen, event), ldelta1);
1324492d43a5Smrg	}
1325f2e35a3aSmrg	if (isDoubleClick3(xw, screen, my_event)
1326492d43a5Smrg	    && SCREEN_FLAG(screen, dclick3_deletes)
1327492d43a5Smrg	    && rowOnCurrentLine(screen, screen->startSel.row, &ldelta1)
1328492d43a5Smrg	    && rowOnCurrentLine(screen, screen->endSel.row, &ldelta2)) {
1329492d43a5Smrg	    ReadLineMovePoint(screen, screen->endSel.col, ldelta2);
1330492d43a5Smrg	    ReadLineDelete(screen, &screen->startSel, &(screen->endSel));
1331492d43a5Smrg	}
1332492d43a5Smrg    }
1333492d43a5Smrg}
1334d522f475Smrg#endif /* OPT_READLINE */
1335d522f475Smrg
1336d522f475Smrg/* ^XM-G<line+' '><col+' '> */
1337d522f475Smrgvoid
1338d522f475SmrgDiredButton(Widget w,
1339894e0ac8Smrg	    XEvent *event,	/* must be XButtonEvent */
1340e0a2b6dfSmrg	    String *params GCC_UNUSED,	/* selections */
1341d522f475Smrg	    Cardinal *num_params GCC_UNUSED)
1342d522f475Smrg{
1343956cc18dSsnj    XtermWidget xw;
1344956cc18dSsnj
1345956cc18dSsnj    if ((xw = getXtermWidget(w)) != 0) {
1346956cc18dSsnj	TScreen *screen = TScreenOf(xw);
1347d522f475Smrg
1348492d43a5Smrg	if (IsBtnEvent(event)
13492eaa94a1Schristos	    && (event->xbutton.y >= screen->border)
13502eaa94a1Schristos	    && (event->xbutton.x >= OriginX(screen))) {
13512e4f8982Smrg	    Char Line[6];
13522e4f8982Smrg	    unsigned line, col;
13532e4f8982Smrg
135420d2c4d2Smrg	    line = (unsigned) ((event->xbutton.y - screen->border)
135520d2c4d2Smrg			       / FontHeight(screen));
135620d2c4d2Smrg	    col = (unsigned) ((event->xbutton.x - OriginX(screen))
135720d2c4d2Smrg			      / FontWidth(screen));
1358d522f475Smrg	    Line[0] = CONTROL('X');
1359d522f475Smrg	    Line[1] = ANSI_ESC;
1360d522f475Smrg	    Line[2] = 'G';
13612eaa94a1Schristos	    Line[3] = CharOf(' ' + col);
13622eaa94a1Schristos	    Line[4] = CharOf(' ' + line);
1363d522f475Smrg	    v_write(screen->respond, Line, 5);
1364d522f475Smrg	}
1365d522f475Smrg    }
1366d522f475Smrg}
1367d522f475Smrg
1368d522f475Smrg#if OPT_READLINE
1369d522f475Smrgvoid
1370d522f475SmrgReadLineButton(Widget w,
1371894e0ac8Smrg	       XEvent *event,	/* must be XButtonEvent */
1372f2e35a3aSmrg	       String *params,	/* selections */
1373f2e35a3aSmrg	       Cardinal *num_params)
1374d522f475Smrg{
1375956cc18dSsnj    XtermWidget xw;
1376956cc18dSsnj
1377956cc18dSsnj    if ((xw = getXtermWidget(w)) != 0) {
1378956cc18dSsnj	TScreen *screen = TScreenOf(xw);
1379d522f475Smrg	Char Line[6];
1380d522f475Smrg	int line, col, ldelta = 0;
1381d522f475Smrg
1382492d43a5Smrg	if (!IsBtnEvent(event)
1383913cc679Smrg	    || (okSendMousePos(xw) != MOUSE_OFF) || ExtendingSelection)
1384d522f475Smrg	    goto finish;
1385d522f475Smrg	if (event->type == ButtonRelease) {
1386d522f475Smrg	    int delta;
1387d522f475Smrg
1388d522f475Smrg	    if (lastButtonDownTime == (Time) 0) {
1389d522f475Smrg		/* first time and once in a blue moon */
1390d522f475Smrg		delta = screen->multiClickTime + 1;
1391d522f475Smrg	    } else if (event->xbutton.time > lastButtonDownTime) {
1392d522f475Smrg		/* most of the time */
13932eaa94a1Schristos		delta = (int) (event->xbutton.time - lastButtonDownTime);
1394d522f475Smrg	    } else {
1395d522f475Smrg		/* time has rolled over since lastButtonUpTime */
13962eaa94a1Schristos		delta = (int) ((((Time) ~ 0) - lastButtonDownTime) + event->xbutton.time);
1397d522f475Smrg	    }
1398d522f475Smrg	    if (delta > screen->multiClickTime)
1399d522f475Smrg		goto finish;	/* All this work for this... */
1400d522f475Smrg	}
1401d522f475Smrg	line = (event->xbutton.y - screen->border) / FontHeight(screen);
1402956cc18dSsnj	if (!rowOnCurrentLine(screen, line, &ldelta))
1403956cc18dSsnj	    goto finish;
1404d522f475Smrg	/* Correct by half a width - we are acting on a boundary, not on a cell. */
1405d522f475Smrg	col = (event->xbutton.x - OriginX(screen) + (FontWidth(screen) - 1)
1406d522f475Smrg	       / 2)
1407d522f475Smrg	    / FontWidth(screen) - screen->cur_col + ldelta * MaxCols(screen);
1408d522f475Smrg	if (col == 0)
1409d522f475Smrg	    goto finish;
1410d522f475Smrg	Line[0] = ANSI_ESC;
1411d522f475Smrg	/* XXX: sometimes it is better to send '['? */
1412d522f475Smrg	Line[1] = 'O';
14132eaa94a1Schristos	Line[2] = CharOf(col > 0 ? 'C' : 'D');
1414d522f475Smrg	if (col < 0)
1415d522f475Smrg	    col = -col;
1416d522f475Smrg	while (col--)
1417d522f475Smrg	    v_write(screen->respond, Line, 3);
1418d522f475Smrg      finish:
1419d522f475Smrg	if (event->type == ButtonRelease)
1420d522f475Smrg	    do_select_end(xw, event, params, num_params, False);
1421d522f475Smrg    }
1422d522f475Smrg}
1423d522f475Smrg#endif /* OPT_READLINE */
1424d522f475Smrg
1425d522f475Smrg/* repeats <ESC>n or <ESC>p */
1426d522f475Smrgvoid
1427d522f475SmrgViButton(Widget w,
1428894e0ac8Smrg	 XEvent *event,		/* must be XButtonEvent */
1429e0a2b6dfSmrg	 String *params GCC_UNUSED,	/* selections */
1430d522f475Smrg	 Cardinal *num_params GCC_UNUSED)
1431d522f475Smrg{
1432956cc18dSsnj    XtermWidget xw;
1433956cc18dSsnj
1434956cc18dSsnj    if ((xw = getXtermWidget(w)) != 0) {
1435956cc18dSsnj	TScreen *screen = TScreenOf(xw);
1436d522f475Smrg	int pty = screen->respond;
1437d522f475Smrg
1438492d43a5Smrg	if (IsBtnEvent(event)) {
14392e4f8982Smrg	    int line;
1440d522f475Smrg
1441d522f475Smrg	    line = screen->cur_row -
1442d522f475Smrg		((event->xbutton.y - screen->border) / FontHeight(screen));
14432e4f8982Smrg
1444d522f475Smrg	    if (line != 0) {
14452e4f8982Smrg		Char Line[6];
14462e4f8982Smrg
1447d522f475Smrg		Line[0] = ANSI_ESC;	/* force an exit from insert-mode */
1448d522f475Smrg		v_write(pty, Line, 1);
1449d522f475Smrg
1450d522f475Smrg		if (line < 0) {
1451d522f475Smrg		    line = -line;
1452d522f475Smrg		    Line[0] = CONTROL('n');
1453d522f475Smrg		} else {
1454d522f475Smrg		    Line[0] = CONTROL('p');
1455d522f475Smrg		}
1456d522f475Smrg		while (--line >= 0)
1457d522f475Smrg		    v_write(pty, Line, 1);
1458d522f475Smrg	    }
1459d522f475Smrg	}
1460d522f475Smrg    }
1461d522f475Smrg}
1462d522f475Smrg
1463d522f475Smrg/*
1464d522f475Smrg * This function handles button-motion events
1465d522f475Smrg */
1466d522f475Smrg/*ARGSUSED*/
1467d522f475Smrgvoid
1468d522f475SmrgHandleSelectExtend(Widget w,
1469894e0ac8Smrg		   XEvent *event,	/* must be XMotionEvent */
1470e0a2b6dfSmrg		   String *params GCC_UNUSED,
1471d522f475Smrg		   Cardinal *num_params GCC_UNUSED)
1472d522f475Smrg{
1473956cc18dSsnj    XtermWidget xw;
1474956cc18dSsnj
1475956cc18dSsnj    if ((xw = getXtermWidget(w)) != 0) {
1476956cc18dSsnj	TScreen *screen = TScreenOf(xw);
1477d522f475Smrg	CELL cell;
1478d522f475Smrg
1479f2e35a3aSmrg	TRACE_EVENT("HandleSelectExtend", event, params, num_params);
14800bd37d32Smrg
1481d522f475Smrg	screen->selection_time = event->xmotion.time;
1482d522f475Smrg	switch (screen->eventMode) {
1483d522f475Smrg	    /* If not in one of the DEC mouse-reporting modes */
1484d522f475Smrg	case LEFTEXTENSION:
1485d522f475Smrg	case RIGHTEXTENSION:
1486d522f475Smrg	    PointToCELL(screen, event->xmotion.y, event->xmotion.x, &cell);
1487d522f475Smrg	    ExtendExtend(xw, &cell);
1488d522f475Smrg	    break;
1489d522f475Smrg
1490d522f475Smrg	    /* If in motion reporting mode, send mouse position to
1491d522f475Smrg	       character process as a key sequence \E[M... */
1492d522f475Smrg	case NORMAL:
1493d522f475Smrg	    /* will get here if send_mouse_pos != MOUSE_OFF */
1494913cc679Smrg	    if (okSendMousePos(xw) == BTN_EVENT_MOUSE
1495913cc679Smrg		|| okSendMousePos(xw) == ANY_EVENT_MOUSE) {
1496d522f475Smrg		(void) SendMousePosition(xw, event);
1497d522f475Smrg	    }
1498d522f475Smrg	    break;
1499d522f475Smrg	}
1500d522f475Smrg    }
1501d522f475Smrg}
1502d522f475Smrg
1503d522f475Smrgvoid
1504d522f475SmrgHandleKeyboardSelectExtend(Widget w,
1505894e0ac8Smrg			   XEvent *event GCC_UNUSED,	/* must be XButtonEvent */
1506e0a2b6dfSmrg			   String *params GCC_UNUSED,
1507d522f475Smrg			   Cardinal *num_params GCC_UNUSED)
1508d522f475Smrg{
1509956cc18dSsnj    XtermWidget xw;
1510956cc18dSsnj
1511956cc18dSsnj    if ((xw = getXtermWidget(w)) != 0) {
1512956cc18dSsnj	TScreen *screen = TScreenOf(xw);
15130bd37d32Smrg
1514f2e35a3aSmrg	TRACE_EVENT("HandleKeyboardSelectExtend", event, params, num_params);
1515d522f475Smrg	ExtendExtend(xw, &screen->cursorp);
1516d522f475Smrg    }
1517d522f475Smrg}
1518d522f475Smrg
1519d522f475Smrgstatic void
1520d522f475Smrgdo_select_end(XtermWidget xw,
1521894e0ac8Smrg	      XEvent *event,	/* must be XButtonEvent */
1522e0a2b6dfSmrg	      String *params,	/* selections */
1523d522f475Smrg	      Cardinal *num_params,
1524d522f475Smrg	      Bool use_cursor_loc)
1525d522f475Smrg{
1526956cc18dSsnj    TScreen *screen = TScreenOf(xw);
1527d522f475Smrg
1528d522f475Smrg    screen->selection_time = event->xbutton.time;
1529f2e35a3aSmrg
1530f2e35a3aSmrg    TRACE(("do_select_end %s @%ld\n",
1531f2e35a3aSmrg	   visibleEventMode(screen->eventMode),
1532f2e35a3aSmrg	   screen->selection_time));
1533f2e35a3aSmrg
1534d522f475Smrg    switch (screen->eventMode) {
1535d522f475Smrg    case NORMAL:
1536d522f475Smrg	(void) SendMousePosition(xw, event);
1537d522f475Smrg	break;
1538d522f475Smrg    case LEFTEXTENSION:
1539d522f475Smrg    case RIGHTEXTENSION:
1540d522f475Smrg	EndExtend(xw, event, params, *num_params, use_cursor_loc);
1541d522f475Smrg#if OPT_READLINE
1542913cc679Smrg	readlineExtend(xw, event);
1543d522f475Smrg#endif /* OPT_READLINE */
1544d522f475Smrg	break;
1545d522f475Smrg    }
1546d522f475Smrg}
1547d522f475Smrg
1548d522f475Smrgvoid
1549d522f475SmrgHandleSelectEnd(Widget w,
1550894e0ac8Smrg		XEvent *event,	/* must be XButtonEvent */
1551e0a2b6dfSmrg		String *params,	/* selections */
1552d522f475Smrg		Cardinal *num_params)
1553d522f475Smrg{
1554956cc18dSsnj    XtermWidget xw;
1555956cc18dSsnj
1556956cc18dSsnj    if ((xw = getXtermWidget(w)) != 0) {
15570bd37d32Smrg	TRACE(("HandleSelectEnd\n"));
1558956cc18dSsnj	do_select_end(xw, event, params, num_params, False);
1559956cc18dSsnj    }
1560d522f475Smrg}
1561d522f475Smrg
1562d522f475Smrgvoid
1563d522f475SmrgHandleKeyboardSelectEnd(Widget w,
1564894e0ac8Smrg			XEvent *event,	/* must be XButtonEvent */
1565e0a2b6dfSmrg			String *params,		/* selections */
1566d522f475Smrg			Cardinal *num_params)
1567d522f475Smrg{
1568956cc18dSsnj    XtermWidget xw;
1569956cc18dSsnj
1570956cc18dSsnj    if ((xw = getXtermWidget(w)) != 0) {
15710bd37d32Smrg	TRACE(("HandleKeyboardSelectEnd\n"));
1572956cc18dSsnj	do_select_end(xw, event, params, num_params, True);
1573956cc18dSsnj    }
1574d522f475Smrg}
1575d522f475Smrg
1576f2e35a3aSmrgvoid
1577f2e35a3aSmrgHandlePointerMotion(Widget w,
1578f2e35a3aSmrg		    XEvent *event,
1579f2e35a3aSmrg		    String *params,	/* selections */
1580f2e35a3aSmrg		    Cardinal *num_params)
1581f2e35a3aSmrg{
1582f2e35a3aSmrg    XtermWidget xw;
1583f2e35a3aSmrg
1584f2e35a3aSmrg    (void) params;
1585f2e35a3aSmrg    (void) num_params;
1586f2e35a3aSmrg    if ((xw = getXtermWidget(w)) != 0) {
1587f2e35a3aSmrg	TRACE(("HandlePointerMotion\n"));
1588f2e35a3aSmrg	if (event->type == MotionNotify)
1589f2e35a3aSmrg	    (void) SendMousePosition(xw, event);
1590f2e35a3aSmrg    }
1591f2e35a3aSmrg}
1592f2e35a3aSmrg
1593f2e35a3aSmrgvoid
1594f2e35a3aSmrgHandlePointerButton(Widget w,
1595f2e35a3aSmrg		    XEvent *event,
1596f2e35a3aSmrg		    String *params,	/* selections */
1597f2e35a3aSmrg		    Cardinal *num_params)
1598f2e35a3aSmrg{
1599f2e35a3aSmrg    XtermWidget xw;
1600f2e35a3aSmrg
1601f2e35a3aSmrg    (void) params;
1602f2e35a3aSmrg    (void) num_params;
1603f2e35a3aSmrg    if ((xw = getXtermWidget(w)) != 0) {
1604f2e35a3aSmrg	TRACE(("HandlePointerButton\n"));
1605f2e35a3aSmrg	if (IsBtnEvent(event))
1606f2e35a3aSmrg	    (void) SendMousePosition(xw, event);
1607f2e35a3aSmrg    }
1608f2e35a3aSmrg}
1609f2e35a3aSmrg
1610492d43a5Smrg/*
16116879286fSmrg * Copy the selection data to the given target(s).
1612492d43a5Smrg */
1613492d43a5Smrgvoid
16146879286fSmrgHandleCopySelection(Widget w,
1615894e0ac8Smrg		    XEvent *event,
1616e0a2b6dfSmrg		    String *params,	/* list of targets */
16176879286fSmrg		    Cardinal *num_params)
1618492d43a5Smrg{
1619492d43a5Smrg    XtermWidget xw;
1620492d43a5Smrg
1621492d43a5Smrg    if ((xw = getXtermWidget(w)) != 0) {
1622f2e35a3aSmrg	TRACE_EVENT("HandleCopySelection", event, params, num_params);
16236879286fSmrg	SelectSet(xw, event, params, *num_params);
1624492d43a5Smrg    }
1625492d43a5Smrg}
1626492d43a5Smrg
1627d522f475Smrgstruct _SelectionList {
1628d522f475Smrg    String *params;
1629d522f475Smrg    Cardinal count;
1630d522f475Smrg    Atom *targets;
1631d522f475Smrg    Time time;
1632d522f475Smrg};
1633d522f475Smrg
1634d522f475Smrgstatic unsigned
1635d522f475SmrgDECtoASCII(unsigned ch)
1636d522f475Smrg{
1637d522f475Smrg    if (xtermIsDecGraphic(ch)) {
16382eaa94a1Schristos	ch = CharOf("###########+++++##-##++++|######"[ch]);
16392eaa94a1Schristos	/*           01234567890123456789012345678901 */
1640d522f475Smrg    }
1641d522f475Smrg    return ch;
1642d522f475Smrg}
164320d2c4d2Smrg
164420d2c4d2Smrg#if OPT_WIDE_CHARS
164520d2c4d2Smrgstatic Cardinal
1646e0a2b6dfSmrgaddXtermChar(Char **buffer, Cardinal *used, Cardinal offset, unsigned value)
164720d2c4d2Smrg{
164820d2c4d2Smrg    if (offset + 1 >= *used) {
164920d2c4d2Smrg	*used = 1 + (2 * (offset + 1));
165020d2c4d2Smrg	allocXtermChars(buffer, *used);
165120d2c4d2Smrg    }
165220d2c4d2Smrg    (*buffer)[offset++] = (Char) value;
165320d2c4d2Smrg    return offset;
165420d2c4d2Smrg}
165520d2c4d2Smrg#define AddChar(buffer, used, offset, value) \
165620d2c4d2Smrg	offset = addXtermChar(buffer, used, offset, (unsigned) value)
165720d2c4d2Smrg
1658d522f475Smrg/*
1659d522f475Smrg * Convert a UTF-8 string to Latin-1, replacing non Latin-1 characters by `#',
1660d522f475Smrg * or ASCII/Latin-1 equivalents for special cases.
1661d522f475Smrg */
1662d522f475Smrgstatic Char *
1663e0a2b6dfSmrgUTF8toLatin1(TScreen *screen, Char *s, unsigned long len, unsigned long *result)
1664d522f475Smrg{
1665d522f475Smrg    static Char *buffer;
1666956cc18dSsnj    static Cardinal used;
1667d522f475Smrg
166820d2c4d2Smrg    Cardinal offset = 0;
1669d522f475Smrg
167020d2c4d2Smrg    if (len != 0) {
1671d522f475Smrg	PtyData data;
1672d522f475Smrg
1673d522f475Smrg	fakePtyData(&data, s, s + len);
1674894e0ac8Smrg	while (decodeUtf8(screen, &data)) {
1675956cc18dSsnj	    Bool fails = False;
1676956cc18dSsnj	    Bool extra = False;
1677f2e35a3aSmrg	    IChar value;
1678f2e35a3aSmrg	    skipPtyData(&data, value);
1679d522f475Smrg	    if (value == UCS_REPL) {
1680956cc18dSsnj		fails = True;
1681d522f475Smrg	    } else if (value < 256) {
168220d2c4d2Smrg		AddChar(&buffer, &used, offset, CharOf(value));
1683d522f475Smrg	    } else {
1684f2e35a3aSmrg		unsigned eqv = ucs2dec(screen, value);
1685d522f475Smrg		if (xtermIsDecGraphic(eqv)) {
168620d2c4d2Smrg		    AddChar(&buffer, &used, offset, DECtoASCII(eqv));
1687d522f475Smrg		} else {
1688d522f475Smrg		    eqv = AsciiEquivs(value);
1689956cc18dSsnj		    if (eqv == value) {
1690956cc18dSsnj			fails = True;
1691956cc18dSsnj		    } else {
169220d2c4d2Smrg			AddChar(&buffer, &used, offset, eqv);
1693956cc18dSsnj		    }
1694956cc18dSsnj		    if (isWide((wchar_t) value))
1695956cc18dSsnj			extra = True;
1696956cc18dSsnj		}
1697956cc18dSsnj	    }
1698956cc18dSsnj
1699956cc18dSsnj	    /*
1700956cc18dSsnj	     * If we're not able to plug in a single-byte result, insert the
1701956cc18dSsnj	     * defaultString (which normally is a single "#", but could be
1702956cc18dSsnj	     * whatever the user wants).
1703956cc18dSsnj	     */
1704956cc18dSsnj	    if (fails) {
17052e4f8982Smrg		const Char *p;
17062e4f8982Smrg
1707492d43a5Smrg		for (p = (const Char *) screen->default_string; *p != '\0'; ++p) {
170820d2c4d2Smrg		    AddChar(&buffer, &used, offset, *p);
1709d522f475Smrg		}
1710d522f475Smrg	    }
1711956cc18dSsnj	    if (extra)
171220d2c4d2Smrg		AddChar(&buffer, &used, offset, ' ');
1713d522f475Smrg	}
171420d2c4d2Smrg	AddChar(&buffer, &used, offset, '\0');
171520d2c4d2Smrg	*result = (unsigned long) (offset - 1);
1716d522f475Smrg    } else {
1717d522f475Smrg	*result = 0;
1718d522f475Smrg    }
1719d522f475Smrg    return buffer;
1720d522f475Smrg}
172120d2c4d2Smrg
172220d2c4d2Smrgint
172320d2c4d2SmrgxtermUtf8ToTextList(XtermWidget xw,
172420d2c4d2Smrg		    XTextProperty * text_prop,
172520d2c4d2Smrg		    char ***text_list,
172620d2c4d2Smrg		    int *text_list_count)
172720d2c4d2Smrg{
172820d2c4d2Smrg    TScreen *screen = TScreenOf(xw);
172920d2c4d2Smrg    Display *dpy = screen->display;
173020d2c4d2Smrg    int rc = -1;
173120d2c4d2Smrg
173220d2c4d2Smrg    if (text_prop->format == 8
173320d2c4d2Smrg	&& (rc = Xutf8TextPropertyToTextList(dpy, text_prop,
173420d2c4d2Smrg					     text_list,
173520d2c4d2Smrg					     text_list_count)) >= 0) {
173620d2c4d2Smrg	if (*text_list != NULL && *text_list_count != 0) {
173720d2c4d2Smrg	    int i;
173820d2c4d2Smrg	    Char *data;
173920d2c4d2Smrg	    char **new_text_list, *tmp;
174020d2c4d2Smrg	    unsigned long size, new_size;
174120d2c4d2Smrg
174220d2c4d2Smrg	    TRACE(("xtermUtf8ToTextList size %d\n", *text_list_count));
174320d2c4d2Smrg
174420d2c4d2Smrg	    /*
174520d2c4d2Smrg	     * XLib StringList actually uses only two pointers, one for the
174620d2c4d2Smrg	     * list itself, and one for the data.  Pointer to the data is the
174720d2c4d2Smrg	     * first element of the list, the rest (if any) list elements point
174820d2c4d2Smrg	     * to the same memory block as the first element
174920d2c4d2Smrg	     */
175020d2c4d2Smrg	    new_size = 0;
175120d2c4d2Smrg	    for (i = 0; i < *text_list_count; ++i) {
175220d2c4d2Smrg		data = (Char *) (*text_list)[i];
175320d2c4d2Smrg		size = strlen((*text_list)[i]) + 1;
175420d2c4d2Smrg		(void) UTF8toLatin1(screen, data, size, &size);
175520d2c4d2Smrg		new_size += size + 1;
175620d2c4d2Smrg	    }
1757a1f3da82Smrg	    new_text_list = TypeXtMallocN(char *, *text_list_count);
175820d2c4d2Smrg	    new_text_list[0] = tmp = XtMalloc((Cardinal) new_size);
175920d2c4d2Smrg	    for (i = 0; i < (*text_list_count); ++i) {
176020d2c4d2Smrg		data = (Char *) (*text_list)[i];
176120d2c4d2Smrg		size = strlen((*text_list)[i]) + 1;
17620bd37d32Smrg		if ((data = UTF8toLatin1(screen, data, size, &size)) != 0) {
17630bd37d32Smrg		    memcpy(tmp, data, size + 1);
17640bd37d32Smrg		    new_text_list[i] = tmp;
17650bd37d32Smrg		    tmp += size + 1;
17660bd37d32Smrg		}
176720d2c4d2Smrg	    }
176820d2c4d2Smrg	    XFreeStringList((*text_list));
176920d2c4d2Smrg	    *text_list = new_text_list;
177020d2c4d2Smrg	} else {
177120d2c4d2Smrg	    rc = -1;
177220d2c4d2Smrg	}
177320d2c4d2Smrg    }
177420d2c4d2Smrg    return rc;
177520d2c4d2Smrg}
1776d522f475Smrg#endif /* OPT_WIDE_CHARS */
1777d522f475Smrg
1778956cc18dSsnjstatic char *
1779956cc18dSsnjparseItem(char *value, char *nextc)
1780d522f475Smrg{
1781956cc18dSsnj    char *nextp = value;
1782956cc18dSsnj    while (*nextp != '\0' && *nextp != ',') {
1783956cc18dSsnj	*nextp = x_toupper(*nextp);
1784956cc18dSsnj	++nextp;
1785956cc18dSsnj    }
1786956cc18dSsnj    *nextc = *nextp;
1787956cc18dSsnj    *nextp = '\0';
1788d522f475Smrg
1789956cc18dSsnj    return nextp;
1790956cc18dSsnj}
1791d522f475Smrg
1792956cc18dSsnj/*
1793956cc18dSsnj * All of the wanted strings are unique in the first character, so we can
1794956cc18dSsnj * use simple abbreviations.
1795956cc18dSsnj */
1796956cc18dSsnjstatic Bool
1797956cc18dSsnjsameItem(const char *actual, const char *wanted)
1798956cc18dSsnj{
1799956cc18dSsnj    Bool result = False;
1800956cc18dSsnj    size_t have = strlen(actual);
1801956cc18dSsnj    size_t need = strlen(wanted);
1802d522f475Smrg
1803956cc18dSsnj    if (have != 0 && have <= need) {
1804956cc18dSsnj	if (!strncmp(actual, wanted, have)) {
1805956cc18dSsnj	    TRACE(("...matched \"%s\"\n", wanted));
1806956cc18dSsnj	    result = True;
1807956cc18dSsnj	}
1808956cc18dSsnj    }
1809956cc18dSsnj
1810956cc18dSsnj    return result;
1811956cc18dSsnj}
1812956cc18dSsnj
1813956cc18dSsnj/*
1814956cc18dSsnj * Handle the eightBitSelectTypes or utf8SelectTypes resource values.
1815956cc18dSsnj */
1816956cc18dSsnjstatic Bool
1817894e0ac8SmrgoverrideTargets(Widget w, String value, Atom **resultp)
1818956cc18dSsnj{
1819956cc18dSsnj    Bool override = False;
1820956cc18dSsnj    XtermWidget xw;
1821956cc18dSsnj
1822956cc18dSsnj    if ((xw = getXtermWidget(w)) != 0) {
1823956cc18dSsnj	TScreen *screen = TScreenOf(xw);
1824956cc18dSsnj
182520d2c4d2Smrg	if (!IsEmpty(value)) {
1826492d43a5Smrg	    char *copied = x_strdup(value);
1827956cc18dSsnj	    if (copied != 0) {
1828956cc18dSsnj		Atom *result = 0;
1829956cc18dSsnj		Cardinal count = 1;
1830956cc18dSsnj		int n;
1831d522f475Smrg
1832956cc18dSsnj		TRACE(("decoding SelectTypes \"%s\"\n", value));
1833956cc18dSsnj		for (n = 0; copied[n] != '\0'; ++n) {
1834956cc18dSsnj		    if (copied[n] == ',')
1835956cc18dSsnj			++count;
1836956cc18dSsnj		}
1837a1f3da82Smrg		result = TypeXtMallocN(Atom, (2 * count) + 1);
1838956cc18dSsnj		if (result == NULL) {
1839956cc18dSsnj		    TRACE(("Couldn't allocate selection types\n"));
1840956cc18dSsnj		} else {
1841956cc18dSsnj		    char nextc = '?';
184220d2c4d2Smrg		    char *listp = (char *) copied;
1843956cc18dSsnj		    count = 0;
1844956cc18dSsnj		    do {
1845956cc18dSsnj			char *nextp = parseItem(listp, &nextc);
18460bd37d32Smrg			char *item = x_strtrim(listp);
18470bd37d32Smrg			size_t len = (item ? strlen(item) : 0);
1848956cc18dSsnj
1849956cc18dSsnj			if (len == 0) {
1850a1f3da82Smrg			    /* EMPTY */ ;
1851956cc18dSsnj			}
1852956cc18dSsnj#if OPT_WIDE_CHARS
18530bd37d32Smrg			else if (sameItem(item, "UTF8")) {
1854956cc18dSsnj			    result[count++] = XA_UTF8_STRING(XtDisplay(w));
1855956cc18dSsnj			}
1856956cc18dSsnj#endif
18570bd37d32Smrg			else if (sameItem(item, "I18N")) {
1858956cc18dSsnj			    if (screen->i18nSelections) {
1859956cc18dSsnj				result[count++] = XA_TEXT(XtDisplay(w));
1860956cc18dSsnj				result[count++] = XA_COMPOUND_TEXT(XtDisplay(w));
1861956cc18dSsnj			    }
18620bd37d32Smrg			} else if (sameItem(item, "TEXT")) {
1863956cc18dSsnj			    result[count++] = XA_TEXT(XtDisplay(w));
18640bd37d32Smrg			} else if (sameItem(item, "COMPOUND_TEXT")) {
1865956cc18dSsnj			    result[count++] = XA_COMPOUND_TEXT(XtDisplay(w));
18660bd37d32Smrg			} else if (sameItem(item, "STRING")) {
1867956cc18dSsnj			    result[count++] = XA_STRING;
1868956cc18dSsnj			}
1869956cc18dSsnj			*nextp++ = nextc;
1870956cc18dSsnj			listp = nextp;
18710bd37d32Smrg			free(item);
1872956cc18dSsnj		    } while (nextc != '\0');
1873956cc18dSsnj		    if (count) {
1874956cc18dSsnj			result[count] = None;
1875956cc18dSsnj			override = True;
1876956cc18dSsnj			*resultp = result;
1877956cc18dSsnj		    } else {
1878956cc18dSsnj			XtFree((char *) result);
1879956cc18dSsnj		    }
1880956cc18dSsnj		}
18810bd37d32Smrg		free(copied);
1882956cc18dSsnj	    } else {
1883956cc18dSsnj		TRACE(("Couldn't allocate copy of selection types\n"));
1884d522f475Smrg	    }
1885956cc18dSsnj	}
1886956cc18dSsnj    }
1887956cc18dSsnj    return override;
1888956cc18dSsnj}
1889956cc18dSsnj
1890956cc18dSsnj#if OPT_WIDE_CHARS
1891956cc18dSsnjstatic Atom *
1892e0a2b6dfSmrgallocUtf8Targets(Widget w, TScreen *screen)
1893956cc18dSsnj{
1894956cc18dSsnj    Atom **resultp = &(screen->selection_targets_utf8);
1895956cc18dSsnj
1896956cc18dSsnj    if (*resultp == 0) {
1897956cc18dSsnj	Atom *result;
1898956cc18dSsnj
1899956cc18dSsnj	if (!overrideTargets(w, screen->utf8_select_types, &result)) {
1900a1f3da82Smrg	    result = TypeXtMallocN(Atom, 5);
1901956cc18dSsnj	    if (result == NULL) {
1902956cc18dSsnj		TRACE(("Couldn't allocate utf-8 selection targets\n"));
1903956cc18dSsnj	    } else {
1904956cc18dSsnj		int n = 0;
1905956cc18dSsnj
1906e39b573cSmrg		if (XSupportsLocale()) {
1907e39b573cSmrg		    result[n++] = XA_UTF8_STRING(XtDisplay(w));
1908d522f475Smrg#ifdef X_HAVE_UTF8_STRING
1909e39b573cSmrg		    if (screen->i18nSelections) {
1910e39b573cSmrg			result[n++] = XA_TEXT(XtDisplay(w));
1911e39b573cSmrg			result[n++] = XA_COMPOUND_TEXT(XtDisplay(w));
1912e39b573cSmrg		    }
1913d522f475Smrg#endif
1914e39b573cSmrg		}
1915956cc18dSsnj		result[n++] = XA_STRING;
1916956cc18dSsnj		result[n] = None;
1917956cc18dSsnj	    }
1918d522f475Smrg	}
1919956cc18dSsnj
1920956cc18dSsnj	*resultp = result;
1921d522f475Smrg    }
1922956cc18dSsnj
1923956cc18dSsnj    return *resultp;
1924956cc18dSsnj}
1925d522f475Smrg#endif
1926d522f475Smrg
1927956cc18dSsnjstatic Atom *
1928e0a2b6dfSmrgalloc8bitTargets(Widget w, TScreen *screen)
1929956cc18dSsnj{
1930956cc18dSsnj    Atom **resultp = &(screen->selection_targets_8bit);
1931956cc18dSsnj
1932956cc18dSsnj    if (*resultp == 0) {
1933956cc18dSsnj	Atom *result = 0;
1934956cc18dSsnj
1935956cc18dSsnj	if (!overrideTargets(w, screen->eightbit_select_types, &result)) {
1936a1f3da82Smrg	    result = TypeXtMallocN(Atom, 5);
1937956cc18dSsnj	    if (result == NULL) {
1938956cc18dSsnj		TRACE(("Couldn't allocate 8bit selection targets\n"));
1939956cc18dSsnj	    } else {
1940956cc18dSsnj		int n = 0;
1941956cc18dSsnj
1942e39b573cSmrg		if (XSupportsLocale()) {
1943d522f475Smrg#ifdef X_HAVE_UTF8_STRING
1944e39b573cSmrg		    result[n++] = XA_UTF8_STRING(XtDisplay(w));
1945956cc18dSsnj#endif
1946e39b573cSmrg		    if (screen->i18nSelections) {
1947e39b573cSmrg			result[n++] = XA_TEXT(XtDisplay(w));
1948e39b573cSmrg			result[n++] = XA_COMPOUND_TEXT(XtDisplay(w));
1949e39b573cSmrg		    }
1950956cc18dSsnj		}
1951956cc18dSsnj		result[n++] = XA_STRING;
1952956cc18dSsnj		result[n] = None;
1953956cc18dSsnj	    }
1954956cc18dSsnj	}
1955956cc18dSsnj
1956956cc18dSsnj	*resultp = result;
1957956cc18dSsnj    }
1958956cc18dSsnj
1959956cc18dSsnj    return *resultp;
1960956cc18dSsnj}
1961956cc18dSsnj
1962956cc18dSsnjstatic Atom *
1963956cc18dSsnj_SelectionTargets(Widget w)
1964956cc18dSsnj{
1965956cc18dSsnj    Atom *result;
1966956cc18dSsnj    XtermWidget xw;
1967956cc18dSsnj
1968956cc18dSsnj    if ((xw = getXtermWidget(w)) == 0) {
1969956cc18dSsnj	result = NULL;
1970956cc18dSsnj    } else {
19712e4f8982Smrg	TScreen *screen = TScreenOf(xw);
1972956cc18dSsnj
1973956cc18dSsnj#if OPT_WIDE_CHARS
1974956cc18dSsnj	if (screen->wide_chars) {
1975956cc18dSsnj	    result = allocUtf8Targets(w, screen);
1976956cc18dSsnj	} else
1977d522f475Smrg#endif
1978956cc18dSsnj	{
1979956cc18dSsnj	    /* not screen->wide_chars */
1980956cc18dSsnj	    result = alloc8bitTargets(w, screen);
1981d522f475Smrg	}
1982d522f475Smrg    }
1983956cc18dSsnj
1984956cc18dSsnj    return result;
1985d522f475Smrg}
1986d522f475Smrg
1987d522f475Smrg#define isSELECT(value) (!strcmp(value, "SELECT"))
1988d522f475Smrg
1989f2e35a3aSmrgstatic int
1990f2e35a3aSmrgDefaultSelection(TScreen *screen)
1991f2e35a3aSmrg{
1992f2e35a3aSmrg    return (screen->selectToClipboard ? 1 : 0);
1993f2e35a3aSmrg}
1994f2e35a3aSmrg
1995f2e35a3aSmrgstatic int
1996f2e35a3aSmrgTargetToSelection(TScreen *screen, String name)
1997f2e35a3aSmrg{
1998f2e35a3aSmrg    int result = -1;
1999f2e35a3aSmrg    int cutb;
2000f2e35a3aSmrg
2001f2e35a3aSmrg    if (isSELECT(name)) {
2002f2e35a3aSmrg	result = DefaultSelection(screen);
2003f2e35a3aSmrg    } else if (!strcmp(name, PRIMARY_NAME)) {
2004f2e35a3aSmrg	result = PRIMARY_CODE;
2005f2e35a3aSmrg    } else if (!strcmp(name, CLIPBOARD_NAME)) {
2006f2e35a3aSmrg	result = CLIPBOARD_CODE;
2007f2e35a3aSmrg    } else if (!strcmp(name, SECONDARY_NAME)) {
2008f2e35a3aSmrg	result = SECONDARY_CODE;
2009f2e35a3aSmrg    } else if (sscanf(name, "CUT_BUFFER%d", &cutb) == 1) {
2010f2e35a3aSmrg	if (cutb >= 0 && cutb < MAX_CUT_BUFFER) {
2011f2e35a3aSmrg	    result = CutBufferToCode(cutb);
2012f2e35a3aSmrg	} else {
2013f2e35a3aSmrg	    xtermWarning("unexpected cut-buffer code: %d\n", cutb);
2014f2e35a3aSmrg	}
2015f2e35a3aSmrg    } else {
2016f2e35a3aSmrg	xtermWarning("unexpected selection target: %s\n", name);
2017f2e35a3aSmrg    }
2018f2e35a3aSmrg    TRACE2(("TargetToSelection(%s) ->%d\n", name, result));
2019f2e35a3aSmrg    return result;
2020f2e35a3aSmrg}
2021f2e35a3aSmrg
2022f2e35a3aSmrgvoid
2023d522f475SmrgUnmapSelections(XtermWidget xw)
2024d522f475Smrg{
2025956cc18dSsnj    TScreen *screen = TScreenOf(xw);
2026d522f475Smrg    Cardinal n;
2027d522f475Smrg
2028d522f475Smrg    if (screen->mappedSelect) {
2029d522f475Smrg	for (n = 0; screen->mappedSelect[n] != 0; ++n)
203020d2c4d2Smrg	    free((void *) screen->mappedSelect[n]);
2031f2e35a3aSmrg	FreeAndNull(screen->mappedSelect);
2032d522f475Smrg    }
2033d522f475Smrg}
2034d522f475Smrg
2035d522f475Smrg/*
2036d522f475Smrg * xterm generally uses the primary selection.  Some applications prefer
2037d522f475Smrg * (or are limited to) the clipboard.  Since the translations resource is
2038d522f475Smrg * complicated, users seldom change the way it affects selection.  But it
2039d522f475Smrg * is simple to remap the choice between primary and clipboard before the
2040d522f475Smrg * call to XmuInternStrings().
2041d522f475Smrg */
2042d522f475Smrgstatic String *
2043e0a2b6dfSmrgMapSelections(XtermWidget xw, String *params, Cardinal num_params)
2044d522f475Smrg{
2045d522f475Smrg    String *result = params;
2046d522f475Smrg
2047913cc679Smrg    if (params != 0 && num_params > 0) {
2048d522f475Smrg	Cardinal j;
2049d522f475Smrg	Boolean map = False;
2050d522f475Smrg
2051d522f475Smrg	for (j = 0; j < num_params; ++j) {
2052d522f475Smrg	    TRACE(("param[%d]:%s\n", j, params[j]));
2053d522f475Smrg	    if (isSELECT(params[j])) {
2054d522f475Smrg		map = True;
2055d522f475Smrg		break;
2056d522f475Smrg	    }
2057d522f475Smrg	}
2058d522f475Smrg	if (map) {
2059956cc18dSsnj	    TScreen *screen = TScreenOf(xw);
2060956cc18dSsnj	    const char *mapTo = (screen->selectToClipboard
2061f2e35a3aSmrg				 ? CLIPBOARD_NAME
2062f2e35a3aSmrg				 : PRIMARY_NAME);
2063d522f475Smrg
2064d522f475Smrg	    UnmapSelections(xw);
2065d522f475Smrg	    if ((result = TypeMallocN(String, num_params + 1)) != 0) {
2066d522f475Smrg		result[num_params] = 0;
2067d522f475Smrg		for (j = 0; j < num_params; ++j) {
2068d522f475Smrg		    result[j] = x_strdup((isSELECT(params[j])
2069d522f475Smrg					  ? mapTo
2070d522f475Smrg					  : params[j]));
2071d522f475Smrg		    if (result[j] == 0) {
2072d522f475Smrg			UnmapSelections(xw);
20730bd37d32Smrg			while (j != 0) {
20740bd37d32Smrg			    free((void *) result[--j]);
20750bd37d32Smrg			}
2076f2e35a3aSmrg			FreeAndNull(result);
2077d522f475Smrg			break;
2078d522f475Smrg		    }
2079d522f475Smrg		}
2080956cc18dSsnj		screen->mappedSelect = result;
2081d522f475Smrg	    }
2082d522f475Smrg	}
2083d522f475Smrg    }
2084d522f475Smrg    return result;
2085d522f475Smrg}
2086d522f475Smrg
2087d522f475Smrg/*
2088d522f475Smrg * Lookup the cut-buffer number, which will be in the range 0-7.
2089f2e35a3aSmrg * If it is not a cut-buffer, it is a type of selection, e.g., primary.
2090d522f475Smrg */
2091d522f475Smrgstatic int
209220d2c4d2SmrgCutBuffer(Atom code)
2093d522f475Smrg{
2094d522f475Smrg    int cutbuffer;
209520d2c4d2Smrg    switch ((unsigned) code) {
2096d522f475Smrg    case XA_CUT_BUFFER0:
2097d522f475Smrg	cutbuffer = 0;
2098d522f475Smrg	break;
2099d522f475Smrg    case XA_CUT_BUFFER1:
2100d522f475Smrg	cutbuffer = 1;
2101d522f475Smrg	break;
2102d522f475Smrg    case XA_CUT_BUFFER2:
2103d522f475Smrg	cutbuffer = 2;
2104d522f475Smrg	break;
2105d522f475Smrg    case XA_CUT_BUFFER3:
2106d522f475Smrg	cutbuffer = 3;
2107d522f475Smrg	break;
2108d522f475Smrg    case XA_CUT_BUFFER4:
2109d522f475Smrg	cutbuffer = 4;
2110d522f475Smrg	break;
2111d522f475Smrg    case XA_CUT_BUFFER5:
2112d522f475Smrg	cutbuffer = 5;
2113d522f475Smrg	break;
2114d522f475Smrg    case XA_CUT_BUFFER6:
2115d522f475Smrg	cutbuffer = 6;
2116d522f475Smrg	break;
2117d522f475Smrg    case XA_CUT_BUFFER7:
2118d522f475Smrg	cutbuffer = 7;
2119d522f475Smrg	break;
2120d522f475Smrg    default:
2121d522f475Smrg	cutbuffer = -1;
2122d522f475Smrg	break;
2123d522f475Smrg    }
2124f2e35a3aSmrg    TRACE2(("CutBuffer(%d) = %d\n", (int) code, cutbuffer));
2125d522f475Smrg    return cutbuffer;
2126d522f475Smrg}
2127d522f475Smrg
2128d522f475Smrg#if OPT_PASTE64
2129d522f475Smrgstatic void
2130d522f475SmrgFinishPaste64(XtermWidget xw)
2131d522f475Smrg{
2132956cc18dSsnj    TScreen *screen = TScreenOf(xw);
2133956cc18dSsnj
2134956cc18dSsnj    TRACE(("FinishPaste64(%d)\n", screen->base64_paste));
2135956cc18dSsnj    if (screen->base64_paste) {
2136956cc18dSsnj	screen->base64_paste = 0;
2137956cc18dSsnj	unparseputc1(xw, screen->base64_final);
2138d522f475Smrg	unparse_end(xw);
2139d522f475Smrg    }
2140d522f475Smrg}
2141d522f475Smrg#endif
2142d522f475Smrg
2143d522f475Smrg#if !OPT_PASTE64
2144d522f475Smrgstatic
2145d522f475Smrg#endif
2146d522f475Smrgvoid
2147d522f475SmrgxtermGetSelection(Widget w,
2148d522f475Smrg		  Time ev_time,
2149e0a2b6dfSmrg		  String *params,	/* selections in precedence order */
2150d522f475Smrg		  Cardinal num_params,
2151894e0ac8Smrg		  Atom *targets)
2152d522f475Smrg{
2153d522f475Smrg    Atom selection;
2154d522f475Smrg    int cutbuffer;
2155d522f475Smrg    Atom target;
2156d522f475Smrg
2157956cc18dSsnj    XtermWidget xw;
2158956cc18dSsnj
215920d2c4d2Smrg    if (num_params == 0)
216020d2c4d2Smrg	return;
2161956cc18dSsnj    if ((xw = getXtermWidget(w)) == 0)
2162d522f475Smrg	return;
2163d522f475Smrg
2164e0a2b6dfSmrg    TRACE(("xtermGetSelection num_params %d @%ld\n", num_params, ev_time));
2165956cc18dSsnj    params = MapSelections(xw, params, num_params);
2166d522f475Smrg
2167d522f475Smrg    XmuInternStrings(XtDisplay(w), params, (Cardinal) 1, &selection);
2168d522f475Smrg    cutbuffer = CutBuffer(selection);
2169d522f475Smrg
2170956cc18dSsnj    TRACE(("Cutbuffer: %d, target: %s\n", cutbuffer,
2171956cc18dSsnj	   (targets
2172956cc18dSsnj	    ? visibleSelectionTarget(XtDisplay(w), targets[0])
2173956cc18dSsnj	    : "None")));
2174d522f475Smrg
2175d522f475Smrg    if (cutbuffer >= 0) {
2176d522f475Smrg	int inbytes;
2177d522f475Smrg	unsigned long nbytes;
2178d522f475Smrg	int fmt8 = 8;
2179d522f475Smrg	Atom type = XA_STRING;
2180d522f475Smrg	char *line;
2181d522f475Smrg
2182d522f475Smrg	/* 'line' is freed in SelectionReceived */
2183d522f475Smrg	line = XFetchBuffer(XtDisplay(w), &inbytes, cutbuffer);
2184d522f475Smrg	nbytes = (unsigned long) inbytes;
2185d522f475Smrg
21860bd37d32Smrg	if (nbytes > 0) {
2187d522f475Smrg	    SelectionReceived(w, NULL, &selection, &type, (XtPointer) line,
2188d522f475Smrg			      &nbytes, &fmt8);
21890bd37d32Smrg	} else if (num_params > 1) {
2190d522f475Smrg	    xtermGetSelection(w, ev_time, params + 1, num_params - 1, NULL);
2191d522f475Smrg	}
2192d522f475Smrg#if OPT_PASTE64
2193d522f475Smrg	else {
2194956cc18dSsnj	    FinishPaste64(xw);
2195d522f475Smrg	}
2196d522f475Smrg#endif
2197d522f475Smrg    } else {
2198d522f475Smrg
2199d522f475Smrg	if (targets == NULL || targets[0] == None) {
2200d522f475Smrg	    targets = _SelectionTargets(w);
2201d522f475Smrg	}
2202d522f475Smrg
2203d522f475Smrg	if (targets != 0) {
22042e4f8982Smrg	    struct _SelectionList *list;
22052e4f8982Smrg
2206d522f475Smrg	    target = targets[0];
2207d522f475Smrg
2208d522f475Smrg	    if (targets[1] == None) {	/* last target in list */
2209d522f475Smrg		params++;
2210d522f475Smrg		num_params--;
2211d522f475Smrg		targets = _SelectionTargets(w);
2212d522f475Smrg	    } else {
2213d522f475Smrg		targets = &(targets[1]);
2214d522f475Smrg	    }
2215d522f475Smrg
2216d522f475Smrg	    if (num_params) {
2217d522f475Smrg		/* 'list' is freed in SelectionReceived */
2218a1f3da82Smrg		list = TypeXtMalloc(struct _SelectionList);
2219d522f475Smrg		if (list != 0) {
2220d522f475Smrg		    list->params = params;
2221d522f475Smrg		    list->count = num_params;
2222d522f475Smrg		    list->targets = targets;
2223d522f475Smrg		    list->time = ev_time;
2224d522f475Smrg		}
2225d522f475Smrg	    } else {
2226d522f475Smrg		list = NULL;
2227d522f475Smrg	    }
2228d522f475Smrg
2229d522f475Smrg	    XtGetSelectionValue(w, selection,
2230d522f475Smrg				target,
2231d522f475Smrg				SelectionReceived,
2232d522f475Smrg				(XtPointer) list, ev_time);
2233d522f475Smrg	}
2234d522f475Smrg    }
2235d522f475Smrg}
2236d522f475Smrg
2237d522f475Smrg#if OPT_TRACE && OPT_WIDE_CHARS
2238d522f475Smrgstatic void
2239e0a2b6dfSmrgGettingSelection(Display *dpy, Atom type, Char *line, unsigned long len)
2240d522f475Smrg{
2241d522f475Smrg    Char *cp;
2242913cc679Smrg    const char *name = TraceAtomName(dpy, type);
2243d522f475Smrg
224401037d57Smrg    TRACE(("Getting %s (type=%ld, length=%ld)\n", name, (long int) type, len));
2245d522f475Smrg    for (cp = line; cp < line + len; cp++) {
2246956cc18dSsnj	TRACE(("[%d:%lu]", (int) (cp + 1 - line), len));
2247d522f475Smrg	if (isprint(*cp)) {
2248d522f475Smrg	    TRACE(("%c\n", *cp));
2249d522f475Smrg	} else {
2250d522f475Smrg	    TRACE(("\\x%02x\n", *cp));
2251d522f475Smrg	}
2252d522f475Smrg    }
2253d522f475Smrg}
2254d522f475Smrg#else
2255d522f475Smrg#define GettingSelection(dpy,type,line,len)	/* nothing */
2256d522f475Smrg#endif
2257d522f475Smrg
2258d522f475Smrg#ifdef VMS
2259d522f475Smrg#  define tty_vwrite(pty,lag,l)		tt_write(lag,l)
2260d522f475Smrg#else /* !( VMS ) */
2261d522f475Smrg#  define tty_vwrite(pty,lag,l)		v_write(pty,lag,l)
2262d522f475Smrg#endif /* defined VMS */
2263d522f475Smrg
2264d522f475Smrg#if OPT_PASTE64
2265d522f475Smrg/* Return base64 code character given 6-bit number */
2266d522f475Smrgstatic const char base64_code[] = "\
2267d522f475SmrgABCDEFGHIJKLMNOPQRSTUVWXYZ\
2268d522f475Smrgabcdefghijklmnopqrstuvwxyz\
2269d522f475Smrg0123456789+/";
2270d522f475Smrgstatic void
2271e0a2b6dfSmrgbase64_flush(TScreen *screen)
2272d522f475Smrg{
2273d522f475Smrg    Char x;
227401037d57Smrg
227501037d57Smrg    TRACE(("base64_flush count %d, pad %d (%d)\n",
227601037d57Smrg	   screen->base64_count,
227701037d57Smrg	   screen->base64_pad,
227801037d57Smrg	   screen->base64_pad & 3));
227901037d57Smrg
2280d522f475Smrg    switch (screen->base64_count) {
2281d522f475Smrg    case 0:
2282d522f475Smrg	break;
2283d522f475Smrg    case 2:
22842eaa94a1Schristos	x = CharOf(base64_code[screen->base64_accu << 4]);
2285d522f475Smrg	tty_vwrite(screen->respond, &x, 1);
2286d522f475Smrg	break;
2287d522f475Smrg    case 4:
22882eaa94a1Schristos	x = CharOf(base64_code[screen->base64_accu << 2]);
2289d522f475Smrg	tty_vwrite(screen->respond, &x, 1);
2290d522f475Smrg	break;
2291d522f475Smrg    }
229201037d57Smrg    if (screen->base64_pad & 3) {
2293d522f475Smrg	tty_vwrite(screen->respond,
2294492d43a5Smrg		   (const Char *) "===",
229501037d57Smrg		   (unsigned) (3 - (screen->base64_pad & 3)));
229601037d57Smrg    }
2297d522f475Smrg    screen->base64_count = 0;
2298d522f475Smrg    screen->base64_accu = 0;
2299d522f475Smrg    screen->base64_pad = 0;
2300d522f475Smrg}
2301d522f475Smrg#endif /* OPT_PASTE64 */
2302d522f475Smrg
2303e0a2b6dfSmrg/*
2304e0a2b6dfSmrg * Translate ISO-8859-1 or UTF-8 data to NRCS.
2305e0a2b6dfSmrg */
2306d522f475Smrgstatic void
2307f2e35a3aSmrgToNational(XtermWidget xw, Char *buffer, unsigned *length)
2308e0a2b6dfSmrg{
2309f2e35a3aSmrg    TScreen *screen = TScreenOf(xw);
2310f2e35a3aSmrg    DECNRCM_codes gsetL = screen->gsets[screen->curgl];
2311f2e35a3aSmrg    DECNRCM_codes gsetR = screen->gsets[screen->curgr];
2312e0a2b6dfSmrg
2313e0a2b6dfSmrg#if OPT_WIDE_CHARS
2314e0a2b6dfSmrg    if ((screen->utf8_nrc_mode | screen->utf8_mode) != uFalse) {
23152e4f8982Smrg	Char *p;
2316e0a2b6dfSmrg	PtyData *data = TypeXtMallocX(PtyData, *length);
2317e0a2b6dfSmrg
2318e0a2b6dfSmrg	memset(data, 0, sizeof(*data));
2319e0a2b6dfSmrg	data->next = data->buffer;
2320e0a2b6dfSmrg	data->last = data->buffer + *length;
2321e0a2b6dfSmrg	memcpy(data->buffer, buffer, (size_t) *length);
2322e0a2b6dfSmrg	p = buffer;
2323e0a2b6dfSmrg	while (data->next < data->last) {
23242e4f8982Smrg	    unsigned chr, out, gl, gr;
23252e4f8982Smrg
2326894e0ac8Smrg	    if (!decodeUtf8(screen, data)) {
2327e0a2b6dfSmrg		data->utf_size = 1;
2328e0a2b6dfSmrg		data->utf_data = data->next[0];
2329e0a2b6dfSmrg	    }
2330e0a2b6dfSmrg	    data->next += data->utf_size;
2331e0a2b6dfSmrg	    chr = data->utf_data;
2332e0a2b6dfSmrg	    out = chr;
2333f2e35a3aSmrg	    if ((gl = xtermCharSetIn(xw, chr, gsetL)) != chr) {
2334e0a2b6dfSmrg		out = gl;
2335f2e35a3aSmrg	    } else if ((gr = xtermCharSetIn(xw, chr, gsetR)) != chr) {
2336e0a2b6dfSmrg		out = gr;
2337e0a2b6dfSmrg	    }
2338e0a2b6dfSmrg	    *p++ = (Char) ((out < 256) ? out : ' ');
2339e0a2b6dfSmrg	}
2340e0a2b6dfSmrg	*length = (unsigned) (p - buffer);
2341e0a2b6dfSmrg	free(data);
2342e0a2b6dfSmrg    } else
2343e0a2b6dfSmrg#endif
2344e0a2b6dfSmrg    {
23452e4f8982Smrg	Char *p;
23462e4f8982Smrg
2347e0a2b6dfSmrg	for (p = buffer; (int) (p - buffer) < (int) *length; ++p) {
23482e4f8982Smrg	    unsigned gl, gr;
23492e4f8982Smrg	    unsigned chr = *p;
23502e4f8982Smrg	    unsigned out = chr;
2351f2e35a3aSmrg	    if ((gl = xtermCharSetIn(xw, chr, gsetL)) != chr) {
2352e0a2b6dfSmrg		out = gl;
2353f2e35a3aSmrg	    } else if ((gr = xtermCharSetIn(xw, chr, gsetR)) != chr) {
2354e0a2b6dfSmrg		out = gr;
2355e0a2b6dfSmrg	    }
2356e0a2b6dfSmrg	    *p = (Char) out;
2357e0a2b6dfSmrg	}
2358e0a2b6dfSmrg    }
2359e0a2b6dfSmrg}
2360e0a2b6dfSmrg
2361e0a2b6dfSmrgstatic void
2362e0a2b6dfSmrg_qWriteSelectionData(XtermWidget xw, Char *lag, unsigned length)
2363d522f475Smrg{
23640bd37d32Smrg    TScreen *screen = TScreenOf(xw);
23650bd37d32Smrg
2366e0a2b6dfSmrg    /*
2367e0a2b6dfSmrg     * If we are pasting into a window which is using NRCS, we want to map
2368e0a2b6dfSmrg     * the text from the normal encoding (ISO-8859-1 or UTF-8) into the coding
2369e0a2b6dfSmrg     * that an application would use to write characters with NRCS.
2370e0a2b6dfSmrg     *
2371e0a2b6dfSmrg     * TODO: handle conversion from UTF-8, and adjust length.  This can be done
2372e0a2b6dfSmrg     * in the same buffer because the target is always 8-bit.
2373e0a2b6dfSmrg     */
2374e0a2b6dfSmrg    if ((xw->flags & NATIONAL) && (length != 0)) {
2375f2e35a3aSmrg	ToNational(xw, lag, &length);
2376e0a2b6dfSmrg    }
2377d522f475Smrg#if OPT_PASTE64
2378d522f475Smrg    if (screen->base64_paste) {
2379d522f475Smrg	/* Send data as base64 */
2380d522f475Smrg	Char *p = lag;
2381d522f475Smrg	Char buf[64];
2382d522f475Smrg	unsigned x = 0;
23830bd37d32Smrg
238401037d57Smrg	TRACE(("convert to base64 %d:%s\n", length, visibleChars(p, length)));
238501037d57Smrg
23860bd37d32Smrg	/*
23870bd37d32Smrg	 * Handle the case where the selection is from _this_ xterm, which
23880bd37d32Smrg	 * puts part of the reply in the buffer before the selection callback
23890bd37d32Smrg	 * happens.
23900bd37d32Smrg	 */
23910bd37d32Smrg	if (screen->base64_paste && screen->unparse_len) {
23920bd37d32Smrg	    unparse_end(xw);
23930bd37d32Smrg	}
2394d522f475Smrg	while (length--) {
2395d522f475Smrg	    switch (screen->base64_count) {
2396d522f475Smrg	    case 0:
23972eaa94a1Schristos		buf[x++] = CharOf(base64_code[*p >> 2]);
23982eaa94a1Schristos		screen->base64_accu = (unsigned) (*p & 0x3);
2399d522f475Smrg		screen->base64_count = 2;
2400d522f475Smrg		++p;
2401d522f475Smrg		break;
2402d522f475Smrg	    case 2:
24032eaa94a1Schristos		buf[x++] = CharOf(base64_code[(screen->base64_accu << 4) +
24042eaa94a1Schristos					      (*p >> 4)]);
24052eaa94a1Schristos		screen->base64_accu = (unsigned) (*p & 0xF);
2406d522f475Smrg		screen->base64_count = 4;
2407d522f475Smrg		++p;
2408d522f475Smrg		break;
2409d522f475Smrg	    case 4:
24102eaa94a1Schristos		buf[x++] = CharOf(base64_code[(screen->base64_accu << 2) +
24112eaa94a1Schristos					      (*p >> 6)]);
24122eaa94a1Schristos		buf[x++] = CharOf(base64_code[*p & 0x3F]);
2413d522f475Smrg		screen->base64_accu = 0;
2414d522f475Smrg		screen->base64_count = 0;
2415d522f475Smrg		++p;
2416d522f475Smrg		break;
2417d522f475Smrg	    }
2418d522f475Smrg	    if (x >= 63) {
2419d522f475Smrg		/* Write 63 or 64 characters */
2420d522f475Smrg		screen->base64_pad += x;
242101037d57Smrg		TRACE(("writing base64 interim %s\n", visibleChars(buf, x)));
2422d522f475Smrg		tty_vwrite(screen->respond, buf, x);
2423d522f475Smrg		x = 0;
2424d522f475Smrg	    }
2425d522f475Smrg	}
2426d522f475Smrg	if (x != 0) {
2427d522f475Smrg	    screen->base64_pad += x;
242801037d57Smrg	    TRACE(("writing base64 finish %s\n", visibleChars(buf, x)));
2429d522f475Smrg	    tty_vwrite(screen->respond, buf, x);
2430d522f475Smrg	}
2431d522f475Smrg    } else
2432d522f475Smrg#endif /* OPT_PASTE64 */
2433d522f475Smrg#if OPT_READLINE
2434d522f475Smrg    if (SCREEN_FLAG(screen, paste_quotes)) {
2435d522f475Smrg	while (length--) {
2436492d43a5Smrg	    tty_vwrite(screen->respond, (const Char *) "\026", 1);	/* Control-V */
2437d522f475Smrg	    tty_vwrite(screen->respond, lag++, 1);
2438d522f475Smrg	}
2439d522f475Smrg    } else
2440d522f475Smrg#endif
244101037d57Smrg    {
244201037d57Smrg	TRACE(("writing base64 padding %s\n", visibleChars(lag, length)));
2443d522f475Smrg	tty_vwrite(screen->respond, lag, length);
244401037d57Smrg    }
2445d522f475Smrg}
2446d522f475Smrg
2447d522f475Smrgstatic void
2448e0a2b6dfSmrg_WriteSelectionData(XtermWidget xw, Char *line, size_t length)
2449d522f475Smrg{
2450d522f475Smrg    /* Write data to pty a line at a time. */
2451d522f475Smrg    /* Doing this one line at a time may no longer be necessary
2452d522f475Smrg       because v_write has been re-written. */
2453d522f475Smrg
24542e4f8982Smrg#if OPT_PASTE64
24550bd37d32Smrg    TScreen *screen = TScreenOf(xw);
24562e4f8982Smrg#endif
2457d522f475Smrg    Char *lag, *end;
2458d522f475Smrg
2459d522f475Smrg    /* in the VMS version, if tt_pasting isn't set to True then qio
2460d522f475Smrg       reads aren't blocked and an infinite loop is entered, where the
2461d522f475Smrg       pasted text shows up as new input, goes in again, shows up
2462d522f475Smrg       again, ad nauseum. */
2463d522f475Smrg#ifdef VMS
2464d522f475Smrg    tt_pasting = True;
2465d522f475Smrg#endif
2466d522f475Smrg
2467d522f475Smrg    end = &line[length];
2468d522f475Smrg    lag = line;
2469d522f475Smrg
2470d522f475Smrg#if OPT_PASTE64
2471d522f475Smrg    if (screen->base64_paste) {
24720bd37d32Smrg	_qWriteSelectionData(xw, lag, (unsigned) (end - lag));
2473d522f475Smrg	base64_flush(screen);
2474d522f475Smrg    } else
2475d522f475Smrg#endif
2476d522f475Smrg    {
2477d522f475Smrg	if (!SCREEN_FLAG(screen, paste_literal_nl)) {
2478d522f475Smrg	    Char *cp;
2479d522f475Smrg	    for (cp = line; cp != end; cp++) {
2480d522f475Smrg		if (*cp == '\n') {
2481d522f475Smrg		    *cp = '\r';
24820bd37d32Smrg		    _qWriteSelectionData(xw, lag, (unsigned) (cp - lag + 1));
2483d522f475Smrg		    lag = cp + 1;
2484d522f475Smrg		}
2485d522f475Smrg	    }
2486d522f475Smrg	}
2487d522f475Smrg
2488d522f475Smrg	if (lag != end) {
24890bd37d32Smrg	    _qWriteSelectionData(xw, lag, (unsigned) (end - lag));
2490d522f475Smrg	}
2491d522f475Smrg    }
2492d522f475Smrg#ifdef VMS
2493d522f475Smrg    tt_pasting = False;
2494d522f475Smrg    tt_start_read();		/* reenable reads or a character may be lost */
2495d522f475Smrg#endif
2496d522f475Smrg}
2497d522f475Smrg
2498f2e35a3aSmrg#if OPT_PASTE64 || OPT_READLINE
2499d522f475Smrgstatic void
2500e0a2b6dfSmrg_WriteKey(TScreen *screen, const Char *in)
2501d522f475Smrg{
2502d522f475Smrg    Char line[16];
2503d522f475Smrg    unsigned count = 0;
2504492d43a5Smrg    size_t length = strlen((const char *) in);
2505d522f475Smrg
2506d522f475Smrg    if (screen->control_eight_bits) {
2507d522f475Smrg	line[count++] = ANSI_CSI;
2508d522f475Smrg    } else {
2509d522f475Smrg	line[count++] = ANSI_ESC;
2510d522f475Smrg	line[count++] = '[';
2511d522f475Smrg    }
2512d522f475Smrg    while (length--)
2513d522f475Smrg	line[count++] = *in++;
2514d522f475Smrg    line[count++] = '~';
2515d522f475Smrg    tty_vwrite(screen->respond, line, count);
2516d522f475Smrg}
2517d522f475Smrg#endif /* OPT_READLINE */
2518d522f475Smrg
25190bd37d32Smrg/*
25200bd37d32Smrg * Unless enabled by the user, strip control characters other than formatting.
25210bd37d32Smrg */
25220bd37d32Smrgstatic size_t
25230bd37d32SmrgremoveControls(XtermWidget xw, char *value)
25240bd37d32Smrg{
25250bd37d32Smrg    TScreen *screen = TScreenOf(xw);
25260bd37d32Smrg    size_t dst = 0;
25270bd37d32Smrg
25280bd37d32Smrg    if (screen->allowPasteControls) {
25290bd37d32Smrg	dst = strlen(value);
25300bd37d32Smrg    } else {
25312e4f8982Smrg	size_t src = 0;
25320bd37d32Smrg	while ((value[dst] = value[src]) != '\0') {
25330bd37d32Smrg	    int ch = CharOf(value[src++]);
2534f2e35a3aSmrg
2535f2e35a3aSmrg#define ReplacePaste(n) \
2536f2e35a3aSmrg	    if (screen->disallow_paste_controls[n]) \
2537f2e35a3aSmrg		value[dst] = ' '
2538f2e35a3aSmrg
25390bd37d32Smrg	    if (ch < 32) {
2540f2e35a3aSmrg		ReplacePaste(epC0);
2541f2e35a3aSmrg		ReplacePaste(ch);
2542f2e35a3aSmrg		++dst;
2543f2e35a3aSmrg	    } else if (ch == ANSI_DEL) {
2544f2e35a3aSmrg		ReplacePaste(epDEL);
2545f2e35a3aSmrg		++dst;
25460bd37d32Smrg	    }
25470bd37d32Smrg#if OPT_WIDE_CHARS
2548e0a2b6dfSmrg	    else if (screen->utf8_inparse || screen->utf8_nrc_mode)
25490bd37d32Smrg		++dst;
25500bd37d32Smrg#endif
25510bd37d32Smrg#if OPT_C1_PRINT || OPT_WIDE_CHARS
25520bd37d32Smrg	    else if (screen->c1_printable)
25530bd37d32Smrg		++dst;
25540bd37d32Smrg#endif
25550bd37d32Smrg	    else if (ch >= 128 && ch < 160)
25560bd37d32Smrg		continue;
25570bd37d32Smrg	    else
25580bd37d32Smrg		++dst;
25590bd37d32Smrg	}
25600bd37d32Smrg    }
25610bd37d32Smrg    return dst;
25620bd37d32Smrg}
25630bd37d32Smrg
2564f2e35a3aSmrg#if OPT_SELECTION_OPS
2565f2e35a3aSmrgstatic void
2566f2e35a3aSmrgbeginInternalSelect(XtermWidget xw)
2567f2e35a3aSmrg{
2568f2e35a3aSmrg    TScreen *screen = TScreenOf(xw);
2569f2e35a3aSmrg    InternalSelect *mydata = &(screen->internal_select);
2570f2e35a3aSmrg
2571f2e35a3aSmrg    (void) mydata;
2572f2e35a3aSmrg    /* override flags so that SelectionReceived only updates a buffer */
2573f2e35a3aSmrg#if OPT_PASTE64
2574f2e35a3aSmrg    mydata->base64_paste = screen->base64_paste;
2575f2e35a3aSmrg    screen->base64_paste = 0;
2576f2e35a3aSmrg#endif
2577f2e35a3aSmrg#if OPT_PASTE64 || OPT_READLINE
2578f2e35a3aSmrg    mydata->paste_brackets = screen->paste_brackets;
2579f2e35a3aSmrg    SCREEN_FLAG_unset(screen, paste_brackets);
2580f2e35a3aSmrg#endif
2581f2e35a3aSmrg}
2582f2e35a3aSmrg
2583f2e35a3aSmrgstatic void
2584f2e35a3aSmrgfinishInternalSelect(XtermWidget xw)
2585f2e35a3aSmrg{
2586f2e35a3aSmrg    TScreen *screen = TScreenOf(xw);
2587f2e35a3aSmrg    InternalSelect *mydata = &(screen->internal_select);
2588f2e35a3aSmrg
2589f2e35a3aSmrg    (void) mydata;
2590f2e35a3aSmrg#if OPT_PASTE64
2591f2e35a3aSmrg    screen->base64_paste = mydata->base64_paste;
2592f2e35a3aSmrg#endif
2593f2e35a3aSmrg#if OPT_PASTE64 || OPT_READLINE
2594f2e35a3aSmrg    screen->paste_brackets = mydata->paste_brackets;
2595f2e35a3aSmrg#endif
2596f2e35a3aSmrg}
2597f2e35a3aSmrg
2598f2e35a3aSmrg#else
2599f2e35a3aSmrg#define finishInternalSelect(xw)	/* nothing */
2600f2e35a3aSmrg#endif /* OPT_SELECTION_OPS */
2601f2e35a3aSmrg
2602d522f475Smrg/* SelectionReceived: stuff received selection text into pty */
2603d522f475Smrg
2604d522f475Smrg/* ARGSUSED */
2605d522f475Smrgstatic void
2606d522f475SmrgSelectionReceived(Widget w,
2607d522f475Smrg		  XtPointer client_data,
2608894e0ac8Smrg		  Atom *selection GCC_UNUSED,
2609894e0ac8Smrg		  Atom *type,
2610d522f475Smrg		  XtPointer value,
2611d522f475Smrg		  unsigned long *length,
2612d522f475Smrg		  int *format)
2613d522f475Smrg{
2614d522f475Smrg    char **text_list = NULL;
26152e4f8982Smrg    int text_list_count = 0;
2616d522f475Smrg    XTextProperty text_prop;
2617d522f475Smrg    TScreen *screen;
2618d522f475Smrg    Display *dpy;
2619d522f475Smrg#if OPT_TRACE && OPT_WIDE_CHARS
2620d522f475Smrg    Char *line = (Char *) value;
2621d522f475Smrg#endif
2622d522f475Smrg
2623956cc18dSsnj    XtermWidget xw;
2624956cc18dSsnj
2625956cc18dSsnj    if ((xw = getXtermWidget(w)) == 0)
2626d522f475Smrg	return;
2627956cc18dSsnj
2628956cc18dSsnj    screen = TScreenOf(xw);
2629d522f475Smrg    dpy = XtDisplay(w);
2630d522f475Smrg
2631d522f475Smrg    if (*type == 0		/*XT_CONVERT_FAIL */
2632d522f475Smrg	|| *length == 0
263301037d57Smrg	|| value == NULL) {
263401037d57Smrg	TRACE(("...no data to convert\n"));
2635d522f475Smrg	goto fail;
263601037d57Smrg    }
2637d522f475Smrg
2638d522f475Smrg    text_prop.value = (unsigned char *) value;
2639d522f475Smrg    text_prop.encoding = *type;
2640d522f475Smrg    text_prop.format = *format;
2641d522f475Smrg    text_prop.nitems = *length;
2642d522f475Smrg
264301037d57Smrg    TRACE(("SelectionReceived %s %s format %d, nitems %ld\n",
2644913cc679Smrg	   TraceAtomName(screen->display, *selection),
2645956cc18dSsnj	   visibleSelectionTarget(dpy, text_prop.encoding),
2646956cc18dSsnj	   text_prop.format,
2647956cc18dSsnj	   text_prop.nitems));
2648956cc18dSsnj
2649d522f475Smrg#if OPT_WIDE_CHARS
2650e39b573cSmrg    if (XSupportsLocale() && screen->wide_chars) {
2651d522f475Smrg	if (*type == XA_UTF8_STRING(dpy) ||
2652d522f475Smrg	    *type == XA_STRING ||
2653d522f475Smrg	    *type == XA_COMPOUND_TEXT(dpy)) {
2654d522f475Smrg	    GettingSelection(dpy, *type, line, *length);
2655d522f475Smrg	    if (Xutf8TextPropertyToTextList(dpy, &text_prop,
2656d522f475Smrg					    &text_list,
2657d522f475Smrg					    &text_list_count) < 0) {
2658e39b573cSmrg		TRACE(("default Xutf8 Conversion failed\n"));
2659d522f475Smrg		text_list = NULL;
2660d522f475Smrg	    }
2661d522f475Smrg	}
2662d522f475Smrg    } else
2663d522f475Smrg#endif /* OPT_WIDE_CHARS */
2664d522f475Smrg    {
2665d522f475Smrg	/* Convert the selection to locale's multibyte encoding. */
2666d522f475Smrg
2667d522f475Smrg	if (*type == XA_UTF8_STRING(dpy) ||
2668d522f475Smrg	    *type == XA_STRING ||
2669d522f475Smrg	    *type == XA_COMPOUND_TEXT(dpy)) {
2670d522f475Smrg	    Status rc;
2671d522f475Smrg
2672d522f475Smrg	    GettingSelection(dpy, *type, line, *length);
2673d522f475Smrg
2674d522f475Smrg#if OPT_WIDE_CHARS
2675d522f475Smrg	    if (*type == XA_UTF8_STRING(dpy) &&
2676d522f475Smrg		!(screen->wide_chars || screen->c1_printable)) {
267720d2c4d2Smrg		rc = xtermUtf8ToTextList(xw, &text_prop,
267820d2c4d2Smrg					 &text_list, &text_list_count);
2679d522f475Smrg	    } else
2680d522f475Smrg#endif
2681e39b573cSmrg	    if (*type == XA_STRING && (!XSupportsLocale() || screen->brokenSelections)) {
2682d522f475Smrg		rc = XTextPropertyToStringList(&text_prop,
2683d522f475Smrg					       &text_list, &text_list_count);
2684d522f475Smrg	    } else {
2685d522f475Smrg		rc = XmbTextPropertyToTextList(dpy, &text_prop,
2686d522f475Smrg					       &text_list,
2687d522f475Smrg					       &text_list_count);
2688d522f475Smrg	    }
2689d522f475Smrg	    if (rc < 0) {
2690d522f475Smrg		TRACE(("Conversion failed\n"));
2691d522f475Smrg		text_list = NULL;
2692d522f475Smrg	    }
2693d522f475Smrg	}
2694d522f475Smrg    }
2695d522f475Smrg
2696d522f475Smrg    if (text_list != NULL && text_list_count != 0) {
2697d522f475Smrg	int i;
2698d522f475Smrg
2699d522f475Smrg#if OPT_PASTE64
2700d522f475Smrg	if (screen->base64_paste) {
2701a1f3da82Smrg	    /* EMPTY */ ;
2702d522f475Smrg	} else
2703d522f475Smrg#endif
2704f2e35a3aSmrg#if OPT_PASTE64 || OPT_READLINE
2705f2e35a3aSmrg	if (SCREEN_FLAG(screen, paste_brackets) && !screen->selectToBuffer) {
2706492d43a5Smrg	    _WriteKey(screen, (const Char *) "200");
2707d522f475Smrg	}
2708d522f475Smrg#endif
2709d522f475Smrg	for (i = 0; i < text_list_count; i++) {
27100bd37d32Smrg	    size_t len = removeControls(xw, text_list[i]);
271101037d57Smrg
27120bd37d32Smrg	    if (screen->selectToBuffer) {
271301037d57Smrg		InternalSelect *mydata = &(screen->internal_select);
2714f2e35a3aSmrg		if (!mydata->done) {
2715f2e35a3aSmrg		    size_t have = (mydata->buffer
2716f2e35a3aSmrg				   ? strlen(mydata->buffer)
2717f2e35a3aSmrg				   : 0);
2718f2e35a3aSmrg		    size_t need = have + len + 1;
2719f2e35a3aSmrg		    char *buffer = realloc(mydata->buffer, need);
2720f2e35a3aSmrg
2721f2e35a3aSmrg		    if (buffer != 0) {
2722f2e35a3aSmrg			strcpy(buffer + have, text_list[i]);
2723f2e35a3aSmrg			mydata->buffer = buffer;
2724f2e35a3aSmrg		    }
2725f2e35a3aSmrg		    TRACE(("FormatSelect %d.%d .. %d.%d %s\n",
2726f2e35a3aSmrg			   screen->startSel.row,
2727f2e35a3aSmrg			   screen->startSel.col,
2728f2e35a3aSmrg			   screen->endSel.row,
2729f2e35a3aSmrg			   screen->endSel.col,
2730f2e35a3aSmrg			   mydata->buffer));
2731f2e35a3aSmrg		    mydata->format_select(w, mydata->format, mydata->buffer,
2732f2e35a3aSmrg					  &(screen->startSel),
2733f2e35a3aSmrg					  &(screen->endSel));
2734f2e35a3aSmrg		    mydata->done = True;
27350bd37d32Smrg		}
273601037d57Smrg
27370bd37d32Smrg	    } else {
27380bd37d32Smrg		_WriteSelectionData(xw, (Char *) text_list[i], len);
27390bd37d32Smrg	    }
2740d522f475Smrg	}
2741d522f475Smrg#if OPT_PASTE64
2742d522f475Smrg	if (screen->base64_paste) {
2743956cc18dSsnj	    FinishPaste64(xw);
2744d522f475Smrg	} else
2745d522f475Smrg#endif
2746f2e35a3aSmrg#if OPT_PASTE64 || OPT_READLINE
2747f2e35a3aSmrg	if (SCREEN_FLAG(screen, paste_brackets) && !screen->selectToBuffer) {
2748492d43a5Smrg	    _WriteKey(screen, (const Char *) "201");
2749d522f475Smrg	}
2750d522f475Smrg#endif
2751f2e35a3aSmrg	if (screen->selectToBuffer) {
2752f2e35a3aSmrg	    InternalSelect *mydata = &(screen->internal_select);
2753f2e35a3aSmrg	    finishInternalSelect(xw);
2754f2e35a3aSmrg	    if (mydata->done) {
2755f2e35a3aSmrg		free(mydata->format);
2756f2e35a3aSmrg		free(mydata->buffer);
2757f2e35a3aSmrg		memset(mydata, 0, sizeof(*mydata));
2758f2e35a3aSmrg	    }
2759f2e35a3aSmrg	    screen->selectToBuffer = False;
2760f2e35a3aSmrg	}
2761d522f475Smrg	XFreeStringList(text_list);
276201037d57Smrg    } else {
276301037d57Smrg	TRACE(("...empty text-list\n"));
2764d522f475Smrg	goto fail;
276501037d57Smrg    }
2766d522f475Smrg
2767d522f475Smrg    XtFree((char *) client_data);
2768d522f475Smrg    XtFree((char *) value);
2769d522f475Smrg
2770d522f475Smrg    return;
2771d522f475Smrg
2772d522f475Smrg  fail:
2773d522f475Smrg    if (client_data != 0) {
2774d522f475Smrg	struct _SelectionList *list = (struct _SelectionList *) client_data;
2775956cc18dSsnj
2776956cc18dSsnj	TRACE(("SelectionReceived ->xtermGetSelection\n"));
2777d522f475Smrg	xtermGetSelection(w, list->time,
2778d522f475Smrg			  list->params, list->count, list->targets);
2779d522f475Smrg	XtFree((char *) client_data);
2780d522f475Smrg#if OPT_PASTE64
2781d522f475Smrg    } else {
2782956cc18dSsnj	FinishPaste64(xw);
2783d522f475Smrg#endif
2784d522f475Smrg    }
2785d522f475Smrg    return;
2786d522f475Smrg}
2787d522f475Smrg
2788d522f475Smrgvoid
2789d522f475SmrgHandleInsertSelection(Widget w,
2790894e0ac8Smrg		      XEvent *event,	/* assumed to be XButtonEvent* */
2791e0a2b6dfSmrg		      String *params,	/* selections in precedence order */
2792d522f475Smrg		      Cardinal *num_params)
2793d522f475Smrg{
2794956cc18dSsnj    XtermWidget xw;
2795d522f475Smrg
2796956cc18dSsnj    if ((xw = getXtermWidget(w)) != 0) {
2797f2e35a3aSmrg	TRACE_EVENT("HandleInsertSelection", event, params, num_params);
2798d522f475Smrg	if (!SendMousePosition(xw, event)) {
2799d522f475Smrg#if OPT_READLINE
2800d522f475Smrg	    int ldelta;
2801956cc18dSsnj	    TScreen *screen = TScreenOf(xw);
2802492d43a5Smrg	    if (IsBtnEvent(event)
2803f2e35a3aSmrg		&& !OverrideEvent(event)
2804913cc679Smrg		&& (okSendMousePos(xw) == MOUSE_OFF)
2805d522f475Smrg		&& SCREEN_FLAG(screen, paste_moves)
2806d522f475Smrg		&& rowOnCurrentLine(screen, eventRow(screen, event), &ldelta))
2807d522f475Smrg		ReadLineMovePoint(screen, eventColBetween(screen, event), ldelta);
2808d522f475Smrg#endif /* OPT_READLINE */
2809d522f475Smrg
2810d522f475Smrg	    xtermGetSelection(w, event->xbutton.time, params, *num_params, NULL);
2811d522f475Smrg	}
2812d522f475Smrg    }
2813d522f475Smrg}
2814d522f475Smrg
2815d522f475Smrgstatic SelectUnit
2816956cc18dSsnjEvalSelectUnit(XtermWidget xw,
2817d522f475Smrg	       Time buttonDownTime,
2818d522f475Smrg	       SelectUnit defaultUnit,
2819d522f475Smrg	       unsigned int button)
2820d522f475Smrg{
2821956cc18dSsnj    TScreen *screen = TScreenOf(xw);
2822d522f475Smrg    SelectUnit result;
2823d522f475Smrg    int delta;
2824d522f475Smrg
2825d522f475Smrg    if (button != screen->lastButton) {
282620d2c4d2Smrg	delta = screen->multiClickTime + 1;
2827d522f475Smrg    } else if (screen->lastButtonUpTime == (Time) 0) {
2828d522f475Smrg	/* first time and once in a blue moon */
2829d522f475Smrg	delta = screen->multiClickTime + 1;
2830d522f475Smrg    } else if (buttonDownTime > screen->lastButtonUpTime) {
2831d522f475Smrg	/* most of the time */
28322eaa94a1Schristos	delta = (int) (buttonDownTime - screen->lastButtonUpTime);
2833d522f475Smrg    } else {
2834d522f475Smrg	/* time has rolled over since lastButtonUpTime */
28352eaa94a1Schristos	delta = (int) ((((Time) ~ 0) - screen->lastButtonUpTime) + buttonDownTime);
2836d522f475Smrg    }
2837d522f475Smrg
2838d522f475Smrg    if (delta > screen->multiClickTime) {
2839d522f475Smrg	screen->numberOfClicks = 1;
2840d522f475Smrg	result = defaultUnit;
2841d522f475Smrg    } else {
2842d522f475Smrg	result = screen->selectMap[screen->numberOfClicks % screen->maxClicks];
2843d522f475Smrg	screen->numberOfClicks += 1;
2844d522f475Smrg    }
2845d522f475Smrg    TRACE(("EvalSelectUnit(%d) = %d\n", screen->numberOfClicks, result));
2846d522f475Smrg    return result;
2847d522f475Smrg}
2848d522f475Smrg
2849d522f475Smrgstatic void
2850d522f475Smrgdo_select_start(XtermWidget xw,
2851894e0ac8Smrg		XEvent *event,	/* must be XButtonEvent* */
2852e0a2b6dfSmrg		CELL *cell)
2853d522f475Smrg{
2854956cc18dSsnj    TScreen *screen = TScreenOf(xw);
2855d522f475Smrg
2856d522f475Smrg    if (SendMousePosition(xw, event))
2857d522f475Smrg	return;
2858956cc18dSsnj    screen->selectUnit = EvalSelectUnit(xw,
2859d522f475Smrg					event->xbutton.time,
2860d522f475Smrg					Select_CHAR,
2861d522f475Smrg					event->xbutton.button);
2862d522f475Smrg    screen->replyToEmacs = False;
2863d522f475Smrg
2864d522f475Smrg#if OPT_READLINE
2865d522f475Smrg    lastButtonDownTime = event->xbutton.time;
2866d522f475Smrg#endif
2867d522f475Smrg
2868d522f475Smrg    StartSelect(xw, cell);
2869d522f475Smrg}
2870d522f475Smrg
2871d522f475Smrg/* ARGSUSED */
2872d522f475Smrgvoid
2873d522f475SmrgHandleSelectStart(Widget w,
2874894e0ac8Smrg		  XEvent *event,	/* must be XButtonEvent* */
2875e0a2b6dfSmrg		  String *params GCC_UNUSED,
2876d522f475Smrg		  Cardinal *num_params GCC_UNUSED)
2877d522f475Smrg{
2878956cc18dSsnj    XtermWidget xw;
2879956cc18dSsnj
2880956cc18dSsnj    if ((xw = getXtermWidget(w)) != 0) {
2881956cc18dSsnj	TScreen *screen = TScreenOf(xw);
2882d522f475Smrg	CELL cell;
2883d522f475Smrg
2884f2e35a3aSmrg	TRACE_EVENT("HandleSelectStart", event, params, num_params);
2885d522f475Smrg	screen->firstValidRow = 0;
2886d522f475Smrg	screen->lastValidRow = screen->max_row;
2887d522f475Smrg	PointToCELL(screen, event->xbutton.y, event->xbutton.x, &cell);
2888d522f475Smrg
2889d522f475Smrg#if OPT_READLINE
2890d522f475Smrg	ExtendingSelection = 0;
2891d522f475Smrg#endif
2892d522f475Smrg
2893d522f475Smrg	do_select_start(xw, event, &cell);
2894d522f475Smrg    }
2895d522f475Smrg}
2896d522f475Smrg
2897d522f475Smrg/* ARGSUSED */
2898d522f475Smrgvoid
2899d522f475SmrgHandleKeyboardSelectStart(Widget w,
2900894e0ac8Smrg			  XEvent *event,	/* must be XButtonEvent* */
2901e0a2b6dfSmrg			  String *params GCC_UNUSED,
2902d522f475Smrg			  Cardinal *num_params GCC_UNUSED)
2903d522f475Smrg{
2904956cc18dSsnj    XtermWidget xw;
2905956cc18dSsnj
2906956cc18dSsnj    if ((xw = getXtermWidget(w)) != 0) {
2907956cc18dSsnj	TScreen *screen = TScreenOf(xw);
29080bd37d32Smrg
2909f2e35a3aSmrg	TRACE_EVENT("HandleKeyboardSelectStart", event, params, num_params);
2910d522f475Smrg	do_select_start(xw, event, &screen->cursorp);
2911d522f475Smrg    }
2912d522f475Smrg}
2913d522f475Smrg
2914d522f475Smrgstatic void
2915894e0ac8SmrgTrackDown(XtermWidget xw, XButtonEvent *event)
2916d522f475Smrg{
2917956cc18dSsnj    TScreen *screen = TScreenOf(xw);
2918d522f475Smrg    CELL cell;
2919d522f475Smrg
2920956cc18dSsnj    screen->selectUnit = EvalSelectUnit(xw,
2921d522f475Smrg					event->time,
2922d522f475Smrg					Select_CHAR,
2923d522f475Smrg					event->button);
2924d522f475Smrg    if (screen->numberOfClicks > 1) {
2925d522f475Smrg	PointToCELL(screen, event->y, event->x, &cell);
2926d522f475Smrg	screen->replyToEmacs = True;
2927d522f475Smrg	StartSelect(xw, &cell);
2928d522f475Smrg    } else {
2929d522f475Smrg	screen->waitingForTrackInfo = True;
2930492d43a5Smrg	EditorButton(xw, event);
2931d522f475Smrg    }
2932d522f475Smrg}
2933d522f475Smrg
2934d522f475Smrg#define boundsCheck(x)	if (x < 0) \
2935d522f475Smrg			    x = 0; \
2936d522f475Smrg			else if (x >= screen->max_row) \
2937d522f475Smrg			    x = screen->max_row
2938d522f475Smrg
2939d522f475Smrgvoid
2940d522f475SmrgTrackMouse(XtermWidget xw,
2941d522f475Smrg	   int func,
2942e0a2b6dfSmrg	   CELL *start,
2943d522f475Smrg	   int firstrow,
2944d522f475Smrg	   int lastrow)
2945d522f475Smrg{
2946956cc18dSsnj    TScreen *screen = TScreenOf(xw);
2947d522f475Smrg
2948d522f475Smrg    if (screen->waitingForTrackInfo) {	/* if Timed, ignore */
2949d522f475Smrg	screen->waitingForTrackInfo = False;
2950d522f475Smrg
2951d522f475Smrg	if (func != 0) {
2952d522f475Smrg	    CELL first = *start;
2953d522f475Smrg
2954d522f475Smrg	    boundsCheck(first.row);
2955d522f475Smrg	    boundsCheck(firstrow);
2956d522f475Smrg	    boundsCheck(lastrow);
2957d522f475Smrg	    screen->firstValidRow = firstrow;
2958d522f475Smrg	    screen->lastValidRow = lastrow;
2959d522f475Smrg	    screen->replyToEmacs = True;
2960d522f475Smrg	    StartSelect(xw, &first);
2961d522f475Smrg	}
2962d522f475Smrg    }
2963d522f475Smrg}
2964d522f475Smrg
2965d522f475Smrgstatic void
2966e0a2b6dfSmrgStartSelect(XtermWidget xw, const CELL *cell)
2967d522f475Smrg{
2968956cc18dSsnj    TScreen *screen = TScreenOf(xw);
2969d522f475Smrg
2970d522f475Smrg    TRACE(("StartSelect row=%d, col=%d\n", cell->row, cell->col));
2971d522f475Smrg    if (screen->cursor_state)
2972f2e35a3aSmrg	HideCursor(xw);
2973d522f475Smrg    if (screen->numberOfClicks == 1) {
2974d522f475Smrg	/* set start of selection */
2975d522f475Smrg	screen->rawPos = *cell;
2976d522f475Smrg    }
2977d522f475Smrg    /* else use old values in rawPos */
2978d522f475Smrg    screen->saveStartR = screen->startExt = screen->rawPos;
2979d522f475Smrg    screen->saveEndR = screen->endExt = screen->rawPos;
2980d522f475Smrg    if (Coordinate(screen, cell) < Coordinate(screen, &(screen->rawPos))) {
2981d522f475Smrg	screen->eventMode = LEFTEXTENSION;
2982d522f475Smrg	screen->startExt = *cell;
2983d522f475Smrg    } else {
2984d522f475Smrg	screen->eventMode = RIGHTEXTENSION;
2985d522f475Smrg	screen->endExt = *cell;
2986d522f475Smrg    }
2987f2e35a3aSmrg    ComputeSelect(xw, &(screen->startExt), &(screen->endExt), False, True);
2988d522f475Smrg}
2989d522f475Smrg
2990d522f475Smrgstatic void
2991d522f475SmrgEndExtend(XtermWidget xw,
2992894e0ac8Smrg	  XEvent *event,	/* must be XButtonEvent */
2993e0a2b6dfSmrg	  String *params,	/* selections */
2994d522f475Smrg	  Cardinal num_params,
2995d522f475Smrg	  Bool use_cursor_loc)
2996d522f475Smrg{
2997d522f475Smrg    CELL cell;
2998956cc18dSsnj    TScreen *screen = TScreenOf(xw);
2999d522f475Smrg
3000f2e35a3aSmrg    TRACE_EVENT("EndExtend", event, params, &num_params);
3001d522f475Smrg    if (use_cursor_loc) {
3002d522f475Smrg	cell = screen->cursorp;
3003d522f475Smrg    } else {
3004d522f475Smrg	PointToCELL(screen, event->xbutton.y, event->xbutton.x, &cell);
3005d522f475Smrg    }
3006d522f475Smrg    ExtendExtend(xw, &cell);
30072e4f8982Smrg
3008d522f475Smrg    screen->lastButtonUpTime = event->xbutton.time;
3009d522f475Smrg    screen->lastButton = event->xbutton.button;
30102e4f8982Smrg
3011d522f475Smrg    if (!isSameCELL(&(screen->startSel), &(screen->endSel))) {
3012d522f475Smrg	if (screen->replyToEmacs) {
30132e4f8982Smrg	    Char line[64];
30142e4f8982Smrg	    unsigned count = 0;
30152e4f8982Smrg
3016d522f475Smrg	    if (screen->control_eight_bits) {
3017d522f475Smrg		line[count++] = ANSI_CSI;
3018d522f475Smrg	    } else {
3019d522f475Smrg		line[count++] = ANSI_ESC;
3020d522f475Smrg		line[count++] = '[';
3021d522f475Smrg	    }
3022d522f475Smrg	    if (isSameCELL(&(screen->rawPos), &(screen->startSel))
3023d522f475Smrg		&& isSameCELL(&cell, &(screen->endSel))) {
3024d522f475Smrg		/* Use short-form emacs select */
30250bd37d32Smrg
30260bd37d32Smrg		switch (screen->extend_coords) {
30270bd37d32Smrg		case 0:
30280bd37d32Smrg		case SET_EXT_MODE_MOUSE:
30290bd37d32Smrg		    line[count++] = 't';
30300bd37d32Smrg		    break;
30310bd37d32Smrg		case SET_SGR_EXT_MODE_MOUSE:
3032f2e35a3aSmrg		case SET_PIXEL_POSITION_MOUSE:
30330bd37d32Smrg		    line[count++] = '<';
30340bd37d32Smrg		    break;
30350bd37d32Smrg		}
30360bd37d32Smrg
3037492d43a5Smrg		count = EmitMousePosition(screen, line, count, screen->endSel.col);
30380bd37d32Smrg		count = EmitMousePositionSeparator(screen, line, count);
3039492d43a5Smrg		count = EmitMousePosition(screen, line, count, screen->endSel.row);
30400bd37d32Smrg
30410bd37d32Smrg		switch (screen->extend_coords) {
30420bd37d32Smrg		case SET_SGR_EXT_MODE_MOUSE:
30430bd37d32Smrg		case SET_URXVT_EXT_MODE_MOUSE:
3044f2e35a3aSmrg		case SET_PIXEL_POSITION_MOUSE:
30450bd37d32Smrg		    line[count++] = 't';
30460bd37d32Smrg		    break;
30470bd37d32Smrg		}
3048d522f475Smrg	    } else {
3049d522f475Smrg		/* long-form, specify everything */
30500bd37d32Smrg
30510bd37d32Smrg		switch (screen->extend_coords) {
30520bd37d32Smrg		case 0:
30530bd37d32Smrg		case SET_EXT_MODE_MOUSE:
30540bd37d32Smrg		    line[count++] = 'T';
30550bd37d32Smrg		    break;
30560bd37d32Smrg		case SET_SGR_EXT_MODE_MOUSE:
3057f2e35a3aSmrg		case SET_PIXEL_POSITION_MOUSE:
30580bd37d32Smrg		    line[count++] = '<';
30590bd37d32Smrg		    break;
30600bd37d32Smrg		}
30610bd37d32Smrg
3062492d43a5Smrg		count = EmitMousePosition(screen, line, count, screen->startSel.col);
30630bd37d32Smrg		count = EmitMousePositionSeparator(screen, line, count);
3064492d43a5Smrg		count = EmitMousePosition(screen, line, count, screen->startSel.row);
30650bd37d32Smrg		count = EmitMousePositionSeparator(screen, line, count);
3066492d43a5Smrg		count = EmitMousePosition(screen, line, count, screen->endSel.col);
30670bd37d32Smrg		count = EmitMousePositionSeparator(screen, line, count);
3068492d43a5Smrg		count = EmitMousePosition(screen, line, count, screen->endSel.row);
30690bd37d32Smrg		count = EmitMousePositionSeparator(screen, line, count);
3070492d43a5Smrg		count = EmitMousePosition(screen, line, count, cell.col);
30710bd37d32Smrg		count = EmitMousePositionSeparator(screen, line, count);
3072492d43a5Smrg		count = EmitMousePosition(screen, line, count, cell.row);
30730bd37d32Smrg
30740bd37d32Smrg		switch (screen->extend_coords) {
30750bd37d32Smrg		case SET_SGR_EXT_MODE_MOUSE:
30760bd37d32Smrg		case SET_URXVT_EXT_MODE_MOUSE:
3077f2e35a3aSmrg		case SET_PIXEL_POSITION_MOUSE:
30780bd37d32Smrg		    line[count++] = 'T';
30790bd37d32Smrg		    break;
30800bd37d32Smrg		}
3081d522f475Smrg	    }
3082d522f475Smrg	    v_write(screen->respond, line, count);
3083f2e35a3aSmrg	    UnHiliteText(xw);
3084d522f475Smrg	}
3085d522f475Smrg    }
3086d522f475Smrg    SelectSet(xw, event, params, num_params);
3087d522f475Smrg    screen->eventMode = NORMAL;
3088d522f475Smrg}
3089d522f475Smrg
3090d522f475Smrgvoid
3091d522f475SmrgHandleSelectSet(Widget w,
3092894e0ac8Smrg		XEvent *event,
3093e0a2b6dfSmrg		String *params,
3094d522f475Smrg		Cardinal *num_params)
3095d522f475Smrg{
3096956cc18dSsnj    XtermWidget xw;
3097956cc18dSsnj
3098956cc18dSsnj    if ((xw = getXtermWidget(w)) != 0) {
3099f2e35a3aSmrg	TRACE_EVENT("HandleSelectSet", event, params, num_params);
3100956cc18dSsnj	SelectSet(xw, event, params, *num_params);
3101d522f475Smrg    }
3102d522f475Smrg}
3103d522f475Smrg
3104d522f475Smrg/* ARGSUSED */
3105d522f475Smrgstatic void
3106d522f475SmrgSelectSet(XtermWidget xw,
3107894e0ac8Smrg	  XEvent *event GCC_UNUSED,
3108e0a2b6dfSmrg	  String *params,
3109d522f475Smrg	  Cardinal num_params)
3110d522f475Smrg{
3111956cc18dSsnj    TScreen *screen = TScreenOf(xw);
3112d522f475Smrg
3113d522f475Smrg    TRACE(("SelectSet\n"));
3114d522f475Smrg    /* Only do select stuff if non-null select */
3115d522f475Smrg    if (!isSameCELL(&(screen->startSel), &(screen->endSel))) {
3116f2e35a3aSmrg	Cardinal n;
3117f2e35a3aSmrg	for (n = 0; n < num_params; ++n) {
3118f2e35a3aSmrg	    SaltTextAway(xw,
3119f2e35a3aSmrg			 TargetToSelection(screen, params[n]),
3120f2e35a3aSmrg			 &(screen->startSel), &(screen->endSel));
3121f2e35a3aSmrg	}
31220bd37d32Smrg	_OwnSelection(xw, params, num_params);
3123d522f475Smrg    } else {
31240bd37d32Smrg	ScrnDisownSelection(xw);
3125d522f475Smrg    }
3126d522f475Smrg}
3127d522f475Smrg
3128d522f475Smrg#define Abs(x)		((x) < 0 ? -(x) : (x))
3129d522f475Smrg
3130d522f475Smrg/* ARGSUSED */
3131d522f475Smrgstatic void
3132d522f475Smrgdo_start_extend(XtermWidget xw,
3133894e0ac8Smrg		XEvent *event,	/* must be XButtonEvent* */
3134e0a2b6dfSmrg		String *params GCC_UNUSED,
3135d522f475Smrg		Cardinal *num_params GCC_UNUSED,
3136d522f475Smrg		Bool use_cursor_loc)
3137d522f475Smrg{
3138956cc18dSsnj    TScreen *screen = TScreenOf(xw);
3139d522f475Smrg    int coord;
3140d522f475Smrg    CELL cell;
3141d522f475Smrg
3142d522f475Smrg    if (SendMousePosition(xw, event))
3143d522f475Smrg	return;
3144d522f475Smrg
3145d522f475Smrg    screen->firstValidRow = 0;
3146d522f475Smrg    screen->lastValidRow = screen->max_row;
3147d522f475Smrg#if OPT_READLINE
3148f2e35a3aSmrg    if (OverrideEvent(event)
3149d522f475Smrg	|| event->xbutton.button != Button3
3150d522f475Smrg	|| !(SCREEN_FLAG(screen, dclick3_deletes)))
3151d522f475Smrg#endif
3152956cc18dSsnj	screen->selectUnit = EvalSelectUnit(xw,
3153d522f475Smrg					    event->xbutton.time,
3154d522f475Smrg					    screen->selectUnit,
3155d522f475Smrg					    event->xbutton.button);
3156d522f475Smrg    screen->replyToEmacs = False;
3157d522f475Smrg
3158d522f475Smrg#if OPT_READLINE
3159f2e35a3aSmrg    CheckSecondPress3(xw, screen, event);
3160d522f475Smrg#endif
3161d522f475Smrg
3162d522f475Smrg    if (screen->numberOfClicks == 1
3163f2e35a3aSmrg	|| (SCREEN_FLAG(screen, dclick3_deletes)
3164f2e35a3aSmrg	    && !OverrideEvent(event))) {
3165d522f475Smrg	/* Save existing selection so we can reestablish it if the guy
3166d522f475Smrg	   extends past the other end of the selection */
3167d522f475Smrg	screen->saveStartR = screen->startExt = screen->startRaw;
3168d522f475Smrg	screen->saveEndR = screen->endExt = screen->endRaw;
3169d522f475Smrg    } else {
3170d522f475Smrg	/* He just needed the selection mode changed, use old values. */
3171d522f475Smrg	screen->startExt = screen->startRaw = screen->saveStartR;
3172d522f475Smrg	screen->endExt = screen->endRaw = screen->saveEndR;
3173d522f475Smrg    }
3174d522f475Smrg    if (use_cursor_loc) {
3175d522f475Smrg	cell = screen->cursorp;
3176d522f475Smrg    } else {
3177d522f475Smrg	PointToCELL(screen, event->xbutton.y, event->xbutton.x, &cell);
3178d522f475Smrg    }
3179d522f475Smrg    coord = Coordinate(screen, &cell);
3180d522f475Smrg
3181d522f475Smrg    if (Abs(coord - Coordinate(screen, &(screen->startSel)))
3182d522f475Smrg	< Abs(coord - Coordinate(screen, &(screen->endSel)))
3183d522f475Smrg	|| coord < Coordinate(screen, &(screen->startSel))) {
3184d522f475Smrg	/* point is close to left side of selection */
3185d522f475Smrg	screen->eventMode = LEFTEXTENSION;
3186d522f475Smrg	screen->startExt = cell;
3187d522f475Smrg    } else {
3188d522f475Smrg	/* point is close to left side of selection */
3189d522f475Smrg	screen->eventMode = RIGHTEXTENSION;
3190d522f475Smrg	screen->endExt = cell;
3191d522f475Smrg    }
3192f2e35a3aSmrg    ComputeSelect(xw, &(screen->startExt), &(screen->endExt), True, True);
3193d522f475Smrg
3194d522f475Smrg#if OPT_READLINE
3195d522f475Smrg    if (!isSameCELL(&(screen->startSel), &(screen->endSel)))
3196d522f475Smrg	ExtendingSelection = 1;
3197d522f475Smrg#endif
3198d522f475Smrg}
3199d522f475Smrg
3200d522f475Smrgstatic void
3201e0a2b6dfSmrgExtendExtend(XtermWidget xw, const CELL *cell)
3202d522f475Smrg{
3203956cc18dSsnj    TScreen *screen = TScreenOf(xw);
3204d522f475Smrg    int coord = Coordinate(screen, cell);
3205d522f475Smrg
3206d522f475Smrg    TRACE(("ExtendExtend row=%d, col=%d\n", cell->row, cell->col));
3207d522f475Smrg    if (screen->eventMode == LEFTEXTENSION
3208d522f475Smrg	&& ((coord + (screen->selectUnit != Select_CHAR))
3209d522f475Smrg	    > Coordinate(screen, &(screen->endSel)))) {
3210d522f475Smrg	/* Whoops, he's changed his mind.  Do RIGHTEXTENSION */
3211d522f475Smrg	screen->eventMode = RIGHTEXTENSION;
3212d522f475Smrg	screen->startExt = screen->saveStartR;
3213d522f475Smrg    } else if (screen->eventMode == RIGHTEXTENSION
3214d522f475Smrg	       && coord < Coordinate(screen, &(screen->startSel))) {
3215d522f475Smrg	/* Whoops, he's changed his mind.  Do LEFTEXTENSION */
3216d522f475Smrg	screen->eventMode = LEFTEXTENSION;
3217d522f475Smrg	screen->endExt = screen->saveEndR;
3218d522f475Smrg    }
3219d522f475Smrg    if (screen->eventMode == LEFTEXTENSION) {
3220d522f475Smrg	screen->startExt = *cell;
3221d522f475Smrg    } else {
3222d522f475Smrg	screen->endExt = *cell;
3223d522f475Smrg    }
3224f2e35a3aSmrg    ComputeSelect(xw, &(screen->startExt), &(screen->endExt), False, True);
3225d522f475Smrg
3226d522f475Smrg#if OPT_READLINE
3227d522f475Smrg    if (!isSameCELL(&(screen->startSel), &(screen->endSel)))
3228d522f475Smrg	ExtendingSelection = 1;
3229d522f475Smrg#endif
3230d522f475Smrg}
3231d522f475Smrg
3232d522f475Smrgvoid
3233d522f475SmrgHandleStartExtend(Widget w,
3234894e0ac8Smrg		  XEvent *event,	/* must be XButtonEvent* */
3235e0a2b6dfSmrg		  String *params,	/* unused */
3236d522f475Smrg		  Cardinal *num_params)		/* unused */
3237d522f475Smrg{
3238956cc18dSsnj    XtermWidget xw;
3239956cc18dSsnj
3240956cc18dSsnj    if ((xw = getXtermWidget(w)) != 0) {
3241f2e35a3aSmrg	TRACE_EVENT("HandleStartExtend", event, params, num_params);
3242956cc18dSsnj	do_start_extend(xw, event, params, num_params, False);
3243956cc18dSsnj    }
3244d522f475Smrg}
3245d522f475Smrg
3246d522f475Smrgvoid
3247d522f475SmrgHandleKeyboardStartExtend(Widget w,
3248894e0ac8Smrg			  XEvent *event,	/* must be XButtonEvent* */
3249e0a2b6dfSmrg			  String *params,	/* unused */
3250d522f475Smrg			  Cardinal *num_params)		/* unused */
3251d522f475Smrg{
3252956cc18dSsnj    XtermWidget xw;
3253956cc18dSsnj
3254956cc18dSsnj    if ((xw = getXtermWidget(w)) != 0) {
3255f2e35a3aSmrg	TRACE_EVENT("HandleKeyboardStartExtend", event, params, num_params);
3256956cc18dSsnj	do_start_extend(xw, event, params, num_params, True);
3257956cc18dSsnj    }
3258d522f475Smrg}
3259d522f475Smrg
3260d522f475Smrgvoid
3261e0a2b6dfSmrgScrollSelection(TScreen *screen, int amount, Bool always)
3262d522f475Smrg{
3263d522f475Smrg    int minrow = INX2ROW(screen, -screen->savedlines);
3264d522f475Smrg    int maxrow = INX2ROW(screen, screen->max_row);
3265d522f475Smrg    int maxcol = screen->max_col;
3266d522f475Smrg
3267d522f475Smrg#define scroll_update_one(cell) \
3268d522f475Smrg	(cell)->row += amount; \
3269d522f475Smrg	if ((cell)->row < minrow) { \
3270d522f475Smrg	    (cell)->row = minrow; \
3271d522f475Smrg	    (cell)->col = 0; \
3272d522f475Smrg	} \
3273d522f475Smrg	if ((cell)->row > maxrow) { \
3274d522f475Smrg	    (cell)->row = maxrow; \
3275d522f475Smrg	    (cell)->col = maxcol; \
3276d522f475Smrg	}
3277d522f475Smrg
3278d522f475Smrg    scroll_update_one(&(screen->startRaw));
3279d522f475Smrg    scroll_update_one(&(screen->endRaw));
3280d522f475Smrg    scroll_update_one(&(screen->startSel));
3281d522f475Smrg    scroll_update_one(&(screen->endSel));
3282d522f475Smrg
3283d522f475Smrg    scroll_update_one(&(screen->rawPos));
3284d522f475Smrg
3285d522f475Smrg    /*
3286d522f475Smrg     * If we are told to scroll the selection but it lies outside the scrolling
3287d522f475Smrg     * margins, then that could cause the selection to move (bad).  It is not
3288d522f475Smrg     * simple to fix, because this function is called both for the scrollbar
3289d522f475Smrg     * actions as well as application scrolling.  The 'always' flag is set in
3290d522f475Smrg     * the former case.  The rest of the logic handles the latter.
3291d522f475Smrg     */
3292d522f475Smrg    if (ScrnHaveSelection(screen)) {
3293d522f475Smrg	int adjust;
3294d522f475Smrg
3295d522f475Smrg	adjust = ROW2INX(screen, screen->startH.row);
3296d522f475Smrg	if (always
32970bd37d32Smrg	    || !ScrnHaveRowMargins(screen)
32980bd37d32Smrg	    || ScrnIsRowInMargins(screen, adjust)) {
3299d522f475Smrg	    scroll_update_one(&screen->startH);
3300d522f475Smrg	}
3301d522f475Smrg	adjust = ROW2INX(screen, screen->endH.row);
3302d522f475Smrg	if (always
33030bd37d32Smrg	    || !ScrnHaveRowMargins(screen)
33040bd37d32Smrg	    || ScrnIsRowInMargins(screen, adjust)) {
3305d522f475Smrg	    scroll_update_one(&screen->endH);
3306d522f475Smrg	}
3307d522f475Smrg    }
3308d522f475Smrg
3309d522f475Smrg    screen->startHCoord = Coordinate(screen, &screen->startH);
3310d522f475Smrg    screen->endHCoord = Coordinate(screen, &screen->endH);
3311d522f475Smrg}
3312d522f475Smrg
3313d522f475Smrg/*ARGSUSED*/
3314d522f475Smrgvoid
3315f2e35a3aSmrgResizeSelection(TScreen *screen, int rows, int cols)
3316d522f475Smrg{
3317d522f475Smrg    rows--;			/* decr to get 0-max */
3318d522f475Smrg    cols--;
3319d522f475Smrg
3320d522f475Smrg    if (screen->startRaw.row > rows)
3321d522f475Smrg	screen->startRaw.row = rows;
3322d522f475Smrg    if (screen->startSel.row > rows)
3323d522f475Smrg	screen->startSel.row = rows;
3324d522f475Smrg    if (screen->endRaw.row > rows)
3325d522f475Smrg	screen->endRaw.row = rows;
3326d522f475Smrg    if (screen->endSel.row > rows)
3327d522f475Smrg	screen->endSel.row = rows;
3328d522f475Smrg    if (screen->rawPos.row > rows)
3329d522f475Smrg	screen->rawPos.row = rows;
3330d522f475Smrg
3331d522f475Smrg    if (screen->startRaw.col > cols)
3332d522f475Smrg	screen->startRaw.col = cols;
3333d522f475Smrg    if (screen->startSel.col > cols)
3334d522f475Smrg	screen->startSel.col = cols;
3335d522f475Smrg    if (screen->endRaw.col > cols)
3336d522f475Smrg	screen->endRaw.col = cols;
3337d522f475Smrg    if (screen->endSel.col > cols)
3338d522f475Smrg	screen->endSel.col = cols;
3339d522f475Smrg    if (screen->rawPos.col > cols)
3340d522f475Smrg	screen->rawPos.col = cols;
3341d522f475Smrg}
3342d522f475Smrg
3343d522f475Smrg#if OPT_WIDE_CHARS
3344f2e35a3aSmrg#define isWideCell(row, col) isWideFrg((int)XTERM_CELL(row, col))
3345d522f475Smrg#endif
3346d522f475Smrg
3347d522f475Smrgstatic void
3348e0a2b6dfSmrgPointToCELL(TScreen *screen,
3349d522f475Smrg	    int y,
3350d522f475Smrg	    int x,
3351e0a2b6dfSmrg	    CELL *cell)
3352d522f475Smrg/* Convert pixel coordinates to character coordinates.
3353d522f475Smrg   Rows are clipped between firstValidRow and lastValidRow.
3354d522f475Smrg   Columns are clipped between to be 0 or greater, but are not clipped to some
3355d522f475Smrg       maximum value. */
3356d522f475Smrg{
3357d522f475Smrg    cell->row = (y - screen->border) / FontHeight(screen);
3358d522f475Smrg    if (cell->row < screen->firstValidRow)
3359d522f475Smrg	cell->row = screen->firstValidRow;
3360d522f475Smrg    else if (cell->row > screen->lastValidRow)
3361d522f475Smrg	cell->row = screen->lastValidRow;
3362d522f475Smrg    cell->col = (x - OriginX(screen)) / FontWidth(screen);
3363d522f475Smrg    if (cell->col < 0)
3364d522f475Smrg	cell->col = 0;
3365d522f475Smrg    else if (cell->col > MaxCols(screen)) {
3366d522f475Smrg	cell->col = MaxCols(screen);
3367d522f475Smrg    }
3368d522f475Smrg#if OPT_WIDE_CHARS
3369d522f475Smrg    /*
3370d522f475Smrg     * If we got a click on the right half of a doublewidth character,
3371d522f475Smrg     * pretend it happened on the left half.
3372d522f475Smrg     */
3373d522f475Smrg    if (cell->col > 0
3374d522f475Smrg	&& isWideCell(cell->row, cell->col - 1)
3375d522f475Smrg	&& (XTERM_CELL(cell->row, cell->col) == HIDDEN_CHAR)) {
3376d522f475Smrg	cell->col -= 1;
3377d522f475Smrg    }
3378d522f475Smrg#endif
3379d522f475Smrg}
3380d522f475Smrg
3381d522f475Smrg/*
3382d522f475Smrg * Find the last column at which text was drawn on the given row.
3383d522f475Smrg */
3384d522f475Smrgstatic int
338501037d57SmrgLastTextCol(TScreen *screen, CLineData *ld, int row)
3386d522f475Smrg{
338720d2c4d2Smrg    int i = -1;
3388d522f475Smrg
338920d2c4d2Smrg    if (ld != 0) {
339020d2c4d2Smrg	if (okScrnRow(screen, row)) {
33912e4f8982Smrg	    const IAttr *ch;
339220d2c4d2Smrg	    for (i = screen->max_col,
339320d2c4d2Smrg		 ch = ld->attribs + i;
339420d2c4d2Smrg		 i >= 0 && !(*ch & CHARDRAWN);
339520d2c4d2Smrg		 ch--, i--) {
339620d2c4d2Smrg		;
339720d2c4d2Smrg	    }
3398d522f475Smrg#if OPT_DEC_CHRSET
339920d2c4d2Smrg	    if (CSET_DOUBLE(GetLineDblCS(ld))) {
340020d2c4d2Smrg		i *= 2;
340120d2c4d2Smrg	    }
3402d522f475Smrg#endif
340320d2c4d2Smrg	}
3404d522f475Smrg    }
3405d522f475Smrg    return (i);
3406d522f475Smrg}
3407d522f475Smrg
3408d522f475Smrg#if !OPT_WIDE_CHARS
3409d522f475Smrg/*
3410d522f475Smrg** double click table for cut and paste in 8 bits
3411d522f475Smrg**
3412d522f475Smrg** This table is divided in four parts :
3413d522f475Smrg**
3414d522f475Smrg**	- control characters	[0,0x1f] U [0x80,0x9f]
3415d522f475Smrg**	- separators		[0x20,0x3f] U [0xa0,0xb9]
3416d522f475Smrg**	- binding characters	[0x40,0x7f] U [0xc0,0xff]
3417d522f475Smrg**	- exceptions
3418d522f475Smrg*/
3419d522f475Smrg/* *INDENT-OFF* */
3420d522f475Smrgstatic int charClass[256] =
3421d522f475Smrg{
3422d522f475Smrg/* NUL  SOH  STX  ETX  EOT  ENQ  ACK  BEL */
3423d522f475Smrg    32,  1,    1,   1,   1,   1,   1,   1,
34240bd37d32Smrg/*  BS   HT   NL   VT   FF   CR   SO   SI */
3425d522f475Smrg     1,  32,   1,   1,   1,   1,   1,   1,
3426d522f475Smrg/* DLE  DC1  DC2  DC3  DC4  NAK  SYN  ETB */
3427d522f475Smrg     1,   1,   1,   1,   1,   1,   1,   1,
3428d522f475Smrg/* CAN   EM  SUB  ESC   FS   GS   RS   US */
3429d522f475Smrg     1,   1,   1,   1,   1,   1,   1,   1,
3430d522f475Smrg/*  SP    !    "    #    $    %    &    ' */
3431d522f475Smrg    32,  33,  34,  35,  36,  37,  38,  39,
3432d522f475Smrg/*   (    )    *    +    ,    -    .    / */
3433d522f475Smrg    40,  41,  42,  43,  44,  45,  46,  47,
3434d522f475Smrg/*   0    1    2    3    4    5    6    7 */
3435d522f475Smrg    48,  48,  48,  48,  48,  48,  48,  48,
3436d522f475Smrg/*   8    9    :    ;    <    =    >    ? */
3437d522f475Smrg    48,  48,  58,  59,  60,  61,  62,  63,
3438d522f475Smrg/*   @    A    B    C    D    E    F    G */
3439d522f475Smrg    64,  48,  48,  48,  48,  48,  48,  48,
3440d522f475Smrg/*   H    I    J    K    L    M    N    O */
3441d522f475Smrg    48,  48,  48,  48,  48,  48,  48,  48,
3442d522f475Smrg/*   P    Q    R    S    T    U    V    W */
3443d522f475Smrg    48,  48,  48,  48,  48,  48,  48,  48,
3444d522f475Smrg/*   X    Y    Z    [    \    ]    ^    _ */
3445d522f475Smrg    48,  48,  48,  91,  92,  93,  94,  48,
3446d522f475Smrg/*   `    a    b    c    d    e    f    g */
3447d522f475Smrg    96,  48,  48,  48,  48,  48,  48,  48,
3448d522f475Smrg/*   h    i    j    k    l    m    n    o */
3449d522f475Smrg    48,  48,  48,  48,  48,  48,  48,  48,
3450d522f475Smrg/*   p    q    r    s    t    u    v    w */
3451d522f475Smrg    48,  48,  48,  48,  48,  48,  48,  48,
3452d522f475Smrg/*   x    y    z    {    |    }    ~  DEL */
3453d522f475Smrg    48,  48,  48, 123, 124, 125, 126,   1,
3454d522f475Smrg/* x80  x81  x82  x83  IND  NEL  SSA  ESA */
3455d522f475Smrg    1,    1,   1,   1,   1,   1,   1,   1,
3456d522f475Smrg/* HTS  HTJ  VTS  PLD  PLU   RI  SS2  SS3 */
3457d522f475Smrg    1,    1,   1,   1,   1,   1,   1,   1,
3458d522f475Smrg/* DCS  PU1  PU2  STS  CCH   MW  SPA  EPA */
3459d522f475Smrg    1,    1,   1,   1,   1,   1,   1,   1,
3460d522f475Smrg/* x98  x99  x9A  CSI   ST  OSC   PM  APC */
3461d522f475Smrg    1,    1,   1,   1,   1,   1,   1,   1,
3462d522f475Smrg/*   -    i   c/    L   ox   Y-    |   So */
3463d522f475Smrg    160, 161, 162, 163, 164, 165, 166, 167,
3464d522f475Smrg/*  ..   c0   ip   <<    _        R0    - */
3465d522f475Smrg    168, 169, 170, 171, 172, 173, 174, 175,
3466d522f475Smrg/*   o   +-    2    3    '    u   q|    . */
3467d522f475Smrg    176, 177, 178, 179, 180, 181, 182, 183,
3468d522f475Smrg/*   ,    1    2   >>  1/4  1/2  3/4    ? */
3469d522f475Smrg    184, 185, 186, 187, 188, 189, 190, 191,
3470d522f475Smrg/*  A`   A'   A^   A~   A:   Ao   AE   C, */
3471d522f475Smrg     48,  48,  48,  48,  48,  48,  48,  48,
3472d522f475Smrg/*  E`   E'   E^   E:   I`   I'   I^   I: */
3473d522f475Smrg     48,  48,  48,  48,  48,  48,  48,  48,
3474d522f475Smrg/*  D-   N~   O`   O'   O^   O~   O:    X */
3475d522f475Smrg     48,  48,  48,  48,  48,  48,  48, 215,
3476d522f475Smrg/*  O/   U`   U'   U^   U:   Y'    P    B */
3477d522f475Smrg     48,  48,  48,  48,  48,  48,  48,  48,
3478d522f475Smrg/*  a`   a'   a^   a~   a:   ao   ae   c, */
3479d522f475Smrg     48,  48,  48,  48,  48,  48,  48,  48,
3480d522f475Smrg/*  e`   e'   e^   e:    i`  i'   i^   i: */
3481d522f475Smrg     48,  48,  48,  48,  48,  48,  48,  48,
3482d522f475Smrg/*   d   n~   o`   o'   o^   o~   o:   -: */
3483d522f475Smrg     48,  48,  48,  48,  48,  48,  48, 247,
3484d522f475Smrg/*  o/   u`   u'   u^   u:   y'    P   y: */
3485d522f475Smrg     48,  48,  48,  48,  48,  48,  48,  48};
3486d522f475Smrg/* *INDENT-ON* */
3487d522f475Smrg
3488d522f475Smrgint
3489d522f475SmrgSetCharacterClassRange(int low,	/* in range of [0..255] */
3490d522f475Smrg		       int high,
3491d522f475Smrg		       int value)	/* arbitrary */
3492d522f475Smrg{
3493d522f475Smrg
3494d522f475Smrg    if (low < 0 || high > 255 || high < low)
3495d522f475Smrg	return (-1);
3496d522f475Smrg
3497d522f475Smrg    for (; low <= high; low++)
3498d522f475Smrg	charClass[low] = value;
3499d522f475Smrg
3500d522f475Smrg    return (0);
3501d522f475Smrg}
3502d522f475Smrg#endif
3503d522f475Smrg
3504d522f475Smrgstatic int
3505e0a2b6dfSmrgclass_of(LineData *ld, CELL *cell)
3506d522f475Smrg{
3507d522f475Smrg    CELL temp = *cell;
35080bd37d32Smrg    int result = 0;
3509d522f475Smrg
3510d522f475Smrg#if OPT_DEC_CHRSET
3511956cc18dSsnj    if (CSET_DOUBLE(GetLineDblCS(ld))) {
3512d522f475Smrg	temp.col /= 2;
3513d522f475Smrg    }
3514d522f475Smrg#endif
35150bd37d32Smrg    if (temp.col < (int) ld->lineSize)
35160bd37d32Smrg	result = CharacterClass((int) (ld->charData[temp.col]));
35170bd37d32Smrg    return result;
3518d522f475Smrg}
3519956cc18dSsnj
3520956cc18dSsnj#if OPT_WIDE_CHARS
3521956cc18dSsnj#define CClassSelects(name, cclass) \
3522956cc18dSsnj	 (CClassOf(name) == cclass \
3523956cc18dSsnj	 || XTERM_CELL(screen->name.row, screen->name.col) == HIDDEN_CHAR)
3524d522f475Smrg#else
3525956cc18dSsnj#define CClassSelects(name, cclass) \
3526956cc18dSsnj	 (class_of(ld.name, &((screen->name))) == cclass)
3527d522f475Smrg#endif
3528d522f475Smrg
3529956cc18dSsnj#define CClassOf(name) class_of(ld.name, &((screen->name)))
3530956cc18dSsnj
3531913cc679Smrg#if OPT_REPORT_CCLASS
3532913cc679Smrgstatic int
3533913cc679Smrgshow_cclass_range(int lo, int hi)
3534913cc679Smrg{
3535913cc679Smrg    int cclass = CharacterClass(lo);
3536913cc679Smrg    int ident = (cclass == lo);
3537913cc679Smrg    int more = 0;
3538913cc679Smrg    if (ident) {
3539913cc679Smrg	int ch;
3540913cc679Smrg	for (ch = lo + 1; ch <= hi; ch++) {
3541913cc679Smrg	    if (CharacterClass(ch) != ch) {
3542913cc679Smrg		ident = 0;
3543913cc679Smrg		break;
3544913cc679Smrg	    }
3545913cc679Smrg	}
3546913cc679Smrg	if (ident && (hi < 255)) {
3547913cc679Smrg	    ch = hi + 1;
3548913cc679Smrg	    if (CharacterClass(ch) == ch) {
3549913cc679Smrg		if (ch >= 255 || CharacterClass(ch + 1) != ch) {
3550913cc679Smrg		    more = 1;
3551913cc679Smrg		}
3552913cc679Smrg	    }
3553913cc679Smrg	}
3554913cc679Smrg    }
3555913cc679Smrg    if (!more) {
3556913cc679Smrg	if (lo == hi) {
3557913cc679Smrg	    printf("\t%d", lo);
3558913cc679Smrg	} else {
3559913cc679Smrg	    printf("\t%d-%d", lo, hi);
3560913cc679Smrg	}
3561913cc679Smrg	if (!ident)
3562913cc679Smrg	    printf(":%d", cclass);
3563913cc679Smrg	if (hi < 255)
3564913cc679Smrg	    printf(", \\");
3565913cc679Smrg	printf("\n");
3566913cc679Smrg    }
3567913cc679Smrg    return !more;
3568913cc679Smrg}
3569913cc679Smrg
3570913cc679Smrgvoid
3571913cc679Smrgreport_char_class(XtermWidget xw)
3572913cc679Smrg{
3573913cc679Smrg    /* simple table, to match documentation */
3574913cc679Smrg    static const char charnames[] =
3575913cc679Smrg    "NUL\0" "SOH\0" "STX\0" "ETX\0" "EOT\0" "ENQ\0" "ACK\0" "BEL\0"
3576913cc679Smrg    " BS\0" " HT\0" " NL\0" " VT\0" " NP\0" " CR\0" " SO\0" " SI\0"
3577913cc679Smrg    "DLE\0" "DC1\0" "DC2\0" "DC3\0" "DC4\0" "NAK\0" "SYN\0" "ETB\0"
3578913cc679Smrg    "CAN\0" " EM\0" "SUB\0" "ESC\0" " FS\0" " GS\0" " RS\0" " US\0"
3579913cc679Smrg    " SP\0" "  !\0" "  \"\0" "  #\0" "  $\0" "  %\0" "  &\0" "  '\0"
3580913cc679Smrg    "  (\0" "  )\0" "  *\0" "  +\0" "  ,\0" "  -\0" "  .\0" "  /\0"
3581913cc679Smrg    "  0\0" "  1\0" "  2\0" "  3\0" "  4\0" "  5\0" "  6\0" "  7\0"
3582913cc679Smrg    "  8\0" "  9\0" "  :\0" "  ;\0" "  <\0" "  =\0" "  >\0" "  ?\0"
3583913cc679Smrg    "  @\0" "  A\0" "  B\0" "  C\0" "  D\0" "  E\0" "  F\0" "  G\0"
3584913cc679Smrg    "  H\0" "  I\0" "  J\0" "  K\0" "  L\0" "  M\0" "  N\0" "  O\0"
3585913cc679Smrg    "  P\0" "  Q\0" "  R\0" "  S\0" "  T\0" "  U\0" "  V\0" "  W\0"
3586913cc679Smrg    "  X\0" "  Y\0" "  Z\0" "  [\0" "  \\\0" "  ]\0" "  ^\0" "  _\0"
3587913cc679Smrg    "  `\0" "  a\0" "  b\0" "  c\0" "  d\0" "  e\0" "  f\0" "  g\0"
3588913cc679Smrg    "  h\0" "  i\0" "  j\0" "  k\0" "  l\0" "  m\0" "  n\0" "  o\0"
3589913cc679Smrg    "  p\0" "  q\0" "  r\0" "  s\0" "  t\0" "  u\0" "  v\0" "  w\0"
3590913cc679Smrg    "  x\0" "  y\0" "  z\0" "  {\0" "  |\0" "  }\0" "  ~\0" "DEL\0"
3591913cc679Smrg    "x80\0" "x81\0" "x82\0" "x83\0" "IND\0" "NEL\0" "SSA\0" "ESA\0"
3592913cc679Smrg    "HTS\0" "HTJ\0" "VTS\0" "PLD\0" "PLU\0" " RI\0" "SS2\0" "SS3\0"
3593913cc679Smrg    "DCS\0" "PU1\0" "PU2\0" "STS\0" "CCH\0" " MW\0" "SPA\0" "EPA\0"
3594913cc679Smrg    "x98\0" "x99\0" "x9A\0" "CSI\0" " ST\0" "OSC\0" " PM\0" "APC\0"
3595913cc679Smrg    "  -\0" "  i\0" " c/\0" "  L\0" " ox\0" " Y-\0" "  |\0" " So\0"
3596913cc679Smrg    " ..\0" " c0\0" " ip\0" " <<\0" "  _\0" "   \0" " R0\0" "  -\0"
3597913cc679Smrg    "  o\0" " +-\0" "  2\0" "  3\0" "  '\0" "  u\0" " q|\0" "  .\0"
3598913cc679Smrg    "  ,\0" "  1\0" "  2\0" " >>\0" "1/4\0" "1/2\0" "3/4\0" "  ?\0"
3599913cc679Smrg    " A`\0" " A'\0" " A^\0" " A~\0" " A:\0" " Ao\0" " AE\0" " C,\0"
3600913cc679Smrg    " E`\0" " E'\0" " E^\0" " E:\0" " I`\0" " I'\0" " I^\0" " I:\0"
3601913cc679Smrg    " D-\0" " N~\0" " O`\0" " O'\0" " O^\0" " O~\0" " O:\0" "  X\0"
3602913cc679Smrg    " O/\0" " U`\0" " U'\0" " U^\0" " U:\0" " Y'\0" "  P\0" "  B\0"
3603913cc679Smrg    " a`\0" " a'\0" " a^\0" " a~\0" " a:\0" " ao\0" " ae\0" " c,\0"
3604913cc679Smrg    " e`\0" " e'\0" " e^\0" " e:\0" " i`\0" " i'\0" " i^\0" " i:\0"
3605913cc679Smrg    "  d\0" " n~\0" " o`\0" " o'\0" " o^\0" " o~\0" " o:\0" " -:\0"
3606913cc679Smrg    " o/\0" " u`\0" " u'\0" " u^\0" " u:\0" " y'\0" "  P\0" " y:\0";
3607913cc679Smrg    int ch, dh;
3608913cc679Smrg    int class_p;
3609913cc679Smrg
3610913cc679Smrg    (void) xw;
3611913cc679Smrg
3612913cc679Smrg    printf("static int charClass[256] = {\n");
3613913cc679Smrg    for (ch = 0; ch < 256; ++ch) {
3614913cc679Smrg	const char *s = charnames + (ch * 4);
3615913cc679Smrg	if ((ch & 7) == 0)
3616913cc679Smrg	    printf("/*");
3617913cc679Smrg	printf(" %s ", s);
3618913cc679Smrg	if (((ch + 1) & 7) == 0) {
3619913cc679Smrg	    printf("*/\n  ");
3620913cc679Smrg	    for (dh = ch - 7; dh <= ch; ++dh) {
3621913cc679Smrg		printf(" %3d%s", CharacterClass(dh), dh == 255 ? "};" : ",");
3622913cc679Smrg	    }
3623913cc679Smrg	    printf("\n");
3624913cc679Smrg	}
3625913cc679Smrg    }
3626913cc679Smrg
3627913cc679Smrg    /* print the table as if it were the charClass resource */
3628913cc679Smrg    printf("\n");
3629913cc679Smrg    printf("The table is equivalent to this \"charClass\" resource:\n");
3630913cc679Smrg    class_p = CharacterClass(dh = 0);
3631913cc679Smrg    for (ch = 0; ch < 256; ++ch) {
3632913cc679Smrg	int class_c = CharacterClass(ch);
3633913cc679Smrg	if (class_c != class_p) {
3634913cc679Smrg	    if (show_cclass_range(dh, ch - 1)) {
3635913cc679Smrg		dh = ch;
3636913cc679Smrg		class_p = class_c;
3637913cc679Smrg	    }
3638913cc679Smrg	}
3639913cc679Smrg    }
3640913cc679Smrg    if (dh < 255) {
3641913cc679Smrg	show_cclass_range(dh, 255);
3642913cc679Smrg    }
3643913cc679Smrg
3644913cc679Smrg    if_OPT_WIDE_CHARS(TScreenOf(xw), {
3645913cc679Smrg	/* if this is a wide-character configuration, print all intervals */
3646913cc679Smrg	report_wide_char_class();
3647913cc679Smrg    });
3648913cc679Smrg}
3649913cc679Smrg#endif
3650913cc679Smrg
3651d522f475Smrg/*
3652d522f475Smrg * If the given column is past the end of text on the given row, bump to the
3653d522f475Smrg * beginning of the next line.
3654d522f475Smrg */
3655d522f475Smrgstatic Boolean
3656e0a2b6dfSmrgokPosition(TScreen *screen,
3657e0a2b6dfSmrg	   LineData **ld,
3658e0a2b6dfSmrg	   CELL *cell)
3659d522f475Smrg{
366020d2c4d2Smrg    Boolean result = True;
366120d2c4d2Smrg
366220d2c4d2Smrg    if (cell->row > screen->max_row) {
366320d2c4d2Smrg	result = False;
3664f2e35a3aSmrg	TRACE(("okPosition cell row %d > screen max %d\n", cell->row, screen->max_row));
366520d2c4d2Smrg    } else if (cell->col > (LastTextCol(screen, *ld, cell->row) + 1)) {
3666f2e35a3aSmrg	TRACE(("okPosition cell col %d > screen max %d\n", cell->col,
3667f2e35a3aSmrg	       (LastTextCol(screen, *ld, cell->row) + 1)));
366820d2c4d2Smrg	if (cell->row < screen->max_row) {
3669f2e35a3aSmrg	    TRACE(("okPosition cell row %d < screen max %d\n", cell->row, screen->max_row));
367020d2c4d2Smrg	    cell->col = 0;
367120d2c4d2Smrg	    *ld = GET_LINEDATA(screen, ++cell->row);
367220d2c4d2Smrg	    result = False;
367320d2c4d2Smrg	}
3674d522f475Smrg    }
367520d2c4d2Smrg    return result;
3676d522f475Smrg}
3677d522f475Smrg
3678d522f475Smrgstatic void
3679e0a2b6dfSmrgtrimLastLine(TScreen *screen,
3680e0a2b6dfSmrg	     LineData **ld,
3681e0a2b6dfSmrg	     CELL *last)
3682d522f475Smrg{
368320d2c4d2Smrg    if (screen->cutNewline && last->row < screen->max_row) {
3684d522f475Smrg	last->col = 0;
3685956cc18dSsnj	*ld = GET_LINEDATA(screen, ++last->row);
3686d522f475Smrg    } else {
3687956cc18dSsnj	last->col = LastTextCol(screen, *ld, last->row) + 1;
3688d522f475Smrg    }
3689d522f475Smrg}
3690d522f475Smrg
3691d522f475Smrg#if OPT_SELECT_REGEX
3692d522f475Smrg/*
3693d522f475Smrg * Returns the first row of a wrapped line.
3694d522f475Smrg */
3695d522f475Smrgstatic int
3696e0a2b6dfSmrgfirstRowOfLine(TScreen *screen, int row, Bool visible)
3697d522f475Smrg{
3698956cc18dSsnj    LineData *ld = 0;
3699d522f475Smrg    int limit = visible ? 0 : -screen->savedlines;
3700d522f475Smrg
3701d522f475Smrg    while (row > limit &&
3702956cc18dSsnj	   (ld = GET_LINEDATA(screen, row - 1)) != 0 &&
3703956cc18dSsnj	   LineTstWrapped(ld)) {
3704d522f475Smrg	--row;
3705956cc18dSsnj    }
3706d522f475Smrg    return row;
3707d522f475Smrg}
3708d522f475Smrg
3709d522f475Smrg/*
3710d522f475Smrg * Returns the last row of a wrapped line.
3711d522f475Smrg */
3712d522f475Smrgstatic int
3713e0a2b6dfSmrglastRowOfLine(TScreen *screen, int row)
3714d522f475Smrg{
3715956cc18dSsnj    LineData *ld;
3716956cc18dSsnj
3717d522f475Smrg    while (row < screen->max_row &&
3718956cc18dSsnj	   (ld = GET_LINEDATA(screen, row)) != 0 &&
3719956cc18dSsnj	   LineTstWrapped(ld)) {
3720d522f475Smrg	++row;
3721956cc18dSsnj    }
3722d522f475Smrg    return row;
3723d522f475Smrg}
3724d522f475Smrg
3725d522f475Smrg/*
3726d522f475Smrg * Returns the number of cells on the range of rows.
3727d522f475Smrg */
3728d522f475Smrgstatic unsigned
3729e0a2b6dfSmrglengthOfLines(TScreen *screen, int firstRow, int lastRow)
3730d522f475Smrg{
3731d522f475Smrg    unsigned length = 0;
3732d522f475Smrg    int n;
3733d522f475Smrg
3734d522f475Smrg    for (n = firstRow; n <= lastRow; ++n) {
3735956cc18dSsnj	LineData *ld = GET_LINEDATA(screen, n);
3736956cc18dSsnj	int value = LastTextCol(screen, ld, n);
3737d522f475Smrg	if (value >= 0)
37382eaa94a1Schristos	    length += (unsigned) (value + 1);
3739d522f475Smrg    }
3740d522f475Smrg    return length;
3741d522f475Smrg}
3742d522f475Smrg
3743d522f475Smrg/*
3744d522f475Smrg * Make a copy of the wrapped-line which corresponds to the given row as a
3745d522f475Smrg * string of bytes.  Construct an index for the columns from the beginning of
3746d522f475Smrg * the line.
3747d522f475Smrg */
3748d522f475Smrgstatic char *
3749e0a2b6dfSmrgmake_indexed_text(TScreen *screen, int row, unsigned length, int *indexed)
3750d522f475Smrg{
3751d522f475Smrg    Char *result = 0;
375220d2c4d2Smrg    size_t need = (length + 1);
3753d522f475Smrg
3754d522f475Smrg    /*
3755d522f475Smrg     * Get a quick upper bound to the number of bytes needed, if the whole
3756d522f475Smrg     * string were UTF-8.
3757d522f475Smrg     */
3758d522f475Smrg    if_OPT_WIDE_CHARS(screen, {
3759956cc18dSsnj	need *= ((screen->lineExtra + 1) * 6);
3760d522f475Smrg    });
3761d522f475Smrg
3762d522f475Smrg    if ((result = TypeCallocN(Char, need + 1)) != 0) {
3763956cc18dSsnj	LineData *ld = GET_LINEDATA(screen, row);
3764d522f475Smrg	unsigned used = 0;
3765d522f475Smrg	Char *last = result;
3766d522f475Smrg
3767d522f475Smrg	do {
3768d522f475Smrg	    int col = 0;
3769956cc18dSsnj	    int limit = LastTextCol(screen, ld, row);
3770d522f475Smrg
3771d522f475Smrg	    while (col <= limit) {
3772d522f475Smrg		Char *next = last;
3773956cc18dSsnj		unsigned data = ld->charData[col];
3774d522f475Smrg
37750bd37d32Smrg		assert(col < (int) ld->lineSize);
3776d522f475Smrg		/* some internal points may not be drawn */
3777d522f475Smrg		if (data == 0)
3778d522f475Smrg		    data = ' ';
3779d522f475Smrg
3780d522f475Smrg		if_WIDE_OR_NARROW(screen, {
3781d522f475Smrg		    next = convertToUTF8(last, data);
3782d522f475Smrg		}
3783d522f475Smrg		, {
3784d522f475Smrg		    *next++ = CharOf(data);
3785d522f475Smrg		});
3786d522f475Smrg
3787d522f475Smrg		if_OPT_WIDE_CHARS(screen, {
3788956cc18dSsnj		    size_t off;
3789956cc18dSsnj		    for_each_combData(off, ld) {
3790956cc18dSsnj			data = ld->combData[off][col];
3791956cc18dSsnj			if (data == 0)
3792d522f475Smrg			    break;
3793d522f475Smrg			next = convertToUTF8(next, data);
3794d522f475Smrg		    }
3795d522f475Smrg		});
3796d522f475Smrg
379720d2c4d2Smrg		indexed[used] = (int) (last - result);
3798d522f475Smrg		*next = 0;
3799d522f475Smrg		/* TRACE(("index[%d.%d] %d:%s\n", row, used, indexed[used], last)); */
3800d522f475Smrg		last = next;
3801d522f475Smrg		++used;
3802d522f475Smrg		++col;
380320d2c4d2Smrg		indexed[used] = (int) (next - result);
3804d522f475Smrg	    }
3805d522f475Smrg	} while (used < length &&
3806956cc18dSsnj		 LineTstWrapped(ld) &&
3807956cc18dSsnj		 (ld = GET_LINEDATA(screen, ++row)) != 0 &&
3808956cc18dSsnj		 row < screen->max_row);
3809d522f475Smrg    }
3810d522f475Smrg    /* TRACE(("result:%s\n", result)); */
3811d522f475Smrg    return (char *) result;
3812d522f475Smrg}
3813d522f475Smrg
3814d522f475Smrg/*
3815d522f475Smrg * Find the column given an offset into the character string by using the
3816d522f475Smrg * index constructed in make_indexed_text().
3817d522f475Smrg */
3818d522f475Smrgstatic int
3819d522f475SmrgindexToCol(int *indexed, int len, int off)
3820d522f475Smrg{
3821d522f475Smrg    int col = 0;
3822d522f475Smrg    while (indexed[col] < len) {
3823d522f475Smrg	if (indexed[col] >= off)
3824d522f475Smrg	    break;
3825d522f475Smrg	++col;
3826d522f475Smrg    }
3827d522f475Smrg    return col;
3828d522f475Smrg}
3829d522f475Smrg
3830d522f475Smrg/*
3831d522f475Smrg * Given a row number, and a column offset from that (which may be wrapped),
3832d522f475Smrg * set the cell to the actual row/column values.
3833d522f475Smrg */
3834d522f475Smrgstatic void
3835e0a2b6dfSmrgcolumnToCell(TScreen *screen, int row, int col, CELL *cell)
3836d522f475Smrg{
3837d522f475Smrg    while (row < screen->max_row) {
383801037d57Smrg	CLineData *ld = GET_LINEDATA(screen, row);
3839956cc18dSsnj	int last = LastTextCol(screen, ld, row);
3840d522f475Smrg
3841d522f475Smrg	/* TRACE(("last(%d) = %d, have %d\n", row, last, col)); */
3842d522f475Smrg	if (col <= last) {
3843d522f475Smrg	    break;
3844d522f475Smrg	}
3845d522f475Smrg	/*
3846d522f475Smrg	 * Stop if the current row does not wrap (does not continue the current
3847d522f475Smrg	 * line).
3848d522f475Smrg	 */
3849956cc18dSsnj	if (!LineTstWrapped(ld)) {
3850d522f475Smrg	    col = last + 1;
3851d522f475Smrg	    break;
3852d522f475Smrg	}
3853d522f475Smrg	col -= (last + 1);
3854d522f475Smrg	++row;
3855d522f475Smrg    }
3856d522f475Smrg    if (col < 0)
3857d522f475Smrg	col = 0;
3858d522f475Smrg    cell->row = row;
3859d522f475Smrg    cell->col = col;
3860d522f475Smrg}
3861d522f475Smrg
3862d522f475Smrg/*
3863d522f475Smrg * Given a cell, find the corresponding column offset.
3864d522f475Smrg */
3865d522f475Smrgstatic int
3866e0a2b6dfSmrgcellToColumn(TScreen *screen, CELL *cell)
3867d522f475Smrg{
386801037d57Smrg    CLineData *ld = 0;
3869d522f475Smrg    int col = cell->col;
3870d522f475Smrg    int row = firstRowOfLine(screen, cell->row, False);
3871d522f475Smrg    while (row < cell->row) {
3872956cc18dSsnj	ld = GET_LINEDATA(screen, row);
3873956cc18dSsnj	col += LastTextCol(screen, ld, row++);
3874d522f475Smrg    }
3875956cc18dSsnj#if OPT_DEC_CHRSET
3876956cc18dSsnj    if (ld == 0)
3877956cc18dSsnj	ld = GET_LINEDATA(screen, row);
3878956cc18dSsnj    if (CSET_DOUBLE(GetLineDblCS(ld)))
3879956cc18dSsnj	col /= 2;
3880956cc18dSsnj#endif
3881d522f475Smrg    return col;
3882d522f475Smrg}
3883d522f475Smrg
3884d522f475Smrgstatic void
3885e0a2b6dfSmrgdo_select_regex(TScreen *screen, CELL *startc, CELL *endc)
3886d522f475Smrg{
3887956cc18dSsnj    LineData *ld = GET_LINEDATA(screen, startc->row);
3888d522f475Smrg    int inx = ((screen->numberOfClicks - 1) % screen->maxClicks);
3889d522f475Smrg    char *expr = screen->selectExpr[inx];
3890d522f475Smrg    regex_t preg;
3891d522f475Smrg    regmatch_t match;
3892d522f475Smrg
389301037d57Smrg    TRACE(("Select_REGEX[%d]:%s\n", inx, NonNull(expr)));
3894956cc18dSsnj    if (okPosition(screen, &ld, startc) && expr != 0) {
3895d522f475Smrg	if (regcomp(&preg, expr, REG_EXTENDED) == 0) {
3896d522f475Smrg	    int firstRow = firstRowOfLine(screen, startc->row, True);
3897d522f475Smrg	    int lastRow = lastRowOfLine(screen, firstRow);
3898d522f475Smrg	    unsigned size = lengthOfLines(screen, firstRow, lastRow);
3899d522f475Smrg	    int actual = cellToColumn(screen, startc);
39002e4f8982Smrg	    int *indexed;
3901d522f475Smrg
3902d522f475Smrg	    TRACE(("regcomp ok rows %d..%d bytes %d\n",
3903d522f475Smrg		   firstRow, lastRow, size));
3904d522f475Smrg
3905d522f475Smrg	    if ((indexed = TypeCallocN(int, size + 1)) != 0) {
39062e4f8982Smrg		char *search;
3907d522f475Smrg		if ((search = make_indexed_text(screen,
3908d522f475Smrg						firstRow,
3909d522f475Smrg						size,
3910d522f475Smrg						indexed)) != 0) {
39112eaa94a1Schristos		    int len = (int) strlen(search);
3912d522f475Smrg		    int col;
3913d522f475Smrg		    int best_col = -1;
3914d522f475Smrg		    int best_len = -1;
3915d522f475Smrg
3916913cc679Smrg		    startc->row = 0;
3917913cc679Smrg		    startc->col = 0;
3918913cc679Smrg		    endc->row = 0;
3919913cc679Smrg		    endc->col = 0;
3920913cc679Smrg
3921d522f475Smrg		    for (col = 0; indexed[col] < len; ++col) {
3922d522f475Smrg			if (regexec(&preg,
3923d522f475Smrg				    search + indexed[col],
392420d2c4d2Smrg				    (size_t) 1, &match, 0) == 0) {
3925894e0ac8Smrg			    int start_inx = (int) (match.rm_so + indexed[col]);
3926894e0ac8Smrg			    int finis_inx = (int) (match.rm_eo + indexed[col]);
3927d522f475Smrg			    int start_col = indexToCol(indexed, len, start_inx);
3928d522f475Smrg			    int finis_col = indexToCol(indexed, len, finis_inx);
3929d522f475Smrg
3930d522f475Smrg			    if (start_col <= actual &&
3931913cc679Smrg				actual <= finis_col) {
3932d522f475Smrg				int test = finis_col - start_col;
3933d522f475Smrg				if (best_len < test) {
3934d522f475Smrg				    best_len = test;
3935d522f475Smrg				    best_col = start_col;
3936d522f475Smrg				    TRACE(("match column %d len %d\n",
3937d522f475Smrg					   best_col,
3938d522f475Smrg					   best_len));
3939d522f475Smrg				}
3940d522f475Smrg			    }
3941d522f475Smrg			}
3942d522f475Smrg		    }
3943d522f475Smrg		    if (best_col >= 0) {
3944d522f475Smrg			int best_nxt = best_col + best_len;
3945d522f475Smrg			columnToCell(screen, firstRow, best_col, startc);
3946d522f475Smrg			columnToCell(screen, firstRow, best_nxt, endc);
3947d522f475Smrg			TRACE(("search::%s\n", search));
3948d522f475Smrg			TRACE(("indexed:%d..%d -> %d..%d\n",
3949d522f475Smrg			       best_col, best_nxt,
3950d522f475Smrg			       indexed[best_col],
3951d522f475Smrg			       indexed[best_nxt]));
3952d522f475Smrg			TRACE(("matched:%d:%s\n",
3953d522f475Smrg			       indexed[best_nxt] + 1 -
3954d522f475Smrg			       indexed[best_col],
3955956cc18dSsnj			       visibleChars((Char *) (search + indexed[best_col]),
3956d522f475Smrg					    (unsigned) (indexed[best_nxt] +
3957d522f475Smrg							1 -
3958d522f475Smrg							indexed[best_col]))));
3959d522f475Smrg		    }
3960d522f475Smrg		    free(search);
3961d522f475Smrg		}
3962d522f475Smrg		free(indexed);
3963956cc18dSsnj#if OPT_DEC_CHRSET
3964956cc18dSsnj		if ((ld = GET_LINEDATA(screen, startc->row)) != 0) {
3965956cc18dSsnj		    if (CSET_DOUBLE(GetLineDblCS(ld)))
3966956cc18dSsnj			startc->col *= 2;
3967956cc18dSsnj		}
3968956cc18dSsnj		if ((ld = GET_LINEDATA(screen, endc->row)) != 0) {
3969956cc18dSsnj		    if (CSET_DOUBLE(GetLineDblCS(ld)))
3970956cc18dSsnj			endc->col *= 2;
3971956cc18dSsnj		}
3972956cc18dSsnj#endif
3973d522f475Smrg	    }
3974d522f475Smrg	    regfree(&preg);
3975d522f475Smrg	}
3976d522f475Smrg    }
3977d522f475Smrg}
3978d522f475Smrg#endif /* OPT_SELECT_REGEX */
3979d522f475Smrg
3980956cc18dSsnj#define InitRow(name) \
3981956cc18dSsnj	ld.name = GET_LINEDATA(screen, screen->name.row)
3982956cc18dSsnj
3983956cc18dSsnj#define NextRow(name) \
3984956cc18dSsnj	ld.name = GET_LINEDATA(screen, ++screen->name.row)
3985956cc18dSsnj
3986956cc18dSsnj#define PrevRow(name) \
3987956cc18dSsnj	ld.name = GET_LINEDATA(screen, --screen->name.row)
3988956cc18dSsnj
398920d2c4d2Smrg#define MoreRows(name) \
399020d2c4d2Smrg	(screen->name.row < screen->max_row)
399120d2c4d2Smrg
3992956cc18dSsnj#define isPrevWrapped(name) \
3993956cc18dSsnj	(screen->name.row > 0 \
3994956cc18dSsnj	   && (ltmp = GET_LINEDATA(screen, screen->name.row - 1)) != 0 \
3995956cc18dSsnj	   && LineTstWrapped(ltmp))
3996956cc18dSsnj
3997d522f475Smrg/*
3998d522f475Smrg * sets startSel endSel
3999d522f475Smrg * ensuring that they have legal values
4000d522f475Smrg */
4001d522f475Smrgstatic void
4002d522f475SmrgComputeSelect(XtermWidget xw,
4003e0a2b6dfSmrg	      CELL *startc,
4004e0a2b6dfSmrg	      CELL *endc,
4005f2e35a3aSmrg	      Bool extend,
4006f2e35a3aSmrg	      Bool normal)
4007d522f475Smrg{
4008956cc18dSsnj    TScreen *screen = TScreenOf(xw);
4009956cc18dSsnj
4010d522f475Smrg    int cclass;
4011d522f475Smrg    CELL first = *startc;
4012d522f475Smrg    CELL last = *endc;
4013956cc18dSsnj    Boolean ignored = False;
4014956cc18dSsnj
4015956cc18dSsnj    struct {
4016956cc18dSsnj	LineData *startSel;
4017956cc18dSsnj	LineData *endSel;
4018956cc18dSsnj    } ld;
4019956cc18dSsnj    LineData *ltmp;
4020d522f475Smrg
4021d522f475Smrg    TRACE(("ComputeSelect(startRow=%d, startCol=%d, endRow=%d, endCol=%d, %sextend)\n",
4022d522f475Smrg	   first.row, first.col,
4023d522f475Smrg	   last.row, last.col,
4024d522f475Smrg	   extend ? "" : "no"));
4025d522f475Smrg
4026d522f475Smrg#if OPT_WIDE_CHARS
4027d522f475Smrg    if (first.col > 1
4028d522f475Smrg	&& isWideCell(first.row, first.col - 1)
4029d522f475Smrg	&& XTERM_CELL(first.row, first.col - 0) == HIDDEN_CHAR) {
403020d2c4d2Smrg	TRACE(("Adjusting start. Changing downwards from %i.\n", first.col));
4031d522f475Smrg	first.col -= 1;
4032d522f475Smrg	if (last.col == (first.col + 1))
4033d522f475Smrg	    last.col--;
4034d522f475Smrg    }
4035d522f475Smrg
4036d522f475Smrg    if (last.col > 1
4037d522f475Smrg	&& isWideCell(last.row, last.col - 1)
4038d522f475Smrg	&& XTERM_CELL(last.row, last.col) == HIDDEN_CHAR) {
4039d522f475Smrg	last.col += 1;
4040d522f475Smrg    }
4041d522f475Smrg#endif
4042d522f475Smrg
4043d522f475Smrg    if (Coordinate(screen, &first) <= Coordinate(screen, &last)) {
4044d522f475Smrg	screen->startSel = screen->startRaw = first;
4045d522f475Smrg	screen->endSel = screen->endRaw = last;
4046d522f475Smrg    } else {			/* Swap them */
4047d522f475Smrg	screen->startSel = screen->startRaw = last;
4048d522f475Smrg	screen->endSel = screen->endRaw = first;
4049d522f475Smrg    }
4050d522f475Smrg
4051956cc18dSsnj    InitRow(startSel);
4052956cc18dSsnj    InitRow(endSel);
4053956cc18dSsnj
4054d522f475Smrg    switch (screen->selectUnit) {
4055d522f475Smrg    case Select_CHAR:
4056956cc18dSsnj	(void) okPosition(screen, &(ld.startSel), &(screen->startSel));
4057956cc18dSsnj	(void) okPosition(screen, &(ld.endSel), &(screen->endSel));
4058d522f475Smrg	break;
4059d522f475Smrg
4060d522f475Smrg    case Select_WORD:
4061d522f475Smrg	TRACE(("Select_WORD\n"));
4062956cc18dSsnj	if (okPosition(screen, &(ld.startSel), &(screen->startSel))) {
4063f2e35a3aSmrg	    CELL mark;
4064956cc18dSsnj	    cclass = CClassOf(startSel);
4065f2e35a3aSmrg	    TRACE(("...starting with class %d\n", cclass));
4066d522f475Smrg	    do {
4067f2e35a3aSmrg		mark = screen->startSel;
4068d522f475Smrg		--screen->startSel.col;
4069956cc18dSsnj		if (screen->startSel.col < 0
4070956cc18dSsnj		    && isPrevWrapped(startSel)) {
4071956cc18dSsnj		    PrevRow(startSel);
4072956cc18dSsnj		    screen->startSel.col = LastTextCol(screen, ld.startSel, screen->startSel.row);
4073d522f475Smrg		}
4074d522f475Smrg	    } while (screen->startSel.col >= 0
4075956cc18dSsnj		     && CClassSelects(startSel, cclass));
4076f2e35a3aSmrg	    if (normal)
4077f2e35a3aSmrg		++screen->startSel.col;
4078f2e35a3aSmrg	    else
4079f2e35a3aSmrg		screen->startSel = mark;
4080d522f475Smrg	}
4081d522f475Smrg#if OPT_WIDE_CHARS
4082f2e35a3aSmrg#define SkipHiddenCell(mark) \
4083f2e35a3aSmrg	if (mark.col && XTERM_CELL(mark.row, mark.col) == HIDDEN_CHAR) \
4084f2e35a3aSmrg	    mark.col++
4085f2e35a3aSmrg#else
4086f2e35a3aSmrg#define SkipHiddenCell(mark)	/* nothing */
4087d522f475Smrg#endif
4088f2e35a3aSmrg	SkipHiddenCell(screen->startSel);
4089f2e35a3aSmrg
4090f2e35a3aSmrg	if (!normal) {
4091f2e35a3aSmrg	    screen->endSel = screen->startSel;
4092f2e35a3aSmrg	    ld.endSel = ld.startSel;
4093f2e35a3aSmrg	}
4094d522f475Smrg
4095956cc18dSsnj	if (okPosition(screen, &(ld.endSel), &(screen->endSel))) {
40962e4f8982Smrg	    int length = LastTextCol(screen, ld.endSel, screen->endSel.row);
4097956cc18dSsnj	    cclass = CClassOf(endSel);
4098f2e35a3aSmrg	    TRACE(("...ending with class %d\n", cclass));
4099d522f475Smrg	    do {
4100d522f475Smrg		++screen->endSel.col;
4101d522f475Smrg		if (screen->endSel.col > length
4102956cc18dSsnj		    && LineTstWrapped(ld.endSel)) {
410320d2c4d2Smrg		    if (!MoreRows(endSel))
410420d2c4d2Smrg			break;
4105d522f475Smrg		    screen->endSel.col = 0;
4106956cc18dSsnj		    NextRow(endSel);
4107956cc18dSsnj		    length = LastTextCol(screen, ld.endSel, screen->endSel.row);
4108d522f475Smrg		}
4109d522f475Smrg	    } while (screen->endSel.col <= length
4110956cc18dSsnj		     && CClassSelects(endSel, cclass));
4111f2e35a3aSmrg	    if (normal
4112f2e35a3aSmrg		&& screen->endSel.col > length + 1
411320d2c4d2Smrg		&& MoreRows(endSel)) {
4114d522f475Smrg		screen->endSel.col = 0;
4115956cc18dSsnj		NextRow(endSel);
4116d522f475Smrg	    }
4117d522f475Smrg	}
4118f2e35a3aSmrg	SkipHiddenCell(screen->endSel);
4119d522f475Smrg
4120d522f475Smrg	screen->saveStartW = screen->startSel;
4121d522f475Smrg	break;
4122d522f475Smrg
4123d522f475Smrg    case Select_LINE:
4124d522f475Smrg	TRACE(("Select_LINE\n"));
412520d2c4d2Smrg	while (LineTstWrapped(ld.endSel)
412620d2c4d2Smrg	       && MoreRows(endSel)) {
4127956cc18dSsnj	    NextRow(endSel);
4128d522f475Smrg	}
4129d522f475Smrg	if (screen->cutToBeginningOfLine
4130d522f475Smrg	    || screen->startSel.row < screen->saveStartW.row) {
4131d522f475Smrg	    screen->startSel.col = 0;
4132956cc18dSsnj	    while (isPrevWrapped(startSel)) {
4133956cc18dSsnj		PrevRow(startSel);
4134d522f475Smrg	    }
4135d522f475Smrg	} else if (!extend) {
4136d522f475Smrg	    if ((first.row < screen->saveStartW.row)
4137d522f475Smrg		|| (isSameRow(&first, &(screen->saveStartW))
4138d522f475Smrg		    && first.col < screen->saveStartW.col)) {
4139d522f475Smrg		screen->startSel.col = 0;
4140956cc18dSsnj		while (isPrevWrapped(startSel)) {
4141956cc18dSsnj		    PrevRow(startSel);
4142d522f475Smrg		}
4143d522f475Smrg	    } else {
4144d522f475Smrg		screen->startSel = screen->saveStartW;
4145d522f475Smrg	    }
4146d522f475Smrg	}
4147956cc18dSsnj	trimLastLine(screen, &(ld.endSel), &(screen->endSel));
4148d522f475Smrg	break;
4149d522f475Smrg
4150d522f475Smrg    case Select_GROUP:		/* paragraph */
4151d522f475Smrg	TRACE(("Select_GROUP\n"));
4152956cc18dSsnj	if (okPosition(screen, &(ld.startSel), &(screen->startSel))) {
4153d522f475Smrg	    /* scan backward for beginning of group */
4154d522f475Smrg	    while (screen->startSel.row > 0 &&
4155956cc18dSsnj		   (LastTextCol(screen, ld.startSel, screen->startSel.row -
4156956cc18dSsnj				1) > 0 ||
4157956cc18dSsnj		    isPrevWrapped(startSel))) {
4158956cc18dSsnj		PrevRow(startSel);
4159d522f475Smrg	    }
4160d522f475Smrg	    screen->startSel.col = 0;
4161d522f475Smrg	    /* scan forward for end of group */
416220d2c4d2Smrg	    while (MoreRows(endSel) &&
4163956cc18dSsnj		   (LastTextCol(screen, ld.endSel, screen->endSel.row + 1) >
4164956cc18dSsnj		    0 ||
4165956cc18dSsnj		    LineTstWrapped(ld.endSel))) {
4166956cc18dSsnj		NextRow(endSel);
4167d522f475Smrg	    }
4168956cc18dSsnj	    trimLastLine(screen, &(ld.endSel), &(screen->endSel));
4169d522f475Smrg	}
4170d522f475Smrg	break;
4171d522f475Smrg
4172d522f475Smrg    case Select_PAGE:		/* everything one can see */
4173d522f475Smrg	TRACE(("Select_PAGE\n"));
4174d522f475Smrg	screen->startSel.row = 0;
4175d522f475Smrg	screen->startSel.col = 0;
417620d2c4d2Smrg	screen->endSel.row = MaxRows(screen);
4177d522f475Smrg	screen->endSel.col = 0;
4178d522f475Smrg	break;
4179d522f475Smrg
4180d522f475Smrg    case Select_ALL:		/* counts scrollback if in normal screen */
4181d522f475Smrg	TRACE(("Select_ALL\n"));
4182d522f475Smrg	screen->startSel.row = -screen->savedlines;
4183d522f475Smrg	screen->startSel.col = 0;
418420d2c4d2Smrg	screen->endSel.row = MaxRows(screen);
4185d522f475Smrg	screen->endSel.col = 0;
4186d522f475Smrg	break;
4187d522f475Smrg
4188d522f475Smrg#if OPT_SELECT_REGEX
4189d522f475Smrg    case Select_REGEX:
4190d522f475Smrg	do_select_regex(screen, &(screen->startSel), &(screen->endSel));
4191d522f475Smrg	break;
4192d522f475Smrg#endif
4193d522f475Smrg
4194d522f475Smrg    case NSELECTUNITS:		/* always ignore */
4195956cc18dSsnj	ignored = True;
4196956cc18dSsnj	break;
4197d522f475Smrg    }
4198d522f475Smrg
4199956cc18dSsnj    if (!ignored) {
4200956cc18dSsnj	/* check boundaries */
4201956cc18dSsnj	ScrollSelection(screen, 0, False);
4202956cc18dSsnj	TrackText(xw, &(screen->startSel), &(screen->endSel));
4203956cc18dSsnj    }
4204d522f475Smrg
4205d522f475Smrg    return;
4206d522f475Smrg}
4207d522f475Smrg
4208d522f475Smrg/* Guaranteed (first.row, first.col) <= (last.row, last.col) */
4209d522f475Smrgstatic void
4210d522f475SmrgTrackText(XtermWidget xw,
4211e0a2b6dfSmrg	  const CELL *firstp,
4212e0a2b6dfSmrg	  const CELL *lastp)
4213d522f475Smrg{
4214956cc18dSsnj    TScreen *screen = TScreenOf(xw);
4215d522f475Smrg    int from, to;
4216d522f475Smrg    CELL old_start, old_end;
4217d522f475Smrg    CELL first = *firstp;
4218d522f475Smrg    CELL last = *lastp;
4219d522f475Smrg
4220d522f475Smrg    TRACE(("TrackText(first=%d,%d, last=%d,%d)\n",
4221d522f475Smrg	   first.row, first.col, last.row, last.col));
4222d522f475Smrg
4223d522f475Smrg    old_start = screen->startH;
4224d522f475Smrg    old_end = screen->endH;
42250bd37d32Smrg    TRACE(("...previous(first=%d,%d, last=%d,%d)\n",
42260bd37d32Smrg	   old_start.row, old_start.col,
42270bd37d32Smrg	   old_end.row, old_end.col));
4228d522f475Smrg    if (isSameCELL(&first, &old_start) &&
42290bd37d32Smrg	isSameCELL(&last, &old_end)) {
4230d522f475Smrg	return;
42310bd37d32Smrg    }
42320bd37d32Smrg
4233d522f475Smrg    screen->startH = first;
4234d522f475Smrg    screen->endH = last;
4235d522f475Smrg    from = Coordinate(screen, &screen->startH);
4236d522f475Smrg    to = Coordinate(screen, &screen->endH);
4237d522f475Smrg    if (to <= screen->startHCoord || from > screen->endHCoord) {
4238d522f475Smrg	/* No overlap whatsoever between old and new hilite */
4239d522f475Smrg	ReHiliteText(xw, &old_start, &old_end);
4240d522f475Smrg	ReHiliteText(xw, &first, &last);
4241d522f475Smrg    } else {
4242d522f475Smrg	if (from < screen->startHCoord) {
4243d522f475Smrg	    /* Extend left end */
4244d522f475Smrg	    ReHiliteText(xw, &first, &old_start);
4245d522f475Smrg	} else if (from > screen->startHCoord) {
4246d522f475Smrg	    /* Shorten left end */
4247d522f475Smrg	    ReHiliteText(xw, &old_start, &first);
4248d522f475Smrg	}
4249d522f475Smrg	if (to > screen->endHCoord) {
4250d522f475Smrg	    /* Extend right end */
4251d522f475Smrg	    ReHiliteText(xw, &old_end, &last);
4252d522f475Smrg	} else if (to < screen->endHCoord) {
4253d522f475Smrg	    /* Shorten right end */
4254d522f475Smrg	    ReHiliteText(xw, &last, &old_end);
4255d522f475Smrg	}
4256d522f475Smrg    }
4257d522f475Smrg    screen->startHCoord = from;
4258d522f475Smrg    screen->endHCoord = to;
4259d522f475Smrg}
4260d522f475Smrg
4261f2e35a3aSmrgstatic void
4262f2e35a3aSmrgUnHiliteText(XtermWidget xw)
4263f2e35a3aSmrg{
4264f2e35a3aSmrg    TrackText(xw, &zeroCELL, &zeroCELL);
4265f2e35a3aSmrg}
4266f2e35a3aSmrg
4267d522f475Smrg/* Guaranteed that (first->row, first->col) <= (last->row, last->col) */
4268d522f475Smrgstatic void
4269d522f475SmrgReHiliteText(XtermWidget xw,
4270e0a2b6dfSmrg	     CELL *firstp,
4271e0a2b6dfSmrg	     CELL *lastp)
4272d522f475Smrg{
4273956cc18dSsnj    TScreen *screen = TScreenOf(xw);
4274d522f475Smrg    CELL first = *firstp;
4275d522f475Smrg    CELL last = *lastp;
4276d522f475Smrg
4277d522f475Smrg    TRACE(("ReHiliteText from %d.%d to %d.%d\n",
4278d522f475Smrg	   first.row, first.col, last.row, last.col));
4279d522f475Smrg
4280d522f475Smrg    if (first.row < 0)
4281d522f475Smrg	first.row = first.col = 0;
4282d522f475Smrg    else if (first.row > screen->max_row)
4283d522f475Smrg	return;			/* nothing to do, since last.row >= first.row */
4284d522f475Smrg
4285d522f475Smrg    if (last.row < 0)
4286d522f475Smrg	return;			/* nothing to do, since first.row <= last.row */
4287d522f475Smrg    else if (last.row > screen->max_row) {
4288d522f475Smrg	last.row = screen->max_row;
4289d522f475Smrg	last.col = MaxCols(screen);
4290d522f475Smrg    }
4291d522f475Smrg    if (isSameCELL(&first, &last))
4292d522f475Smrg	return;
4293d522f475Smrg
4294d522f475Smrg    if (!isSameRow(&first, &last)) {	/* do multiple rows */
42952e4f8982Smrg	int i;
4296d522f475Smrg	if ((i = screen->max_col - first.col + 1) > 0) {	/* first row */
4297d522f475Smrg	    ScrnRefresh(xw, first.row, first.col, 1, i, True);
4298d522f475Smrg	}
4299d522f475Smrg	if ((i = last.row - first.row - 1) > 0) {	/* middle rows */
4300d522f475Smrg	    ScrnRefresh(xw, first.row + 1, 0, i, MaxCols(screen), True);
4301d522f475Smrg	}
4302d522f475Smrg	if (last.col > 0 && last.row <= screen->max_row) {	/* last row */
4303d522f475Smrg	    ScrnRefresh(xw, last.row, 0, 1, last.col, True);
4304d522f475Smrg	}
4305d522f475Smrg    } else {			/* do single row */
4306d522f475Smrg	ScrnRefresh(xw, first.row, first.col, 1, last.col - first.col, True);
4307d522f475Smrg    }
4308d522f475Smrg}
4309d522f475Smrg
4310d522f475Smrg/*
4311913cc679Smrg * Guaranteed that (cellc->row, cellc->col) <= (cell->row, cell->col),
4312913cc679Smrg * and that both points are valid
4313d522f475Smrg * (may have cell->row = screen->max_row+1, cell->col = 0).
4314d522f475Smrg */
4315d522f475Smrgstatic void
4316d522f475SmrgSaltTextAway(XtermWidget xw,
4317f2e35a3aSmrg	     int which,
4318e0a2b6dfSmrg	     CELL *cellc,
4319e0a2b6dfSmrg	     CELL *cell)
4320d522f475Smrg{
4321956cc18dSsnj    TScreen *screen = TScreenOf(xw);
4322f2e35a3aSmrg    SelectedCells *scp;
4323f2e35a3aSmrg    int i;
4324d522f475Smrg    int eol;
4325f2e35a3aSmrg    int need = 0;
4326f2e35a3aSmrg    size_t have = 0;
4327d522f475Smrg    Char *line;
4328d522f475Smrg    Char *lp;
4329d522f475Smrg    CELL first = *cellc;
4330d522f475Smrg    CELL last = *cell;
4331d522f475Smrg
4332f2e35a3aSmrg    if (which < 0 || which >= MAX_SELECTIONS) {
4333f2e35a3aSmrg	TRACE(("SaltTextAway - which selection?\n"));
4334f2e35a3aSmrg	return;
4335f2e35a3aSmrg    }
4336f2e35a3aSmrg    scp = &(screen->selected_cells[which]);
4337f2e35a3aSmrg
4338f2e35a3aSmrg    TRACE(("SaltTextAway which=%d, first=%d,%d, last=%d,%d\n",
4339f2e35a3aSmrg	   which, first.row, first.col, last.row, last.col));
4340f2e35a3aSmrg
4341d522f475Smrg    if (isSameRow(&first, &last) && first.col > last.col) {
43422e4f8982Smrg	int tmp;
4343956cc18dSsnj	EXCHANGE(first.col, last.col, tmp);
4344d522f475Smrg    }
4345d522f475Smrg
4346d522f475Smrg    --last.col;
4347d522f475Smrg    /* first we need to know how long the string is before we can save it */
4348d522f475Smrg
4349d522f475Smrg    if (isSameRow(&last, &first)) {
4350f2e35a3aSmrg	need = Length(screen, first.row, first.col, last.col);
4351d522f475Smrg    } else {			/* two cases, cut is on same line, cut spans multiple lines */
4352f2e35a3aSmrg	need += Length(screen, first.row, first.col, screen->max_col) + 1;
4353d522f475Smrg	for (i = first.row + 1; i < last.row; i++)
4354f2e35a3aSmrg	    need += Length(screen, i, 0, screen->max_col) + 1;
4355d522f475Smrg	if (last.col >= 0)
4356f2e35a3aSmrg	    need += Length(screen, last.row, 0, last.col);
4357d522f475Smrg    }
4358d522f475Smrg
4359d522f475Smrg    /* UTF-8 may require more space */
4360d522f475Smrg    if_OPT_WIDE_CHARS(screen, {
4361f2e35a3aSmrg	if (need > 0) {
4362f2e35a3aSmrg	    if (screen->max_combining > 0)
4363f2e35a3aSmrg		need += screen->max_combining;
4364f2e35a3aSmrg	    need *= 6;
4365f2e35a3aSmrg	}
4366d522f475Smrg    });
4367d522f475Smrg
4368d522f475Smrg    /* now get some memory to save it in */
4369f2e35a3aSmrg    if (need < 0)
4370f2e35a3aSmrg	return;
4371d522f475Smrg
4372f2e35a3aSmrg    if (scp->data_limit <= (unsigned) need) {
4373f2e35a3aSmrg	if ((line = (Char *) malloc((size_t) need + 1)) == 0)
4374d522f475Smrg	    SysError(ERROR_BMALLOC2);
4375f2e35a3aSmrg	free(scp->data_buffer);
4376f2e35a3aSmrg	scp->data_buffer = line;
4377f2e35a3aSmrg	scp->data_limit = (size_t) (need + 1);
4378d522f475Smrg    } else {
4379f2e35a3aSmrg	line = scp->data_buffer;
4380d522f475Smrg    }
4381d522f475Smrg
4382f2e35a3aSmrg    if (line == 0)
4383d522f475Smrg	return;
4384d522f475Smrg
4385f2e35a3aSmrg    line[need] = '\0';		/* make sure it is null terminated */
4386d522f475Smrg    lp = line;			/* lp points to where to save the text */
4387d522f475Smrg    if (isSameRow(&last, &first)) {
4388d522f475Smrg	lp = SaveText(screen, last.row, first.col, last.col, lp, &eol);
4389d522f475Smrg    } else {
4390d522f475Smrg	lp = SaveText(screen, first.row, first.col, screen->max_col, lp, &eol);
4391d522f475Smrg	if (eol)
4392d522f475Smrg	    *lp++ = '\n';	/* put in newline at end of line */
4393d522f475Smrg	for (i = first.row + 1; i < last.row; i++) {
4394d522f475Smrg	    lp = SaveText(screen, i, 0, screen->max_col, lp, &eol);
4395d522f475Smrg	    if (eol)
4396d522f475Smrg		*lp++ = '\n';
4397d522f475Smrg	}
4398d522f475Smrg	if (last.col >= 0)
4399d522f475Smrg	    lp = SaveText(screen, last.row, 0, last.col, lp, &eol);
4400d522f475Smrg    }
4401d522f475Smrg    *lp = '\0';			/* make sure we have end marked */
4402d522f475Smrg
4403f2e35a3aSmrg    have = (size_t) (lp - line);
4404f2e35a3aSmrg    /*
4405f2e35a3aSmrg     * Scanning the buffer twice is unnecessary.  Discard unwanted memory if
4406f2e35a3aSmrg     * the estimate is too-far off.
4407f2e35a3aSmrg     */
4408f2e35a3aSmrg    if ((have * 2) < (size_t) need) {
4409f2e35a3aSmrg	Char *next;
4410f2e35a3aSmrg	scp->data_limit = have + 1;
4411f2e35a3aSmrg	next = realloc(line, scp->data_limit);
4412f2e35a3aSmrg	if (next == NULL) {
4413f2e35a3aSmrg	    free(line);
4414f2e35a3aSmrg	    scp->data_length = 0;
4415f2e35a3aSmrg	    scp->data_limit = 0;
4416f2e35a3aSmrg	}
4417f2e35a3aSmrg	scp->data_buffer = next;
4418f2e35a3aSmrg    }
4419f2e35a3aSmrg    scp->data_length = have;
4420d522f475Smrg
4421f2e35a3aSmrg    TRACE(("Salted TEXT:%u:%s\n", (unsigned) have,
4422f2e35a3aSmrg	   visibleChars(scp->data_buffer, (unsigned) have)));
4423d522f475Smrg}
4424d522f475Smrg
4425d522f475Smrg#if OPT_PASTE64
4426d522f475Smrgvoid
4427f2e35a3aSmrgClearSelectionBuffer(TScreen *screen, String selection)
4428d522f475Smrg{
4429f2e35a3aSmrg    int which = TargetToSelection(screen, selection);
4430f2e35a3aSmrg    SelectedCells *scp = &(screen->selected_cells[okSelectionCode(which)]);
4431f2e35a3aSmrg    FreeAndNull(scp->data_buffer);
4432f2e35a3aSmrg    scp->data_limit = 0;
4433f2e35a3aSmrg    scp->data_length = 0;
4434d522f475Smrg    screen->base64_count = 0;
4435d522f475Smrg}
4436d522f475Smrg
4437d522f475Smrgstatic void
4438f2e35a3aSmrgAppendStrToSelectionBuffer(SelectedCells * scp, Char *text, size_t len)
4439d522f475Smrg{
4440d522f475Smrg    if (len != 0) {
4441f2e35a3aSmrg	size_t j = (scp->data_length + len);
4442f2e35a3aSmrg	size_t k = j + (j >> 2) + 80;
4443f2e35a3aSmrg	if (j + 1 >= scp->data_limit) {
4444f2e35a3aSmrg	    Char *line;
4445f2e35a3aSmrg	    if (!scp->data_length) {
4446f2e35a3aSmrg		line = (Char *) malloc(k);
4447d522f475Smrg	    } else {
4448f2e35a3aSmrg		line = (Char *) realloc(scp->data_buffer, k);
4449d522f475Smrg	    }
4450f2e35a3aSmrg	    if (line == 0)
4451f2e35a3aSmrg		SysError(ERROR_BMALLOC2);
4452f2e35a3aSmrg	    scp->data_buffer = line;
4453f2e35a3aSmrg	    scp->data_limit = k;
4454d522f475Smrg	}
4455f2e35a3aSmrg	if (scp->data_buffer != 0) {
4456f2e35a3aSmrg	    memcpy(scp->data_buffer + scp->data_length, text, len);
4457f2e35a3aSmrg	    scp->data_length += len;
4458f2e35a3aSmrg	    scp->data_buffer[scp->data_length] = 0;
445920d2c4d2Smrg	}
4460d522f475Smrg    }
4461d522f475Smrg}
4462d522f475Smrg
4463d522f475Smrgvoid
4464f2e35a3aSmrgAppendToSelectionBuffer(TScreen *screen, unsigned c, String selection)
4465d522f475Smrg{
4466f2e35a3aSmrg    int which = TargetToSelection(screen, selection);
4467f2e35a3aSmrg    SelectedCells *scp = &(screen->selected_cells[okSelectionCode(which)]);
44682eaa94a1Schristos    unsigned six;
4469d522f475Smrg    Char ch;
4470d522f475Smrg
4471d522f475Smrg    /* Decode base64 character */
4472d522f475Smrg    if (c >= 'A' && c <= 'Z')
4473d522f475Smrg	six = c - 'A';
4474d522f475Smrg    else if (c >= 'a' && c <= 'z')
4475d522f475Smrg	six = c - 'a' + 26;
4476d522f475Smrg    else if (c >= '0' && c <= '9')
4477d522f475Smrg	six = c - '0' + 52;
4478d522f475Smrg    else if (c == '+')
4479d522f475Smrg	six = 62;
4480d522f475Smrg    else if (c == '/')
4481d522f475Smrg	six = 63;
4482d522f475Smrg    else
4483d522f475Smrg	return;
4484d522f475Smrg
4485d522f475Smrg    /* Accumulate bytes */
4486d522f475Smrg    switch (screen->base64_count) {
4487d522f475Smrg    case 0:
4488d522f475Smrg	screen->base64_accu = six;
4489d522f475Smrg	screen->base64_count = 6;
4490d522f475Smrg	break;
4491d522f475Smrg
4492d522f475Smrg    case 2:
44932eaa94a1Schristos	ch = CharOf((screen->base64_accu << 6) + six);
4494d522f475Smrg	screen->base64_count = 0;
4495f2e35a3aSmrg	AppendStrToSelectionBuffer(scp, &ch, (size_t) 1);
4496d522f475Smrg	break;
4497d522f475Smrg
4498d522f475Smrg    case 4:
44992eaa94a1Schristos	ch = CharOf((screen->base64_accu << 4) + (six >> 2));
4500d522f475Smrg	screen->base64_accu = (six & 0x3);
4501d522f475Smrg	screen->base64_count = 2;
4502f2e35a3aSmrg	AppendStrToSelectionBuffer(scp, &ch, (size_t) 1);
4503d522f475Smrg	break;
4504d522f475Smrg
4505d522f475Smrg    case 6:
45062eaa94a1Schristos	ch = CharOf((screen->base64_accu << 2) + (six >> 4));
4507d522f475Smrg	screen->base64_accu = (six & 0xF);
4508d522f475Smrg	screen->base64_count = 4;
4509f2e35a3aSmrg	AppendStrToSelectionBuffer(scp, &ch, (size_t) 1);
4510d522f475Smrg	break;
4511d522f475Smrg    }
4512d522f475Smrg}
4513d522f475Smrg
4514d522f475Smrgvoid
4515e0a2b6dfSmrgCompleteSelection(XtermWidget xw, String *args, Cardinal len)
4516d522f475Smrg{
4517956cc18dSsnj    TScreen *screen = TScreenOf(xw);
4518956cc18dSsnj
4519956cc18dSsnj    screen->base64_count = 0;
4520956cc18dSsnj    screen->base64_accu = 0;
4521d522f475Smrg    _OwnSelection(xw, args, len);
4522d522f475Smrg}
4523d522f475Smrg#endif /* OPT_PASTE64 */
4524d522f475Smrg
4525d522f475Smrgstatic Bool
4526d522f475Smrg_ConvertSelectionHelper(Widget w,
4527f2e35a3aSmrg			SelectedCells * scp,
4528894e0ac8Smrg			Atom *type,
4529d522f475Smrg			XtPointer *value,
4530d522f475Smrg			unsigned long *length,
4531d522f475Smrg			int *format,
4532d522f475Smrg			int (*conversion_function) (Display *,
4533d522f475Smrg						    char **, int,
4534d522f475Smrg						    XICCEncodingStyle,
4535d522f475Smrg						    XTextProperty *),
4536d522f475Smrg			XICCEncodingStyle conversion_style)
4537d522f475Smrg{
453801037d57Smrg    *value = 0;
453901037d57Smrg    *length = 0;
454001037d57Smrg    *type = 0;
454101037d57Smrg    *format = 0;
454201037d57Smrg
4543f2e35a3aSmrg    if (getXtermWidget(w) != 0) {
4544d522f475Smrg	Display *dpy = XtDisplay(w);
4545d522f475Smrg	XTextProperty textprop;
454601037d57Smrg	int out_n = 0;
454701037d57Smrg	char *result = 0;
4548f2e35a3aSmrg	char *the_data = (char *) scp->data_buffer;
454901037d57Smrg	char *the_next;
4550f2e35a3aSmrg	unsigned long remaining = scp->data_length;
455101037d57Smrg
455201037d57Smrg	TRACE(("converting %ld:'%s'\n",
4553f2e35a3aSmrg	       (long) scp->data_length,
4554f2e35a3aSmrg	       visibleChars(scp->data_buffer, (unsigned) scp->data_length)));
455501037d57Smrg	/*
455601037d57Smrg	 * For most selections, we can convert in one pass.  It is possible
455701037d57Smrg	 * that some applications contain embedded nulls, e.g., using xterm's
455801037d57Smrg	 * paste64 feature.  For those cases, we will build up the result in
455901037d57Smrg	 * parts.
456001037d57Smrg	 */
4561f2e35a3aSmrg	if (memchr(the_data, 0, scp->data_length) != 0) {
456201037d57Smrg	    TRACE(("selection contains embedded nulls\n"));
4563f2e35a3aSmrg	    result = calloc(scp->data_length + 1, sizeof(char));
456401037d57Smrg	}
4565d522f475Smrg
456601037d57Smrg      next_try:
456701037d57Smrg	memset(&textprop, 0, sizeof(textprop));
4568d522f475Smrg	if (conversion_function(dpy, &the_data, 1,
4569d522f475Smrg				conversion_style,
4570d522f475Smrg				&textprop) >= Success) {
457101037d57Smrg	    if ((result != 0)
457201037d57Smrg		&& (textprop.value != 0)
457301037d57Smrg		&& (textprop.format == 8)) {
457401037d57Smrg		char *text_values = (char *) textprop.value;
457501037d57Smrg		unsigned long in_n;
457601037d57Smrg
457701037d57Smrg		if (out_n == 0) {
457801037d57Smrg		    *value = result;
457901037d57Smrg		    *type = textprop.encoding;
458001037d57Smrg		    *format = textprop.format;
458101037d57Smrg		}
458201037d57Smrg		for (in_n = 0; in_n < textprop.nitems; ++in_n) {
458301037d57Smrg		    result[out_n++] = text_values[in_n];
458401037d57Smrg		}
458501037d57Smrg		*length += textprop.nitems;
458601037d57Smrg		if ((the_next = memchr(the_data, 0, remaining)) != 0) {
458701037d57Smrg		    unsigned long this_was = (unsigned long) (the_next - the_data);
458801037d57Smrg		    this_was++;
458901037d57Smrg		    the_data += this_was;
459001037d57Smrg		    remaining -= this_was;
459101037d57Smrg		    result[out_n++] = 0;
459201037d57Smrg		    *length += 1;
459301037d57Smrg		    if (remaining)
459401037d57Smrg			goto next_try;
459501037d57Smrg		}
459601037d57Smrg		return True;
459701037d57Smrg	    } else {
459801037d57Smrg		free(result);
459901037d57Smrg		*value = (XtPointer) textprop.value;
460001037d57Smrg		*length = textprop.nitems;
460101037d57Smrg		*type = textprop.encoding;
460201037d57Smrg		*format = textprop.format;
460301037d57Smrg		return True;
460401037d57Smrg	    }
4605d522f475Smrg	}
460601037d57Smrg	free(result);
4607d522f475Smrg    }
4608d522f475Smrg    return False;
4609d522f475Smrg}
4610d522f475Smrg
46112eaa94a1Schristosstatic Boolean
46122eaa94a1SchristosSaveConvertedLength(XtPointer *target, unsigned long source)
46132eaa94a1Schristos{
46142eaa94a1Schristos    Boolean result = False;
46152eaa94a1Schristos
46162eaa94a1Schristos    *target = XtMalloc(4);
46172eaa94a1Schristos    if (*target != 0) {
46182eaa94a1Schristos	result = True;
46192eaa94a1Schristos	if (sizeof(unsigned long) == 4) {
46202eaa94a1Schristos	    *(unsigned long *) *target = source;
46212eaa94a1Schristos	} else if (sizeof(unsigned) == 4) {
462220d2c4d2Smrg	    *(unsigned *) *target = (unsigned) source;
46232eaa94a1Schristos	} else if (sizeof(unsigned short) == 4) {
46242eaa94a1Schristos	    *(unsigned short *) *target = (unsigned short) source;
46252eaa94a1Schristos	} else {
46262eaa94a1Schristos	    /* FIXME - does this depend on byte-order? */
46272eaa94a1Schristos	    unsigned long temp = source;
462820d2c4d2Smrg	    memcpy((char *) *target,
462920d2c4d2Smrg		   ((char *) &temp) + sizeof(temp) - 4,
463020d2c4d2Smrg		   (size_t) 4);
46312eaa94a1Schristos	}
46322eaa94a1Schristos    }
46332eaa94a1Schristos    return result;
46342eaa94a1Schristos}
46352eaa94a1Schristos
4636f2e35a3aSmrg#define keepClipboard(d,atom) ((screen->keepClipboard) && \
4637f2e35a3aSmrg	 (atom == XA_CLIPBOARD(d)))
46382e4f8982Smrg
4639d522f475Smrgstatic Boolean
4640d522f475SmrgConvertSelection(Widget w,
4641894e0ac8Smrg		 Atom *selection,
4642894e0ac8Smrg		 Atom *target,
4643894e0ac8Smrg		 Atom *type,
4644d522f475Smrg		 XtPointer *value,
4645d522f475Smrg		 unsigned long *length,
4646d522f475Smrg		 int *format)
4647d522f475Smrg{
4648d522f475Smrg    Display *dpy = XtDisplay(w);
4649d522f475Smrg    TScreen *screen;
4650f2e35a3aSmrg    SelectedCells *scp;
4651d522f475Smrg    Bool result = False;
4652d522f475Smrg
46532e4f8982Smrg    Char *data;
46542e4f8982Smrg    unsigned long data_length;
46552e4f8982Smrg
4656956cc18dSsnj    XtermWidget xw;
4657956cc18dSsnj
4658956cc18dSsnj    if ((xw = getXtermWidget(w)) == 0)
4659d522f475Smrg	return False;
4660d522f475Smrg
4661956cc18dSsnj    screen = TScreenOf(xw);
4662d522f475Smrg
4663f2e35a3aSmrg    TRACE(("ConvertSelection %s -> %s\n",
4664f2e35a3aSmrg	   TraceAtomName(screen->display, *selection),
4665956cc18dSsnj	   visibleSelectionTarget(dpy, *target)));
4666956cc18dSsnj
4667f2e35a3aSmrg    if (keepClipboard(dpy, *selection)) {
46682e4f8982Smrg	TRACE(("asked for clipboard\n"));
4669f2e35a3aSmrg	scp = &(screen->clipboard_data);
46702e4f8982Smrg    } else {
46712e4f8982Smrg	TRACE(("asked for selection\n"));
4672f2e35a3aSmrg	scp = &(screen->selected_cells[AtomToSelection(dpy, *selection)]);
46732e4f8982Smrg    }
46742e4f8982Smrg
4675f2e35a3aSmrg    data = scp->data_buffer;
4676f2e35a3aSmrg    data_length = scp->data_length;
46772e4f8982Smrg    if (data == NULL) {
4678f2e35a3aSmrg	TRACE(("...no selection-data\n"));
4679f2e35a3aSmrg	return False;
468001037d57Smrg    }
468101037d57Smrg
4682d522f475Smrg    if (*target == XA_TARGETS(dpy)) {
4683d522f475Smrg	Atom *targetP;
4684d522f475Smrg	XPointer std_return = 0;
4685d522f475Smrg	unsigned long std_length;
4686d522f475Smrg
4687d522f475Smrg	if (XmuConvertStandardSelection(w, screen->selection_time, selection,
4688d522f475Smrg					target, type, &std_return,
4689d522f475Smrg					&std_length, format)) {
4690956cc18dSsnj	    Atom *my_targets = _SelectionTargets(w);
46912e4f8982Smrg	    Atom *allocP;
46922e4f8982Smrg	    Atom *std_targets;
4693956cc18dSsnj
4694956cc18dSsnj	    TRACE(("XmuConvertStandardSelection - success\n"));
4695a1f3da82Smrg	    std_targets = (Atom *) (void *) (std_return);
4696d522f475Smrg	    *length = std_length + 6;
4697d522f475Smrg
4698a1f3da82Smrg	    targetP = TypeXtMallocN(Atom, *length);
4699d522f475Smrg	    allocP = targetP;
4700d522f475Smrg
4701d522f475Smrg	    *value = (XtPointer) targetP;
4702d522f475Smrg
47030bd37d32Smrg	    if (my_targets != 0) {
47040bd37d32Smrg		while (*my_targets != None) {
47050bd37d32Smrg		    *targetP++ = *my_targets++;
47060bd37d32Smrg		}
4707956cc18dSsnj	    }
4708d522f475Smrg	    *targetP++ = XA_LENGTH(dpy);
4709d522f475Smrg	    *targetP++ = XA_LIST_LENGTH(dpy);
4710d522f475Smrg
47112eaa94a1Schristos	    *length = std_length + (unsigned long) (targetP - allocP);
4712d522f475Smrg
4713d522f475Smrg	    memcpy(targetP, std_targets, sizeof(Atom) * std_length);
4714d522f475Smrg	    XtFree((char *) std_targets);
4715d522f475Smrg	    *type = XA_ATOM;
4716d522f475Smrg	    *format = 32;
4717d522f475Smrg	    result = True;
4718956cc18dSsnj	} else {
4719956cc18dSsnj	    TRACE(("XmuConvertStandardSelection - failed\n"));
4720d522f475Smrg	}
4721d522f475Smrg    }
4722d522f475Smrg#if OPT_WIDE_CHARS
4723d522f475Smrg    else if (screen->wide_chars && *target == XA_STRING) {
4724d522f475Smrg	result =
4725f2e35a3aSmrg	    _ConvertSelectionHelper(w, scp,
4726f2e35a3aSmrg				    type, value, length, format,
4727d522f475Smrg				    Xutf8TextListToTextProperty,
4728d522f475Smrg				    XStringStyle);
4729956cc18dSsnj	TRACE(("...Xutf8TextListToTextProperty:%d\n", result));
4730d522f475Smrg    } else if (screen->wide_chars && *target == XA_UTF8_STRING(dpy)) {
4731d522f475Smrg	result =
4732f2e35a3aSmrg	    _ConvertSelectionHelper(w, scp,
4733f2e35a3aSmrg				    type, value, length, format,
4734d522f475Smrg				    Xutf8TextListToTextProperty,
4735d522f475Smrg				    XUTF8StringStyle);
4736956cc18dSsnj	TRACE(("...Xutf8TextListToTextProperty:%d\n", result));
4737d522f475Smrg    } else if (screen->wide_chars && *target == XA_TEXT(dpy)) {
4738d522f475Smrg	result =
4739f2e35a3aSmrg	    _ConvertSelectionHelper(w, scp,
4740f2e35a3aSmrg				    type, value, length, format,
4741d522f475Smrg				    Xutf8TextListToTextProperty,
4742d522f475Smrg				    XStdICCTextStyle);
4743956cc18dSsnj	TRACE(("...Xutf8TextListToTextProperty:%d\n", result));
4744d522f475Smrg    } else if (screen->wide_chars && *target == XA_COMPOUND_TEXT(dpy)) {
4745d522f475Smrg	result =
4746f2e35a3aSmrg	    _ConvertSelectionHelper(w, scp,
4747f2e35a3aSmrg				    type, value, length, format,
4748d522f475Smrg				    Xutf8TextListToTextProperty,
4749d522f475Smrg				    XCompoundTextStyle);
4750956cc18dSsnj	TRACE(("...Xutf8TextListToTextProperty:%d\n", result));
4751d522f475Smrg    }
4752d522f475Smrg#endif
4753d522f475Smrg
4754d522f475Smrg    else if (*target == XA_STRING) {	/* not wide_chars */
4755d522f475Smrg	/* We can only reach this point if the selection requestor
4756d522f475Smrg	   requested STRING before any of TEXT, COMPOUND_TEXT or
4757d522f475Smrg	   UTF8_STRING.  We therefore assume that the requestor is not
4758d522f475Smrg	   properly internationalised, and dump raw eight-bit data
4759d522f475Smrg	   with no conversion into the selection.  Yes, this breaks
4760d522f475Smrg	   the ICCCM in non-Latin-1 locales. */
4761d522f475Smrg	*type = XA_STRING;
4762f2e35a3aSmrg	*value = (XtPointer) data;
4763f2e35a3aSmrg	*length = data_length;
4764d522f475Smrg	*format = 8;
4765d522f475Smrg	result = True;
4766956cc18dSsnj	TRACE(("...raw 8-bit data:%d\n", result));
4767d522f475Smrg    } else if (*target == XA_TEXT(dpy)) {	/* not wide_chars */
4768d522f475Smrg	result =
4769f2e35a3aSmrg	    _ConvertSelectionHelper(w, scp,
4770f2e35a3aSmrg				    type, value, length, format,
4771d522f475Smrg				    XmbTextListToTextProperty,
4772d522f475Smrg				    XStdICCTextStyle);
4773956cc18dSsnj	TRACE(("...XmbTextListToTextProperty(StdICC):%d\n", result));
4774d522f475Smrg    } else if (*target == XA_COMPOUND_TEXT(dpy)) {	/* not wide_chars */
4775d522f475Smrg	result =
4776f2e35a3aSmrg	    _ConvertSelectionHelper(w, scp,
4777f2e35a3aSmrg				    type, value, length, format,
4778d522f475Smrg				    XmbTextListToTextProperty,
4779d522f475Smrg				    XCompoundTextStyle);
4780956cc18dSsnj	TRACE(("...XmbTextListToTextProperty(Compound):%d\n", result));
4781d522f475Smrg    }
4782d522f475Smrg#ifdef X_HAVE_UTF8_STRING
4783d522f475Smrg    else if (*target == XA_UTF8_STRING(dpy)) {	/* not wide_chars */
4784d522f475Smrg	result =
4785f2e35a3aSmrg	    _ConvertSelectionHelper(w, scp,
4786f2e35a3aSmrg				    type, value, length, format,
4787d522f475Smrg				    XmbTextListToTextProperty,
4788d522f475Smrg				    XUTF8StringStyle);
4789956cc18dSsnj	TRACE(("...XmbTextListToTextProperty(UTF8):%d\n", result));
4790d522f475Smrg    }
4791d522f475Smrg#endif
4792d522f475Smrg    else if (*target == XA_LIST_LENGTH(dpy)) {
479320d2c4d2Smrg	result = SaveConvertedLength(value, (unsigned long) 1);
4794d522f475Smrg	*type = XA_INTEGER;
4795d522f475Smrg	*length = 1;
4796d522f475Smrg	*format = 32;
4797956cc18dSsnj	TRACE(("...list of values:%d\n", result));
4798d522f475Smrg    } else if (*target == XA_LENGTH(dpy)) {
4799d522f475Smrg	/* This value is wrong if we have UTF-8 text */
4800f2e35a3aSmrg	result = SaveConvertedLength(value, scp->data_length);
4801d522f475Smrg	*type = XA_INTEGER;
4802d522f475Smrg	*length = 1;
4803d522f475Smrg	*format = 32;
4804956cc18dSsnj	TRACE(("...list of values:%d\n", result));
4805d522f475Smrg    } else if (XmuConvertStandardSelection(w,
4806d522f475Smrg					   screen->selection_time, selection,
4807d522f475Smrg					   target, type, (XPointer *) value,
4808d522f475Smrg					   length, format)) {
4809d522f475Smrg	result = True;
4810956cc18dSsnj	TRACE(("...XmuConvertStandardSelection:%d\n", result));
4811d522f475Smrg    }
4812d522f475Smrg
4813d522f475Smrg    /* else */
48142eaa94a1Schristos    return (Boolean) result;
4815d522f475Smrg}
4816d522f475Smrg
4817d522f475Smrgstatic void
4818894e0ac8SmrgLoseSelection(Widget w, Atom *selection)
4819d522f475Smrg{
4820d522f475Smrg    TScreen *screen;
4821d522f475Smrg    Atom *atomP;
4822d522f475Smrg    Cardinal i;
4823d522f475Smrg
4824956cc18dSsnj    XtermWidget xw;
4825956cc18dSsnj
4826956cc18dSsnj    if ((xw = getXtermWidget(w)) == 0)
4827d522f475Smrg	return;
4828d522f475Smrg
4829956cc18dSsnj    screen = TScreenOf(xw);
4830913cc679Smrg    TRACE(("LoseSelection %s\n", TraceAtomName(screen->display, *selection)));
483101037d57Smrg
4832d522f475Smrg    for (i = 0, atomP = screen->selection_atoms;
4833d522f475Smrg	 i < screen->selection_count; i++, atomP++) {
4834d522f475Smrg	if (*selection == *atomP)
4835d522f475Smrg	    *atomP = (Atom) 0;
4836d522f475Smrg	if (CutBuffer(*atomP) >= 0) {
4837d522f475Smrg	    *atomP = (Atom) 0;
4838d522f475Smrg	}
4839d522f475Smrg    }
4840d522f475Smrg
4841d522f475Smrg    for (i = screen->selection_count; i; i--) {
4842d522f475Smrg	if (screen->selection_atoms[i - 1] != 0)
4843d522f475Smrg	    break;
4844d522f475Smrg    }
4845d522f475Smrg    screen->selection_count = i;
4846d522f475Smrg
4847d522f475Smrg    for (i = 0, atomP = screen->selection_atoms;
4848d522f475Smrg	 i < screen->selection_count; i++, atomP++) {
4849d522f475Smrg	if (*atomP == (Atom) 0) {
4850d522f475Smrg	    *atomP = screen->selection_atoms[--screen->selection_count];
4851d522f475Smrg	}
4852d522f475Smrg    }
4853d522f475Smrg
4854d522f475Smrg    if (screen->selection_count == 0)
4855f2e35a3aSmrg	UnHiliteText(xw);
4856d522f475Smrg}
4857d522f475Smrg
4858d522f475Smrg/* ARGSUSED */
4859d522f475Smrgstatic void
4860d522f475SmrgSelectionDone(Widget w GCC_UNUSED,
4861894e0ac8Smrg	      Atom *selection GCC_UNUSED,
4862894e0ac8Smrg	      Atom *target GCC_UNUSED)
4863d522f475Smrg{
4864d522f475Smrg    /* empty proc so Intrinsics know we want to keep storage */
486501037d57Smrg    TRACE(("SelectionDone\n"));
4866d522f475Smrg}
4867d522f475Smrg
4868d522f475Smrgstatic void
4869d522f475Smrg_OwnSelection(XtermWidget xw,
4870e0a2b6dfSmrg	      String *selections,
4871d522f475Smrg	      Cardinal count)
4872d522f475Smrg{
4873956cc18dSsnj    TScreen *screen = TScreenOf(xw);
4874f2e35a3aSmrg    Display *dpy = screen->display;
4875d522f475Smrg    Atom *atoms = screen->selection_atoms;
4876d522f475Smrg    Cardinal i;
4877d522f475Smrg    Bool have_selection = False;
4878f2e35a3aSmrg    SelectedCells *scp;
4879d522f475Smrg
488020d2c4d2Smrg    if (count == 0)
488120d2c4d2Smrg	return;
4882d522f475Smrg
4883f2e35a3aSmrg    TRACE(("_OwnSelection count %d\n", count));
4884d522f475Smrg    selections = MapSelections(xw, selections, count);
4885d522f475Smrg
4886d522f475Smrg    if (count > screen->sel_atoms_size) {
4887d522f475Smrg	XtFree((char *) atoms);
4888a1f3da82Smrg	atoms = TypeXtMallocN(Atom, count);
4889d522f475Smrg	screen->selection_atoms = atoms;
4890d522f475Smrg	screen->sel_atoms_size = count;
4891d522f475Smrg    }
4892f2e35a3aSmrg    XmuInternStrings(dpy, selections, count, atoms);
4893d522f475Smrg    for (i = 0; i < count; i++) {
4894d522f475Smrg	int cutbuffer = CutBuffer(atoms[i]);
4895d522f475Smrg	if (cutbuffer >= 0) {
48962eaa94a1Schristos	    unsigned long limit =
4897f2e35a3aSmrg	    (unsigned long) (4 * XMaxRequestSize(dpy) - 32);
4898f2e35a3aSmrg	    scp = &(screen->selected_cells[CutBufferToCode(cutbuffer)]);
4899f2e35a3aSmrg	    if (scp->data_length > limit) {
490020d2c4d2Smrg		TRACE(("selection too big (%lu bytes), not storing in CUT_BUFFER%d\n",
4901f2e35a3aSmrg		       (unsigned long) scp->data_length, cutbuffer));
49020bd37d32Smrg		xtermWarning("selection too big (%lu bytes), not storing in CUT_BUFFER%d\n",
4903f2e35a3aSmrg			     (unsigned long) scp->data_length, cutbuffer);
4904d522f475Smrg	    } else {
4905d522f475Smrg		/* This used to just use the UTF-8 data, which was totally
4906894e0ac8Smrg		 * broken as not even the corresponding paste code in xterm
4907d522f475Smrg		 * understood this!  So now it converts to Latin1 first.
4908d522f475Smrg		 *   Robert Brady, 2000-09-05
4909d522f475Smrg		 */
4910f2e35a3aSmrg		unsigned long length = scp->data_length;
4911f2e35a3aSmrg		Char *data = scp->data_buffer;
4912d522f475Smrg		if_OPT_WIDE_CHARS((screen), {
4913956cc18dSsnj		    data = UTF8toLatin1(screen, data, length, &length);
4914d522f475Smrg		});
4915d522f475Smrg		TRACE(("XStoreBuffer(%d)\n", cutbuffer));
4916f2e35a3aSmrg		XStoreBuffer(dpy,
4917d522f475Smrg			     (char *) data,
4918d522f475Smrg			     (int) length,
4919d522f475Smrg			     cutbuffer);
4920d522f475Smrg	    }
4921f2e35a3aSmrg	} else {
4922f2e35a3aSmrg	    int which = AtomToSelection(dpy, atoms[i]);
4923f2e35a3aSmrg	    if (keepClipboard(dpy, atoms[i])) {
4924f2e35a3aSmrg		Char *buf;
4925f2e35a3aSmrg		SelectedCells *tcp = &(screen->clipboard_data);
4926f2e35a3aSmrg		TRACE(("saving selection to clipboard buffer\n"));
4927f2e35a3aSmrg		scp = &(screen->selected_cells[CLIPBOARD_CODE]);
4928f2e35a3aSmrg		if ((buf = (Char *) malloc((size_t) scp->data_length)) == 0)
4929f2e35a3aSmrg		    SysError(ERROR_BMALLOC2);
49302e4f8982Smrg
4931f2e35a3aSmrg		free(tcp->data_buffer);
4932f2e35a3aSmrg		memcpy(buf, scp->data_buffer, scp->data_length);
4933f2e35a3aSmrg		tcp->data_buffer = buf;
4934f2e35a3aSmrg		tcp->data_limit = scp->data_length;
4935f2e35a3aSmrg		tcp->data_length = scp->data_length;
4936f2e35a3aSmrg	    }
4937f2e35a3aSmrg	    scp = &(screen->selected_cells[which]);
4938f2e35a3aSmrg	    if (scp->data_length == 0) {
4939f2e35a3aSmrg		TRACE(("XtDisownSelection(%s, @%ld)\n",
4940f2e35a3aSmrg		       TraceAtomName(screen->display, atoms[i]),
4941f2e35a3aSmrg		       (long) screen->selection_time));
4942f2e35a3aSmrg		XtDisownSelection((Widget) xw,
4943f2e35a3aSmrg				  atoms[i],
4944f2e35a3aSmrg				  screen->selection_time);
4945f2e35a3aSmrg	    } else if (!screen->replyToEmacs && atoms[i] != 0) {
4946f2e35a3aSmrg		TRACE(("XtOwnSelection(%s, @%ld)\n",
4947f2e35a3aSmrg		       TraceAtomName(screen->display, atoms[i]),
4948f2e35a3aSmrg		       (long) screen->selection_time));
4949f2e35a3aSmrg		have_selection |=
4950f2e35a3aSmrg		    XtOwnSelection((Widget) xw, atoms[i],
4951f2e35a3aSmrg				   screen->selection_time,
4952f2e35a3aSmrg				   ConvertSelection,
4953f2e35a3aSmrg				   LoseSelection,
4954f2e35a3aSmrg				   SelectionDone);
4955f2e35a3aSmrg	    }
4956d522f475Smrg	}
4957f2e35a3aSmrg	TRACE(("... _OwnSelection used length %lu value %s\n",
4958f2e35a3aSmrg	       (unsigned long) scp->data_length,
4959f2e35a3aSmrg	       visibleChars(scp->data_buffer,
4960f2e35a3aSmrg			    (unsigned) scp->data_length)));
4961d522f475Smrg    }
4962d522f475Smrg    if (!screen->replyToEmacs)
4963d522f475Smrg	screen->selection_count = count;
4964d522f475Smrg    if (!have_selection)
4965f2e35a3aSmrg	UnHiliteText(xw);
4966d522f475Smrg}
4967d522f475Smrg
4968d522f475Smrgstatic void
4969e0a2b6dfSmrgResetSelectionState(TScreen *screen)
4970d522f475Smrg{
4971d522f475Smrg    screen->selection_count = 0;
4972d522f475Smrg    screen->startH = zeroCELL;
4973d522f475Smrg    screen->endH = zeroCELL;
4974d522f475Smrg}
4975d522f475Smrg
4976d522f475Smrgvoid
4977d522f475SmrgDisownSelection(XtermWidget xw)
4978d522f475Smrg{
4979956cc18dSsnj    TScreen *screen = TScreenOf(xw);
4980d522f475Smrg    Atom *atoms = screen->selection_atoms;
4981d522f475Smrg    Cardinal count = screen->selection_count;
4982d522f475Smrg    Cardinal i;
4983d522f475Smrg
4984d522f475Smrg    TRACE(("DisownSelection count %d, start %d.%d, end %d.%d\n",
4985d522f475Smrg	   count,
4986d522f475Smrg	   screen->startH.row,
4987d522f475Smrg	   screen->startH.col,
4988d522f475Smrg	   screen->endH.row,
4989d522f475Smrg	   screen->endH.col));
4990d522f475Smrg
4991d522f475Smrg    for (i = 0; i < count; i++) {
4992d522f475Smrg	int cutbuffer = CutBuffer(atoms[i]);
4993d522f475Smrg	if (cutbuffer < 0) {
4994d522f475Smrg	    XtDisownSelection((Widget) xw, atoms[i],
4995d522f475Smrg			      screen->selection_time);
4996d522f475Smrg	}
4997d522f475Smrg    }
4998d522f475Smrg    /*
4999d522f475Smrg     * If none of the callbacks via XtDisownSelection() reset highlighting
5000d522f475Smrg     * do it now.
5001d522f475Smrg     */
5002d522f475Smrg    if (ScrnHaveSelection(screen)) {
5003d522f475Smrg	/* save data which will be reset */
5004d522f475Smrg	CELL first = screen->startH;
5005d522f475Smrg	CELL last = screen->endH;
5006d522f475Smrg
5007d522f475Smrg	ResetSelectionState(screen);
5008d522f475Smrg	ReHiliteText(xw, &first, &last);
5009d522f475Smrg    } else {
5010d522f475Smrg	ResetSelectionState(screen);
5011d522f475Smrg    }
5012d522f475Smrg}
5013d522f475Smrg
5014d522f475Smrgvoid
5015d522f475SmrgUnhiliteSelection(XtermWidget xw)
5016d522f475Smrg{
5017956cc18dSsnj    TScreen *screen = TScreenOf(xw);
5018d522f475Smrg
5019d522f475Smrg    if (ScrnHaveSelection(screen)) {
5020d522f475Smrg	CELL first = screen->startH;
5021d522f475Smrg	CELL last = screen->endH;
5022d522f475Smrg
5023d522f475Smrg	screen->startH = zeroCELL;
5024d522f475Smrg	screen->endH = zeroCELL;
5025d522f475Smrg	ReHiliteText(xw, &first, &last);
5026d522f475Smrg    }
5027d522f475Smrg}
5028d522f475Smrg
5029d522f475Smrg/* returns number of chars in line from scol to ecol out */
5030d522f475Smrg/* ARGSUSED */
5031d522f475Smrgstatic int
5032e0a2b6dfSmrgLength(TScreen *screen,
5033d522f475Smrg       int row,
5034d522f475Smrg       int scol,
5035d522f475Smrg       int ecol)
5036d522f475Smrg{
503701037d57Smrg    CLineData *ld = GET_LINEDATA(screen, row);
503801037d57Smrg    const int lastcol = LastTextCol(screen, ld, row);
5039d522f475Smrg
5040d522f475Smrg    if (ecol > lastcol)
5041d522f475Smrg	ecol = lastcol;
5042d522f475Smrg    return (ecol - scol + 1);
5043d522f475Smrg}
5044d522f475Smrg
5045d522f475Smrg/* copies text into line, preallocated */
5046d522f475Smrgstatic Char *
5047e0a2b6dfSmrgSaveText(TScreen *screen,
5048d522f475Smrg	 int row,
5049d522f475Smrg	 int scol,
5050d522f475Smrg	 int ecol,
5051e0a2b6dfSmrg	 Char *lp,		/* pointer to where to put the text */
5052d522f475Smrg	 int *eol)
5053d522f475Smrg{
5054956cc18dSsnj    LineData *ld;
5055d522f475Smrg    int i = 0;
5056d522f475Smrg    Char *result = lp;
5057d522f475Smrg#if OPT_WIDE_CHARS
50582eaa94a1Schristos    unsigned previous = 0;
5059d522f475Smrg#endif
5060d522f475Smrg
5061956cc18dSsnj    ld = GET_LINEDATA(screen, row);
5062d522f475Smrg    i = Length(screen, row, scol, ecol);
5063d522f475Smrg    ecol = scol + i;
5064d522f475Smrg#if OPT_DEC_CHRSET
5065956cc18dSsnj    if (CSET_DOUBLE(GetLineDblCS(ld))) {
5066d522f475Smrg	scol = (scol + 0) / 2;
5067d522f475Smrg	ecol = (ecol + 1) / 2;
5068d522f475Smrg    }
5069d522f475Smrg#endif
5070956cc18dSsnj    *eol = !LineTstWrapped(ld);
5071d522f475Smrg    for (i = scol; i < ecol; i++) {
50722e4f8982Smrg	unsigned c;
50730bd37d32Smrg	assert(i < (int) ld->lineSize);
5074956cc18dSsnj	c = E2A(ld->charData[i]);
5075d522f475Smrg#if OPT_WIDE_CHARS
5076d522f475Smrg	/* We want to strip out every occurrence of HIDDEN_CHAR AFTER a
5077d522f475Smrg	 * wide character.
5078d522f475Smrg	 */
5079894e0ac8Smrg	if (c == HIDDEN_CHAR) {
5080894e0ac8Smrg	    if (isWide((int) previous)) {
5081894e0ac8Smrg		previous = c;
5082894e0ac8Smrg		/* Combining characters attached to double-width characters
5083894e0ac8Smrg		   are in memory attached to the HIDDEN_CHAR */
5084894e0ac8Smrg		if_OPT_WIDE_CHARS(screen, {
5085894e0ac8Smrg		    if ((screen->utf8_nrc_mode | screen->utf8_mode) != uFalse) {
5086894e0ac8Smrg			size_t off;
5087894e0ac8Smrg			for_each_combData(off, ld) {
50882e4f8982Smrg			    unsigned ch = ld->combData[off][i];
5089894e0ac8Smrg			    if (ch == 0)
5090894e0ac8Smrg				break;
5091894e0ac8Smrg			    lp = convertToUTF8(lp, ch);
5092894e0ac8Smrg			}
5093d522f475Smrg		    }
5094894e0ac8Smrg		});
5095894e0ac8Smrg		continue;
5096894e0ac8Smrg	    } else {
5097894e0ac8Smrg		c = ' ';	/* should not happen, but just in case... */
5098894e0ac8Smrg	    }
5099d522f475Smrg	}
5100d522f475Smrg	previous = c;
5101e0a2b6dfSmrg	if ((screen->utf8_nrc_mode | screen->utf8_mode) != uFalse) {
5102d522f475Smrg	    lp = convertToUTF8(lp, (c != 0) ? c : ' ');
5103d522f475Smrg	    if_OPT_WIDE_CHARS(screen, {
5104956cc18dSsnj		size_t off;
5105956cc18dSsnj		for_each_combData(off, ld) {
51062e4f8982Smrg		    unsigned ch = ld->combData[off][i];
5107956cc18dSsnj		    if (ch == 0)
5108d522f475Smrg			break;
5109d522f475Smrg		    lp = convertToUTF8(lp, ch);
5110d522f475Smrg		}
5111d522f475Smrg	    });
5112d522f475Smrg	} else
5113d522f475Smrg#endif
5114d522f475Smrg	{
5115d522f475Smrg	    if (c == 0) {
5116d522f475Smrg		c = E2A(' ');
5117d522f475Smrg	    } else if (c < E2A(' ')) {
5118d522f475Smrg		c = DECtoASCII(c);
5119d522f475Smrg	    } else if (c == 0x7f) {
5120d522f475Smrg		c = 0x5f;
5121d522f475Smrg	    }
51222eaa94a1Schristos	    *lp++ = CharOf(A2E(c));
5123d522f475Smrg	}
5124d522f475Smrg	if (c != E2A(' '))
5125d522f475Smrg	    result = lp;
5126d522f475Smrg    }
5127d522f475Smrg
5128d522f475Smrg    /*
5129d522f475Smrg     * If requested, trim trailing blanks from selected lines.  Do not do this
5130d522f475Smrg     * if the line is wrapped.
5131d522f475Smrg     */
5132d522f475Smrg    if (!*eol || !screen->trim_selection)
5133d522f475Smrg	result = lp;
5134d522f475Smrg
5135d522f475Smrg    return (result);
5136d522f475Smrg}
5137d522f475Smrg
5138f2e35a3aSmrg/*
5139f2e35a3aSmrg * This adds together the bits:
5140f2e35a3aSmrg *   shift key   -> 1
5141f2e35a3aSmrg *   meta key    -> 2
5142f2e35a3aSmrg *   control key -> 4
5143f2e35a3aSmrg */
5144f2e35a3aSmrgstatic unsigned
5145f2e35a3aSmrgKeyState(XtermWidget xw, unsigned x)
5146f2e35a3aSmrg{
5147f2e35a3aSmrg    return ((((x) & (ShiftMask | ControlMask)))
5148f2e35a3aSmrg	    + (((x) & MetaMask(xw)) ? 2 : 0));
5149f2e35a3aSmrg}
5150f2e35a3aSmrg
5151f2e35a3aSmrg/* 32 + following 8-bit word:
5152d522f475Smrg
5153d522f475Smrg   1:0  Button no: 0, 1, 2.  3=release.
5154d522f475Smrg     2  shift
5155d522f475Smrg     3  meta
5156d522f475Smrg     4  ctrl
5157d522f475Smrg     5  set for motion notify
5158f2e35a3aSmrg     6  set for wheel (and button 6 and 7)
5159f2e35a3aSmrg     7  set for buttons 8 to 11
5160d522f475Smrg*/
5161d522f475Smrg
5162d522f475Smrg/* Position: 32 - 255. */
5163a1f3da82Smrgstatic int
5164f2e35a3aSmrgBtnCode(XtermWidget xw, XButtonEvent *event, int button)
5165d522f475Smrg{
5166f2e35a3aSmrg    int result = (int) (32 + (KeyState(xw, event->state) << 2));
5167d522f475Smrg
51680bd37d32Smrg    if (event->type == MotionNotify)
51690bd37d32Smrg	result += 32;
51700bd37d32Smrg
5171f2e35a3aSmrg    if (button < 0) {
5172d522f475Smrg	result += 3;
5173d522f475Smrg    } else {
5174f2e35a3aSmrg	result += button & 3;
5175f2e35a3aSmrg	if (button & 4)
5176f2e35a3aSmrg	    result += 64;
5177f2e35a3aSmrg	if (button & 8)
5178f2e35a3aSmrg	    result += 128;
5179d522f475Smrg    }
51800bd37d32Smrg    TRACE(("BtnCode button %d, %s state " FMT_MODIFIER_NAMES " ->%#x\n",
51810bd37d32Smrg	   button,
51820bd37d32Smrg	   visibleEventType(event->type),
51830bd37d32Smrg	   ARG_MODIFIER_NAMES(event->state),
51840bd37d32Smrg	   result));
5185a1f3da82Smrg    return result;
5186a1f3da82Smrg}
5187a1f3da82Smrg
5188a1f3da82Smrgstatic unsigned
5189913cc679SmrgEmitButtonCode(XtermWidget xw,
5190e0a2b6dfSmrg	       Char *line,
51910bd37d32Smrg	       unsigned count,
5192894e0ac8Smrg	       XButtonEvent *event,
51930bd37d32Smrg	       int button)
5194a1f3da82Smrg{
5195913cc679Smrg    TScreen *screen = TScreenOf(xw);
51960bd37d32Smrg    int value;
5197a1f3da82Smrg
5198913cc679Smrg    if (okSendMousePos(xw) == X10_MOUSE) {
51990bd37d32Smrg	value = CharOf(' ' + button);
5200a1f3da82Smrg    } else {
5201f2e35a3aSmrg	value = BtnCode(xw, event, button);
52020bd37d32Smrg    }
52030bd37d32Smrg
52040bd37d32Smrg    switch (screen->extend_coords) {
52050bd37d32Smrg    default:
52060bd37d32Smrg	line[count++] = CharOf(value);
52070bd37d32Smrg	break;
52080bd37d32Smrg    case SET_SGR_EXT_MODE_MOUSE:
5209f2e35a3aSmrg    case SET_PIXEL_POSITION_MOUSE:
52100bd37d32Smrg	value -= 32;		/* encoding starts at zero */
52110bd37d32Smrg	/* FALLTHRU */
52120bd37d32Smrg    case SET_URXVT_EXT_MODE_MOUSE:
52130bd37d32Smrg	count += (unsigned) sprintf((char *) line + count, "%d", value);
52140bd37d32Smrg	break;
52150bd37d32Smrg    case SET_EXT_MODE_MOUSE:
52160bd37d32Smrg	if (value < 128) {
52170bd37d32Smrg	    line[count++] = CharOf(value);
52180bd37d32Smrg	} else {
52190bd37d32Smrg	    line[count++] = CharOf(0xC0 + (value >> 6));
52200bd37d32Smrg	    line[count++] = CharOf(0x80 + (value & 0x3F));
52210bd37d32Smrg	}
52220bd37d32Smrg	break;
5223a1f3da82Smrg    }
5224a1f3da82Smrg    return count;
5225d522f475Smrg}
5226d522f475Smrg
52270bd37d32Smrgstatic int
52280bd37d32SmrgFirstBitN(int bits)
52290bd37d32Smrg{
52300bd37d32Smrg    int result = -1;
52310bd37d32Smrg    if (bits > 0) {
52320bd37d32Smrg	result = 0;
52330bd37d32Smrg	while (!(bits & 1)) {
52340bd37d32Smrg	    bits /= 2;
52350bd37d32Smrg	    ++result;
52360bd37d32Smrg	}
52370bd37d32Smrg    }
52380bd37d32Smrg    return result;
52390bd37d32Smrg}
52400bd37d32Smrg
52410bd37d32Smrg#define ButtonBit(button) ((button >= 0) ? (1 << (button)) : 0)
52420bd37d32Smrg
5243913cc679Smrg#define EMIT_BUTTON(button) EmitButtonCode(xw, line, count, event, button)
52440bd37d32Smrg
5245d522f475Smrgstatic void
5246894e0ac8SmrgEditorButton(XtermWidget xw, XButtonEvent *event)
5247d522f475Smrg{
5248956cc18dSsnj    TScreen *screen = TScreenOf(xw);
5249d522f475Smrg    int pty = screen->respond;
52500bd37d32Smrg    int mouse_limit = MouseLimit(screen);
52510bd37d32Smrg    Char line[32];
52520bd37d32Smrg    Char final = 'M';
5253d522f475Smrg    int row, col;
5254d522f475Smrg    int button;
5255d522f475Smrg    unsigned count = 0;
5256d522f475Smrg    Boolean changed = True;
5257d522f475Smrg
5258d522f475Smrg    /* If button event, get button # adjusted for DEC compatibility */
52592eaa94a1Schristos    button = (int) (event->button - 1);
5260d522f475Smrg    if (button >= 3)
5261d522f475Smrg	button++;
5262d522f475Smrg
5263f2e35a3aSmrg    /* Ignore buttons that cannot be encoded */
5264f2e35a3aSmrg    if (screen->send_mouse_pos == X10_MOUSE) {
5265f2e35a3aSmrg	if (button > 3)
5266f2e35a3aSmrg	    return;
5267f2e35a3aSmrg    } else if (screen->extend_coords == SET_SGR_EXT_MODE_MOUSE
5268f2e35a3aSmrg	       || screen->extend_coords == SET_URXVT_EXT_MODE_MOUSE
5269f2e35a3aSmrg	       || screen->extend_coords == SET_PIXEL_POSITION_MOUSE) {
5270f2e35a3aSmrg	if (button > 15) {
5271f2e35a3aSmrg	    return;
5272f2e35a3aSmrg	}
5273f2e35a3aSmrg    } else {
5274f2e35a3aSmrg	if (button > 11) {
5275f2e35a3aSmrg	    return;
5276f2e35a3aSmrg	}
5277f2e35a3aSmrg    }
5278d522f475Smrg
5279f2e35a3aSmrg    if (screen->extend_coords == SET_PIXEL_POSITION_MOUSE) {
5280f2e35a3aSmrg	row = event->y - OriginY(screen);
5281f2e35a3aSmrg	col = event->x - OriginX(screen);
5282f2e35a3aSmrg    } else {
5283f2e35a3aSmrg	/* Compute character position of mouse pointer */
5284f2e35a3aSmrg	row = (event->y - screen->border) / FontHeight(screen);
5285f2e35a3aSmrg	col = (event->x - OriginX(screen)) / FontWidth(screen);
5286d522f475Smrg
5287f2e35a3aSmrg	/* Limit to screen dimensions */
5288f2e35a3aSmrg	if (row < 0)
5289f2e35a3aSmrg	    row = 0;
5290f2e35a3aSmrg	else if (row > screen->max_row)
5291f2e35a3aSmrg	    row = screen->max_row;
5292492d43a5Smrg
5293f2e35a3aSmrg	if (col < 0)
5294f2e35a3aSmrg	    col = 0;
5295f2e35a3aSmrg	else if (col > screen->max_col)
5296f2e35a3aSmrg	    col = screen->max_col;
5297f2e35a3aSmrg
5298f2e35a3aSmrg	if (mouse_limit > 0) {
5299f2e35a3aSmrg	    /* Limit to representable mouse dimensions */
5300f2e35a3aSmrg	    if (row > mouse_limit)
5301f2e35a3aSmrg		row = mouse_limit;
5302f2e35a3aSmrg	    if (col > mouse_limit)
5303f2e35a3aSmrg		col = mouse_limit;
5304f2e35a3aSmrg	}
53050bd37d32Smrg    }
5306d522f475Smrg
5307d522f475Smrg    /* Build key sequence starting with \E[M */
5308d522f475Smrg    if (screen->control_eight_bits) {
5309d522f475Smrg	line[count++] = ANSI_CSI;
5310d522f475Smrg    } else {
5311d522f475Smrg	line[count++] = ANSI_ESC;
5312d522f475Smrg	line[count++] = '[';
5313d522f475Smrg    }
53140bd37d32Smrg    switch (screen->extend_coords) {
53150bd37d32Smrg    case 0:
53160bd37d32Smrg    case SET_EXT_MODE_MOUSE:
5317d522f475Smrg#if OPT_SCO_FUNC_KEYS
53180bd37d32Smrg	if (xw->keyboard.type == keyboardIsSCO) {
53190bd37d32Smrg	    /*
53200bd37d32Smrg	     * SCO function key F1 is \E[M, which would conflict with xterm's
53210bd37d32Smrg	     * normal kmous.
53220bd37d32Smrg	     */
53230bd37d32Smrg	    line[count++] = '>';
53240bd37d32Smrg	}
5325d522f475Smrg#endif
53260bd37d32Smrg	line[count++] = final;
53270bd37d32Smrg	break;
53280bd37d32Smrg    case SET_SGR_EXT_MODE_MOUSE:
5329f2e35a3aSmrg    case SET_PIXEL_POSITION_MOUSE:
53300bd37d32Smrg	line[count++] = '<';
53310bd37d32Smrg	break;
53320bd37d32Smrg    }
5333d522f475Smrg
5334d522f475Smrg    /* Add event code to key sequence */
5335913cc679Smrg    if (okSendMousePos(xw) == X10_MOUSE) {
53360bd37d32Smrg	count = EMIT_BUTTON(button);
5337d522f475Smrg    } else {
5338d522f475Smrg	/* Button-Motion events */
5339d522f475Smrg	switch (event->type) {
5340d522f475Smrg	case ButtonPress:
53410bd37d32Smrg	    screen->mouse_button |= ButtonBit(button);
53420bd37d32Smrg	    count = EMIT_BUTTON(button);
5343d522f475Smrg	    break;
5344d522f475Smrg	case ButtonRelease:
5345d522f475Smrg	    /*
5346f2e35a3aSmrg	     * The (vertical) wheel mouse interface generates release-events
5347f2e35a3aSmrg	     * for buttons 4 and 5.
5348f2e35a3aSmrg	     *
5349f2e35a3aSmrg	     * The X10/X11 xterm protocol maps the release for buttons 1..3 to
5350f2e35a3aSmrg	     * a -1, which will be later mapped into a "0" (some button was
5351f2e35a3aSmrg	     * released),  At this point, buttons 1..3 are encoded 0..2 (the
5352f2e35a3aSmrg	     * code 3 is unused).
5353f2e35a3aSmrg	     *
5354f2e35a3aSmrg	     * The SGR (extended) xterm mouse protocol keeps the button number
5355f2e35a3aSmrg	     * and uses a "m" to indicate button release.
5356f2e35a3aSmrg	     *
5357f2e35a3aSmrg	     * The behavior for mice with more buttons is unclear, and may be
5358f2e35a3aSmrg	     * revised -TD
5359d522f475Smrg	     */
53600bd37d32Smrg	    screen->mouse_button &= ~ButtonBit(button);
5361f2e35a3aSmrg	    if (button < 3 || button > 5) {
53620bd37d32Smrg		switch (screen->extend_coords) {
53630bd37d32Smrg		case SET_SGR_EXT_MODE_MOUSE:
5364f2e35a3aSmrg		case SET_PIXEL_POSITION_MOUSE:
53650bd37d32Smrg		    final = 'm';
53660bd37d32Smrg		    break;
53670bd37d32Smrg		default:
53680bd37d32Smrg		    button = -1;
53690bd37d32Smrg		    break;
53700bd37d32Smrg		}
53710bd37d32Smrg	    }
53720bd37d32Smrg	    count = EMIT_BUTTON(button);
5373d522f475Smrg	    break;
5374d522f475Smrg	case MotionNotify:
5375d522f475Smrg	    /* BTN_EVENT_MOUSE and ANY_EVENT_MOUSE modes send motion
5376d522f475Smrg	     * events only if character cell has changed.
5377d522f475Smrg	     */
5378d522f475Smrg	    if ((row == screen->mouse_row)
5379d522f475Smrg		&& (col == screen->mouse_col)) {
5380d522f475Smrg		changed = False;
5381d522f475Smrg	    } else {
53820bd37d32Smrg		count = EMIT_BUTTON(FirstBitN(screen->mouse_button));
5383d522f475Smrg	    }
5384d522f475Smrg	    break;
5385d522f475Smrg	default:
5386d522f475Smrg	    changed = False;
5387d522f475Smrg	    break;
5388d522f475Smrg	}
5389d522f475Smrg    }
5390d522f475Smrg
5391d522f475Smrg    if (changed) {
5392d522f475Smrg	screen->mouse_row = row;
5393d522f475Smrg	screen->mouse_col = col;
5394d522f475Smrg
5395492d43a5Smrg	TRACE(("mouse at %d,%d button+mask = %#x\n", row, col, line[count - 1]));
5396d522f475Smrg
5397492d43a5Smrg	/* Add pointer position to key sequence */
53980bd37d32Smrg	count = EmitMousePositionSeparator(screen, line, count);
5399492d43a5Smrg	count = EmitMousePosition(screen, line, count, col);
54000bd37d32Smrg	count = EmitMousePositionSeparator(screen, line, count);
5401492d43a5Smrg	count = EmitMousePosition(screen, line, count, row);
5402d522f475Smrg
54030bd37d32Smrg	switch (screen->extend_coords) {
54040bd37d32Smrg	case SET_SGR_EXT_MODE_MOUSE:
54050bd37d32Smrg	case SET_URXVT_EXT_MODE_MOUSE:
5406f2e35a3aSmrg	case SET_PIXEL_POSITION_MOUSE:
54070bd37d32Smrg	    line[count++] = final;
54080bd37d32Smrg	    break;
54090bd37d32Smrg	}
54100bd37d32Smrg
5411d522f475Smrg	/* Transmit key sequence to process running under xterm */
5412f2e35a3aSmrg	TRACE(("EditorButton -> %s\n", visibleChars(line, count)));
5413d522f475Smrg	v_write(pty, line, count);
5414d522f475Smrg    }
5415d522f475Smrg    return;
5416d522f475Smrg}
5417d522f475Smrg
5418913cc679Smrg/*
5419913cc679Smrg * Check the current send_mouse_pos against allowed mouse-operations, returning
5420913cc679Smrg * none if it is disallowed.
5421913cc679Smrg */
5422913cc679SmrgXtermMouseModes
5423913cc679SmrgokSendMousePos(XtermWidget xw)
5424913cc679Smrg{
5425913cc679Smrg    TScreen *screen = TScreenOf(xw);
5426f2e35a3aSmrg    XtermMouseModes result = (XtermMouseModes) screen->send_mouse_pos;
5427913cc679Smrg
5428f2e35a3aSmrg    switch ((int) result) {
5429913cc679Smrg    case MOUSE_OFF:
5430913cc679Smrg	break;
5431913cc679Smrg    case X10_MOUSE:
5432913cc679Smrg	if (!AllowMouseOps(xw, emX10))
5433913cc679Smrg	    result = MOUSE_OFF;
5434913cc679Smrg	break;
5435913cc679Smrg    case VT200_MOUSE:
5436913cc679Smrg	if (!AllowMouseOps(xw, emVT200Click))
5437913cc679Smrg	    result = MOUSE_OFF;
5438913cc679Smrg	break;
5439913cc679Smrg    case VT200_HIGHLIGHT_MOUSE:
5440913cc679Smrg	if (!AllowMouseOps(xw, emVT200Hilite))
5441913cc679Smrg	    result = MOUSE_OFF;
5442913cc679Smrg	break;
5443913cc679Smrg    case BTN_EVENT_MOUSE:
5444913cc679Smrg	if (!AllowMouseOps(xw, emAnyButton))
5445913cc679Smrg	    result = MOUSE_OFF;
5446913cc679Smrg	break;
5447913cc679Smrg    case ANY_EVENT_MOUSE:
5448913cc679Smrg	if (!AllowMouseOps(xw, emAnyEvent))
5449913cc679Smrg	    result = MOUSE_OFF;
5450913cc679Smrg	break;
5451913cc679Smrg    case DEC_LOCATOR:
5452913cc679Smrg	if (!AllowMouseOps(xw, emLocator))
5453913cc679Smrg	    result = MOUSE_OFF;
5454913cc679Smrg	break;
5455913cc679Smrg    }
5456913cc679Smrg    return result;
5457913cc679Smrg}
5458913cc679Smrg
5459d522f475Smrg#if OPT_FOCUS_EVENT
5460913cc679Smrg/*
5461913cc679Smrg * Check the current send_focus_pos against allowed mouse-operations, returning
5462913cc679Smrg * none if it is disallowed.
5463913cc679Smrg */
5464913cc679Smrgstatic int
5465913cc679SmrgokSendFocusPos(XtermWidget xw)
5466d522f475Smrg{
5467956cc18dSsnj    TScreen *screen = TScreenOf(xw);
5468913cc679Smrg    int result = screen->send_focus_pos;
5469913cc679Smrg
5470913cc679Smrg    if (!AllowMouseOps(xw, emFocusEvent)) {
5471913cc679Smrg	result = False;
5472913cc679Smrg    }
5473913cc679Smrg    return result;
5474913cc679Smrg}
5475d522f475Smrg
5476913cc679Smrgvoid
5477913cc679SmrgSendFocusButton(XtermWidget xw, XFocusChangeEvent *event)
5478913cc679Smrg{
5479913cc679Smrg    if (okSendFocusPos(xw)) {
5480d522f475Smrg	ANSI reply;
5481d522f475Smrg
5482d522f475Smrg	memset(&reply, 0, sizeof(reply));
5483d522f475Smrg	reply.a_type = ANSI_CSI;
5484d522f475Smrg
5485d522f475Smrg#if OPT_SCO_FUNC_KEYS
5486d522f475Smrg	if (xw->keyboard.type == keyboardIsSCO) {
5487d522f475Smrg	    reply.a_pintro = '>';
5488d522f475Smrg	}
5489d522f475Smrg#endif
54902eaa94a1Schristos	reply.a_final = CharOf((event->type == FocusIn) ? 'I' : 'O');
5491d522f475Smrg	unparseseq(xw, &reply);
5492d522f475Smrg    }
5493d522f475Smrg    return;
5494d522f475Smrg}
5495d522f475Smrg#endif /* OPT_FOCUS_EVENT */
54960bd37d32Smrg
54970bd37d32Smrg#if OPT_SELECTION_OPS
54980bd37d32Smrg/*
54990bd37d32Smrg * Get the event-time, needed to process selections.
55000bd37d32Smrg */
55010bd37d32Smrgstatic Time
5502894e0ac8SmrggetEventTime(XEvent *event)
55030bd37d32Smrg{
55040bd37d32Smrg    Time result;
55050bd37d32Smrg
55060bd37d32Smrg    if (IsBtnEvent(event)) {
55070bd37d32Smrg	result = ((XButtonEvent *) event)->time;
55080bd37d32Smrg    } else if (IsKeyEvent(event)) {
55090bd37d32Smrg	result = ((XKeyEvent *) event)->time;
55100bd37d32Smrg    } else {
55110bd37d32Smrg	result = 0;
55120bd37d32Smrg    }
55130bd37d32Smrg
55140bd37d32Smrg    return result;
55150bd37d32Smrg}
55160bd37d32Smrg
55170bd37d32Smrg/* obtain the selection string, passing the endpoints to caller's parameters */
551801037d57Smrgstatic void
551901037d57SmrgdoSelectionFormat(XtermWidget xw,
552001037d57Smrg		  Widget w,
552101037d57Smrg		  XEvent *event,
552201037d57Smrg		  String *params,
552301037d57Smrg		  Cardinal *num_params,
552401037d57Smrg		  FormatSelect format_select)
55250bd37d32Smrg{
55260bd37d32Smrg    TScreen *screen = TScreenOf(xw);
552701037d57Smrg    InternalSelect *mydata = &(screen->internal_select);
552801037d57Smrg
552901037d57Smrg    memset(mydata, 0, sizeof(*mydata));
553001037d57Smrg    mydata->format = x_strdup(params[0]);
553101037d57Smrg    mydata->format_select = format_select;
55320bd37d32Smrg
55330bd37d32Smrg    screen->selectToBuffer = True;
5534f2e35a3aSmrg    beginInternalSelect(xw);
5535f2e35a3aSmrg
55360bd37d32Smrg    xtermGetSelection(w, getEventTime(event), params + 1, *num_params - 1, NULL);
5537f2e35a3aSmrg
5538f2e35a3aSmrg    if (screen->selectToBuffer)
5539f2e35a3aSmrg	finishInternalSelect(xw);
55400bd37d32Smrg}
55410bd37d32Smrg
55420bd37d32Smrg/* obtain data from the screen, passing the endpoints to caller's parameters */
55430bd37d32Smrgstatic char *
5544913cc679SmrggetDataFromScreen(XtermWidget xw, XEvent *event, String method, CELL *start, CELL *finish)
55450bd37d32Smrg{
55460bd37d32Smrg    TScreen *screen = TScreenOf(xw);
55470bd37d32Smrg
55480bd37d32Smrg    CELL save_old_start = screen->startH;
55490bd37d32Smrg    CELL save_old_end = screen->endH;
55500bd37d32Smrg
55510bd37d32Smrg    CELL save_startSel = screen->startSel;
55520bd37d32Smrg    CELL save_startRaw = screen->startRaw;
55530bd37d32Smrg    CELL save_finishSel = screen->endSel;
55540bd37d32Smrg    CELL save_finishRaw = screen->endRaw;
55550bd37d32Smrg
55560bd37d32Smrg    int save_firstValidRow = screen->firstValidRow;
55570bd37d32Smrg    int save_lastValidRow = screen->lastValidRow;
55580bd37d32Smrg
555901037d57Smrg    const Cardinal noClick = 0;
556001037d57Smrg    int save_numberOfClicks = screen->numberOfClicks;
556101037d57Smrg
55620bd37d32Smrg    SelectUnit saveUnits = screen->selectUnit;
556301037d57Smrg    SelectUnit saveMap = screen->selectMap[noClick];
55640bd37d32Smrg#if OPT_SELECT_REGEX
556501037d57Smrg    char *saveExpr = screen->selectExpr[noClick];
55660bd37d32Smrg#endif
5567f2e35a3aSmrg    SelectedCells *scp = &(screen->selected_cells[PRIMARY_CODE]);
5568f2e35a3aSmrg    SelectedCells save_selection = *scp;
55690bd37d32Smrg
55700bd37d32Smrg    char *result = 0;
55710bd37d32Smrg
55720bd37d32Smrg    TRACE(("getDataFromScreen %s\n", method));
55730bd37d32Smrg
5574f2e35a3aSmrg    memset(scp, 0, sizeof(*scp));
55750bd37d32Smrg
557601037d57Smrg    screen->numberOfClicks = 1;
557701037d57Smrg    lookupSelectUnit(xw, noClick, method);
557801037d57Smrg    screen->selectUnit = screen->selectMap[noClick];
55790bd37d32Smrg
55800bd37d32Smrg    memset(start, 0, sizeof(*start));
5581913cc679Smrg    if (IsBtnEvent(event)) {
5582913cc679Smrg	XButtonEvent *btn_event = (XButtonEvent *) event;
5583913cc679Smrg	CELL cell;
5584913cc679Smrg	screen->firstValidRow = 0;
5585913cc679Smrg	screen->lastValidRow = screen->max_row;
5586913cc679Smrg	PointToCELL(screen, btn_event->y, btn_event->x, &cell);
5587913cc679Smrg	start->row = cell.row;
5588913cc679Smrg	start->col = cell.col;
5589913cc679Smrg	finish->row = cell.row;
5590913cc679Smrg	finish->col = screen->max_col;
5591913cc679Smrg    } else {
5592913cc679Smrg	start->row = screen->cur_row;
5593913cc679Smrg	start->col = screen->cur_col;
5594913cc679Smrg	finish->row = screen->cur_row;
5595913cc679Smrg	finish->col = screen->max_col;
5596913cc679Smrg    }
55970bd37d32Smrg
5598f2e35a3aSmrg    ComputeSelect(xw, start, finish, False, False);
5599f2e35a3aSmrg    SaltTextAway(xw,
5600f2e35a3aSmrg		 TargetToSelection(screen, PRIMARY_NAME),
5601f2e35a3aSmrg		 &(screen->startSel), &(screen->endSel));
56020bd37d32Smrg
5603f2e35a3aSmrg    if (scp->data_limit && scp->data_buffer) {
5604f2e35a3aSmrg	TRACE(("...getDataFromScreen selection-data %.*s\n",
5605f2e35a3aSmrg	       (int) scp->data_limit,
5606f2e35a3aSmrg	       scp->data_buffer));
5607f2e35a3aSmrg	result = malloc(scp->data_limit + 1);
56080bd37d32Smrg	if (result) {
5609f2e35a3aSmrg	    memcpy(result, scp->data_buffer, scp->data_limit);
5610f2e35a3aSmrg	    result[scp->data_limit] = 0;
56110bd37d32Smrg	}
5612f2e35a3aSmrg	free(scp->data_buffer);
5613f2e35a3aSmrg	scp->data_limit = 0;
56140bd37d32Smrg    }
56150bd37d32Smrg
56160bd37d32Smrg    TRACE(("...getDataFromScreen restoring previous selection\n"));
56170bd37d32Smrg
56180bd37d32Smrg    screen->startSel = save_startSel;
56190bd37d32Smrg    screen->startRaw = save_startRaw;
56200bd37d32Smrg    screen->endSel = save_finishSel;
56210bd37d32Smrg    screen->endRaw = save_finishRaw;
56220bd37d32Smrg
56230bd37d32Smrg    screen->firstValidRow = save_firstValidRow;
56240bd37d32Smrg    screen->lastValidRow = save_lastValidRow;
56250bd37d32Smrg
562601037d57Smrg    screen->numberOfClicks = save_numberOfClicks;
56270bd37d32Smrg    screen->selectUnit = saveUnits;
562801037d57Smrg    screen->selectMap[noClick] = saveMap;
56290bd37d32Smrg#if OPT_SELECT_REGEX
563001037d57Smrg    screen->selectExpr[noClick] = saveExpr;
56310bd37d32Smrg#endif
56320bd37d32Smrg
5633f2e35a3aSmrg    screen->selected_cells[0] = save_selection;
56340bd37d32Smrg
56350bd37d32Smrg    TrackText(xw, &save_old_start, &save_old_end);
56360bd37d32Smrg
56370bd37d32Smrg    TRACE(("...getDataFromScreen done\n"));
56380bd37d32Smrg    return result;
56390bd37d32Smrg}
56400bd37d32Smrg
56410bd37d32Smrg/*
56420bd37d32Smrg * Split-up the format before substituting data, to avoid quoting issues.
56430bd37d32Smrg * The resource mechanism has a limited ability to handle escapes.  We take
56440bd37d32Smrg * the result as if it were an sh-type string and parse it into a regular
56450bd37d32Smrg * argv array.
56460bd37d32Smrg */
56470bd37d32Smrgstatic char **
56480bd37d32SmrgtokenizeFormat(String format)
56490bd37d32Smrg{
56500bd37d32Smrg    char **result = 0;
56510bd37d32Smrg
56520bd37d32Smrg    format = x_skip_blanks(format);
56530bd37d32Smrg    if (*format != '\0') {
56540bd37d32Smrg	char *blob = x_strdup(format);
56552e4f8982Smrg	int pass;
56560bd37d32Smrg
56570bd37d32Smrg	for (pass = 0; pass < 2; ++pass) {
56580bd37d32Smrg	    int used = 0;
56590bd37d32Smrg	    int first = 1;
56600bd37d32Smrg	    int escaped = 0;
56610bd37d32Smrg	    int squoted = 0;
56620bd37d32Smrg	    int dquoted = 0;
56632e4f8982Smrg	    int n;
5664f2e35a3aSmrg	    int argc = 0;
56650bd37d32Smrg
56660bd37d32Smrg	    for (n = 0; format[n] != '\0'; ++n) {
56670bd37d32Smrg		if (escaped) {
56680bd37d32Smrg		    blob[used++] = format[n];
56690bd37d32Smrg		    escaped = 0;
56700bd37d32Smrg		} else if (format[n] == '"') {
56710bd37d32Smrg		    if (!squoted) {
56720bd37d32Smrg			if (!dquoted)
56730bd37d32Smrg			    blob[used++] = format[n];
56740bd37d32Smrg			dquoted = !dquoted;
56750bd37d32Smrg		    }
56760bd37d32Smrg		} else if (format[n] == '\'') {
56770bd37d32Smrg		    if (!dquoted) {
56780bd37d32Smrg			if (!squoted)
56790bd37d32Smrg			    blob[used++] = format[n];
56800bd37d32Smrg			squoted = !squoted;
56810bd37d32Smrg		    }
56820bd37d32Smrg		} else if (format[n] == '\\') {
56830bd37d32Smrg		    blob[used++] = format[n];
56840bd37d32Smrg		    escaped = 1;
56850bd37d32Smrg		} else {
56860bd37d32Smrg		    if (first) {
56870bd37d32Smrg			first = 0;
56880bd37d32Smrg			if (pass) {
56890bd37d32Smrg			    result[argc] = &blob[n];
56900bd37d32Smrg			}
56910bd37d32Smrg			++argc;
56920bd37d32Smrg		    }
56930bd37d32Smrg		    if (isspace((Char) format[n])) {
56940bd37d32Smrg			first = !isspace((Char) format[n + 1]);
56950bd37d32Smrg			if (squoted || dquoted) {
56960bd37d32Smrg			    blob[used++] = format[n];
56970bd37d32Smrg			} else if (first) {
56980bd37d32Smrg			    blob[used++] = '\0';
56990bd37d32Smrg			}
57000bd37d32Smrg		    } else {
57010bd37d32Smrg			blob[used++] = format[n];
57020bd37d32Smrg		    }
57030bd37d32Smrg		}
57040bd37d32Smrg	    }
57050bd37d32Smrg	    blob[used] = '\0';
57060bd37d32Smrg	    assert(strlen(blob) <= strlen(format));
57070bd37d32Smrg	    if (!pass) {
57080bd37d32Smrg		result = TypeCallocN(char *, argc + 1);
57090bd37d32Smrg		if (result == 0) {
57100bd37d32Smrg		    free(blob);
57110bd37d32Smrg		    break;
57120bd37d32Smrg		}
57130bd37d32Smrg	    }
57140bd37d32Smrg	}
57150bd37d32Smrg    }
57160bd37d32Smrg#if OPT_TRACE
57170bd37d32Smrg    if (result) {
5718f2e35a3aSmrg	int n;
57190bd37d32Smrg	TRACE(("tokenizeFormat %s\n", format));
5720f2e35a3aSmrg	for (n = 0; result[n]; ++n) {
5721f2e35a3aSmrg	    TRACE(("argv[%d] = %s\n", n, result[n]));
57220bd37d32Smrg	}
57230bd37d32Smrg    }
57240bd37d32Smrg#endif
57250bd37d32Smrg
57260bd37d32Smrg    return result;
57270bd37d32Smrg}
57280bd37d32Smrg
57290bd37d32Smrgstatic void
5730e0a2b6dfSmrgformatVideoAttrs(XtermWidget xw, char *buffer, CELL *cell)
57310bd37d32Smrg{
57320bd37d32Smrg    TScreen *screen = TScreenOf(xw);
57330bd37d32Smrg    LineData *ld = GET_LINEDATA(screen, cell->row);
57340bd37d32Smrg
57350bd37d32Smrg    *buffer = '\0';
57360bd37d32Smrg    if (ld != 0 && cell->col < (int) ld->lineSize) {
5737894e0ac8Smrg	IAttr attribs = ld->attribs[cell->col];
57380bd37d32Smrg	const char *delim = "";
57390bd37d32Smrg
57400bd37d32Smrg	if (attribs & INVERSE) {
57410bd37d32Smrg	    buffer += sprintf(buffer, "7");
57420bd37d32Smrg	    delim = ";";
57430bd37d32Smrg	}
57440bd37d32Smrg	if (attribs & UNDERLINE) {
57450bd37d32Smrg	    buffer += sprintf(buffer, "%s4", delim);
57460bd37d32Smrg	    delim = ";";
57470bd37d32Smrg	}
57480bd37d32Smrg	if (attribs & BOLD) {
57490bd37d32Smrg	    buffer += sprintf(buffer, "%s1", delim);
57500bd37d32Smrg	    delim = ";";
57510bd37d32Smrg	}
57520bd37d32Smrg	if (attribs & BLINK) {
57530bd37d32Smrg	    buffer += sprintf(buffer, "%s5", delim);
57540bd37d32Smrg	    delim = ";";
57550bd37d32Smrg	}
57560bd37d32Smrg#if OPT_ISO_COLORS
57570bd37d32Smrg	if (attribs & FG_COLOR) {
5758f2e35a3aSmrg	    Pixel fg = extract_fg(xw, ld->color[cell->col], attribs);
57590bd37d32Smrg	    if (fg < 8) {
57600bd37d32Smrg		fg += 30;
57610bd37d32Smrg	    } else if (fg < 16) {
57620bd37d32Smrg		fg += 90;
57630bd37d32Smrg	    } else {
57640bd37d32Smrg		buffer += sprintf(buffer, "%s38;5", delim);
57650bd37d32Smrg		delim = ";";
57660bd37d32Smrg	    }
5767f2e35a3aSmrg	    buffer += sprintf(buffer, "%s%lu", delim, fg);
57680bd37d32Smrg	    delim = ";";
57690bd37d32Smrg	}
57700bd37d32Smrg	if (attribs & BG_COLOR) {
5771f2e35a3aSmrg	    Pixel bg = extract_bg(xw, ld->color[cell->col], attribs);
57720bd37d32Smrg	    if (bg < 8) {
57730bd37d32Smrg		bg += 40;
57740bd37d32Smrg	    } else if (bg < 16) {
57750bd37d32Smrg		bg += 100;
57760bd37d32Smrg	    } else {
57770bd37d32Smrg		buffer += sprintf(buffer, "%s48;5", delim);
57780bd37d32Smrg		delim = ";";
57790bd37d32Smrg	    }
5780f2e35a3aSmrg	    (void) sprintf(buffer, "%s%lu", delim, bg);
57810bd37d32Smrg	}
57820bd37d32Smrg#endif
57830bd37d32Smrg    }
57840bd37d32Smrg}
57850bd37d32Smrg
57862e4f8982Smrgstatic char *
57872e4f8982SmrgformatStrlen(char *target, char *source, int freeit)
57882e4f8982Smrg{
57892e4f8982Smrg    if (source != 0) {
57902e4f8982Smrg	sprintf(target, "%u", (unsigned) strlen(source));
57912e4f8982Smrg	if (freeit) {
57922e4f8982Smrg	    free(source);
57932e4f8982Smrg	}
57942e4f8982Smrg    } else {
57952e4f8982Smrg	strcpy(target, "0");
57962e4f8982Smrg    }
57972e4f8982Smrg    return target;
57982e4f8982Smrg}
57992e4f8982Smrg
58000bd37d32Smrg/* substitute data into format, reallocating the result */
58010bd37d32Smrgstatic char *
58020bd37d32SmrgexpandFormat(XtermWidget xw,
58030bd37d32Smrg	     const char *format,
58040bd37d32Smrg	     char *data,
5805e0a2b6dfSmrg	     CELL *start,
5806e0a2b6dfSmrg	     CELL *finish)
58070bd37d32Smrg{
58080bd37d32Smrg    char *result = 0;
58090bd37d32Smrg    if (!IsEmpty(format)) {
58100bd37d32Smrg	static char empty[1];
58110bd37d32Smrg	int pass;
58120bd37d32Smrg	int n;
58130bd37d32Smrg	char numbers[80];
58140bd37d32Smrg
58150bd37d32Smrg	if (data == 0)
58160bd37d32Smrg	    data = empty;
58170bd37d32Smrg
58180bd37d32Smrg	for (pass = 0; pass < 2; ++pass) {
58190bd37d32Smrg	    size_t need = 0;
58200bd37d32Smrg
58210bd37d32Smrg	    for (n = 0; format[n] != '\0'; ++n) {
58220bd37d32Smrg
58230bd37d32Smrg		if (format[n] == '%') {
58242e4f8982Smrg		    char *value = 0;
58252e4f8982Smrg
58260bd37d32Smrg		    switch (format[++n]) {
58270bd37d32Smrg		    case '%':
58280bd37d32Smrg			if (pass) {
58290bd37d32Smrg			    result[need] = format[n];
58300bd37d32Smrg			}
58310bd37d32Smrg			++need;
58320bd37d32Smrg			break;
58330bd37d32Smrg		    case 'P':
58340bd37d32Smrg			sprintf(numbers, "%d;%d",
58350bd37d32Smrg				TScreenOf(xw)->topline + start->row + 1,
58360bd37d32Smrg				start->col + 1);
58370bd37d32Smrg			value = numbers;
58380bd37d32Smrg			break;
58390bd37d32Smrg		    case 'p':
58400bd37d32Smrg			sprintf(numbers, "%d;%d",
58410bd37d32Smrg				TScreenOf(xw)->topline + finish->row + 1,
58420bd37d32Smrg				finish->col + 1);
58430bd37d32Smrg			value = numbers;
58440bd37d32Smrg			break;
58452e4f8982Smrg		    case 'R':
58462e4f8982Smrg			value = formatStrlen(numbers, x_strrtrim(data), 1);
58472e4f8982Smrg			break;
58482e4f8982Smrg		    case 'r':
58492e4f8982Smrg			value = x_strrtrim(data);
58502e4f8982Smrg			break;
58510bd37d32Smrg		    case 'S':
58522e4f8982Smrg			value = formatStrlen(numbers, data, 0);
58530bd37d32Smrg			break;
58540bd37d32Smrg		    case 's':
58550bd37d32Smrg			value = data;
58560bd37d32Smrg			break;
58570bd37d32Smrg		    case 'T':
58582e4f8982Smrg			value = formatStrlen(numbers, x_strtrim(data), 1);
58590bd37d32Smrg			break;
58600bd37d32Smrg		    case 't':
58610bd37d32Smrg			value = x_strtrim(data);
58620bd37d32Smrg			break;
58630bd37d32Smrg		    case 'V':
58640bd37d32Smrg			formatVideoAttrs(xw, numbers, start);
58650bd37d32Smrg			value = numbers;
58660bd37d32Smrg			break;
58670bd37d32Smrg		    case 'v':
58680bd37d32Smrg			formatVideoAttrs(xw, numbers, finish);
58690bd37d32Smrg			value = numbers;
58700bd37d32Smrg			break;
58710bd37d32Smrg		    default:
58720bd37d32Smrg			if (pass) {
58730bd37d32Smrg			    result[need] = format[n];
58740bd37d32Smrg			}
58750bd37d32Smrg			--n;
58760bd37d32Smrg			++need;
58770bd37d32Smrg			break;
58780bd37d32Smrg		    }
58790bd37d32Smrg		    if (value != 0) {
58800bd37d32Smrg			if (pass) {
58810bd37d32Smrg			    strcpy(result + need, value);
58820bd37d32Smrg			}
58830bd37d32Smrg			need += strlen(value);
58840bd37d32Smrg			if (value != numbers && value != data) {
58850bd37d32Smrg			    free(value);
58860bd37d32Smrg			}
58870bd37d32Smrg		    }
58880bd37d32Smrg		} else {
58890bd37d32Smrg		    if (pass) {
58900bd37d32Smrg			result[need] = format[n];
58910bd37d32Smrg		    }
58920bd37d32Smrg		    ++need;
58930bd37d32Smrg		}
58940bd37d32Smrg	    }
58950bd37d32Smrg	    if (pass) {
58960bd37d32Smrg		result[need] = '\0';
58970bd37d32Smrg	    } else {
58980bd37d32Smrg		++need;
58990bd37d32Smrg		result = malloc(need);
59000bd37d32Smrg		if (result == 0) {
59010bd37d32Smrg		    break;
59020bd37d32Smrg		}
59030bd37d32Smrg	    }
59040bd37d32Smrg	}
59050bd37d32Smrg    }
59060bd37d32Smrg    TRACE(("expandFormat(%s) = %s\n", NonNull(format), NonNull(result)));
59070bd37d32Smrg    return result;
59080bd37d32Smrg}
59090bd37d32Smrg
59100bd37d32Smrg/* execute the command after forking.  The main process frees its data */
59110bd37d32Smrgstatic void
59122e4f8982SmrgexecuteCommand(pid_t pid, char **argv)
59130bd37d32Smrg{
59142e4f8982Smrg    (void) pid;
59150bd37d32Smrg    if (argv != 0 && argv[0] != 0) {
59162e4f8982Smrg	char *child_cwd = ProcGetCWD(pid);
59172e4f8982Smrg
59180bd37d32Smrg	if (fork() == 0) {
59192e4f8982Smrg	    if (child_cwd) {
59202e4f8982Smrg		IGNORE_RC(chdir(child_cwd));	/* We don't care if this fails */
59212e4f8982Smrg	    }
59220bd37d32Smrg	    execvp(argv[0], argv);
59230bd37d32Smrg	    exit(EXIT_FAILURE);
59240bd37d32Smrg	}
5925913cc679Smrg	free(child_cwd);
59260bd37d32Smrg    }
59270bd37d32Smrg}
59280bd37d32Smrg
59290bd37d32Smrgstatic void
59300bd37d32SmrgfreeArgv(char *blob, char **argv)
59310bd37d32Smrg{
59320bd37d32Smrg    if (blob) {
59330bd37d32Smrg	free(blob);
59340bd37d32Smrg	if (argv) {
59352e4f8982Smrg	    int n;
59360bd37d32Smrg	    for (n = 0; argv[n]; ++n)
59370bd37d32Smrg		free(argv[n]);
59380bd37d32Smrg	    free(argv);
59390bd37d32Smrg	}
59400bd37d32Smrg    }
59410bd37d32Smrg}
59420bd37d32Smrg
594301037d57Smrgstatic void
594401037d57SmrgreallyExecFormatted(Widget w, char *format, char *data, CELL *start, CELL *finish)
594501037d57Smrg{
594601037d57Smrg    XtermWidget xw;
594701037d57Smrg
594801037d57Smrg    if ((xw = getXtermWidget(w)) != 0) {
594901037d57Smrg	char **argv;
595001037d57Smrg
595101037d57Smrg	if ((argv = tokenizeFormat(format)) != 0) {
59522e4f8982Smrg	    char *blob = argv[0];
59532e4f8982Smrg	    int argc;
59542e4f8982Smrg
595501037d57Smrg	    for (argc = 0; argv[argc] != 0; ++argc) {
595601037d57Smrg		argv[argc] = expandFormat(xw, argv[argc], data, start, finish);
595701037d57Smrg	    }
59582e4f8982Smrg	    executeCommand(TScreenOf(xw)->pid, argv);
595901037d57Smrg	    freeArgv(blob, argv);
596001037d57Smrg	}
596101037d57Smrg    }
596201037d57Smrg}
596301037d57Smrg
59640bd37d32Smrgvoid
59650bd37d32SmrgHandleExecFormatted(Widget w,
596601037d57Smrg		    XEvent *event,
5967e0a2b6dfSmrg		    String *params,	/* selections */
59680bd37d32Smrg		    Cardinal *num_params)
59690bd37d32Smrg{
59700bd37d32Smrg    XtermWidget xw;
59710bd37d32Smrg
5972f2e35a3aSmrg    TRACE_EVENT("HandleExecFormatted", event, params, num_params);
597301037d57Smrg    if ((xw = getXtermWidget(w)) != 0 &&
597401037d57Smrg	(*num_params > 1)) {
597501037d57Smrg	doSelectionFormat(xw, w, event, params, num_params, reallyExecFormatted);
59760bd37d32Smrg    }
59770bd37d32Smrg}
59780bd37d32Smrg
59790bd37d32Smrgvoid
59800bd37d32SmrgHandleExecSelectable(Widget w,
5981913cc679Smrg		     XEvent *event,
5982e0a2b6dfSmrg		     String *params,	/* selections */
59830bd37d32Smrg		     Cardinal *num_params)
59840bd37d32Smrg{
59850bd37d32Smrg    XtermWidget xw;
59860bd37d32Smrg
59870bd37d32Smrg    if ((xw = getXtermWidget(w)) != 0) {
5988f2e35a3aSmrg	TRACE_EVENT("HandleExecSelectable", event, params, num_params);
59890bd37d32Smrg
59900bd37d32Smrg	if (*num_params == 2) {
59910bd37d32Smrg	    CELL start, finish;
59920bd37d32Smrg	    char *data;
59930bd37d32Smrg	    char **argv;
59940bd37d32Smrg
5995913cc679Smrg	    data = getDataFromScreen(xw, event, params[1], &start, &finish);
59960bd37d32Smrg	    if (data != 0) {
59970bd37d32Smrg		if ((argv = tokenizeFormat(params[0])) != 0) {
59982e4f8982Smrg		    char *blob = argv[0];
59992e4f8982Smrg		    int argc;
60002e4f8982Smrg
60010bd37d32Smrg		    for (argc = 0; argv[argc] != 0; ++argc) {
60020bd37d32Smrg			argv[argc] = expandFormat(xw, argv[argc], data,
60030bd37d32Smrg						  &start, &finish);
60040bd37d32Smrg		    }
60052e4f8982Smrg		    executeCommand(TScreenOf(xw)->pid, argv);
60060bd37d32Smrg		    freeArgv(blob, argv);
60070bd37d32Smrg		}
6008894e0ac8Smrg		free(data);
60090bd37d32Smrg	    }
60100bd37d32Smrg	}
60110bd37d32Smrg    }
60120bd37d32Smrg}
60130bd37d32Smrg
601401037d57Smrgstatic void
601501037d57SmrgreallyInsertFormatted(Widget w, char *format, char *data, CELL *start, CELL *finish)
601601037d57Smrg{
601701037d57Smrg    XtermWidget xw;
601801037d57Smrg
601901037d57Smrg    if ((xw = getXtermWidget(w)) != 0) {
602001037d57Smrg	char *exps;
602101037d57Smrg
602201037d57Smrg	if ((exps = expandFormat(xw, format, data, start, finish)) != 0) {
602301037d57Smrg	    unparseputs(xw, exps);
602401037d57Smrg	    unparse_end(xw);
602501037d57Smrg	    free(exps);
602601037d57Smrg	}
602701037d57Smrg    }
602801037d57Smrg}
602901037d57Smrg
60300bd37d32Smrgvoid
60310bd37d32SmrgHandleInsertFormatted(Widget w,
603201037d57Smrg		      XEvent *event,
6033e0a2b6dfSmrg		      String *params,	/* selections */
60340bd37d32Smrg		      Cardinal *num_params)
60350bd37d32Smrg{
60360bd37d32Smrg    XtermWidget xw;
60370bd37d32Smrg
6038f2e35a3aSmrg    TRACE_EVENT("HandleInsertFormatted", event, params, num_params);
603901037d57Smrg    if ((xw = getXtermWidget(w)) != 0 &&
604001037d57Smrg	(*num_params > 1)) {
604101037d57Smrg	doSelectionFormat(xw, w, event, params, num_params, reallyInsertFormatted);
60420bd37d32Smrg    }
60430bd37d32Smrg}
60440bd37d32Smrg
60450bd37d32Smrgvoid
60460bd37d32SmrgHandleInsertSelectable(Widget w,
6047913cc679Smrg		       XEvent *event,
6048e0a2b6dfSmrg		       String *params,	/* selections */
60490bd37d32Smrg		       Cardinal *num_params)
60500bd37d32Smrg{
60510bd37d32Smrg    XtermWidget xw;
60520bd37d32Smrg
60530bd37d32Smrg    if ((xw = getXtermWidget(w)) != 0) {
6054f2e35a3aSmrg	TRACE_EVENT("HandleInsertSelectable", event, params, num_params);
60550bd37d32Smrg
60560bd37d32Smrg	if (*num_params == 2) {
60570bd37d32Smrg	    CELL start, finish;
60580bd37d32Smrg	    char *data;
60590bd37d32Smrg	    char *temp = x_strdup(params[0]);
60600bd37d32Smrg
6061913cc679Smrg	    data = getDataFromScreen(xw, event, params[1], &start, &finish);
60620bd37d32Smrg	    if (data != 0) {
60632e4f8982Smrg		char *exps = expandFormat(xw, temp, data, &start, &finish);
60640bd37d32Smrg		if (exps != 0) {
60650bd37d32Smrg		    unparseputs(xw, exps);
606601037d57Smrg		    unparse_end(xw);
60670bd37d32Smrg		    free(exps);
60680bd37d32Smrg		}
60690bd37d32Smrg		free(data);
60700bd37d32Smrg	    }
60710bd37d32Smrg	    free(temp);
60720bd37d32Smrg	}
60730bd37d32Smrg    }
60740bd37d32Smrg}
60750bd37d32Smrg#endif /* OPT_SELECTION_OPS */
6076