button.c revision 2e4f8982
12e4f8982Smrg/* $XTermId: button.c,v 1.505 2016/05/30 19:42:44 tom Exp $ */
2d522f475Smrg
3d522f475Smrg/*
42e4f8982Smrg * Copyright 1999-2015,2016 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
82d522f475Smrg#ifdef HAVE_PCREPOSIX_H
83d522f475Smrg#include <pcreposix.h>
84d522f475Smrg#else /* POSIX regex.h */
85d522f475Smrg#include <sys/types.h>
86d522f475Smrg#include <regex.h>
87d522f475Smrg#endif
88d522f475Smrg#endif
89d522f475Smrg
90d522f475Smrg#if OPT_WIDE_CHARS
91d522f475Smrg#include <ctype.h>
92d522f475Smrg#include <wcwidth.h>
93d522f475Smrg#else
94d522f475Smrg#define CharacterClass(value) \
95d522f475Smrg	charClass[value & ((sizeof(charClass)/sizeof(charClass[0]))-1)]
96d522f475Smrg#endif
97d522f475Smrg
98956cc18dSsnj    /*
99956cc18dSsnj     * We'll generally map rows to indices when doing selection.
100956cc18dSsnj     * Simplify that with a macro.
101956cc18dSsnj     *
102956cc18dSsnj     * Note that ROW2INX() is safe to use with auto increment/decrement for
103956cc18dSsnj     * the row expression since that is evaluated once.
104956cc18dSsnj     */
105956cc18dSsnj#define GET_LINEDATA(screen, row) \
106956cc18dSsnj	getLineData(screen, ROW2INX(screen, row))
107956cc18dSsnj
108956cc18dSsnj    /*
109956cc18dSsnj     * We reserve shift modifier for cut/paste operations.  In principle we
110956cc18dSsnj     * can pass through control and meta modifiers, but in practice, the
111956cc18dSsnj     * popup menu uses control, and the window manager is likely to use meta,
112956cc18dSsnj     * so those events are not delivered to SendMousePosition.
113956cc18dSsnj     */
114d522f475Smrg#define OurModifiers (ShiftMask | ControlMask | Mod1Mask)
115d522f475Smrg#define AllModifiers (ShiftMask | LockMask | ControlMask | Mod1Mask | \
116d522f475Smrg		      Mod2Mask | Mod3Mask | Mod4Mask | Mod5Mask)
117d522f475Smrg
118492d43a5Smrg#define BtnModifiers(event) (event->state & OurModifiers)
119492d43a5Smrg#define KeyModifiers(event) (event->xbutton.state & OurModifiers)
120492d43a5Smrg
121492d43a5Smrg#define IsBtnEvent(event) ((event)->type == ButtonPress || (event)->type == ButtonRelease)
1220bd37d32Smrg#define IsKeyEvent(event) ((event)->type == KeyPress    || (event)->type == KeyRelease)
123d522f475Smrg
1242eaa94a1Schristos#define KeyState(x) (((int) ((x) & (ShiftMask|ControlMask))) \
1252eaa94a1Schristos			  + (((x) & Mod1Mask) ? 2 : 0))
126d522f475Smrg    /* adds together the bits:
127d522f475Smrg       shift key -> 1
128d522f475Smrg       meta key  -> 2
129d522f475Smrg       control key -> 4 */
130d522f475Smrg
131d522f475Smrg#define	Coordinate(s,c)	((c)->row * MaxCols(s) + (c)->col)
132d522f475Smrg
133d522f475Smrgstatic const CELL zeroCELL =
134d522f475Smrg{0, 0};
135d522f475Smrg
136d522f475Smrg#if OPT_DEC_LOCATOR
137894e0ac8Smrgstatic Bool SendLocatorPosition(XtermWidget xw, XButtonEvent *event);
138894e0ac8Smrgstatic void CheckLocatorPosition(XtermWidget xw, XButtonEvent *event);
139d522f475Smrg#endif /* OPT_DEC_LOCATOR */
140d522f475Smrg
141d522f475Smrg/* Multi-click handling */
142d522f475Smrg#if OPT_READLINE
143d522f475Smrgstatic Time lastButtonDownTime = 0;
144d522f475Smrgstatic int ExtendingSelection = 0;
145d522f475Smrgstatic Time lastButton3UpTime = 0;
146d522f475Smrgstatic Time lastButton3DoubleDownTime = 0;
147d522f475Smrgstatic CELL lastButton3;	/* At the release time */
148d522f475Smrg#endif /* OPT_READLINE */
149d522f475Smrg
150e0a2b6dfSmrgstatic Char *SaveText(TScreen *screen, int row, int scol, int ecol,
151e0a2b6dfSmrg		      Char *lp, int *eol);
152e0a2b6dfSmrgstatic int Length(TScreen *screen, int row, int scol, int ecol);
153e0a2b6dfSmrgstatic void ComputeSelect(XtermWidget xw, CELL *startc, CELL *endc, Bool extend);
154894e0ac8Smrgstatic void EditorButton(XtermWidget xw, XButtonEvent *event);
155894e0ac8Smrgstatic void EndExtend(XtermWidget w, XEvent *event, String *params, Cardinal
156d522f475Smrg		      num_params, Bool use_cursor_loc);
157e0a2b6dfSmrgstatic void ExtendExtend(XtermWidget xw, const CELL *cell);
158e0a2b6dfSmrgstatic void PointToCELL(TScreen *screen, int y, int x, CELL *cell);
159e0a2b6dfSmrgstatic void ReHiliteText(XtermWidget xw, CELL *first, CELL *last);
160e0a2b6dfSmrgstatic void SaltTextAway(XtermWidget xw, CELL *cellc, CELL *cell);
161894e0ac8Smrgstatic void SelectSet(XtermWidget xw, XEvent *event, String *params, Cardinal num_params);
162d522f475Smrgstatic void SelectionReceived PROTO_XT_SEL_CB_ARGS;
163e0a2b6dfSmrgstatic void StartSelect(XtermWidget xw, const CELL *cell);
164894e0ac8Smrgstatic void TrackDown(XtermWidget xw, XButtonEvent *event);
165e0a2b6dfSmrgstatic void TrackText(XtermWidget xw, const CELL *first, const CELL *last);
166e0a2b6dfSmrgstatic void _OwnSelection(XtermWidget xw, String *selections, Cardinal count);
167894e0ac8Smrgstatic void do_select_end(XtermWidget xw, XEvent *event, String *params,
168d522f475Smrg			  Cardinal *num_params, Bool use_cursor_loc);
169d522f475Smrg
170492d43a5Smrg#define MOUSE_LIMIT (255 - 32)
171d522f475Smrg
172492d43a5Smrg/* Send SET_EXT_SIZE_MOUSE to enable offsets up to EXT_MOUSE_LIMIT */
173492d43a5Smrg#define EXT_MOUSE_LIMIT (2047 - 32)
174492d43a5Smrg#define EXT_MOUSE_START (127 - 32)
175d522f475Smrg
1760bd37d32Smrgstatic int
177e0a2b6dfSmrgMouseLimit(TScreen *screen)
1780bd37d32Smrg{
1790bd37d32Smrg    int mouse_limit;
1800bd37d32Smrg
1810bd37d32Smrg    switch (screen->extend_coords) {
1820bd37d32Smrg    default:
1830bd37d32Smrg	mouse_limit = MOUSE_LIMIT;
1840bd37d32Smrg	break;
1850bd37d32Smrg    case SET_EXT_MODE_MOUSE:
1860bd37d32Smrg	mouse_limit = EXT_MOUSE_LIMIT;
1870bd37d32Smrg	break;
1880bd37d32Smrg    case SET_SGR_EXT_MODE_MOUSE:
1890bd37d32Smrg    case SET_URXVT_EXT_MODE_MOUSE:
1900bd37d32Smrg	mouse_limit = -1;
1910bd37d32Smrg	break;
1920bd37d32Smrg    }
1930bd37d32Smrg    return mouse_limit;
1940bd37d32Smrg}
1950bd37d32Smrg
196492d43a5Smrgstatic unsigned
197e0a2b6dfSmrgEmitMousePosition(TScreen *screen, Char line[], unsigned count, int value)
198492d43a5Smrg{
1990bd37d32Smrg    int mouse_limit = MouseLimit(screen);
200492d43a5Smrg
201492d43a5Smrg    /*
202492d43a5Smrg     * Add pointer position to key sequence
203492d43a5Smrg     *
204492d43a5Smrg     * In extended mode we encode large positions as two-byte UTF-8.
205492d43a5Smrg     *
206492d43a5Smrg     * NOTE: historically, it was possible to emit 256, which became
207492d43a5Smrg     * zero by truncation to 8 bits. While this was arguably a bug,
208492d43a5Smrg     * it's also somewhat useful as a past-end marker. We preserve
209492d43a5Smrg     * this behavior for both normal and extended mouse modes.
210492d43a5Smrg     */
2110bd37d32Smrg    switch (screen->extend_coords) {
2120bd37d32Smrg    default:
2130bd37d32Smrg	if (value == mouse_limit) {
2140bd37d32Smrg	    line[count++] = CharOf(0);
2150bd37d32Smrg	} else {
2160bd37d32Smrg	    line[count++] = CharOf(' ' + value + 1);
2170bd37d32Smrg	}
2180bd37d32Smrg	break;
2190bd37d32Smrg    case SET_EXT_MODE_MOUSE:
2200bd37d32Smrg	if (value == mouse_limit) {
2210bd37d32Smrg	    line[count++] = CharOf(0);
2220bd37d32Smrg	} else if (value < EXT_MOUSE_START) {
2230bd37d32Smrg	    line[count++] = CharOf(' ' + value + 1);
2240bd37d32Smrg	} else {
2250bd37d32Smrg	    value += ' ' + 1;
2260bd37d32Smrg	    line[count++] = CharOf(0xC0 + (value >> 6));
2270bd37d32Smrg	    line[count++] = CharOf(0x80 + (value & 0x3F));
2280bd37d32Smrg	}
2290bd37d32Smrg	break;
2300bd37d32Smrg    case SET_SGR_EXT_MODE_MOUSE:
2310bd37d32Smrg	/* FALLTHRU */
2320bd37d32Smrg    case SET_URXVT_EXT_MODE_MOUSE:
2330bd37d32Smrg	count += (unsigned) sprintf((char *) line + count, "%d", value + 1);
2340bd37d32Smrg	break;
2350bd37d32Smrg    }
2360bd37d32Smrg    return count;
2370bd37d32Smrg}
2380bd37d32Smrg
2390bd37d32Smrgstatic unsigned
240e0a2b6dfSmrgEmitMousePositionSeparator(TScreen *screen, Char line[], unsigned count)
2410bd37d32Smrg{
2420bd37d32Smrg    switch (screen->extend_coords) {
2430bd37d32Smrg    case SET_SGR_EXT_MODE_MOUSE:
2440bd37d32Smrg    case SET_URXVT_EXT_MODE_MOUSE:
2450bd37d32Smrg	line[count++] = ';';
2460bd37d32Smrg	break;
247d522f475Smrg    }
248492d43a5Smrg    return count;
249492d43a5Smrg}
250d522f475Smrg
251492d43a5SmrgBool
252894e0ac8SmrgSendMousePosition(XtermWidget xw, XEvent *event)
253492d43a5Smrg{
254492d43a5Smrg    TScreen *screen = TScreenOf(xw);
255492d43a5Smrg    XButtonEvent *my_event = (XButtonEvent *) event;
256492d43a5Smrg    Bool result = False;
257d522f475Smrg
258d522f475Smrg    switch (screen->send_mouse_pos) {
259492d43a5Smrg    case MOUSE_OFF:
260492d43a5Smrg	/* If send_mouse_pos mode isn't on, we shouldn't be here */
261492d43a5Smrg	break;
262d522f475Smrg
263d522f475Smrg    case BTN_EVENT_MOUSE:
264d522f475Smrg    case ANY_EVENT_MOUSE:
265492d43a5Smrg	if (KeyModifiers(event) == 0 || KeyModifiers(event) == ControlMask) {
266492d43a5Smrg	    /* xterm extension for motion reporting. June 1998 */
267492d43a5Smrg	    /* EditorButton() will distinguish between the modes */
268492d43a5Smrg	    switch (event->type) {
269492d43a5Smrg	    case MotionNotify:
270492d43a5Smrg		my_event->button = 0;
271492d43a5Smrg		/* FALLTHRU */
272492d43a5Smrg	    case ButtonPress:
273492d43a5Smrg		/* FALLTHRU */
274492d43a5Smrg	    case ButtonRelease:
275492d43a5Smrg		EditorButton(xw, my_event);
276492d43a5Smrg		result = True;
277492d43a5Smrg		break;
2782eaa94a1Schristos	    }
279d522f475Smrg	}
280492d43a5Smrg	break;
281d522f475Smrg
282d522f475Smrg    default:
283492d43a5Smrg	/* Make sure the event is an appropriate type */
284492d43a5Smrg	if (IsBtnEvent(event)) {
285492d43a5Smrg	    switch (screen->send_mouse_pos) {
286492d43a5Smrg	    case X10_MOUSE:	/* X10 compatibility sequences */
287492d43a5Smrg
288492d43a5Smrg		if (BtnModifiers(my_event) == 0) {
289492d43a5Smrg		    if (my_event->type == ButtonPress)
290492d43a5Smrg			EditorButton(xw, my_event);
291492d43a5Smrg		    result = True;
292492d43a5Smrg		}
293492d43a5Smrg		break;
294492d43a5Smrg
295492d43a5Smrg	    case VT200_HIGHLIGHT_MOUSE:	/* DEC vt200 hilite tracking */
296492d43a5Smrg		if (my_event->type == ButtonPress &&
297492d43a5Smrg		    BtnModifiers(my_event) == 0 &&
298492d43a5Smrg		    my_event->button == Button1) {
299492d43a5Smrg		    TrackDown(xw, my_event);
300492d43a5Smrg		    result = True;
301492d43a5Smrg		} else if (BtnModifiers(my_event) == 0
302492d43a5Smrg			   || BtnModifiers(my_event) == ControlMask) {
303492d43a5Smrg		    EditorButton(xw, my_event);
304492d43a5Smrg		    result = True;
305492d43a5Smrg		}
306492d43a5Smrg		break;
307492d43a5Smrg
308492d43a5Smrg	    case VT200_MOUSE:	/* DEC vt200 compatible */
309492d43a5Smrg		if (BtnModifiers(my_event) == 0
310492d43a5Smrg		    || BtnModifiers(my_event) == ControlMask) {
311492d43a5Smrg		    EditorButton(xw, my_event);
312492d43a5Smrg		    result = True;
313492d43a5Smrg		}
314492d43a5Smrg		break;
315492d43a5Smrg
316492d43a5Smrg#if OPT_DEC_LOCATOR
317492d43a5Smrg	    case DEC_LOCATOR:
318492d43a5Smrg		result = SendLocatorPosition(xw, my_event);
319492d43a5Smrg		break;
320492d43a5Smrg#endif /* OPT_DEC_LOCATOR */
321492d43a5Smrg	    }
322492d43a5Smrg	}
323d522f475Smrg    }
324492d43a5Smrg    return result;
325d522f475Smrg}
326d522f475Smrg
327d522f475Smrg#if OPT_DEC_LOCATOR
328d522f475Smrg
329d522f475Smrg#define	LocatorCoords( row, col, x, y, oor )			\
330d522f475Smrg    if( screen->locator_pixels ) {				\
331d522f475Smrg	(oor)=False; (row) = (y)+1; (col) = (x)+1;		\
332d522f475Smrg	/* Limit to screen dimensions */			\
333d522f475Smrg	if ((row) < 1) (row) = 1,(oor)=True;			\
334d522f475Smrg	else if ((row) > screen->border*2+Height(screen))	\
335d522f475Smrg	    (row) = screen->border*2+Height(screen),(oor)=True;	\
336d522f475Smrg	if ((col) < 1) (col) = 1,(oor)=True;			\
337d522f475Smrg	else if ((col) > OriginX(screen)*2+Width(screen))	\
338d522f475Smrg	    (col) = OriginX(screen)*2+Width(screen),(oor)=True;	\
339d522f475Smrg    } else {							\
340d522f475Smrg	(oor)=False;						\
341d522f475Smrg	/* Compute character position of mouse pointer */	\
342d522f475Smrg	(row) = ((y) - screen->border) / FontHeight(screen);	\
343d522f475Smrg	(col) = ((x) - OriginX(screen)) / FontWidth(screen);	\
344d522f475Smrg	/* Limit to screen dimensions */			\
345d522f475Smrg	if ((row) < 0) (row) = 0,(oor)=True;			\
346d522f475Smrg	else if ((row) > screen->max_row)			\
347d522f475Smrg	    (row) = screen->max_row,(oor)=True;			\
348d522f475Smrg	if ((col) < 0) (col) = 0,(oor)=True;			\
349d522f475Smrg	else if ((col) > screen->max_col)			\
350d522f475Smrg	    (col) = screen->max_col,(oor)=True;			\
351d522f475Smrg	(row)++; (col)++;					\
352d522f475Smrg    }
353d522f475Smrg
354d522f475Smrgstatic Bool
355894e0ac8SmrgSendLocatorPosition(XtermWidget xw, XButtonEvent *event)
356d522f475Smrg{
357d522f475Smrg    ANSI reply;
358956cc18dSsnj    TScreen *screen = TScreenOf(xw);
359d522f475Smrg    int row, col;
360d522f475Smrg    Bool oor;
361d522f475Smrg    int button;
3622eaa94a1Schristos    unsigned state;
363d522f475Smrg
364d522f475Smrg    /* Make sure the event is an appropriate type */
365492d43a5Smrg    if ((!IsBtnEvent(event) &&
366d522f475Smrg	 !screen->loc_filter) ||
367492d43a5Smrg	(BtnModifiers(event) != 0 && BtnModifiers(event) != ControlMask))
368d522f475Smrg	return (False);
369d522f475Smrg
370d522f475Smrg    if ((event->type == ButtonPress &&
371d522f475Smrg	 !(screen->locator_events & LOC_BTNS_DN)) ||
372d522f475Smrg	(event->type == ButtonRelease &&
373d522f475Smrg	 !(screen->locator_events & LOC_BTNS_UP)))
374d522f475Smrg	return (True);
375d522f475Smrg
376d522f475Smrg    if (event->type == MotionNotify) {
377d522f475Smrg	CheckLocatorPosition(xw, event);
378d522f475Smrg	return (True);
379d522f475Smrg    }
380d522f475Smrg
381d522f475Smrg    /* get button # */
382492d43a5Smrg    button = (int) event->button - 1;
383d522f475Smrg
384492d43a5Smrg    LocatorCoords(row, col, event->x, event->y, oor);
385d522f475Smrg
386d522f475Smrg    /*
387d522f475Smrg     * DECterm mouse:
388d522f475Smrg     *
389d522f475Smrg     * ESCAPE '[' event ; mask ; row ; column '&' 'w'
390d522f475Smrg     */
391d522f475Smrg    memset(&reply, 0, sizeof(reply));
392d522f475Smrg    reply.a_type = ANSI_CSI;
393d522f475Smrg
394d522f475Smrg    if (oor) {
395d522f475Smrg	reply.a_nparam = 1;
396d522f475Smrg	reply.a_param[0] = 0;	/* Event - 0 = locator unavailable */
397d522f475Smrg	reply.a_inters = '&';
398d522f475Smrg	reply.a_final = 'w';
399d522f475Smrg	unparseseq(xw, &reply);
400d522f475Smrg
401d522f475Smrg	if (screen->locator_reset) {
402d522f475Smrg	    MotionOff(screen, xw);
403d522f475Smrg	    screen->send_mouse_pos = MOUSE_OFF;
404d522f475Smrg	}
405d522f475Smrg	return (True);
406d522f475Smrg    }
407d522f475Smrg
408d522f475Smrg    /*
409d522f475Smrg     * event:
410d522f475Smrg     *        1       no buttons
411d522f475Smrg     *        2       left button down
412d522f475Smrg     *        3       left button up
413d522f475Smrg     *        4       middle button down
414d522f475Smrg     *        5       middle button up
415d522f475Smrg     *        6       right button down
416d522f475Smrg     *        7       right button up
417d522f475Smrg     *        8       M4 down
418d522f475Smrg     *        9       M4 up
419d522f475Smrg     */
420d522f475Smrg    reply.a_nparam = 4;
421d522f475Smrg    switch (event->type) {
422d522f475Smrg    case ButtonPress:
4232eaa94a1Schristos	reply.a_param[0] = (ParmType) (2 + (button << 1));
424d522f475Smrg	break;
425d522f475Smrg    case ButtonRelease:
4262eaa94a1Schristos	reply.a_param[0] = (ParmType) (3 + (button << 1));
427d522f475Smrg	break;
428d522f475Smrg    default:
429d522f475Smrg	return (True);
430d522f475Smrg    }
431d522f475Smrg    /*
432d522f475Smrg     * mask:
433d522f475Smrg     * bit 7   bit 6   bit 5   bit 4   bit 3   bit 2       bit 1         bit 0
434d522f475Smrg     *                                 M4 down left down   middle down   right down
435d522f475Smrg     *
436d522f475Smrg     * Notice that Button1 (left) and Button3 (right) are swapped in the mask.
437d522f475Smrg     * Also, mask should be the state after the button press/release,
438d522f475Smrg     * X provides the state not including the button press/release.
439d522f475Smrg     */
440492d43a5Smrg    state = (event->state
441d522f475Smrg	     & (Button1Mask | Button2Mask | Button3Mask | Button4Mask)) >> 8;
4424e40088cSchristos    /* update mask to "after" state */
44320d2c4d2Smrg    state ^= ((unsigned) (1 << button));
4444e40088cSchristos    /* swap Button1 & Button3 */
445956cc18dSsnj    state = ((state & (unsigned) ~(4 | 1))
446956cc18dSsnj	     | ((state & 1) ? 4 : 0)
447956cc18dSsnj	     | ((state & 4) ? 1 : 0));
448d522f475Smrg
4492eaa94a1Schristos    reply.a_param[1] = (ParmType) state;
4502eaa94a1Schristos    reply.a_param[2] = (ParmType) row;
4512eaa94a1Schristos    reply.a_param[3] = (ParmType) col;
452d522f475Smrg    reply.a_inters = '&';
453d522f475Smrg    reply.a_final = 'w';
454d522f475Smrg
455d522f475Smrg    unparseseq(xw, &reply);
456d522f475Smrg
457d522f475Smrg    if (screen->locator_reset) {
458d522f475Smrg	MotionOff(screen, xw);
459d522f475Smrg	screen->send_mouse_pos = MOUSE_OFF;
460d522f475Smrg    }
461d522f475Smrg
462d522f475Smrg    /*
463d522f475Smrg     * DECterm turns the Locator off if a button is pressed while a filter rectangle
464d522f475Smrg     * is active. This might be a bug, but I don't know, so I'll emulate it anyways.
465d522f475Smrg     */
466d522f475Smrg    if (screen->loc_filter) {
467d522f475Smrg	screen->send_mouse_pos = MOUSE_OFF;
468d522f475Smrg	screen->loc_filter = False;
469d522f475Smrg	screen->locator_events = 0;
470d522f475Smrg	MotionOff(screen, xw);
471d522f475Smrg    }
472d522f475Smrg
473d522f475Smrg    return (True);
474d522f475Smrg}
475d522f475Smrg
476d522f475Smrg/*
477d522f475Smrg * mask:
478d522f475Smrg * bit 7   bit 6   bit 5   bit 4   bit 3   bit 2       bit 1         bit 0
479d522f475Smrg *                                 M4 down left down   middle down   right down
480d522f475Smrg *
481d522f475Smrg * Button1 (left) and Button3 (right) are swapped in the mask relative to X.
482d522f475Smrg */
483d522f475Smrg#define	ButtonState(state, mask)	\
4842eaa94a1Schristos{ (state) = (int) (((mask) & (Button1Mask | Button2Mask | Button3Mask | Button4Mask)) >> 8);	\
485d522f475Smrg  /* swap Button1 & Button3 */								\
486d522f475Smrg  (state) = ((state) & ~(4|1)) | (((state)&1)?4:0) | (((state)&4)?1:0);			\
487d522f475Smrg}
488d522f475Smrg
489d522f475Smrgvoid
490d522f475SmrgGetLocatorPosition(XtermWidget xw)
491d522f475Smrg{
492d522f475Smrg    ANSI reply;
493956cc18dSsnj    TScreen *screen = TScreenOf(xw);
494d522f475Smrg    Window root, child;
495d522f475Smrg    int rx, ry, x, y;
496d522f475Smrg    unsigned int mask;
497d522f475Smrg    int row = 0, col = 0;
498d522f475Smrg    Bool oor = False;
499d522f475Smrg    Bool ret = False;
500d522f475Smrg    int state;
501d522f475Smrg
502d522f475Smrg    /*
503d522f475Smrg     * DECterm turns the Locator off if the position is requested while a filter rectangle
504d522f475Smrg     * is active.  This might be a bug, but I don't know, so I'll emulate it anyways.
505d522f475Smrg     */
506d522f475Smrg    if (screen->loc_filter) {
507d522f475Smrg	screen->send_mouse_pos = MOUSE_OFF;
508d522f475Smrg	screen->loc_filter = False;
509d522f475Smrg	screen->locator_events = 0;
510d522f475Smrg	MotionOff(screen, xw);
511d522f475Smrg    }
512d522f475Smrg
513d522f475Smrg    memset(&reply, 0, sizeof(reply));
514d522f475Smrg    reply.a_type = ANSI_CSI;
515d522f475Smrg
516d522f475Smrg    if (screen->send_mouse_pos == DEC_LOCATOR) {
517d522f475Smrg	ret = XQueryPointer(screen->display, VWindow(screen), &root,
518d522f475Smrg			    &child, &rx, &ry, &x, &y, &mask);
519d522f475Smrg	if (ret) {
520d522f475Smrg	    LocatorCoords(row, col, x, y, oor);
521d522f475Smrg	}
522d522f475Smrg    }
523d522f475Smrg    if (ret == False || oor) {
524d522f475Smrg	reply.a_nparam = 1;
525d522f475Smrg	reply.a_param[0] = 0;	/* Event - 0 = locator unavailable */
526d522f475Smrg	reply.a_inters = '&';
527d522f475Smrg	reply.a_final = 'w';
528d522f475Smrg	unparseseq(xw, &reply);
529d522f475Smrg
530d522f475Smrg	if (screen->locator_reset) {
531d522f475Smrg	    MotionOff(screen, xw);
532d522f475Smrg	    screen->send_mouse_pos = MOUSE_OFF;
533d522f475Smrg	}
534d522f475Smrg	return;
535d522f475Smrg    }
536d522f475Smrg
537d522f475Smrg    ButtonState(state, mask);
538d522f475Smrg
539d522f475Smrg    reply.a_nparam = 4;
540d522f475Smrg    reply.a_param[0] = 1;	/* Event - 1 = response to locator request */
5412eaa94a1Schristos    reply.a_param[1] = (ParmType) state;
5422eaa94a1Schristos    reply.a_param[2] = (ParmType) row;
5432eaa94a1Schristos    reply.a_param[3] = (ParmType) col;
544d522f475Smrg    reply.a_inters = '&';
545d522f475Smrg    reply.a_final = 'w';
546d522f475Smrg    unparseseq(xw, &reply);
547d522f475Smrg
548d522f475Smrg    if (screen->locator_reset) {
549d522f475Smrg	MotionOff(screen, xw);
550d522f475Smrg	screen->send_mouse_pos = MOUSE_OFF;
551d522f475Smrg    }
552d522f475Smrg}
553d522f475Smrg
554d522f475Smrgvoid
555d522f475SmrgInitLocatorFilter(XtermWidget xw)
556d522f475Smrg{
557d522f475Smrg    ANSI reply;
558956cc18dSsnj    TScreen *screen = TScreenOf(xw);
559d522f475Smrg    Window root, child;
560d522f475Smrg    int rx, ry, x, y;
561d522f475Smrg    unsigned int mask;
562d522f475Smrg    int row = 0, col = 0;
563d522f475Smrg    Bool oor = 0;
564d522f475Smrg    Bool ret;
565d522f475Smrg
566d522f475Smrg    ret = XQueryPointer(screen->display, VWindow(screen),
567d522f475Smrg			&root, &child, &rx, &ry, &x, &y, &mask);
568d522f475Smrg    if (ret) {
569d522f475Smrg	LocatorCoords(row, col, x, y, oor);
570d522f475Smrg    }
571d522f475Smrg    if (ret == False || oor) {
572d522f475Smrg	/* Locator is unavailable */
573d522f475Smrg
574d522f475Smrg	if (screen->loc_filter_top != LOC_FILTER_POS ||
575d522f475Smrg	    screen->loc_filter_left != LOC_FILTER_POS ||
576d522f475Smrg	    screen->loc_filter_bottom != LOC_FILTER_POS ||
577d522f475Smrg	    screen->loc_filter_right != LOC_FILTER_POS) {
578d522f475Smrg	    /*
579d522f475Smrg	     * If any explicit coordinates were received,
580d522f475Smrg	     * report immediately with no coordinates.
581d522f475Smrg	     */
582d522f475Smrg	    memset(&reply, 0, sizeof(reply));
583d522f475Smrg	    reply.a_type = ANSI_CSI;
584d522f475Smrg	    reply.a_nparam = 1;
585d522f475Smrg	    reply.a_param[0] = 0;	/* Event - 0 = locator unavailable */
586d522f475Smrg	    reply.a_inters = '&';
587d522f475Smrg	    reply.a_final = 'w';
588d522f475Smrg	    unparseseq(xw, &reply);
589d522f475Smrg
590d522f475Smrg	    if (screen->locator_reset) {
591d522f475Smrg		MotionOff(screen, xw);
592d522f475Smrg		screen->send_mouse_pos = MOUSE_OFF;
593d522f475Smrg	    }
594d522f475Smrg	} else {
595d522f475Smrg	    /*
596d522f475Smrg	     * No explicit coordinates were received, and the pointer is
597d522f475Smrg	     * unavailable.  Report when the pointer re-enters the window.
598d522f475Smrg	     */
599d522f475Smrg	    screen->loc_filter = True;
600d522f475Smrg	    MotionOn(screen, xw);
601d522f475Smrg	}
602d522f475Smrg	return;
603d522f475Smrg    }
604d522f475Smrg
605d522f475Smrg    /*
606d522f475Smrg     * Adjust rectangle coordinates:
607d522f475Smrg     *  1. Replace "LOC_FILTER_POS" with current coordinates
608d522f475Smrg     *  2. Limit coordinates to screen size
609d522f475Smrg     *  3. make sure top and left are less than bottom and right, resp.
610d522f475Smrg     */
611d522f475Smrg    if (screen->locator_pixels) {
612d522f475Smrg	rx = OriginX(screen) * 2 + Width(screen);
613d522f475Smrg	ry = screen->border * 2 + Height(screen);
614d522f475Smrg    } else {
615d522f475Smrg	rx = screen->max_col;
616d522f475Smrg	ry = screen->max_row;
617d522f475Smrg    }
618d522f475Smrg
619d522f475Smrg#define	Adjust( coord, def, max )				\
620d522f475Smrg	if( (coord) == LOC_FILTER_POS )	(coord) = (def);	\
621d522f475Smrg	else if ((coord) < 1)		(coord) = 1;		\
622d522f475Smrg	else if ((coord) > (max))	(coord) = (max)
623d522f475Smrg
624d522f475Smrg    Adjust(screen->loc_filter_top, row, ry);
625d522f475Smrg    Adjust(screen->loc_filter_left, col, rx);
626d522f475Smrg    Adjust(screen->loc_filter_bottom, row, ry);
627d522f475Smrg    Adjust(screen->loc_filter_right, col, rx);
628d522f475Smrg
629d522f475Smrg    if (screen->loc_filter_top > screen->loc_filter_bottom) {
630d522f475Smrg	ry = screen->loc_filter_top;
631d522f475Smrg	screen->loc_filter_top = screen->loc_filter_bottom;
632d522f475Smrg	screen->loc_filter_bottom = ry;
633d522f475Smrg    }
634d522f475Smrg
635d522f475Smrg    if (screen->loc_filter_left > screen->loc_filter_right) {
636d522f475Smrg	rx = screen->loc_filter_left;
637d522f475Smrg	screen->loc_filter_left = screen->loc_filter_right;
638d522f475Smrg	screen->loc_filter_right = rx;
639d522f475Smrg    }
640d522f475Smrg
641d522f475Smrg    if ((col < screen->loc_filter_left) ||
642d522f475Smrg	(col > screen->loc_filter_right) ||
643d522f475Smrg	(row < screen->loc_filter_top) ||
644d522f475Smrg	(row > screen->loc_filter_bottom)) {
6452e4f8982Smrg	int state;
6462e4f8982Smrg
647d522f475Smrg	/* Pointer is already outside the rectangle - report immediately */
648d522f475Smrg	ButtonState(state, mask);
649d522f475Smrg
650d522f475Smrg	memset(&reply, 0, sizeof(reply));
651d522f475Smrg	reply.a_type = ANSI_CSI;
652d522f475Smrg	reply.a_nparam = 4;
653d522f475Smrg	reply.a_param[0] = 10;	/* Event - 10 = locator outside filter */
6542eaa94a1Schristos	reply.a_param[1] = (ParmType) state;
6552eaa94a1Schristos	reply.a_param[2] = (ParmType) row;
6562eaa94a1Schristos	reply.a_param[3] = (ParmType) col;
657d522f475Smrg	reply.a_inters = '&';
658d522f475Smrg	reply.a_final = 'w';
659d522f475Smrg	unparseseq(xw, &reply);
660d522f475Smrg
661d522f475Smrg	if (screen->locator_reset) {
662d522f475Smrg	    MotionOff(screen, xw);
663d522f475Smrg	    screen->send_mouse_pos = MOUSE_OFF;
664d522f475Smrg	}
665d522f475Smrg	return;
666d522f475Smrg    }
667d522f475Smrg
668d522f475Smrg    /*
669d522f475Smrg     * Rectangle is set up.  Allow pointer tracking
670d522f475Smrg     * to detect if the mouse leaves the rectangle.
671d522f475Smrg     */
672d522f475Smrg    screen->loc_filter = True;
673d522f475Smrg    MotionOn(screen, xw);
674d522f475Smrg}
675d522f475Smrg
676d522f475Smrgstatic void
677894e0ac8SmrgCheckLocatorPosition(XtermWidget xw, XButtonEvent *event)
678d522f475Smrg{
679d522f475Smrg    ANSI reply;
680956cc18dSsnj    TScreen *screen = TScreenOf(xw);
681d522f475Smrg    int row, col;
682d522f475Smrg    Bool oor;
683d522f475Smrg
684492d43a5Smrg    LocatorCoords(row, col, event->x, event->y, oor);
685d522f475Smrg
686d522f475Smrg    /*
687d522f475Smrg     * Send report if the pointer left the filter rectangle, if
688d522f475Smrg     * the pointer left the window, or if the filter rectangle
689d522f475Smrg     * had no coordinates and the pointer re-entered the window.
690d522f475Smrg     */
691d522f475Smrg    if (oor || (screen->loc_filter_top == LOC_FILTER_POS) ||
692d522f475Smrg	(col < screen->loc_filter_left) ||
693d522f475Smrg	(col > screen->loc_filter_right) ||
694d522f475Smrg	(row < screen->loc_filter_top) ||
695d522f475Smrg	(row > screen->loc_filter_bottom)) {
696d522f475Smrg	/* Filter triggered - disable it */
697d522f475Smrg	screen->loc_filter = False;
698d522f475Smrg	MotionOff(screen, xw);
699d522f475Smrg
700d522f475Smrg	memset(&reply, 0, sizeof(reply));
701d522f475Smrg	reply.a_type = ANSI_CSI;
702d522f475Smrg	if (oor) {
703d522f475Smrg	    reply.a_nparam = 1;
704d522f475Smrg	    reply.a_param[0] = 0;	/* Event - 0 = locator unavailable */
705d522f475Smrg	} else {
7062e4f8982Smrg	    int state;
7072e4f8982Smrg
708492d43a5Smrg	    ButtonState(state, event->state);
709d522f475Smrg
710d522f475Smrg	    reply.a_nparam = 4;
711d522f475Smrg	    reply.a_param[0] = 10;	/* Event - 10 = locator outside filter */
7122eaa94a1Schristos	    reply.a_param[1] = (ParmType) state;
7132eaa94a1Schristos	    reply.a_param[2] = (ParmType) row;
7142eaa94a1Schristos	    reply.a_param[3] = (ParmType) col;
715d522f475Smrg	}
716d522f475Smrg
717d522f475Smrg	reply.a_inters = '&';
718d522f475Smrg	reply.a_final = 'w';
719d522f475Smrg	unparseseq(xw, &reply);
720d522f475Smrg
721d522f475Smrg	if (screen->locator_reset) {
722d522f475Smrg	    MotionOff(screen, xw);
723d522f475Smrg	    screen->send_mouse_pos = MOUSE_OFF;
724d522f475Smrg	}
725d522f475Smrg    }
726d522f475Smrg}
727d522f475Smrg#endif /* OPT_DEC_LOCATOR */
728d522f475Smrg
729d522f475Smrg#if OPT_READLINE
730d522f475Smrgstatic int
731894e0ac8SmrgisClick1_clean(TScreen *screen, XButtonEvent *event)
732d522f475Smrg{
733d522f475Smrg    int delta;
734d522f475Smrg
735492d43a5Smrg    if (!IsBtnEvent(event)
736d522f475Smrg    /* Disable on Shift-Click-1, including the application-mouse modes */
737492d43a5Smrg	|| (BtnModifiers(event) & ShiftMask)
738d522f475Smrg	|| (screen->send_mouse_pos != MOUSE_OFF)	/* Kinda duplicate... */
739d522f475Smrg	||ExtendingSelection)	/* Was moved */
740d522f475Smrg	return 0;
741d522f475Smrg
742d522f475Smrg    if (event->type != ButtonRelease)
743d522f475Smrg	return 0;
744d522f475Smrg
745d522f475Smrg    if (lastButtonDownTime == (Time) 0) {
746d522f475Smrg	/* first time or once in a blue moon */
747d522f475Smrg	delta = screen->multiClickTime + 1;
748492d43a5Smrg    } else if (event->time > lastButtonDownTime) {
749d522f475Smrg	/* most of the time */
750492d43a5Smrg	delta = (int) (event->time - lastButtonDownTime);
751d522f475Smrg    } else {
752d522f475Smrg	/* time has rolled over since lastButtonUpTime */
753492d43a5Smrg	delta = (int) ((((Time) ~ 0) - lastButtonDownTime) + event->time);
754d522f475Smrg    }
755d522f475Smrg
756d522f475Smrg    return delta <= screen->multiClickTime;
757d522f475Smrg}
758d522f475Smrg
759d522f475Smrgstatic int
760894e0ac8SmrgisDoubleClick3(TScreen *screen, XButtonEvent *event)
761d522f475Smrg{
762d522f475Smrg    int delta;
763d522f475Smrg
764d522f475Smrg    if (event->type != ButtonRelease
765492d43a5Smrg	|| (BtnModifiers(event) & ShiftMask)
766492d43a5Smrg	|| event->button != Button3) {
767d522f475Smrg	lastButton3UpTime = 0;	/* Disable the cached info */
768d522f475Smrg	return 0;
769d522f475Smrg    }
770d522f475Smrg    /* Process Btn3Release. */
771d522f475Smrg    if (lastButton3DoubleDownTime == (Time) 0) {
772d522f475Smrg	/* No previous click or once in a blue moon */
773d522f475Smrg	delta = screen->multiClickTime + 1;
774492d43a5Smrg    } else if (event->time > lastButton3DoubleDownTime) {
775d522f475Smrg	/* most of the time */
776492d43a5Smrg	delta = (int) (event->time - lastButton3DoubleDownTime);
777d522f475Smrg    } else {
778d522f475Smrg	/* time has rolled over since lastButton3DoubleDownTime */
779492d43a5Smrg	delta = (int) ((((Time) ~ 0) - lastButton3DoubleDownTime) + event->time);
780d522f475Smrg    }
781d522f475Smrg    if (delta <= screen->multiClickTime) {
782d522f475Smrg	/* Double click */
783d522f475Smrg	CELL cell;
784d522f475Smrg
785d522f475Smrg	/* Cannot check ExtendingSelection, since mouse-3 always sets it */
786492d43a5Smrg	PointToCELL(screen, event->y, event->x, &cell);
787d522f475Smrg	if (isSameCELL(&cell, &lastButton3)) {
788d522f475Smrg	    lastButton3DoubleDownTime = 0;	/* Disable the third click */
789d522f475Smrg	    return 1;
790d522f475Smrg	}
791d522f475Smrg    }
792d522f475Smrg    /* Not a double click, memorize for future check. */
793492d43a5Smrg    lastButton3UpTime = event->time;
794492d43a5Smrg    PointToCELL(screen, event->y, event->x, &lastButton3);
795d522f475Smrg    return 0;
796d522f475Smrg}
797d522f475Smrg
798d522f475Smrgstatic int
799894e0ac8SmrgCheckSecondPress3(TScreen *screen, XEvent *event)
800d522f475Smrg{
801d522f475Smrg    int delta;
802d522f475Smrg
803d522f475Smrg    if (event->type != ButtonPress
804492d43a5Smrg	|| (KeyModifiers(event) & ShiftMask)
805d522f475Smrg	|| event->xbutton.button != Button3) {
806d522f475Smrg	lastButton3DoubleDownTime = 0;	/* Disable the cached info */
807d522f475Smrg	return 0;
808d522f475Smrg    }
809d522f475Smrg    /* Process Btn3Press. */
810d522f475Smrg    if (lastButton3UpTime == (Time) 0) {
811d522f475Smrg	/* No previous click or once in a blue moon */
812d522f475Smrg	delta = screen->multiClickTime + 1;
813d522f475Smrg    } else if (event->xbutton.time > lastButton3UpTime) {
814d522f475Smrg	/* most of the time */
8152eaa94a1Schristos	delta = (int) (event->xbutton.time - lastButton3UpTime);
816d522f475Smrg    } else {
817d522f475Smrg	/* time has rolled over since lastButton3UpTime */
8182eaa94a1Schristos	delta = (int) ((((Time) ~ 0) - lastButton3UpTime) + event->xbutton.time);
819d522f475Smrg    }
820d522f475Smrg    if (delta <= screen->multiClickTime) {
821d522f475Smrg	CELL cell;
822d522f475Smrg
823d522f475Smrg	PointToCELL(screen, event->xbutton.y, event->xbutton.x, &cell);
824d522f475Smrg	if (isSameCELL(&cell, &lastButton3)) {
825d522f475Smrg	    /* A candidate for a double-click */
826d522f475Smrg	    lastButton3DoubleDownTime = event->xbutton.time;
827d522f475Smrg	    PointToCELL(screen, event->xbutton.y, event->xbutton.x, &lastButton3);
828d522f475Smrg	    return 1;
829d522f475Smrg	}
830d522f475Smrg	lastButton3UpTime = 0;	/* Disable the info about the previous click */
831d522f475Smrg    }
832d522f475Smrg    /* Either too long, or moved, disable. */
833d522f475Smrg    lastButton3DoubleDownTime = 0;
834d522f475Smrg    return 0;
835d522f475Smrg}
836d522f475Smrg
837d522f475Smrgstatic int
838e0a2b6dfSmrgrowOnCurrentLine(TScreen *screen,
839d522f475Smrg		 int line,
840d522f475Smrg		 int *deltap)	/* must be XButtonEvent */
841d522f475Smrg{
842956cc18dSsnj    int result = 1;
843d522f475Smrg
844d522f475Smrg    *deltap = 0;
8452e4f8982Smrg
846956cc18dSsnj    if (line != screen->cur_row) {
8472e4f8982Smrg	int l1, l2;
8482e4f8982Smrg
849956cc18dSsnj	if (line < screen->cur_row)
850956cc18dSsnj	    l1 = line, l2 = screen->cur_row;
851956cc18dSsnj	else
852956cc18dSsnj	    l2 = line, l1 = screen->cur_row;
853956cc18dSsnj	l1--;
854956cc18dSsnj	while (++l1 < l2) {
855956cc18dSsnj	    LineData *ld = GET_LINEDATA(screen, l1);
856956cc18dSsnj	    if (!LineTstWrapped(ld)) {
857956cc18dSsnj		result = 0;
858956cc18dSsnj		break;
859956cc18dSsnj	    }
860956cc18dSsnj	}
861956cc18dSsnj	if (result) {
862956cc18dSsnj	    /* Everything is on one "wrapped line" now */
863956cc18dSsnj	    *deltap = line - screen->cur_row;
864956cc18dSsnj	}
865956cc18dSsnj    }
866956cc18dSsnj    return result;
867d522f475Smrg}
868d522f475Smrg
869d522f475Smrgstatic int
870894e0ac8SmrgeventRow(TScreen *screen, XEvent *event)	/* must be XButtonEvent */
871d522f475Smrg{
872d522f475Smrg    return (event->xbutton.y - screen->border) / FontHeight(screen);
873d522f475Smrg}
874d522f475Smrg
875d522f475Smrgstatic int
876894e0ac8SmrgeventColBetween(TScreen *screen, XEvent *event)		/* must be XButtonEvent */
877d522f475Smrg{
878d522f475Smrg    /* Correct by half a width - we are acting on a boundary, not on a cell. */
879d522f475Smrg    return ((event->xbutton.x - OriginX(screen) + (FontWidth(screen) - 1) / 2)
880d522f475Smrg	    / FontWidth(screen));
881d522f475Smrg}
882d522f475Smrg
883d522f475Smrgstatic int
884e0a2b6dfSmrgReadLineMovePoint(TScreen *screen, int col, int ldelta)
885d522f475Smrg{
886d522f475Smrg    Char line[6];
887d522f475Smrg    unsigned count = 0;
888d522f475Smrg
889d522f475Smrg    col += ldelta * MaxCols(screen) - screen->cur_col;
890d522f475Smrg    if (col == 0)
891d522f475Smrg	return 0;
892d522f475Smrg    if (screen->control_eight_bits) {
893d522f475Smrg	line[count++] = ANSI_CSI;
894d522f475Smrg    } else {
895d522f475Smrg	line[count++] = ANSI_ESC;
896d522f475Smrg	line[count++] = '[';	/* XXX maybe sometimes O is better? */
897d522f475Smrg    }
89820d2c4d2Smrg    line[count] = CharOf(col > 0 ? 'C' : 'D');
899d522f475Smrg    if (col < 0)
900d522f475Smrg	col = -col;
901d522f475Smrg    while (col--)
902d522f475Smrg	v_write(screen->respond, line, 3);
903d522f475Smrg    return 1;
904d522f475Smrg}
905d522f475Smrg
906d522f475Smrgstatic int
907e0a2b6dfSmrgReadLineDelete(TScreen *screen, CELL *cell1, CELL *cell2)
908d522f475Smrg{
909d522f475Smrg    int del;
910d522f475Smrg
911d522f475Smrg    del = (cell2->col - cell1->col) + ((cell2->row - cell1->row) * MaxCols(screen));
912d522f475Smrg    if (del <= 0)		/* Just in case... */
913d522f475Smrg	return 0;
914d522f475Smrg    while (del--)
915492d43a5Smrg	v_write(screen->respond, (const Char *) "\177", 1);
916d522f475Smrg    return 1;
917d522f475Smrg}
918492d43a5Smrg
919492d43a5Smrgstatic void
920894e0ac8SmrgreadlineExtend(TScreen *screen, XEvent *event)
921492d43a5Smrg{
922492d43a5Smrg    int ldelta1, ldelta2;
923492d43a5Smrg
924492d43a5Smrg    if (IsBtnEvent(event)) {
925492d43a5Smrg	XButtonEvent *my_event = (XButtonEvent *) event;
926492d43a5Smrg	if (isClick1_clean(screen, my_event)
927492d43a5Smrg	    && SCREEN_FLAG(screen, click1_moves)
928492d43a5Smrg	    && rowOnCurrentLine(screen, eventRow(screen, event), &ldelta1)) {
929492d43a5Smrg	    ReadLineMovePoint(screen, eventColBetween(screen, event), ldelta1);
930492d43a5Smrg	}
931492d43a5Smrg	if (isDoubleClick3(screen, my_event)
932492d43a5Smrg	    && SCREEN_FLAG(screen, dclick3_deletes)
933492d43a5Smrg	    && rowOnCurrentLine(screen, screen->startSel.row, &ldelta1)
934492d43a5Smrg	    && rowOnCurrentLine(screen, screen->endSel.row, &ldelta2)) {
935492d43a5Smrg	    ReadLineMovePoint(screen, screen->endSel.col, ldelta2);
936492d43a5Smrg	    ReadLineDelete(screen, &screen->startSel, &(screen->endSel));
937492d43a5Smrg	}
938492d43a5Smrg    }
939492d43a5Smrg}
940492d43a5Smrg
941d522f475Smrg#endif /* OPT_READLINE */
942d522f475Smrg
943d522f475Smrg/* ^XM-G<line+' '><col+' '> */
944d522f475Smrgvoid
945d522f475SmrgDiredButton(Widget w,
946894e0ac8Smrg	    XEvent *event,	/* must be XButtonEvent */
947e0a2b6dfSmrg	    String *params GCC_UNUSED,	/* selections */
948d522f475Smrg	    Cardinal *num_params GCC_UNUSED)
949d522f475Smrg{
950956cc18dSsnj    XtermWidget xw;
951956cc18dSsnj
952956cc18dSsnj    if ((xw = getXtermWidget(w)) != 0) {
953956cc18dSsnj	TScreen *screen = TScreenOf(xw);
954d522f475Smrg
955492d43a5Smrg	if (IsBtnEvent(event)
9562eaa94a1Schristos	    && (event->xbutton.y >= screen->border)
9572eaa94a1Schristos	    && (event->xbutton.x >= OriginX(screen))) {
9582e4f8982Smrg	    Char Line[6];
9592e4f8982Smrg	    unsigned line, col;
9602e4f8982Smrg
96120d2c4d2Smrg	    line = (unsigned) ((event->xbutton.y - screen->border)
96220d2c4d2Smrg			       / FontHeight(screen));
96320d2c4d2Smrg	    col = (unsigned) ((event->xbutton.x - OriginX(screen))
96420d2c4d2Smrg			      / FontWidth(screen));
965d522f475Smrg	    Line[0] = CONTROL('X');
966d522f475Smrg	    Line[1] = ANSI_ESC;
967d522f475Smrg	    Line[2] = 'G';
9682eaa94a1Schristos	    Line[3] = CharOf(' ' + col);
9692eaa94a1Schristos	    Line[4] = CharOf(' ' + line);
970d522f475Smrg	    v_write(screen->respond, Line, 5);
971d522f475Smrg	}
972d522f475Smrg    }
973d522f475Smrg}
974d522f475Smrg
975d522f475Smrg#if OPT_READLINE
976d522f475Smrgvoid
977d522f475SmrgReadLineButton(Widget w,
978894e0ac8Smrg	       XEvent *event,	/* must be XButtonEvent */
979e0a2b6dfSmrg	       String *params GCC_UNUSED,	/* selections */
980d522f475Smrg	       Cardinal *num_params GCC_UNUSED)
981d522f475Smrg{
982956cc18dSsnj    XtermWidget xw;
983956cc18dSsnj
984956cc18dSsnj    if ((xw = getXtermWidget(w)) != 0) {
985956cc18dSsnj	TScreen *screen = TScreenOf(xw);
986d522f475Smrg	Char Line[6];
987d522f475Smrg	int line, col, ldelta = 0;
988d522f475Smrg
989492d43a5Smrg	if (!IsBtnEvent(event)
990d522f475Smrg	    || (screen->send_mouse_pos != MOUSE_OFF) || ExtendingSelection)
991d522f475Smrg	    goto finish;
992d522f475Smrg	if (event->type == ButtonRelease) {
993d522f475Smrg	    int delta;
994d522f475Smrg
995d522f475Smrg	    if (lastButtonDownTime == (Time) 0) {
996d522f475Smrg		/* first time and once in a blue moon */
997d522f475Smrg		delta = screen->multiClickTime + 1;
998d522f475Smrg	    } else if (event->xbutton.time > lastButtonDownTime) {
999d522f475Smrg		/* most of the time */
10002eaa94a1Schristos		delta = (int) (event->xbutton.time - lastButtonDownTime);
1001d522f475Smrg	    } else {
1002d522f475Smrg		/* time has rolled over since lastButtonUpTime */
10032eaa94a1Schristos		delta = (int) ((((Time) ~ 0) - lastButtonDownTime) + event->xbutton.time);
1004d522f475Smrg	    }
1005d522f475Smrg	    if (delta > screen->multiClickTime)
1006d522f475Smrg		goto finish;	/* All this work for this... */
1007d522f475Smrg	}
1008d522f475Smrg	line = (event->xbutton.y - screen->border) / FontHeight(screen);
1009956cc18dSsnj	if (!rowOnCurrentLine(screen, line, &ldelta))
1010956cc18dSsnj	    goto finish;
1011d522f475Smrg	/* Correct by half a width - we are acting on a boundary, not on a cell. */
1012d522f475Smrg	col = (event->xbutton.x - OriginX(screen) + (FontWidth(screen) - 1)
1013d522f475Smrg	       / 2)
1014d522f475Smrg	    / FontWidth(screen) - screen->cur_col + ldelta * MaxCols(screen);
1015d522f475Smrg	if (col == 0)
1016d522f475Smrg	    goto finish;
1017d522f475Smrg	Line[0] = ANSI_ESC;
1018d522f475Smrg	/* XXX: sometimes it is better to send '['? */
1019d522f475Smrg	Line[1] = 'O';
10202eaa94a1Schristos	Line[2] = CharOf(col > 0 ? 'C' : 'D');
1021d522f475Smrg	if (col < 0)
1022d522f475Smrg	    col = -col;
1023d522f475Smrg	while (col--)
1024d522f475Smrg	    v_write(screen->respond, Line, 3);
1025d522f475Smrg      finish:
1026d522f475Smrg	if (event->type == ButtonRelease)
1027d522f475Smrg	    do_select_end(xw, event, params, num_params, False);
1028d522f475Smrg    }
1029d522f475Smrg}
1030d522f475Smrg#endif /* OPT_READLINE */
1031d522f475Smrg
1032d522f475Smrg/* repeats <ESC>n or <ESC>p */
1033d522f475Smrgvoid
1034d522f475SmrgViButton(Widget w,
1035894e0ac8Smrg	 XEvent *event,		/* must be XButtonEvent */
1036e0a2b6dfSmrg	 String *params GCC_UNUSED,	/* selections */
1037d522f475Smrg	 Cardinal *num_params GCC_UNUSED)
1038d522f475Smrg{
1039956cc18dSsnj    XtermWidget xw;
1040956cc18dSsnj
1041956cc18dSsnj    if ((xw = getXtermWidget(w)) != 0) {
1042956cc18dSsnj	TScreen *screen = TScreenOf(xw);
1043d522f475Smrg	int pty = screen->respond;
1044d522f475Smrg
1045492d43a5Smrg	if (IsBtnEvent(event)) {
10462e4f8982Smrg	    int line;
1047d522f475Smrg
1048d522f475Smrg	    line = screen->cur_row -
1049d522f475Smrg		((event->xbutton.y - screen->border) / FontHeight(screen));
10502e4f8982Smrg
1051d522f475Smrg	    if (line != 0) {
10522e4f8982Smrg		Char Line[6];
10532e4f8982Smrg
1054d522f475Smrg		Line[0] = ANSI_ESC;	/* force an exit from insert-mode */
1055d522f475Smrg		v_write(pty, Line, 1);
1056d522f475Smrg
1057d522f475Smrg		if (line < 0) {
1058d522f475Smrg		    line = -line;
1059d522f475Smrg		    Line[0] = CONTROL('n');
1060d522f475Smrg		} else {
1061d522f475Smrg		    Line[0] = CONTROL('p');
1062d522f475Smrg		}
1063d522f475Smrg		while (--line >= 0)
1064d522f475Smrg		    v_write(pty, Line, 1);
1065d522f475Smrg	    }
1066d522f475Smrg	}
1067d522f475Smrg    }
1068d522f475Smrg}
1069d522f475Smrg
1070d522f475Smrg/*
1071d522f475Smrg * This function handles button-motion events
1072d522f475Smrg */
1073d522f475Smrg/*ARGSUSED*/
1074d522f475Smrgvoid
1075d522f475SmrgHandleSelectExtend(Widget w,
1076894e0ac8Smrg		   XEvent *event,	/* must be XMotionEvent */
1077e0a2b6dfSmrg		   String *params GCC_UNUSED,
1078d522f475Smrg		   Cardinal *num_params GCC_UNUSED)
1079d522f475Smrg{
1080956cc18dSsnj    XtermWidget xw;
1081956cc18dSsnj
1082956cc18dSsnj    if ((xw = getXtermWidget(w)) != 0) {
1083956cc18dSsnj	TScreen *screen = TScreenOf(xw);
1084d522f475Smrg	CELL cell;
1085d522f475Smrg
1086e0a2b6dfSmrg	TRACE(("HandleSelectExtend @%ld\n", event->xmotion.time));
10870bd37d32Smrg
1088d522f475Smrg	screen->selection_time = event->xmotion.time;
1089d522f475Smrg	switch (screen->eventMode) {
1090d522f475Smrg	    /* If not in one of the DEC mouse-reporting modes */
1091d522f475Smrg	case LEFTEXTENSION:
1092d522f475Smrg	case RIGHTEXTENSION:
1093d522f475Smrg	    PointToCELL(screen, event->xmotion.y, event->xmotion.x, &cell);
1094d522f475Smrg	    ExtendExtend(xw, &cell);
1095d522f475Smrg	    break;
1096d522f475Smrg
1097d522f475Smrg	    /* If in motion reporting mode, send mouse position to
1098d522f475Smrg	       character process as a key sequence \E[M... */
1099d522f475Smrg	case NORMAL:
1100d522f475Smrg	    /* will get here if send_mouse_pos != MOUSE_OFF */
1101d522f475Smrg	    if (screen->send_mouse_pos == BTN_EVENT_MOUSE
1102d522f475Smrg		|| screen->send_mouse_pos == ANY_EVENT_MOUSE) {
1103d522f475Smrg		(void) SendMousePosition(xw, event);
1104d522f475Smrg	    }
1105d522f475Smrg	    break;
1106d522f475Smrg	}
1107d522f475Smrg    }
1108d522f475Smrg}
1109d522f475Smrg
1110d522f475Smrgvoid
1111d522f475SmrgHandleKeyboardSelectExtend(Widget w,
1112894e0ac8Smrg			   XEvent *event GCC_UNUSED,	/* must be XButtonEvent */
1113e0a2b6dfSmrg			   String *params GCC_UNUSED,
1114d522f475Smrg			   Cardinal *num_params GCC_UNUSED)
1115d522f475Smrg{
1116956cc18dSsnj    XtermWidget xw;
1117956cc18dSsnj
1118956cc18dSsnj    if ((xw = getXtermWidget(w)) != 0) {
1119956cc18dSsnj	TScreen *screen = TScreenOf(xw);
11200bd37d32Smrg
11210bd37d32Smrg	TRACE(("HandleKeyboardSelectExtend\n"));
1122d522f475Smrg	ExtendExtend(xw, &screen->cursorp);
1123d522f475Smrg    }
1124d522f475Smrg}
1125d522f475Smrg
1126d522f475Smrgstatic void
1127d522f475Smrgdo_select_end(XtermWidget xw,
1128894e0ac8Smrg	      XEvent *event,	/* must be XButtonEvent */
1129e0a2b6dfSmrg	      String *params,	/* selections */
1130d522f475Smrg	      Cardinal *num_params,
1131d522f475Smrg	      Bool use_cursor_loc)
1132d522f475Smrg{
1133956cc18dSsnj    TScreen *screen = TScreenOf(xw);
1134d522f475Smrg
1135d522f475Smrg    screen->selection_time = event->xbutton.time;
1136e0a2b6dfSmrg    TRACE(("do_select_end @%ld\n", screen->selection_time));
1137d522f475Smrg    switch (screen->eventMode) {
1138d522f475Smrg    case NORMAL:
1139d522f475Smrg	(void) SendMousePosition(xw, event);
1140d522f475Smrg	break;
1141d522f475Smrg    case LEFTEXTENSION:
1142d522f475Smrg    case RIGHTEXTENSION:
1143d522f475Smrg	EndExtend(xw, event, params, *num_params, use_cursor_loc);
1144d522f475Smrg#if OPT_READLINE
1145492d43a5Smrg	readlineExtend(screen, event);
1146d522f475Smrg#endif /* OPT_READLINE */
1147d522f475Smrg	break;
1148d522f475Smrg    }
1149d522f475Smrg}
1150d522f475Smrg
1151d522f475Smrgvoid
1152d522f475SmrgHandleSelectEnd(Widget w,
1153894e0ac8Smrg		XEvent *event,	/* must be XButtonEvent */
1154e0a2b6dfSmrg		String *params,	/* selections */
1155d522f475Smrg		Cardinal *num_params)
1156d522f475Smrg{
1157956cc18dSsnj    XtermWidget xw;
1158956cc18dSsnj
1159956cc18dSsnj    if ((xw = getXtermWidget(w)) != 0) {
11600bd37d32Smrg	TRACE(("HandleSelectEnd\n"));
1161956cc18dSsnj	do_select_end(xw, event, params, num_params, False);
1162956cc18dSsnj    }
1163d522f475Smrg}
1164d522f475Smrg
1165d522f475Smrgvoid
1166d522f475SmrgHandleKeyboardSelectEnd(Widget w,
1167894e0ac8Smrg			XEvent *event,	/* must be XButtonEvent */
1168e0a2b6dfSmrg			String *params,		/* selections */
1169d522f475Smrg			Cardinal *num_params)
1170d522f475Smrg{
1171956cc18dSsnj    XtermWidget xw;
1172956cc18dSsnj
1173956cc18dSsnj    if ((xw = getXtermWidget(w)) != 0) {
11740bd37d32Smrg	TRACE(("HandleKeyboardSelectEnd\n"));
1175956cc18dSsnj	do_select_end(xw, event, params, num_params, True);
1176956cc18dSsnj    }
1177d522f475Smrg}
1178d522f475Smrg
1179492d43a5Smrg/*
11806879286fSmrg * Copy the selection data to the given target(s).
1181492d43a5Smrg */
1182492d43a5Smrgvoid
11836879286fSmrgHandleCopySelection(Widget w,
1184894e0ac8Smrg		    XEvent *event,
1185e0a2b6dfSmrg		    String *params,	/* list of targets */
11866879286fSmrg		    Cardinal *num_params)
1187492d43a5Smrg{
1188492d43a5Smrg    XtermWidget xw;
1189492d43a5Smrg
1190492d43a5Smrg    if ((xw = getXtermWidget(w)) != 0) {
11910bd37d32Smrg	TRACE(("HandleCopySelection\n"));
11926879286fSmrg	SelectSet(xw, event, params, *num_params);
1193492d43a5Smrg    }
1194492d43a5Smrg}
1195492d43a5Smrg
1196d522f475Smrgstruct _SelectionList {
1197d522f475Smrg    String *params;
1198d522f475Smrg    Cardinal count;
1199d522f475Smrg    Atom *targets;
1200d522f475Smrg    Time time;
1201d522f475Smrg};
1202d522f475Smrg
1203d522f475Smrgstatic unsigned
1204d522f475SmrgDECtoASCII(unsigned ch)
1205d522f475Smrg{
1206d522f475Smrg    if (xtermIsDecGraphic(ch)) {
12072eaa94a1Schristos	ch = CharOf("###########+++++##-##++++|######"[ch]);
12082eaa94a1Schristos	/*           01234567890123456789012345678901 */
1209d522f475Smrg    }
1210d522f475Smrg    return ch;
1211d522f475Smrg}
121220d2c4d2Smrg
121320d2c4d2Smrg#if OPT_WIDE_CHARS
121420d2c4d2Smrgstatic Cardinal
1215e0a2b6dfSmrgaddXtermChar(Char **buffer, Cardinal *used, Cardinal offset, unsigned value)
121620d2c4d2Smrg{
121720d2c4d2Smrg    if (offset + 1 >= *used) {
121820d2c4d2Smrg	*used = 1 + (2 * (offset + 1));
121920d2c4d2Smrg	allocXtermChars(buffer, *used);
122020d2c4d2Smrg    }
122120d2c4d2Smrg    (*buffer)[offset++] = (Char) value;
122220d2c4d2Smrg    return offset;
122320d2c4d2Smrg}
122420d2c4d2Smrg#define AddChar(buffer, used, offset, value) \
122520d2c4d2Smrg	offset = addXtermChar(buffer, used, offset, (unsigned) value)
122620d2c4d2Smrg
1227d522f475Smrg/*
1228d522f475Smrg * Convert a UTF-8 string to Latin-1, replacing non Latin-1 characters by `#',
1229d522f475Smrg * or ASCII/Latin-1 equivalents for special cases.
1230d522f475Smrg */
1231d522f475Smrgstatic Char *
1232e0a2b6dfSmrgUTF8toLatin1(TScreen *screen, Char *s, unsigned long len, unsigned long *result)
1233d522f475Smrg{
1234d522f475Smrg    static Char *buffer;
1235956cc18dSsnj    static Cardinal used;
1236d522f475Smrg
123720d2c4d2Smrg    Cardinal offset = 0;
1238d522f475Smrg
123920d2c4d2Smrg    if (len != 0) {
1240d522f475Smrg	PtyData data;
1241d522f475Smrg
1242d522f475Smrg	fakePtyData(&data, s, s + len);
1243894e0ac8Smrg	while (decodeUtf8(screen, &data)) {
1244956cc18dSsnj	    Bool fails = False;
1245956cc18dSsnj	    Bool extra = False;
1246d522f475Smrg	    IChar value = skipPtyData(&data);
1247d522f475Smrg	    if (value == UCS_REPL) {
1248956cc18dSsnj		fails = True;
1249d522f475Smrg	    } else if (value < 256) {
125020d2c4d2Smrg		AddChar(&buffer, &used, offset, CharOf(value));
1251d522f475Smrg	    } else {
1252d522f475Smrg		unsigned eqv = ucs2dec(value);
1253d522f475Smrg		if (xtermIsDecGraphic(eqv)) {
125420d2c4d2Smrg		    AddChar(&buffer, &used, offset, DECtoASCII(eqv));
1255d522f475Smrg		} else {
1256d522f475Smrg		    eqv = AsciiEquivs(value);
1257956cc18dSsnj		    if (eqv == value) {
1258956cc18dSsnj			fails = True;
1259956cc18dSsnj		    } else {
126020d2c4d2Smrg			AddChar(&buffer, &used, offset, eqv);
1261956cc18dSsnj		    }
1262956cc18dSsnj		    if (isWide((wchar_t) value))
1263956cc18dSsnj			extra = True;
1264956cc18dSsnj		}
1265956cc18dSsnj	    }
1266956cc18dSsnj
1267956cc18dSsnj	    /*
1268956cc18dSsnj	     * If we're not able to plug in a single-byte result, insert the
1269956cc18dSsnj	     * defaultString (which normally is a single "#", but could be
1270956cc18dSsnj	     * whatever the user wants).
1271956cc18dSsnj	     */
1272956cc18dSsnj	    if (fails) {
12732e4f8982Smrg		const Char *p;
12742e4f8982Smrg
1275492d43a5Smrg		for (p = (const Char *) screen->default_string; *p != '\0'; ++p) {
127620d2c4d2Smrg		    AddChar(&buffer, &used, offset, *p);
1277d522f475Smrg		}
1278d522f475Smrg	    }
1279956cc18dSsnj	    if (extra)
128020d2c4d2Smrg		AddChar(&buffer, &used, offset, ' ');
1281d522f475Smrg	}
128220d2c4d2Smrg	AddChar(&buffer, &used, offset, '\0');
128320d2c4d2Smrg	*result = (unsigned long) (offset - 1);
1284d522f475Smrg    } else {
1285d522f475Smrg	*result = 0;
1286d522f475Smrg    }
1287d522f475Smrg    return buffer;
1288d522f475Smrg}
128920d2c4d2Smrg
129020d2c4d2Smrgint
129120d2c4d2SmrgxtermUtf8ToTextList(XtermWidget xw,
129220d2c4d2Smrg		    XTextProperty * text_prop,
129320d2c4d2Smrg		    char ***text_list,
129420d2c4d2Smrg		    int *text_list_count)
129520d2c4d2Smrg{
129620d2c4d2Smrg    TScreen *screen = TScreenOf(xw);
129720d2c4d2Smrg    Display *dpy = screen->display;
129820d2c4d2Smrg    int rc = -1;
129920d2c4d2Smrg
130020d2c4d2Smrg    if (text_prop->format == 8
130120d2c4d2Smrg	&& (rc = Xutf8TextPropertyToTextList(dpy, text_prop,
130220d2c4d2Smrg					     text_list,
130320d2c4d2Smrg					     text_list_count)) >= 0) {
130420d2c4d2Smrg	if (*text_list != NULL && *text_list_count != 0) {
130520d2c4d2Smrg	    int i;
130620d2c4d2Smrg	    Char *data;
130720d2c4d2Smrg	    char **new_text_list, *tmp;
130820d2c4d2Smrg	    unsigned long size, new_size;
130920d2c4d2Smrg
131020d2c4d2Smrg	    TRACE(("xtermUtf8ToTextList size %d\n", *text_list_count));
131120d2c4d2Smrg
131220d2c4d2Smrg	    /*
131320d2c4d2Smrg	     * XLib StringList actually uses only two pointers, one for the
131420d2c4d2Smrg	     * list itself, and one for the data.  Pointer to the data is the
131520d2c4d2Smrg	     * first element of the list, the rest (if any) list elements point
131620d2c4d2Smrg	     * to the same memory block as the first element
131720d2c4d2Smrg	     */
131820d2c4d2Smrg	    new_size = 0;
131920d2c4d2Smrg	    for (i = 0; i < *text_list_count; ++i) {
132020d2c4d2Smrg		data = (Char *) (*text_list)[i];
132120d2c4d2Smrg		size = strlen((*text_list)[i]) + 1;
132220d2c4d2Smrg		(void) UTF8toLatin1(screen, data, size, &size);
132320d2c4d2Smrg		new_size += size + 1;
132420d2c4d2Smrg	    }
1325a1f3da82Smrg	    new_text_list = TypeXtMallocN(char *, *text_list_count);
132620d2c4d2Smrg	    new_text_list[0] = tmp = XtMalloc((Cardinal) new_size);
132720d2c4d2Smrg	    for (i = 0; i < (*text_list_count); ++i) {
132820d2c4d2Smrg		data = (Char *) (*text_list)[i];
132920d2c4d2Smrg		size = strlen((*text_list)[i]) + 1;
13300bd37d32Smrg		if ((data = UTF8toLatin1(screen, data, size, &size)) != 0) {
13310bd37d32Smrg		    memcpy(tmp, data, size + 1);
13320bd37d32Smrg		    new_text_list[i] = tmp;
13330bd37d32Smrg		    tmp += size + 1;
13340bd37d32Smrg		}
133520d2c4d2Smrg	    }
133620d2c4d2Smrg	    XFreeStringList((*text_list));
133720d2c4d2Smrg	    *text_list = new_text_list;
133820d2c4d2Smrg	} else {
133920d2c4d2Smrg	    rc = -1;
134020d2c4d2Smrg	}
134120d2c4d2Smrg    }
134220d2c4d2Smrg    return rc;
134320d2c4d2Smrg}
1344d522f475Smrg#endif /* OPT_WIDE_CHARS */
1345d522f475Smrg
1346956cc18dSsnjstatic char *
1347956cc18dSsnjparseItem(char *value, char *nextc)
1348d522f475Smrg{
1349956cc18dSsnj    char *nextp = value;
1350956cc18dSsnj    while (*nextp != '\0' && *nextp != ',') {
1351956cc18dSsnj	*nextp = x_toupper(*nextp);
1352956cc18dSsnj	++nextp;
1353956cc18dSsnj    }
1354956cc18dSsnj    *nextc = *nextp;
1355956cc18dSsnj    *nextp = '\0';
1356d522f475Smrg
1357956cc18dSsnj    return nextp;
1358956cc18dSsnj}
1359d522f475Smrg
1360956cc18dSsnj/*
1361956cc18dSsnj * All of the wanted strings are unique in the first character, so we can
1362956cc18dSsnj * use simple abbreviations.
1363956cc18dSsnj */
1364956cc18dSsnjstatic Bool
1365956cc18dSsnjsameItem(const char *actual, const char *wanted)
1366956cc18dSsnj{
1367956cc18dSsnj    Bool result = False;
1368956cc18dSsnj    size_t have = strlen(actual);
1369956cc18dSsnj    size_t need = strlen(wanted);
1370d522f475Smrg
1371956cc18dSsnj    if (have != 0 && have <= need) {
1372956cc18dSsnj	if (!strncmp(actual, wanted, have)) {
1373956cc18dSsnj	    TRACE(("...matched \"%s\"\n", wanted));
1374956cc18dSsnj	    result = True;
1375956cc18dSsnj	}
1376956cc18dSsnj    }
1377956cc18dSsnj
1378956cc18dSsnj    return result;
1379956cc18dSsnj}
1380956cc18dSsnj
1381956cc18dSsnj/*
1382956cc18dSsnj * Handle the eightBitSelectTypes or utf8SelectTypes resource values.
1383956cc18dSsnj */
1384956cc18dSsnjstatic Bool
1385894e0ac8SmrgoverrideTargets(Widget w, String value, Atom **resultp)
1386956cc18dSsnj{
1387956cc18dSsnj    Bool override = False;
1388956cc18dSsnj    XtermWidget xw;
1389956cc18dSsnj
1390956cc18dSsnj    if ((xw = getXtermWidget(w)) != 0) {
1391956cc18dSsnj	TScreen *screen = TScreenOf(xw);
1392956cc18dSsnj
139320d2c4d2Smrg	if (!IsEmpty(value)) {
1394492d43a5Smrg	    char *copied = x_strdup(value);
1395956cc18dSsnj	    if (copied != 0) {
1396956cc18dSsnj		Atom *result = 0;
1397956cc18dSsnj		Cardinal count = 1;
1398956cc18dSsnj		int n;
1399d522f475Smrg
1400956cc18dSsnj		TRACE(("decoding SelectTypes \"%s\"\n", value));
1401956cc18dSsnj		for (n = 0; copied[n] != '\0'; ++n) {
1402956cc18dSsnj		    if (copied[n] == ',')
1403956cc18dSsnj			++count;
1404956cc18dSsnj		}
1405a1f3da82Smrg		result = TypeXtMallocN(Atom, (2 * count) + 1);
1406956cc18dSsnj		if (result == NULL) {
1407956cc18dSsnj		    TRACE(("Couldn't allocate selection types\n"));
1408956cc18dSsnj		} else {
1409956cc18dSsnj		    char nextc = '?';
141020d2c4d2Smrg		    char *listp = (char *) copied;
1411956cc18dSsnj		    count = 0;
1412956cc18dSsnj		    do {
1413956cc18dSsnj			char *nextp = parseItem(listp, &nextc);
14140bd37d32Smrg			char *item = x_strtrim(listp);
14150bd37d32Smrg			size_t len = (item ? strlen(item) : 0);
1416956cc18dSsnj
1417956cc18dSsnj			if (len == 0) {
1418a1f3da82Smrg			    /* EMPTY */ ;
1419956cc18dSsnj			}
1420956cc18dSsnj#if OPT_WIDE_CHARS
14210bd37d32Smrg			else if (sameItem(item, "UTF8")) {
1422956cc18dSsnj			    result[count++] = XA_UTF8_STRING(XtDisplay(w));
1423956cc18dSsnj			}
1424956cc18dSsnj#endif
14250bd37d32Smrg			else if (sameItem(item, "I18N")) {
1426956cc18dSsnj			    if (screen->i18nSelections) {
1427956cc18dSsnj				result[count++] = XA_TEXT(XtDisplay(w));
1428956cc18dSsnj				result[count++] = XA_COMPOUND_TEXT(XtDisplay(w));
1429956cc18dSsnj			    }
14300bd37d32Smrg			} else if (sameItem(item, "TEXT")) {
1431956cc18dSsnj			    result[count++] = XA_TEXT(XtDisplay(w));
14320bd37d32Smrg			} else if (sameItem(item, "COMPOUND_TEXT")) {
1433956cc18dSsnj			    result[count++] = XA_COMPOUND_TEXT(XtDisplay(w));
14340bd37d32Smrg			} else if (sameItem(item, "STRING")) {
1435956cc18dSsnj			    result[count++] = XA_STRING;
1436956cc18dSsnj			}
1437956cc18dSsnj			*nextp++ = nextc;
1438956cc18dSsnj			listp = nextp;
14390bd37d32Smrg			free(item);
1440956cc18dSsnj		    } while (nextc != '\0');
1441956cc18dSsnj		    if (count) {
1442956cc18dSsnj			result[count] = None;
1443956cc18dSsnj			override = True;
1444956cc18dSsnj			*resultp = result;
1445956cc18dSsnj		    } else {
1446956cc18dSsnj			XtFree((char *) result);
1447956cc18dSsnj		    }
1448956cc18dSsnj		}
14490bd37d32Smrg		free(copied);
1450956cc18dSsnj	    } else {
1451956cc18dSsnj		TRACE(("Couldn't allocate copy of selection types\n"));
1452d522f475Smrg	    }
1453956cc18dSsnj	}
1454956cc18dSsnj    }
1455956cc18dSsnj    return override;
1456956cc18dSsnj}
1457956cc18dSsnj
1458956cc18dSsnj#if OPT_WIDE_CHARS
1459956cc18dSsnjstatic Atom *
1460e0a2b6dfSmrgallocUtf8Targets(Widget w, TScreen *screen)
1461956cc18dSsnj{
1462956cc18dSsnj    Atom **resultp = &(screen->selection_targets_utf8);
1463956cc18dSsnj
1464956cc18dSsnj    if (*resultp == 0) {
1465956cc18dSsnj	Atom *result;
1466956cc18dSsnj
1467956cc18dSsnj	if (!overrideTargets(w, screen->utf8_select_types, &result)) {
1468a1f3da82Smrg	    result = TypeXtMallocN(Atom, 5);
1469956cc18dSsnj	    if (result == NULL) {
1470956cc18dSsnj		TRACE(("Couldn't allocate utf-8 selection targets\n"));
1471956cc18dSsnj	    } else {
1472956cc18dSsnj		int n = 0;
1473956cc18dSsnj
1474e39b573cSmrg		if (XSupportsLocale()) {
1475e39b573cSmrg		    result[n++] = XA_UTF8_STRING(XtDisplay(w));
1476d522f475Smrg#ifdef X_HAVE_UTF8_STRING
1477e39b573cSmrg		    if (screen->i18nSelections) {
1478e39b573cSmrg			result[n++] = XA_TEXT(XtDisplay(w));
1479e39b573cSmrg			result[n++] = XA_COMPOUND_TEXT(XtDisplay(w));
1480e39b573cSmrg		    }
1481d522f475Smrg#endif
1482e39b573cSmrg		}
1483956cc18dSsnj		result[n++] = XA_STRING;
1484956cc18dSsnj		result[n] = None;
1485956cc18dSsnj	    }
1486d522f475Smrg	}
1487956cc18dSsnj
1488956cc18dSsnj	*resultp = result;
1489d522f475Smrg    }
1490956cc18dSsnj
1491956cc18dSsnj    return *resultp;
1492956cc18dSsnj}
1493d522f475Smrg#endif
1494d522f475Smrg
1495956cc18dSsnjstatic Atom *
1496e0a2b6dfSmrgalloc8bitTargets(Widget w, TScreen *screen)
1497956cc18dSsnj{
1498956cc18dSsnj    Atom **resultp = &(screen->selection_targets_8bit);
1499956cc18dSsnj
1500956cc18dSsnj    if (*resultp == 0) {
1501956cc18dSsnj	Atom *result = 0;
1502956cc18dSsnj
1503956cc18dSsnj	if (!overrideTargets(w, screen->eightbit_select_types, &result)) {
1504a1f3da82Smrg	    result = TypeXtMallocN(Atom, 5);
1505956cc18dSsnj	    if (result == NULL) {
1506956cc18dSsnj		TRACE(("Couldn't allocate 8bit selection targets\n"));
1507956cc18dSsnj	    } else {
1508956cc18dSsnj		int n = 0;
1509956cc18dSsnj
1510e39b573cSmrg		if (XSupportsLocale()) {
1511d522f475Smrg#ifdef X_HAVE_UTF8_STRING
1512e39b573cSmrg		    result[n++] = XA_UTF8_STRING(XtDisplay(w));
1513956cc18dSsnj#endif
1514e39b573cSmrg		    if (screen->i18nSelections) {
1515e39b573cSmrg			result[n++] = XA_TEXT(XtDisplay(w));
1516e39b573cSmrg			result[n++] = XA_COMPOUND_TEXT(XtDisplay(w));
1517e39b573cSmrg		    }
1518956cc18dSsnj		}
1519956cc18dSsnj		result[n++] = XA_STRING;
1520956cc18dSsnj		result[n] = None;
1521956cc18dSsnj	    }
1522956cc18dSsnj	}
1523956cc18dSsnj
1524956cc18dSsnj	*resultp = result;
1525956cc18dSsnj    }
1526956cc18dSsnj
1527956cc18dSsnj    return *resultp;
1528956cc18dSsnj}
1529956cc18dSsnj
1530956cc18dSsnjstatic Atom *
1531956cc18dSsnj_SelectionTargets(Widget w)
1532956cc18dSsnj{
1533956cc18dSsnj    Atom *result;
1534956cc18dSsnj    XtermWidget xw;
1535956cc18dSsnj
1536956cc18dSsnj    if ((xw = getXtermWidget(w)) == 0) {
1537956cc18dSsnj	result = NULL;
1538956cc18dSsnj    } else {
15392e4f8982Smrg	TScreen *screen = TScreenOf(xw);
1540956cc18dSsnj
1541956cc18dSsnj#if OPT_WIDE_CHARS
1542956cc18dSsnj	if (screen->wide_chars) {
1543956cc18dSsnj	    result = allocUtf8Targets(w, screen);
1544956cc18dSsnj	} else
1545d522f475Smrg#endif
1546956cc18dSsnj	{
1547956cc18dSsnj	    /* not screen->wide_chars */
1548956cc18dSsnj	    result = alloc8bitTargets(w, screen);
1549d522f475Smrg	}
1550d522f475Smrg    }
1551956cc18dSsnj
1552956cc18dSsnj    return result;
1553d522f475Smrg}
1554d522f475Smrg
1555d522f475Smrg#define isSELECT(value) (!strcmp(value, "SELECT"))
1556d522f475Smrg
1557d522f475Smrgstatic void
1558d522f475SmrgUnmapSelections(XtermWidget xw)
1559d522f475Smrg{
1560956cc18dSsnj    TScreen *screen = TScreenOf(xw);
1561d522f475Smrg    Cardinal n;
1562d522f475Smrg
1563d522f475Smrg    if (screen->mappedSelect) {
1564d522f475Smrg	for (n = 0; screen->mappedSelect[n] != 0; ++n)
156520d2c4d2Smrg	    free((void *) screen->mappedSelect[n]);
1566d522f475Smrg	free(screen->mappedSelect);
1567d522f475Smrg	screen->mappedSelect = 0;
1568d522f475Smrg    }
1569d522f475Smrg}
1570d522f475Smrg
1571d522f475Smrg/*
1572d522f475Smrg * xterm generally uses the primary selection.  Some applications prefer
1573d522f475Smrg * (or are limited to) the clipboard.  Since the translations resource is
1574d522f475Smrg * complicated, users seldom change the way it affects selection.  But it
1575d522f475Smrg * is simple to remap the choice between primary and clipboard before the
1576d522f475Smrg * call to XmuInternStrings().
1577d522f475Smrg */
1578d522f475Smrgstatic String *
1579e0a2b6dfSmrgMapSelections(XtermWidget xw, String *params, Cardinal num_params)
1580d522f475Smrg{
1581d522f475Smrg    String *result = params;
1582d522f475Smrg
1583d522f475Smrg    if (num_params > 0) {
1584d522f475Smrg	Cardinal j;
1585d522f475Smrg	Boolean map = False;
1586d522f475Smrg
1587d522f475Smrg	for (j = 0; j < num_params; ++j) {
1588d522f475Smrg	    TRACE(("param[%d]:%s\n", j, params[j]));
1589d522f475Smrg	    if (isSELECT(params[j])) {
1590d522f475Smrg		map = True;
1591d522f475Smrg		break;
1592d522f475Smrg	    }
1593d522f475Smrg	}
1594d522f475Smrg	if (map) {
1595956cc18dSsnj	    TScreen *screen = TScreenOf(xw);
1596956cc18dSsnj	    const char *mapTo = (screen->selectToClipboard
1597d522f475Smrg				 ? "CLIPBOARD"
1598d522f475Smrg				 : "PRIMARY");
1599d522f475Smrg
1600d522f475Smrg	    UnmapSelections(xw);
1601d522f475Smrg	    if ((result = TypeMallocN(String, num_params + 1)) != 0) {
1602d522f475Smrg		result[num_params] = 0;
1603d522f475Smrg		for (j = 0; j < num_params; ++j) {
1604d522f475Smrg		    result[j] = x_strdup((isSELECT(params[j])
1605d522f475Smrg					  ? mapTo
1606d522f475Smrg					  : params[j]));
1607d522f475Smrg		    if (result[j] == 0) {
1608d522f475Smrg			UnmapSelections(xw);
16090bd37d32Smrg			while (j != 0) {
16100bd37d32Smrg			    free((void *) result[--j]);
16110bd37d32Smrg			}
16120bd37d32Smrg			free(result);
1613d522f475Smrg			result = 0;
1614d522f475Smrg			break;
1615d522f475Smrg		    }
1616d522f475Smrg		}
1617956cc18dSsnj		screen->mappedSelect = result;
1618d522f475Smrg	    }
1619d522f475Smrg	}
1620d522f475Smrg    }
1621d522f475Smrg    return result;
1622d522f475Smrg}
1623d522f475Smrg
1624d522f475Smrg/*
1625d522f475Smrg * Lookup the cut-buffer number, which will be in the range 0-7.
1626d522f475Smrg * If it is not a cut-buffer, it is the primary selection (-1).
1627d522f475Smrg */
1628d522f475Smrgstatic int
162920d2c4d2SmrgCutBuffer(Atom code)
1630d522f475Smrg{
1631d522f475Smrg    int cutbuffer;
163220d2c4d2Smrg    switch ((unsigned) code) {
1633d522f475Smrg    case XA_CUT_BUFFER0:
1634d522f475Smrg	cutbuffer = 0;
1635d522f475Smrg	break;
1636d522f475Smrg    case XA_CUT_BUFFER1:
1637d522f475Smrg	cutbuffer = 1;
1638d522f475Smrg	break;
1639d522f475Smrg    case XA_CUT_BUFFER2:
1640d522f475Smrg	cutbuffer = 2;
1641d522f475Smrg	break;
1642d522f475Smrg    case XA_CUT_BUFFER3:
1643d522f475Smrg	cutbuffer = 3;
1644d522f475Smrg	break;
1645d522f475Smrg    case XA_CUT_BUFFER4:
1646d522f475Smrg	cutbuffer = 4;
1647d522f475Smrg	break;
1648d522f475Smrg    case XA_CUT_BUFFER5:
1649d522f475Smrg	cutbuffer = 5;
1650d522f475Smrg	break;
1651d522f475Smrg    case XA_CUT_BUFFER6:
1652d522f475Smrg	cutbuffer = 6;
1653d522f475Smrg	break;
1654d522f475Smrg    case XA_CUT_BUFFER7:
1655d522f475Smrg	cutbuffer = 7;
1656d522f475Smrg	break;
1657d522f475Smrg    default:
1658d522f475Smrg	cutbuffer = -1;
1659d522f475Smrg	break;
1660d522f475Smrg    }
16610bd37d32Smrg    TRACE(("CutBuffer(%d) = %d\n", (int) code, cutbuffer));
1662d522f475Smrg    return cutbuffer;
1663d522f475Smrg}
1664d522f475Smrg
1665d522f475Smrg#if OPT_PASTE64
1666d522f475Smrgstatic void
1667d522f475SmrgFinishPaste64(XtermWidget xw)
1668d522f475Smrg{
1669956cc18dSsnj    TScreen *screen = TScreenOf(xw);
1670956cc18dSsnj
1671956cc18dSsnj    TRACE(("FinishPaste64(%d)\n", screen->base64_paste));
1672956cc18dSsnj    if (screen->base64_paste) {
1673956cc18dSsnj	screen->base64_paste = 0;
1674956cc18dSsnj	unparseputc1(xw, screen->base64_final);
1675d522f475Smrg	unparse_end(xw);
1676d522f475Smrg    }
1677d522f475Smrg}
1678d522f475Smrg#endif
1679d522f475Smrg
1680d522f475Smrg#if !OPT_PASTE64
1681d522f475Smrgstatic
1682d522f475Smrg#endif
1683d522f475Smrgvoid
1684d522f475SmrgxtermGetSelection(Widget w,
1685d522f475Smrg		  Time ev_time,
1686e0a2b6dfSmrg		  String *params,	/* selections in precedence order */
1687d522f475Smrg		  Cardinal num_params,
1688894e0ac8Smrg		  Atom *targets)
1689d522f475Smrg{
1690d522f475Smrg    Atom selection;
1691d522f475Smrg    int cutbuffer;
1692d522f475Smrg    Atom target;
1693d522f475Smrg
1694956cc18dSsnj    XtermWidget xw;
1695956cc18dSsnj
169620d2c4d2Smrg    if (num_params == 0)
169720d2c4d2Smrg	return;
1698956cc18dSsnj    if ((xw = getXtermWidget(w)) == 0)
1699d522f475Smrg	return;
1700d522f475Smrg
1701e0a2b6dfSmrg    TRACE(("xtermGetSelection num_params %d @%ld\n", num_params, ev_time));
1702956cc18dSsnj    params = MapSelections(xw, params, num_params);
1703d522f475Smrg
1704d522f475Smrg    XmuInternStrings(XtDisplay(w), params, (Cardinal) 1, &selection);
1705d522f475Smrg    cutbuffer = CutBuffer(selection);
1706d522f475Smrg
1707956cc18dSsnj    TRACE(("Cutbuffer: %d, target: %s\n", cutbuffer,
1708956cc18dSsnj	   (targets
1709956cc18dSsnj	    ? visibleSelectionTarget(XtDisplay(w), targets[0])
1710956cc18dSsnj	    : "None")));
1711d522f475Smrg
1712d522f475Smrg    if (cutbuffer >= 0) {
1713d522f475Smrg	int inbytes;
1714d522f475Smrg	unsigned long nbytes;
1715d522f475Smrg	int fmt8 = 8;
1716d522f475Smrg	Atom type = XA_STRING;
1717d522f475Smrg	char *line;
1718d522f475Smrg
1719d522f475Smrg	/* 'line' is freed in SelectionReceived */
1720d522f475Smrg	line = XFetchBuffer(XtDisplay(w), &inbytes, cutbuffer);
1721d522f475Smrg	nbytes = (unsigned long) inbytes;
1722d522f475Smrg
17230bd37d32Smrg	if (nbytes > 0) {
1724d522f475Smrg	    SelectionReceived(w, NULL, &selection, &type, (XtPointer) line,
1725d522f475Smrg			      &nbytes, &fmt8);
17260bd37d32Smrg	} else if (num_params > 1) {
1727d522f475Smrg	    xtermGetSelection(w, ev_time, params + 1, num_params - 1, NULL);
1728d522f475Smrg	}
1729d522f475Smrg#if OPT_PASTE64
1730d522f475Smrg	else {
1731956cc18dSsnj	    FinishPaste64(xw);
1732d522f475Smrg	}
1733d522f475Smrg#endif
1734d522f475Smrg    } else {
1735d522f475Smrg
1736d522f475Smrg	if (targets == NULL || targets[0] == None) {
1737d522f475Smrg	    targets = _SelectionTargets(w);
1738d522f475Smrg	}
1739d522f475Smrg
1740d522f475Smrg	if (targets != 0) {
17412e4f8982Smrg	    struct _SelectionList *list;
17422e4f8982Smrg
1743d522f475Smrg	    target = targets[0];
1744d522f475Smrg
1745d522f475Smrg	    if (targets[1] == None) {	/* last target in list */
1746d522f475Smrg		params++;
1747d522f475Smrg		num_params--;
1748d522f475Smrg		targets = _SelectionTargets(w);
1749d522f475Smrg	    } else {
1750d522f475Smrg		targets = &(targets[1]);
1751d522f475Smrg	    }
1752d522f475Smrg
1753d522f475Smrg	    if (num_params) {
1754d522f475Smrg		/* 'list' is freed in SelectionReceived */
1755a1f3da82Smrg		list = TypeXtMalloc(struct _SelectionList);
1756d522f475Smrg		if (list != 0) {
1757d522f475Smrg		    list->params = params;
1758d522f475Smrg		    list->count = num_params;
1759d522f475Smrg		    list->targets = targets;
1760d522f475Smrg		    list->time = ev_time;
1761d522f475Smrg		}
1762d522f475Smrg	    } else {
1763d522f475Smrg		list = NULL;
1764d522f475Smrg	    }
1765d522f475Smrg
1766d522f475Smrg	    XtGetSelectionValue(w, selection,
1767d522f475Smrg				target,
1768d522f475Smrg				SelectionReceived,
1769d522f475Smrg				(XtPointer) list, ev_time);
1770d522f475Smrg	}
1771d522f475Smrg    }
1772d522f475Smrg}
1773d522f475Smrg
1774d522f475Smrg#if OPT_TRACE && OPT_WIDE_CHARS
1775d522f475Smrgstatic void
1776e0a2b6dfSmrgGettingSelection(Display *dpy, Atom type, Char *line, unsigned long len)
1777d522f475Smrg{
1778d522f475Smrg    Char *cp;
1779d522f475Smrg    char *name;
1780d522f475Smrg
1781d522f475Smrg    name = XGetAtomName(dpy, type);
1782d522f475Smrg
178301037d57Smrg    TRACE(("Getting %s (type=%ld, length=%ld)\n", name, (long int) type, len));
1784d522f475Smrg    for (cp = line; cp < line + len; cp++) {
1785956cc18dSsnj	TRACE(("[%d:%lu]", (int) (cp + 1 - line), len));
1786d522f475Smrg	if (isprint(*cp)) {
1787d522f475Smrg	    TRACE(("%c\n", *cp));
1788d522f475Smrg	} else {
1789d522f475Smrg	    TRACE(("\\x%02x\n", *cp));
1790d522f475Smrg	}
1791d522f475Smrg    }
1792d522f475Smrg}
1793d522f475Smrg#else
1794d522f475Smrg#define GettingSelection(dpy,type,line,len)	/* nothing */
1795d522f475Smrg#endif
1796d522f475Smrg
1797d522f475Smrg#ifdef VMS
1798d522f475Smrg#  define tty_vwrite(pty,lag,l)		tt_write(lag,l)
1799d522f475Smrg#else /* !( VMS ) */
1800d522f475Smrg#  define tty_vwrite(pty,lag,l)		v_write(pty,lag,l)
1801d522f475Smrg#endif /* defined VMS */
1802d522f475Smrg
1803d522f475Smrg#if OPT_PASTE64
1804d522f475Smrg/* Return base64 code character given 6-bit number */
1805d522f475Smrgstatic const char base64_code[] = "\
1806d522f475SmrgABCDEFGHIJKLMNOPQRSTUVWXYZ\
1807d522f475Smrgabcdefghijklmnopqrstuvwxyz\
1808d522f475Smrg0123456789+/";
1809d522f475Smrgstatic void
1810e0a2b6dfSmrgbase64_flush(TScreen *screen)
1811d522f475Smrg{
1812d522f475Smrg    Char x;
181301037d57Smrg
181401037d57Smrg    TRACE(("base64_flush count %d, pad %d (%d)\n",
181501037d57Smrg	   screen->base64_count,
181601037d57Smrg	   screen->base64_pad,
181701037d57Smrg	   screen->base64_pad & 3));
181801037d57Smrg
1819d522f475Smrg    switch (screen->base64_count) {
1820d522f475Smrg    case 0:
1821d522f475Smrg	break;
1822d522f475Smrg    case 2:
18232eaa94a1Schristos	x = CharOf(base64_code[screen->base64_accu << 4]);
1824d522f475Smrg	tty_vwrite(screen->respond, &x, 1);
1825d522f475Smrg	break;
1826d522f475Smrg    case 4:
18272eaa94a1Schristos	x = CharOf(base64_code[screen->base64_accu << 2]);
1828d522f475Smrg	tty_vwrite(screen->respond, &x, 1);
1829d522f475Smrg	break;
1830d522f475Smrg    }
183101037d57Smrg    if (screen->base64_pad & 3) {
1832d522f475Smrg	tty_vwrite(screen->respond,
1833492d43a5Smrg		   (const Char *) "===",
183401037d57Smrg		   (unsigned) (3 - (screen->base64_pad & 3)));
183501037d57Smrg    }
1836d522f475Smrg    screen->base64_count = 0;
1837d522f475Smrg    screen->base64_accu = 0;
1838d522f475Smrg    screen->base64_pad = 0;
1839d522f475Smrg}
1840d522f475Smrg#endif /* OPT_PASTE64 */
1841d522f475Smrg
1842e0a2b6dfSmrg/*
1843e0a2b6dfSmrg * Translate ISO-8859-1 or UTF-8 data to NRCS.
1844e0a2b6dfSmrg */
1845d522f475Smrgstatic void
1846e0a2b6dfSmrgToNational(TScreen *screen, Char *buffer, unsigned *length)
1847e0a2b6dfSmrg{
1848e0a2b6dfSmrg    int gsetL = screen->gsets[screen->curgl];
1849e0a2b6dfSmrg    int gsetR = screen->gsets[screen->curgr];
1850e0a2b6dfSmrg
1851e0a2b6dfSmrg#if OPT_WIDE_CHARS
1852e0a2b6dfSmrg    if ((screen->utf8_nrc_mode | screen->utf8_mode) != uFalse) {
18532e4f8982Smrg	Char *p;
1854e0a2b6dfSmrg	PtyData *data = TypeXtMallocX(PtyData, *length);
1855e0a2b6dfSmrg
1856e0a2b6dfSmrg	memset(data, 0, sizeof(*data));
1857e0a2b6dfSmrg	data->next = data->buffer;
1858e0a2b6dfSmrg	data->last = data->buffer + *length;
1859e0a2b6dfSmrg	memcpy(data->buffer, buffer, (size_t) *length);
1860e0a2b6dfSmrg	p = buffer;
1861e0a2b6dfSmrg	while (data->next < data->last) {
18622e4f8982Smrg	    unsigned chr, out, gl, gr;
18632e4f8982Smrg
1864894e0ac8Smrg	    if (!decodeUtf8(screen, data)) {
1865e0a2b6dfSmrg		data->utf_size = 1;
1866e0a2b6dfSmrg		data->utf_data = data->next[0];
1867e0a2b6dfSmrg	    }
1868e0a2b6dfSmrg	    data->next += data->utf_size;
1869e0a2b6dfSmrg	    chr = data->utf_data;
1870e0a2b6dfSmrg	    out = chr;
1871e0a2b6dfSmrg	    if ((gl = xtermCharSetIn(screen, chr, gsetL)) != chr) {
1872e0a2b6dfSmrg		out = gl;
1873e0a2b6dfSmrg	    } else if ((gr = xtermCharSetIn(screen, chr, gsetR)) != chr) {
1874e0a2b6dfSmrg		out = gr;
1875e0a2b6dfSmrg	    }
1876e0a2b6dfSmrg	    *p++ = (Char) ((out < 256) ? out : ' ');
1877e0a2b6dfSmrg	}
1878e0a2b6dfSmrg	*length = (unsigned) (p - buffer);
1879e0a2b6dfSmrg	free(data);
1880e0a2b6dfSmrg    } else
1881e0a2b6dfSmrg#endif
1882e0a2b6dfSmrg    {
18832e4f8982Smrg	Char *p;
18842e4f8982Smrg
1885e0a2b6dfSmrg	for (p = buffer; (int) (p - buffer) < (int) *length; ++p) {
18862e4f8982Smrg	    unsigned gl, gr;
18872e4f8982Smrg	    unsigned chr = *p;
18882e4f8982Smrg	    unsigned out = chr;
1889e0a2b6dfSmrg	    if ((gl = xtermCharSetIn(screen, chr, gsetL)) != chr) {
1890e0a2b6dfSmrg		out = gl;
1891e0a2b6dfSmrg	    } else if ((gr = xtermCharSetIn(screen, chr, gsetR)) != chr) {
1892e0a2b6dfSmrg		out = gr;
1893e0a2b6dfSmrg	    }
1894e0a2b6dfSmrg	    *p = (Char) out;
1895e0a2b6dfSmrg	}
1896e0a2b6dfSmrg    }
1897e0a2b6dfSmrg}
1898e0a2b6dfSmrg
1899e0a2b6dfSmrgstatic void
1900e0a2b6dfSmrg_qWriteSelectionData(XtermWidget xw, Char *lag, unsigned length)
1901d522f475Smrg{
19020bd37d32Smrg    TScreen *screen = TScreenOf(xw);
19030bd37d32Smrg
1904e0a2b6dfSmrg    /*
1905e0a2b6dfSmrg     * If we are pasting into a window which is using NRCS, we want to map
1906e0a2b6dfSmrg     * the text from the normal encoding (ISO-8859-1 or UTF-8) into the coding
1907e0a2b6dfSmrg     * that an application would use to write characters with NRCS.
1908e0a2b6dfSmrg     *
1909e0a2b6dfSmrg     * TODO: handle conversion from UTF-8, and adjust length.  This can be done
1910e0a2b6dfSmrg     * in the same buffer because the target is always 8-bit.
1911e0a2b6dfSmrg     */
1912e0a2b6dfSmrg    if ((xw->flags & NATIONAL) && (length != 0)) {
1913e0a2b6dfSmrg	ToNational(screen, lag, &length);
1914e0a2b6dfSmrg    }
1915d522f475Smrg#if OPT_PASTE64
1916d522f475Smrg    if (screen->base64_paste) {
1917d522f475Smrg	/* Send data as base64 */
1918d522f475Smrg	Char *p = lag;
1919d522f475Smrg	Char buf[64];
1920d522f475Smrg	unsigned x = 0;
19210bd37d32Smrg
192201037d57Smrg	TRACE(("convert to base64 %d:%s\n", length, visibleChars(p, length)));
192301037d57Smrg
19240bd37d32Smrg	/*
19250bd37d32Smrg	 * Handle the case where the selection is from _this_ xterm, which
19260bd37d32Smrg	 * puts part of the reply in the buffer before the selection callback
19270bd37d32Smrg	 * happens.
19280bd37d32Smrg	 */
19290bd37d32Smrg	if (screen->base64_paste && screen->unparse_len) {
19300bd37d32Smrg	    unparse_end(xw);
19310bd37d32Smrg	}
1932d522f475Smrg	while (length--) {
1933d522f475Smrg	    switch (screen->base64_count) {
1934d522f475Smrg	    case 0:
19352eaa94a1Schristos		buf[x++] = CharOf(base64_code[*p >> 2]);
19362eaa94a1Schristos		screen->base64_accu = (unsigned) (*p & 0x3);
1937d522f475Smrg		screen->base64_count = 2;
1938d522f475Smrg		++p;
1939d522f475Smrg		break;
1940d522f475Smrg	    case 2:
19412eaa94a1Schristos		buf[x++] = CharOf(base64_code[(screen->base64_accu << 4) +
19422eaa94a1Schristos					      (*p >> 4)]);
19432eaa94a1Schristos		screen->base64_accu = (unsigned) (*p & 0xF);
1944d522f475Smrg		screen->base64_count = 4;
1945d522f475Smrg		++p;
1946d522f475Smrg		break;
1947d522f475Smrg	    case 4:
19482eaa94a1Schristos		buf[x++] = CharOf(base64_code[(screen->base64_accu << 2) +
19492eaa94a1Schristos					      (*p >> 6)]);
19502eaa94a1Schristos		buf[x++] = CharOf(base64_code[*p & 0x3F]);
1951d522f475Smrg		screen->base64_accu = 0;
1952d522f475Smrg		screen->base64_count = 0;
1953d522f475Smrg		++p;
1954d522f475Smrg		break;
1955d522f475Smrg	    }
1956d522f475Smrg	    if (x >= 63) {
1957d522f475Smrg		/* Write 63 or 64 characters */
1958d522f475Smrg		screen->base64_pad += x;
195901037d57Smrg		TRACE(("writing base64 interim %s\n", visibleChars(buf, x)));
1960d522f475Smrg		tty_vwrite(screen->respond, buf, x);
1961d522f475Smrg		x = 0;
1962d522f475Smrg	    }
1963d522f475Smrg	}
1964d522f475Smrg	if (x != 0) {
1965d522f475Smrg	    screen->base64_pad += x;
196601037d57Smrg	    TRACE(("writing base64 finish %s\n", visibleChars(buf, x)));
1967d522f475Smrg	    tty_vwrite(screen->respond, buf, x);
1968d522f475Smrg	}
1969d522f475Smrg    } else
1970d522f475Smrg#endif /* OPT_PASTE64 */
1971d522f475Smrg#if OPT_READLINE
1972d522f475Smrg    if (SCREEN_FLAG(screen, paste_quotes)) {
1973d522f475Smrg	while (length--) {
1974492d43a5Smrg	    tty_vwrite(screen->respond, (const Char *) "\026", 1);	/* Control-V */
1975d522f475Smrg	    tty_vwrite(screen->respond, lag++, 1);
1976d522f475Smrg	}
1977d522f475Smrg    } else
1978d522f475Smrg#endif
197901037d57Smrg    {
198001037d57Smrg	TRACE(("writing base64 padding %s\n", visibleChars(lag, length)));
1981d522f475Smrg	tty_vwrite(screen->respond, lag, length);
198201037d57Smrg    }
1983d522f475Smrg}
1984d522f475Smrg
1985d522f475Smrgstatic void
1986e0a2b6dfSmrg_WriteSelectionData(XtermWidget xw, Char *line, size_t length)
1987d522f475Smrg{
1988d522f475Smrg    /* Write data to pty a line at a time. */
1989d522f475Smrg    /* Doing this one line at a time may no longer be necessary
1990d522f475Smrg       because v_write has been re-written. */
1991d522f475Smrg
19922e4f8982Smrg#if OPT_PASTE64
19930bd37d32Smrg    TScreen *screen = TScreenOf(xw);
19942e4f8982Smrg#endif
1995d522f475Smrg    Char *lag, *end;
1996d522f475Smrg
1997d522f475Smrg    /* in the VMS version, if tt_pasting isn't set to True then qio
1998d522f475Smrg       reads aren't blocked and an infinite loop is entered, where the
1999d522f475Smrg       pasted text shows up as new input, goes in again, shows up
2000d522f475Smrg       again, ad nauseum. */
2001d522f475Smrg#ifdef VMS
2002d522f475Smrg    tt_pasting = True;
2003d522f475Smrg#endif
2004d522f475Smrg
2005d522f475Smrg    end = &line[length];
2006d522f475Smrg    lag = line;
2007d522f475Smrg
2008d522f475Smrg#if OPT_PASTE64
2009d522f475Smrg    if (screen->base64_paste) {
20100bd37d32Smrg	_qWriteSelectionData(xw, lag, (unsigned) (end - lag));
2011d522f475Smrg	base64_flush(screen);
2012d522f475Smrg    } else
2013d522f475Smrg#endif
2014d522f475Smrg    {
2015d522f475Smrg	if (!SCREEN_FLAG(screen, paste_literal_nl)) {
2016d522f475Smrg	    Char *cp;
2017d522f475Smrg	    for (cp = line; cp != end; cp++) {
2018d522f475Smrg		if (*cp == '\n') {
2019d522f475Smrg		    *cp = '\r';
20200bd37d32Smrg		    _qWriteSelectionData(xw, lag, (unsigned) (cp - lag + 1));
2021d522f475Smrg		    lag = cp + 1;
2022d522f475Smrg		}
2023d522f475Smrg	    }
2024d522f475Smrg	}
2025d522f475Smrg
2026d522f475Smrg	if (lag != end) {
20270bd37d32Smrg	    _qWriteSelectionData(xw, lag, (unsigned) (end - lag));
2028d522f475Smrg	}
2029d522f475Smrg    }
2030d522f475Smrg#ifdef VMS
2031d522f475Smrg    tt_pasting = False;
2032d522f475Smrg    tt_start_read();		/* reenable reads or a character may be lost */
2033d522f475Smrg#endif
2034d522f475Smrg}
2035d522f475Smrg
2036d522f475Smrg#if OPT_READLINE
2037d522f475Smrgstatic void
2038e0a2b6dfSmrg_WriteKey(TScreen *screen, const Char *in)
2039d522f475Smrg{
2040d522f475Smrg    Char line[16];
2041d522f475Smrg    unsigned count = 0;
2042492d43a5Smrg    size_t length = strlen((const char *) in);
2043d522f475Smrg
2044d522f475Smrg    if (screen->control_eight_bits) {
2045d522f475Smrg	line[count++] = ANSI_CSI;
2046d522f475Smrg    } else {
2047d522f475Smrg	line[count++] = ANSI_ESC;
2048d522f475Smrg	line[count++] = '[';
2049d522f475Smrg    }
2050d522f475Smrg    while (length--)
2051d522f475Smrg	line[count++] = *in++;
2052d522f475Smrg    line[count++] = '~';
2053d522f475Smrg    tty_vwrite(screen->respond, line, count);
2054d522f475Smrg}
2055d522f475Smrg#endif /* OPT_READLINE */
2056d522f475Smrg
20570bd37d32Smrg/*
20580bd37d32Smrg * Unless enabled by the user, strip control characters other than formatting.
20590bd37d32Smrg */
20600bd37d32Smrgstatic size_t
20610bd37d32SmrgremoveControls(XtermWidget xw, char *value)
20620bd37d32Smrg{
20630bd37d32Smrg    TScreen *screen = TScreenOf(xw);
20640bd37d32Smrg    size_t dst = 0;
20650bd37d32Smrg
20660bd37d32Smrg    if (screen->allowPasteControls) {
20670bd37d32Smrg	dst = strlen(value);
20680bd37d32Smrg    } else {
20692e4f8982Smrg	size_t src = 0;
20700bd37d32Smrg	while ((value[dst] = value[src]) != '\0') {
20710bd37d32Smrg	    int ch = CharOf(value[src++]);
20720bd37d32Smrg	    if (ch < 32) {
20730bd37d32Smrg		switch (ch) {
20740bd37d32Smrg		case '\b':
20750bd37d32Smrg		case '\t':
20760bd37d32Smrg		case '\n':
20770bd37d32Smrg		case '\r':
20780bd37d32Smrg		    ++dst;
20790bd37d32Smrg		    break;
20800bd37d32Smrg		default:
20810bd37d32Smrg		    continue;
20820bd37d32Smrg		}
20830bd37d32Smrg	    }
20840bd37d32Smrg#if OPT_WIDE_CHARS
2085e0a2b6dfSmrg	    else if (screen->utf8_inparse || screen->utf8_nrc_mode)
20860bd37d32Smrg		++dst;
20870bd37d32Smrg#endif
20880bd37d32Smrg#if OPT_C1_PRINT || OPT_WIDE_CHARS
20890bd37d32Smrg	    else if (screen->c1_printable)
20900bd37d32Smrg		++dst;
20910bd37d32Smrg#endif
20920bd37d32Smrg	    else if (ch >= 128 && ch < 160)
20930bd37d32Smrg		continue;
20940bd37d32Smrg	    else
20950bd37d32Smrg		++dst;
20960bd37d32Smrg	}
20970bd37d32Smrg    }
20980bd37d32Smrg    return dst;
20990bd37d32Smrg}
21000bd37d32Smrg
2101d522f475Smrg/* SelectionReceived: stuff received selection text into pty */
2102d522f475Smrg
2103d522f475Smrg/* ARGSUSED */
2104d522f475Smrgstatic void
2105d522f475SmrgSelectionReceived(Widget w,
2106d522f475Smrg		  XtPointer client_data,
2107894e0ac8Smrg		  Atom *selection GCC_UNUSED,
2108894e0ac8Smrg		  Atom *type,
2109d522f475Smrg		  XtPointer value,
2110d522f475Smrg		  unsigned long *length,
2111d522f475Smrg		  int *format)
2112d522f475Smrg{
2113d522f475Smrg    char **text_list = NULL;
21142e4f8982Smrg    int text_list_count = 0;
2115d522f475Smrg    XTextProperty text_prop;
2116d522f475Smrg    TScreen *screen;
2117d522f475Smrg    Display *dpy;
2118d522f475Smrg#if OPT_TRACE && OPT_WIDE_CHARS
2119d522f475Smrg    Char *line = (Char *) value;
2120d522f475Smrg#endif
2121d522f475Smrg
2122956cc18dSsnj    XtermWidget xw;
2123956cc18dSsnj
2124956cc18dSsnj    if ((xw = getXtermWidget(w)) == 0)
2125d522f475Smrg	return;
2126956cc18dSsnj
2127956cc18dSsnj    screen = TScreenOf(xw);
2128d522f475Smrg    dpy = XtDisplay(w);
2129d522f475Smrg
2130d522f475Smrg    if (*type == 0		/*XT_CONVERT_FAIL */
2131d522f475Smrg	|| *length == 0
213201037d57Smrg	|| value == NULL) {
213301037d57Smrg	TRACE(("...no data to convert\n"));
2134d522f475Smrg	goto fail;
213501037d57Smrg    }
2136d522f475Smrg
2137d522f475Smrg    text_prop.value = (unsigned char *) value;
2138d522f475Smrg    text_prop.encoding = *type;
2139d522f475Smrg    text_prop.format = *format;
2140d522f475Smrg    text_prop.nitems = *length;
2141d522f475Smrg
214201037d57Smrg    TRACE(("SelectionReceived %s %s format %d, nitems %ld\n",
214301037d57Smrg	   XGetAtomName(screen->display, *selection),
2144956cc18dSsnj	   visibleSelectionTarget(dpy, text_prop.encoding),
2145956cc18dSsnj	   text_prop.format,
2146956cc18dSsnj	   text_prop.nitems));
2147956cc18dSsnj
2148d522f475Smrg#if OPT_WIDE_CHARS
2149e39b573cSmrg    if (XSupportsLocale() && screen->wide_chars) {
2150d522f475Smrg	if (*type == XA_UTF8_STRING(dpy) ||
2151d522f475Smrg	    *type == XA_STRING ||
2152d522f475Smrg	    *type == XA_COMPOUND_TEXT(dpy)) {
2153d522f475Smrg	    GettingSelection(dpy, *type, line, *length);
2154d522f475Smrg	    if (Xutf8TextPropertyToTextList(dpy, &text_prop,
2155d522f475Smrg					    &text_list,
2156d522f475Smrg					    &text_list_count) < 0) {
2157e39b573cSmrg		TRACE(("default Xutf8 Conversion failed\n"));
2158d522f475Smrg		text_list = NULL;
2159d522f475Smrg	    }
2160d522f475Smrg	}
2161d522f475Smrg    } else
2162d522f475Smrg#endif /* OPT_WIDE_CHARS */
2163d522f475Smrg    {
2164d522f475Smrg	/* Convert the selection to locale's multibyte encoding. */
2165d522f475Smrg
2166d522f475Smrg	if (*type == XA_UTF8_STRING(dpy) ||
2167d522f475Smrg	    *type == XA_STRING ||
2168d522f475Smrg	    *type == XA_COMPOUND_TEXT(dpy)) {
2169d522f475Smrg	    Status rc;
2170d522f475Smrg
2171d522f475Smrg	    GettingSelection(dpy, *type, line, *length);
2172d522f475Smrg
2173d522f475Smrg#if OPT_WIDE_CHARS
2174d522f475Smrg	    if (*type == XA_UTF8_STRING(dpy) &&
2175d522f475Smrg		!(screen->wide_chars || screen->c1_printable)) {
217620d2c4d2Smrg		rc = xtermUtf8ToTextList(xw, &text_prop,
217720d2c4d2Smrg					 &text_list, &text_list_count);
2178d522f475Smrg	    } else
2179d522f475Smrg#endif
2180e39b573cSmrg	    if (*type == XA_STRING && (!XSupportsLocale() || screen->brokenSelections)) {
2181d522f475Smrg		rc = XTextPropertyToStringList(&text_prop,
2182d522f475Smrg					       &text_list, &text_list_count);
2183d522f475Smrg	    } else {
2184d522f475Smrg		rc = XmbTextPropertyToTextList(dpy, &text_prop,
2185d522f475Smrg					       &text_list,
2186d522f475Smrg					       &text_list_count);
2187d522f475Smrg	    }
2188d522f475Smrg	    if (rc < 0) {
2189d522f475Smrg		TRACE(("Conversion failed\n"));
2190d522f475Smrg		text_list = NULL;
2191d522f475Smrg	    }
2192d522f475Smrg	}
2193d522f475Smrg    }
2194d522f475Smrg
2195d522f475Smrg    if (text_list != NULL && text_list_count != 0) {
2196d522f475Smrg	int i;
2197d522f475Smrg
2198d522f475Smrg#if OPT_PASTE64
2199d522f475Smrg	if (screen->base64_paste) {
2200a1f3da82Smrg	    /* EMPTY */ ;
2201d522f475Smrg	} else
2202d522f475Smrg#endif
2203d522f475Smrg#if OPT_READLINE
2204d522f475Smrg	if (SCREEN_FLAG(screen, paste_brackets)) {
2205492d43a5Smrg	    _WriteKey(screen, (const Char *) "200");
2206d522f475Smrg	}
2207d522f475Smrg#endif
2208d522f475Smrg	for (i = 0; i < text_list_count; i++) {
22090bd37d32Smrg	    size_t len = removeControls(xw, text_list[i]);
221001037d57Smrg
22110bd37d32Smrg	    if (screen->selectToBuffer) {
221201037d57Smrg		InternalSelect *mydata = &(screen->internal_select);
221301037d57Smrg		size_t have = (mydata->buffer
221401037d57Smrg			       ? strlen(mydata->buffer)
22150bd37d32Smrg			       : 0);
22160bd37d32Smrg		size_t need = have + len + 1;
221701037d57Smrg		char *buffer = realloc(mydata->buffer, need);
221801037d57Smrg
221901037d57Smrg		screen->selectToBuffer = False;
222001037d57Smrg#if OPT_PASTE64
222101037d57Smrg		screen->base64_paste = mydata->base64_paste;
222201037d57Smrg#endif
222301037d57Smrg#if OPT_READLINE
222401037d57Smrg		screen->paste_brackets = mydata->paste_brackets;
222501037d57Smrg#endif
22260bd37d32Smrg		if (buffer != 0) {
22270bd37d32Smrg		    strcpy(buffer + have, text_list[i]);
222801037d57Smrg		    mydata->buffer = buffer;
22290bd37d32Smrg		}
223001037d57Smrg		TRACE(("FormatSelect %d.%d .. %d.%d %s\n",
223101037d57Smrg		       screen->startSel.row,
223201037d57Smrg		       screen->startSel.col,
223301037d57Smrg		       screen->endSel.row,
223401037d57Smrg		       screen->endSel.col,
223501037d57Smrg		       mydata->buffer));
223601037d57Smrg		mydata->format_select(w, mydata->format, mydata->buffer,
223701037d57Smrg				      &(screen->startSel),
223801037d57Smrg				      &(screen->endSel));
223901037d57Smrg
224001037d57Smrg		free(mydata->format);
224101037d57Smrg		free(mydata->buffer);
224201037d57Smrg		memset(mydata, 0, sizeof(*mydata));
22430bd37d32Smrg	    } else {
22440bd37d32Smrg		_WriteSelectionData(xw, (Char *) text_list[i], len);
22450bd37d32Smrg	    }
2246d522f475Smrg	}
2247d522f475Smrg#if OPT_PASTE64
2248d522f475Smrg	if (screen->base64_paste) {
2249956cc18dSsnj	    FinishPaste64(xw);
2250d522f475Smrg	} else
2251d522f475Smrg#endif
2252d522f475Smrg#if OPT_READLINE
2253d522f475Smrg	if (SCREEN_FLAG(screen, paste_brackets)) {
2254492d43a5Smrg	    _WriteKey(screen, (const Char *) "201");
2255d522f475Smrg	}
2256d522f475Smrg#endif
2257d522f475Smrg	XFreeStringList(text_list);
225801037d57Smrg    } else {
225901037d57Smrg	TRACE(("...empty text-list\n"));
2260d522f475Smrg	goto fail;
226101037d57Smrg    }
2262d522f475Smrg
2263d522f475Smrg    XtFree((char *) client_data);
2264d522f475Smrg    XtFree((char *) value);
2265d522f475Smrg
2266d522f475Smrg    return;
2267d522f475Smrg
2268d522f475Smrg  fail:
2269d522f475Smrg    if (client_data != 0) {
2270d522f475Smrg	struct _SelectionList *list = (struct _SelectionList *) client_data;
2271956cc18dSsnj
2272956cc18dSsnj	TRACE(("SelectionReceived ->xtermGetSelection\n"));
2273d522f475Smrg	xtermGetSelection(w, list->time,
2274d522f475Smrg			  list->params, list->count, list->targets);
2275d522f475Smrg	XtFree((char *) client_data);
2276d522f475Smrg#if OPT_PASTE64
2277d522f475Smrg    } else {
2278956cc18dSsnj	FinishPaste64(xw);
2279d522f475Smrg#endif
2280d522f475Smrg    }
2281d522f475Smrg    return;
2282d522f475Smrg}
2283d522f475Smrg
2284d522f475Smrgvoid
2285d522f475SmrgHandleInsertSelection(Widget w,
2286894e0ac8Smrg		      XEvent *event,	/* assumed to be XButtonEvent* */
2287e0a2b6dfSmrg		      String *params,	/* selections in precedence order */
2288d522f475Smrg		      Cardinal *num_params)
2289d522f475Smrg{
2290956cc18dSsnj    XtermWidget xw;
2291d522f475Smrg
2292956cc18dSsnj    if ((xw = getXtermWidget(w)) != 0) {
22930bd37d32Smrg	TRACE(("HandleInsertSelection\n"));
2294d522f475Smrg	if (!SendMousePosition(xw, event)) {
2295d522f475Smrg#if OPT_READLINE
2296d522f475Smrg	    int ldelta;
2297956cc18dSsnj	    TScreen *screen = TScreenOf(xw);
2298492d43a5Smrg	    if (IsBtnEvent(event)
2299d522f475Smrg	    /* Disable on Shift-mouse, including the application-mouse modes */
2300492d43a5Smrg		&& !(KeyModifiers(event) & ShiftMask)
2301d522f475Smrg		&& (screen->send_mouse_pos == MOUSE_OFF)
2302d522f475Smrg		&& SCREEN_FLAG(screen, paste_moves)
2303d522f475Smrg		&& rowOnCurrentLine(screen, eventRow(screen, event), &ldelta))
2304d522f475Smrg		ReadLineMovePoint(screen, eventColBetween(screen, event), ldelta);
2305d522f475Smrg#endif /* OPT_READLINE */
2306d522f475Smrg
2307d522f475Smrg	    xtermGetSelection(w, event->xbutton.time, params, *num_params, NULL);
2308d522f475Smrg	}
2309d522f475Smrg    }
2310d522f475Smrg}
2311d522f475Smrg
2312d522f475Smrgstatic SelectUnit
2313956cc18dSsnjEvalSelectUnit(XtermWidget xw,
2314d522f475Smrg	       Time buttonDownTime,
2315d522f475Smrg	       SelectUnit defaultUnit,
2316d522f475Smrg	       unsigned int button)
2317d522f475Smrg{
2318956cc18dSsnj    TScreen *screen = TScreenOf(xw);
2319d522f475Smrg    SelectUnit result;
2320d522f475Smrg    int delta;
2321d522f475Smrg
2322d522f475Smrg    if (button != screen->lastButton) {
232320d2c4d2Smrg	delta = screen->multiClickTime + 1;
2324d522f475Smrg    } else if (screen->lastButtonUpTime == (Time) 0) {
2325d522f475Smrg	/* first time and once in a blue moon */
2326d522f475Smrg	delta = screen->multiClickTime + 1;
2327d522f475Smrg    } else if (buttonDownTime > screen->lastButtonUpTime) {
2328d522f475Smrg	/* most of the time */
23292eaa94a1Schristos	delta = (int) (buttonDownTime - screen->lastButtonUpTime);
2330d522f475Smrg    } else {
2331d522f475Smrg	/* time has rolled over since lastButtonUpTime */
23322eaa94a1Schristos	delta = (int) ((((Time) ~ 0) - screen->lastButtonUpTime) + buttonDownTime);
2333d522f475Smrg    }
2334d522f475Smrg
2335d522f475Smrg    if (delta > screen->multiClickTime) {
2336d522f475Smrg	screen->numberOfClicks = 1;
2337d522f475Smrg	result = defaultUnit;
2338d522f475Smrg    } else {
2339d522f475Smrg	result = screen->selectMap[screen->numberOfClicks % screen->maxClicks];
2340d522f475Smrg	screen->numberOfClicks += 1;
2341d522f475Smrg    }
2342d522f475Smrg    TRACE(("EvalSelectUnit(%d) = %d\n", screen->numberOfClicks, result));
2343d522f475Smrg    return result;
2344d522f475Smrg}
2345d522f475Smrg
2346d522f475Smrgstatic void
2347d522f475Smrgdo_select_start(XtermWidget xw,
2348894e0ac8Smrg		XEvent *event,	/* must be XButtonEvent* */
2349e0a2b6dfSmrg		CELL *cell)
2350d522f475Smrg{
2351956cc18dSsnj    TScreen *screen = TScreenOf(xw);
2352d522f475Smrg
2353d522f475Smrg    if (SendMousePosition(xw, event))
2354d522f475Smrg	return;
2355956cc18dSsnj    screen->selectUnit = EvalSelectUnit(xw,
2356d522f475Smrg					event->xbutton.time,
2357d522f475Smrg					Select_CHAR,
2358d522f475Smrg					event->xbutton.button);
2359d522f475Smrg    screen->replyToEmacs = False;
2360d522f475Smrg
2361d522f475Smrg#if OPT_READLINE
2362d522f475Smrg    lastButtonDownTime = event->xbutton.time;
2363d522f475Smrg#endif
2364d522f475Smrg
2365d522f475Smrg    StartSelect(xw, cell);
2366d522f475Smrg}
2367d522f475Smrg
2368d522f475Smrg/* ARGSUSED */
2369d522f475Smrgvoid
2370d522f475SmrgHandleSelectStart(Widget w,
2371894e0ac8Smrg		  XEvent *event,	/* must be XButtonEvent* */
2372e0a2b6dfSmrg		  String *params GCC_UNUSED,
2373d522f475Smrg		  Cardinal *num_params GCC_UNUSED)
2374d522f475Smrg{
2375956cc18dSsnj    XtermWidget xw;
2376956cc18dSsnj
2377956cc18dSsnj    if ((xw = getXtermWidget(w)) != 0) {
2378956cc18dSsnj	TScreen *screen = TScreenOf(xw);
2379d522f475Smrg	CELL cell;
2380d522f475Smrg
23810bd37d32Smrg	TRACE(("HandleSelectStart\n"));
2382d522f475Smrg	screen->firstValidRow = 0;
2383d522f475Smrg	screen->lastValidRow = screen->max_row;
2384d522f475Smrg	PointToCELL(screen, event->xbutton.y, event->xbutton.x, &cell);
2385d522f475Smrg
2386d522f475Smrg#if OPT_READLINE
2387d522f475Smrg	ExtendingSelection = 0;
2388d522f475Smrg#endif
2389d522f475Smrg
2390d522f475Smrg	do_select_start(xw, event, &cell);
2391d522f475Smrg    }
2392d522f475Smrg}
2393d522f475Smrg
2394d522f475Smrg/* ARGSUSED */
2395d522f475Smrgvoid
2396d522f475SmrgHandleKeyboardSelectStart(Widget w,
2397894e0ac8Smrg			  XEvent *event,	/* must be XButtonEvent* */
2398e0a2b6dfSmrg			  String *params GCC_UNUSED,
2399d522f475Smrg			  Cardinal *num_params GCC_UNUSED)
2400d522f475Smrg{
2401956cc18dSsnj    XtermWidget xw;
2402956cc18dSsnj
2403956cc18dSsnj    if ((xw = getXtermWidget(w)) != 0) {
2404956cc18dSsnj	TScreen *screen = TScreenOf(xw);
24050bd37d32Smrg
24060bd37d32Smrg	TRACE(("HandleKeyboardSelectStart\n"));
2407d522f475Smrg	do_select_start(xw, event, &screen->cursorp);
2408d522f475Smrg    }
2409d522f475Smrg}
2410d522f475Smrg
2411d522f475Smrgstatic void
2412894e0ac8SmrgTrackDown(XtermWidget xw, XButtonEvent *event)
2413d522f475Smrg{
2414956cc18dSsnj    TScreen *screen = TScreenOf(xw);
2415d522f475Smrg    CELL cell;
2416d522f475Smrg
2417956cc18dSsnj    screen->selectUnit = EvalSelectUnit(xw,
2418d522f475Smrg					event->time,
2419d522f475Smrg					Select_CHAR,
2420d522f475Smrg					event->button);
2421d522f475Smrg    if (screen->numberOfClicks > 1) {
2422d522f475Smrg	PointToCELL(screen, event->y, event->x, &cell);
2423d522f475Smrg	screen->replyToEmacs = True;
2424d522f475Smrg	StartSelect(xw, &cell);
2425d522f475Smrg    } else {
2426d522f475Smrg	screen->waitingForTrackInfo = True;
2427492d43a5Smrg	EditorButton(xw, event);
2428d522f475Smrg    }
2429d522f475Smrg}
2430d522f475Smrg
2431d522f475Smrg#define boundsCheck(x)	if (x < 0) \
2432d522f475Smrg			    x = 0; \
2433d522f475Smrg			else if (x >= screen->max_row) \
2434d522f475Smrg			    x = screen->max_row
2435d522f475Smrg
2436d522f475Smrgvoid
2437d522f475SmrgTrackMouse(XtermWidget xw,
2438d522f475Smrg	   int func,
2439e0a2b6dfSmrg	   CELL *start,
2440d522f475Smrg	   int firstrow,
2441d522f475Smrg	   int lastrow)
2442d522f475Smrg{
2443956cc18dSsnj    TScreen *screen = TScreenOf(xw);
2444d522f475Smrg
2445d522f475Smrg    if (screen->waitingForTrackInfo) {	/* if Timed, ignore */
2446d522f475Smrg	screen->waitingForTrackInfo = False;
2447d522f475Smrg
2448d522f475Smrg	if (func != 0) {
2449d522f475Smrg	    CELL first = *start;
2450d522f475Smrg
2451d522f475Smrg	    boundsCheck(first.row);
2452d522f475Smrg	    boundsCheck(firstrow);
2453d522f475Smrg	    boundsCheck(lastrow);
2454d522f475Smrg	    screen->firstValidRow = firstrow;
2455d522f475Smrg	    screen->lastValidRow = lastrow;
2456d522f475Smrg	    screen->replyToEmacs = True;
2457d522f475Smrg	    StartSelect(xw, &first);
2458d522f475Smrg	}
2459d522f475Smrg    }
2460d522f475Smrg}
2461d522f475Smrg
2462d522f475Smrgstatic void
2463e0a2b6dfSmrgStartSelect(XtermWidget xw, const CELL *cell)
2464d522f475Smrg{
2465956cc18dSsnj    TScreen *screen = TScreenOf(xw);
2466d522f475Smrg
2467d522f475Smrg    TRACE(("StartSelect row=%d, col=%d\n", cell->row, cell->col));
2468d522f475Smrg    if (screen->cursor_state)
2469d522f475Smrg	HideCursor();
2470d522f475Smrg    if (screen->numberOfClicks == 1) {
2471d522f475Smrg	/* set start of selection */
2472d522f475Smrg	screen->rawPos = *cell;
2473d522f475Smrg    }
2474d522f475Smrg    /* else use old values in rawPos */
2475d522f475Smrg    screen->saveStartR = screen->startExt = screen->rawPos;
2476d522f475Smrg    screen->saveEndR = screen->endExt = screen->rawPos;
2477d522f475Smrg    if (Coordinate(screen, cell) < Coordinate(screen, &(screen->rawPos))) {
2478d522f475Smrg	screen->eventMode = LEFTEXTENSION;
2479d522f475Smrg	screen->startExt = *cell;
2480d522f475Smrg    } else {
2481d522f475Smrg	screen->eventMode = RIGHTEXTENSION;
2482d522f475Smrg	screen->endExt = *cell;
2483d522f475Smrg    }
2484d522f475Smrg    ComputeSelect(xw, &(screen->startExt), &(screen->endExt), False);
2485d522f475Smrg}
2486d522f475Smrg
2487d522f475Smrgstatic void
2488d522f475SmrgEndExtend(XtermWidget xw,
2489894e0ac8Smrg	  XEvent *event,	/* must be XButtonEvent */
2490e0a2b6dfSmrg	  String *params,	/* selections */
2491d522f475Smrg	  Cardinal num_params,
2492d522f475Smrg	  Bool use_cursor_loc)
2493d522f475Smrg{
2494d522f475Smrg    CELL cell;
2495956cc18dSsnj    TScreen *screen = TScreenOf(xw);
2496d522f475Smrg
2497d522f475Smrg    if (use_cursor_loc) {
2498d522f475Smrg	cell = screen->cursorp;
2499d522f475Smrg    } else {
2500d522f475Smrg	PointToCELL(screen, event->xbutton.y, event->xbutton.x, &cell);
2501d522f475Smrg    }
2502d522f475Smrg    ExtendExtend(xw, &cell);
25032e4f8982Smrg
2504d522f475Smrg    screen->lastButtonUpTime = event->xbutton.time;
2505d522f475Smrg    screen->lastButton = event->xbutton.button;
25062e4f8982Smrg
2507d522f475Smrg    if (!isSameCELL(&(screen->startSel), &(screen->endSel))) {
2508d522f475Smrg	if (screen->replyToEmacs) {
25092e4f8982Smrg	    Char line[64];
25102e4f8982Smrg	    unsigned count = 0;
25112e4f8982Smrg
2512d522f475Smrg	    if (screen->control_eight_bits) {
2513d522f475Smrg		line[count++] = ANSI_CSI;
2514d522f475Smrg	    } else {
2515d522f475Smrg		line[count++] = ANSI_ESC;
2516d522f475Smrg		line[count++] = '[';
2517d522f475Smrg	    }
2518d522f475Smrg	    if (isSameCELL(&(screen->rawPos), &(screen->startSel))
2519d522f475Smrg		&& isSameCELL(&cell, &(screen->endSel))) {
2520d522f475Smrg		/* Use short-form emacs select */
25210bd37d32Smrg
25220bd37d32Smrg		switch (screen->extend_coords) {
25230bd37d32Smrg		case 0:
25240bd37d32Smrg		case SET_EXT_MODE_MOUSE:
25250bd37d32Smrg		    line[count++] = 't';
25260bd37d32Smrg		    break;
25270bd37d32Smrg		case SET_SGR_EXT_MODE_MOUSE:
25280bd37d32Smrg		    line[count++] = '<';
25290bd37d32Smrg		    break;
25300bd37d32Smrg		}
25310bd37d32Smrg
2532492d43a5Smrg		count = EmitMousePosition(screen, line, count, screen->endSel.col);
25330bd37d32Smrg		count = EmitMousePositionSeparator(screen, line, count);
2534492d43a5Smrg		count = EmitMousePosition(screen, line, count, screen->endSel.row);
25350bd37d32Smrg
25360bd37d32Smrg		switch (screen->extend_coords) {
25370bd37d32Smrg		case SET_SGR_EXT_MODE_MOUSE:
25380bd37d32Smrg		case SET_URXVT_EXT_MODE_MOUSE:
25390bd37d32Smrg		    line[count++] = 't';
25400bd37d32Smrg		    break;
25410bd37d32Smrg		}
2542d522f475Smrg	    } else {
2543d522f475Smrg		/* long-form, specify everything */
25440bd37d32Smrg
25450bd37d32Smrg		switch (screen->extend_coords) {
25460bd37d32Smrg		case 0:
25470bd37d32Smrg		case SET_EXT_MODE_MOUSE:
25480bd37d32Smrg		    line[count++] = 'T';
25490bd37d32Smrg		    break;
25500bd37d32Smrg		case SET_SGR_EXT_MODE_MOUSE:
25510bd37d32Smrg		    line[count++] = '<';
25520bd37d32Smrg		    break;
25530bd37d32Smrg		}
25540bd37d32Smrg
2555492d43a5Smrg		count = EmitMousePosition(screen, line, count, screen->startSel.col);
25560bd37d32Smrg		count = EmitMousePositionSeparator(screen, line, count);
2557492d43a5Smrg		count = EmitMousePosition(screen, line, count, screen->startSel.row);
25580bd37d32Smrg		count = EmitMousePositionSeparator(screen, line, count);
2559492d43a5Smrg		count = EmitMousePosition(screen, line, count, screen->endSel.col);
25600bd37d32Smrg		count = EmitMousePositionSeparator(screen, line, count);
2561492d43a5Smrg		count = EmitMousePosition(screen, line, count, screen->endSel.row);
25620bd37d32Smrg		count = EmitMousePositionSeparator(screen, line, count);
2563492d43a5Smrg		count = EmitMousePosition(screen, line, count, cell.col);
25640bd37d32Smrg		count = EmitMousePositionSeparator(screen, line, count);
2565492d43a5Smrg		count = EmitMousePosition(screen, line, count, cell.row);
25660bd37d32Smrg
25670bd37d32Smrg		switch (screen->extend_coords) {
25680bd37d32Smrg		case SET_SGR_EXT_MODE_MOUSE:
25690bd37d32Smrg		case SET_URXVT_EXT_MODE_MOUSE:
25700bd37d32Smrg		    line[count++] = 'T';
25710bd37d32Smrg		    break;
25720bd37d32Smrg		}
2573d522f475Smrg	    }
2574d522f475Smrg	    v_write(screen->respond, line, count);
2575d522f475Smrg	    TrackText(xw, &zeroCELL, &zeroCELL);
2576d522f475Smrg	}
2577d522f475Smrg    }
2578d522f475Smrg    SelectSet(xw, event, params, num_params);
2579d522f475Smrg    screen->eventMode = NORMAL;
2580d522f475Smrg}
2581d522f475Smrg
2582d522f475Smrgvoid
2583d522f475SmrgHandleSelectSet(Widget w,
2584894e0ac8Smrg		XEvent *event,
2585e0a2b6dfSmrg		String *params,
2586d522f475Smrg		Cardinal *num_params)
2587d522f475Smrg{
2588956cc18dSsnj    XtermWidget xw;
2589956cc18dSsnj
2590956cc18dSsnj    if ((xw = getXtermWidget(w)) != 0) {
25910bd37d32Smrg	TRACE(("HandleSelectSet\n"));
2592956cc18dSsnj	SelectSet(xw, event, params, *num_params);
2593d522f475Smrg    }
2594d522f475Smrg}
2595d522f475Smrg
2596d522f475Smrg/* ARGSUSED */
2597d522f475Smrgstatic void
2598d522f475SmrgSelectSet(XtermWidget xw,
2599894e0ac8Smrg	  XEvent *event GCC_UNUSED,
2600e0a2b6dfSmrg	  String *params,
2601d522f475Smrg	  Cardinal num_params)
2602d522f475Smrg{
2603956cc18dSsnj    TScreen *screen = TScreenOf(xw);
2604d522f475Smrg
2605d522f475Smrg    TRACE(("SelectSet\n"));
2606d522f475Smrg    /* Only do select stuff if non-null select */
2607d522f475Smrg    if (!isSameCELL(&(screen->startSel), &(screen->endSel))) {
26080bd37d32Smrg	SaltTextAway(xw, &(screen->startSel), &(screen->endSel));
26090bd37d32Smrg	_OwnSelection(xw, params, num_params);
2610d522f475Smrg    } else {
26110bd37d32Smrg	ScrnDisownSelection(xw);
2612d522f475Smrg    }
2613d522f475Smrg}
2614d522f475Smrg
2615d522f475Smrg#define Abs(x)		((x) < 0 ? -(x) : (x))
2616d522f475Smrg
2617d522f475Smrg/* ARGSUSED */
2618d522f475Smrgstatic void
2619d522f475Smrgdo_start_extend(XtermWidget xw,
2620894e0ac8Smrg		XEvent *event,	/* must be XButtonEvent* */
2621e0a2b6dfSmrg		String *params GCC_UNUSED,
2622d522f475Smrg		Cardinal *num_params GCC_UNUSED,
2623d522f475Smrg		Bool use_cursor_loc)
2624d522f475Smrg{
2625956cc18dSsnj    TScreen *screen = TScreenOf(xw);
2626d522f475Smrg    int coord;
2627d522f475Smrg    CELL cell;
2628d522f475Smrg
2629d522f475Smrg    if (SendMousePosition(xw, event))
2630d522f475Smrg	return;
2631d522f475Smrg
2632d522f475Smrg    screen->firstValidRow = 0;
2633d522f475Smrg    screen->lastValidRow = screen->max_row;
2634d522f475Smrg#if OPT_READLINE
2635492d43a5Smrg    if ((KeyModifiers(event) & ShiftMask)
2636d522f475Smrg	|| event->xbutton.button != Button3
2637d522f475Smrg	|| !(SCREEN_FLAG(screen, dclick3_deletes)))
2638d522f475Smrg#endif
2639956cc18dSsnj	screen->selectUnit = EvalSelectUnit(xw,
2640d522f475Smrg					    event->xbutton.time,
2641d522f475Smrg					    screen->selectUnit,
2642d522f475Smrg					    event->xbutton.button);
2643d522f475Smrg    screen->replyToEmacs = False;
2644d522f475Smrg
2645d522f475Smrg#if OPT_READLINE
2646d522f475Smrg    CheckSecondPress3(screen, event);
2647d522f475Smrg#endif
2648d522f475Smrg
2649d522f475Smrg    if (screen->numberOfClicks == 1
2650d522f475Smrg	|| (SCREEN_FLAG(screen, dclick3_deletes)	/* Dclick special */
2651492d43a5Smrg	    &&!(KeyModifiers(event) & ShiftMask))) {
2652d522f475Smrg	/* Save existing selection so we can reestablish it if the guy
2653d522f475Smrg	   extends past the other end of the selection */
2654d522f475Smrg	screen->saveStartR = screen->startExt = screen->startRaw;
2655d522f475Smrg	screen->saveEndR = screen->endExt = screen->endRaw;
2656d522f475Smrg    } else {
2657d522f475Smrg	/* He just needed the selection mode changed, use old values. */
2658d522f475Smrg	screen->startExt = screen->startRaw = screen->saveStartR;
2659d522f475Smrg	screen->endExt = screen->endRaw = screen->saveEndR;
2660d522f475Smrg    }
2661d522f475Smrg    if (use_cursor_loc) {
2662d522f475Smrg	cell = screen->cursorp;
2663d522f475Smrg    } else {
2664d522f475Smrg	PointToCELL(screen, event->xbutton.y, event->xbutton.x, &cell);
2665d522f475Smrg    }
2666d522f475Smrg    coord = Coordinate(screen, &cell);
2667d522f475Smrg
2668d522f475Smrg    if (Abs(coord - Coordinate(screen, &(screen->startSel)))
2669d522f475Smrg	< Abs(coord - Coordinate(screen, &(screen->endSel)))
2670d522f475Smrg	|| coord < Coordinate(screen, &(screen->startSel))) {
2671d522f475Smrg	/* point is close to left side of selection */
2672d522f475Smrg	screen->eventMode = LEFTEXTENSION;
2673d522f475Smrg	screen->startExt = cell;
2674d522f475Smrg    } else {
2675d522f475Smrg	/* point is close to left side of selection */
2676d522f475Smrg	screen->eventMode = RIGHTEXTENSION;
2677d522f475Smrg	screen->endExt = cell;
2678d522f475Smrg    }
2679d522f475Smrg    ComputeSelect(xw, &(screen->startExt), &(screen->endExt), True);
2680d522f475Smrg
2681d522f475Smrg#if OPT_READLINE
2682d522f475Smrg    if (!isSameCELL(&(screen->startSel), &(screen->endSel)))
2683d522f475Smrg	ExtendingSelection = 1;
2684d522f475Smrg#endif
2685d522f475Smrg}
2686d522f475Smrg
2687d522f475Smrgstatic void
2688e0a2b6dfSmrgExtendExtend(XtermWidget xw, const CELL *cell)
2689d522f475Smrg{
2690956cc18dSsnj    TScreen *screen = TScreenOf(xw);
2691d522f475Smrg    int coord = Coordinate(screen, cell);
2692d522f475Smrg
2693d522f475Smrg    TRACE(("ExtendExtend row=%d, col=%d\n", cell->row, cell->col));
2694d522f475Smrg    if (screen->eventMode == LEFTEXTENSION
2695d522f475Smrg	&& ((coord + (screen->selectUnit != Select_CHAR))
2696d522f475Smrg	    > Coordinate(screen, &(screen->endSel)))) {
2697d522f475Smrg	/* Whoops, he's changed his mind.  Do RIGHTEXTENSION */
2698d522f475Smrg	screen->eventMode = RIGHTEXTENSION;
2699d522f475Smrg	screen->startExt = screen->saveStartR;
2700d522f475Smrg    } else if (screen->eventMode == RIGHTEXTENSION
2701d522f475Smrg	       && coord < Coordinate(screen, &(screen->startSel))) {
2702d522f475Smrg	/* Whoops, he's changed his mind.  Do LEFTEXTENSION */
2703d522f475Smrg	screen->eventMode = LEFTEXTENSION;
2704d522f475Smrg	screen->endExt = screen->saveEndR;
2705d522f475Smrg    }
2706d522f475Smrg    if (screen->eventMode == LEFTEXTENSION) {
2707d522f475Smrg	screen->startExt = *cell;
2708d522f475Smrg    } else {
2709d522f475Smrg	screen->endExt = *cell;
2710d522f475Smrg    }
2711d522f475Smrg    ComputeSelect(xw, &(screen->startExt), &(screen->endExt), False);
2712d522f475Smrg
2713d522f475Smrg#if OPT_READLINE
2714d522f475Smrg    if (!isSameCELL(&(screen->startSel), &(screen->endSel)))
2715d522f475Smrg	ExtendingSelection = 1;
2716d522f475Smrg#endif
2717d522f475Smrg}
2718d522f475Smrg
2719d522f475Smrgvoid
2720d522f475SmrgHandleStartExtend(Widget w,
2721894e0ac8Smrg		  XEvent *event,	/* must be XButtonEvent* */
2722e0a2b6dfSmrg		  String *params,	/* unused */
2723d522f475Smrg		  Cardinal *num_params)		/* unused */
2724d522f475Smrg{
2725956cc18dSsnj    XtermWidget xw;
2726956cc18dSsnj
2727956cc18dSsnj    if ((xw = getXtermWidget(w)) != 0) {
27280bd37d32Smrg	TRACE(("HandleStartExtend\n"));
2729956cc18dSsnj	do_start_extend(xw, event, params, num_params, False);
2730956cc18dSsnj    }
2731d522f475Smrg}
2732d522f475Smrg
2733d522f475Smrgvoid
2734d522f475SmrgHandleKeyboardStartExtend(Widget w,
2735894e0ac8Smrg			  XEvent *event,	/* must be XButtonEvent* */
2736e0a2b6dfSmrg			  String *params,	/* unused */
2737d522f475Smrg			  Cardinal *num_params)		/* unused */
2738d522f475Smrg{
2739956cc18dSsnj    XtermWidget xw;
2740956cc18dSsnj
2741956cc18dSsnj    if ((xw = getXtermWidget(w)) != 0) {
27420bd37d32Smrg	TRACE(("HandleKeyboardStartExtend\n"));
2743956cc18dSsnj	do_start_extend(xw, event, params, num_params, True);
2744956cc18dSsnj    }
2745d522f475Smrg}
2746d522f475Smrg
2747d522f475Smrgvoid
2748e0a2b6dfSmrgScrollSelection(TScreen *screen, int amount, Bool always)
2749d522f475Smrg{
2750d522f475Smrg    int minrow = INX2ROW(screen, -screen->savedlines);
2751d522f475Smrg    int maxrow = INX2ROW(screen, screen->max_row);
2752d522f475Smrg    int maxcol = screen->max_col;
2753d522f475Smrg
2754d522f475Smrg#define scroll_update_one(cell) \
2755d522f475Smrg	(cell)->row += amount; \
2756d522f475Smrg	if ((cell)->row < minrow) { \
2757d522f475Smrg	    (cell)->row = minrow; \
2758d522f475Smrg	    (cell)->col = 0; \
2759d522f475Smrg	} \
2760d522f475Smrg	if ((cell)->row > maxrow) { \
2761d522f475Smrg	    (cell)->row = maxrow; \
2762d522f475Smrg	    (cell)->col = maxcol; \
2763d522f475Smrg	}
2764d522f475Smrg
2765d522f475Smrg    scroll_update_one(&(screen->startRaw));
2766d522f475Smrg    scroll_update_one(&(screen->endRaw));
2767d522f475Smrg    scroll_update_one(&(screen->startSel));
2768d522f475Smrg    scroll_update_one(&(screen->endSel));
2769d522f475Smrg
2770d522f475Smrg    scroll_update_one(&(screen->rawPos));
2771d522f475Smrg
2772d522f475Smrg    /*
2773d522f475Smrg     * If we are told to scroll the selection but it lies outside the scrolling
2774d522f475Smrg     * margins, then that could cause the selection to move (bad).  It is not
2775d522f475Smrg     * simple to fix, because this function is called both for the scrollbar
2776d522f475Smrg     * actions as well as application scrolling.  The 'always' flag is set in
2777d522f475Smrg     * the former case.  The rest of the logic handles the latter.
2778d522f475Smrg     */
2779d522f475Smrg    if (ScrnHaveSelection(screen)) {
2780d522f475Smrg	int adjust;
2781d522f475Smrg
2782d522f475Smrg	adjust = ROW2INX(screen, screen->startH.row);
2783d522f475Smrg	if (always
27840bd37d32Smrg	    || !ScrnHaveRowMargins(screen)
27850bd37d32Smrg	    || ScrnIsRowInMargins(screen, adjust)) {
2786d522f475Smrg	    scroll_update_one(&screen->startH);
2787d522f475Smrg	}
2788d522f475Smrg	adjust = ROW2INX(screen, screen->endH.row);
2789d522f475Smrg	if (always
27900bd37d32Smrg	    || !ScrnHaveRowMargins(screen)
27910bd37d32Smrg	    || ScrnIsRowInMargins(screen, adjust)) {
2792d522f475Smrg	    scroll_update_one(&screen->endH);
2793d522f475Smrg	}
2794d522f475Smrg    }
2795d522f475Smrg
2796d522f475Smrg    screen->startHCoord = Coordinate(screen, &screen->startH);
2797d522f475Smrg    screen->endHCoord = Coordinate(screen, &screen->endH);
2798d522f475Smrg}
2799d522f475Smrg
2800d522f475Smrg/*ARGSUSED*/
2801d522f475Smrgvoid
2802e0a2b6dfSmrgResizeSelection(TScreen *screen GCC_UNUSED, int rows, int cols)
2803d522f475Smrg{
2804d522f475Smrg    rows--;			/* decr to get 0-max */
2805d522f475Smrg    cols--;
2806d522f475Smrg
2807d522f475Smrg    if (screen->startRaw.row > rows)
2808d522f475Smrg	screen->startRaw.row = rows;
2809d522f475Smrg    if (screen->startSel.row > rows)
2810d522f475Smrg	screen->startSel.row = rows;
2811d522f475Smrg    if (screen->endRaw.row > rows)
2812d522f475Smrg	screen->endRaw.row = rows;
2813d522f475Smrg    if (screen->endSel.row > rows)
2814d522f475Smrg	screen->endSel.row = rows;
2815d522f475Smrg    if (screen->rawPos.row > rows)
2816d522f475Smrg	screen->rawPos.row = rows;
2817d522f475Smrg
2818d522f475Smrg    if (screen->startRaw.col > cols)
2819d522f475Smrg	screen->startRaw.col = cols;
2820d522f475Smrg    if (screen->startSel.col > cols)
2821d522f475Smrg	screen->startSel.col = cols;
2822d522f475Smrg    if (screen->endRaw.col > cols)
2823d522f475Smrg	screen->endRaw.col = cols;
2824d522f475Smrg    if (screen->endSel.col > cols)
2825d522f475Smrg	screen->endSel.col = cols;
2826d522f475Smrg    if (screen->rawPos.col > cols)
2827d522f475Smrg	screen->rawPos.col = cols;
2828d522f475Smrg}
2829d522f475Smrg
2830d522f475Smrg#if OPT_WIDE_CHARS
2831d522f475SmrgBool
2832d522f475Smrgiswide(int i)
2833d522f475Smrg{
283420d2c4d2Smrg    return (i == HIDDEN_CHAR) || (WideCells(i) == 2);
2835d522f475Smrg}
2836d522f475Smrg
2837d522f475Smrg#define isWideCell(row, col) iswide((int)XTERM_CELL(row, col))
2838d522f475Smrg#endif
2839d522f475Smrg
2840d522f475Smrgstatic void
2841e0a2b6dfSmrgPointToCELL(TScreen *screen,
2842d522f475Smrg	    int y,
2843d522f475Smrg	    int x,
2844e0a2b6dfSmrg	    CELL *cell)
2845d522f475Smrg/* Convert pixel coordinates to character coordinates.
2846d522f475Smrg   Rows are clipped between firstValidRow and lastValidRow.
2847d522f475Smrg   Columns are clipped between to be 0 or greater, but are not clipped to some
2848d522f475Smrg       maximum value. */
2849d522f475Smrg{
2850d522f475Smrg    cell->row = (y - screen->border) / FontHeight(screen);
2851d522f475Smrg    if (cell->row < screen->firstValidRow)
2852d522f475Smrg	cell->row = screen->firstValidRow;
2853d522f475Smrg    else if (cell->row > screen->lastValidRow)
2854d522f475Smrg	cell->row = screen->lastValidRow;
2855d522f475Smrg    cell->col = (x - OriginX(screen)) / FontWidth(screen);
2856d522f475Smrg    if (cell->col < 0)
2857d522f475Smrg	cell->col = 0;
2858d522f475Smrg    else if (cell->col > MaxCols(screen)) {
2859d522f475Smrg	cell->col = MaxCols(screen);
2860d522f475Smrg    }
2861d522f475Smrg#if OPT_WIDE_CHARS
2862d522f475Smrg    /*
2863d522f475Smrg     * If we got a click on the right half of a doublewidth character,
2864d522f475Smrg     * pretend it happened on the left half.
2865d522f475Smrg     */
2866d522f475Smrg    if (cell->col > 0
2867d522f475Smrg	&& isWideCell(cell->row, cell->col - 1)
2868d522f475Smrg	&& (XTERM_CELL(cell->row, cell->col) == HIDDEN_CHAR)) {
2869d522f475Smrg	cell->col -= 1;
2870d522f475Smrg    }
2871d522f475Smrg#endif
2872d522f475Smrg}
2873d522f475Smrg
2874d522f475Smrg/*
2875d522f475Smrg * Find the last column at which text was drawn on the given row.
2876d522f475Smrg */
2877d522f475Smrgstatic int
287801037d57SmrgLastTextCol(TScreen *screen, CLineData *ld, int row)
2879d522f475Smrg{
288020d2c4d2Smrg    int i = -1;
2881d522f475Smrg
288220d2c4d2Smrg    if (ld != 0) {
288320d2c4d2Smrg	if (okScrnRow(screen, row)) {
28842e4f8982Smrg	    const IAttr *ch;
288520d2c4d2Smrg	    for (i = screen->max_col,
288620d2c4d2Smrg		 ch = ld->attribs + i;
288720d2c4d2Smrg		 i >= 0 && !(*ch & CHARDRAWN);
288820d2c4d2Smrg		 ch--, i--) {
288920d2c4d2Smrg		;
289020d2c4d2Smrg	    }
2891d522f475Smrg#if OPT_DEC_CHRSET
289220d2c4d2Smrg	    if (CSET_DOUBLE(GetLineDblCS(ld))) {
289320d2c4d2Smrg		i *= 2;
289420d2c4d2Smrg	    }
2895d522f475Smrg#endif
289620d2c4d2Smrg	}
2897d522f475Smrg    }
2898d522f475Smrg    return (i);
2899d522f475Smrg}
2900d522f475Smrg
2901d522f475Smrg#if !OPT_WIDE_CHARS
2902d522f475Smrg/*
2903d522f475Smrg** double click table for cut and paste in 8 bits
2904d522f475Smrg**
2905d522f475Smrg** This table is divided in four parts :
2906d522f475Smrg**
2907d522f475Smrg**	- control characters	[0,0x1f] U [0x80,0x9f]
2908d522f475Smrg**	- separators		[0x20,0x3f] U [0xa0,0xb9]
2909d522f475Smrg**	- binding characters	[0x40,0x7f] U [0xc0,0xff]
2910d522f475Smrg**	- exceptions
2911d522f475Smrg*/
2912d522f475Smrg/* *INDENT-OFF* */
2913d522f475Smrgstatic int charClass[256] =
2914d522f475Smrg{
2915d522f475Smrg/* NUL  SOH  STX  ETX  EOT  ENQ  ACK  BEL */
2916d522f475Smrg    32,  1,    1,   1,   1,   1,   1,   1,
29170bd37d32Smrg/*  BS   HT   NL   VT   FF   CR   SO   SI */
2918d522f475Smrg     1,  32,   1,   1,   1,   1,   1,   1,
2919d522f475Smrg/* DLE  DC1  DC2  DC3  DC4  NAK  SYN  ETB */
2920d522f475Smrg     1,   1,   1,   1,   1,   1,   1,   1,
2921d522f475Smrg/* CAN   EM  SUB  ESC   FS   GS   RS   US */
2922d522f475Smrg     1,   1,   1,   1,   1,   1,   1,   1,
2923d522f475Smrg/*  SP    !    "    #    $    %    &    ' */
2924d522f475Smrg    32,  33,  34,  35,  36,  37,  38,  39,
2925d522f475Smrg/*   (    )    *    +    ,    -    .    / */
2926d522f475Smrg    40,  41,  42,  43,  44,  45,  46,  47,
2927d522f475Smrg/*   0    1    2    3    4    5    6    7 */
2928d522f475Smrg    48,  48,  48,  48,  48,  48,  48,  48,
2929d522f475Smrg/*   8    9    :    ;    <    =    >    ? */
2930d522f475Smrg    48,  48,  58,  59,  60,  61,  62,  63,
2931d522f475Smrg/*   @    A    B    C    D    E    F    G */
2932d522f475Smrg    64,  48,  48,  48,  48,  48,  48,  48,
2933d522f475Smrg/*   H    I    J    K    L    M    N    O */
2934d522f475Smrg    48,  48,  48,  48,  48,  48,  48,  48,
2935d522f475Smrg/*   P    Q    R    S    T    U    V    W */
2936d522f475Smrg    48,  48,  48,  48,  48,  48,  48,  48,
2937d522f475Smrg/*   X    Y    Z    [    \    ]    ^    _ */
2938d522f475Smrg    48,  48,  48,  91,  92,  93,  94,  48,
2939d522f475Smrg/*   `    a    b    c    d    e    f    g */
2940d522f475Smrg    96,  48,  48,  48,  48,  48,  48,  48,
2941d522f475Smrg/*   h    i    j    k    l    m    n    o */
2942d522f475Smrg    48,  48,  48,  48,  48,  48,  48,  48,
2943d522f475Smrg/*   p    q    r    s    t    u    v    w */
2944d522f475Smrg    48,  48,  48,  48,  48,  48,  48,  48,
2945d522f475Smrg/*   x    y    z    {    |    }    ~  DEL */
2946d522f475Smrg    48,  48,  48, 123, 124, 125, 126,   1,
2947d522f475Smrg/* x80  x81  x82  x83  IND  NEL  SSA  ESA */
2948d522f475Smrg    1,    1,   1,   1,   1,   1,   1,   1,
2949d522f475Smrg/* HTS  HTJ  VTS  PLD  PLU   RI  SS2  SS3 */
2950d522f475Smrg    1,    1,   1,   1,   1,   1,   1,   1,
2951d522f475Smrg/* DCS  PU1  PU2  STS  CCH   MW  SPA  EPA */
2952d522f475Smrg    1,    1,   1,   1,   1,   1,   1,   1,
2953d522f475Smrg/* x98  x99  x9A  CSI   ST  OSC   PM  APC */
2954d522f475Smrg    1,    1,   1,   1,   1,   1,   1,   1,
2955d522f475Smrg/*   -    i   c/    L   ox   Y-    |   So */
2956d522f475Smrg    160, 161, 162, 163, 164, 165, 166, 167,
2957d522f475Smrg/*  ..   c0   ip   <<    _        R0    - */
2958d522f475Smrg    168, 169, 170, 171, 172, 173, 174, 175,
2959d522f475Smrg/*   o   +-    2    3    '    u   q|    . */
2960d522f475Smrg    176, 177, 178, 179, 180, 181, 182, 183,
2961d522f475Smrg/*   ,    1    2   >>  1/4  1/2  3/4    ? */
2962d522f475Smrg    184, 185, 186, 187, 188, 189, 190, 191,
2963d522f475Smrg/*  A`   A'   A^   A~   A:   Ao   AE   C, */
2964d522f475Smrg     48,  48,  48,  48,  48,  48,  48,  48,
2965d522f475Smrg/*  E`   E'   E^   E:   I`   I'   I^   I: */
2966d522f475Smrg     48,  48,  48,  48,  48,  48,  48,  48,
2967d522f475Smrg/*  D-   N~   O`   O'   O^   O~   O:    X */
2968d522f475Smrg     48,  48,  48,  48,  48,  48,  48, 215,
2969d522f475Smrg/*  O/   U`   U'   U^   U:   Y'    P    B */
2970d522f475Smrg     48,  48,  48,  48,  48,  48,  48,  48,
2971d522f475Smrg/*  a`   a'   a^   a~   a:   ao   ae   c, */
2972d522f475Smrg     48,  48,  48,  48,  48,  48,  48,  48,
2973d522f475Smrg/*  e`   e'   e^   e:    i`  i'   i^   i: */
2974d522f475Smrg     48,  48,  48,  48,  48,  48,  48,  48,
2975d522f475Smrg/*   d   n~   o`   o'   o^   o~   o:   -: */
2976d522f475Smrg     48,  48,  48,  48,  48,  48,  48, 247,
2977d522f475Smrg/*  o/   u`   u'   u^   u:   y'    P   y: */
2978d522f475Smrg     48,  48,  48,  48,  48,  48,  48,  48};
2979d522f475Smrg/* *INDENT-ON* */
2980d522f475Smrg
2981d522f475Smrgint
2982d522f475SmrgSetCharacterClassRange(int low,	/* in range of [0..255] */
2983d522f475Smrg		       int high,
2984d522f475Smrg		       int value)	/* arbitrary */
2985d522f475Smrg{
2986d522f475Smrg
2987d522f475Smrg    if (low < 0 || high > 255 || high < low)
2988d522f475Smrg	return (-1);
2989d522f475Smrg
2990d522f475Smrg    for (; low <= high; low++)
2991d522f475Smrg	charClass[low] = value;
2992d522f475Smrg
2993d522f475Smrg    return (0);
2994d522f475Smrg}
2995d522f475Smrg#endif
2996d522f475Smrg
2997d522f475Smrgstatic int
2998e0a2b6dfSmrgclass_of(LineData *ld, CELL *cell)
2999d522f475Smrg{
3000d522f475Smrg    CELL temp = *cell;
30010bd37d32Smrg    int result = 0;
3002d522f475Smrg
3003d522f475Smrg#if OPT_DEC_CHRSET
3004956cc18dSsnj    if (CSET_DOUBLE(GetLineDblCS(ld))) {
3005d522f475Smrg	temp.col /= 2;
3006d522f475Smrg    }
3007d522f475Smrg#endif
30080bd37d32Smrg    if (temp.col < (int) ld->lineSize)
30090bd37d32Smrg	result = CharacterClass((int) (ld->charData[temp.col]));
30100bd37d32Smrg    return result;
3011d522f475Smrg}
3012956cc18dSsnj
3013956cc18dSsnj#if OPT_WIDE_CHARS
3014956cc18dSsnj#define CClassSelects(name, cclass) \
3015956cc18dSsnj	 (CClassOf(name) == cclass \
3016956cc18dSsnj	 || XTERM_CELL(screen->name.row, screen->name.col) == HIDDEN_CHAR)
3017d522f475Smrg#else
3018956cc18dSsnj#define CClassSelects(name, cclass) \
3019956cc18dSsnj	 (class_of(ld.name, &((screen->name))) == cclass)
3020d522f475Smrg#endif
3021d522f475Smrg
3022956cc18dSsnj#define CClassOf(name) class_of(ld.name, &((screen->name)))
3023956cc18dSsnj
3024d522f475Smrg/*
3025d522f475Smrg * If the given column is past the end of text on the given row, bump to the
3026d522f475Smrg * beginning of the next line.
3027d522f475Smrg */
3028d522f475Smrgstatic Boolean
3029e0a2b6dfSmrgokPosition(TScreen *screen,
3030e0a2b6dfSmrg	   LineData **ld,
3031e0a2b6dfSmrg	   CELL *cell)
3032d522f475Smrg{
303320d2c4d2Smrg    Boolean result = True;
303420d2c4d2Smrg
303520d2c4d2Smrg    if (cell->row > screen->max_row) {
303620d2c4d2Smrg	result = False;
303720d2c4d2Smrg    } else if (cell->col > (LastTextCol(screen, *ld, cell->row) + 1)) {
303820d2c4d2Smrg	if (cell->row < screen->max_row) {
303920d2c4d2Smrg	    cell->col = 0;
304020d2c4d2Smrg	    *ld = GET_LINEDATA(screen, ++cell->row);
304120d2c4d2Smrg	    result = False;
304220d2c4d2Smrg	}
3043d522f475Smrg    }
304420d2c4d2Smrg    return result;
3045d522f475Smrg}
3046d522f475Smrg
3047d522f475Smrgstatic void
3048e0a2b6dfSmrgtrimLastLine(TScreen *screen,
3049e0a2b6dfSmrg	     LineData **ld,
3050e0a2b6dfSmrg	     CELL *last)
3051d522f475Smrg{
305220d2c4d2Smrg    if (screen->cutNewline && last->row < screen->max_row) {
3053d522f475Smrg	last->col = 0;
3054956cc18dSsnj	*ld = GET_LINEDATA(screen, ++last->row);
3055d522f475Smrg    } else {
3056956cc18dSsnj	last->col = LastTextCol(screen, *ld, last->row) + 1;
3057d522f475Smrg    }
3058d522f475Smrg}
3059d522f475Smrg
3060d522f475Smrg#if OPT_SELECT_REGEX
3061d522f475Smrg/*
3062d522f475Smrg * Returns the first row of a wrapped line.
3063d522f475Smrg */
3064d522f475Smrgstatic int
3065e0a2b6dfSmrgfirstRowOfLine(TScreen *screen, int row, Bool visible)
3066d522f475Smrg{
3067956cc18dSsnj    LineData *ld = 0;
3068d522f475Smrg    int limit = visible ? 0 : -screen->savedlines;
3069d522f475Smrg
3070d522f475Smrg    while (row > limit &&
3071956cc18dSsnj	   (ld = GET_LINEDATA(screen, row - 1)) != 0 &&
3072956cc18dSsnj	   LineTstWrapped(ld)) {
3073d522f475Smrg	--row;
3074956cc18dSsnj    }
3075d522f475Smrg    return row;
3076d522f475Smrg}
3077d522f475Smrg
3078d522f475Smrg/*
3079d522f475Smrg * Returns the last row of a wrapped line.
3080d522f475Smrg */
3081d522f475Smrgstatic int
3082e0a2b6dfSmrglastRowOfLine(TScreen *screen, int row)
3083d522f475Smrg{
3084956cc18dSsnj    LineData *ld;
3085956cc18dSsnj
3086d522f475Smrg    while (row < screen->max_row &&
3087956cc18dSsnj	   (ld = GET_LINEDATA(screen, row)) != 0 &&
3088956cc18dSsnj	   LineTstWrapped(ld)) {
3089d522f475Smrg	++row;
3090956cc18dSsnj    }
3091d522f475Smrg    return row;
3092d522f475Smrg}
3093d522f475Smrg
3094d522f475Smrg/*
3095d522f475Smrg * Returns the number of cells on the range of rows.
3096d522f475Smrg */
3097d522f475Smrgstatic unsigned
3098e0a2b6dfSmrglengthOfLines(TScreen *screen, int firstRow, int lastRow)
3099d522f475Smrg{
3100d522f475Smrg    unsigned length = 0;
3101d522f475Smrg    int n;
3102d522f475Smrg
3103d522f475Smrg    for (n = firstRow; n <= lastRow; ++n) {
3104956cc18dSsnj	LineData *ld = GET_LINEDATA(screen, n);
3105956cc18dSsnj	int value = LastTextCol(screen, ld, n);
3106d522f475Smrg	if (value >= 0)
31072eaa94a1Schristos	    length += (unsigned) (value + 1);
3108d522f475Smrg    }
3109d522f475Smrg    return length;
3110d522f475Smrg}
3111d522f475Smrg
3112d522f475Smrg/*
3113d522f475Smrg * Make a copy of the wrapped-line which corresponds to the given row as a
3114d522f475Smrg * string of bytes.  Construct an index for the columns from the beginning of
3115d522f475Smrg * the line.
3116d522f475Smrg */
3117d522f475Smrgstatic char *
3118e0a2b6dfSmrgmake_indexed_text(TScreen *screen, int row, unsigned length, int *indexed)
3119d522f475Smrg{
3120d522f475Smrg    Char *result = 0;
312120d2c4d2Smrg    size_t need = (length + 1);
3122d522f475Smrg
3123d522f475Smrg    /*
3124d522f475Smrg     * Get a quick upper bound to the number of bytes needed, if the whole
3125d522f475Smrg     * string were UTF-8.
3126d522f475Smrg     */
3127d522f475Smrg    if_OPT_WIDE_CHARS(screen, {
3128956cc18dSsnj	need *= ((screen->lineExtra + 1) * 6);
3129d522f475Smrg    });
3130d522f475Smrg
3131d522f475Smrg    if ((result = TypeCallocN(Char, need + 1)) != 0) {
3132956cc18dSsnj	LineData *ld = GET_LINEDATA(screen, row);
3133d522f475Smrg	unsigned used = 0;
3134d522f475Smrg	Char *last = result;
3135d522f475Smrg
3136d522f475Smrg	do {
3137d522f475Smrg	    int col = 0;
3138956cc18dSsnj	    int limit = LastTextCol(screen, ld, row);
3139d522f475Smrg
3140d522f475Smrg	    while (col <= limit) {
3141d522f475Smrg		Char *next = last;
3142956cc18dSsnj		unsigned data = ld->charData[col];
3143d522f475Smrg
31440bd37d32Smrg		assert(col < (int) ld->lineSize);
3145d522f475Smrg		/* some internal points may not be drawn */
3146d522f475Smrg		if (data == 0)
3147d522f475Smrg		    data = ' ';
3148d522f475Smrg
3149d522f475Smrg		if_WIDE_OR_NARROW(screen, {
3150d522f475Smrg		    next = convertToUTF8(last, data);
3151d522f475Smrg		}
3152d522f475Smrg		, {
3153d522f475Smrg		    *next++ = CharOf(data);
3154d522f475Smrg		});
3155d522f475Smrg
3156d522f475Smrg		if_OPT_WIDE_CHARS(screen, {
3157956cc18dSsnj		    size_t off;
3158956cc18dSsnj		    for_each_combData(off, ld) {
3159956cc18dSsnj			data = ld->combData[off][col];
3160956cc18dSsnj			if (data == 0)
3161d522f475Smrg			    break;
3162d522f475Smrg			next = convertToUTF8(next, data);
3163d522f475Smrg		    }
3164d522f475Smrg		});
3165d522f475Smrg
316620d2c4d2Smrg		indexed[used] = (int) (last - result);
3167d522f475Smrg		*next = 0;
3168d522f475Smrg		/* TRACE(("index[%d.%d] %d:%s\n", row, used, indexed[used], last)); */
3169d522f475Smrg		last = next;
3170d522f475Smrg		++used;
3171d522f475Smrg		++col;
317220d2c4d2Smrg		indexed[used] = (int) (next - result);
3173d522f475Smrg	    }
3174d522f475Smrg	} while (used < length &&
3175956cc18dSsnj		 LineTstWrapped(ld) &&
3176956cc18dSsnj		 (ld = GET_LINEDATA(screen, ++row)) != 0 &&
3177956cc18dSsnj		 row < screen->max_row);
3178d522f475Smrg    }
3179d522f475Smrg    /* TRACE(("result:%s\n", result)); */
3180d522f475Smrg    return (char *) result;
3181d522f475Smrg}
3182d522f475Smrg
3183d522f475Smrg/*
3184d522f475Smrg * Find the column given an offset into the character string by using the
3185d522f475Smrg * index constructed in make_indexed_text().
3186d522f475Smrg */
3187d522f475Smrgstatic int
3188d522f475SmrgindexToCol(int *indexed, int len, int off)
3189d522f475Smrg{
3190d522f475Smrg    int col = 0;
3191d522f475Smrg    while (indexed[col] < len) {
3192d522f475Smrg	if (indexed[col] >= off)
3193d522f475Smrg	    break;
3194d522f475Smrg	++col;
3195d522f475Smrg    }
3196d522f475Smrg    return col;
3197d522f475Smrg}
3198d522f475Smrg
3199d522f475Smrg/*
3200d522f475Smrg * Given a row number, and a column offset from that (which may be wrapped),
3201d522f475Smrg * set the cell to the actual row/column values.
3202d522f475Smrg */
3203d522f475Smrgstatic void
3204e0a2b6dfSmrgcolumnToCell(TScreen *screen, int row, int col, CELL *cell)
3205d522f475Smrg{
3206d522f475Smrg    while (row < screen->max_row) {
320701037d57Smrg	CLineData *ld = GET_LINEDATA(screen, row);
3208956cc18dSsnj	int last = LastTextCol(screen, ld, row);
3209d522f475Smrg
3210d522f475Smrg	/* TRACE(("last(%d) = %d, have %d\n", row, last, col)); */
3211d522f475Smrg	if (col <= last) {
3212d522f475Smrg	    break;
3213d522f475Smrg	}
3214d522f475Smrg	/*
3215d522f475Smrg	 * Stop if the current row does not wrap (does not continue the current
3216d522f475Smrg	 * line).
3217d522f475Smrg	 */
3218956cc18dSsnj	if (!LineTstWrapped(ld)) {
3219d522f475Smrg	    col = last + 1;
3220d522f475Smrg	    break;
3221d522f475Smrg	}
3222d522f475Smrg	col -= (last + 1);
3223d522f475Smrg	++row;
3224d522f475Smrg    }
3225d522f475Smrg    if (col < 0)
3226d522f475Smrg	col = 0;
3227d522f475Smrg    cell->row = row;
3228d522f475Smrg    cell->col = col;
3229d522f475Smrg}
3230d522f475Smrg
3231d522f475Smrg/*
3232d522f475Smrg * Given a cell, find the corresponding column offset.
3233d522f475Smrg */
3234d522f475Smrgstatic int
3235e0a2b6dfSmrgcellToColumn(TScreen *screen, CELL *cell)
3236d522f475Smrg{
323701037d57Smrg    CLineData *ld = 0;
3238d522f475Smrg    int col = cell->col;
3239d522f475Smrg    int row = firstRowOfLine(screen, cell->row, False);
3240d522f475Smrg    while (row < cell->row) {
3241956cc18dSsnj	ld = GET_LINEDATA(screen, row);
3242956cc18dSsnj	col += LastTextCol(screen, ld, row++);
3243d522f475Smrg    }
3244956cc18dSsnj#if OPT_DEC_CHRSET
3245956cc18dSsnj    if (ld == 0)
3246956cc18dSsnj	ld = GET_LINEDATA(screen, row);
3247956cc18dSsnj    if (CSET_DOUBLE(GetLineDblCS(ld)))
3248956cc18dSsnj	col /= 2;
3249956cc18dSsnj#endif
3250d522f475Smrg    return col;
3251d522f475Smrg}
3252d522f475Smrg
3253d522f475Smrgstatic void
3254e0a2b6dfSmrgdo_select_regex(TScreen *screen, CELL *startc, CELL *endc)
3255d522f475Smrg{
3256956cc18dSsnj    LineData *ld = GET_LINEDATA(screen, startc->row);
3257d522f475Smrg    int inx = ((screen->numberOfClicks - 1) % screen->maxClicks);
3258d522f475Smrg    char *expr = screen->selectExpr[inx];
3259d522f475Smrg    regex_t preg;
3260d522f475Smrg    regmatch_t match;
3261d522f475Smrg
326201037d57Smrg    TRACE(("Select_REGEX[%d]:%s\n", inx, NonNull(expr)));
3263956cc18dSsnj    if (okPosition(screen, &ld, startc) && expr != 0) {
3264d522f475Smrg	if (regcomp(&preg, expr, REG_EXTENDED) == 0) {
3265d522f475Smrg	    int firstRow = firstRowOfLine(screen, startc->row, True);
3266d522f475Smrg	    int lastRow = lastRowOfLine(screen, firstRow);
3267d522f475Smrg	    unsigned size = lengthOfLines(screen, firstRow, lastRow);
3268d522f475Smrg	    int actual = cellToColumn(screen, startc);
32692e4f8982Smrg	    int *indexed;
3270d522f475Smrg
3271d522f475Smrg	    TRACE(("regcomp ok rows %d..%d bytes %d\n",
3272d522f475Smrg		   firstRow, lastRow, size));
3273d522f475Smrg
3274d522f475Smrg	    if ((indexed = TypeCallocN(int, size + 1)) != 0) {
32752e4f8982Smrg		char *search;
3276d522f475Smrg		if ((search = make_indexed_text(screen,
3277d522f475Smrg						firstRow,
3278d522f475Smrg						size,
3279d522f475Smrg						indexed)) != 0) {
32802eaa94a1Schristos		    int len = (int) strlen(search);
3281d522f475Smrg		    int col;
3282d522f475Smrg		    int best_col = -1;
3283d522f475Smrg		    int best_len = -1;
3284d522f475Smrg
3285d522f475Smrg		    for (col = 0; indexed[col] < len; ++col) {
3286d522f475Smrg			if (regexec(&preg,
3287d522f475Smrg				    search + indexed[col],
328820d2c4d2Smrg				    (size_t) 1, &match, 0) == 0) {
3289894e0ac8Smrg			    int start_inx = (int) (match.rm_so + indexed[col]);
3290894e0ac8Smrg			    int finis_inx = (int) (match.rm_eo + indexed[col]);
3291d522f475Smrg			    int start_col = indexToCol(indexed, len, start_inx);
3292d522f475Smrg			    int finis_col = indexToCol(indexed, len, finis_inx);
3293d522f475Smrg
3294d522f475Smrg			    if (start_col <= actual &&
3295d522f475Smrg				actual < finis_col) {
3296d522f475Smrg				int test = finis_col - start_col;
3297d522f475Smrg				if (best_len < test) {
3298d522f475Smrg				    best_len = test;
3299d522f475Smrg				    best_col = start_col;
3300d522f475Smrg				    TRACE(("match column %d len %d\n",
3301d522f475Smrg					   best_col,
3302d522f475Smrg					   best_len));
3303d522f475Smrg				}
3304d522f475Smrg			    }
3305d522f475Smrg			}
3306d522f475Smrg		    }
3307d522f475Smrg		    if (best_col >= 0) {
3308d522f475Smrg			int best_nxt = best_col + best_len;
3309d522f475Smrg			columnToCell(screen, firstRow, best_col, startc);
3310d522f475Smrg			columnToCell(screen, firstRow, best_nxt, endc);
3311d522f475Smrg			TRACE(("search::%s\n", search));
3312d522f475Smrg			TRACE(("indexed:%d..%d -> %d..%d\n",
3313d522f475Smrg			       best_col, best_nxt,
3314d522f475Smrg			       indexed[best_col],
3315d522f475Smrg			       indexed[best_nxt]));
3316d522f475Smrg			TRACE(("matched:%d:%s\n",
3317d522f475Smrg			       indexed[best_nxt] + 1 -
3318d522f475Smrg			       indexed[best_col],
3319956cc18dSsnj			       visibleChars((Char *) (search + indexed[best_col]),
3320d522f475Smrg					    (unsigned) (indexed[best_nxt] +
3321d522f475Smrg							1 -
3322d522f475Smrg							indexed[best_col]))));
3323d522f475Smrg		    }
3324d522f475Smrg		    free(search);
3325d522f475Smrg		}
3326d522f475Smrg		free(indexed);
3327956cc18dSsnj#if OPT_DEC_CHRSET
3328956cc18dSsnj		if ((ld = GET_LINEDATA(screen, startc->row)) != 0) {
3329956cc18dSsnj		    if (CSET_DOUBLE(GetLineDblCS(ld)))
3330956cc18dSsnj			startc->col *= 2;
3331956cc18dSsnj		}
3332956cc18dSsnj		if ((ld = GET_LINEDATA(screen, endc->row)) != 0) {
3333956cc18dSsnj		    if (CSET_DOUBLE(GetLineDblCS(ld)))
3334956cc18dSsnj			endc->col *= 2;
3335956cc18dSsnj		}
3336956cc18dSsnj#endif
3337d522f475Smrg	    }
3338d522f475Smrg	    regfree(&preg);
3339d522f475Smrg	}
3340d522f475Smrg    }
3341d522f475Smrg}
3342d522f475Smrg#endif /* OPT_SELECT_REGEX */
3343d522f475Smrg
3344956cc18dSsnj#define InitRow(name) \
3345956cc18dSsnj	ld.name = GET_LINEDATA(screen, screen->name.row)
3346956cc18dSsnj
3347956cc18dSsnj#define NextRow(name) \
3348956cc18dSsnj	ld.name = GET_LINEDATA(screen, ++screen->name.row)
3349956cc18dSsnj
3350956cc18dSsnj#define PrevRow(name) \
3351956cc18dSsnj	ld.name = GET_LINEDATA(screen, --screen->name.row)
3352956cc18dSsnj
335320d2c4d2Smrg#define MoreRows(name) \
335420d2c4d2Smrg	(screen->name.row < screen->max_row)
335520d2c4d2Smrg
3356956cc18dSsnj#define isPrevWrapped(name) \
3357956cc18dSsnj	(screen->name.row > 0 \
3358956cc18dSsnj	   && (ltmp = GET_LINEDATA(screen, screen->name.row - 1)) != 0 \
3359956cc18dSsnj	   && LineTstWrapped(ltmp))
3360956cc18dSsnj
3361d522f475Smrg/*
3362d522f475Smrg * sets startSel endSel
3363d522f475Smrg * ensuring that they have legal values
3364d522f475Smrg */
3365d522f475Smrgstatic void
3366d522f475SmrgComputeSelect(XtermWidget xw,
3367e0a2b6dfSmrg	      CELL *startc,
3368e0a2b6dfSmrg	      CELL *endc,
3369d522f475Smrg	      Bool extend)
3370d522f475Smrg{
3371956cc18dSsnj    TScreen *screen = TScreenOf(xw);
3372956cc18dSsnj
3373d522f475Smrg    int cclass;
3374d522f475Smrg    CELL first = *startc;
3375d522f475Smrg    CELL last = *endc;
3376956cc18dSsnj    Boolean ignored = False;
3377956cc18dSsnj
3378956cc18dSsnj    struct {
3379956cc18dSsnj	LineData *startSel;
3380956cc18dSsnj	LineData *endSel;
3381956cc18dSsnj    } ld;
3382956cc18dSsnj    LineData *ltmp;
3383d522f475Smrg
3384d522f475Smrg    TRACE(("ComputeSelect(startRow=%d, startCol=%d, endRow=%d, endCol=%d, %sextend)\n",
3385d522f475Smrg	   first.row, first.col,
3386d522f475Smrg	   last.row, last.col,
3387d522f475Smrg	   extend ? "" : "no"));
3388d522f475Smrg
3389d522f475Smrg#if OPT_WIDE_CHARS
3390d522f475Smrg    if (first.col > 1
3391d522f475Smrg	&& isWideCell(first.row, first.col - 1)
3392d522f475Smrg	&& XTERM_CELL(first.row, first.col - 0) == HIDDEN_CHAR) {
339320d2c4d2Smrg	TRACE(("Adjusting start. Changing downwards from %i.\n", first.col));
3394d522f475Smrg	first.col -= 1;
3395d522f475Smrg	if (last.col == (first.col + 1))
3396d522f475Smrg	    last.col--;
3397d522f475Smrg    }
3398d522f475Smrg
3399d522f475Smrg    if (last.col > 1
3400d522f475Smrg	&& isWideCell(last.row, last.col - 1)
3401d522f475Smrg	&& XTERM_CELL(last.row, last.col) == HIDDEN_CHAR) {
3402d522f475Smrg	last.col += 1;
3403d522f475Smrg    }
3404d522f475Smrg#endif
3405d522f475Smrg
3406d522f475Smrg    if (Coordinate(screen, &first) <= Coordinate(screen, &last)) {
3407d522f475Smrg	screen->startSel = screen->startRaw = first;
3408d522f475Smrg	screen->endSel = screen->endRaw = last;
3409d522f475Smrg    } else {			/* Swap them */
3410d522f475Smrg	screen->startSel = screen->startRaw = last;
3411d522f475Smrg	screen->endSel = screen->endRaw = first;
3412d522f475Smrg    }
3413d522f475Smrg
3414956cc18dSsnj    InitRow(startSel);
3415956cc18dSsnj    InitRow(endSel);
3416956cc18dSsnj
3417d522f475Smrg    switch (screen->selectUnit) {
3418d522f475Smrg    case Select_CHAR:
3419956cc18dSsnj	(void) okPosition(screen, &(ld.startSel), &(screen->startSel));
3420956cc18dSsnj	(void) okPosition(screen, &(ld.endSel), &(screen->endSel));
3421d522f475Smrg	break;
3422d522f475Smrg
3423d522f475Smrg    case Select_WORD:
3424d522f475Smrg	TRACE(("Select_WORD\n"));
3425956cc18dSsnj	if (okPosition(screen, &(ld.startSel), &(screen->startSel))) {
3426956cc18dSsnj	    cclass = CClassOf(startSel);
3427d522f475Smrg	    do {
3428d522f475Smrg		--screen->startSel.col;
3429956cc18dSsnj		if (screen->startSel.col < 0
3430956cc18dSsnj		    && isPrevWrapped(startSel)) {
3431956cc18dSsnj		    PrevRow(startSel);
3432956cc18dSsnj		    screen->startSel.col = LastTextCol(screen, ld.startSel, screen->startSel.row);
3433d522f475Smrg		}
3434d522f475Smrg	    } while (screen->startSel.col >= 0
3435956cc18dSsnj		     && CClassSelects(startSel, cclass));
3436d522f475Smrg	    ++screen->startSel.col;
3437d522f475Smrg	}
3438d522f475Smrg#if OPT_WIDE_CHARS
3439d522f475Smrg	if (screen->startSel.col
3440d522f475Smrg	    && XTERM_CELL(screen->startSel.row,
3441d522f475Smrg			  screen->startSel.col) == HIDDEN_CHAR)
3442d522f475Smrg	    screen->startSel.col++;
3443d522f475Smrg#endif
3444d522f475Smrg
3445956cc18dSsnj	if (okPosition(screen, &(ld.endSel), &(screen->endSel))) {
34462e4f8982Smrg	    int length = LastTextCol(screen, ld.endSel, screen->endSel.row);
3447956cc18dSsnj	    cclass = CClassOf(endSel);
3448d522f475Smrg	    do {
3449d522f475Smrg		++screen->endSel.col;
3450d522f475Smrg		if (screen->endSel.col > length
3451956cc18dSsnj		    && LineTstWrapped(ld.endSel)) {
345220d2c4d2Smrg		    if (!MoreRows(endSel))
345320d2c4d2Smrg			break;
3454d522f475Smrg		    screen->endSel.col = 0;
3455956cc18dSsnj		    NextRow(endSel);
3456956cc18dSsnj		    length = LastTextCol(screen, ld.endSel, screen->endSel.row);
3457d522f475Smrg		}
3458d522f475Smrg	    } while (screen->endSel.col <= length
3459956cc18dSsnj		     && CClassSelects(endSel, cclass));
3460d522f475Smrg	    /* Word-select selects if pointing to any char in "word",
3461d522f475Smrg	     * especially note that it includes the last character in a word.
3462d522f475Smrg	     * So we do no --endSel.col and do special eol handling.
3463d522f475Smrg	     */
346420d2c4d2Smrg	    if (screen->endSel.col > length + 1
346520d2c4d2Smrg		&& MoreRows(endSel)) {
3466d522f475Smrg		screen->endSel.col = 0;
3467956cc18dSsnj		NextRow(endSel);
3468d522f475Smrg	    }
3469d522f475Smrg	}
3470d522f475Smrg#if OPT_WIDE_CHARS
3471d522f475Smrg	if (screen->endSel.col
3472d522f475Smrg	    && XTERM_CELL(screen->endSel.row,
3473d522f475Smrg			  screen->endSel.col) == HIDDEN_CHAR)
3474d522f475Smrg	    screen->endSel.col++;
3475d522f475Smrg#endif
3476d522f475Smrg
3477d522f475Smrg	screen->saveStartW = screen->startSel;
3478d522f475Smrg	break;
3479d522f475Smrg
3480d522f475Smrg    case Select_LINE:
3481d522f475Smrg	TRACE(("Select_LINE\n"));
348220d2c4d2Smrg	while (LineTstWrapped(ld.endSel)
348320d2c4d2Smrg	       && MoreRows(endSel)) {
3484956cc18dSsnj	    NextRow(endSel);
3485d522f475Smrg	}
3486d522f475Smrg	if (screen->cutToBeginningOfLine
3487d522f475Smrg	    || screen->startSel.row < screen->saveStartW.row) {
3488d522f475Smrg	    screen->startSel.col = 0;
3489956cc18dSsnj	    while (isPrevWrapped(startSel)) {
3490956cc18dSsnj		PrevRow(startSel);
3491d522f475Smrg	    }
3492d522f475Smrg	} else if (!extend) {
3493d522f475Smrg	    if ((first.row < screen->saveStartW.row)
3494d522f475Smrg		|| (isSameRow(&first, &(screen->saveStartW))
3495d522f475Smrg		    && first.col < screen->saveStartW.col)) {
3496d522f475Smrg		screen->startSel.col = 0;
3497956cc18dSsnj		while (isPrevWrapped(startSel)) {
3498956cc18dSsnj		    PrevRow(startSel);
3499d522f475Smrg		}
3500d522f475Smrg	    } else {
3501d522f475Smrg		screen->startSel = screen->saveStartW;
3502d522f475Smrg	    }
3503d522f475Smrg	}
3504956cc18dSsnj	trimLastLine(screen, &(ld.endSel), &(screen->endSel));
3505d522f475Smrg	break;
3506d522f475Smrg
3507d522f475Smrg    case Select_GROUP:		/* paragraph */
3508d522f475Smrg	TRACE(("Select_GROUP\n"));
3509956cc18dSsnj	if (okPosition(screen, &(ld.startSel), &(screen->startSel))) {
3510d522f475Smrg	    /* scan backward for beginning of group */
3511d522f475Smrg	    while (screen->startSel.row > 0 &&
3512956cc18dSsnj		   (LastTextCol(screen, ld.startSel, screen->startSel.row -
3513956cc18dSsnj				1) > 0 ||
3514956cc18dSsnj		    isPrevWrapped(startSel))) {
3515956cc18dSsnj		PrevRow(startSel);
3516d522f475Smrg	    }
3517d522f475Smrg	    screen->startSel.col = 0;
3518d522f475Smrg	    /* scan forward for end of group */
351920d2c4d2Smrg	    while (MoreRows(endSel) &&
3520956cc18dSsnj		   (LastTextCol(screen, ld.endSel, screen->endSel.row + 1) >
3521956cc18dSsnj		    0 ||
3522956cc18dSsnj		    LineTstWrapped(ld.endSel))) {
3523956cc18dSsnj		NextRow(endSel);
3524d522f475Smrg	    }
3525956cc18dSsnj	    trimLastLine(screen, &(ld.endSel), &(screen->endSel));
3526d522f475Smrg	}
3527d522f475Smrg	break;
3528d522f475Smrg
3529d522f475Smrg    case Select_PAGE:		/* everything one can see */
3530d522f475Smrg	TRACE(("Select_PAGE\n"));
3531d522f475Smrg	screen->startSel.row = 0;
3532d522f475Smrg	screen->startSel.col = 0;
353320d2c4d2Smrg	screen->endSel.row = MaxRows(screen);
3534d522f475Smrg	screen->endSel.col = 0;
3535d522f475Smrg	break;
3536d522f475Smrg
3537d522f475Smrg    case Select_ALL:		/* counts scrollback if in normal screen */
3538d522f475Smrg	TRACE(("Select_ALL\n"));
3539d522f475Smrg	screen->startSel.row = -screen->savedlines;
3540d522f475Smrg	screen->startSel.col = 0;
354120d2c4d2Smrg	screen->endSel.row = MaxRows(screen);
3542d522f475Smrg	screen->endSel.col = 0;
3543d522f475Smrg	break;
3544d522f475Smrg
3545d522f475Smrg#if OPT_SELECT_REGEX
3546d522f475Smrg    case Select_REGEX:
3547d522f475Smrg	do_select_regex(screen, &(screen->startSel), &(screen->endSel));
3548d522f475Smrg	break;
3549d522f475Smrg#endif
3550d522f475Smrg
3551d522f475Smrg    case NSELECTUNITS:		/* always ignore */
3552956cc18dSsnj	ignored = True;
3553956cc18dSsnj	break;
3554d522f475Smrg    }
3555d522f475Smrg
3556956cc18dSsnj    if (!ignored) {
3557956cc18dSsnj	/* check boundaries */
3558956cc18dSsnj	ScrollSelection(screen, 0, False);
3559956cc18dSsnj	TrackText(xw, &(screen->startSel), &(screen->endSel));
3560956cc18dSsnj    }
3561d522f475Smrg
3562d522f475Smrg    return;
3563d522f475Smrg}
3564d522f475Smrg
3565d522f475Smrg/* Guaranteed (first.row, first.col) <= (last.row, last.col) */
3566d522f475Smrgstatic void
3567d522f475SmrgTrackText(XtermWidget xw,
3568e0a2b6dfSmrg	  const CELL *firstp,
3569e0a2b6dfSmrg	  const CELL *lastp)
3570d522f475Smrg{
3571956cc18dSsnj    TScreen *screen = TScreenOf(xw);
3572d522f475Smrg    int from, to;
3573d522f475Smrg    CELL old_start, old_end;
3574d522f475Smrg    CELL first = *firstp;
3575d522f475Smrg    CELL last = *lastp;
3576d522f475Smrg
3577d522f475Smrg    TRACE(("TrackText(first=%d,%d, last=%d,%d)\n",
3578d522f475Smrg	   first.row, first.col, last.row, last.col));
3579d522f475Smrg
3580d522f475Smrg    old_start = screen->startH;
3581d522f475Smrg    old_end = screen->endH;
35820bd37d32Smrg    TRACE(("...previous(first=%d,%d, last=%d,%d)\n",
35830bd37d32Smrg	   old_start.row, old_start.col,
35840bd37d32Smrg	   old_end.row, old_end.col));
3585d522f475Smrg    if (isSameCELL(&first, &old_start) &&
35860bd37d32Smrg	isSameCELL(&last, &old_end)) {
3587d522f475Smrg	return;
35880bd37d32Smrg    }
35890bd37d32Smrg
3590d522f475Smrg    screen->startH = first;
3591d522f475Smrg    screen->endH = last;
3592d522f475Smrg    from = Coordinate(screen, &screen->startH);
3593d522f475Smrg    to = Coordinate(screen, &screen->endH);
3594d522f475Smrg    if (to <= screen->startHCoord || from > screen->endHCoord) {
3595d522f475Smrg	/* No overlap whatsoever between old and new hilite */
3596d522f475Smrg	ReHiliteText(xw, &old_start, &old_end);
3597d522f475Smrg	ReHiliteText(xw, &first, &last);
3598d522f475Smrg    } else {
3599d522f475Smrg	if (from < screen->startHCoord) {
3600d522f475Smrg	    /* Extend left end */
3601d522f475Smrg	    ReHiliteText(xw, &first, &old_start);
3602d522f475Smrg	} else if (from > screen->startHCoord) {
3603d522f475Smrg	    /* Shorten left end */
3604d522f475Smrg	    ReHiliteText(xw, &old_start, &first);
3605d522f475Smrg	}
3606d522f475Smrg	if (to > screen->endHCoord) {
3607d522f475Smrg	    /* Extend right end */
3608d522f475Smrg	    ReHiliteText(xw, &old_end, &last);
3609d522f475Smrg	} else if (to < screen->endHCoord) {
3610d522f475Smrg	    /* Shorten right end */
3611d522f475Smrg	    ReHiliteText(xw, &last, &old_end);
3612d522f475Smrg	}
3613d522f475Smrg    }
3614d522f475Smrg    screen->startHCoord = from;
3615d522f475Smrg    screen->endHCoord = to;
3616d522f475Smrg}
3617d522f475Smrg
3618d522f475Smrg/* Guaranteed that (first->row, first->col) <= (last->row, last->col) */
3619d522f475Smrgstatic void
3620d522f475SmrgReHiliteText(XtermWidget xw,
3621e0a2b6dfSmrg	     CELL *firstp,
3622e0a2b6dfSmrg	     CELL *lastp)
3623d522f475Smrg{
3624956cc18dSsnj    TScreen *screen = TScreenOf(xw);
3625d522f475Smrg    CELL first = *firstp;
3626d522f475Smrg    CELL last = *lastp;
3627d522f475Smrg
3628d522f475Smrg    TRACE(("ReHiliteText from %d.%d to %d.%d\n",
3629d522f475Smrg	   first.row, first.col, last.row, last.col));
3630d522f475Smrg
3631d522f475Smrg    if (first.row < 0)
3632d522f475Smrg	first.row = first.col = 0;
3633d522f475Smrg    else if (first.row > screen->max_row)
3634d522f475Smrg	return;			/* nothing to do, since last.row >= first.row */
3635d522f475Smrg
3636d522f475Smrg    if (last.row < 0)
3637d522f475Smrg	return;			/* nothing to do, since first.row <= last.row */
3638d522f475Smrg    else if (last.row > screen->max_row) {
3639d522f475Smrg	last.row = screen->max_row;
3640d522f475Smrg	last.col = MaxCols(screen);
3641d522f475Smrg    }
3642d522f475Smrg    if (isSameCELL(&first, &last))
3643d522f475Smrg	return;
3644d522f475Smrg
3645d522f475Smrg    if (!isSameRow(&first, &last)) {	/* do multiple rows */
36462e4f8982Smrg	int i;
3647d522f475Smrg	if ((i = screen->max_col - first.col + 1) > 0) {	/* first row */
3648d522f475Smrg	    ScrnRefresh(xw, first.row, first.col, 1, i, True);
3649d522f475Smrg	}
3650d522f475Smrg	if ((i = last.row - first.row - 1) > 0) {	/* middle rows */
3651d522f475Smrg	    ScrnRefresh(xw, first.row + 1, 0, i, MaxCols(screen), True);
3652d522f475Smrg	}
3653d522f475Smrg	if (last.col > 0 && last.row <= screen->max_row) {	/* last row */
3654d522f475Smrg	    ScrnRefresh(xw, last.row, 0, 1, last.col, True);
3655d522f475Smrg	}
3656d522f475Smrg    } else {			/* do single row */
3657d522f475Smrg	ScrnRefresh(xw, first.row, first.col, 1, last.col - first.col, True);
3658d522f475Smrg    }
3659d522f475Smrg}
3660d522f475Smrg
3661d522f475Smrg/*
3662d522f475Smrg * Guaranteed that (cellc->row, cellc->col) <= (cell->row, cell->col), and that both points are valid
3663d522f475Smrg * (may have cell->row = screen->max_row+1, cell->col = 0).
3664d522f475Smrg */
3665d522f475Smrgstatic void
3666d522f475SmrgSaltTextAway(XtermWidget xw,
3667e0a2b6dfSmrg	     CELL *cellc,
3668e0a2b6dfSmrg	     CELL *cell)
3669d522f475Smrg{
3670956cc18dSsnj    TScreen *screen = TScreenOf(xw);
3671d522f475Smrg    int i, j = 0;
3672d522f475Smrg    int eol;
3673d522f475Smrg    Char *line;
3674d522f475Smrg    Char *lp;
3675d522f475Smrg    CELL first = *cellc;
3676d522f475Smrg    CELL last = *cell;
3677d522f475Smrg
3678d522f475Smrg    if (isSameRow(&first, &last) && first.col > last.col) {
36792e4f8982Smrg	int tmp;
3680956cc18dSsnj	EXCHANGE(first.col, last.col, tmp);
3681d522f475Smrg    }
3682d522f475Smrg
3683d522f475Smrg    --last.col;
3684d522f475Smrg    /* first we need to know how long the string is before we can save it */
3685d522f475Smrg
3686d522f475Smrg    if (isSameRow(&last, &first)) {
3687d522f475Smrg	j = Length(screen, first.row, first.col, last.col);
3688d522f475Smrg    } else {			/* two cases, cut is on same line, cut spans multiple lines */
3689d522f475Smrg	j += Length(screen, first.row, first.col, screen->max_col) + 1;
3690d522f475Smrg	for (i = first.row + 1; i < last.row; i++)
3691d522f475Smrg	    j += Length(screen, i, 0, screen->max_col) + 1;
3692d522f475Smrg	if (last.col >= 0)
3693d522f475Smrg	    j += Length(screen, last.row, 0, last.col);
3694d522f475Smrg    }
3695d522f475Smrg
3696d522f475Smrg    /* UTF-8 may require more space */
3697d522f475Smrg    if_OPT_WIDE_CHARS(screen, {
3698d522f475Smrg	j *= 4;
3699d522f475Smrg    });
3700d522f475Smrg
3701d522f475Smrg    /* now get some memory to save it in */
3702d522f475Smrg
3703d522f475Smrg    if (screen->selection_size <= j) {
370420d2c4d2Smrg	if ((line = (Char *) malloc((size_t) j + 1)) == 0)
3705d522f475Smrg	    SysError(ERROR_BMALLOC2);
3706d522f475Smrg	XtFree((char *) screen->selection_data);
3707d522f475Smrg	screen->selection_data = line;
3708d522f475Smrg	screen->selection_size = j + 1;
3709d522f475Smrg    } else {
3710d522f475Smrg	line = screen->selection_data;
3711d522f475Smrg    }
3712d522f475Smrg
3713d522f475Smrg    if ((line == 0)
3714d522f475Smrg	|| (j < 0))
3715d522f475Smrg	return;
3716d522f475Smrg
3717d522f475Smrg    line[j] = '\0';		/* make sure it is null terminated */
3718d522f475Smrg    lp = line;			/* lp points to where to save the text */
3719d522f475Smrg    if (isSameRow(&last, &first)) {
3720d522f475Smrg	lp = SaveText(screen, last.row, first.col, last.col, lp, &eol);
3721d522f475Smrg    } else {
3722d522f475Smrg	lp = SaveText(screen, first.row, first.col, screen->max_col, lp, &eol);
3723d522f475Smrg	if (eol)
3724d522f475Smrg	    *lp++ = '\n';	/* put in newline at end of line */
3725d522f475Smrg	for (i = first.row + 1; i < last.row; i++) {
3726d522f475Smrg	    lp = SaveText(screen, i, 0, screen->max_col, lp, &eol);
3727d522f475Smrg	    if (eol)
3728d522f475Smrg		*lp++ = '\n';
3729d522f475Smrg	}
3730d522f475Smrg	if (last.col >= 0)
3731d522f475Smrg	    lp = SaveText(screen, last.row, 0, last.col, lp, &eol);
3732d522f475Smrg    }
3733d522f475Smrg    *lp = '\0';			/* make sure we have end marked */
3734d522f475Smrg
3735956cc18dSsnj    TRACE(("Salted TEXT:%d:%s\n", (int) (lp - line),
3736956cc18dSsnj	   visibleChars(line, (unsigned) (lp - line))));
3737d522f475Smrg
37382eaa94a1Schristos    screen->selection_length = (unsigned long) (lp - line);
3739d522f475Smrg}
3740d522f475Smrg
3741d522f475Smrg#if OPT_PASTE64
3742d522f475Smrgvoid
3743e0a2b6dfSmrgClearSelectionBuffer(TScreen *screen)
3744d522f475Smrg{
3745d522f475Smrg    screen->selection_length = 0;
3746d522f475Smrg    screen->base64_count = 0;
3747d522f475Smrg}
3748d522f475Smrg
3749d522f475Smrgstatic void
3750e0a2b6dfSmrgAppendStrToSelectionBuffer(TScreen *screen, Char *text, size_t len)
3751d522f475Smrg{
3752d522f475Smrg    if (len != 0) {
37532eaa94a1Schristos	int j = (int) (screen->selection_length + len);		/* New length */
3754d522f475Smrg	int k = j + (j >> 2) + 80;	/* New size if we grow buffer: grow by ~50% */
3755d522f475Smrg	if (j + 1 >= screen->selection_size) {
3756d522f475Smrg	    if (!screen->selection_length) {
3757d522f475Smrg		/* New buffer */
3758d522f475Smrg		Char *line;
375920d2c4d2Smrg		if ((line = (Char *) malloc((size_t) k)) == 0)
3760d522f475Smrg		    SysError(ERROR_BMALLOC2);
3761d522f475Smrg		XtFree((char *) screen->selection_data);
3762d522f475Smrg		screen->selection_data = line;
3763d522f475Smrg	    } else {
3764d522f475Smrg		/* Realloc buffer */
3765d522f475Smrg		screen->selection_data = (Char *)
3766d522f475Smrg		    realloc(screen->selection_data,
376720d2c4d2Smrg			    (size_t) k);
3768d522f475Smrg		if (screen->selection_data == 0)
3769d522f475Smrg		    SysError(ERROR_BMALLOC2);
3770d522f475Smrg	    }
3771d522f475Smrg	    screen->selection_size = k;
3772d522f475Smrg	}
377320d2c4d2Smrg	if (screen->selection_data != 0) {
377420d2c4d2Smrg	    memcpy(screen->selection_data + screen->selection_length, text, len);
377520d2c4d2Smrg	    screen->selection_length += len;
377620d2c4d2Smrg	    screen->selection_data[screen->selection_length] = 0;
377720d2c4d2Smrg	}
3778d522f475Smrg    }
3779d522f475Smrg}
3780d522f475Smrg
3781d522f475Smrgvoid
3782e0a2b6dfSmrgAppendToSelectionBuffer(TScreen *screen, unsigned c)
3783d522f475Smrg{
37842eaa94a1Schristos    unsigned six;
3785d522f475Smrg    Char ch;
3786d522f475Smrg
3787d522f475Smrg    /* Decode base64 character */
3788d522f475Smrg    if (c >= 'A' && c <= 'Z')
3789d522f475Smrg	six = c - 'A';
3790d522f475Smrg    else if (c >= 'a' && c <= 'z')
3791d522f475Smrg	six = c - 'a' + 26;
3792d522f475Smrg    else if (c >= '0' && c <= '9')
3793d522f475Smrg	six = c - '0' + 52;
3794d522f475Smrg    else if (c == '+')
3795d522f475Smrg	six = 62;
3796d522f475Smrg    else if (c == '/')
3797d522f475Smrg	six = 63;
3798d522f475Smrg    else
3799d522f475Smrg	return;
3800d522f475Smrg
3801d522f475Smrg    /* Accumulate bytes */
3802d522f475Smrg    switch (screen->base64_count) {
3803d522f475Smrg    case 0:
3804d522f475Smrg	screen->base64_accu = six;
3805d522f475Smrg	screen->base64_count = 6;
3806d522f475Smrg	break;
3807d522f475Smrg
3808d522f475Smrg    case 2:
38092eaa94a1Schristos	ch = CharOf((screen->base64_accu << 6) + six);
3810d522f475Smrg	screen->base64_count = 0;
381120d2c4d2Smrg	AppendStrToSelectionBuffer(screen, &ch, (size_t) 1);
3812d522f475Smrg	break;
3813d522f475Smrg
3814d522f475Smrg    case 4:
38152eaa94a1Schristos	ch = CharOf((screen->base64_accu << 4) + (six >> 2));
3816d522f475Smrg	screen->base64_accu = (six & 0x3);
3817d522f475Smrg	screen->base64_count = 2;
381820d2c4d2Smrg	AppendStrToSelectionBuffer(screen, &ch, (size_t) 1);
3819d522f475Smrg	break;
3820d522f475Smrg
3821d522f475Smrg    case 6:
38222eaa94a1Schristos	ch = CharOf((screen->base64_accu << 2) + (six >> 4));
3823d522f475Smrg	screen->base64_accu = (six & 0xF);
3824d522f475Smrg	screen->base64_count = 4;
382520d2c4d2Smrg	AppendStrToSelectionBuffer(screen, &ch, (size_t) 1);
3826d522f475Smrg	break;
3827d522f475Smrg    }
3828d522f475Smrg}
3829d522f475Smrg
3830d522f475Smrgvoid
3831e0a2b6dfSmrgCompleteSelection(XtermWidget xw, String *args, Cardinal len)
3832d522f475Smrg{
3833956cc18dSsnj    TScreen *screen = TScreenOf(xw);
3834956cc18dSsnj
3835956cc18dSsnj    screen->base64_count = 0;
3836956cc18dSsnj    screen->base64_accu = 0;
3837d522f475Smrg    _OwnSelection(xw, args, len);
3838d522f475Smrg}
3839d522f475Smrg#endif /* OPT_PASTE64 */
3840d522f475Smrg
3841d522f475Smrgstatic Bool
3842d522f475Smrg_ConvertSelectionHelper(Widget w,
3843894e0ac8Smrg			Atom *type,
3844d522f475Smrg			XtPointer *value,
3845d522f475Smrg			unsigned long *length,
38462e4f8982Smrg			Char *data,
38472e4f8982Smrg			unsigned long remaining,
3848d522f475Smrg			int *format,
3849d522f475Smrg			int (*conversion_function) (Display *,
3850d522f475Smrg						    char **, int,
3851d522f475Smrg						    XICCEncodingStyle,
3852d522f475Smrg						    XTextProperty *),
3853d522f475Smrg			XICCEncodingStyle conversion_style)
3854d522f475Smrg{
3855956cc18dSsnj    XtermWidget xw;
3856956cc18dSsnj
385701037d57Smrg    *value = 0;
385801037d57Smrg    *length = 0;
385901037d57Smrg    *type = 0;
386001037d57Smrg    *format = 0;
386101037d57Smrg
3862956cc18dSsnj    if ((xw = getXtermWidget(w)) != 0) {
3863956cc18dSsnj	TScreen *screen = TScreenOf(xw);
3864d522f475Smrg	Display *dpy = XtDisplay(w);
3865d522f475Smrg	XTextProperty textprop;
386601037d57Smrg	int out_n = 0;
386701037d57Smrg	char *result = 0;
38682e4f8982Smrg	char *the_data = (char *) data;
386901037d57Smrg	char *the_next;
387001037d57Smrg
387101037d57Smrg	TRACE(("converting %ld:'%s'\n",
387201037d57Smrg	       (long) screen->selection_length,
387301037d57Smrg	       visibleChars(screen->selection_data, (unsigned) screen->selection_length)));
387401037d57Smrg	/*
387501037d57Smrg	 * For most selections, we can convert in one pass.  It is possible
387601037d57Smrg	 * that some applications contain embedded nulls, e.g., using xterm's
387701037d57Smrg	 * paste64 feature.  For those cases, we will build up the result in
387801037d57Smrg	 * parts.
387901037d57Smrg	 */
388001037d57Smrg	if (memchr(the_data, 0, screen->selection_length) != 0) {
388101037d57Smrg	    TRACE(("selection contains embedded nulls\n"));
388201037d57Smrg	    result = calloc(screen->selection_length + 1, sizeof(char));
388301037d57Smrg	}
3884d522f475Smrg
388501037d57Smrg      next_try:
388601037d57Smrg	memset(&textprop, 0, sizeof(textprop));
3887d522f475Smrg	if (conversion_function(dpy, &the_data, 1,
3888d522f475Smrg				conversion_style,
3889d522f475Smrg				&textprop) >= Success) {
389001037d57Smrg	    if ((result != 0)
389101037d57Smrg		&& (textprop.value != 0)
389201037d57Smrg		&& (textprop.format == 8)) {
389301037d57Smrg		char *text_values = (char *) textprop.value;
389401037d57Smrg		unsigned long in_n;
389501037d57Smrg
389601037d57Smrg		if (out_n == 0) {
389701037d57Smrg		    *value = result;
389801037d57Smrg		    *type = textprop.encoding;
389901037d57Smrg		    *format = textprop.format;
390001037d57Smrg		}
390101037d57Smrg		for (in_n = 0; in_n < textprop.nitems; ++in_n) {
390201037d57Smrg		    result[out_n++] = text_values[in_n];
390301037d57Smrg		}
390401037d57Smrg		*length += textprop.nitems;
390501037d57Smrg		if ((the_next = memchr(the_data, 0, remaining)) != 0) {
390601037d57Smrg		    unsigned long this_was = (unsigned long) (the_next - the_data);
390701037d57Smrg		    this_was++;
390801037d57Smrg		    the_data += this_was;
390901037d57Smrg		    remaining -= this_was;
391001037d57Smrg		    result[out_n++] = 0;
391101037d57Smrg		    *length += 1;
391201037d57Smrg		    if (remaining)
391301037d57Smrg			goto next_try;
391401037d57Smrg		}
391501037d57Smrg		return True;
391601037d57Smrg	    } else {
391701037d57Smrg		free(result);
391801037d57Smrg		*value = (XtPointer) textprop.value;
391901037d57Smrg		*length = textprop.nitems;
392001037d57Smrg		*type = textprop.encoding;
392101037d57Smrg		*format = textprop.format;
392201037d57Smrg		return True;
392301037d57Smrg	    }
3924d522f475Smrg	}
392501037d57Smrg	free(result);
3926d522f475Smrg    }
3927d522f475Smrg    return False;
3928d522f475Smrg}
3929d522f475Smrg
39302eaa94a1Schristosstatic Boolean
39312eaa94a1SchristosSaveConvertedLength(XtPointer *target, unsigned long source)
39322eaa94a1Schristos{
39332eaa94a1Schristos    Boolean result = False;
39342eaa94a1Schristos
39352eaa94a1Schristos    *target = XtMalloc(4);
39362eaa94a1Schristos    if (*target != 0) {
39372eaa94a1Schristos	result = True;
39382eaa94a1Schristos	if (sizeof(unsigned long) == 4) {
39392eaa94a1Schristos	    *(unsigned long *) *target = source;
39402eaa94a1Schristos	} else if (sizeof(unsigned) == 4) {
394120d2c4d2Smrg	    *(unsigned *) *target = (unsigned) source;
39422eaa94a1Schristos	} else if (sizeof(unsigned short) == 4) {
39432eaa94a1Schristos	    *(unsigned short *) *target = (unsigned short) source;
39442eaa94a1Schristos	} else {
39452eaa94a1Schristos	    /* FIXME - does this depend on byte-order? */
39462eaa94a1Schristos	    unsigned long temp = source;
394720d2c4d2Smrg	    memcpy((char *) *target,
394820d2c4d2Smrg		   ((char *) &temp) + sizeof(temp) - 4,
394920d2c4d2Smrg		   (size_t) 4);
39502eaa94a1Schristos	}
39512eaa94a1Schristos    }
39522eaa94a1Schristos    return result;
39532eaa94a1Schristos}
39542eaa94a1Schristos
39552e4f8982Smrg#define keepClipboard(atom) ((screen->keepClipboard) && \
39562e4f8982Smrg	 (atom == XInternAtom(screen->display, "CLIPBOARD", False)))
39572e4f8982Smrg
3958d522f475Smrgstatic Boolean
3959d522f475SmrgConvertSelection(Widget w,
3960894e0ac8Smrg		 Atom *selection,
3961894e0ac8Smrg		 Atom *target,
3962894e0ac8Smrg		 Atom *type,
3963d522f475Smrg		 XtPointer *value,
3964d522f475Smrg		 unsigned long *length,
3965d522f475Smrg		 int *format)
3966d522f475Smrg{
3967d522f475Smrg    Display *dpy = XtDisplay(w);
3968d522f475Smrg    TScreen *screen;
3969d522f475Smrg    Bool result = False;
3970d522f475Smrg
39712e4f8982Smrg    Char *data;
39722e4f8982Smrg    unsigned long data_length;
39732e4f8982Smrg
3974956cc18dSsnj    XtermWidget xw;
3975956cc18dSsnj
3976956cc18dSsnj    if ((xw = getXtermWidget(w)) == 0)
3977d522f475Smrg	return False;
3978d522f475Smrg
3979956cc18dSsnj    screen = TScreenOf(xw);
3980d522f475Smrg
3981956cc18dSsnj    TRACE(("ConvertSelection %s\n",
3982956cc18dSsnj	   visibleSelectionTarget(dpy, *target)));
3983956cc18dSsnj
39842e4f8982Smrg    if (keepClipboard(*selection)) {
39852e4f8982Smrg	TRACE(("asked for clipboard\n"));
39862e4f8982Smrg	data = screen->clipboard_data;
39872e4f8982Smrg	data_length = screen->clipboard_size;
39882e4f8982Smrg    } else {
39892e4f8982Smrg	TRACE(("asked for selection\n"));
39902e4f8982Smrg	data = screen->selection_data;
39912e4f8982Smrg	data_length = screen->selection_length;
39922e4f8982Smrg    }
39932e4f8982Smrg
39942e4f8982Smrg    if (data == NULL) {
399501037d57Smrg	TRACE(("...FIXME: no selection_data\n"));
399601037d57Smrg	return False;		/* can this happen? */
399701037d57Smrg    }
399801037d57Smrg
3999d522f475Smrg    if (*target == XA_TARGETS(dpy)) {
4000d522f475Smrg	Atom *targetP;
4001d522f475Smrg	XPointer std_return = 0;
4002d522f475Smrg	unsigned long std_length;
4003d522f475Smrg
4004d522f475Smrg	if (XmuConvertStandardSelection(w, screen->selection_time, selection,
4005d522f475Smrg					target, type, &std_return,
4006d522f475Smrg					&std_length, format)) {
4007956cc18dSsnj	    Atom *my_targets = _SelectionTargets(w);
40082e4f8982Smrg	    Atom *allocP;
40092e4f8982Smrg	    Atom *std_targets;
4010956cc18dSsnj
4011956cc18dSsnj	    TRACE(("XmuConvertStandardSelection - success\n"));
4012a1f3da82Smrg	    std_targets = (Atom *) (void *) (std_return);
4013d522f475Smrg	    *length = std_length + 6;
4014d522f475Smrg
4015a1f3da82Smrg	    targetP = TypeXtMallocN(Atom, *length);
4016d522f475Smrg	    allocP = targetP;
4017d522f475Smrg
4018d522f475Smrg	    *value = (XtPointer) targetP;
4019d522f475Smrg
40200bd37d32Smrg	    if (my_targets != 0) {
40210bd37d32Smrg		while (*my_targets != None) {
40220bd37d32Smrg		    *targetP++ = *my_targets++;
40230bd37d32Smrg		}
4024956cc18dSsnj	    }
4025d522f475Smrg	    *targetP++ = XA_LENGTH(dpy);
4026d522f475Smrg	    *targetP++ = XA_LIST_LENGTH(dpy);
4027d522f475Smrg
40282eaa94a1Schristos	    *length = std_length + (unsigned long) (targetP - allocP);
4029d522f475Smrg
4030d522f475Smrg	    memcpy(targetP, std_targets, sizeof(Atom) * std_length);
4031d522f475Smrg	    XtFree((char *) std_targets);
4032d522f475Smrg	    *type = XA_ATOM;
4033d522f475Smrg	    *format = 32;
4034d522f475Smrg	    result = True;
4035956cc18dSsnj	} else {
4036956cc18dSsnj	    TRACE(("XmuConvertStandardSelection - failed\n"));
4037d522f475Smrg	}
4038d522f475Smrg    }
4039d522f475Smrg#if OPT_WIDE_CHARS
4040d522f475Smrg    else if (screen->wide_chars && *target == XA_STRING) {
4041d522f475Smrg	result =
4042d522f475Smrg	    _ConvertSelectionHelper(w,
40432e4f8982Smrg				    type, value, length, data,
40442e4f8982Smrg				    data_length, format,
4045d522f475Smrg				    Xutf8TextListToTextProperty,
4046d522f475Smrg				    XStringStyle);
4047956cc18dSsnj	TRACE(("...Xutf8TextListToTextProperty:%d\n", result));
4048d522f475Smrg    } else if (screen->wide_chars && *target == XA_UTF8_STRING(dpy)) {
4049d522f475Smrg	result =
4050d522f475Smrg	    _ConvertSelectionHelper(w,
40512e4f8982Smrg				    type, value, length, data,
40522e4f8982Smrg				    data_length, format,
4053d522f475Smrg				    Xutf8TextListToTextProperty,
4054d522f475Smrg				    XUTF8StringStyle);
4055956cc18dSsnj	TRACE(("...Xutf8TextListToTextProperty:%d\n", result));
4056d522f475Smrg    } else if (screen->wide_chars && *target == XA_TEXT(dpy)) {
4057d522f475Smrg	result =
4058d522f475Smrg	    _ConvertSelectionHelper(w,
40592e4f8982Smrg				    type, value, length, data,
40602e4f8982Smrg				    data_length, format,
4061d522f475Smrg				    Xutf8TextListToTextProperty,
4062d522f475Smrg				    XStdICCTextStyle);
4063956cc18dSsnj	TRACE(("...Xutf8TextListToTextProperty:%d\n", result));
4064d522f475Smrg    } else if (screen->wide_chars && *target == XA_COMPOUND_TEXT(dpy)) {
4065d522f475Smrg	result =
4066d522f475Smrg	    _ConvertSelectionHelper(w,
40672e4f8982Smrg				    type, value, length, data,
40682e4f8982Smrg				    data_length, format,
4069d522f475Smrg				    Xutf8TextListToTextProperty,
4070d522f475Smrg				    XCompoundTextStyle);
4071956cc18dSsnj	TRACE(("...Xutf8TextListToTextProperty:%d\n", result));
4072d522f475Smrg    }
4073d522f475Smrg#endif
4074d522f475Smrg
4075d522f475Smrg    else if (*target == XA_STRING) {	/* not wide_chars */
4076d522f475Smrg	/* We can only reach this point if the selection requestor
4077d522f475Smrg	   requested STRING before any of TEXT, COMPOUND_TEXT or
4078d522f475Smrg	   UTF8_STRING.  We therefore assume that the requestor is not
4079d522f475Smrg	   properly internationalised, and dump raw eight-bit data
4080d522f475Smrg	   with no conversion into the selection.  Yes, this breaks
4081d522f475Smrg	   the ICCCM in non-Latin-1 locales. */
4082d522f475Smrg	*type = XA_STRING;
4083d522f475Smrg	*value = (XtPointer) screen->selection_data;
4084d522f475Smrg	*length = screen->selection_length;
4085d522f475Smrg	*format = 8;
4086d522f475Smrg	result = True;
4087956cc18dSsnj	TRACE(("...raw 8-bit data:%d\n", result));
4088d522f475Smrg    } else if (*target == XA_TEXT(dpy)) {	/* not wide_chars */
4089d522f475Smrg	result =
4090d522f475Smrg	    _ConvertSelectionHelper(w,
40912e4f8982Smrg				    type, value, length, data,
40922e4f8982Smrg				    data_length, format,
4093d522f475Smrg				    XmbTextListToTextProperty,
4094d522f475Smrg				    XStdICCTextStyle);
4095956cc18dSsnj	TRACE(("...XmbTextListToTextProperty(StdICC):%d\n", result));
4096d522f475Smrg    } else if (*target == XA_COMPOUND_TEXT(dpy)) {	/* not wide_chars */
4097d522f475Smrg	result =
4098d522f475Smrg	    _ConvertSelectionHelper(w,
40992e4f8982Smrg				    type, value, length, data,
41002e4f8982Smrg				    data_length, format,
4101d522f475Smrg				    XmbTextListToTextProperty,
4102d522f475Smrg				    XCompoundTextStyle);
4103956cc18dSsnj	TRACE(("...XmbTextListToTextProperty(Compound):%d\n", result));
4104d522f475Smrg    }
4105d522f475Smrg#ifdef X_HAVE_UTF8_STRING
4106d522f475Smrg    else if (*target == XA_UTF8_STRING(dpy)) {	/* not wide_chars */
4107d522f475Smrg	result =
4108d522f475Smrg	    _ConvertSelectionHelper(w,
41092e4f8982Smrg				    type, value, length, data,
41102e4f8982Smrg				    data_length, format,
4111d522f475Smrg				    XmbTextListToTextProperty,
4112d522f475Smrg				    XUTF8StringStyle);
4113956cc18dSsnj	TRACE(("...XmbTextListToTextProperty(UTF8):%d\n", result));
4114d522f475Smrg    }
4115d522f475Smrg#endif
4116d522f475Smrg    else if (*target == XA_LIST_LENGTH(dpy)) {
411720d2c4d2Smrg	result = SaveConvertedLength(value, (unsigned long) 1);
4118d522f475Smrg	*type = XA_INTEGER;
4119d522f475Smrg	*length = 1;
4120d522f475Smrg	*format = 32;
4121956cc18dSsnj	TRACE(("...list of values:%d\n", result));
4122d522f475Smrg    } else if (*target == XA_LENGTH(dpy)) {
4123d522f475Smrg	/* This value is wrong if we have UTF-8 text */
41242eaa94a1Schristos	result = SaveConvertedLength(value, screen->selection_length);
4125d522f475Smrg	*type = XA_INTEGER;
4126d522f475Smrg	*length = 1;
4127d522f475Smrg	*format = 32;
4128956cc18dSsnj	TRACE(("...list of values:%d\n", result));
4129d522f475Smrg    } else if (XmuConvertStandardSelection(w,
4130d522f475Smrg					   screen->selection_time, selection,
4131d522f475Smrg					   target, type, (XPointer *) value,
4132d522f475Smrg					   length, format)) {
4133d522f475Smrg	result = True;
4134956cc18dSsnj	TRACE(("...XmuConvertStandardSelection:%d\n", result));
4135d522f475Smrg    }
4136d522f475Smrg
4137d522f475Smrg    /* else */
41382eaa94a1Schristos    return (Boolean) result;
4139d522f475Smrg}
4140d522f475Smrg
4141d522f475Smrgstatic void
4142894e0ac8SmrgLoseSelection(Widget w, Atom *selection)
4143d522f475Smrg{
4144d522f475Smrg    TScreen *screen;
4145d522f475Smrg    Atom *atomP;
4146d522f475Smrg    Cardinal i;
4147d522f475Smrg
4148956cc18dSsnj    XtermWidget xw;
4149956cc18dSsnj
4150956cc18dSsnj    if ((xw = getXtermWidget(w)) == 0)
4151d522f475Smrg	return;
4152d522f475Smrg
4153956cc18dSsnj    screen = TScreenOf(xw);
415401037d57Smrg    TRACE(("LoseSelection %s\n", XGetAtomName(screen->display, *selection)));
415501037d57Smrg
4156d522f475Smrg    for (i = 0, atomP = screen->selection_atoms;
4157d522f475Smrg	 i < screen->selection_count; i++, atomP++) {
4158d522f475Smrg	if (*selection == *atomP)
4159d522f475Smrg	    *atomP = (Atom) 0;
4160d522f475Smrg	if (CutBuffer(*atomP) >= 0) {
4161d522f475Smrg	    *atomP = (Atom) 0;
4162d522f475Smrg	}
4163d522f475Smrg    }
4164d522f475Smrg
4165d522f475Smrg    for (i = screen->selection_count; i; i--) {
4166d522f475Smrg	if (screen->selection_atoms[i - 1] != 0)
4167d522f475Smrg	    break;
4168d522f475Smrg    }
4169d522f475Smrg    screen->selection_count = i;
4170d522f475Smrg
4171d522f475Smrg    for (i = 0, atomP = screen->selection_atoms;
4172d522f475Smrg	 i < screen->selection_count; i++, atomP++) {
4173d522f475Smrg	if (*atomP == (Atom) 0) {
4174d522f475Smrg	    *atomP = screen->selection_atoms[--screen->selection_count];
4175d522f475Smrg	}
4176d522f475Smrg    }
4177d522f475Smrg
4178d522f475Smrg    if (screen->selection_count == 0)
4179956cc18dSsnj	TrackText(xw, &zeroCELL, &zeroCELL);
4180d522f475Smrg}
4181d522f475Smrg
4182d522f475Smrg/* ARGSUSED */
4183d522f475Smrgstatic void
4184d522f475SmrgSelectionDone(Widget w GCC_UNUSED,
4185894e0ac8Smrg	      Atom *selection GCC_UNUSED,
4186894e0ac8Smrg	      Atom *target GCC_UNUSED)
4187d522f475Smrg{
4188d522f475Smrg    /* empty proc so Intrinsics know we want to keep storage */
418901037d57Smrg    TRACE(("SelectionDone\n"));
4190d522f475Smrg}
4191d522f475Smrg
4192d522f475Smrgstatic void
4193d522f475Smrg_OwnSelection(XtermWidget xw,
4194e0a2b6dfSmrg	      String *selections,
4195d522f475Smrg	      Cardinal count)
4196d522f475Smrg{
4197956cc18dSsnj    TScreen *screen = TScreenOf(xw);
4198d522f475Smrg    Atom *atoms = screen->selection_atoms;
4199d522f475Smrg    Cardinal i;
4200d522f475Smrg    Bool have_selection = False;
4201d522f475Smrg
420220d2c4d2Smrg    if (count == 0)
420320d2c4d2Smrg	return;
4204d522f475Smrg
420501037d57Smrg    TRACE(("_OwnSelection count %d, length %ld value %s\n", count,
420601037d57Smrg	   screen->selection_length,
420701037d57Smrg	   visibleChars(screen->selection_data, (unsigned) screen->selection_length)));
4208d522f475Smrg    selections = MapSelections(xw, selections, count);
4209d522f475Smrg
4210d522f475Smrg    if (count > screen->sel_atoms_size) {
4211d522f475Smrg	XtFree((char *) atoms);
4212a1f3da82Smrg	atoms = TypeXtMallocN(Atom, count);
4213d522f475Smrg	screen->selection_atoms = atoms;
4214d522f475Smrg	screen->sel_atoms_size = count;
4215d522f475Smrg    }
4216d522f475Smrg    XmuInternStrings(XtDisplay((Widget) xw), selections, count, atoms);
4217d522f475Smrg    for (i = 0; i < count; i++) {
4218d522f475Smrg	int cutbuffer = CutBuffer(atoms[i]);
4219d522f475Smrg	if (cutbuffer >= 0) {
42202eaa94a1Schristos	    unsigned long limit =
42212eaa94a1Schristos	    (unsigned long) (4 * XMaxRequestSize(XtDisplay((Widget) xw)) - 32);
42222eaa94a1Schristos	    if (screen->selection_length > limit) {
422320d2c4d2Smrg		TRACE(("selection too big (%lu bytes), not storing in CUT_BUFFER%d\n",
422420d2c4d2Smrg		       screen->selection_length, cutbuffer));
42250bd37d32Smrg		xtermWarning("selection too big (%lu bytes), not storing in CUT_BUFFER%d\n",
42260bd37d32Smrg			     screen->selection_length, cutbuffer);
4227d522f475Smrg	    } else {
4228d522f475Smrg		/* This used to just use the UTF-8 data, which was totally
4229894e0ac8Smrg		 * broken as not even the corresponding paste code in xterm
4230d522f475Smrg		 * understood this!  So now it converts to Latin1 first.
4231d522f475Smrg		 *   Robert Brady, 2000-09-05
4232d522f475Smrg		 */
4233d522f475Smrg		unsigned long length = screen->selection_length;
4234d522f475Smrg		Char *data = screen->selection_data;
4235d522f475Smrg		if_OPT_WIDE_CHARS((screen), {
4236956cc18dSsnj		    data = UTF8toLatin1(screen, data, length, &length);
4237d522f475Smrg		});
4238d522f475Smrg		TRACE(("XStoreBuffer(%d)\n", cutbuffer));
4239d522f475Smrg		XStoreBuffer(XtDisplay((Widget) xw),
4240d522f475Smrg			     (char *) data,
4241d522f475Smrg			     (int) length,
4242d522f475Smrg			     cutbuffer);
4243d522f475Smrg	    }
42442e4f8982Smrg	} else if (keepClipboard(atoms[i])) {
42452e4f8982Smrg	    Char *buf;
42462e4f8982Smrg	    TRACE(("saving selection to clipboard buffer\n"));
42472e4f8982Smrg	    if ((buf = (Char *) malloc((size_t) screen->selection_length))
42482e4f8982Smrg		== 0)
42492e4f8982Smrg		SysError(ERROR_BMALLOC2);
42502e4f8982Smrg
42512e4f8982Smrg	    XtFree((char *) screen->clipboard_data);
42522e4f8982Smrg	    memcpy(buf, screen->selection_data, screen->selection_length);
42532e4f8982Smrg	    screen->clipboard_data = buf;
42542e4f8982Smrg	    screen->clipboard_size = screen->selection_length;
425501037d57Smrg	} else if (screen->selection_length == 0) {
425601037d57Smrg	    XtDisownSelection((Widget) xw, atoms[i], screen->selection_time);
4257d522f475Smrg	} else if (!screen->replyToEmacs) {
4258d522f475Smrg	    have_selection |=
4259d522f475Smrg		XtOwnSelection((Widget) xw, atoms[i],
4260d522f475Smrg			       screen->selection_time,
4261d522f475Smrg			       ConvertSelection, LoseSelection, SelectionDone);
4262d522f475Smrg	}
4263d522f475Smrg    }
4264d522f475Smrg    if (!screen->replyToEmacs)
4265d522f475Smrg	screen->selection_count = count;
4266d522f475Smrg    if (!have_selection)
4267d522f475Smrg	TrackText(xw, &zeroCELL, &zeroCELL);
4268d522f475Smrg}
4269d522f475Smrg
4270d522f475Smrgstatic void
4271e0a2b6dfSmrgResetSelectionState(TScreen *screen)
4272d522f475Smrg{
4273d522f475Smrg    screen->selection_count = 0;
4274d522f475Smrg    screen->startH = zeroCELL;
4275d522f475Smrg    screen->endH = zeroCELL;
4276d522f475Smrg}
4277d522f475Smrg
4278d522f475Smrgvoid
4279d522f475SmrgDisownSelection(XtermWidget xw)
4280d522f475Smrg{
4281956cc18dSsnj    TScreen *screen = TScreenOf(xw);
4282d522f475Smrg    Atom *atoms = screen->selection_atoms;
4283d522f475Smrg    Cardinal count = screen->selection_count;
4284d522f475Smrg    Cardinal i;
4285d522f475Smrg
4286d522f475Smrg    TRACE(("DisownSelection count %d, start %d.%d, end %d.%d\n",
4287d522f475Smrg	   count,
4288d522f475Smrg	   screen->startH.row,
4289d522f475Smrg	   screen->startH.col,
4290d522f475Smrg	   screen->endH.row,
4291d522f475Smrg	   screen->endH.col));
4292d522f475Smrg
4293d522f475Smrg    for (i = 0; i < count; i++) {
4294d522f475Smrg	int cutbuffer = CutBuffer(atoms[i]);
4295d522f475Smrg	if (cutbuffer < 0) {
4296d522f475Smrg	    XtDisownSelection((Widget) xw, atoms[i],
4297d522f475Smrg			      screen->selection_time);
4298d522f475Smrg	}
4299d522f475Smrg    }
4300d522f475Smrg    /*
4301d522f475Smrg     * If none of the callbacks via XtDisownSelection() reset highlighting
4302d522f475Smrg     * do it now.
4303d522f475Smrg     */
4304d522f475Smrg    if (ScrnHaveSelection(screen)) {
4305d522f475Smrg	/* save data which will be reset */
4306d522f475Smrg	CELL first = screen->startH;
4307d522f475Smrg	CELL last = screen->endH;
4308d522f475Smrg
4309d522f475Smrg	ResetSelectionState(screen);
4310d522f475Smrg	ReHiliteText(xw, &first, &last);
4311d522f475Smrg    } else {
4312d522f475Smrg	ResetSelectionState(screen);
4313d522f475Smrg    }
4314d522f475Smrg}
4315d522f475Smrg
4316d522f475Smrgvoid
4317d522f475SmrgUnhiliteSelection(XtermWidget xw)
4318d522f475Smrg{
4319956cc18dSsnj    TScreen *screen = TScreenOf(xw);
4320d522f475Smrg
4321d522f475Smrg    if (ScrnHaveSelection(screen)) {
4322d522f475Smrg	CELL first = screen->startH;
4323d522f475Smrg	CELL last = screen->endH;
4324d522f475Smrg
4325d522f475Smrg	screen->startH = zeroCELL;
4326d522f475Smrg	screen->endH = zeroCELL;
4327d522f475Smrg	ReHiliteText(xw, &first, &last);
4328d522f475Smrg    }
4329d522f475Smrg}
4330d522f475Smrg
4331d522f475Smrg/* returns number of chars in line from scol to ecol out */
4332d522f475Smrg/* ARGSUSED */
4333d522f475Smrgstatic int
4334e0a2b6dfSmrgLength(TScreen *screen,
4335d522f475Smrg       int row,
4336d522f475Smrg       int scol,
4337d522f475Smrg       int ecol)
4338d522f475Smrg{
433901037d57Smrg    CLineData *ld = GET_LINEDATA(screen, row);
434001037d57Smrg    const int lastcol = LastTextCol(screen, ld, row);
4341d522f475Smrg
4342d522f475Smrg    if (ecol > lastcol)
4343d522f475Smrg	ecol = lastcol;
4344d522f475Smrg    return (ecol - scol + 1);
4345d522f475Smrg}
4346d522f475Smrg
4347d522f475Smrg/* copies text into line, preallocated */
4348d522f475Smrgstatic Char *
4349e0a2b6dfSmrgSaveText(TScreen *screen,
4350d522f475Smrg	 int row,
4351d522f475Smrg	 int scol,
4352d522f475Smrg	 int ecol,
4353e0a2b6dfSmrg	 Char *lp,		/* pointer to where to put the text */
4354d522f475Smrg	 int *eol)
4355d522f475Smrg{
4356956cc18dSsnj    LineData *ld;
4357d522f475Smrg    int i = 0;
4358d522f475Smrg    Char *result = lp;
4359d522f475Smrg#if OPT_WIDE_CHARS
43602eaa94a1Schristos    unsigned previous = 0;
4361d522f475Smrg#endif
4362d522f475Smrg
4363956cc18dSsnj    ld = GET_LINEDATA(screen, row);
4364d522f475Smrg    i = Length(screen, row, scol, ecol);
4365d522f475Smrg    ecol = scol + i;
4366d522f475Smrg#if OPT_DEC_CHRSET
4367956cc18dSsnj    if (CSET_DOUBLE(GetLineDblCS(ld))) {
4368d522f475Smrg	scol = (scol + 0) / 2;
4369d522f475Smrg	ecol = (ecol + 1) / 2;
4370d522f475Smrg    }
4371d522f475Smrg#endif
4372956cc18dSsnj    *eol = !LineTstWrapped(ld);
4373d522f475Smrg    for (i = scol; i < ecol; i++) {
43742e4f8982Smrg	unsigned c;
43750bd37d32Smrg	assert(i < (int) ld->lineSize);
4376956cc18dSsnj	c = E2A(ld->charData[i]);
4377d522f475Smrg#if OPT_WIDE_CHARS
4378d522f475Smrg	/* We want to strip out every occurrence of HIDDEN_CHAR AFTER a
4379d522f475Smrg	 * wide character.
4380d522f475Smrg	 */
4381894e0ac8Smrg	if (c == HIDDEN_CHAR) {
4382894e0ac8Smrg	    if (isWide((int) previous)) {
4383894e0ac8Smrg		previous = c;
4384894e0ac8Smrg		/* Combining characters attached to double-width characters
4385894e0ac8Smrg		   are in memory attached to the HIDDEN_CHAR */
4386894e0ac8Smrg		if_OPT_WIDE_CHARS(screen, {
4387894e0ac8Smrg		    if ((screen->utf8_nrc_mode | screen->utf8_mode) != uFalse) {
4388894e0ac8Smrg			size_t off;
4389894e0ac8Smrg			for_each_combData(off, ld) {
43902e4f8982Smrg			    unsigned ch = ld->combData[off][i];
4391894e0ac8Smrg			    if (ch == 0)
4392894e0ac8Smrg				break;
4393894e0ac8Smrg			    lp = convertToUTF8(lp, ch);
4394894e0ac8Smrg			}
4395d522f475Smrg		    }
4396894e0ac8Smrg		});
4397894e0ac8Smrg		continue;
4398894e0ac8Smrg	    } else {
4399894e0ac8Smrg		c = ' ';	/* should not happen, but just in case... */
4400894e0ac8Smrg	    }
4401d522f475Smrg	}
4402d522f475Smrg	previous = c;
4403e0a2b6dfSmrg	if ((screen->utf8_nrc_mode | screen->utf8_mode) != uFalse) {
4404d522f475Smrg	    lp = convertToUTF8(lp, (c != 0) ? c : ' ');
4405d522f475Smrg	    if_OPT_WIDE_CHARS(screen, {
4406956cc18dSsnj		size_t off;
4407956cc18dSsnj		for_each_combData(off, ld) {
44082e4f8982Smrg		    unsigned ch = ld->combData[off][i];
4409956cc18dSsnj		    if (ch == 0)
4410d522f475Smrg			break;
4411d522f475Smrg		    lp = convertToUTF8(lp, ch);
4412d522f475Smrg		}
4413d522f475Smrg	    });
4414d522f475Smrg	} else
4415d522f475Smrg#endif
4416d522f475Smrg	{
4417d522f475Smrg	    if (c == 0) {
4418d522f475Smrg		c = E2A(' ');
4419d522f475Smrg	    } else if (c < E2A(' ')) {
4420d522f475Smrg		c = DECtoASCII(c);
4421d522f475Smrg	    } else if (c == 0x7f) {
4422d522f475Smrg		c = 0x5f;
4423d522f475Smrg	    }
44242eaa94a1Schristos	    *lp++ = CharOf(A2E(c));
4425d522f475Smrg	}
4426d522f475Smrg	if (c != E2A(' '))
4427d522f475Smrg	    result = lp;
4428d522f475Smrg    }
4429d522f475Smrg
4430d522f475Smrg    /*
4431d522f475Smrg     * If requested, trim trailing blanks from selected lines.  Do not do this
4432d522f475Smrg     * if the line is wrapped.
4433d522f475Smrg     */
4434d522f475Smrg    if (!*eol || !screen->trim_selection)
4435d522f475Smrg	result = lp;
4436d522f475Smrg
4437d522f475Smrg    return (result);
4438d522f475Smrg}
4439d522f475Smrg
4440d522f475Smrg/* 32 + following 7-bit word:
4441d522f475Smrg
4442d522f475Smrg   1:0  Button no: 0, 1, 2.  3=release.
4443d522f475Smrg     2  shift
4444d522f475Smrg     3  meta
4445d522f475Smrg     4  ctrl
4446d522f475Smrg     5  set for motion notify
4447d522f475Smrg     6  set for wheel
4448d522f475Smrg*/
4449d522f475Smrg
4450d522f475Smrg/* Position: 32 - 255. */
4451a1f3da82Smrgstatic int
4452894e0ac8SmrgBtnCode(XButtonEvent *event, int button)
4453d522f475Smrg{
44542eaa94a1Schristos    int result = (int) (32 + (KeyState(event->state) << 2));
4455d522f475Smrg
44560bd37d32Smrg    if (event->type == MotionNotify)
44570bd37d32Smrg	result += 32;
44580bd37d32Smrg
4459d522f475Smrg    if (button < 0 || button > 5) {
4460d522f475Smrg	result += 3;
4461d522f475Smrg    } else {
4462d522f475Smrg	if (button > 3)
4463d522f475Smrg	    result += (64 - 4);
4464d522f475Smrg	result += button;
4465d522f475Smrg    }
44660bd37d32Smrg    TRACE(("BtnCode button %d, %s state " FMT_MODIFIER_NAMES " ->%#x\n",
44670bd37d32Smrg	   button,
44680bd37d32Smrg	   visibleEventType(event->type),
44690bd37d32Smrg	   ARG_MODIFIER_NAMES(event->state),
44700bd37d32Smrg	   result));
4471a1f3da82Smrg    return result;
4472a1f3da82Smrg}
4473a1f3da82Smrg
4474a1f3da82Smrgstatic unsigned
4475e0a2b6dfSmrgEmitButtonCode(TScreen *screen,
4476e0a2b6dfSmrg	       Char *line,
44770bd37d32Smrg	       unsigned count,
4478894e0ac8Smrg	       XButtonEvent *event,
44790bd37d32Smrg	       int button)
4480a1f3da82Smrg{
44810bd37d32Smrg    int value;
4482a1f3da82Smrg
44830bd37d32Smrg    if (screen->send_mouse_pos == X10_MOUSE) {
44840bd37d32Smrg	value = CharOf(' ' + button);
4485a1f3da82Smrg    } else {
44860bd37d32Smrg	value = BtnCode(event, button);
44870bd37d32Smrg    }
44880bd37d32Smrg
44890bd37d32Smrg    switch (screen->extend_coords) {
44900bd37d32Smrg    default:
44910bd37d32Smrg	line[count++] = CharOf(value);
44920bd37d32Smrg	break;
44930bd37d32Smrg    case SET_SGR_EXT_MODE_MOUSE:
44940bd37d32Smrg	value -= 32;		/* encoding starts at zero */
44950bd37d32Smrg	/* FALLTHRU */
44960bd37d32Smrg    case SET_URXVT_EXT_MODE_MOUSE:
44970bd37d32Smrg	count += (unsigned) sprintf((char *) line + count, "%d", value);
44980bd37d32Smrg	break;
44990bd37d32Smrg    case SET_EXT_MODE_MOUSE:
45000bd37d32Smrg	if (value < 128) {
45010bd37d32Smrg	    line[count++] = CharOf(value);
45020bd37d32Smrg	} else {
45030bd37d32Smrg	    line[count++] = CharOf(0xC0 + (value >> 6));
45040bd37d32Smrg	    line[count++] = CharOf(0x80 + (value & 0x3F));
45050bd37d32Smrg	}
45060bd37d32Smrg	break;
4507a1f3da82Smrg    }
4508a1f3da82Smrg    return count;
4509d522f475Smrg}
4510d522f475Smrg
45110bd37d32Smrgstatic int
45120bd37d32SmrgFirstBitN(int bits)
45130bd37d32Smrg{
45140bd37d32Smrg    int result = -1;
45150bd37d32Smrg    if (bits > 0) {
45160bd37d32Smrg	result = 0;
45170bd37d32Smrg	while (!(bits & 1)) {
45180bd37d32Smrg	    bits /= 2;
45190bd37d32Smrg	    ++result;
45200bd37d32Smrg	}
45210bd37d32Smrg    }
45220bd37d32Smrg    return result;
45230bd37d32Smrg}
45240bd37d32Smrg
45250bd37d32Smrg#define ButtonBit(button) ((button >= 0) ? (1 << (button)) : 0)
45260bd37d32Smrg
45270bd37d32Smrg#define EMIT_BUTTON(button) EmitButtonCode(screen, line, count, event, button)
45280bd37d32Smrg
4529d522f475Smrgstatic void
4530894e0ac8SmrgEditorButton(XtermWidget xw, XButtonEvent *event)
4531d522f475Smrg{
4532956cc18dSsnj    TScreen *screen = TScreenOf(xw);
4533d522f475Smrg    int pty = screen->respond;
45340bd37d32Smrg    int mouse_limit = MouseLimit(screen);
45350bd37d32Smrg    Char line[32];
45360bd37d32Smrg    Char final = 'M';
4537d522f475Smrg    int row, col;
4538d522f475Smrg    int button;
4539d522f475Smrg    unsigned count = 0;
4540d522f475Smrg    Boolean changed = True;
4541d522f475Smrg
4542d522f475Smrg    /* If button event, get button # adjusted for DEC compatibility */
45432eaa94a1Schristos    button = (int) (event->button - 1);
4544d522f475Smrg    if (button >= 3)
4545d522f475Smrg	button++;
4546d522f475Smrg
4547d522f475Smrg    /* Compute character position of mouse pointer */
4548d522f475Smrg    row = (event->y - screen->border) / FontHeight(screen);
4549d522f475Smrg    col = (event->x - OriginX(screen)) / FontWidth(screen);
4550d522f475Smrg
4551d522f475Smrg    /* Limit to screen dimensions */
4552d522f475Smrg    if (row < 0)
4553d522f475Smrg	row = 0;
4554d522f475Smrg    else if (row > screen->max_row)
4555d522f475Smrg	row = screen->max_row;
4556d522f475Smrg
4557d522f475Smrg    if (col < 0)
4558d522f475Smrg	col = 0;
4559d522f475Smrg    else if (col > screen->max_col)
4560d522f475Smrg	col = screen->max_col;
4561492d43a5Smrg
45620bd37d32Smrg    if (mouse_limit > 0) {
45630bd37d32Smrg	/* Limit to representable mouse dimensions */
45640bd37d32Smrg	if (row > mouse_limit)
45650bd37d32Smrg	    row = mouse_limit;
45660bd37d32Smrg	if (col > mouse_limit)
45670bd37d32Smrg	    col = mouse_limit;
45680bd37d32Smrg    }
4569d522f475Smrg
4570d522f475Smrg    /* Build key sequence starting with \E[M */
4571d522f475Smrg    if (screen->control_eight_bits) {
4572d522f475Smrg	line[count++] = ANSI_CSI;
4573d522f475Smrg    } else {
4574d522f475Smrg	line[count++] = ANSI_ESC;
4575d522f475Smrg	line[count++] = '[';
4576d522f475Smrg    }
45770bd37d32Smrg    switch (screen->extend_coords) {
45780bd37d32Smrg    case 0:
45790bd37d32Smrg    case SET_EXT_MODE_MOUSE:
4580d522f475Smrg#if OPT_SCO_FUNC_KEYS
45810bd37d32Smrg	if (xw->keyboard.type == keyboardIsSCO) {
45820bd37d32Smrg	    /*
45830bd37d32Smrg	     * SCO function key F1 is \E[M, which would conflict with xterm's
45840bd37d32Smrg	     * normal kmous.
45850bd37d32Smrg	     */
45860bd37d32Smrg	    line[count++] = '>';
45870bd37d32Smrg	}
4588d522f475Smrg#endif
45890bd37d32Smrg	line[count++] = final;
45900bd37d32Smrg	break;
45910bd37d32Smrg    case SET_SGR_EXT_MODE_MOUSE:
45920bd37d32Smrg	line[count++] = '<';
45930bd37d32Smrg	break;
45940bd37d32Smrg    }
4595d522f475Smrg
4596d522f475Smrg    /* Add event code to key sequence */
4597d522f475Smrg    if (screen->send_mouse_pos == X10_MOUSE) {
45980bd37d32Smrg	count = EMIT_BUTTON(button);
4599d522f475Smrg    } else {
4600d522f475Smrg	/* Button-Motion events */
4601d522f475Smrg	switch (event->type) {
4602d522f475Smrg	case ButtonPress:
46030bd37d32Smrg	    screen->mouse_button |= ButtonBit(button);
46040bd37d32Smrg	    count = EMIT_BUTTON(button);
4605d522f475Smrg	    break;
4606d522f475Smrg	case ButtonRelease:
4607d522f475Smrg	    /*
4608d522f475Smrg	     * Wheel mouse interface generates release-events for buttons
4609d522f475Smrg	     * 4 and 5, coded here as 3 and 4 respectively.  We change the
46100bd37d32Smrg	     * release for buttons 1..3 to a -1, which will be later mapped
46110bd37d32Smrg	     * into a "0" (some button was released).
4612d522f475Smrg	     */
46130bd37d32Smrg	    screen->mouse_button &= ~ButtonBit(button);
46140bd37d32Smrg	    if (button < 3) {
46150bd37d32Smrg		switch (screen->extend_coords) {
46160bd37d32Smrg		case SET_SGR_EXT_MODE_MOUSE:
46170bd37d32Smrg		    final = 'm';
46180bd37d32Smrg		    break;
46190bd37d32Smrg		default:
46200bd37d32Smrg		    button = -1;
46210bd37d32Smrg		    break;
46220bd37d32Smrg		}
46230bd37d32Smrg	    }
46240bd37d32Smrg	    count = EMIT_BUTTON(button);
4625d522f475Smrg	    break;
4626d522f475Smrg	case MotionNotify:
4627d522f475Smrg	    /* BTN_EVENT_MOUSE and ANY_EVENT_MOUSE modes send motion
4628d522f475Smrg	     * events only if character cell has changed.
4629d522f475Smrg	     */
4630d522f475Smrg	    if ((row == screen->mouse_row)
4631d522f475Smrg		&& (col == screen->mouse_col)) {
4632d522f475Smrg		changed = False;
4633d522f475Smrg	    } else {
46340bd37d32Smrg		count = EMIT_BUTTON(FirstBitN(screen->mouse_button));
4635d522f475Smrg	    }
4636d522f475Smrg	    break;
4637d522f475Smrg	default:
4638d522f475Smrg	    changed = False;
4639d522f475Smrg	    break;
4640d522f475Smrg	}
4641d522f475Smrg    }
4642d522f475Smrg
4643d522f475Smrg    if (changed) {
4644d522f475Smrg	screen->mouse_row = row;
4645d522f475Smrg	screen->mouse_col = col;
4646d522f475Smrg
4647492d43a5Smrg	TRACE(("mouse at %d,%d button+mask = %#x\n", row, col, line[count - 1]));
4648d522f475Smrg
4649492d43a5Smrg	/* Add pointer position to key sequence */
46500bd37d32Smrg	count = EmitMousePositionSeparator(screen, line, count);
4651492d43a5Smrg	count = EmitMousePosition(screen, line, count, col);
46520bd37d32Smrg	count = EmitMousePositionSeparator(screen, line, count);
4653492d43a5Smrg	count = EmitMousePosition(screen, line, count, row);
4654d522f475Smrg
46550bd37d32Smrg	switch (screen->extend_coords) {
46560bd37d32Smrg	case SET_SGR_EXT_MODE_MOUSE:
46570bd37d32Smrg	case SET_URXVT_EXT_MODE_MOUSE:
46580bd37d32Smrg	    line[count++] = final;
46590bd37d32Smrg	    break;
46600bd37d32Smrg	}
46610bd37d32Smrg
4662d522f475Smrg	/* Transmit key sequence to process running under xterm */
4663d522f475Smrg	v_write(pty, line, count);
4664d522f475Smrg    }
4665d522f475Smrg    return;
4666d522f475Smrg}
4667d522f475Smrg
4668d522f475Smrg#if OPT_FOCUS_EVENT
4669d522f475Smrgvoid
4670894e0ac8SmrgSendFocusButton(XtermWidget xw, XFocusChangeEvent *event)
4671d522f475Smrg{
4672956cc18dSsnj    TScreen *screen = TScreenOf(xw);
4673d522f475Smrg
4674d522f475Smrg    if (screen->send_focus_pos) {
4675d522f475Smrg	ANSI reply;
4676d522f475Smrg
4677d522f475Smrg	memset(&reply, 0, sizeof(reply));
4678d522f475Smrg	reply.a_type = ANSI_CSI;
4679d522f475Smrg
4680d522f475Smrg#if OPT_SCO_FUNC_KEYS
4681d522f475Smrg	if (xw->keyboard.type == keyboardIsSCO) {
4682d522f475Smrg	    reply.a_pintro = '>';
4683d522f475Smrg	}
4684d522f475Smrg#endif
46852eaa94a1Schristos	reply.a_final = CharOf((event->type == FocusIn) ? 'I' : 'O');
4686d522f475Smrg	unparseseq(xw, &reply);
4687d522f475Smrg    }
4688d522f475Smrg    return;
4689d522f475Smrg}
4690d522f475Smrg#endif /* OPT_FOCUS_EVENT */
46910bd37d32Smrg
46920bd37d32Smrg#if OPT_SELECTION_OPS
46930bd37d32Smrg/*
46940bd37d32Smrg * Get the event-time, needed to process selections.
46950bd37d32Smrg */
46960bd37d32Smrgstatic Time
4697894e0ac8SmrggetEventTime(XEvent *event)
46980bd37d32Smrg{
46990bd37d32Smrg    Time result;
47000bd37d32Smrg
47010bd37d32Smrg    if (IsBtnEvent(event)) {
47020bd37d32Smrg	result = ((XButtonEvent *) event)->time;
47030bd37d32Smrg    } else if (IsKeyEvent(event)) {
47040bd37d32Smrg	result = ((XKeyEvent *) event)->time;
47050bd37d32Smrg    } else {
47060bd37d32Smrg	result = 0;
47070bd37d32Smrg    }
47080bd37d32Smrg
47090bd37d32Smrg    return result;
47100bd37d32Smrg}
47110bd37d32Smrg
47120bd37d32Smrg/* obtain the selection string, passing the endpoints to caller's parameters */
471301037d57Smrgstatic void
471401037d57SmrgdoSelectionFormat(XtermWidget xw,
471501037d57Smrg		  Widget w,
471601037d57Smrg		  XEvent *event,
471701037d57Smrg		  String *params,
471801037d57Smrg		  Cardinal *num_params,
471901037d57Smrg		  FormatSelect format_select)
47200bd37d32Smrg{
47210bd37d32Smrg    TScreen *screen = TScreenOf(xw);
472201037d57Smrg    InternalSelect *mydata = &(screen->internal_select);
472301037d57Smrg
472401037d57Smrg    memset(mydata, 0, sizeof(*mydata));
472501037d57Smrg    mydata->format = x_strdup(params[0]);
472601037d57Smrg    mydata->format_select = format_select;
47270bd37d32Smrg
47280bd37d32Smrg    /* override flags so that SelectionReceived only updates a buffer */
47290bd37d32Smrg#if OPT_PASTE64
473001037d57Smrg    mydata->base64_paste = screen->base64_paste;
47310bd37d32Smrg    screen->base64_paste = 0;
47320bd37d32Smrg#endif
47330bd37d32Smrg#if OPT_READLINE
473401037d57Smrg    mydata->paste_brackets = screen->paste_brackets;
47350bd37d32Smrg    SCREEN_FLAG_unset(screen, paste_brackets);
47360bd37d32Smrg#endif
47370bd37d32Smrg
47380bd37d32Smrg    screen->selectToBuffer = True;
47390bd37d32Smrg    xtermGetSelection(w, getEventTime(event), params + 1, *num_params - 1, NULL);
47400bd37d32Smrg}
47410bd37d32Smrg
47420bd37d32Smrg/* obtain data from the screen, passing the endpoints to caller's parameters */
47430bd37d32Smrgstatic char *
4744e0a2b6dfSmrggetDataFromScreen(XtermWidget xw, String method, CELL *start, CELL *finish)
47450bd37d32Smrg{
47460bd37d32Smrg    TScreen *screen = TScreenOf(xw);
47470bd37d32Smrg
47480bd37d32Smrg    CELL save_old_start = screen->startH;
47490bd37d32Smrg    CELL save_old_end = screen->endH;
47500bd37d32Smrg
47510bd37d32Smrg    CELL save_startSel = screen->startSel;
47520bd37d32Smrg    CELL save_startRaw = screen->startRaw;
47530bd37d32Smrg    CELL save_finishSel = screen->endSel;
47540bd37d32Smrg    CELL save_finishRaw = screen->endRaw;
47550bd37d32Smrg
47560bd37d32Smrg    int save_firstValidRow = screen->firstValidRow;
47570bd37d32Smrg    int save_lastValidRow = screen->lastValidRow;
47580bd37d32Smrg
475901037d57Smrg    const Cardinal noClick = 0;
476001037d57Smrg    int save_numberOfClicks = screen->numberOfClicks;
476101037d57Smrg
47620bd37d32Smrg    SelectUnit saveUnits = screen->selectUnit;
476301037d57Smrg    SelectUnit saveMap = screen->selectMap[noClick];
47640bd37d32Smrg#if OPT_SELECT_REGEX
476501037d57Smrg    char *saveExpr = screen->selectExpr[noClick];
47660bd37d32Smrg#endif
47670bd37d32Smrg
47680bd37d32Smrg    Char *save_selection_data = screen->selection_data;
47690bd37d32Smrg    int save_selection_size = screen->selection_size;
47700bd37d32Smrg    unsigned long save_selection_length = screen->selection_length;
47710bd37d32Smrg
47720bd37d32Smrg    char *result = 0;
47730bd37d32Smrg
47740bd37d32Smrg    TRACE(("getDataFromScreen %s\n", method));
47750bd37d32Smrg
47760bd37d32Smrg    screen->selection_data = 0;
47770bd37d32Smrg    screen->selection_size = 0;
47780bd37d32Smrg    screen->selection_length = 0;
47790bd37d32Smrg
478001037d57Smrg    screen->numberOfClicks = 1;
478101037d57Smrg    lookupSelectUnit(xw, noClick, method);
478201037d57Smrg    screen->selectUnit = screen->selectMap[noClick];
47830bd37d32Smrg
47840bd37d32Smrg    memset(start, 0, sizeof(*start));
47850bd37d32Smrg    start->row = screen->cur_row;
47860bd37d32Smrg    start->col = screen->cur_col;
478701037d57Smrg    finish->row = screen->cur_row;
478801037d57Smrg    finish->col = screen->max_col;
47890bd37d32Smrg
47900bd37d32Smrg    ComputeSelect(xw, start, finish, False);
47910bd37d32Smrg    SaltTextAway(xw, &(screen->startSel), &(screen->endSel));
47920bd37d32Smrg
47930bd37d32Smrg    if (screen->selection_length && screen->selection_data) {
47940bd37d32Smrg	TRACE(("...getDataFromScreen selection_data %.*s\n",
47950bd37d32Smrg	       (int) screen->selection_length,
47960bd37d32Smrg	       screen->selection_data));
47970bd37d32Smrg	result = malloc(screen->selection_length + 1);
47980bd37d32Smrg	if (result) {
47990bd37d32Smrg	    memcpy(result, screen->selection_data, screen->selection_length);
48000bd37d32Smrg	    result[screen->selection_length] = 0;
48010bd37d32Smrg	}
48020bd37d32Smrg	free(screen->selection_data);
48030bd37d32Smrg    }
48040bd37d32Smrg
48050bd37d32Smrg    TRACE(("...getDataFromScreen restoring previous selection\n"));
48060bd37d32Smrg
48070bd37d32Smrg    screen->startSel = save_startSel;
48080bd37d32Smrg    screen->startRaw = save_startRaw;
48090bd37d32Smrg    screen->endSel = save_finishSel;
48100bd37d32Smrg    screen->endRaw = save_finishRaw;
48110bd37d32Smrg
48120bd37d32Smrg    screen->firstValidRow = save_firstValidRow;
48130bd37d32Smrg    screen->lastValidRow = save_lastValidRow;
48140bd37d32Smrg
481501037d57Smrg    screen->numberOfClicks = save_numberOfClicks;
48160bd37d32Smrg    screen->selectUnit = saveUnits;
481701037d57Smrg    screen->selectMap[noClick] = saveMap;
48180bd37d32Smrg#if OPT_SELECT_REGEX
481901037d57Smrg    screen->selectExpr[noClick] = saveExpr;
48200bd37d32Smrg#endif
48210bd37d32Smrg
48220bd37d32Smrg    screen->selection_data = save_selection_data;
48230bd37d32Smrg    screen->selection_size = save_selection_size;
48240bd37d32Smrg    screen->selection_length = save_selection_length;
48250bd37d32Smrg
48260bd37d32Smrg    TrackText(xw, &save_old_start, &save_old_end);
48270bd37d32Smrg
48280bd37d32Smrg    TRACE(("...getDataFromScreen done\n"));
48290bd37d32Smrg    return result;
48300bd37d32Smrg}
48310bd37d32Smrg
48320bd37d32Smrg/*
48330bd37d32Smrg * Split-up the format before substituting data, to avoid quoting issues.
48340bd37d32Smrg * The resource mechanism has a limited ability to handle escapes.  We take
48350bd37d32Smrg * the result as if it were an sh-type string and parse it into a regular
48360bd37d32Smrg * argv array.
48370bd37d32Smrg */
48380bd37d32Smrgstatic char **
48390bd37d32SmrgtokenizeFormat(String format)
48400bd37d32Smrg{
48410bd37d32Smrg    char **result = 0;
48420bd37d32Smrg    int argc;
48430bd37d32Smrg
48440bd37d32Smrg    format = x_skip_blanks(format);
48450bd37d32Smrg    if (*format != '\0') {
48460bd37d32Smrg	char *blob = x_strdup(format);
48472e4f8982Smrg	int pass;
48480bd37d32Smrg
48490bd37d32Smrg	for (pass = 0; pass < 2; ++pass) {
48500bd37d32Smrg	    int used = 0;
48510bd37d32Smrg	    int first = 1;
48520bd37d32Smrg	    int escaped = 0;
48530bd37d32Smrg	    int squoted = 0;
48540bd37d32Smrg	    int dquoted = 0;
48552e4f8982Smrg	    int n;
48560bd37d32Smrg
48570bd37d32Smrg	    argc = 0;
48580bd37d32Smrg	    for (n = 0; format[n] != '\0'; ++n) {
48590bd37d32Smrg		if (escaped) {
48600bd37d32Smrg		    blob[used++] = format[n];
48610bd37d32Smrg		    escaped = 0;
48620bd37d32Smrg		} else if (format[n] == '"') {
48630bd37d32Smrg		    if (!squoted) {
48640bd37d32Smrg			if (!dquoted)
48650bd37d32Smrg			    blob[used++] = format[n];
48660bd37d32Smrg			dquoted = !dquoted;
48670bd37d32Smrg		    }
48680bd37d32Smrg		} else if (format[n] == '\'') {
48690bd37d32Smrg		    if (!dquoted) {
48700bd37d32Smrg			if (!squoted)
48710bd37d32Smrg			    blob[used++] = format[n];
48720bd37d32Smrg			squoted = !squoted;
48730bd37d32Smrg		    }
48740bd37d32Smrg		} else if (format[n] == '\\') {
48750bd37d32Smrg		    blob[used++] = format[n];
48760bd37d32Smrg		    escaped = 1;
48770bd37d32Smrg		} else {
48780bd37d32Smrg		    if (first) {
48790bd37d32Smrg			first = 0;
48800bd37d32Smrg			if (pass) {
48810bd37d32Smrg			    result[argc] = &blob[n];
48820bd37d32Smrg			}
48830bd37d32Smrg			++argc;
48840bd37d32Smrg		    }
48850bd37d32Smrg		    if (isspace((Char) format[n])) {
48860bd37d32Smrg			first = !isspace((Char) format[n + 1]);
48870bd37d32Smrg			if (squoted || dquoted) {
48880bd37d32Smrg			    blob[used++] = format[n];
48890bd37d32Smrg			} else if (first) {
48900bd37d32Smrg			    blob[used++] = '\0';
48910bd37d32Smrg			}
48920bd37d32Smrg		    } else {
48930bd37d32Smrg			blob[used++] = format[n];
48940bd37d32Smrg		    }
48950bd37d32Smrg		}
48960bd37d32Smrg	    }
48970bd37d32Smrg	    blob[used] = '\0';
48980bd37d32Smrg	    assert(strlen(blob) <= strlen(format));
48990bd37d32Smrg	    if (!pass) {
49000bd37d32Smrg		result = TypeCallocN(char *, argc + 1);
49010bd37d32Smrg		if (result == 0) {
49020bd37d32Smrg		    free(blob);
49030bd37d32Smrg		    break;
49040bd37d32Smrg		}
49050bd37d32Smrg	    }
49060bd37d32Smrg	}
49070bd37d32Smrg    }
49080bd37d32Smrg#if OPT_TRACE
49090bd37d32Smrg    if (result) {
49100bd37d32Smrg	TRACE(("tokenizeFormat %s\n", format));
49110bd37d32Smrg	for (argc = 0; result[argc]; ++argc) {
49120bd37d32Smrg	    TRACE(("argv[%d] = %s\n", argc, result[argc]));
49130bd37d32Smrg	}
49140bd37d32Smrg    }
49150bd37d32Smrg#endif
49160bd37d32Smrg
49170bd37d32Smrg    return result;
49180bd37d32Smrg}
49190bd37d32Smrg
49200bd37d32Smrgstatic void
4921e0a2b6dfSmrgformatVideoAttrs(XtermWidget xw, char *buffer, CELL *cell)
49220bd37d32Smrg{
49230bd37d32Smrg    TScreen *screen = TScreenOf(xw);
49240bd37d32Smrg    LineData *ld = GET_LINEDATA(screen, cell->row);
49250bd37d32Smrg
49260bd37d32Smrg    *buffer = '\0';
49270bd37d32Smrg    if (ld != 0 && cell->col < (int) ld->lineSize) {
4928894e0ac8Smrg	IAttr attribs = ld->attribs[cell->col];
49290bd37d32Smrg	const char *delim = "";
49300bd37d32Smrg
49310bd37d32Smrg	if (attribs & INVERSE) {
49320bd37d32Smrg	    buffer += sprintf(buffer, "7");
49330bd37d32Smrg	    delim = ";";
49340bd37d32Smrg	}
49350bd37d32Smrg	if (attribs & UNDERLINE) {
49360bd37d32Smrg	    buffer += sprintf(buffer, "%s4", delim);
49370bd37d32Smrg	    delim = ";";
49380bd37d32Smrg	}
49390bd37d32Smrg	if (attribs & BOLD) {
49400bd37d32Smrg	    buffer += sprintf(buffer, "%s1", delim);
49410bd37d32Smrg	    delim = ";";
49420bd37d32Smrg	}
49430bd37d32Smrg	if (attribs & BLINK) {
49440bd37d32Smrg	    buffer += sprintf(buffer, "%s5", delim);
49450bd37d32Smrg	    delim = ";";
49460bd37d32Smrg	}
49470bd37d32Smrg#if OPT_ISO_COLORS
49480bd37d32Smrg	if (attribs & FG_COLOR) {
49490bd37d32Smrg	    unsigned fg = extract_fg(xw, ld->color[cell->col], attribs);
49500bd37d32Smrg	    if (fg < 8) {
49510bd37d32Smrg		fg += 30;
49520bd37d32Smrg	    } else if (fg < 16) {
49530bd37d32Smrg		fg += 90;
49540bd37d32Smrg	    } else {
49550bd37d32Smrg		buffer += sprintf(buffer, "%s38;5", delim);
49560bd37d32Smrg		delim = ";";
49570bd37d32Smrg	    }
49580bd37d32Smrg	    buffer += sprintf(buffer, "%s%u", delim, fg);
49590bd37d32Smrg	    delim = ";";
49600bd37d32Smrg	}
49610bd37d32Smrg	if (attribs & BG_COLOR) {
49620bd37d32Smrg	    unsigned bg = extract_bg(xw, ld->color[cell->col], attribs);
49630bd37d32Smrg	    if (bg < 8) {
49640bd37d32Smrg		bg += 40;
49650bd37d32Smrg	    } else if (bg < 16) {
49660bd37d32Smrg		bg += 100;
49670bd37d32Smrg	    } else {
49680bd37d32Smrg		buffer += sprintf(buffer, "%s48;5", delim);
49690bd37d32Smrg		delim = ";";
49700bd37d32Smrg	    }
49710bd37d32Smrg	    (void) sprintf(buffer, "%s%u", delim, bg);
49720bd37d32Smrg	}
49730bd37d32Smrg#endif
49740bd37d32Smrg    }
49750bd37d32Smrg}
49760bd37d32Smrg
49772e4f8982Smrgstatic char *
49782e4f8982SmrgformatStrlen(char *target, char *source, int freeit)
49792e4f8982Smrg{
49802e4f8982Smrg    if (source != 0) {
49812e4f8982Smrg	sprintf(target, "%u", (unsigned) strlen(source));
49822e4f8982Smrg	if (freeit) {
49832e4f8982Smrg	    free(source);
49842e4f8982Smrg	}
49852e4f8982Smrg    } else {
49862e4f8982Smrg	strcpy(target, "0");
49872e4f8982Smrg    }
49882e4f8982Smrg    return target;
49892e4f8982Smrg}
49902e4f8982Smrg
49910bd37d32Smrg/* substitute data into format, reallocating the result */
49920bd37d32Smrgstatic char *
49930bd37d32SmrgexpandFormat(XtermWidget xw,
49940bd37d32Smrg	     const char *format,
49950bd37d32Smrg	     char *data,
4996e0a2b6dfSmrg	     CELL *start,
4997e0a2b6dfSmrg	     CELL *finish)
49980bd37d32Smrg{
49990bd37d32Smrg    char *result = 0;
50000bd37d32Smrg    if (!IsEmpty(format)) {
50010bd37d32Smrg	static char empty[1];
50020bd37d32Smrg	int pass;
50030bd37d32Smrg	int n;
50040bd37d32Smrg	char numbers[80];
50050bd37d32Smrg
50060bd37d32Smrg	if (data == 0)
50070bd37d32Smrg	    data = empty;
50080bd37d32Smrg
50090bd37d32Smrg	for (pass = 0; pass < 2; ++pass) {
50100bd37d32Smrg	    size_t need = 0;
50110bd37d32Smrg
50120bd37d32Smrg	    for (n = 0; format[n] != '\0'; ++n) {
50130bd37d32Smrg
50140bd37d32Smrg		if (format[n] == '%') {
50152e4f8982Smrg		    char *value = 0;
50162e4f8982Smrg
50170bd37d32Smrg		    switch (format[++n]) {
50180bd37d32Smrg		    case '%':
50190bd37d32Smrg			if (pass) {
50200bd37d32Smrg			    result[need] = format[n];
50210bd37d32Smrg			}
50220bd37d32Smrg			++need;
50230bd37d32Smrg			break;
50240bd37d32Smrg		    case 'P':
50250bd37d32Smrg			sprintf(numbers, "%d;%d",
50260bd37d32Smrg				TScreenOf(xw)->topline + start->row + 1,
50270bd37d32Smrg				start->col + 1);
50280bd37d32Smrg			value = numbers;
50290bd37d32Smrg			break;
50300bd37d32Smrg		    case 'p':
50310bd37d32Smrg			sprintf(numbers, "%d;%d",
50320bd37d32Smrg				TScreenOf(xw)->topline + finish->row + 1,
50330bd37d32Smrg				finish->col + 1);
50340bd37d32Smrg			value = numbers;
50350bd37d32Smrg			break;
50362e4f8982Smrg		    case 'R':
50372e4f8982Smrg			value = formatStrlen(numbers, x_strrtrim(data), 1);
50382e4f8982Smrg			break;
50392e4f8982Smrg		    case 'r':
50402e4f8982Smrg			value = x_strrtrim(data);
50412e4f8982Smrg			break;
50420bd37d32Smrg		    case 'S':
50432e4f8982Smrg			value = formatStrlen(numbers, data, 0);
50440bd37d32Smrg			break;
50450bd37d32Smrg		    case 's':
50460bd37d32Smrg			value = data;
50470bd37d32Smrg			break;
50480bd37d32Smrg		    case 'T':
50492e4f8982Smrg			value = formatStrlen(numbers, x_strtrim(data), 1);
50500bd37d32Smrg			break;
50510bd37d32Smrg		    case 't':
50520bd37d32Smrg			value = x_strtrim(data);
50530bd37d32Smrg			break;
50540bd37d32Smrg		    case 'V':
50550bd37d32Smrg			formatVideoAttrs(xw, numbers, start);
50560bd37d32Smrg			value = numbers;
50570bd37d32Smrg			break;
50580bd37d32Smrg		    case 'v':
50590bd37d32Smrg			formatVideoAttrs(xw, numbers, finish);
50600bd37d32Smrg			value = numbers;
50610bd37d32Smrg			break;
50620bd37d32Smrg		    default:
50630bd37d32Smrg			if (pass) {
50640bd37d32Smrg			    result[need] = format[n];
50650bd37d32Smrg			}
50660bd37d32Smrg			--n;
50670bd37d32Smrg			++need;
50680bd37d32Smrg			break;
50690bd37d32Smrg		    }
50700bd37d32Smrg		    if (value != 0) {
50710bd37d32Smrg			if (pass) {
50720bd37d32Smrg			    strcpy(result + need, value);
50730bd37d32Smrg			}
50740bd37d32Smrg			need += strlen(value);
50750bd37d32Smrg			if (value != numbers && value != data) {
50760bd37d32Smrg			    free(value);
50770bd37d32Smrg			}
50780bd37d32Smrg		    }
50790bd37d32Smrg		} else {
50800bd37d32Smrg		    if (pass) {
50810bd37d32Smrg			result[need] = format[n];
50820bd37d32Smrg		    }
50830bd37d32Smrg		    ++need;
50840bd37d32Smrg		}
50850bd37d32Smrg	    }
50860bd37d32Smrg	    if (pass) {
50870bd37d32Smrg		result[need] = '\0';
50880bd37d32Smrg	    } else {
50890bd37d32Smrg		++need;
50900bd37d32Smrg		result = malloc(need);
50910bd37d32Smrg		if (result == 0) {
50920bd37d32Smrg		    break;
50930bd37d32Smrg		}
50940bd37d32Smrg	    }
50950bd37d32Smrg	}
50960bd37d32Smrg    }
50970bd37d32Smrg    TRACE(("expandFormat(%s) = %s\n", NonNull(format), NonNull(result)));
50980bd37d32Smrg    return result;
50990bd37d32Smrg}
51000bd37d32Smrg
51010bd37d32Smrg/* execute the command after forking.  The main process frees its data */
51020bd37d32Smrgstatic void
51032e4f8982SmrgexecuteCommand(pid_t pid, char **argv)
51040bd37d32Smrg{
51052e4f8982Smrg    (void) pid;
51060bd37d32Smrg    if (argv != 0 && argv[0] != 0) {
51072e4f8982Smrg	char *child_cwd = ProcGetCWD(pid);
51082e4f8982Smrg
51090bd37d32Smrg	if (fork() == 0) {
51102e4f8982Smrg	    if (child_cwd) {
51112e4f8982Smrg		IGNORE_RC(chdir(child_cwd));	/* We don't care if this fails */
51122e4f8982Smrg	    }
51130bd37d32Smrg	    execvp(argv[0], argv);
51140bd37d32Smrg	    exit(EXIT_FAILURE);
51150bd37d32Smrg	}
51160bd37d32Smrg    }
51170bd37d32Smrg}
51180bd37d32Smrg
51190bd37d32Smrgstatic void
51200bd37d32SmrgfreeArgv(char *blob, char **argv)
51210bd37d32Smrg{
51220bd37d32Smrg    if (blob) {
51230bd37d32Smrg	free(blob);
51240bd37d32Smrg	if (argv) {
51252e4f8982Smrg	    int n;
51260bd37d32Smrg	    for (n = 0; argv[n]; ++n)
51270bd37d32Smrg		free(argv[n]);
51280bd37d32Smrg	    free(argv);
51290bd37d32Smrg	}
51300bd37d32Smrg    }
51310bd37d32Smrg}
51320bd37d32Smrg
513301037d57Smrgstatic void
513401037d57SmrgreallyExecFormatted(Widget w, char *format, char *data, CELL *start, CELL *finish)
513501037d57Smrg{
513601037d57Smrg    XtermWidget xw;
513701037d57Smrg
513801037d57Smrg    if ((xw = getXtermWidget(w)) != 0) {
513901037d57Smrg	char **argv;
514001037d57Smrg
514101037d57Smrg	if ((argv = tokenizeFormat(format)) != 0) {
51422e4f8982Smrg	    char *blob = argv[0];
51432e4f8982Smrg	    int argc;
51442e4f8982Smrg
514501037d57Smrg	    for (argc = 0; argv[argc] != 0; ++argc) {
514601037d57Smrg		argv[argc] = expandFormat(xw, argv[argc], data, start, finish);
514701037d57Smrg	    }
51482e4f8982Smrg	    executeCommand(TScreenOf(xw)->pid, argv);
514901037d57Smrg	    freeArgv(blob, argv);
515001037d57Smrg	}
515101037d57Smrg    }
515201037d57Smrg}
515301037d57Smrg
51540bd37d32Smrgvoid
51550bd37d32SmrgHandleExecFormatted(Widget w,
515601037d57Smrg		    XEvent *event,
5157e0a2b6dfSmrg		    String *params,	/* selections */
51580bd37d32Smrg		    Cardinal *num_params)
51590bd37d32Smrg{
51600bd37d32Smrg    XtermWidget xw;
51610bd37d32Smrg
516201037d57Smrg    TRACE(("HandleExecFormatted(%d)\n", *num_params));
516301037d57Smrg    if ((xw = getXtermWidget(w)) != 0 &&
516401037d57Smrg	(*num_params > 1)) {
516501037d57Smrg	doSelectionFormat(xw, w, event, params, num_params, reallyExecFormatted);
51660bd37d32Smrg    }
51670bd37d32Smrg}
51680bd37d32Smrg
51690bd37d32Smrgvoid
51700bd37d32SmrgHandleExecSelectable(Widget w,
5171894e0ac8Smrg		     XEvent *event GCC_UNUSED,
5172e0a2b6dfSmrg		     String *params,	/* selections */
51730bd37d32Smrg		     Cardinal *num_params)
51740bd37d32Smrg{
51750bd37d32Smrg    XtermWidget xw;
51760bd37d32Smrg
51770bd37d32Smrg    if ((xw = getXtermWidget(w)) != 0) {
51780bd37d32Smrg	TRACE(("HandleExecSelectable(%d)\n", *num_params));
51790bd37d32Smrg
51800bd37d32Smrg	if (*num_params == 2) {
51810bd37d32Smrg	    CELL start, finish;
51820bd37d32Smrg	    char *data;
51830bd37d32Smrg	    char **argv;
51840bd37d32Smrg
51850bd37d32Smrg	    data = getDataFromScreen(xw, params[1], &start, &finish);
51860bd37d32Smrg	    if (data != 0) {
51870bd37d32Smrg		if ((argv = tokenizeFormat(params[0])) != 0) {
51882e4f8982Smrg		    char *blob = argv[0];
51892e4f8982Smrg		    int argc;
51902e4f8982Smrg
51910bd37d32Smrg		    for (argc = 0; argv[argc] != 0; ++argc) {
51920bd37d32Smrg			argv[argc] = expandFormat(xw, argv[argc], data,
51930bd37d32Smrg						  &start, &finish);
51940bd37d32Smrg		    }
51952e4f8982Smrg		    executeCommand(TScreenOf(xw)->pid, argv);
51960bd37d32Smrg		    freeArgv(blob, argv);
51970bd37d32Smrg		}
5198894e0ac8Smrg		free(data);
51990bd37d32Smrg	    }
52000bd37d32Smrg	}
52010bd37d32Smrg    }
52020bd37d32Smrg}
52030bd37d32Smrg
520401037d57Smrgstatic void
520501037d57SmrgreallyInsertFormatted(Widget w, char *format, char *data, CELL *start, CELL *finish)
520601037d57Smrg{
520701037d57Smrg    XtermWidget xw;
520801037d57Smrg
520901037d57Smrg    if ((xw = getXtermWidget(w)) != 0) {
521001037d57Smrg	char *exps;
521101037d57Smrg
521201037d57Smrg	if ((exps = expandFormat(xw, format, data, start, finish)) != 0) {
521301037d57Smrg	    unparseputs(xw, exps);
521401037d57Smrg	    unparse_end(xw);
521501037d57Smrg	    free(exps);
521601037d57Smrg	}
521701037d57Smrg    }
521801037d57Smrg}
521901037d57Smrg
52200bd37d32Smrgvoid
52210bd37d32SmrgHandleInsertFormatted(Widget w,
522201037d57Smrg		      XEvent *event,
5223e0a2b6dfSmrg		      String *params,	/* selections */
52240bd37d32Smrg		      Cardinal *num_params)
52250bd37d32Smrg{
52260bd37d32Smrg    XtermWidget xw;
52270bd37d32Smrg
522801037d57Smrg    TRACE(("HandleInsertFormatted(%d)\n", *num_params));
522901037d57Smrg    if ((xw = getXtermWidget(w)) != 0 &&
523001037d57Smrg	(*num_params > 1)) {
523101037d57Smrg	doSelectionFormat(xw, w, event, params, num_params, reallyInsertFormatted);
52320bd37d32Smrg    }
52330bd37d32Smrg}
52340bd37d32Smrg
52350bd37d32Smrgvoid
52360bd37d32SmrgHandleInsertSelectable(Widget w,
5237894e0ac8Smrg		       XEvent *event GCC_UNUSED,
5238e0a2b6dfSmrg		       String *params,	/* selections */
52390bd37d32Smrg		       Cardinal *num_params)
52400bd37d32Smrg{
52410bd37d32Smrg    XtermWidget xw;
52420bd37d32Smrg
52430bd37d32Smrg    if ((xw = getXtermWidget(w)) != 0) {
52440bd37d32Smrg	TRACE(("HandleInsertSelectable(%d)\n", *num_params));
52450bd37d32Smrg
52460bd37d32Smrg	if (*num_params == 2) {
52470bd37d32Smrg	    CELL start, finish;
52480bd37d32Smrg	    char *data;
52490bd37d32Smrg	    char *temp = x_strdup(params[0]);
52500bd37d32Smrg
52510bd37d32Smrg	    data = getDataFromScreen(xw, params[1], &start, &finish);
52520bd37d32Smrg	    if (data != 0) {
52532e4f8982Smrg		char *exps = expandFormat(xw, temp, data, &start, &finish);
52540bd37d32Smrg		if (exps != 0) {
52550bd37d32Smrg		    unparseputs(xw, exps);
525601037d57Smrg		    unparse_end(xw);
52570bd37d32Smrg		    free(exps);
52580bd37d32Smrg		}
52590bd37d32Smrg		free(data);
52600bd37d32Smrg	    }
52610bd37d32Smrg	    free(temp);
52620bd37d32Smrg	}
52630bd37d32Smrg    }
52640bd37d32Smrg}
52650bd37d32Smrg#endif /* OPT_SELECTION_OPS */
5266