button.c revision 01037d57
101037d57Smrg/* $XTermId: button.c,v 1.492 2015/04/10 10:16:19 tom Exp $ */
2d522f475Smrg
3d522f475Smrg/*
401037d57Smrg * Copyright 1999-2014,2015 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    int state;
566d522f475Smrg
567d522f475Smrg    ret = XQueryPointer(screen->display, VWindow(screen),
568d522f475Smrg			&root, &child, &rx, &ry, &x, &y, &mask);
569d522f475Smrg    if (ret) {
570d522f475Smrg	LocatorCoords(row, col, x, y, oor);
571d522f475Smrg    }
572d522f475Smrg    if (ret == False || oor) {
573d522f475Smrg	/* Locator is unavailable */
574d522f475Smrg
575d522f475Smrg	if (screen->loc_filter_top != LOC_FILTER_POS ||
576d522f475Smrg	    screen->loc_filter_left != LOC_FILTER_POS ||
577d522f475Smrg	    screen->loc_filter_bottom != LOC_FILTER_POS ||
578d522f475Smrg	    screen->loc_filter_right != LOC_FILTER_POS) {
579d522f475Smrg	    /*
580d522f475Smrg	     * If any explicit coordinates were received,
581d522f475Smrg	     * report immediately with no coordinates.
582d522f475Smrg	     */
583d522f475Smrg	    memset(&reply, 0, sizeof(reply));
584d522f475Smrg	    reply.a_type = ANSI_CSI;
585d522f475Smrg	    reply.a_nparam = 1;
586d522f475Smrg	    reply.a_param[0] = 0;	/* Event - 0 = locator unavailable */
587d522f475Smrg	    reply.a_inters = '&';
588d522f475Smrg	    reply.a_final = 'w';
589d522f475Smrg	    unparseseq(xw, &reply);
590d522f475Smrg
591d522f475Smrg	    if (screen->locator_reset) {
592d522f475Smrg		MotionOff(screen, xw);
593d522f475Smrg		screen->send_mouse_pos = MOUSE_OFF;
594d522f475Smrg	    }
595d522f475Smrg	} else {
596d522f475Smrg	    /*
597d522f475Smrg	     * No explicit coordinates were received, and the pointer is
598d522f475Smrg	     * unavailable.  Report when the pointer re-enters the window.
599d522f475Smrg	     */
600d522f475Smrg	    screen->loc_filter = True;
601d522f475Smrg	    MotionOn(screen, xw);
602d522f475Smrg	}
603d522f475Smrg	return;
604d522f475Smrg    }
605d522f475Smrg
606d522f475Smrg    /*
607d522f475Smrg     * Adjust rectangle coordinates:
608d522f475Smrg     *  1. Replace "LOC_FILTER_POS" with current coordinates
609d522f475Smrg     *  2. Limit coordinates to screen size
610d522f475Smrg     *  3. make sure top and left are less than bottom and right, resp.
611d522f475Smrg     */
612d522f475Smrg    if (screen->locator_pixels) {
613d522f475Smrg	rx = OriginX(screen) * 2 + Width(screen);
614d522f475Smrg	ry = screen->border * 2 + Height(screen);
615d522f475Smrg    } else {
616d522f475Smrg	rx = screen->max_col;
617d522f475Smrg	ry = screen->max_row;
618d522f475Smrg    }
619d522f475Smrg
620d522f475Smrg#define	Adjust( coord, def, max )				\
621d522f475Smrg	if( (coord) == LOC_FILTER_POS )	(coord) = (def);	\
622d522f475Smrg	else if ((coord) < 1)		(coord) = 1;		\
623d522f475Smrg	else if ((coord) > (max))	(coord) = (max)
624d522f475Smrg
625d522f475Smrg    Adjust(screen->loc_filter_top, row, ry);
626d522f475Smrg    Adjust(screen->loc_filter_left, col, rx);
627d522f475Smrg    Adjust(screen->loc_filter_bottom, row, ry);
628d522f475Smrg    Adjust(screen->loc_filter_right, col, rx);
629d522f475Smrg
630d522f475Smrg    if (screen->loc_filter_top > screen->loc_filter_bottom) {
631d522f475Smrg	ry = screen->loc_filter_top;
632d522f475Smrg	screen->loc_filter_top = screen->loc_filter_bottom;
633d522f475Smrg	screen->loc_filter_bottom = ry;
634d522f475Smrg    }
635d522f475Smrg
636d522f475Smrg    if (screen->loc_filter_left > screen->loc_filter_right) {
637d522f475Smrg	rx = screen->loc_filter_left;
638d522f475Smrg	screen->loc_filter_left = screen->loc_filter_right;
639d522f475Smrg	screen->loc_filter_right = rx;
640d522f475Smrg    }
641d522f475Smrg
642d522f475Smrg    if ((col < screen->loc_filter_left) ||
643d522f475Smrg	(col > screen->loc_filter_right) ||
644d522f475Smrg	(row < screen->loc_filter_top) ||
645d522f475Smrg	(row > screen->loc_filter_bottom)) {
646d522f475Smrg	/* Pointer is already outside the rectangle - report immediately */
647d522f475Smrg	ButtonState(state, mask);
648d522f475Smrg
649d522f475Smrg	memset(&reply, 0, sizeof(reply));
650d522f475Smrg	reply.a_type = ANSI_CSI;
651d522f475Smrg	reply.a_nparam = 4;
652d522f475Smrg	reply.a_param[0] = 10;	/* Event - 10 = locator outside filter */
6532eaa94a1Schristos	reply.a_param[1] = (ParmType) state;
6542eaa94a1Schristos	reply.a_param[2] = (ParmType) row;
6552eaa94a1Schristos	reply.a_param[3] = (ParmType) col;
656d522f475Smrg	reply.a_inters = '&';
657d522f475Smrg	reply.a_final = 'w';
658d522f475Smrg	unparseseq(xw, &reply);
659d522f475Smrg
660d522f475Smrg	if (screen->locator_reset) {
661d522f475Smrg	    MotionOff(screen, xw);
662d522f475Smrg	    screen->send_mouse_pos = MOUSE_OFF;
663d522f475Smrg	}
664d522f475Smrg	return;
665d522f475Smrg    }
666d522f475Smrg
667d522f475Smrg    /*
668d522f475Smrg     * Rectangle is set up.  Allow pointer tracking
669d522f475Smrg     * to detect if the mouse leaves the rectangle.
670d522f475Smrg     */
671d522f475Smrg    screen->loc_filter = True;
672d522f475Smrg    MotionOn(screen, xw);
673d522f475Smrg}
674d522f475Smrg
675d522f475Smrgstatic void
676894e0ac8SmrgCheckLocatorPosition(XtermWidget xw, XButtonEvent *event)
677d522f475Smrg{
678d522f475Smrg    ANSI reply;
679956cc18dSsnj    TScreen *screen = TScreenOf(xw);
680d522f475Smrg    int row, col;
681d522f475Smrg    Bool oor;
682d522f475Smrg    int state;
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 {
706492d43a5Smrg	    ButtonState(state, event->state);
707d522f475Smrg
708d522f475Smrg	    reply.a_nparam = 4;
709d522f475Smrg	    reply.a_param[0] = 10;	/* Event - 10 = locator outside filter */
7102eaa94a1Schristos	    reply.a_param[1] = (ParmType) state;
7112eaa94a1Schristos	    reply.a_param[2] = (ParmType) row;
7122eaa94a1Schristos	    reply.a_param[3] = (ParmType) col;
713d522f475Smrg	}
714d522f475Smrg
715d522f475Smrg	reply.a_inters = '&';
716d522f475Smrg	reply.a_final = 'w';
717d522f475Smrg	unparseseq(xw, &reply);
718d522f475Smrg
719d522f475Smrg	if (screen->locator_reset) {
720d522f475Smrg	    MotionOff(screen, xw);
721d522f475Smrg	    screen->send_mouse_pos = MOUSE_OFF;
722d522f475Smrg	}
723d522f475Smrg    }
724d522f475Smrg}
725d522f475Smrg#endif /* OPT_DEC_LOCATOR */
726d522f475Smrg
727d522f475Smrg#if OPT_READLINE
728d522f475Smrgstatic int
729894e0ac8SmrgisClick1_clean(TScreen *screen, XButtonEvent *event)
730d522f475Smrg{
731d522f475Smrg    int delta;
732d522f475Smrg
733492d43a5Smrg    if (!IsBtnEvent(event)
734d522f475Smrg    /* Disable on Shift-Click-1, including the application-mouse modes */
735492d43a5Smrg	|| (BtnModifiers(event) & ShiftMask)
736d522f475Smrg	|| (screen->send_mouse_pos != MOUSE_OFF)	/* Kinda duplicate... */
737d522f475Smrg	||ExtendingSelection)	/* Was moved */
738d522f475Smrg	return 0;
739d522f475Smrg
740d522f475Smrg    if (event->type != ButtonRelease)
741d522f475Smrg	return 0;
742d522f475Smrg
743d522f475Smrg    if (lastButtonDownTime == (Time) 0) {
744d522f475Smrg	/* first time or once in a blue moon */
745d522f475Smrg	delta = screen->multiClickTime + 1;
746492d43a5Smrg    } else if (event->time > lastButtonDownTime) {
747d522f475Smrg	/* most of the time */
748492d43a5Smrg	delta = (int) (event->time - lastButtonDownTime);
749d522f475Smrg    } else {
750d522f475Smrg	/* time has rolled over since lastButtonUpTime */
751492d43a5Smrg	delta = (int) ((((Time) ~ 0) - lastButtonDownTime) + event->time);
752d522f475Smrg    }
753d522f475Smrg
754d522f475Smrg    return delta <= screen->multiClickTime;
755d522f475Smrg}
756d522f475Smrg
757d522f475Smrgstatic int
758894e0ac8SmrgisDoubleClick3(TScreen *screen, XButtonEvent *event)
759d522f475Smrg{
760d522f475Smrg    int delta;
761d522f475Smrg
762d522f475Smrg    if (event->type != ButtonRelease
763492d43a5Smrg	|| (BtnModifiers(event) & ShiftMask)
764492d43a5Smrg	|| event->button != Button3) {
765d522f475Smrg	lastButton3UpTime = 0;	/* Disable the cached info */
766d522f475Smrg	return 0;
767d522f475Smrg    }
768d522f475Smrg    /* Process Btn3Release. */
769d522f475Smrg    if (lastButton3DoubleDownTime == (Time) 0) {
770d522f475Smrg	/* No previous click or once in a blue moon */
771d522f475Smrg	delta = screen->multiClickTime + 1;
772492d43a5Smrg    } else if (event->time > lastButton3DoubleDownTime) {
773d522f475Smrg	/* most of the time */
774492d43a5Smrg	delta = (int) (event->time - lastButton3DoubleDownTime);
775d522f475Smrg    } else {
776d522f475Smrg	/* time has rolled over since lastButton3DoubleDownTime */
777492d43a5Smrg	delta = (int) ((((Time) ~ 0) - lastButton3DoubleDownTime) + event->time);
778d522f475Smrg    }
779d522f475Smrg    if (delta <= screen->multiClickTime) {
780d522f475Smrg	/* Double click */
781d522f475Smrg	CELL cell;
782d522f475Smrg
783d522f475Smrg	/* Cannot check ExtendingSelection, since mouse-3 always sets it */
784492d43a5Smrg	PointToCELL(screen, event->y, event->x, &cell);
785d522f475Smrg	if (isSameCELL(&cell, &lastButton3)) {
786d522f475Smrg	    lastButton3DoubleDownTime = 0;	/* Disable the third click */
787d522f475Smrg	    return 1;
788d522f475Smrg	}
789d522f475Smrg    }
790d522f475Smrg    /* Not a double click, memorize for future check. */
791492d43a5Smrg    lastButton3UpTime = event->time;
792492d43a5Smrg    PointToCELL(screen, event->y, event->x, &lastButton3);
793d522f475Smrg    return 0;
794d522f475Smrg}
795d522f475Smrg
796d522f475Smrgstatic int
797894e0ac8SmrgCheckSecondPress3(TScreen *screen, XEvent *event)
798d522f475Smrg{
799d522f475Smrg    int delta;
800d522f475Smrg
801d522f475Smrg    if (event->type != ButtonPress
802492d43a5Smrg	|| (KeyModifiers(event) & ShiftMask)
803d522f475Smrg	|| event->xbutton.button != Button3) {
804d522f475Smrg	lastButton3DoubleDownTime = 0;	/* Disable the cached info */
805d522f475Smrg	return 0;
806d522f475Smrg    }
807d522f475Smrg    /* Process Btn3Press. */
808d522f475Smrg    if (lastButton3UpTime == (Time) 0) {
809d522f475Smrg	/* No previous click or once in a blue moon */
810d522f475Smrg	delta = screen->multiClickTime + 1;
811d522f475Smrg    } else if (event->xbutton.time > lastButton3UpTime) {
812d522f475Smrg	/* most of the time */
8132eaa94a1Schristos	delta = (int) (event->xbutton.time - lastButton3UpTime);
814d522f475Smrg    } else {
815d522f475Smrg	/* time has rolled over since lastButton3UpTime */
8162eaa94a1Schristos	delta = (int) ((((Time) ~ 0) - lastButton3UpTime) + event->xbutton.time);
817d522f475Smrg    }
818d522f475Smrg    if (delta <= screen->multiClickTime) {
819d522f475Smrg	CELL cell;
820d522f475Smrg
821d522f475Smrg	PointToCELL(screen, event->xbutton.y, event->xbutton.x, &cell);
822d522f475Smrg	if (isSameCELL(&cell, &lastButton3)) {
823d522f475Smrg	    /* A candidate for a double-click */
824d522f475Smrg	    lastButton3DoubleDownTime = event->xbutton.time;
825d522f475Smrg	    PointToCELL(screen, event->xbutton.y, event->xbutton.x, &lastButton3);
826d522f475Smrg	    return 1;
827d522f475Smrg	}
828d522f475Smrg	lastButton3UpTime = 0;	/* Disable the info about the previous click */
829d522f475Smrg    }
830d522f475Smrg    /* Either too long, or moved, disable. */
831d522f475Smrg    lastButton3DoubleDownTime = 0;
832d522f475Smrg    return 0;
833d522f475Smrg}
834d522f475Smrg
835d522f475Smrgstatic int
836e0a2b6dfSmrgrowOnCurrentLine(TScreen *screen,
837d522f475Smrg		 int line,
838d522f475Smrg		 int *deltap)	/* must be XButtonEvent */
839d522f475Smrg{
840956cc18dSsnj    int result = 1;
841d522f475Smrg    int l1, l2;
842d522f475Smrg
843d522f475Smrg    *deltap = 0;
844956cc18dSsnj    if (line != screen->cur_row) {
845956cc18dSsnj	if (line < screen->cur_row)
846956cc18dSsnj	    l1 = line, l2 = screen->cur_row;
847956cc18dSsnj	else
848956cc18dSsnj	    l2 = line, l1 = screen->cur_row;
849956cc18dSsnj	l1--;
850956cc18dSsnj	while (++l1 < l2) {
851956cc18dSsnj	    LineData *ld = GET_LINEDATA(screen, l1);
852956cc18dSsnj	    if (!LineTstWrapped(ld)) {
853956cc18dSsnj		result = 0;
854956cc18dSsnj		break;
855956cc18dSsnj	    }
856956cc18dSsnj	}
857956cc18dSsnj	if (result) {
858956cc18dSsnj	    /* Everything is on one "wrapped line" now */
859956cc18dSsnj	    *deltap = line - screen->cur_row;
860956cc18dSsnj	}
861956cc18dSsnj    }
862956cc18dSsnj    return result;
863d522f475Smrg}
864d522f475Smrg
865d522f475Smrgstatic int
866894e0ac8SmrgeventRow(TScreen *screen, XEvent *event)	/* must be XButtonEvent */
867d522f475Smrg{
868d522f475Smrg    return (event->xbutton.y - screen->border) / FontHeight(screen);
869d522f475Smrg}
870d522f475Smrg
871d522f475Smrgstatic int
872894e0ac8SmrgeventColBetween(TScreen *screen, XEvent *event)		/* must be XButtonEvent */
873d522f475Smrg{
874d522f475Smrg    /* Correct by half a width - we are acting on a boundary, not on a cell. */
875d522f475Smrg    return ((event->xbutton.x - OriginX(screen) + (FontWidth(screen) - 1) / 2)
876d522f475Smrg	    / FontWidth(screen));
877d522f475Smrg}
878d522f475Smrg
879d522f475Smrgstatic int
880e0a2b6dfSmrgReadLineMovePoint(TScreen *screen, int col, int ldelta)
881d522f475Smrg{
882d522f475Smrg    Char line[6];
883d522f475Smrg    unsigned count = 0;
884d522f475Smrg
885d522f475Smrg    col += ldelta * MaxCols(screen) - screen->cur_col;
886d522f475Smrg    if (col == 0)
887d522f475Smrg	return 0;
888d522f475Smrg    if (screen->control_eight_bits) {
889d522f475Smrg	line[count++] = ANSI_CSI;
890d522f475Smrg    } else {
891d522f475Smrg	line[count++] = ANSI_ESC;
892d522f475Smrg	line[count++] = '[';	/* XXX maybe sometimes O is better? */
893d522f475Smrg    }
89420d2c4d2Smrg    line[count] = CharOf(col > 0 ? 'C' : 'D');
895d522f475Smrg    if (col < 0)
896d522f475Smrg	col = -col;
897d522f475Smrg    while (col--)
898d522f475Smrg	v_write(screen->respond, line, 3);
899d522f475Smrg    return 1;
900d522f475Smrg}
901d522f475Smrg
902d522f475Smrgstatic int
903e0a2b6dfSmrgReadLineDelete(TScreen *screen, CELL *cell1, CELL *cell2)
904d522f475Smrg{
905d522f475Smrg    int del;
906d522f475Smrg
907d522f475Smrg    del = (cell2->col - cell1->col) + ((cell2->row - cell1->row) * MaxCols(screen));
908d522f475Smrg    if (del <= 0)		/* Just in case... */
909d522f475Smrg	return 0;
910d522f475Smrg    while (del--)
911492d43a5Smrg	v_write(screen->respond, (const Char *) "\177", 1);
912d522f475Smrg    return 1;
913d522f475Smrg}
914492d43a5Smrg
915492d43a5Smrgstatic void
916894e0ac8SmrgreadlineExtend(TScreen *screen, XEvent *event)
917492d43a5Smrg{
918492d43a5Smrg    int ldelta1, ldelta2;
919492d43a5Smrg
920492d43a5Smrg    if (IsBtnEvent(event)) {
921492d43a5Smrg	XButtonEvent *my_event = (XButtonEvent *) event;
922492d43a5Smrg	if (isClick1_clean(screen, my_event)
923492d43a5Smrg	    && SCREEN_FLAG(screen, click1_moves)
924492d43a5Smrg	    && rowOnCurrentLine(screen, eventRow(screen, event), &ldelta1)) {
925492d43a5Smrg	    ReadLineMovePoint(screen, eventColBetween(screen, event), ldelta1);
926492d43a5Smrg	}
927492d43a5Smrg	if (isDoubleClick3(screen, my_event)
928492d43a5Smrg	    && SCREEN_FLAG(screen, dclick3_deletes)
929492d43a5Smrg	    && rowOnCurrentLine(screen, screen->startSel.row, &ldelta1)
930492d43a5Smrg	    && rowOnCurrentLine(screen, screen->endSel.row, &ldelta2)) {
931492d43a5Smrg	    ReadLineMovePoint(screen, screen->endSel.col, ldelta2);
932492d43a5Smrg	    ReadLineDelete(screen, &screen->startSel, &(screen->endSel));
933492d43a5Smrg	}
934492d43a5Smrg    }
935492d43a5Smrg}
936492d43a5Smrg
937d522f475Smrg#endif /* OPT_READLINE */
938d522f475Smrg
939d522f475Smrg/* ^XM-G<line+' '><col+' '> */
940d522f475Smrgvoid
941d522f475SmrgDiredButton(Widget w,
942894e0ac8Smrg	    XEvent *event,	/* must be XButtonEvent */
943e0a2b6dfSmrg	    String *params GCC_UNUSED,	/* selections */
944d522f475Smrg	    Cardinal *num_params GCC_UNUSED)
945d522f475Smrg{
946956cc18dSsnj    XtermWidget xw;
947956cc18dSsnj
948956cc18dSsnj    if ((xw = getXtermWidget(w)) != 0) {
949956cc18dSsnj	TScreen *screen = TScreenOf(xw);
950d522f475Smrg	Char Line[6];
951d522f475Smrg	unsigned line, col;
952d522f475Smrg
953492d43a5Smrg	if (IsBtnEvent(event)
9542eaa94a1Schristos	    && (event->xbutton.y >= screen->border)
9552eaa94a1Schristos	    && (event->xbutton.x >= OriginX(screen))) {
95620d2c4d2Smrg	    line = (unsigned) ((event->xbutton.y - screen->border)
95720d2c4d2Smrg			       / FontHeight(screen));
95820d2c4d2Smrg	    col = (unsigned) ((event->xbutton.x - OriginX(screen))
95920d2c4d2Smrg			      / FontWidth(screen));
960d522f475Smrg	    Line[0] = CONTROL('X');
961d522f475Smrg	    Line[1] = ANSI_ESC;
962d522f475Smrg	    Line[2] = 'G';
9632eaa94a1Schristos	    Line[3] = CharOf(' ' + col);
9642eaa94a1Schristos	    Line[4] = CharOf(' ' + line);
965d522f475Smrg	    v_write(screen->respond, Line, 5);
966d522f475Smrg	}
967d522f475Smrg    }
968d522f475Smrg}
969d522f475Smrg
970d522f475Smrg#if OPT_READLINE
971d522f475Smrgvoid
972d522f475SmrgReadLineButton(Widget w,
973894e0ac8Smrg	       XEvent *event,	/* must be XButtonEvent */
974e0a2b6dfSmrg	       String *params GCC_UNUSED,	/* selections */
975d522f475Smrg	       Cardinal *num_params GCC_UNUSED)
976d522f475Smrg{
977956cc18dSsnj    XtermWidget xw;
978956cc18dSsnj
979956cc18dSsnj    if ((xw = getXtermWidget(w)) != 0) {
980956cc18dSsnj	TScreen *screen = TScreenOf(xw);
981d522f475Smrg	Char Line[6];
982d522f475Smrg	int line, col, ldelta = 0;
983d522f475Smrg
984492d43a5Smrg	if (!IsBtnEvent(event)
985d522f475Smrg	    || (screen->send_mouse_pos != MOUSE_OFF) || ExtendingSelection)
986d522f475Smrg	    goto finish;
987d522f475Smrg	if (event->type == ButtonRelease) {
988d522f475Smrg	    int delta;
989d522f475Smrg
990d522f475Smrg	    if (lastButtonDownTime == (Time) 0) {
991d522f475Smrg		/* first time and once in a blue moon */
992d522f475Smrg		delta = screen->multiClickTime + 1;
993d522f475Smrg	    } else if (event->xbutton.time > lastButtonDownTime) {
994d522f475Smrg		/* most of the time */
9952eaa94a1Schristos		delta = (int) (event->xbutton.time - lastButtonDownTime);
996d522f475Smrg	    } else {
997d522f475Smrg		/* time has rolled over since lastButtonUpTime */
9982eaa94a1Schristos		delta = (int) ((((Time) ~ 0) - lastButtonDownTime) + event->xbutton.time);
999d522f475Smrg	    }
1000d522f475Smrg	    if (delta > screen->multiClickTime)
1001d522f475Smrg		goto finish;	/* All this work for this... */
1002d522f475Smrg	}
1003d522f475Smrg	line = (event->xbutton.y - screen->border) / FontHeight(screen);
1004956cc18dSsnj	if (!rowOnCurrentLine(screen, line, &ldelta))
1005956cc18dSsnj	    goto finish;
1006d522f475Smrg	/* Correct by half a width - we are acting on a boundary, not on a cell. */
1007d522f475Smrg	col = (event->xbutton.x - OriginX(screen) + (FontWidth(screen) - 1)
1008d522f475Smrg	       / 2)
1009d522f475Smrg	    / FontWidth(screen) - screen->cur_col + ldelta * MaxCols(screen);
1010d522f475Smrg	if (col == 0)
1011d522f475Smrg	    goto finish;
1012d522f475Smrg	Line[0] = ANSI_ESC;
1013d522f475Smrg	/* XXX: sometimes it is better to send '['? */
1014d522f475Smrg	Line[1] = 'O';
10152eaa94a1Schristos	Line[2] = CharOf(col > 0 ? 'C' : 'D');
1016d522f475Smrg	if (col < 0)
1017d522f475Smrg	    col = -col;
1018d522f475Smrg	while (col--)
1019d522f475Smrg	    v_write(screen->respond, Line, 3);
1020d522f475Smrg      finish:
1021d522f475Smrg	if (event->type == ButtonRelease)
1022d522f475Smrg	    do_select_end(xw, event, params, num_params, False);
1023d522f475Smrg    }
1024d522f475Smrg}
1025d522f475Smrg#endif /* OPT_READLINE */
1026d522f475Smrg
1027d522f475Smrg/* repeats <ESC>n or <ESC>p */
1028d522f475Smrgvoid
1029d522f475SmrgViButton(Widget w,
1030894e0ac8Smrg	 XEvent *event,		/* must be XButtonEvent */
1031e0a2b6dfSmrg	 String *params GCC_UNUSED,	/* selections */
1032d522f475Smrg	 Cardinal *num_params GCC_UNUSED)
1033d522f475Smrg{
1034956cc18dSsnj    XtermWidget xw;
1035956cc18dSsnj
1036956cc18dSsnj    if ((xw = getXtermWidget(w)) != 0) {
1037956cc18dSsnj	TScreen *screen = TScreenOf(xw);
1038d522f475Smrg	int pty = screen->respond;
1039d522f475Smrg	Char Line[6];
1040d522f475Smrg	int line;
1041d522f475Smrg
1042492d43a5Smrg	if (IsBtnEvent(event)) {
1043d522f475Smrg
1044d522f475Smrg	    line = screen->cur_row -
1045d522f475Smrg		((event->xbutton.y - screen->border) / FontHeight(screen));
1046d522f475Smrg	    if (line != 0) {
1047d522f475Smrg		Line[0] = ANSI_ESC;	/* force an exit from insert-mode */
1048d522f475Smrg		v_write(pty, Line, 1);
1049d522f475Smrg
1050d522f475Smrg		if (line < 0) {
1051d522f475Smrg		    line = -line;
1052d522f475Smrg		    Line[0] = CONTROL('n');
1053d522f475Smrg		} else {
1054d522f475Smrg		    Line[0] = CONTROL('p');
1055d522f475Smrg		}
1056d522f475Smrg		while (--line >= 0)
1057d522f475Smrg		    v_write(pty, Line, 1);
1058d522f475Smrg	    }
1059d522f475Smrg	}
1060d522f475Smrg    }
1061d522f475Smrg}
1062d522f475Smrg
1063d522f475Smrg/*
1064d522f475Smrg * This function handles button-motion events
1065d522f475Smrg */
1066d522f475Smrg/*ARGSUSED*/
1067d522f475Smrgvoid
1068d522f475SmrgHandleSelectExtend(Widget w,
1069894e0ac8Smrg		   XEvent *event,	/* must be XMotionEvent */
1070e0a2b6dfSmrg		   String *params GCC_UNUSED,
1071d522f475Smrg		   Cardinal *num_params GCC_UNUSED)
1072d522f475Smrg{
1073956cc18dSsnj    XtermWidget xw;
1074956cc18dSsnj
1075956cc18dSsnj    if ((xw = getXtermWidget(w)) != 0) {
1076956cc18dSsnj	TScreen *screen = TScreenOf(xw);
1077d522f475Smrg	CELL cell;
1078d522f475Smrg
1079e0a2b6dfSmrg	TRACE(("HandleSelectExtend @%ld\n", event->xmotion.time));
10800bd37d32Smrg
1081d522f475Smrg	screen->selection_time = event->xmotion.time;
1082d522f475Smrg	switch (screen->eventMode) {
1083d522f475Smrg	    /* If not in one of the DEC mouse-reporting modes */
1084d522f475Smrg	case LEFTEXTENSION:
1085d522f475Smrg	case RIGHTEXTENSION:
1086d522f475Smrg	    PointToCELL(screen, event->xmotion.y, event->xmotion.x, &cell);
1087d522f475Smrg	    ExtendExtend(xw, &cell);
1088d522f475Smrg	    break;
1089d522f475Smrg
1090d522f475Smrg	    /* If in motion reporting mode, send mouse position to
1091d522f475Smrg	       character process as a key sequence \E[M... */
1092d522f475Smrg	case NORMAL:
1093d522f475Smrg	    /* will get here if send_mouse_pos != MOUSE_OFF */
1094d522f475Smrg	    if (screen->send_mouse_pos == BTN_EVENT_MOUSE
1095d522f475Smrg		|| screen->send_mouse_pos == ANY_EVENT_MOUSE) {
1096d522f475Smrg		(void) SendMousePosition(xw, event);
1097d522f475Smrg	    }
1098d522f475Smrg	    break;
1099d522f475Smrg	}
1100d522f475Smrg    }
1101d522f475Smrg}
1102d522f475Smrg
1103d522f475Smrgvoid
1104d522f475SmrgHandleKeyboardSelectExtend(Widget w,
1105894e0ac8Smrg			   XEvent *event GCC_UNUSED,	/* must be XButtonEvent */
1106e0a2b6dfSmrg			   String *params GCC_UNUSED,
1107d522f475Smrg			   Cardinal *num_params GCC_UNUSED)
1108d522f475Smrg{
1109956cc18dSsnj    XtermWidget xw;
1110956cc18dSsnj
1111956cc18dSsnj    if ((xw = getXtermWidget(w)) != 0) {
1112956cc18dSsnj	TScreen *screen = TScreenOf(xw);
11130bd37d32Smrg
11140bd37d32Smrg	TRACE(("HandleKeyboardSelectExtend\n"));
1115d522f475Smrg	ExtendExtend(xw, &screen->cursorp);
1116d522f475Smrg    }
1117d522f475Smrg}
1118d522f475Smrg
1119d522f475Smrgstatic void
1120d522f475Smrgdo_select_end(XtermWidget xw,
1121894e0ac8Smrg	      XEvent *event,	/* must be XButtonEvent */
1122e0a2b6dfSmrg	      String *params,	/* selections */
1123d522f475Smrg	      Cardinal *num_params,
1124d522f475Smrg	      Bool use_cursor_loc)
1125d522f475Smrg{
1126956cc18dSsnj    TScreen *screen = TScreenOf(xw);
1127d522f475Smrg
1128d522f475Smrg    screen->selection_time = event->xbutton.time;
1129e0a2b6dfSmrg    TRACE(("do_select_end @%ld\n", screen->selection_time));
1130d522f475Smrg    switch (screen->eventMode) {
1131d522f475Smrg    case NORMAL:
1132d522f475Smrg	(void) SendMousePosition(xw, event);
1133d522f475Smrg	break;
1134d522f475Smrg    case LEFTEXTENSION:
1135d522f475Smrg    case RIGHTEXTENSION:
1136d522f475Smrg	EndExtend(xw, event, params, *num_params, use_cursor_loc);
1137d522f475Smrg#if OPT_READLINE
1138492d43a5Smrg	readlineExtend(screen, event);
1139d522f475Smrg#endif /* OPT_READLINE */
1140d522f475Smrg	break;
1141d522f475Smrg    }
1142d522f475Smrg}
1143d522f475Smrg
1144d522f475Smrgvoid
1145d522f475SmrgHandleSelectEnd(Widget w,
1146894e0ac8Smrg		XEvent *event,	/* must be XButtonEvent */
1147e0a2b6dfSmrg		String *params,	/* selections */
1148d522f475Smrg		Cardinal *num_params)
1149d522f475Smrg{
1150956cc18dSsnj    XtermWidget xw;
1151956cc18dSsnj
1152956cc18dSsnj    if ((xw = getXtermWidget(w)) != 0) {
11530bd37d32Smrg	TRACE(("HandleSelectEnd\n"));
1154956cc18dSsnj	do_select_end(xw, event, params, num_params, False);
1155956cc18dSsnj    }
1156d522f475Smrg}
1157d522f475Smrg
1158d522f475Smrgvoid
1159d522f475SmrgHandleKeyboardSelectEnd(Widget w,
1160894e0ac8Smrg			XEvent *event,	/* must be XButtonEvent */
1161e0a2b6dfSmrg			String *params,		/* selections */
1162d522f475Smrg			Cardinal *num_params)
1163d522f475Smrg{
1164956cc18dSsnj    XtermWidget xw;
1165956cc18dSsnj
1166956cc18dSsnj    if ((xw = getXtermWidget(w)) != 0) {
11670bd37d32Smrg	TRACE(("HandleKeyboardSelectEnd\n"));
1168956cc18dSsnj	do_select_end(xw, event, params, num_params, True);
1169956cc18dSsnj    }
1170d522f475Smrg}
1171d522f475Smrg
1172492d43a5Smrg/*
11736879286fSmrg * Copy the selection data to the given target(s).
1174492d43a5Smrg */
1175492d43a5Smrgvoid
11766879286fSmrgHandleCopySelection(Widget w,
1177894e0ac8Smrg		    XEvent *event,
1178e0a2b6dfSmrg		    String *params,	/* list of targets */
11796879286fSmrg		    Cardinal *num_params)
1180492d43a5Smrg{
1181492d43a5Smrg    XtermWidget xw;
1182492d43a5Smrg
1183492d43a5Smrg    if ((xw = getXtermWidget(w)) != 0) {
11840bd37d32Smrg	TRACE(("HandleCopySelection\n"));
11856879286fSmrg	SelectSet(xw, event, params, *num_params);
1186492d43a5Smrg    }
1187492d43a5Smrg}
1188492d43a5Smrg
1189d522f475Smrgstruct _SelectionList {
1190d522f475Smrg    String *params;
1191d522f475Smrg    Cardinal count;
1192d522f475Smrg    Atom *targets;
1193d522f475Smrg    Time time;
1194d522f475Smrg};
1195d522f475Smrg
1196d522f475Smrgstatic unsigned
1197d522f475SmrgDECtoASCII(unsigned ch)
1198d522f475Smrg{
1199d522f475Smrg    if (xtermIsDecGraphic(ch)) {
12002eaa94a1Schristos	ch = CharOf("###########+++++##-##++++|######"[ch]);
12012eaa94a1Schristos	/*           01234567890123456789012345678901 */
1202d522f475Smrg    }
1203d522f475Smrg    return ch;
1204d522f475Smrg}
120520d2c4d2Smrg
120620d2c4d2Smrg#if OPT_WIDE_CHARS
120720d2c4d2Smrgstatic Cardinal
1208e0a2b6dfSmrgaddXtermChar(Char **buffer, Cardinal *used, Cardinal offset, unsigned value)
120920d2c4d2Smrg{
121020d2c4d2Smrg    if (offset + 1 >= *used) {
121120d2c4d2Smrg	*used = 1 + (2 * (offset + 1));
121220d2c4d2Smrg	allocXtermChars(buffer, *used);
121320d2c4d2Smrg    }
121420d2c4d2Smrg    (*buffer)[offset++] = (Char) value;
121520d2c4d2Smrg    return offset;
121620d2c4d2Smrg}
121720d2c4d2Smrg#define AddChar(buffer, used, offset, value) \
121820d2c4d2Smrg	offset = addXtermChar(buffer, used, offset, (unsigned) value)
121920d2c4d2Smrg
1220d522f475Smrg/*
1221d522f475Smrg * Convert a UTF-8 string to Latin-1, replacing non Latin-1 characters by `#',
1222d522f475Smrg * or ASCII/Latin-1 equivalents for special cases.
1223d522f475Smrg */
1224d522f475Smrgstatic Char *
1225e0a2b6dfSmrgUTF8toLatin1(TScreen *screen, Char *s, unsigned long len, unsigned long *result)
1226d522f475Smrg{
1227d522f475Smrg    static Char *buffer;
1228956cc18dSsnj    static Cardinal used;
1229d522f475Smrg
123020d2c4d2Smrg    Cardinal offset = 0;
1231d522f475Smrg
1232492d43a5Smrg    const Char *p;
1233d522f475Smrg
123420d2c4d2Smrg    if (len != 0) {
1235d522f475Smrg	PtyData data;
1236d522f475Smrg
1237d522f475Smrg	fakePtyData(&data, s, s + len);
1238894e0ac8Smrg	while (decodeUtf8(screen, &data)) {
1239956cc18dSsnj	    Bool fails = False;
1240956cc18dSsnj	    Bool extra = False;
1241d522f475Smrg	    IChar value = skipPtyData(&data);
1242d522f475Smrg	    if (value == UCS_REPL) {
1243956cc18dSsnj		fails = True;
1244d522f475Smrg	    } else if (value < 256) {
124520d2c4d2Smrg		AddChar(&buffer, &used, offset, CharOf(value));
1246d522f475Smrg	    } else {
1247d522f475Smrg		unsigned eqv = ucs2dec(value);
1248d522f475Smrg		if (xtermIsDecGraphic(eqv)) {
124920d2c4d2Smrg		    AddChar(&buffer, &used, offset, DECtoASCII(eqv));
1250d522f475Smrg		} else {
1251d522f475Smrg		    eqv = AsciiEquivs(value);
1252956cc18dSsnj		    if (eqv == value) {
1253956cc18dSsnj			fails = True;
1254956cc18dSsnj		    } else {
125520d2c4d2Smrg			AddChar(&buffer, &used, offset, eqv);
1256956cc18dSsnj		    }
1257956cc18dSsnj		    if (isWide((wchar_t) value))
1258956cc18dSsnj			extra = True;
1259956cc18dSsnj		}
1260956cc18dSsnj	    }
1261956cc18dSsnj
1262956cc18dSsnj	    /*
1263956cc18dSsnj	     * If we're not able to plug in a single-byte result, insert the
1264956cc18dSsnj	     * defaultString (which normally is a single "#", but could be
1265956cc18dSsnj	     * whatever the user wants).
1266956cc18dSsnj	     */
1267956cc18dSsnj	    if (fails) {
1268492d43a5Smrg		for (p = (const Char *) screen->default_string; *p != '\0'; ++p) {
126920d2c4d2Smrg		    AddChar(&buffer, &used, offset, *p);
1270d522f475Smrg		}
1271d522f475Smrg	    }
1272956cc18dSsnj	    if (extra)
127320d2c4d2Smrg		AddChar(&buffer, &used, offset, ' ');
1274d522f475Smrg	}
127520d2c4d2Smrg	AddChar(&buffer, &used, offset, '\0');
127620d2c4d2Smrg	*result = (unsigned long) (offset - 1);
1277d522f475Smrg    } else {
1278d522f475Smrg	*result = 0;
1279d522f475Smrg    }
1280d522f475Smrg    return buffer;
1281d522f475Smrg}
128220d2c4d2Smrg
128320d2c4d2Smrgint
128420d2c4d2SmrgxtermUtf8ToTextList(XtermWidget xw,
128520d2c4d2Smrg		    XTextProperty * text_prop,
128620d2c4d2Smrg		    char ***text_list,
128720d2c4d2Smrg		    int *text_list_count)
128820d2c4d2Smrg{
128920d2c4d2Smrg    TScreen *screen = TScreenOf(xw);
129020d2c4d2Smrg    Display *dpy = screen->display;
129120d2c4d2Smrg    int rc = -1;
129220d2c4d2Smrg
129320d2c4d2Smrg    if (text_prop->format == 8
129420d2c4d2Smrg	&& (rc = Xutf8TextPropertyToTextList(dpy, text_prop,
129520d2c4d2Smrg					     text_list,
129620d2c4d2Smrg					     text_list_count)) >= 0) {
129720d2c4d2Smrg	if (*text_list != NULL && *text_list_count != 0) {
129820d2c4d2Smrg	    int i;
129920d2c4d2Smrg	    Char *data;
130020d2c4d2Smrg	    char **new_text_list, *tmp;
130120d2c4d2Smrg	    unsigned long size, new_size;
130220d2c4d2Smrg
130320d2c4d2Smrg	    TRACE(("xtermUtf8ToTextList size %d\n", *text_list_count));
130420d2c4d2Smrg
130520d2c4d2Smrg	    /*
130620d2c4d2Smrg	     * XLib StringList actually uses only two pointers, one for the
130720d2c4d2Smrg	     * list itself, and one for the data.  Pointer to the data is the
130820d2c4d2Smrg	     * first element of the list, the rest (if any) list elements point
130920d2c4d2Smrg	     * to the same memory block as the first element
131020d2c4d2Smrg	     */
131120d2c4d2Smrg	    new_size = 0;
131220d2c4d2Smrg	    for (i = 0; i < *text_list_count; ++i) {
131320d2c4d2Smrg		data = (Char *) (*text_list)[i];
131420d2c4d2Smrg		size = strlen((*text_list)[i]) + 1;
131520d2c4d2Smrg		(void) UTF8toLatin1(screen, data, size, &size);
131620d2c4d2Smrg		new_size += size + 1;
131720d2c4d2Smrg	    }
1318a1f3da82Smrg	    new_text_list = TypeXtMallocN(char *, *text_list_count);
131920d2c4d2Smrg	    new_text_list[0] = tmp = XtMalloc((Cardinal) new_size);
132020d2c4d2Smrg	    for (i = 0; i < (*text_list_count); ++i) {
132120d2c4d2Smrg		data = (Char *) (*text_list)[i];
132220d2c4d2Smrg		size = strlen((*text_list)[i]) + 1;
13230bd37d32Smrg		if ((data = UTF8toLatin1(screen, data, size, &size)) != 0) {
13240bd37d32Smrg		    memcpy(tmp, data, size + 1);
13250bd37d32Smrg		    new_text_list[i] = tmp;
13260bd37d32Smrg		    tmp += size + 1;
13270bd37d32Smrg		}
132820d2c4d2Smrg	    }
132920d2c4d2Smrg	    XFreeStringList((*text_list));
133020d2c4d2Smrg	    *text_list = new_text_list;
133120d2c4d2Smrg	} else {
133220d2c4d2Smrg	    rc = -1;
133320d2c4d2Smrg	}
133420d2c4d2Smrg    }
133520d2c4d2Smrg    return rc;
133620d2c4d2Smrg}
1337d522f475Smrg#endif /* OPT_WIDE_CHARS */
1338d522f475Smrg
1339956cc18dSsnjstatic char *
1340956cc18dSsnjparseItem(char *value, char *nextc)
1341d522f475Smrg{
1342956cc18dSsnj    char *nextp = value;
1343956cc18dSsnj    while (*nextp != '\0' && *nextp != ',') {
1344956cc18dSsnj	*nextp = x_toupper(*nextp);
1345956cc18dSsnj	++nextp;
1346956cc18dSsnj    }
1347956cc18dSsnj    *nextc = *nextp;
1348956cc18dSsnj    *nextp = '\0';
1349d522f475Smrg
1350956cc18dSsnj    return nextp;
1351956cc18dSsnj}
1352d522f475Smrg
1353956cc18dSsnj/*
1354956cc18dSsnj * All of the wanted strings are unique in the first character, so we can
1355956cc18dSsnj * use simple abbreviations.
1356956cc18dSsnj */
1357956cc18dSsnjstatic Bool
1358956cc18dSsnjsameItem(const char *actual, const char *wanted)
1359956cc18dSsnj{
1360956cc18dSsnj    Bool result = False;
1361956cc18dSsnj    size_t have = strlen(actual);
1362956cc18dSsnj    size_t need = strlen(wanted);
1363d522f475Smrg
1364956cc18dSsnj    if (have != 0 && have <= need) {
1365956cc18dSsnj	if (!strncmp(actual, wanted, have)) {
1366956cc18dSsnj	    TRACE(("...matched \"%s\"\n", wanted));
1367956cc18dSsnj	    result = True;
1368956cc18dSsnj	}
1369956cc18dSsnj    }
1370956cc18dSsnj
1371956cc18dSsnj    return result;
1372956cc18dSsnj}
1373956cc18dSsnj
1374956cc18dSsnj/*
1375956cc18dSsnj * Handle the eightBitSelectTypes or utf8SelectTypes resource values.
1376956cc18dSsnj */
1377956cc18dSsnjstatic Bool
1378894e0ac8SmrgoverrideTargets(Widget w, String value, Atom **resultp)
1379956cc18dSsnj{
1380956cc18dSsnj    Bool override = False;
1381956cc18dSsnj    XtermWidget xw;
1382956cc18dSsnj
1383956cc18dSsnj    if ((xw = getXtermWidget(w)) != 0) {
1384956cc18dSsnj	TScreen *screen = TScreenOf(xw);
1385956cc18dSsnj
138620d2c4d2Smrg	if (!IsEmpty(value)) {
1387492d43a5Smrg	    char *copied = x_strdup(value);
1388956cc18dSsnj	    if (copied != 0) {
1389956cc18dSsnj		Atom *result = 0;
1390956cc18dSsnj		Cardinal count = 1;
1391956cc18dSsnj		int n;
1392d522f475Smrg
1393956cc18dSsnj		TRACE(("decoding SelectTypes \"%s\"\n", value));
1394956cc18dSsnj		for (n = 0; copied[n] != '\0'; ++n) {
1395956cc18dSsnj		    if (copied[n] == ',')
1396956cc18dSsnj			++count;
1397956cc18dSsnj		}
1398a1f3da82Smrg		result = TypeXtMallocN(Atom, (2 * count) + 1);
1399956cc18dSsnj		if (result == NULL) {
1400956cc18dSsnj		    TRACE(("Couldn't allocate selection types\n"));
1401956cc18dSsnj		} else {
1402956cc18dSsnj		    char nextc = '?';
140320d2c4d2Smrg		    char *listp = (char *) copied;
1404956cc18dSsnj		    count = 0;
1405956cc18dSsnj		    do {
1406956cc18dSsnj			char *nextp = parseItem(listp, &nextc);
14070bd37d32Smrg			char *item = x_strtrim(listp);
14080bd37d32Smrg			size_t len = (item ? strlen(item) : 0);
1409956cc18dSsnj
1410956cc18dSsnj			if (len == 0) {
1411a1f3da82Smrg			    /* EMPTY */ ;
1412956cc18dSsnj			}
1413956cc18dSsnj#if OPT_WIDE_CHARS
14140bd37d32Smrg			else if (sameItem(item, "UTF8")) {
1415956cc18dSsnj			    result[count++] = XA_UTF8_STRING(XtDisplay(w));
1416956cc18dSsnj			}
1417956cc18dSsnj#endif
14180bd37d32Smrg			else if (sameItem(item, "I18N")) {
1419956cc18dSsnj			    if (screen->i18nSelections) {
1420956cc18dSsnj				result[count++] = XA_TEXT(XtDisplay(w));
1421956cc18dSsnj				result[count++] = XA_COMPOUND_TEXT(XtDisplay(w));
1422956cc18dSsnj			    }
14230bd37d32Smrg			} else if (sameItem(item, "TEXT")) {
1424956cc18dSsnj			    result[count++] = XA_TEXT(XtDisplay(w));
14250bd37d32Smrg			} else if (sameItem(item, "COMPOUND_TEXT")) {
1426956cc18dSsnj			    result[count++] = XA_COMPOUND_TEXT(XtDisplay(w));
14270bd37d32Smrg			} else if (sameItem(item, "STRING")) {
1428956cc18dSsnj			    result[count++] = XA_STRING;
1429956cc18dSsnj			}
1430956cc18dSsnj			*nextp++ = nextc;
1431956cc18dSsnj			listp = nextp;
14320bd37d32Smrg			free(item);
1433956cc18dSsnj		    } while (nextc != '\0');
1434956cc18dSsnj		    if (count) {
1435956cc18dSsnj			result[count] = None;
1436956cc18dSsnj			override = True;
1437956cc18dSsnj			*resultp = result;
1438956cc18dSsnj		    } else {
1439956cc18dSsnj			XtFree((char *) result);
1440956cc18dSsnj		    }
1441956cc18dSsnj		}
14420bd37d32Smrg		free(copied);
1443956cc18dSsnj	    } else {
1444956cc18dSsnj		TRACE(("Couldn't allocate copy of selection types\n"));
1445d522f475Smrg	    }
1446956cc18dSsnj	}
1447956cc18dSsnj    }
1448956cc18dSsnj    return override;
1449956cc18dSsnj}
1450956cc18dSsnj
1451956cc18dSsnj#if OPT_WIDE_CHARS
1452956cc18dSsnjstatic Atom *
1453e0a2b6dfSmrgallocUtf8Targets(Widget w, TScreen *screen)
1454956cc18dSsnj{
1455956cc18dSsnj    Atom **resultp = &(screen->selection_targets_utf8);
1456956cc18dSsnj
1457956cc18dSsnj    if (*resultp == 0) {
1458956cc18dSsnj	Atom *result;
1459956cc18dSsnj
1460956cc18dSsnj	if (!overrideTargets(w, screen->utf8_select_types, &result)) {
1461a1f3da82Smrg	    result = TypeXtMallocN(Atom, 5);
1462956cc18dSsnj	    if (result == NULL) {
1463956cc18dSsnj		TRACE(("Couldn't allocate utf-8 selection targets\n"));
1464956cc18dSsnj	    } else {
1465956cc18dSsnj		int n = 0;
1466956cc18dSsnj
1467e39b573cSmrg		if (XSupportsLocale()) {
1468e39b573cSmrg		    result[n++] = XA_UTF8_STRING(XtDisplay(w));
1469d522f475Smrg#ifdef X_HAVE_UTF8_STRING
1470e39b573cSmrg		    if (screen->i18nSelections) {
1471e39b573cSmrg			result[n++] = XA_TEXT(XtDisplay(w));
1472e39b573cSmrg			result[n++] = XA_COMPOUND_TEXT(XtDisplay(w));
1473e39b573cSmrg		    }
1474d522f475Smrg#endif
1475e39b573cSmrg		}
1476956cc18dSsnj		result[n++] = XA_STRING;
1477956cc18dSsnj		result[n] = None;
1478956cc18dSsnj	    }
1479d522f475Smrg	}
1480956cc18dSsnj
1481956cc18dSsnj	*resultp = result;
1482d522f475Smrg    }
1483956cc18dSsnj
1484956cc18dSsnj    return *resultp;
1485956cc18dSsnj}
1486d522f475Smrg#endif
1487d522f475Smrg
1488956cc18dSsnjstatic Atom *
1489e0a2b6dfSmrgalloc8bitTargets(Widget w, TScreen *screen)
1490956cc18dSsnj{
1491956cc18dSsnj    Atom **resultp = &(screen->selection_targets_8bit);
1492956cc18dSsnj
1493956cc18dSsnj    if (*resultp == 0) {
1494956cc18dSsnj	Atom *result = 0;
1495956cc18dSsnj
1496956cc18dSsnj	if (!overrideTargets(w, screen->eightbit_select_types, &result)) {
1497a1f3da82Smrg	    result = TypeXtMallocN(Atom, 5);
1498956cc18dSsnj	    if (result == NULL) {
1499956cc18dSsnj		TRACE(("Couldn't allocate 8bit selection targets\n"));
1500956cc18dSsnj	    } else {
1501956cc18dSsnj		int n = 0;
1502956cc18dSsnj
1503e39b573cSmrg		if (XSupportsLocale()) {
1504d522f475Smrg#ifdef X_HAVE_UTF8_STRING
1505e39b573cSmrg		    result[n++] = XA_UTF8_STRING(XtDisplay(w));
1506956cc18dSsnj#endif
1507e39b573cSmrg		    if (screen->i18nSelections) {
1508e39b573cSmrg			result[n++] = XA_TEXT(XtDisplay(w));
1509e39b573cSmrg			result[n++] = XA_COMPOUND_TEXT(XtDisplay(w));
1510e39b573cSmrg		    }
1511956cc18dSsnj		}
1512956cc18dSsnj		result[n++] = XA_STRING;
1513956cc18dSsnj		result[n] = None;
1514956cc18dSsnj	    }
1515956cc18dSsnj	}
1516956cc18dSsnj
1517956cc18dSsnj	*resultp = result;
1518956cc18dSsnj    }
1519956cc18dSsnj
1520956cc18dSsnj    return *resultp;
1521956cc18dSsnj}
1522956cc18dSsnj
1523956cc18dSsnjstatic Atom *
1524956cc18dSsnj_SelectionTargets(Widget w)
1525956cc18dSsnj{
1526956cc18dSsnj    Atom *result;
1527956cc18dSsnj    TScreen *screen;
1528956cc18dSsnj    XtermWidget xw;
1529956cc18dSsnj
1530956cc18dSsnj    if ((xw = getXtermWidget(w)) == 0) {
1531956cc18dSsnj	result = NULL;
1532956cc18dSsnj    } else {
1533956cc18dSsnj	screen = TScreenOf(xw);
1534956cc18dSsnj
1535956cc18dSsnj#if OPT_WIDE_CHARS
1536956cc18dSsnj	if (screen->wide_chars) {
1537956cc18dSsnj	    result = allocUtf8Targets(w, screen);
1538956cc18dSsnj	} else
1539d522f475Smrg#endif
1540956cc18dSsnj	{
1541956cc18dSsnj	    /* not screen->wide_chars */
1542956cc18dSsnj	    result = alloc8bitTargets(w, screen);
1543d522f475Smrg	}
1544d522f475Smrg    }
1545956cc18dSsnj
1546956cc18dSsnj    return result;
1547d522f475Smrg}
1548d522f475Smrg
1549d522f475Smrg#define isSELECT(value) (!strcmp(value, "SELECT"))
1550d522f475Smrg
1551d522f475Smrgstatic void
1552d522f475SmrgUnmapSelections(XtermWidget xw)
1553d522f475Smrg{
1554956cc18dSsnj    TScreen *screen = TScreenOf(xw);
1555d522f475Smrg    Cardinal n;
1556d522f475Smrg
1557d522f475Smrg    if (screen->mappedSelect) {
1558d522f475Smrg	for (n = 0; screen->mappedSelect[n] != 0; ++n)
155920d2c4d2Smrg	    free((void *) screen->mappedSelect[n]);
1560d522f475Smrg	free(screen->mappedSelect);
1561d522f475Smrg	screen->mappedSelect = 0;
1562d522f475Smrg    }
1563d522f475Smrg}
1564d522f475Smrg
1565d522f475Smrg/*
1566d522f475Smrg * xterm generally uses the primary selection.  Some applications prefer
1567d522f475Smrg * (or are limited to) the clipboard.  Since the translations resource is
1568d522f475Smrg * complicated, users seldom change the way it affects selection.  But it
1569d522f475Smrg * is simple to remap the choice between primary and clipboard before the
1570d522f475Smrg * call to XmuInternStrings().
1571d522f475Smrg */
1572d522f475Smrgstatic String *
1573e0a2b6dfSmrgMapSelections(XtermWidget xw, String *params, Cardinal num_params)
1574d522f475Smrg{
1575d522f475Smrg    String *result = params;
1576d522f475Smrg
1577d522f475Smrg    if (num_params > 0) {
1578d522f475Smrg	Cardinal j;
1579d522f475Smrg	Boolean map = False;
1580d522f475Smrg
1581d522f475Smrg	for (j = 0; j < num_params; ++j) {
1582d522f475Smrg	    TRACE(("param[%d]:%s\n", j, params[j]));
1583d522f475Smrg	    if (isSELECT(params[j])) {
1584d522f475Smrg		map = True;
1585d522f475Smrg		break;
1586d522f475Smrg	    }
1587d522f475Smrg	}
1588d522f475Smrg	if (map) {
1589956cc18dSsnj	    TScreen *screen = TScreenOf(xw);
1590956cc18dSsnj	    const char *mapTo = (screen->selectToClipboard
1591d522f475Smrg				 ? "CLIPBOARD"
1592d522f475Smrg				 : "PRIMARY");
1593d522f475Smrg
1594d522f475Smrg	    UnmapSelections(xw);
1595d522f475Smrg	    if ((result = TypeMallocN(String, num_params + 1)) != 0) {
1596d522f475Smrg		result[num_params] = 0;
1597d522f475Smrg		for (j = 0; j < num_params; ++j) {
1598d522f475Smrg		    result[j] = x_strdup((isSELECT(params[j])
1599d522f475Smrg					  ? mapTo
1600d522f475Smrg					  : params[j]));
1601d522f475Smrg		    if (result[j] == 0) {
1602d522f475Smrg			UnmapSelections(xw);
16030bd37d32Smrg			while (j != 0) {
16040bd37d32Smrg			    free((void *) result[--j]);
16050bd37d32Smrg			}
16060bd37d32Smrg			free(result);
1607d522f475Smrg			result = 0;
1608d522f475Smrg			break;
1609d522f475Smrg		    }
1610d522f475Smrg		}
1611956cc18dSsnj		screen->mappedSelect = result;
1612d522f475Smrg	    }
1613d522f475Smrg	}
1614d522f475Smrg    }
1615d522f475Smrg    return result;
1616d522f475Smrg}
1617d522f475Smrg
1618d522f475Smrg/*
1619d522f475Smrg * Lookup the cut-buffer number, which will be in the range 0-7.
1620d522f475Smrg * If it is not a cut-buffer, it is the primary selection (-1).
1621d522f475Smrg */
1622d522f475Smrgstatic int
162320d2c4d2SmrgCutBuffer(Atom code)
1624d522f475Smrg{
1625d522f475Smrg    int cutbuffer;
162620d2c4d2Smrg    switch ((unsigned) code) {
1627d522f475Smrg    case XA_CUT_BUFFER0:
1628d522f475Smrg	cutbuffer = 0;
1629d522f475Smrg	break;
1630d522f475Smrg    case XA_CUT_BUFFER1:
1631d522f475Smrg	cutbuffer = 1;
1632d522f475Smrg	break;
1633d522f475Smrg    case XA_CUT_BUFFER2:
1634d522f475Smrg	cutbuffer = 2;
1635d522f475Smrg	break;
1636d522f475Smrg    case XA_CUT_BUFFER3:
1637d522f475Smrg	cutbuffer = 3;
1638d522f475Smrg	break;
1639d522f475Smrg    case XA_CUT_BUFFER4:
1640d522f475Smrg	cutbuffer = 4;
1641d522f475Smrg	break;
1642d522f475Smrg    case XA_CUT_BUFFER5:
1643d522f475Smrg	cutbuffer = 5;
1644d522f475Smrg	break;
1645d522f475Smrg    case XA_CUT_BUFFER6:
1646d522f475Smrg	cutbuffer = 6;
1647d522f475Smrg	break;
1648d522f475Smrg    case XA_CUT_BUFFER7:
1649d522f475Smrg	cutbuffer = 7;
1650d522f475Smrg	break;
1651d522f475Smrg    default:
1652d522f475Smrg	cutbuffer = -1;
1653d522f475Smrg	break;
1654d522f475Smrg    }
16550bd37d32Smrg    TRACE(("CutBuffer(%d) = %d\n", (int) code, cutbuffer));
1656d522f475Smrg    return cutbuffer;
1657d522f475Smrg}
1658d522f475Smrg
1659d522f475Smrg#if OPT_PASTE64
1660d522f475Smrgstatic void
1661d522f475SmrgFinishPaste64(XtermWidget xw)
1662d522f475Smrg{
1663956cc18dSsnj    TScreen *screen = TScreenOf(xw);
1664956cc18dSsnj
1665956cc18dSsnj    TRACE(("FinishPaste64(%d)\n", screen->base64_paste));
1666956cc18dSsnj    if (screen->base64_paste) {
1667956cc18dSsnj	screen->base64_paste = 0;
1668956cc18dSsnj	unparseputc1(xw, screen->base64_final);
1669d522f475Smrg	unparse_end(xw);
1670d522f475Smrg    }
1671d522f475Smrg}
1672d522f475Smrg#endif
1673d522f475Smrg
1674d522f475Smrg#if !OPT_PASTE64
1675d522f475Smrgstatic
1676d522f475Smrg#endif
1677d522f475Smrgvoid
1678d522f475SmrgxtermGetSelection(Widget w,
1679d522f475Smrg		  Time ev_time,
1680e0a2b6dfSmrg		  String *params,	/* selections in precedence order */
1681d522f475Smrg		  Cardinal num_params,
1682894e0ac8Smrg		  Atom *targets)
1683d522f475Smrg{
1684d522f475Smrg    Atom selection;
1685d522f475Smrg    int cutbuffer;
1686d522f475Smrg    Atom target;
1687d522f475Smrg
1688956cc18dSsnj    XtermWidget xw;
1689956cc18dSsnj
169020d2c4d2Smrg    if (num_params == 0)
169120d2c4d2Smrg	return;
1692956cc18dSsnj    if ((xw = getXtermWidget(w)) == 0)
1693d522f475Smrg	return;
1694d522f475Smrg
1695e0a2b6dfSmrg    TRACE(("xtermGetSelection num_params %d @%ld\n", num_params, ev_time));
1696956cc18dSsnj    params = MapSelections(xw, params, num_params);
1697d522f475Smrg
1698d522f475Smrg    XmuInternStrings(XtDisplay(w), params, (Cardinal) 1, &selection);
1699d522f475Smrg    cutbuffer = CutBuffer(selection);
1700d522f475Smrg
1701956cc18dSsnj    TRACE(("Cutbuffer: %d, target: %s\n", cutbuffer,
1702956cc18dSsnj	   (targets
1703956cc18dSsnj	    ? visibleSelectionTarget(XtDisplay(w), targets[0])
1704956cc18dSsnj	    : "None")));
1705d522f475Smrg
1706d522f475Smrg    if (cutbuffer >= 0) {
1707d522f475Smrg	int inbytes;
1708d522f475Smrg	unsigned long nbytes;
1709d522f475Smrg	int fmt8 = 8;
1710d522f475Smrg	Atom type = XA_STRING;
1711d522f475Smrg	char *line;
1712d522f475Smrg
1713d522f475Smrg	/* 'line' is freed in SelectionReceived */
1714d522f475Smrg	line = XFetchBuffer(XtDisplay(w), &inbytes, cutbuffer);
1715d522f475Smrg	nbytes = (unsigned long) inbytes;
1716d522f475Smrg
17170bd37d32Smrg	if (nbytes > 0) {
1718d522f475Smrg	    SelectionReceived(w, NULL, &selection, &type, (XtPointer) line,
1719d522f475Smrg			      &nbytes, &fmt8);
17200bd37d32Smrg	} else if (num_params > 1) {
1721d522f475Smrg	    xtermGetSelection(w, ev_time, params + 1, num_params - 1, NULL);
1722d522f475Smrg	}
1723d522f475Smrg#if OPT_PASTE64
1724d522f475Smrg	else {
1725956cc18dSsnj	    FinishPaste64(xw);
1726d522f475Smrg	}
1727d522f475Smrg#endif
1728d522f475Smrg    } else {
1729d522f475Smrg	struct _SelectionList *list;
1730d522f475Smrg
1731d522f475Smrg	if (targets == NULL || targets[0] == None) {
1732d522f475Smrg	    targets = _SelectionTargets(w);
1733d522f475Smrg	}
1734d522f475Smrg
1735d522f475Smrg	if (targets != 0) {
1736d522f475Smrg	    target = targets[0];
1737d522f475Smrg
1738d522f475Smrg	    if (targets[1] == None) {	/* last target in list */
1739d522f475Smrg		params++;
1740d522f475Smrg		num_params--;
1741d522f475Smrg		targets = _SelectionTargets(w);
1742d522f475Smrg	    } else {
1743d522f475Smrg		targets = &(targets[1]);
1744d522f475Smrg	    }
1745d522f475Smrg
1746d522f475Smrg	    if (num_params) {
1747d522f475Smrg		/* 'list' is freed in SelectionReceived */
1748a1f3da82Smrg		list = TypeXtMalloc(struct _SelectionList);
1749d522f475Smrg		if (list != 0) {
1750d522f475Smrg		    list->params = params;
1751d522f475Smrg		    list->count = num_params;
1752d522f475Smrg		    list->targets = targets;
1753d522f475Smrg		    list->time = ev_time;
1754d522f475Smrg		}
1755d522f475Smrg	    } else {
1756d522f475Smrg		list = NULL;
1757d522f475Smrg	    }
1758d522f475Smrg
1759d522f475Smrg	    XtGetSelectionValue(w, selection,
1760d522f475Smrg				target,
1761d522f475Smrg				SelectionReceived,
1762d522f475Smrg				(XtPointer) list, ev_time);
1763d522f475Smrg	}
1764d522f475Smrg    }
1765d522f475Smrg}
1766d522f475Smrg
1767d522f475Smrg#if OPT_TRACE && OPT_WIDE_CHARS
1768d522f475Smrgstatic void
1769e0a2b6dfSmrgGettingSelection(Display *dpy, Atom type, Char *line, unsigned long len)
1770d522f475Smrg{
1771d522f475Smrg    Char *cp;
1772d522f475Smrg    char *name;
1773d522f475Smrg
1774d522f475Smrg    name = XGetAtomName(dpy, type);
1775d522f475Smrg
177601037d57Smrg    TRACE(("Getting %s (type=%ld, length=%ld)\n", name, (long int) type, len));
1777d522f475Smrg    for (cp = line; cp < line + len; cp++) {
1778956cc18dSsnj	TRACE(("[%d:%lu]", (int) (cp + 1 - line), len));
1779d522f475Smrg	if (isprint(*cp)) {
1780d522f475Smrg	    TRACE(("%c\n", *cp));
1781d522f475Smrg	} else {
1782d522f475Smrg	    TRACE(("\\x%02x\n", *cp));
1783d522f475Smrg	}
1784d522f475Smrg    }
1785d522f475Smrg}
1786d522f475Smrg#else
1787d522f475Smrg#define GettingSelection(dpy,type,line,len)	/* nothing */
1788d522f475Smrg#endif
1789d522f475Smrg
1790d522f475Smrg#ifdef VMS
1791d522f475Smrg#  define tty_vwrite(pty,lag,l)		tt_write(lag,l)
1792d522f475Smrg#else /* !( VMS ) */
1793d522f475Smrg#  define tty_vwrite(pty,lag,l)		v_write(pty,lag,l)
1794d522f475Smrg#endif /* defined VMS */
1795d522f475Smrg
1796d522f475Smrg#if OPT_PASTE64
1797d522f475Smrg/* Return base64 code character given 6-bit number */
1798d522f475Smrgstatic const char base64_code[] = "\
1799d522f475SmrgABCDEFGHIJKLMNOPQRSTUVWXYZ\
1800d522f475Smrgabcdefghijklmnopqrstuvwxyz\
1801d522f475Smrg0123456789+/";
1802d522f475Smrgstatic void
1803e0a2b6dfSmrgbase64_flush(TScreen *screen)
1804d522f475Smrg{
1805d522f475Smrg    Char x;
180601037d57Smrg
180701037d57Smrg    TRACE(("base64_flush count %d, pad %d (%d)\n",
180801037d57Smrg	   screen->base64_count,
180901037d57Smrg	   screen->base64_pad,
181001037d57Smrg	   screen->base64_pad & 3));
181101037d57Smrg
1812d522f475Smrg    switch (screen->base64_count) {
1813d522f475Smrg    case 0:
1814d522f475Smrg	break;
1815d522f475Smrg    case 2:
18162eaa94a1Schristos	x = CharOf(base64_code[screen->base64_accu << 4]);
1817d522f475Smrg	tty_vwrite(screen->respond, &x, 1);
1818d522f475Smrg	break;
1819d522f475Smrg    case 4:
18202eaa94a1Schristos	x = CharOf(base64_code[screen->base64_accu << 2]);
1821d522f475Smrg	tty_vwrite(screen->respond, &x, 1);
1822d522f475Smrg	break;
1823d522f475Smrg    }
182401037d57Smrg    if (screen->base64_pad & 3) {
1825d522f475Smrg	tty_vwrite(screen->respond,
1826492d43a5Smrg		   (const Char *) "===",
182701037d57Smrg		   (unsigned) (3 - (screen->base64_pad & 3)));
182801037d57Smrg    }
1829d522f475Smrg    screen->base64_count = 0;
1830d522f475Smrg    screen->base64_accu = 0;
1831d522f475Smrg    screen->base64_pad = 0;
1832d522f475Smrg}
1833d522f475Smrg#endif /* OPT_PASTE64 */
1834d522f475Smrg
1835e0a2b6dfSmrg/*
1836e0a2b6dfSmrg * Translate ISO-8859-1 or UTF-8 data to NRCS.
1837e0a2b6dfSmrg */
1838d522f475Smrgstatic void
1839e0a2b6dfSmrgToNational(TScreen *screen, Char *buffer, unsigned *length)
1840e0a2b6dfSmrg{
1841e0a2b6dfSmrg    int gsetL = screen->gsets[screen->curgl];
1842e0a2b6dfSmrg    int gsetR = screen->gsets[screen->curgr];
1843e0a2b6dfSmrg    unsigned chr, out, gl, gr;
1844e0a2b6dfSmrg    Char *p;
1845e0a2b6dfSmrg
1846e0a2b6dfSmrg#if OPT_WIDE_CHARS
1847e0a2b6dfSmrg    if ((screen->utf8_nrc_mode | screen->utf8_mode) != uFalse) {
1848e0a2b6dfSmrg	PtyData *data = TypeXtMallocX(PtyData, *length);
1849e0a2b6dfSmrg
1850e0a2b6dfSmrg	memset(data, 0, sizeof(*data));
1851e0a2b6dfSmrg	data->next = data->buffer;
1852e0a2b6dfSmrg	data->last = data->buffer + *length;
1853e0a2b6dfSmrg	memcpy(data->buffer, buffer, (size_t) *length);
1854e0a2b6dfSmrg	p = buffer;
1855e0a2b6dfSmrg	while (data->next < data->last) {
1856894e0ac8Smrg	    if (!decodeUtf8(screen, data)) {
1857e0a2b6dfSmrg		data->utf_size = 1;
1858e0a2b6dfSmrg		data->utf_data = data->next[0];
1859e0a2b6dfSmrg	    }
1860e0a2b6dfSmrg	    data->next += data->utf_size;
1861e0a2b6dfSmrg	    chr = data->utf_data;
1862e0a2b6dfSmrg	    out = chr;
1863e0a2b6dfSmrg	    if ((gl = xtermCharSetIn(screen, chr, gsetL)) != chr) {
1864e0a2b6dfSmrg		out = gl;
1865e0a2b6dfSmrg	    } else if ((gr = xtermCharSetIn(screen, chr, gsetR)) != chr) {
1866e0a2b6dfSmrg		out = gr;
1867e0a2b6dfSmrg	    }
1868e0a2b6dfSmrg	    *p++ = (Char) ((out < 256) ? out : ' ');
1869e0a2b6dfSmrg	}
1870e0a2b6dfSmrg	*length = (unsigned) (p - buffer);
1871e0a2b6dfSmrg	free(data);
1872e0a2b6dfSmrg    } else
1873e0a2b6dfSmrg#endif
1874e0a2b6dfSmrg    {
1875e0a2b6dfSmrg	for (p = buffer; (int) (p - buffer) < (int) *length; ++p) {
1876e0a2b6dfSmrg	    chr = *p;
1877e0a2b6dfSmrg	    out = chr;
1878e0a2b6dfSmrg	    if ((gl = xtermCharSetIn(screen, chr, gsetL)) != chr) {
1879e0a2b6dfSmrg		out = gl;
1880e0a2b6dfSmrg	    } else if ((gr = xtermCharSetIn(screen, chr, gsetR)) != chr) {
1881e0a2b6dfSmrg		out = gr;
1882e0a2b6dfSmrg	    }
1883e0a2b6dfSmrg	    *p = (Char) out;
1884e0a2b6dfSmrg	}
1885e0a2b6dfSmrg    }
1886e0a2b6dfSmrg}
1887e0a2b6dfSmrg
1888e0a2b6dfSmrgstatic void
1889e0a2b6dfSmrg_qWriteSelectionData(XtermWidget xw, Char *lag, unsigned length)
1890d522f475Smrg{
18910bd37d32Smrg    TScreen *screen = TScreenOf(xw);
18920bd37d32Smrg
1893e0a2b6dfSmrg    /*
1894e0a2b6dfSmrg     * If we are pasting into a window which is using NRCS, we want to map
1895e0a2b6dfSmrg     * the text from the normal encoding (ISO-8859-1 or UTF-8) into the coding
1896e0a2b6dfSmrg     * that an application would use to write characters with NRCS.
1897e0a2b6dfSmrg     *
1898e0a2b6dfSmrg     * TODO: handle conversion from UTF-8, and adjust length.  This can be done
1899e0a2b6dfSmrg     * in the same buffer because the target is always 8-bit.
1900e0a2b6dfSmrg     */
1901e0a2b6dfSmrg    if ((xw->flags & NATIONAL) && (length != 0)) {
1902e0a2b6dfSmrg	ToNational(screen, lag, &length);
1903e0a2b6dfSmrg    }
1904d522f475Smrg#if OPT_PASTE64
1905d522f475Smrg    if (screen->base64_paste) {
1906d522f475Smrg	/* Send data as base64 */
1907d522f475Smrg	Char *p = lag;
1908d522f475Smrg	Char buf[64];
1909d522f475Smrg	unsigned x = 0;
19100bd37d32Smrg
191101037d57Smrg	TRACE(("convert to base64 %d:%s\n", length, visibleChars(p, length)));
191201037d57Smrg
19130bd37d32Smrg	/*
19140bd37d32Smrg	 * Handle the case where the selection is from _this_ xterm, which
19150bd37d32Smrg	 * puts part of the reply in the buffer before the selection callback
19160bd37d32Smrg	 * happens.
19170bd37d32Smrg	 */
19180bd37d32Smrg	if (screen->base64_paste && screen->unparse_len) {
19190bd37d32Smrg	    unparse_end(xw);
19200bd37d32Smrg	}
1921d522f475Smrg	while (length--) {
1922d522f475Smrg	    switch (screen->base64_count) {
1923d522f475Smrg	    case 0:
19242eaa94a1Schristos		buf[x++] = CharOf(base64_code[*p >> 2]);
19252eaa94a1Schristos		screen->base64_accu = (unsigned) (*p & 0x3);
1926d522f475Smrg		screen->base64_count = 2;
1927d522f475Smrg		++p;
1928d522f475Smrg		break;
1929d522f475Smrg	    case 2:
19302eaa94a1Schristos		buf[x++] = CharOf(base64_code[(screen->base64_accu << 4) +
19312eaa94a1Schristos					      (*p >> 4)]);
19322eaa94a1Schristos		screen->base64_accu = (unsigned) (*p & 0xF);
1933d522f475Smrg		screen->base64_count = 4;
1934d522f475Smrg		++p;
1935d522f475Smrg		break;
1936d522f475Smrg	    case 4:
19372eaa94a1Schristos		buf[x++] = CharOf(base64_code[(screen->base64_accu << 2) +
19382eaa94a1Schristos					      (*p >> 6)]);
19392eaa94a1Schristos		buf[x++] = CharOf(base64_code[*p & 0x3F]);
1940d522f475Smrg		screen->base64_accu = 0;
1941d522f475Smrg		screen->base64_count = 0;
1942d522f475Smrg		++p;
1943d522f475Smrg		break;
1944d522f475Smrg	    }
1945d522f475Smrg	    if (x >= 63) {
1946d522f475Smrg		/* Write 63 or 64 characters */
1947d522f475Smrg		screen->base64_pad += x;
194801037d57Smrg		TRACE(("writing base64 interim %s\n", visibleChars(buf, x)));
1949d522f475Smrg		tty_vwrite(screen->respond, buf, x);
1950d522f475Smrg		x = 0;
1951d522f475Smrg	    }
1952d522f475Smrg	}
1953d522f475Smrg	if (x != 0) {
1954d522f475Smrg	    screen->base64_pad += x;
195501037d57Smrg	    TRACE(("writing base64 finish %s\n", visibleChars(buf, x)));
1956d522f475Smrg	    tty_vwrite(screen->respond, buf, x);
1957d522f475Smrg	}
1958d522f475Smrg    } else
1959d522f475Smrg#endif /* OPT_PASTE64 */
1960d522f475Smrg#if OPT_READLINE
1961d522f475Smrg    if (SCREEN_FLAG(screen, paste_quotes)) {
1962d522f475Smrg	while (length--) {
1963492d43a5Smrg	    tty_vwrite(screen->respond, (const Char *) "\026", 1);	/* Control-V */
1964d522f475Smrg	    tty_vwrite(screen->respond, lag++, 1);
1965d522f475Smrg	}
1966d522f475Smrg    } else
1967d522f475Smrg#endif
196801037d57Smrg    {
196901037d57Smrg	TRACE(("writing base64 padding %s\n", visibleChars(lag, length)));
1970d522f475Smrg	tty_vwrite(screen->respond, lag, length);
197101037d57Smrg    }
1972d522f475Smrg}
1973d522f475Smrg
1974d522f475Smrgstatic void
1975e0a2b6dfSmrg_WriteSelectionData(XtermWidget xw, Char *line, size_t length)
1976d522f475Smrg{
1977d522f475Smrg    /* Write data to pty a line at a time. */
1978d522f475Smrg    /* Doing this one line at a time may no longer be necessary
1979d522f475Smrg       because v_write has been re-written. */
1980d522f475Smrg
19810bd37d32Smrg    TScreen *screen = TScreenOf(xw);
1982d522f475Smrg    Char *lag, *end;
1983d522f475Smrg
1984d522f475Smrg    /* in the VMS version, if tt_pasting isn't set to True then qio
1985d522f475Smrg       reads aren't blocked and an infinite loop is entered, where the
1986d522f475Smrg       pasted text shows up as new input, goes in again, shows up
1987d522f475Smrg       again, ad nauseum. */
1988d522f475Smrg#ifdef VMS
1989d522f475Smrg    tt_pasting = True;
1990d522f475Smrg#endif
1991d522f475Smrg
1992d522f475Smrg    end = &line[length];
1993d522f475Smrg    lag = line;
1994d522f475Smrg
1995d522f475Smrg#if OPT_PASTE64
1996d522f475Smrg    if (screen->base64_paste) {
19970bd37d32Smrg	_qWriteSelectionData(xw, lag, (unsigned) (end - lag));
1998d522f475Smrg	base64_flush(screen);
1999d522f475Smrg    } else
2000d522f475Smrg#endif
2001d522f475Smrg    {
2002d522f475Smrg	if (!SCREEN_FLAG(screen, paste_literal_nl)) {
2003d522f475Smrg	    Char *cp;
2004d522f475Smrg	    for (cp = line; cp != end; cp++) {
2005d522f475Smrg		if (*cp == '\n') {
2006d522f475Smrg		    *cp = '\r';
20070bd37d32Smrg		    _qWriteSelectionData(xw, lag, (unsigned) (cp - lag + 1));
2008d522f475Smrg		    lag = cp + 1;
2009d522f475Smrg		}
2010d522f475Smrg	    }
2011d522f475Smrg	}
2012d522f475Smrg
2013d522f475Smrg	if (lag != end) {
20140bd37d32Smrg	    _qWriteSelectionData(xw, lag, (unsigned) (end - lag));
2015d522f475Smrg	}
2016d522f475Smrg    }
2017d522f475Smrg#ifdef VMS
2018d522f475Smrg    tt_pasting = False;
2019d522f475Smrg    tt_start_read();		/* reenable reads or a character may be lost */
2020d522f475Smrg#endif
2021d522f475Smrg}
2022d522f475Smrg
2023d522f475Smrg#if OPT_READLINE
2024d522f475Smrgstatic void
2025e0a2b6dfSmrg_WriteKey(TScreen *screen, const Char *in)
2026d522f475Smrg{
2027d522f475Smrg    Char line[16];
2028d522f475Smrg    unsigned count = 0;
2029492d43a5Smrg    size_t length = strlen((const char *) in);
2030d522f475Smrg
2031d522f475Smrg    if (screen->control_eight_bits) {
2032d522f475Smrg	line[count++] = ANSI_CSI;
2033d522f475Smrg    } else {
2034d522f475Smrg	line[count++] = ANSI_ESC;
2035d522f475Smrg	line[count++] = '[';
2036d522f475Smrg    }
2037d522f475Smrg    while (length--)
2038d522f475Smrg	line[count++] = *in++;
2039d522f475Smrg    line[count++] = '~';
2040d522f475Smrg    tty_vwrite(screen->respond, line, count);
2041d522f475Smrg}
2042d522f475Smrg#endif /* OPT_READLINE */
2043d522f475Smrg
20440bd37d32Smrg/*
20450bd37d32Smrg * Unless enabled by the user, strip control characters other than formatting.
20460bd37d32Smrg */
20470bd37d32Smrgstatic size_t
20480bd37d32SmrgremoveControls(XtermWidget xw, char *value)
20490bd37d32Smrg{
20500bd37d32Smrg    TScreen *screen = TScreenOf(xw);
20510bd37d32Smrg    size_t dst = 0;
20520bd37d32Smrg    size_t src = 0;
20530bd37d32Smrg
20540bd37d32Smrg    if (screen->allowPasteControls) {
20550bd37d32Smrg	dst = strlen(value);
20560bd37d32Smrg    } else {
20570bd37d32Smrg	while ((value[dst] = value[src]) != '\0') {
20580bd37d32Smrg	    int ch = CharOf(value[src++]);
20590bd37d32Smrg	    if (ch < 32) {
20600bd37d32Smrg		switch (ch) {
20610bd37d32Smrg		case '\b':
20620bd37d32Smrg		case '\t':
20630bd37d32Smrg		case '\n':
20640bd37d32Smrg		case '\r':
20650bd37d32Smrg		    ++dst;
20660bd37d32Smrg		    break;
20670bd37d32Smrg		default:
20680bd37d32Smrg		    continue;
20690bd37d32Smrg		}
20700bd37d32Smrg	    }
20710bd37d32Smrg#if OPT_WIDE_CHARS
2072e0a2b6dfSmrg	    else if (screen->utf8_inparse || screen->utf8_nrc_mode)
20730bd37d32Smrg		++dst;
20740bd37d32Smrg#endif
20750bd37d32Smrg#if OPT_C1_PRINT || OPT_WIDE_CHARS
20760bd37d32Smrg	    else if (screen->c1_printable)
20770bd37d32Smrg		++dst;
20780bd37d32Smrg#endif
20790bd37d32Smrg	    else if (ch >= 128 && ch < 160)
20800bd37d32Smrg		continue;
20810bd37d32Smrg	    else
20820bd37d32Smrg		++dst;
20830bd37d32Smrg	}
20840bd37d32Smrg    }
20850bd37d32Smrg    return dst;
20860bd37d32Smrg}
20870bd37d32Smrg
2088d522f475Smrg/* SelectionReceived: stuff received selection text into pty */
2089d522f475Smrg
2090d522f475Smrg/* ARGSUSED */
2091d522f475Smrgstatic void
2092d522f475SmrgSelectionReceived(Widget w,
2093d522f475Smrg		  XtPointer client_data,
2094894e0ac8Smrg		  Atom *selection GCC_UNUSED,
2095894e0ac8Smrg		  Atom *type,
2096d522f475Smrg		  XtPointer value,
2097d522f475Smrg		  unsigned long *length,
2098d522f475Smrg		  int *format)
2099d522f475Smrg{
2100d522f475Smrg    char **text_list = NULL;
2101d522f475Smrg    int text_list_count;
2102d522f475Smrg    XTextProperty text_prop;
2103d522f475Smrg    TScreen *screen;
2104d522f475Smrg    Display *dpy;
2105d522f475Smrg#if OPT_TRACE && OPT_WIDE_CHARS
2106d522f475Smrg    Char *line = (Char *) value;
2107d522f475Smrg#endif
2108d522f475Smrg
2109956cc18dSsnj    XtermWidget xw;
2110956cc18dSsnj
2111956cc18dSsnj    if ((xw = getXtermWidget(w)) == 0)
2112d522f475Smrg	return;
2113956cc18dSsnj
2114956cc18dSsnj    screen = TScreenOf(xw);
2115d522f475Smrg    dpy = XtDisplay(w);
2116d522f475Smrg
2117d522f475Smrg    if (*type == 0		/*XT_CONVERT_FAIL */
2118d522f475Smrg	|| *length == 0
211901037d57Smrg	|| value == NULL) {
212001037d57Smrg	TRACE(("...no data to convert\n"));
2121d522f475Smrg	goto fail;
212201037d57Smrg    }
2123d522f475Smrg
2124d522f475Smrg    text_prop.value = (unsigned char *) value;
2125d522f475Smrg    text_prop.encoding = *type;
2126d522f475Smrg    text_prop.format = *format;
2127d522f475Smrg    text_prop.nitems = *length;
2128d522f475Smrg
212901037d57Smrg    TRACE(("SelectionReceived %s %s format %d, nitems %ld\n",
213001037d57Smrg	   XGetAtomName(screen->display, *selection),
2131956cc18dSsnj	   visibleSelectionTarget(dpy, text_prop.encoding),
2132956cc18dSsnj	   text_prop.format,
2133956cc18dSsnj	   text_prop.nitems));
2134956cc18dSsnj
2135d522f475Smrg#if OPT_WIDE_CHARS
2136e39b573cSmrg    if (XSupportsLocale() && screen->wide_chars) {
2137d522f475Smrg	if (*type == XA_UTF8_STRING(dpy) ||
2138d522f475Smrg	    *type == XA_STRING ||
2139d522f475Smrg	    *type == XA_COMPOUND_TEXT(dpy)) {
2140d522f475Smrg	    GettingSelection(dpy, *type, line, *length);
2141d522f475Smrg	    if (Xutf8TextPropertyToTextList(dpy, &text_prop,
2142d522f475Smrg					    &text_list,
2143d522f475Smrg					    &text_list_count) < 0) {
2144e39b573cSmrg		TRACE(("default Xutf8 Conversion failed\n"));
2145d522f475Smrg		text_list = NULL;
2146d522f475Smrg	    }
2147d522f475Smrg	}
2148d522f475Smrg    } else
2149d522f475Smrg#endif /* OPT_WIDE_CHARS */
2150d522f475Smrg    {
2151d522f475Smrg	/* Convert the selection to locale's multibyte encoding. */
2152d522f475Smrg
2153d522f475Smrg	if (*type == XA_UTF8_STRING(dpy) ||
2154d522f475Smrg	    *type == XA_STRING ||
2155d522f475Smrg	    *type == XA_COMPOUND_TEXT(dpy)) {
2156d522f475Smrg	    Status rc;
2157d522f475Smrg
2158d522f475Smrg	    GettingSelection(dpy, *type, line, *length);
2159d522f475Smrg
2160d522f475Smrg#if OPT_WIDE_CHARS
2161d522f475Smrg	    if (*type == XA_UTF8_STRING(dpy) &&
2162d522f475Smrg		!(screen->wide_chars || screen->c1_printable)) {
216320d2c4d2Smrg		rc = xtermUtf8ToTextList(xw, &text_prop,
216420d2c4d2Smrg					 &text_list, &text_list_count);
2165d522f475Smrg	    } else
2166d522f475Smrg#endif
2167e39b573cSmrg	    if (*type == XA_STRING && (!XSupportsLocale() || screen->brokenSelections)) {
2168d522f475Smrg		rc = XTextPropertyToStringList(&text_prop,
2169d522f475Smrg					       &text_list, &text_list_count);
2170d522f475Smrg	    } else {
2171d522f475Smrg		rc = XmbTextPropertyToTextList(dpy, &text_prop,
2172d522f475Smrg					       &text_list,
2173d522f475Smrg					       &text_list_count);
2174d522f475Smrg	    }
2175d522f475Smrg	    if (rc < 0) {
2176d522f475Smrg		TRACE(("Conversion failed\n"));
2177d522f475Smrg		text_list = NULL;
2178d522f475Smrg	    }
2179d522f475Smrg	}
2180d522f475Smrg    }
2181d522f475Smrg
2182d522f475Smrg    if (text_list != NULL && text_list_count != 0) {
2183d522f475Smrg	int i;
2184d522f475Smrg
2185d522f475Smrg#if OPT_PASTE64
2186d522f475Smrg	if (screen->base64_paste) {
2187a1f3da82Smrg	    /* EMPTY */ ;
2188d522f475Smrg	} else
2189d522f475Smrg#endif
2190d522f475Smrg#if OPT_READLINE
2191d522f475Smrg	if (SCREEN_FLAG(screen, paste_brackets)) {
2192492d43a5Smrg	    _WriteKey(screen, (const Char *) "200");
2193d522f475Smrg	}
2194d522f475Smrg#endif
2195d522f475Smrg	for (i = 0; i < text_list_count; i++) {
21960bd37d32Smrg	    size_t len = removeControls(xw, text_list[i]);
219701037d57Smrg
21980bd37d32Smrg	    if (screen->selectToBuffer) {
219901037d57Smrg		InternalSelect *mydata = &(screen->internal_select);
220001037d57Smrg		size_t have = (mydata->buffer
220101037d57Smrg			       ? strlen(mydata->buffer)
22020bd37d32Smrg			       : 0);
22030bd37d32Smrg		size_t need = have + len + 1;
220401037d57Smrg		char *buffer = realloc(mydata->buffer, need);
220501037d57Smrg
220601037d57Smrg		screen->selectToBuffer = False;
220701037d57Smrg#if OPT_PASTE64
220801037d57Smrg		screen->base64_paste = mydata->base64_paste;
220901037d57Smrg#endif
221001037d57Smrg#if OPT_READLINE
221101037d57Smrg		screen->paste_brackets = mydata->paste_brackets;
221201037d57Smrg#endif
22130bd37d32Smrg		if (buffer != 0) {
22140bd37d32Smrg		    strcpy(buffer + have, text_list[i]);
221501037d57Smrg		    mydata->buffer = buffer;
22160bd37d32Smrg		}
221701037d57Smrg		TRACE(("FormatSelect %d.%d .. %d.%d %s\n",
221801037d57Smrg		       screen->startSel.row,
221901037d57Smrg		       screen->startSel.col,
222001037d57Smrg		       screen->endSel.row,
222101037d57Smrg		       screen->endSel.col,
222201037d57Smrg		       mydata->buffer));
222301037d57Smrg		mydata->format_select(w, mydata->format, mydata->buffer,
222401037d57Smrg				      &(screen->startSel),
222501037d57Smrg				      &(screen->endSel));
222601037d57Smrg
222701037d57Smrg		free(mydata->format);
222801037d57Smrg		free(mydata->buffer);
222901037d57Smrg		memset(mydata, 0, sizeof(*mydata));
22300bd37d32Smrg	    } else {
22310bd37d32Smrg		_WriteSelectionData(xw, (Char *) text_list[i], len);
22320bd37d32Smrg	    }
2233d522f475Smrg	}
2234d522f475Smrg#if OPT_PASTE64
2235d522f475Smrg	if (screen->base64_paste) {
2236956cc18dSsnj	    FinishPaste64(xw);
2237d522f475Smrg	} else
2238d522f475Smrg#endif
2239d522f475Smrg#if OPT_READLINE
2240d522f475Smrg	if (SCREEN_FLAG(screen, paste_brackets)) {
2241492d43a5Smrg	    _WriteKey(screen, (const Char *) "201");
2242d522f475Smrg	}
2243d522f475Smrg#endif
2244d522f475Smrg	XFreeStringList(text_list);
224501037d57Smrg    } else {
224601037d57Smrg	TRACE(("...empty text-list\n"));
2247d522f475Smrg	goto fail;
224801037d57Smrg    }
2249d522f475Smrg
2250d522f475Smrg    XtFree((char *) client_data);
2251d522f475Smrg    XtFree((char *) value);
2252d522f475Smrg
2253d522f475Smrg    return;
2254d522f475Smrg
2255d522f475Smrg  fail:
2256d522f475Smrg    if (client_data != 0) {
2257d522f475Smrg	struct _SelectionList *list = (struct _SelectionList *) client_data;
2258956cc18dSsnj
2259956cc18dSsnj	TRACE(("SelectionReceived ->xtermGetSelection\n"));
2260d522f475Smrg	xtermGetSelection(w, list->time,
2261d522f475Smrg			  list->params, list->count, list->targets);
2262d522f475Smrg	XtFree((char *) client_data);
2263d522f475Smrg#if OPT_PASTE64
2264d522f475Smrg    } else {
2265956cc18dSsnj	FinishPaste64(xw);
2266d522f475Smrg#endif
2267d522f475Smrg    }
2268d522f475Smrg    return;
2269d522f475Smrg}
2270d522f475Smrg
2271d522f475Smrgvoid
2272d522f475SmrgHandleInsertSelection(Widget w,
2273894e0ac8Smrg		      XEvent *event,	/* assumed to be XButtonEvent* */
2274e0a2b6dfSmrg		      String *params,	/* selections in precedence order */
2275d522f475Smrg		      Cardinal *num_params)
2276d522f475Smrg{
2277956cc18dSsnj    XtermWidget xw;
2278d522f475Smrg
2279956cc18dSsnj    if ((xw = getXtermWidget(w)) != 0) {
22800bd37d32Smrg	TRACE(("HandleInsertSelection\n"));
2281d522f475Smrg	if (!SendMousePosition(xw, event)) {
2282d522f475Smrg#if OPT_READLINE
2283d522f475Smrg	    int ldelta;
2284956cc18dSsnj	    TScreen *screen = TScreenOf(xw);
2285492d43a5Smrg	    if (IsBtnEvent(event)
2286d522f475Smrg	    /* Disable on Shift-mouse, including the application-mouse modes */
2287492d43a5Smrg		&& !(KeyModifiers(event) & ShiftMask)
2288d522f475Smrg		&& (screen->send_mouse_pos == MOUSE_OFF)
2289d522f475Smrg		&& SCREEN_FLAG(screen, paste_moves)
2290d522f475Smrg		&& rowOnCurrentLine(screen, eventRow(screen, event), &ldelta))
2291d522f475Smrg		ReadLineMovePoint(screen, eventColBetween(screen, event), ldelta);
2292d522f475Smrg#endif /* OPT_READLINE */
2293d522f475Smrg
2294d522f475Smrg	    xtermGetSelection(w, event->xbutton.time, params, *num_params, NULL);
2295d522f475Smrg	}
2296d522f475Smrg    }
2297d522f475Smrg}
2298d522f475Smrg
2299d522f475Smrgstatic SelectUnit
2300956cc18dSsnjEvalSelectUnit(XtermWidget xw,
2301d522f475Smrg	       Time buttonDownTime,
2302d522f475Smrg	       SelectUnit defaultUnit,
2303d522f475Smrg	       unsigned int button)
2304d522f475Smrg{
2305956cc18dSsnj    TScreen *screen = TScreenOf(xw);
2306d522f475Smrg    SelectUnit result;
2307d522f475Smrg    int delta;
2308d522f475Smrg
2309d522f475Smrg    if (button != screen->lastButton) {
231020d2c4d2Smrg	delta = screen->multiClickTime + 1;
2311d522f475Smrg    } else if (screen->lastButtonUpTime == (Time) 0) {
2312d522f475Smrg	/* first time and once in a blue moon */
2313d522f475Smrg	delta = screen->multiClickTime + 1;
2314d522f475Smrg    } else if (buttonDownTime > screen->lastButtonUpTime) {
2315d522f475Smrg	/* most of the time */
23162eaa94a1Schristos	delta = (int) (buttonDownTime - screen->lastButtonUpTime);
2317d522f475Smrg    } else {
2318d522f475Smrg	/* time has rolled over since lastButtonUpTime */
23192eaa94a1Schristos	delta = (int) ((((Time) ~ 0) - screen->lastButtonUpTime) + buttonDownTime);
2320d522f475Smrg    }
2321d522f475Smrg
2322d522f475Smrg    if (delta > screen->multiClickTime) {
2323d522f475Smrg	screen->numberOfClicks = 1;
2324d522f475Smrg	result = defaultUnit;
2325d522f475Smrg    } else {
2326d522f475Smrg	result = screen->selectMap[screen->numberOfClicks % screen->maxClicks];
2327d522f475Smrg	screen->numberOfClicks += 1;
2328d522f475Smrg    }
2329d522f475Smrg    TRACE(("EvalSelectUnit(%d) = %d\n", screen->numberOfClicks, result));
2330d522f475Smrg    return result;
2331d522f475Smrg}
2332d522f475Smrg
2333d522f475Smrgstatic void
2334d522f475Smrgdo_select_start(XtermWidget xw,
2335894e0ac8Smrg		XEvent *event,	/* must be XButtonEvent* */
2336e0a2b6dfSmrg		CELL *cell)
2337d522f475Smrg{
2338956cc18dSsnj    TScreen *screen = TScreenOf(xw);
2339d522f475Smrg
2340d522f475Smrg    if (SendMousePosition(xw, event))
2341d522f475Smrg	return;
2342956cc18dSsnj    screen->selectUnit = EvalSelectUnit(xw,
2343d522f475Smrg					event->xbutton.time,
2344d522f475Smrg					Select_CHAR,
2345d522f475Smrg					event->xbutton.button);
2346d522f475Smrg    screen->replyToEmacs = False;
2347d522f475Smrg
2348d522f475Smrg#if OPT_READLINE
2349d522f475Smrg    lastButtonDownTime = event->xbutton.time;
2350d522f475Smrg#endif
2351d522f475Smrg
2352d522f475Smrg    StartSelect(xw, cell);
2353d522f475Smrg}
2354d522f475Smrg
2355d522f475Smrg/* ARGSUSED */
2356d522f475Smrgvoid
2357d522f475SmrgHandleSelectStart(Widget w,
2358894e0ac8Smrg		  XEvent *event,	/* must be XButtonEvent* */
2359e0a2b6dfSmrg		  String *params GCC_UNUSED,
2360d522f475Smrg		  Cardinal *num_params GCC_UNUSED)
2361d522f475Smrg{
2362956cc18dSsnj    XtermWidget xw;
2363956cc18dSsnj
2364956cc18dSsnj    if ((xw = getXtermWidget(w)) != 0) {
2365956cc18dSsnj	TScreen *screen = TScreenOf(xw);
2366d522f475Smrg	CELL cell;
2367d522f475Smrg
23680bd37d32Smrg	TRACE(("HandleSelectStart\n"));
2369d522f475Smrg	screen->firstValidRow = 0;
2370d522f475Smrg	screen->lastValidRow = screen->max_row;
2371d522f475Smrg	PointToCELL(screen, event->xbutton.y, event->xbutton.x, &cell);
2372d522f475Smrg
2373d522f475Smrg#if OPT_READLINE
2374d522f475Smrg	ExtendingSelection = 0;
2375d522f475Smrg#endif
2376d522f475Smrg
2377d522f475Smrg	do_select_start(xw, event, &cell);
2378d522f475Smrg    }
2379d522f475Smrg}
2380d522f475Smrg
2381d522f475Smrg/* ARGSUSED */
2382d522f475Smrgvoid
2383d522f475SmrgHandleKeyboardSelectStart(Widget w,
2384894e0ac8Smrg			  XEvent *event,	/* must be XButtonEvent* */
2385e0a2b6dfSmrg			  String *params GCC_UNUSED,
2386d522f475Smrg			  Cardinal *num_params GCC_UNUSED)
2387d522f475Smrg{
2388956cc18dSsnj    XtermWidget xw;
2389956cc18dSsnj
2390956cc18dSsnj    if ((xw = getXtermWidget(w)) != 0) {
2391956cc18dSsnj	TScreen *screen = TScreenOf(xw);
23920bd37d32Smrg
23930bd37d32Smrg	TRACE(("HandleKeyboardSelectStart\n"));
2394d522f475Smrg	do_select_start(xw, event, &screen->cursorp);
2395d522f475Smrg    }
2396d522f475Smrg}
2397d522f475Smrg
2398d522f475Smrgstatic void
2399894e0ac8SmrgTrackDown(XtermWidget xw, XButtonEvent *event)
2400d522f475Smrg{
2401956cc18dSsnj    TScreen *screen = TScreenOf(xw);
2402d522f475Smrg    CELL cell;
2403d522f475Smrg
2404956cc18dSsnj    screen->selectUnit = EvalSelectUnit(xw,
2405d522f475Smrg					event->time,
2406d522f475Smrg					Select_CHAR,
2407d522f475Smrg					event->button);
2408d522f475Smrg    if (screen->numberOfClicks > 1) {
2409d522f475Smrg	PointToCELL(screen, event->y, event->x, &cell);
2410d522f475Smrg	screen->replyToEmacs = True;
2411d522f475Smrg	StartSelect(xw, &cell);
2412d522f475Smrg    } else {
2413d522f475Smrg	screen->waitingForTrackInfo = True;
2414492d43a5Smrg	EditorButton(xw, event);
2415d522f475Smrg    }
2416d522f475Smrg}
2417d522f475Smrg
2418d522f475Smrg#define boundsCheck(x)	if (x < 0) \
2419d522f475Smrg			    x = 0; \
2420d522f475Smrg			else if (x >= screen->max_row) \
2421d522f475Smrg			    x = screen->max_row
2422d522f475Smrg
2423d522f475Smrgvoid
2424d522f475SmrgTrackMouse(XtermWidget xw,
2425d522f475Smrg	   int func,
2426e0a2b6dfSmrg	   CELL *start,
2427d522f475Smrg	   int firstrow,
2428d522f475Smrg	   int lastrow)
2429d522f475Smrg{
2430956cc18dSsnj    TScreen *screen = TScreenOf(xw);
2431d522f475Smrg
2432d522f475Smrg    if (screen->waitingForTrackInfo) {	/* if Timed, ignore */
2433d522f475Smrg	screen->waitingForTrackInfo = False;
2434d522f475Smrg
2435d522f475Smrg	if (func != 0) {
2436d522f475Smrg	    CELL first = *start;
2437d522f475Smrg
2438d522f475Smrg	    boundsCheck(first.row);
2439d522f475Smrg	    boundsCheck(firstrow);
2440d522f475Smrg	    boundsCheck(lastrow);
2441d522f475Smrg	    screen->firstValidRow = firstrow;
2442d522f475Smrg	    screen->lastValidRow = lastrow;
2443d522f475Smrg	    screen->replyToEmacs = True;
2444d522f475Smrg	    StartSelect(xw, &first);
2445d522f475Smrg	}
2446d522f475Smrg    }
2447d522f475Smrg}
2448d522f475Smrg
2449d522f475Smrgstatic void
2450e0a2b6dfSmrgStartSelect(XtermWidget xw, const CELL *cell)
2451d522f475Smrg{
2452956cc18dSsnj    TScreen *screen = TScreenOf(xw);
2453d522f475Smrg
2454d522f475Smrg    TRACE(("StartSelect row=%d, col=%d\n", cell->row, cell->col));
2455d522f475Smrg    if (screen->cursor_state)
2456d522f475Smrg	HideCursor();
2457d522f475Smrg    if (screen->numberOfClicks == 1) {
2458d522f475Smrg	/* set start of selection */
2459d522f475Smrg	screen->rawPos = *cell;
2460d522f475Smrg    }
2461d522f475Smrg    /* else use old values in rawPos */
2462d522f475Smrg    screen->saveStartR = screen->startExt = screen->rawPos;
2463d522f475Smrg    screen->saveEndR = screen->endExt = screen->rawPos;
2464d522f475Smrg    if (Coordinate(screen, cell) < Coordinate(screen, &(screen->rawPos))) {
2465d522f475Smrg	screen->eventMode = LEFTEXTENSION;
2466d522f475Smrg	screen->startExt = *cell;
2467d522f475Smrg    } else {
2468d522f475Smrg	screen->eventMode = RIGHTEXTENSION;
2469d522f475Smrg	screen->endExt = *cell;
2470d522f475Smrg    }
2471d522f475Smrg    ComputeSelect(xw, &(screen->startExt), &(screen->endExt), False);
2472d522f475Smrg}
2473d522f475Smrg
2474d522f475Smrgstatic void
2475d522f475SmrgEndExtend(XtermWidget xw,
2476894e0ac8Smrg	  XEvent *event,	/* must be XButtonEvent */
2477e0a2b6dfSmrg	  String *params,	/* selections */
2478d522f475Smrg	  Cardinal num_params,
2479d522f475Smrg	  Bool use_cursor_loc)
2480d522f475Smrg{
2481d522f475Smrg    CELL cell;
2482d522f475Smrg    unsigned count;
2483956cc18dSsnj    TScreen *screen = TScreenOf(xw);
24840bd37d32Smrg    Char line[64];
2485d522f475Smrg
2486d522f475Smrg    if (use_cursor_loc) {
2487d522f475Smrg	cell = screen->cursorp;
2488d522f475Smrg    } else {
2489d522f475Smrg	PointToCELL(screen, event->xbutton.y, event->xbutton.x, &cell);
2490d522f475Smrg    }
2491d522f475Smrg    ExtendExtend(xw, &cell);
2492d522f475Smrg    screen->lastButtonUpTime = event->xbutton.time;
2493d522f475Smrg    screen->lastButton = event->xbutton.button;
2494d522f475Smrg    if (!isSameCELL(&(screen->startSel), &(screen->endSel))) {
2495d522f475Smrg	if (screen->replyToEmacs) {
2496d522f475Smrg	    count = 0;
2497d522f475Smrg	    if (screen->control_eight_bits) {
2498d522f475Smrg		line[count++] = ANSI_CSI;
2499d522f475Smrg	    } else {
2500d522f475Smrg		line[count++] = ANSI_ESC;
2501d522f475Smrg		line[count++] = '[';
2502d522f475Smrg	    }
2503d522f475Smrg	    if (isSameCELL(&(screen->rawPos), &(screen->startSel))
2504d522f475Smrg		&& isSameCELL(&cell, &(screen->endSel))) {
2505d522f475Smrg		/* Use short-form emacs select */
25060bd37d32Smrg
25070bd37d32Smrg		switch (screen->extend_coords) {
25080bd37d32Smrg		case 0:
25090bd37d32Smrg		case SET_EXT_MODE_MOUSE:
25100bd37d32Smrg		    line[count++] = 't';
25110bd37d32Smrg		    break;
25120bd37d32Smrg		case SET_SGR_EXT_MODE_MOUSE:
25130bd37d32Smrg		    line[count++] = '<';
25140bd37d32Smrg		    break;
25150bd37d32Smrg		}
25160bd37d32Smrg
2517492d43a5Smrg		count = EmitMousePosition(screen, line, count, screen->endSel.col);
25180bd37d32Smrg		count = EmitMousePositionSeparator(screen, line, count);
2519492d43a5Smrg		count = EmitMousePosition(screen, line, count, screen->endSel.row);
25200bd37d32Smrg
25210bd37d32Smrg		switch (screen->extend_coords) {
25220bd37d32Smrg		case SET_SGR_EXT_MODE_MOUSE:
25230bd37d32Smrg		case SET_URXVT_EXT_MODE_MOUSE:
25240bd37d32Smrg		    line[count++] = 't';
25250bd37d32Smrg		    break;
25260bd37d32Smrg		}
2527d522f475Smrg	    } else {
2528d522f475Smrg		/* long-form, specify everything */
25290bd37d32Smrg
25300bd37d32Smrg		switch (screen->extend_coords) {
25310bd37d32Smrg		case 0:
25320bd37d32Smrg		case SET_EXT_MODE_MOUSE:
25330bd37d32Smrg		    line[count++] = 'T';
25340bd37d32Smrg		    break;
25350bd37d32Smrg		case SET_SGR_EXT_MODE_MOUSE:
25360bd37d32Smrg		    line[count++] = '<';
25370bd37d32Smrg		    break;
25380bd37d32Smrg		}
25390bd37d32Smrg
2540492d43a5Smrg		count = EmitMousePosition(screen, line, count, screen->startSel.col);
25410bd37d32Smrg		count = EmitMousePositionSeparator(screen, line, count);
2542492d43a5Smrg		count = EmitMousePosition(screen, line, count, screen->startSel.row);
25430bd37d32Smrg		count = EmitMousePositionSeparator(screen, line, count);
2544492d43a5Smrg		count = EmitMousePosition(screen, line, count, screen->endSel.col);
25450bd37d32Smrg		count = EmitMousePositionSeparator(screen, line, count);
2546492d43a5Smrg		count = EmitMousePosition(screen, line, count, screen->endSel.row);
25470bd37d32Smrg		count = EmitMousePositionSeparator(screen, line, count);
2548492d43a5Smrg		count = EmitMousePosition(screen, line, count, cell.col);
25490bd37d32Smrg		count = EmitMousePositionSeparator(screen, line, count);
2550492d43a5Smrg		count = EmitMousePosition(screen, line, count, cell.row);
25510bd37d32Smrg
25520bd37d32Smrg		switch (screen->extend_coords) {
25530bd37d32Smrg		case SET_SGR_EXT_MODE_MOUSE:
25540bd37d32Smrg		case SET_URXVT_EXT_MODE_MOUSE:
25550bd37d32Smrg		    line[count++] = 'T';
25560bd37d32Smrg		    break;
25570bd37d32Smrg		}
2558d522f475Smrg	    }
2559d522f475Smrg	    v_write(screen->respond, line, count);
2560d522f475Smrg	    TrackText(xw, &zeroCELL, &zeroCELL);
2561d522f475Smrg	}
2562d522f475Smrg    }
2563d522f475Smrg    SelectSet(xw, event, params, num_params);
2564d522f475Smrg    screen->eventMode = NORMAL;
2565d522f475Smrg}
2566d522f475Smrg
2567d522f475Smrgvoid
2568d522f475SmrgHandleSelectSet(Widget w,
2569894e0ac8Smrg		XEvent *event,
2570e0a2b6dfSmrg		String *params,
2571d522f475Smrg		Cardinal *num_params)
2572d522f475Smrg{
2573956cc18dSsnj    XtermWidget xw;
2574956cc18dSsnj
2575956cc18dSsnj    if ((xw = getXtermWidget(w)) != 0) {
25760bd37d32Smrg	TRACE(("HandleSelectSet\n"));
2577956cc18dSsnj	SelectSet(xw, event, params, *num_params);
2578d522f475Smrg    }
2579d522f475Smrg}
2580d522f475Smrg
2581d522f475Smrg/* ARGSUSED */
2582d522f475Smrgstatic void
2583d522f475SmrgSelectSet(XtermWidget xw,
2584894e0ac8Smrg	  XEvent *event GCC_UNUSED,
2585e0a2b6dfSmrg	  String *params,
2586d522f475Smrg	  Cardinal num_params)
2587d522f475Smrg{
2588956cc18dSsnj    TScreen *screen = TScreenOf(xw);
2589d522f475Smrg
2590d522f475Smrg    TRACE(("SelectSet\n"));
2591d522f475Smrg    /* Only do select stuff if non-null select */
2592d522f475Smrg    if (!isSameCELL(&(screen->startSel), &(screen->endSel))) {
25930bd37d32Smrg	SaltTextAway(xw, &(screen->startSel), &(screen->endSel));
25940bd37d32Smrg	_OwnSelection(xw, params, num_params);
2595d522f475Smrg    } else {
25960bd37d32Smrg	ScrnDisownSelection(xw);
2597d522f475Smrg    }
2598d522f475Smrg}
2599d522f475Smrg
2600d522f475Smrg#define Abs(x)		((x) < 0 ? -(x) : (x))
2601d522f475Smrg
2602d522f475Smrg/* ARGSUSED */
2603d522f475Smrgstatic void
2604d522f475Smrgdo_start_extend(XtermWidget xw,
2605894e0ac8Smrg		XEvent *event,	/* must be XButtonEvent* */
2606e0a2b6dfSmrg		String *params GCC_UNUSED,
2607d522f475Smrg		Cardinal *num_params GCC_UNUSED,
2608d522f475Smrg		Bool use_cursor_loc)
2609d522f475Smrg{
2610956cc18dSsnj    TScreen *screen = TScreenOf(xw);
2611d522f475Smrg    int coord;
2612d522f475Smrg    CELL cell;
2613d522f475Smrg
2614d522f475Smrg    if (SendMousePosition(xw, event))
2615d522f475Smrg	return;
2616d522f475Smrg
2617d522f475Smrg    screen->firstValidRow = 0;
2618d522f475Smrg    screen->lastValidRow = screen->max_row;
2619d522f475Smrg#if OPT_READLINE
2620492d43a5Smrg    if ((KeyModifiers(event) & ShiftMask)
2621d522f475Smrg	|| event->xbutton.button != Button3
2622d522f475Smrg	|| !(SCREEN_FLAG(screen, dclick3_deletes)))
2623d522f475Smrg#endif
2624956cc18dSsnj	screen->selectUnit = EvalSelectUnit(xw,
2625d522f475Smrg					    event->xbutton.time,
2626d522f475Smrg					    screen->selectUnit,
2627d522f475Smrg					    event->xbutton.button);
2628d522f475Smrg    screen->replyToEmacs = False;
2629d522f475Smrg
2630d522f475Smrg#if OPT_READLINE
2631d522f475Smrg    CheckSecondPress3(screen, event);
2632d522f475Smrg#endif
2633d522f475Smrg
2634d522f475Smrg    if (screen->numberOfClicks == 1
2635d522f475Smrg	|| (SCREEN_FLAG(screen, dclick3_deletes)	/* Dclick special */
2636492d43a5Smrg	    &&!(KeyModifiers(event) & ShiftMask))) {
2637d522f475Smrg	/* Save existing selection so we can reestablish it if the guy
2638d522f475Smrg	   extends past the other end of the selection */
2639d522f475Smrg	screen->saveStartR = screen->startExt = screen->startRaw;
2640d522f475Smrg	screen->saveEndR = screen->endExt = screen->endRaw;
2641d522f475Smrg    } else {
2642d522f475Smrg	/* He just needed the selection mode changed, use old values. */
2643d522f475Smrg	screen->startExt = screen->startRaw = screen->saveStartR;
2644d522f475Smrg	screen->endExt = screen->endRaw = screen->saveEndR;
2645d522f475Smrg    }
2646d522f475Smrg    if (use_cursor_loc) {
2647d522f475Smrg	cell = screen->cursorp;
2648d522f475Smrg    } else {
2649d522f475Smrg	PointToCELL(screen, event->xbutton.y, event->xbutton.x, &cell);
2650d522f475Smrg    }
2651d522f475Smrg    coord = Coordinate(screen, &cell);
2652d522f475Smrg
2653d522f475Smrg    if (Abs(coord - Coordinate(screen, &(screen->startSel)))
2654d522f475Smrg	< Abs(coord - Coordinate(screen, &(screen->endSel)))
2655d522f475Smrg	|| coord < Coordinate(screen, &(screen->startSel))) {
2656d522f475Smrg	/* point is close to left side of selection */
2657d522f475Smrg	screen->eventMode = LEFTEXTENSION;
2658d522f475Smrg	screen->startExt = cell;
2659d522f475Smrg    } else {
2660d522f475Smrg	/* point is close to left side of selection */
2661d522f475Smrg	screen->eventMode = RIGHTEXTENSION;
2662d522f475Smrg	screen->endExt = cell;
2663d522f475Smrg    }
2664d522f475Smrg    ComputeSelect(xw, &(screen->startExt), &(screen->endExt), True);
2665d522f475Smrg
2666d522f475Smrg#if OPT_READLINE
2667d522f475Smrg    if (!isSameCELL(&(screen->startSel), &(screen->endSel)))
2668d522f475Smrg	ExtendingSelection = 1;
2669d522f475Smrg#endif
2670d522f475Smrg}
2671d522f475Smrg
2672d522f475Smrgstatic void
2673e0a2b6dfSmrgExtendExtend(XtermWidget xw, const CELL *cell)
2674d522f475Smrg{
2675956cc18dSsnj    TScreen *screen = TScreenOf(xw);
2676d522f475Smrg    int coord = Coordinate(screen, cell);
2677d522f475Smrg
2678d522f475Smrg    TRACE(("ExtendExtend row=%d, col=%d\n", cell->row, cell->col));
2679d522f475Smrg    if (screen->eventMode == LEFTEXTENSION
2680d522f475Smrg	&& ((coord + (screen->selectUnit != Select_CHAR))
2681d522f475Smrg	    > Coordinate(screen, &(screen->endSel)))) {
2682d522f475Smrg	/* Whoops, he's changed his mind.  Do RIGHTEXTENSION */
2683d522f475Smrg	screen->eventMode = RIGHTEXTENSION;
2684d522f475Smrg	screen->startExt = screen->saveStartR;
2685d522f475Smrg    } else if (screen->eventMode == RIGHTEXTENSION
2686d522f475Smrg	       && coord < Coordinate(screen, &(screen->startSel))) {
2687d522f475Smrg	/* Whoops, he's changed his mind.  Do LEFTEXTENSION */
2688d522f475Smrg	screen->eventMode = LEFTEXTENSION;
2689d522f475Smrg	screen->endExt = screen->saveEndR;
2690d522f475Smrg    }
2691d522f475Smrg    if (screen->eventMode == LEFTEXTENSION) {
2692d522f475Smrg	screen->startExt = *cell;
2693d522f475Smrg    } else {
2694d522f475Smrg	screen->endExt = *cell;
2695d522f475Smrg    }
2696d522f475Smrg    ComputeSelect(xw, &(screen->startExt), &(screen->endExt), False);
2697d522f475Smrg
2698d522f475Smrg#if OPT_READLINE
2699d522f475Smrg    if (!isSameCELL(&(screen->startSel), &(screen->endSel)))
2700d522f475Smrg	ExtendingSelection = 1;
2701d522f475Smrg#endif
2702d522f475Smrg}
2703d522f475Smrg
2704d522f475Smrgvoid
2705d522f475SmrgHandleStartExtend(Widget w,
2706894e0ac8Smrg		  XEvent *event,	/* must be XButtonEvent* */
2707e0a2b6dfSmrg		  String *params,	/* unused */
2708d522f475Smrg		  Cardinal *num_params)		/* unused */
2709d522f475Smrg{
2710956cc18dSsnj    XtermWidget xw;
2711956cc18dSsnj
2712956cc18dSsnj    if ((xw = getXtermWidget(w)) != 0) {
27130bd37d32Smrg	TRACE(("HandleStartExtend\n"));
2714956cc18dSsnj	do_start_extend(xw, event, params, num_params, False);
2715956cc18dSsnj    }
2716d522f475Smrg}
2717d522f475Smrg
2718d522f475Smrgvoid
2719d522f475SmrgHandleKeyboardStartExtend(Widget w,
2720894e0ac8Smrg			  XEvent *event,	/* must be XButtonEvent* */
2721e0a2b6dfSmrg			  String *params,	/* unused */
2722d522f475Smrg			  Cardinal *num_params)		/* unused */
2723d522f475Smrg{
2724956cc18dSsnj    XtermWidget xw;
2725956cc18dSsnj
2726956cc18dSsnj    if ((xw = getXtermWidget(w)) != 0) {
27270bd37d32Smrg	TRACE(("HandleKeyboardStartExtend\n"));
2728956cc18dSsnj	do_start_extend(xw, event, params, num_params, True);
2729956cc18dSsnj    }
2730d522f475Smrg}
2731d522f475Smrg
2732d522f475Smrgvoid
2733e0a2b6dfSmrgScrollSelection(TScreen *screen, int amount, Bool always)
2734d522f475Smrg{
2735d522f475Smrg    int minrow = INX2ROW(screen, -screen->savedlines);
2736d522f475Smrg    int maxrow = INX2ROW(screen, screen->max_row);
2737d522f475Smrg    int maxcol = screen->max_col;
2738d522f475Smrg
2739d522f475Smrg#define scroll_update_one(cell) \
2740d522f475Smrg	(cell)->row += amount; \
2741d522f475Smrg	if ((cell)->row < minrow) { \
2742d522f475Smrg	    (cell)->row = minrow; \
2743d522f475Smrg	    (cell)->col = 0; \
2744d522f475Smrg	} \
2745d522f475Smrg	if ((cell)->row > maxrow) { \
2746d522f475Smrg	    (cell)->row = maxrow; \
2747d522f475Smrg	    (cell)->col = maxcol; \
2748d522f475Smrg	}
2749d522f475Smrg
2750d522f475Smrg    scroll_update_one(&(screen->startRaw));
2751d522f475Smrg    scroll_update_one(&(screen->endRaw));
2752d522f475Smrg    scroll_update_one(&(screen->startSel));
2753d522f475Smrg    scroll_update_one(&(screen->endSel));
2754d522f475Smrg
2755d522f475Smrg    scroll_update_one(&(screen->rawPos));
2756d522f475Smrg
2757d522f475Smrg    /*
2758d522f475Smrg     * If we are told to scroll the selection but it lies outside the scrolling
2759d522f475Smrg     * margins, then that could cause the selection to move (bad).  It is not
2760d522f475Smrg     * simple to fix, because this function is called both for the scrollbar
2761d522f475Smrg     * actions as well as application scrolling.  The 'always' flag is set in
2762d522f475Smrg     * the former case.  The rest of the logic handles the latter.
2763d522f475Smrg     */
2764d522f475Smrg    if (ScrnHaveSelection(screen)) {
2765d522f475Smrg	int adjust;
2766d522f475Smrg
2767d522f475Smrg	adjust = ROW2INX(screen, screen->startH.row);
2768d522f475Smrg	if (always
27690bd37d32Smrg	    || !ScrnHaveRowMargins(screen)
27700bd37d32Smrg	    || ScrnIsRowInMargins(screen, adjust)) {
2771d522f475Smrg	    scroll_update_one(&screen->startH);
2772d522f475Smrg	}
2773d522f475Smrg	adjust = ROW2INX(screen, screen->endH.row);
2774d522f475Smrg	if (always
27750bd37d32Smrg	    || !ScrnHaveRowMargins(screen)
27760bd37d32Smrg	    || ScrnIsRowInMargins(screen, adjust)) {
2777d522f475Smrg	    scroll_update_one(&screen->endH);
2778d522f475Smrg	}
2779d522f475Smrg    }
2780d522f475Smrg
2781d522f475Smrg    screen->startHCoord = Coordinate(screen, &screen->startH);
2782d522f475Smrg    screen->endHCoord = Coordinate(screen, &screen->endH);
2783d522f475Smrg}
2784d522f475Smrg
2785d522f475Smrg/*ARGSUSED*/
2786d522f475Smrgvoid
2787e0a2b6dfSmrgResizeSelection(TScreen *screen GCC_UNUSED, int rows, int cols)
2788d522f475Smrg{
2789d522f475Smrg    rows--;			/* decr to get 0-max */
2790d522f475Smrg    cols--;
2791d522f475Smrg
2792d522f475Smrg    if (screen->startRaw.row > rows)
2793d522f475Smrg	screen->startRaw.row = rows;
2794d522f475Smrg    if (screen->startSel.row > rows)
2795d522f475Smrg	screen->startSel.row = rows;
2796d522f475Smrg    if (screen->endRaw.row > rows)
2797d522f475Smrg	screen->endRaw.row = rows;
2798d522f475Smrg    if (screen->endSel.row > rows)
2799d522f475Smrg	screen->endSel.row = rows;
2800d522f475Smrg    if (screen->rawPos.row > rows)
2801d522f475Smrg	screen->rawPos.row = rows;
2802d522f475Smrg
2803d522f475Smrg    if (screen->startRaw.col > cols)
2804d522f475Smrg	screen->startRaw.col = cols;
2805d522f475Smrg    if (screen->startSel.col > cols)
2806d522f475Smrg	screen->startSel.col = cols;
2807d522f475Smrg    if (screen->endRaw.col > cols)
2808d522f475Smrg	screen->endRaw.col = cols;
2809d522f475Smrg    if (screen->endSel.col > cols)
2810d522f475Smrg	screen->endSel.col = cols;
2811d522f475Smrg    if (screen->rawPos.col > cols)
2812d522f475Smrg	screen->rawPos.col = cols;
2813d522f475Smrg}
2814d522f475Smrg
2815d522f475Smrg#if OPT_WIDE_CHARS
2816d522f475SmrgBool
2817d522f475Smrgiswide(int i)
2818d522f475Smrg{
281920d2c4d2Smrg    return (i == HIDDEN_CHAR) || (WideCells(i) == 2);
2820d522f475Smrg}
2821d522f475Smrg
2822d522f475Smrg#define isWideCell(row, col) iswide((int)XTERM_CELL(row, col))
2823d522f475Smrg#endif
2824d522f475Smrg
2825d522f475Smrgstatic void
2826e0a2b6dfSmrgPointToCELL(TScreen *screen,
2827d522f475Smrg	    int y,
2828d522f475Smrg	    int x,
2829e0a2b6dfSmrg	    CELL *cell)
2830d522f475Smrg/* Convert pixel coordinates to character coordinates.
2831d522f475Smrg   Rows are clipped between firstValidRow and lastValidRow.
2832d522f475Smrg   Columns are clipped between to be 0 or greater, but are not clipped to some
2833d522f475Smrg       maximum value. */
2834d522f475Smrg{
2835d522f475Smrg    cell->row = (y - screen->border) / FontHeight(screen);
2836d522f475Smrg    if (cell->row < screen->firstValidRow)
2837d522f475Smrg	cell->row = screen->firstValidRow;
2838d522f475Smrg    else if (cell->row > screen->lastValidRow)
2839d522f475Smrg	cell->row = screen->lastValidRow;
2840d522f475Smrg    cell->col = (x - OriginX(screen)) / FontWidth(screen);
2841d522f475Smrg    if (cell->col < 0)
2842d522f475Smrg	cell->col = 0;
2843d522f475Smrg    else if (cell->col > MaxCols(screen)) {
2844d522f475Smrg	cell->col = MaxCols(screen);
2845d522f475Smrg    }
2846d522f475Smrg#if OPT_WIDE_CHARS
2847d522f475Smrg    /*
2848d522f475Smrg     * If we got a click on the right half of a doublewidth character,
2849d522f475Smrg     * pretend it happened on the left half.
2850d522f475Smrg     */
2851d522f475Smrg    if (cell->col > 0
2852d522f475Smrg	&& isWideCell(cell->row, cell->col - 1)
2853d522f475Smrg	&& (XTERM_CELL(cell->row, cell->col) == HIDDEN_CHAR)) {
2854d522f475Smrg	cell->col -= 1;
2855d522f475Smrg    }
2856d522f475Smrg#endif
2857d522f475Smrg}
2858d522f475Smrg
2859d522f475Smrg/*
2860d522f475Smrg * Find the last column at which text was drawn on the given row.
2861d522f475Smrg */
2862d522f475Smrgstatic int
286301037d57SmrgLastTextCol(TScreen *screen, CLineData *ld, int row)
2864d522f475Smrg{
286520d2c4d2Smrg    int i = -1;
286601037d57Smrg    const IAttr *ch;
2867d522f475Smrg
286820d2c4d2Smrg    if (ld != 0) {
286920d2c4d2Smrg	if (okScrnRow(screen, row)) {
287020d2c4d2Smrg	    for (i = screen->max_col,
287120d2c4d2Smrg		 ch = ld->attribs + i;
287220d2c4d2Smrg		 i >= 0 && !(*ch & CHARDRAWN);
287320d2c4d2Smrg		 ch--, i--) {
287420d2c4d2Smrg		;
287520d2c4d2Smrg	    }
2876d522f475Smrg#if OPT_DEC_CHRSET
287720d2c4d2Smrg	    if (CSET_DOUBLE(GetLineDblCS(ld))) {
287820d2c4d2Smrg		i *= 2;
287920d2c4d2Smrg	    }
2880d522f475Smrg#endif
288120d2c4d2Smrg	}
2882d522f475Smrg    }
2883d522f475Smrg    return (i);
2884d522f475Smrg}
2885d522f475Smrg
2886d522f475Smrg#if !OPT_WIDE_CHARS
2887d522f475Smrg/*
2888d522f475Smrg** double click table for cut and paste in 8 bits
2889d522f475Smrg**
2890d522f475Smrg** This table is divided in four parts :
2891d522f475Smrg**
2892d522f475Smrg**	- control characters	[0,0x1f] U [0x80,0x9f]
2893d522f475Smrg**	- separators		[0x20,0x3f] U [0xa0,0xb9]
2894d522f475Smrg**	- binding characters	[0x40,0x7f] U [0xc0,0xff]
2895d522f475Smrg**	- exceptions
2896d522f475Smrg*/
2897d522f475Smrg/* *INDENT-OFF* */
2898d522f475Smrgstatic int charClass[256] =
2899d522f475Smrg{
2900d522f475Smrg/* NUL  SOH  STX  ETX  EOT  ENQ  ACK  BEL */
2901d522f475Smrg    32,  1,    1,   1,   1,   1,   1,   1,
29020bd37d32Smrg/*  BS   HT   NL   VT   FF   CR   SO   SI */
2903d522f475Smrg     1,  32,   1,   1,   1,   1,   1,   1,
2904d522f475Smrg/* DLE  DC1  DC2  DC3  DC4  NAK  SYN  ETB */
2905d522f475Smrg     1,   1,   1,   1,   1,   1,   1,   1,
2906d522f475Smrg/* CAN   EM  SUB  ESC   FS   GS   RS   US */
2907d522f475Smrg     1,   1,   1,   1,   1,   1,   1,   1,
2908d522f475Smrg/*  SP    !    "    #    $    %    &    ' */
2909d522f475Smrg    32,  33,  34,  35,  36,  37,  38,  39,
2910d522f475Smrg/*   (    )    *    +    ,    -    .    / */
2911d522f475Smrg    40,  41,  42,  43,  44,  45,  46,  47,
2912d522f475Smrg/*   0    1    2    3    4    5    6    7 */
2913d522f475Smrg    48,  48,  48,  48,  48,  48,  48,  48,
2914d522f475Smrg/*   8    9    :    ;    <    =    >    ? */
2915d522f475Smrg    48,  48,  58,  59,  60,  61,  62,  63,
2916d522f475Smrg/*   @    A    B    C    D    E    F    G */
2917d522f475Smrg    64,  48,  48,  48,  48,  48,  48,  48,
2918d522f475Smrg/*   H    I    J    K    L    M    N    O */
2919d522f475Smrg    48,  48,  48,  48,  48,  48,  48,  48,
2920d522f475Smrg/*   P    Q    R    S    T    U    V    W */
2921d522f475Smrg    48,  48,  48,  48,  48,  48,  48,  48,
2922d522f475Smrg/*   X    Y    Z    [    \    ]    ^    _ */
2923d522f475Smrg    48,  48,  48,  91,  92,  93,  94,  48,
2924d522f475Smrg/*   `    a    b    c    d    e    f    g */
2925d522f475Smrg    96,  48,  48,  48,  48,  48,  48,  48,
2926d522f475Smrg/*   h    i    j    k    l    m    n    o */
2927d522f475Smrg    48,  48,  48,  48,  48,  48,  48,  48,
2928d522f475Smrg/*   p    q    r    s    t    u    v    w */
2929d522f475Smrg    48,  48,  48,  48,  48,  48,  48,  48,
2930d522f475Smrg/*   x    y    z    {    |    }    ~  DEL */
2931d522f475Smrg    48,  48,  48, 123, 124, 125, 126,   1,
2932d522f475Smrg/* x80  x81  x82  x83  IND  NEL  SSA  ESA */
2933d522f475Smrg    1,    1,   1,   1,   1,   1,   1,   1,
2934d522f475Smrg/* HTS  HTJ  VTS  PLD  PLU   RI  SS2  SS3 */
2935d522f475Smrg    1,    1,   1,   1,   1,   1,   1,   1,
2936d522f475Smrg/* DCS  PU1  PU2  STS  CCH   MW  SPA  EPA */
2937d522f475Smrg    1,    1,   1,   1,   1,   1,   1,   1,
2938d522f475Smrg/* x98  x99  x9A  CSI   ST  OSC   PM  APC */
2939d522f475Smrg    1,    1,   1,   1,   1,   1,   1,   1,
2940d522f475Smrg/*   -    i   c/    L   ox   Y-    |   So */
2941d522f475Smrg    160, 161, 162, 163, 164, 165, 166, 167,
2942d522f475Smrg/*  ..   c0   ip   <<    _        R0    - */
2943d522f475Smrg    168, 169, 170, 171, 172, 173, 174, 175,
2944d522f475Smrg/*   o   +-    2    3    '    u   q|    . */
2945d522f475Smrg    176, 177, 178, 179, 180, 181, 182, 183,
2946d522f475Smrg/*   ,    1    2   >>  1/4  1/2  3/4    ? */
2947d522f475Smrg    184, 185, 186, 187, 188, 189, 190, 191,
2948d522f475Smrg/*  A`   A'   A^   A~   A:   Ao   AE   C, */
2949d522f475Smrg     48,  48,  48,  48,  48,  48,  48,  48,
2950d522f475Smrg/*  E`   E'   E^   E:   I`   I'   I^   I: */
2951d522f475Smrg     48,  48,  48,  48,  48,  48,  48,  48,
2952d522f475Smrg/*  D-   N~   O`   O'   O^   O~   O:    X */
2953d522f475Smrg     48,  48,  48,  48,  48,  48,  48, 215,
2954d522f475Smrg/*  O/   U`   U'   U^   U:   Y'    P    B */
2955d522f475Smrg     48,  48,  48,  48,  48,  48,  48,  48,
2956d522f475Smrg/*  a`   a'   a^   a~   a:   ao   ae   c, */
2957d522f475Smrg     48,  48,  48,  48,  48,  48,  48,  48,
2958d522f475Smrg/*  e`   e'   e^   e:    i`  i'   i^   i: */
2959d522f475Smrg     48,  48,  48,  48,  48,  48,  48,  48,
2960d522f475Smrg/*   d   n~   o`   o'   o^   o~   o:   -: */
2961d522f475Smrg     48,  48,  48,  48,  48,  48,  48, 247,
2962d522f475Smrg/*  o/   u`   u'   u^   u:   y'    P   y: */
2963d522f475Smrg     48,  48,  48,  48,  48,  48,  48,  48};
2964d522f475Smrg/* *INDENT-ON* */
2965d522f475Smrg
2966d522f475Smrgint
2967d522f475SmrgSetCharacterClassRange(int low,	/* in range of [0..255] */
2968d522f475Smrg		       int high,
2969d522f475Smrg		       int value)	/* arbitrary */
2970d522f475Smrg{
2971d522f475Smrg
2972d522f475Smrg    if (low < 0 || high > 255 || high < low)
2973d522f475Smrg	return (-1);
2974d522f475Smrg
2975d522f475Smrg    for (; low <= high; low++)
2976d522f475Smrg	charClass[low] = value;
2977d522f475Smrg
2978d522f475Smrg    return (0);
2979d522f475Smrg}
2980d522f475Smrg#endif
2981d522f475Smrg
2982d522f475Smrgstatic int
2983e0a2b6dfSmrgclass_of(LineData *ld, CELL *cell)
2984d522f475Smrg{
2985d522f475Smrg    CELL temp = *cell;
29860bd37d32Smrg    int result = 0;
2987d522f475Smrg
2988d522f475Smrg#if OPT_DEC_CHRSET
2989956cc18dSsnj    if (CSET_DOUBLE(GetLineDblCS(ld))) {
2990d522f475Smrg	temp.col /= 2;
2991d522f475Smrg    }
2992d522f475Smrg#endif
29930bd37d32Smrg    if (temp.col < (int) ld->lineSize)
29940bd37d32Smrg	result = CharacterClass((int) (ld->charData[temp.col]));
29950bd37d32Smrg    return result;
2996d522f475Smrg}
2997956cc18dSsnj
2998956cc18dSsnj#if OPT_WIDE_CHARS
2999956cc18dSsnj#define CClassSelects(name, cclass) \
3000956cc18dSsnj	 (CClassOf(name) == cclass \
3001956cc18dSsnj	 || XTERM_CELL(screen->name.row, screen->name.col) == HIDDEN_CHAR)
3002d522f475Smrg#else
3003956cc18dSsnj#define CClassSelects(name, cclass) \
3004956cc18dSsnj	 (class_of(ld.name, &((screen->name))) == cclass)
3005d522f475Smrg#endif
3006d522f475Smrg
3007956cc18dSsnj#define CClassOf(name) class_of(ld.name, &((screen->name)))
3008956cc18dSsnj
3009d522f475Smrg/*
3010d522f475Smrg * If the given column is past the end of text on the given row, bump to the
3011d522f475Smrg * beginning of the next line.
3012d522f475Smrg */
3013d522f475Smrgstatic Boolean
3014e0a2b6dfSmrgokPosition(TScreen *screen,
3015e0a2b6dfSmrg	   LineData **ld,
3016e0a2b6dfSmrg	   CELL *cell)
3017d522f475Smrg{
301820d2c4d2Smrg    Boolean result = True;
301920d2c4d2Smrg
302020d2c4d2Smrg    if (cell->row > screen->max_row) {
302120d2c4d2Smrg	result = False;
302220d2c4d2Smrg    } else if (cell->col > (LastTextCol(screen, *ld, cell->row) + 1)) {
302320d2c4d2Smrg	if (cell->row < screen->max_row) {
302420d2c4d2Smrg	    cell->col = 0;
302520d2c4d2Smrg	    *ld = GET_LINEDATA(screen, ++cell->row);
302620d2c4d2Smrg	    result = False;
302720d2c4d2Smrg	}
3028d522f475Smrg    }
302920d2c4d2Smrg    return result;
3030d522f475Smrg}
3031d522f475Smrg
3032d522f475Smrgstatic void
3033e0a2b6dfSmrgtrimLastLine(TScreen *screen,
3034e0a2b6dfSmrg	     LineData **ld,
3035e0a2b6dfSmrg	     CELL *last)
3036d522f475Smrg{
303720d2c4d2Smrg    if (screen->cutNewline && last->row < screen->max_row) {
3038d522f475Smrg	last->col = 0;
3039956cc18dSsnj	*ld = GET_LINEDATA(screen, ++last->row);
3040d522f475Smrg    } else {
3041956cc18dSsnj	last->col = LastTextCol(screen, *ld, last->row) + 1;
3042d522f475Smrg    }
3043d522f475Smrg}
3044d522f475Smrg
3045d522f475Smrg#if OPT_SELECT_REGEX
3046d522f475Smrg/*
3047d522f475Smrg * Returns the first row of a wrapped line.
3048d522f475Smrg */
3049d522f475Smrgstatic int
3050e0a2b6dfSmrgfirstRowOfLine(TScreen *screen, int row, Bool visible)
3051d522f475Smrg{
3052956cc18dSsnj    LineData *ld = 0;
3053d522f475Smrg    int limit = visible ? 0 : -screen->savedlines;
3054d522f475Smrg
3055d522f475Smrg    while (row > limit &&
3056956cc18dSsnj	   (ld = GET_LINEDATA(screen, row - 1)) != 0 &&
3057956cc18dSsnj	   LineTstWrapped(ld)) {
3058d522f475Smrg	--row;
3059956cc18dSsnj    }
3060d522f475Smrg    return row;
3061d522f475Smrg}
3062d522f475Smrg
3063d522f475Smrg/*
3064d522f475Smrg * Returns the last row of a wrapped line.
3065d522f475Smrg */
3066d522f475Smrgstatic int
3067e0a2b6dfSmrglastRowOfLine(TScreen *screen, int row)
3068d522f475Smrg{
3069956cc18dSsnj    LineData *ld;
3070956cc18dSsnj
3071d522f475Smrg    while (row < screen->max_row &&
3072956cc18dSsnj	   (ld = GET_LINEDATA(screen, row)) != 0 &&
3073956cc18dSsnj	   LineTstWrapped(ld)) {
3074d522f475Smrg	++row;
3075956cc18dSsnj    }
3076d522f475Smrg    return row;
3077d522f475Smrg}
3078d522f475Smrg
3079d522f475Smrg/*
3080d522f475Smrg * Returns the number of cells on the range of rows.
3081d522f475Smrg */
3082d522f475Smrgstatic unsigned
3083e0a2b6dfSmrglengthOfLines(TScreen *screen, int firstRow, int lastRow)
3084d522f475Smrg{
3085d522f475Smrg    unsigned length = 0;
3086d522f475Smrg    int n;
3087d522f475Smrg
3088d522f475Smrg    for (n = firstRow; n <= lastRow; ++n) {
3089956cc18dSsnj	LineData *ld = GET_LINEDATA(screen, n);
3090956cc18dSsnj	int value = LastTextCol(screen, ld, n);
3091d522f475Smrg	if (value >= 0)
30922eaa94a1Schristos	    length += (unsigned) (value + 1);
3093d522f475Smrg    }
3094d522f475Smrg    return length;
3095d522f475Smrg}
3096d522f475Smrg
3097d522f475Smrg/*
3098d522f475Smrg * Make a copy of the wrapped-line which corresponds to the given row as a
3099d522f475Smrg * string of bytes.  Construct an index for the columns from the beginning of
3100d522f475Smrg * the line.
3101d522f475Smrg */
3102d522f475Smrgstatic char *
3103e0a2b6dfSmrgmake_indexed_text(TScreen *screen, int row, unsigned length, int *indexed)
3104d522f475Smrg{
3105d522f475Smrg    Char *result = 0;
310620d2c4d2Smrg    size_t need = (length + 1);
3107d522f475Smrg
3108d522f475Smrg    /*
3109d522f475Smrg     * Get a quick upper bound to the number of bytes needed, if the whole
3110d522f475Smrg     * string were UTF-8.
3111d522f475Smrg     */
3112d522f475Smrg    if_OPT_WIDE_CHARS(screen, {
3113956cc18dSsnj	need *= ((screen->lineExtra + 1) * 6);
3114d522f475Smrg    });
3115d522f475Smrg
3116d522f475Smrg    if ((result = TypeCallocN(Char, need + 1)) != 0) {
3117956cc18dSsnj	LineData *ld = GET_LINEDATA(screen, row);
3118d522f475Smrg	unsigned used = 0;
3119d522f475Smrg	Char *last = result;
3120d522f475Smrg
3121d522f475Smrg	do {
3122d522f475Smrg	    int col = 0;
3123956cc18dSsnj	    int limit = LastTextCol(screen, ld, row);
3124d522f475Smrg
3125d522f475Smrg	    while (col <= limit) {
3126d522f475Smrg		Char *next = last;
3127956cc18dSsnj		unsigned data = ld->charData[col];
3128d522f475Smrg
31290bd37d32Smrg		assert(col < (int) ld->lineSize);
3130d522f475Smrg		/* some internal points may not be drawn */
3131d522f475Smrg		if (data == 0)
3132d522f475Smrg		    data = ' ';
3133d522f475Smrg
3134d522f475Smrg		if_WIDE_OR_NARROW(screen, {
3135d522f475Smrg		    next = convertToUTF8(last, data);
3136d522f475Smrg		}
3137d522f475Smrg		, {
3138d522f475Smrg		    *next++ = CharOf(data);
3139d522f475Smrg		});
3140d522f475Smrg
3141d522f475Smrg		if_OPT_WIDE_CHARS(screen, {
3142956cc18dSsnj		    size_t off;
3143956cc18dSsnj		    for_each_combData(off, ld) {
3144956cc18dSsnj			data = ld->combData[off][col];
3145956cc18dSsnj			if (data == 0)
3146d522f475Smrg			    break;
3147d522f475Smrg			next = convertToUTF8(next, data);
3148d522f475Smrg		    }
3149d522f475Smrg		});
3150d522f475Smrg
315120d2c4d2Smrg		indexed[used] = (int) (last - result);
3152d522f475Smrg		*next = 0;
3153d522f475Smrg		/* TRACE(("index[%d.%d] %d:%s\n", row, used, indexed[used], last)); */
3154d522f475Smrg		last = next;
3155d522f475Smrg		++used;
3156d522f475Smrg		++col;
315720d2c4d2Smrg		indexed[used] = (int) (next - result);
3158d522f475Smrg	    }
3159d522f475Smrg	} while (used < length &&
3160956cc18dSsnj		 LineTstWrapped(ld) &&
3161956cc18dSsnj		 (ld = GET_LINEDATA(screen, ++row)) != 0 &&
3162956cc18dSsnj		 row < screen->max_row);
3163d522f475Smrg    }
3164d522f475Smrg    /* TRACE(("result:%s\n", result)); */
3165d522f475Smrg    return (char *) result;
3166d522f475Smrg}
3167d522f475Smrg
3168d522f475Smrg/*
3169d522f475Smrg * Find the column given an offset into the character string by using the
3170d522f475Smrg * index constructed in make_indexed_text().
3171d522f475Smrg */
3172d522f475Smrgstatic int
3173d522f475SmrgindexToCol(int *indexed, int len, int off)
3174d522f475Smrg{
3175d522f475Smrg    int col = 0;
3176d522f475Smrg    while (indexed[col] < len) {
3177d522f475Smrg	if (indexed[col] >= off)
3178d522f475Smrg	    break;
3179d522f475Smrg	++col;
3180d522f475Smrg    }
3181d522f475Smrg    return col;
3182d522f475Smrg}
3183d522f475Smrg
3184d522f475Smrg/*
3185d522f475Smrg * Given a row number, and a column offset from that (which may be wrapped),
3186d522f475Smrg * set the cell to the actual row/column values.
3187d522f475Smrg */
3188d522f475Smrgstatic void
3189e0a2b6dfSmrgcolumnToCell(TScreen *screen, int row, int col, CELL *cell)
3190d522f475Smrg{
3191d522f475Smrg    while (row < screen->max_row) {
319201037d57Smrg	CLineData *ld = GET_LINEDATA(screen, row);
3193956cc18dSsnj	int last = LastTextCol(screen, ld, row);
3194d522f475Smrg
3195d522f475Smrg	/* TRACE(("last(%d) = %d, have %d\n", row, last, col)); */
3196d522f475Smrg	if (col <= last) {
3197d522f475Smrg	    break;
3198d522f475Smrg	}
3199d522f475Smrg	/*
3200d522f475Smrg	 * Stop if the current row does not wrap (does not continue the current
3201d522f475Smrg	 * line).
3202d522f475Smrg	 */
3203956cc18dSsnj	if (!LineTstWrapped(ld)) {
3204d522f475Smrg	    col = last + 1;
3205d522f475Smrg	    break;
3206d522f475Smrg	}
3207d522f475Smrg	col -= (last + 1);
3208d522f475Smrg	++row;
3209d522f475Smrg    }
3210d522f475Smrg    if (col < 0)
3211d522f475Smrg	col = 0;
3212d522f475Smrg    cell->row = row;
3213d522f475Smrg    cell->col = col;
3214d522f475Smrg}
3215d522f475Smrg
3216d522f475Smrg/*
3217d522f475Smrg * Given a cell, find the corresponding column offset.
3218d522f475Smrg */
3219d522f475Smrgstatic int
3220e0a2b6dfSmrgcellToColumn(TScreen *screen, CELL *cell)
3221d522f475Smrg{
322201037d57Smrg    CLineData *ld = 0;
3223d522f475Smrg    int col = cell->col;
3224d522f475Smrg    int row = firstRowOfLine(screen, cell->row, False);
3225d522f475Smrg    while (row < cell->row) {
3226956cc18dSsnj	ld = GET_LINEDATA(screen, row);
3227956cc18dSsnj	col += LastTextCol(screen, ld, row++);
3228d522f475Smrg    }
3229956cc18dSsnj#if OPT_DEC_CHRSET
3230956cc18dSsnj    if (ld == 0)
3231956cc18dSsnj	ld = GET_LINEDATA(screen, row);
3232956cc18dSsnj    if (CSET_DOUBLE(GetLineDblCS(ld)))
3233956cc18dSsnj	col /= 2;
3234956cc18dSsnj#endif
3235d522f475Smrg    return col;
3236d522f475Smrg}
3237d522f475Smrg
3238d522f475Smrgstatic void
3239e0a2b6dfSmrgdo_select_regex(TScreen *screen, CELL *startc, CELL *endc)
3240d522f475Smrg{
3241956cc18dSsnj    LineData *ld = GET_LINEDATA(screen, startc->row);
3242d522f475Smrg    int inx = ((screen->numberOfClicks - 1) % screen->maxClicks);
3243d522f475Smrg    char *expr = screen->selectExpr[inx];
3244d522f475Smrg    regex_t preg;
3245d522f475Smrg    regmatch_t match;
3246d522f475Smrg    char *search;
3247d522f475Smrg    int *indexed;
3248d522f475Smrg
324901037d57Smrg    TRACE(("Select_REGEX[%d]:%s\n", inx, NonNull(expr)));
3250956cc18dSsnj    if (okPosition(screen, &ld, startc) && expr != 0) {
3251d522f475Smrg	if (regcomp(&preg, expr, REG_EXTENDED) == 0) {
3252d522f475Smrg	    int firstRow = firstRowOfLine(screen, startc->row, True);
3253d522f475Smrg	    int lastRow = lastRowOfLine(screen, firstRow);
3254d522f475Smrg	    unsigned size = lengthOfLines(screen, firstRow, lastRow);
3255d522f475Smrg	    int actual = cellToColumn(screen, startc);
3256d522f475Smrg
3257d522f475Smrg	    TRACE(("regcomp ok rows %d..%d bytes %d\n",
3258d522f475Smrg		   firstRow, lastRow, size));
3259d522f475Smrg
3260d522f475Smrg	    if ((indexed = TypeCallocN(int, size + 1)) != 0) {
3261d522f475Smrg		if ((search = make_indexed_text(screen,
3262d522f475Smrg						firstRow,
3263d522f475Smrg						size,
3264d522f475Smrg						indexed)) != 0) {
32652eaa94a1Schristos		    int len = (int) strlen(search);
3266d522f475Smrg		    int col;
3267d522f475Smrg		    int best_col = -1;
3268d522f475Smrg		    int best_len = -1;
3269d522f475Smrg
3270d522f475Smrg		    for (col = 0; indexed[col] < len; ++col) {
3271d522f475Smrg			if (regexec(&preg,
3272d522f475Smrg				    search + indexed[col],
327320d2c4d2Smrg				    (size_t) 1, &match, 0) == 0) {
3274894e0ac8Smrg			    int start_inx = (int) (match.rm_so + indexed[col]);
3275894e0ac8Smrg			    int finis_inx = (int) (match.rm_eo + indexed[col]);
3276d522f475Smrg			    int start_col = indexToCol(indexed, len, start_inx);
3277d522f475Smrg			    int finis_col = indexToCol(indexed, len, finis_inx);
3278d522f475Smrg
3279d522f475Smrg			    if (start_col <= actual &&
3280d522f475Smrg				actual < finis_col) {
3281d522f475Smrg				int test = finis_col - start_col;
3282d522f475Smrg				if (best_len < test) {
3283d522f475Smrg				    best_len = test;
3284d522f475Smrg				    best_col = start_col;
3285d522f475Smrg				    TRACE(("match column %d len %d\n",
3286d522f475Smrg					   best_col,
3287d522f475Smrg					   best_len));
3288d522f475Smrg				}
3289d522f475Smrg			    }
3290d522f475Smrg			}
3291d522f475Smrg		    }
3292d522f475Smrg		    if (best_col >= 0) {
3293d522f475Smrg			int best_nxt = best_col + best_len;
3294d522f475Smrg			columnToCell(screen, firstRow, best_col, startc);
3295d522f475Smrg			columnToCell(screen, firstRow, best_nxt, endc);
3296d522f475Smrg			TRACE(("search::%s\n", search));
3297d522f475Smrg			TRACE(("indexed:%d..%d -> %d..%d\n",
3298d522f475Smrg			       best_col, best_nxt,
3299d522f475Smrg			       indexed[best_col],
3300d522f475Smrg			       indexed[best_nxt]));
3301d522f475Smrg			TRACE(("matched:%d:%s\n",
3302d522f475Smrg			       indexed[best_nxt] + 1 -
3303d522f475Smrg			       indexed[best_col],
3304956cc18dSsnj			       visibleChars((Char *) (search + indexed[best_col]),
3305d522f475Smrg					    (unsigned) (indexed[best_nxt] +
3306d522f475Smrg							1 -
3307d522f475Smrg							indexed[best_col]))));
3308d522f475Smrg		    }
3309d522f475Smrg		    free(search);
3310d522f475Smrg		}
3311d522f475Smrg		free(indexed);
3312956cc18dSsnj#if OPT_DEC_CHRSET
3313956cc18dSsnj		if ((ld = GET_LINEDATA(screen, startc->row)) != 0) {
3314956cc18dSsnj		    if (CSET_DOUBLE(GetLineDblCS(ld)))
3315956cc18dSsnj			startc->col *= 2;
3316956cc18dSsnj		}
3317956cc18dSsnj		if ((ld = GET_LINEDATA(screen, endc->row)) != 0) {
3318956cc18dSsnj		    if (CSET_DOUBLE(GetLineDblCS(ld)))
3319956cc18dSsnj			endc->col *= 2;
3320956cc18dSsnj		}
3321956cc18dSsnj#endif
3322d522f475Smrg	    }
3323d522f475Smrg	    regfree(&preg);
3324d522f475Smrg	}
3325d522f475Smrg    }
3326d522f475Smrg}
3327d522f475Smrg#endif /* OPT_SELECT_REGEX */
3328d522f475Smrg
3329956cc18dSsnj#define InitRow(name) \
3330956cc18dSsnj	ld.name = GET_LINEDATA(screen, screen->name.row)
3331956cc18dSsnj
3332956cc18dSsnj#define NextRow(name) \
3333956cc18dSsnj	ld.name = GET_LINEDATA(screen, ++screen->name.row)
3334956cc18dSsnj
3335956cc18dSsnj#define PrevRow(name) \
3336956cc18dSsnj	ld.name = GET_LINEDATA(screen, --screen->name.row)
3337956cc18dSsnj
333820d2c4d2Smrg#define MoreRows(name) \
333920d2c4d2Smrg	(screen->name.row < screen->max_row)
334020d2c4d2Smrg
3341956cc18dSsnj#define isPrevWrapped(name) \
3342956cc18dSsnj	(screen->name.row > 0 \
3343956cc18dSsnj	   && (ltmp = GET_LINEDATA(screen, screen->name.row - 1)) != 0 \
3344956cc18dSsnj	   && LineTstWrapped(ltmp))
3345956cc18dSsnj
3346d522f475Smrg/*
3347d522f475Smrg * sets startSel endSel
3348d522f475Smrg * ensuring that they have legal values
3349d522f475Smrg */
3350d522f475Smrgstatic void
3351d522f475SmrgComputeSelect(XtermWidget xw,
3352e0a2b6dfSmrg	      CELL *startc,
3353e0a2b6dfSmrg	      CELL *endc,
3354d522f475Smrg	      Bool extend)
3355d522f475Smrg{
3356956cc18dSsnj    TScreen *screen = TScreenOf(xw);
3357956cc18dSsnj
3358d522f475Smrg    int length;
3359d522f475Smrg    int cclass;
3360d522f475Smrg    CELL first = *startc;
3361d522f475Smrg    CELL last = *endc;
3362956cc18dSsnj    Boolean ignored = False;
3363956cc18dSsnj
3364956cc18dSsnj    struct {
3365956cc18dSsnj	LineData *startSel;
3366956cc18dSsnj	LineData *endSel;
3367956cc18dSsnj    } ld;
3368956cc18dSsnj    LineData *ltmp;
3369d522f475Smrg
3370d522f475Smrg    TRACE(("ComputeSelect(startRow=%d, startCol=%d, endRow=%d, endCol=%d, %sextend)\n",
3371d522f475Smrg	   first.row, first.col,
3372d522f475Smrg	   last.row, last.col,
3373d522f475Smrg	   extend ? "" : "no"));
3374d522f475Smrg
3375d522f475Smrg#if OPT_WIDE_CHARS
3376d522f475Smrg    if (first.col > 1
3377d522f475Smrg	&& isWideCell(first.row, first.col - 1)
3378d522f475Smrg	&& XTERM_CELL(first.row, first.col - 0) == HIDDEN_CHAR) {
337920d2c4d2Smrg	TRACE(("Adjusting start. Changing downwards from %i.\n", first.col));
3380d522f475Smrg	first.col -= 1;
3381d522f475Smrg	if (last.col == (first.col + 1))
3382d522f475Smrg	    last.col--;
3383d522f475Smrg    }
3384d522f475Smrg
3385d522f475Smrg    if (last.col > 1
3386d522f475Smrg	&& isWideCell(last.row, last.col - 1)
3387d522f475Smrg	&& XTERM_CELL(last.row, last.col) == HIDDEN_CHAR) {
3388d522f475Smrg	last.col += 1;
3389d522f475Smrg    }
3390d522f475Smrg#endif
3391d522f475Smrg
3392d522f475Smrg    if (Coordinate(screen, &first) <= Coordinate(screen, &last)) {
3393d522f475Smrg	screen->startSel = screen->startRaw = first;
3394d522f475Smrg	screen->endSel = screen->endRaw = last;
3395d522f475Smrg    } else {			/* Swap them */
3396d522f475Smrg	screen->startSel = screen->startRaw = last;
3397d522f475Smrg	screen->endSel = screen->endRaw = first;
3398d522f475Smrg    }
3399d522f475Smrg
3400956cc18dSsnj    InitRow(startSel);
3401956cc18dSsnj    InitRow(endSel);
3402956cc18dSsnj
3403d522f475Smrg    switch (screen->selectUnit) {
3404d522f475Smrg    case Select_CHAR:
3405956cc18dSsnj	(void) okPosition(screen, &(ld.startSel), &(screen->startSel));
3406956cc18dSsnj	(void) okPosition(screen, &(ld.endSel), &(screen->endSel));
3407d522f475Smrg	break;
3408d522f475Smrg
3409d522f475Smrg    case Select_WORD:
3410d522f475Smrg	TRACE(("Select_WORD\n"));
3411956cc18dSsnj	if (okPosition(screen, &(ld.startSel), &(screen->startSel))) {
3412956cc18dSsnj	    cclass = CClassOf(startSel);
3413d522f475Smrg	    do {
3414d522f475Smrg		--screen->startSel.col;
3415956cc18dSsnj		if (screen->startSel.col < 0
3416956cc18dSsnj		    && isPrevWrapped(startSel)) {
3417956cc18dSsnj		    PrevRow(startSel);
3418956cc18dSsnj		    screen->startSel.col = LastTextCol(screen, ld.startSel, screen->startSel.row);
3419d522f475Smrg		}
3420d522f475Smrg	    } while (screen->startSel.col >= 0
3421956cc18dSsnj		     && CClassSelects(startSel, cclass));
3422d522f475Smrg	    ++screen->startSel.col;
3423d522f475Smrg	}
3424d522f475Smrg#if OPT_WIDE_CHARS
3425d522f475Smrg	if (screen->startSel.col
3426d522f475Smrg	    && XTERM_CELL(screen->startSel.row,
3427d522f475Smrg			  screen->startSel.col) == HIDDEN_CHAR)
3428d522f475Smrg	    screen->startSel.col++;
3429d522f475Smrg#endif
3430d522f475Smrg
3431956cc18dSsnj	if (okPosition(screen, &(ld.endSel), &(screen->endSel))) {
3432956cc18dSsnj	    length = LastTextCol(screen, ld.endSel, screen->endSel.row);
3433956cc18dSsnj	    cclass = CClassOf(endSel);
3434d522f475Smrg	    do {
3435d522f475Smrg		++screen->endSel.col;
3436d522f475Smrg		if (screen->endSel.col > length
3437956cc18dSsnj		    && LineTstWrapped(ld.endSel)) {
343820d2c4d2Smrg		    if (!MoreRows(endSel))
343920d2c4d2Smrg			break;
3440d522f475Smrg		    screen->endSel.col = 0;
3441956cc18dSsnj		    NextRow(endSel);
3442956cc18dSsnj		    length = LastTextCol(screen, ld.endSel, screen->endSel.row);
3443d522f475Smrg		}
3444d522f475Smrg	    } while (screen->endSel.col <= length
3445956cc18dSsnj		     && CClassSelects(endSel, cclass));
3446d522f475Smrg	    /* Word-select selects if pointing to any char in "word",
3447d522f475Smrg	     * especially note that it includes the last character in a word.
3448d522f475Smrg	     * So we do no --endSel.col and do special eol handling.
3449d522f475Smrg	     */
345020d2c4d2Smrg	    if (screen->endSel.col > length + 1
345120d2c4d2Smrg		&& MoreRows(endSel)) {
3452d522f475Smrg		screen->endSel.col = 0;
3453956cc18dSsnj		NextRow(endSel);
3454d522f475Smrg	    }
3455d522f475Smrg	}
3456d522f475Smrg#if OPT_WIDE_CHARS
3457d522f475Smrg	if (screen->endSel.col
3458d522f475Smrg	    && XTERM_CELL(screen->endSel.row,
3459d522f475Smrg			  screen->endSel.col) == HIDDEN_CHAR)
3460d522f475Smrg	    screen->endSel.col++;
3461d522f475Smrg#endif
3462d522f475Smrg
3463d522f475Smrg	screen->saveStartW = screen->startSel;
3464d522f475Smrg	break;
3465d522f475Smrg
3466d522f475Smrg    case Select_LINE:
3467d522f475Smrg	TRACE(("Select_LINE\n"));
346820d2c4d2Smrg	while (LineTstWrapped(ld.endSel)
346920d2c4d2Smrg	       && MoreRows(endSel)) {
3470956cc18dSsnj	    NextRow(endSel);
3471d522f475Smrg	}
3472d522f475Smrg	if (screen->cutToBeginningOfLine
3473d522f475Smrg	    || screen->startSel.row < screen->saveStartW.row) {
3474d522f475Smrg	    screen->startSel.col = 0;
3475956cc18dSsnj	    while (isPrevWrapped(startSel)) {
3476956cc18dSsnj		PrevRow(startSel);
3477d522f475Smrg	    }
3478d522f475Smrg	} else if (!extend) {
3479d522f475Smrg	    if ((first.row < screen->saveStartW.row)
3480d522f475Smrg		|| (isSameRow(&first, &(screen->saveStartW))
3481d522f475Smrg		    && first.col < screen->saveStartW.col)) {
3482d522f475Smrg		screen->startSel.col = 0;
3483956cc18dSsnj		while (isPrevWrapped(startSel)) {
3484956cc18dSsnj		    PrevRow(startSel);
3485d522f475Smrg		}
3486d522f475Smrg	    } else {
3487d522f475Smrg		screen->startSel = screen->saveStartW;
3488d522f475Smrg	    }
3489d522f475Smrg	}
3490956cc18dSsnj	trimLastLine(screen, &(ld.endSel), &(screen->endSel));
3491d522f475Smrg	break;
3492d522f475Smrg
3493d522f475Smrg    case Select_GROUP:		/* paragraph */
3494d522f475Smrg	TRACE(("Select_GROUP\n"));
3495956cc18dSsnj	if (okPosition(screen, &(ld.startSel), &(screen->startSel))) {
3496d522f475Smrg	    /* scan backward for beginning of group */
3497d522f475Smrg	    while (screen->startSel.row > 0 &&
3498956cc18dSsnj		   (LastTextCol(screen, ld.startSel, screen->startSel.row -
3499956cc18dSsnj				1) > 0 ||
3500956cc18dSsnj		    isPrevWrapped(startSel))) {
3501956cc18dSsnj		PrevRow(startSel);
3502d522f475Smrg	    }
3503d522f475Smrg	    screen->startSel.col = 0;
3504d522f475Smrg	    /* scan forward for end of group */
350520d2c4d2Smrg	    while (MoreRows(endSel) &&
3506956cc18dSsnj		   (LastTextCol(screen, ld.endSel, screen->endSel.row + 1) >
3507956cc18dSsnj		    0 ||
3508956cc18dSsnj		    LineTstWrapped(ld.endSel))) {
3509956cc18dSsnj		NextRow(endSel);
3510d522f475Smrg	    }
3511956cc18dSsnj	    trimLastLine(screen, &(ld.endSel), &(screen->endSel));
3512d522f475Smrg	}
3513d522f475Smrg	break;
3514d522f475Smrg
3515d522f475Smrg    case Select_PAGE:		/* everything one can see */
3516d522f475Smrg	TRACE(("Select_PAGE\n"));
3517d522f475Smrg	screen->startSel.row = 0;
3518d522f475Smrg	screen->startSel.col = 0;
351920d2c4d2Smrg	screen->endSel.row = MaxRows(screen);
3520d522f475Smrg	screen->endSel.col = 0;
3521d522f475Smrg	break;
3522d522f475Smrg
3523d522f475Smrg    case Select_ALL:		/* counts scrollback if in normal screen */
3524d522f475Smrg	TRACE(("Select_ALL\n"));
3525d522f475Smrg	screen->startSel.row = -screen->savedlines;
3526d522f475Smrg	screen->startSel.col = 0;
352720d2c4d2Smrg	screen->endSel.row = MaxRows(screen);
3528d522f475Smrg	screen->endSel.col = 0;
3529d522f475Smrg	break;
3530d522f475Smrg
3531d522f475Smrg#if OPT_SELECT_REGEX
3532d522f475Smrg    case Select_REGEX:
3533d522f475Smrg	do_select_regex(screen, &(screen->startSel), &(screen->endSel));
3534d522f475Smrg	break;
3535d522f475Smrg#endif
3536d522f475Smrg
3537d522f475Smrg    case NSELECTUNITS:		/* always ignore */
3538956cc18dSsnj	ignored = True;
3539956cc18dSsnj	break;
3540d522f475Smrg    }
3541d522f475Smrg
3542956cc18dSsnj    if (!ignored) {
3543956cc18dSsnj	/* check boundaries */
3544956cc18dSsnj	ScrollSelection(screen, 0, False);
3545956cc18dSsnj	TrackText(xw, &(screen->startSel), &(screen->endSel));
3546956cc18dSsnj    }
3547d522f475Smrg
3548d522f475Smrg    return;
3549d522f475Smrg}
3550d522f475Smrg
3551d522f475Smrg/* Guaranteed (first.row, first.col) <= (last.row, last.col) */
3552d522f475Smrgstatic void
3553d522f475SmrgTrackText(XtermWidget xw,
3554e0a2b6dfSmrg	  const CELL *firstp,
3555e0a2b6dfSmrg	  const CELL *lastp)
3556d522f475Smrg{
3557956cc18dSsnj    TScreen *screen = TScreenOf(xw);
3558d522f475Smrg    int from, to;
3559d522f475Smrg    CELL old_start, old_end;
3560d522f475Smrg    CELL first = *firstp;
3561d522f475Smrg    CELL last = *lastp;
3562d522f475Smrg
3563d522f475Smrg    TRACE(("TrackText(first=%d,%d, last=%d,%d)\n",
3564d522f475Smrg	   first.row, first.col, last.row, last.col));
3565d522f475Smrg
3566d522f475Smrg    old_start = screen->startH;
3567d522f475Smrg    old_end = screen->endH;
35680bd37d32Smrg    TRACE(("...previous(first=%d,%d, last=%d,%d)\n",
35690bd37d32Smrg	   old_start.row, old_start.col,
35700bd37d32Smrg	   old_end.row, old_end.col));
3571d522f475Smrg    if (isSameCELL(&first, &old_start) &&
35720bd37d32Smrg	isSameCELL(&last, &old_end)) {
3573d522f475Smrg	return;
35740bd37d32Smrg    }
35750bd37d32Smrg
3576d522f475Smrg    screen->startH = first;
3577d522f475Smrg    screen->endH = last;
3578d522f475Smrg    from = Coordinate(screen, &screen->startH);
3579d522f475Smrg    to = Coordinate(screen, &screen->endH);
3580d522f475Smrg    if (to <= screen->startHCoord || from > screen->endHCoord) {
3581d522f475Smrg	/* No overlap whatsoever between old and new hilite */
3582d522f475Smrg	ReHiliteText(xw, &old_start, &old_end);
3583d522f475Smrg	ReHiliteText(xw, &first, &last);
3584d522f475Smrg    } else {
3585d522f475Smrg	if (from < screen->startHCoord) {
3586d522f475Smrg	    /* Extend left end */
3587d522f475Smrg	    ReHiliteText(xw, &first, &old_start);
3588d522f475Smrg	} else if (from > screen->startHCoord) {
3589d522f475Smrg	    /* Shorten left end */
3590d522f475Smrg	    ReHiliteText(xw, &old_start, &first);
3591d522f475Smrg	}
3592d522f475Smrg	if (to > screen->endHCoord) {
3593d522f475Smrg	    /* Extend right end */
3594d522f475Smrg	    ReHiliteText(xw, &old_end, &last);
3595d522f475Smrg	} else if (to < screen->endHCoord) {
3596d522f475Smrg	    /* Shorten right end */
3597d522f475Smrg	    ReHiliteText(xw, &last, &old_end);
3598d522f475Smrg	}
3599d522f475Smrg    }
3600d522f475Smrg    screen->startHCoord = from;
3601d522f475Smrg    screen->endHCoord = to;
3602d522f475Smrg}
3603d522f475Smrg
3604d522f475Smrg/* Guaranteed that (first->row, first->col) <= (last->row, last->col) */
3605d522f475Smrgstatic void
3606d522f475SmrgReHiliteText(XtermWidget xw,
3607e0a2b6dfSmrg	     CELL *firstp,
3608e0a2b6dfSmrg	     CELL *lastp)
3609d522f475Smrg{
3610956cc18dSsnj    TScreen *screen = TScreenOf(xw);
3611d522f475Smrg    int i;
3612d522f475Smrg    CELL first = *firstp;
3613d522f475Smrg    CELL last = *lastp;
3614d522f475Smrg
3615d522f475Smrg    TRACE(("ReHiliteText from %d.%d to %d.%d\n",
3616d522f475Smrg	   first.row, first.col, last.row, last.col));
3617d522f475Smrg
3618d522f475Smrg    if (first.row < 0)
3619d522f475Smrg	first.row = first.col = 0;
3620d522f475Smrg    else if (first.row > screen->max_row)
3621d522f475Smrg	return;			/* nothing to do, since last.row >= first.row */
3622d522f475Smrg
3623d522f475Smrg    if (last.row < 0)
3624d522f475Smrg	return;			/* nothing to do, since first.row <= last.row */
3625d522f475Smrg    else if (last.row > screen->max_row) {
3626d522f475Smrg	last.row = screen->max_row;
3627d522f475Smrg	last.col = MaxCols(screen);
3628d522f475Smrg    }
3629d522f475Smrg    if (isSameCELL(&first, &last))
3630d522f475Smrg	return;
3631d522f475Smrg
3632d522f475Smrg    if (!isSameRow(&first, &last)) {	/* do multiple rows */
3633d522f475Smrg	if ((i = screen->max_col - first.col + 1) > 0) {	/* first row */
3634d522f475Smrg	    ScrnRefresh(xw, first.row, first.col, 1, i, True);
3635d522f475Smrg	}
3636d522f475Smrg	if ((i = last.row - first.row - 1) > 0) {	/* middle rows */
3637d522f475Smrg	    ScrnRefresh(xw, first.row + 1, 0, i, MaxCols(screen), True);
3638d522f475Smrg	}
3639d522f475Smrg	if (last.col > 0 && last.row <= screen->max_row) {	/* last row */
3640d522f475Smrg	    ScrnRefresh(xw, last.row, 0, 1, last.col, True);
3641d522f475Smrg	}
3642d522f475Smrg    } else {			/* do single row */
3643d522f475Smrg	ScrnRefresh(xw, first.row, first.col, 1, last.col - first.col, True);
3644d522f475Smrg    }
3645d522f475Smrg}
3646d522f475Smrg
3647d522f475Smrg/*
3648d522f475Smrg * Guaranteed that (cellc->row, cellc->col) <= (cell->row, cell->col), and that both points are valid
3649d522f475Smrg * (may have cell->row = screen->max_row+1, cell->col = 0).
3650d522f475Smrg */
3651d522f475Smrgstatic void
3652d522f475SmrgSaltTextAway(XtermWidget xw,
3653e0a2b6dfSmrg	     CELL *cellc,
3654e0a2b6dfSmrg	     CELL *cell)
3655d522f475Smrg{
3656956cc18dSsnj    TScreen *screen = TScreenOf(xw);
3657d522f475Smrg    int i, j = 0;
3658d522f475Smrg    int eol;
3659956cc18dSsnj    int tmp;
3660d522f475Smrg    Char *line;
3661d522f475Smrg    Char *lp;
3662d522f475Smrg    CELL first = *cellc;
3663d522f475Smrg    CELL last = *cell;
3664d522f475Smrg
3665d522f475Smrg    if (isSameRow(&first, &last) && first.col > last.col) {
3666956cc18dSsnj	EXCHANGE(first.col, last.col, tmp);
3667d522f475Smrg    }
3668d522f475Smrg
3669d522f475Smrg    --last.col;
3670d522f475Smrg    /* first we need to know how long the string is before we can save it */
3671d522f475Smrg
3672d522f475Smrg    if (isSameRow(&last, &first)) {
3673d522f475Smrg	j = Length(screen, first.row, first.col, last.col);
3674d522f475Smrg    } else {			/* two cases, cut is on same line, cut spans multiple lines */
3675d522f475Smrg	j += Length(screen, first.row, first.col, screen->max_col) + 1;
3676d522f475Smrg	for (i = first.row + 1; i < last.row; i++)
3677d522f475Smrg	    j += Length(screen, i, 0, screen->max_col) + 1;
3678d522f475Smrg	if (last.col >= 0)
3679d522f475Smrg	    j += Length(screen, last.row, 0, last.col);
3680d522f475Smrg    }
3681d522f475Smrg
3682d522f475Smrg    /* UTF-8 may require more space */
3683d522f475Smrg    if_OPT_WIDE_CHARS(screen, {
3684d522f475Smrg	j *= 4;
3685d522f475Smrg    });
3686d522f475Smrg
3687d522f475Smrg    /* now get some memory to save it in */
3688d522f475Smrg
3689d522f475Smrg    if (screen->selection_size <= j) {
369020d2c4d2Smrg	if ((line = (Char *) malloc((size_t) j + 1)) == 0)
3691d522f475Smrg	    SysError(ERROR_BMALLOC2);
3692d522f475Smrg	XtFree((char *) screen->selection_data);
3693d522f475Smrg	screen->selection_data = line;
3694d522f475Smrg	screen->selection_size = j + 1;
3695d522f475Smrg    } else {
3696d522f475Smrg	line = screen->selection_data;
3697d522f475Smrg    }
3698d522f475Smrg
3699d522f475Smrg    if ((line == 0)
3700d522f475Smrg	|| (j < 0))
3701d522f475Smrg	return;
3702d522f475Smrg
3703d522f475Smrg    line[j] = '\0';		/* make sure it is null terminated */
3704d522f475Smrg    lp = line;			/* lp points to where to save the text */
3705d522f475Smrg    if (isSameRow(&last, &first)) {
3706d522f475Smrg	lp = SaveText(screen, last.row, first.col, last.col, lp, &eol);
3707d522f475Smrg    } else {
3708d522f475Smrg	lp = SaveText(screen, first.row, first.col, screen->max_col, lp, &eol);
3709d522f475Smrg	if (eol)
3710d522f475Smrg	    *lp++ = '\n';	/* put in newline at end of line */
3711d522f475Smrg	for (i = first.row + 1; i < last.row; i++) {
3712d522f475Smrg	    lp = SaveText(screen, i, 0, screen->max_col, lp, &eol);
3713d522f475Smrg	    if (eol)
3714d522f475Smrg		*lp++ = '\n';
3715d522f475Smrg	}
3716d522f475Smrg	if (last.col >= 0)
3717d522f475Smrg	    lp = SaveText(screen, last.row, 0, last.col, lp, &eol);
3718d522f475Smrg    }
3719d522f475Smrg    *lp = '\0';			/* make sure we have end marked */
3720d522f475Smrg
3721956cc18dSsnj    TRACE(("Salted TEXT:%d:%s\n", (int) (lp - line),
3722956cc18dSsnj	   visibleChars(line, (unsigned) (lp - line))));
3723d522f475Smrg
37242eaa94a1Schristos    screen->selection_length = (unsigned long) (lp - line);
3725d522f475Smrg}
3726d522f475Smrg
3727d522f475Smrg#if OPT_PASTE64
3728d522f475Smrgvoid
3729e0a2b6dfSmrgClearSelectionBuffer(TScreen *screen)
3730d522f475Smrg{
3731d522f475Smrg    screen->selection_length = 0;
3732d522f475Smrg    screen->base64_count = 0;
3733d522f475Smrg}
3734d522f475Smrg
3735d522f475Smrgstatic void
3736e0a2b6dfSmrgAppendStrToSelectionBuffer(TScreen *screen, Char *text, size_t len)
3737d522f475Smrg{
3738d522f475Smrg    if (len != 0) {
37392eaa94a1Schristos	int j = (int) (screen->selection_length + len);		/* New length */
3740d522f475Smrg	int k = j + (j >> 2) + 80;	/* New size if we grow buffer: grow by ~50% */
3741d522f475Smrg	if (j + 1 >= screen->selection_size) {
3742d522f475Smrg	    if (!screen->selection_length) {
3743d522f475Smrg		/* New buffer */
3744d522f475Smrg		Char *line;
374520d2c4d2Smrg		if ((line = (Char *) malloc((size_t) k)) == 0)
3746d522f475Smrg		    SysError(ERROR_BMALLOC2);
3747d522f475Smrg		XtFree((char *) screen->selection_data);
3748d522f475Smrg		screen->selection_data = line;
3749d522f475Smrg	    } else {
3750d522f475Smrg		/* Realloc buffer */
3751d522f475Smrg		screen->selection_data = (Char *)
3752d522f475Smrg		    realloc(screen->selection_data,
375320d2c4d2Smrg			    (size_t) k);
3754d522f475Smrg		if (screen->selection_data == 0)
3755d522f475Smrg		    SysError(ERROR_BMALLOC2);
3756d522f475Smrg	    }
3757d522f475Smrg	    screen->selection_size = k;
3758d522f475Smrg	}
375920d2c4d2Smrg	if (screen->selection_data != 0) {
376020d2c4d2Smrg	    memcpy(screen->selection_data + screen->selection_length, text, len);
376120d2c4d2Smrg	    screen->selection_length += len;
376220d2c4d2Smrg	    screen->selection_data[screen->selection_length] = 0;
376320d2c4d2Smrg	}
3764d522f475Smrg    }
3765d522f475Smrg}
3766d522f475Smrg
3767d522f475Smrgvoid
3768e0a2b6dfSmrgAppendToSelectionBuffer(TScreen *screen, unsigned c)
3769d522f475Smrg{
37702eaa94a1Schristos    unsigned six;
3771d522f475Smrg    Char ch;
3772d522f475Smrg
3773d522f475Smrg    /* Decode base64 character */
3774d522f475Smrg    if (c >= 'A' && c <= 'Z')
3775d522f475Smrg	six = c - 'A';
3776d522f475Smrg    else if (c >= 'a' && c <= 'z')
3777d522f475Smrg	six = c - 'a' + 26;
3778d522f475Smrg    else if (c >= '0' && c <= '9')
3779d522f475Smrg	six = c - '0' + 52;
3780d522f475Smrg    else if (c == '+')
3781d522f475Smrg	six = 62;
3782d522f475Smrg    else if (c == '/')
3783d522f475Smrg	six = 63;
3784d522f475Smrg    else
3785d522f475Smrg	return;
3786d522f475Smrg
3787d522f475Smrg    /* Accumulate bytes */
3788d522f475Smrg    switch (screen->base64_count) {
3789d522f475Smrg    case 0:
3790d522f475Smrg	screen->base64_accu = six;
3791d522f475Smrg	screen->base64_count = 6;
3792d522f475Smrg	break;
3793d522f475Smrg
3794d522f475Smrg    case 2:
37952eaa94a1Schristos	ch = CharOf((screen->base64_accu << 6) + six);
3796d522f475Smrg	screen->base64_count = 0;
379720d2c4d2Smrg	AppendStrToSelectionBuffer(screen, &ch, (size_t) 1);
3798d522f475Smrg	break;
3799d522f475Smrg
3800d522f475Smrg    case 4:
38012eaa94a1Schristos	ch = CharOf((screen->base64_accu << 4) + (six >> 2));
3802d522f475Smrg	screen->base64_accu = (six & 0x3);
3803d522f475Smrg	screen->base64_count = 2;
380420d2c4d2Smrg	AppendStrToSelectionBuffer(screen, &ch, (size_t) 1);
3805d522f475Smrg	break;
3806d522f475Smrg
3807d522f475Smrg    case 6:
38082eaa94a1Schristos	ch = CharOf((screen->base64_accu << 2) + (six >> 4));
3809d522f475Smrg	screen->base64_accu = (six & 0xF);
3810d522f475Smrg	screen->base64_count = 4;
381120d2c4d2Smrg	AppendStrToSelectionBuffer(screen, &ch, (size_t) 1);
3812d522f475Smrg	break;
3813d522f475Smrg    }
3814d522f475Smrg}
3815d522f475Smrg
3816d522f475Smrgvoid
3817e0a2b6dfSmrgCompleteSelection(XtermWidget xw, String *args, Cardinal len)
3818d522f475Smrg{
3819956cc18dSsnj    TScreen *screen = TScreenOf(xw);
3820956cc18dSsnj
3821956cc18dSsnj    screen->base64_count = 0;
3822956cc18dSsnj    screen->base64_accu = 0;
3823d522f475Smrg    _OwnSelection(xw, args, len);
3824d522f475Smrg}
3825d522f475Smrg#endif /* OPT_PASTE64 */
3826d522f475Smrg
3827d522f475Smrgstatic Bool
3828d522f475Smrg_ConvertSelectionHelper(Widget w,
3829894e0ac8Smrg			Atom *type,
3830d522f475Smrg			XtPointer *value,
3831d522f475Smrg			unsigned long *length,
3832d522f475Smrg			int *format,
3833d522f475Smrg			int (*conversion_function) (Display *,
3834d522f475Smrg						    char **, int,
3835d522f475Smrg						    XICCEncodingStyle,
3836d522f475Smrg						    XTextProperty *),
3837d522f475Smrg			XICCEncodingStyle conversion_style)
3838d522f475Smrg{
3839956cc18dSsnj    XtermWidget xw;
3840956cc18dSsnj
384101037d57Smrg    *value = 0;
384201037d57Smrg    *length = 0;
384301037d57Smrg    *type = 0;
384401037d57Smrg    *format = 0;
384501037d57Smrg
3846956cc18dSsnj    if ((xw = getXtermWidget(w)) != 0) {
3847956cc18dSsnj	TScreen *screen = TScreenOf(xw);
3848d522f475Smrg	Display *dpy = XtDisplay(w);
3849d522f475Smrg	XTextProperty textprop;
385001037d57Smrg	int out_n = 0;
385101037d57Smrg	unsigned long remaining = screen->selection_length;
385201037d57Smrg	char *result = 0;
3853d522f475Smrg	char *the_data = (char *) screen->selection_data;
385401037d57Smrg	char *the_next;
385501037d57Smrg
385601037d57Smrg	TRACE(("converting %ld:'%s'\n",
385701037d57Smrg	       (long) screen->selection_length,
385801037d57Smrg	       visibleChars(screen->selection_data, (unsigned) screen->selection_length)));
385901037d57Smrg	/*
386001037d57Smrg	 * For most selections, we can convert in one pass.  It is possible
386101037d57Smrg	 * that some applications contain embedded nulls, e.g., using xterm's
386201037d57Smrg	 * paste64 feature.  For those cases, we will build up the result in
386301037d57Smrg	 * parts.
386401037d57Smrg	 */
386501037d57Smrg	if (memchr(the_data, 0, screen->selection_length) != 0) {
386601037d57Smrg	    TRACE(("selection contains embedded nulls\n"));
386701037d57Smrg	    result = calloc(screen->selection_length + 1, sizeof(char));
386801037d57Smrg	}
3869d522f475Smrg
387001037d57Smrg      next_try:
387101037d57Smrg	memset(&textprop, 0, sizeof(textprop));
3872d522f475Smrg	if (conversion_function(dpy, &the_data, 1,
3873d522f475Smrg				conversion_style,
3874d522f475Smrg				&textprop) >= Success) {
387501037d57Smrg	    if ((result != 0)
387601037d57Smrg		&& (textprop.value != 0)
387701037d57Smrg		&& (textprop.format == 8)) {
387801037d57Smrg		char *text_values = (char *) textprop.value;
387901037d57Smrg		unsigned long in_n;
388001037d57Smrg
388101037d57Smrg		if (out_n == 0) {
388201037d57Smrg		    *value = result;
388301037d57Smrg		    *type = textprop.encoding;
388401037d57Smrg		    *format = textprop.format;
388501037d57Smrg		}
388601037d57Smrg		for (in_n = 0; in_n < textprop.nitems; ++in_n) {
388701037d57Smrg		    result[out_n++] = text_values[in_n];
388801037d57Smrg		}
388901037d57Smrg		*length += textprop.nitems;
389001037d57Smrg		if ((the_next = memchr(the_data, 0, remaining)) != 0) {
389101037d57Smrg		    unsigned long this_was = (unsigned long) (the_next - the_data);
389201037d57Smrg		    this_was++;
389301037d57Smrg		    the_data += this_was;
389401037d57Smrg		    remaining -= this_was;
389501037d57Smrg		    result[out_n++] = 0;
389601037d57Smrg		    *length += 1;
389701037d57Smrg		    if (remaining)
389801037d57Smrg			goto next_try;
389901037d57Smrg		}
390001037d57Smrg		return True;
390101037d57Smrg	    } else {
390201037d57Smrg		free(result);
390301037d57Smrg		*value = (XtPointer) textprop.value;
390401037d57Smrg		*length = textprop.nitems;
390501037d57Smrg		*type = textprop.encoding;
390601037d57Smrg		*format = textprop.format;
390701037d57Smrg		return True;
390801037d57Smrg	    }
3909d522f475Smrg	}
391001037d57Smrg	free(result);
3911d522f475Smrg    }
3912d522f475Smrg    return False;
3913d522f475Smrg}
3914d522f475Smrg
39152eaa94a1Schristosstatic Boolean
39162eaa94a1SchristosSaveConvertedLength(XtPointer *target, unsigned long source)
39172eaa94a1Schristos{
39182eaa94a1Schristos    Boolean result = False;
39192eaa94a1Schristos
39202eaa94a1Schristos    *target = XtMalloc(4);
39212eaa94a1Schristos    if (*target != 0) {
39222eaa94a1Schristos	result = True;
39232eaa94a1Schristos	if (sizeof(unsigned long) == 4) {
39242eaa94a1Schristos	    *(unsigned long *) *target = source;
39252eaa94a1Schristos	} else if (sizeof(unsigned) == 4) {
392620d2c4d2Smrg	    *(unsigned *) *target = (unsigned) source;
39272eaa94a1Schristos	} else if (sizeof(unsigned short) == 4) {
39282eaa94a1Schristos	    *(unsigned short *) *target = (unsigned short) source;
39292eaa94a1Schristos	} else {
39302eaa94a1Schristos	    /* FIXME - does this depend on byte-order? */
39312eaa94a1Schristos	    unsigned long temp = source;
393220d2c4d2Smrg	    memcpy((char *) *target,
393320d2c4d2Smrg		   ((char *) &temp) + sizeof(temp) - 4,
393420d2c4d2Smrg		   (size_t) 4);
39352eaa94a1Schristos	}
39362eaa94a1Schristos    }
39372eaa94a1Schristos    return result;
39382eaa94a1Schristos}
39392eaa94a1Schristos
3940d522f475Smrgstatic Boolean
3941d522f475SmrgConvertSelection(Widget w,
3942894e0ac8Smrg		 Atom *selection,
3943894e0ac8Smrg		 Atom *target,
3944894e0ac8Smrg		 Atom *type,
3945d522f475Smrg		 XtPointer *value,
3946d522f475Smrg		 unsigned long *length,
3947d522f475Smrg		 int *format)
3948d522f475Smrg{
3949d522f475Smrg    Display *dpy = XtDisplay(w);
3950d522f475Smrg    TScreen *screen;
3951d522f475Smrg    Bool result = False;
3952d522f475Smrg
3953956cc18dSsnj    XtermWidget xw;
3954956cc18dSsnj
3955956cc18dSsnj    if ((xw = getXtermWidget(w)) == 0)
3956d522f475Smrg	return False;
3957d522f475Smrg
3958956cc18dSsnj    screen = TScreenOf(xw);
3959d522f475Smrg
3960956cc18dSsnj    TRACE(("ConvertSelection %s\n",
3961956cc18dSsnj	   visibleSelectionTarget(dpy, *target)));
3962956cc18dSsnj
396301037d57Smrg    if (screen->selection_data == NULL) {
396401037d57Smrg	TRACE(("...FIXME: no selection_data\n"));
396501037d57Smrg	return False;		/* can this happen? */
396601037d57Smrg    }
396701037d57Smrg
3968d522f475Smrg    if (*target == XA_TARGETS(dpy)) {
3969d522f475Smrg	Atom *allocP;
3970d522f475Smrg	Atom *targetP;
3971d522f475Smrg	Atom *std_targets;
3972d522f475Smrg	XPointer std_return = 0;
3973d522f475Smrg	unsigned long std_length;
3974d522f475Smrg
3975d522f475Smrg	if (XmuConvertStandardSelection(w, screen->selection_time, selection,
3976d522f475Smrg					target, type, &std_return,
3977d522f475Smrg					&std_length, format)) {
3978956cc18dSsnj	    Atom *my_targets = _SelectionTargets(w);
3979956cc18dSsnj
3980956cc18dSsnj	    TRACE(("XmuConvertStandardSelection - success\n"));
3981a1f3da82Smrg	    std_targets = (Atom *) (void *) (std_return);
3982d522f475Smrg	    *length = std_length + 6;
3983d522f475Smrg
3984a1f3da82Smrg	    targetP = TypeXtMallocN(Atom, *length);
3985d522f475Smrg	    allocP = targetP;
3986d522f475Smrg
3987d522f475Smrg	    *value = (XtPointer) targetP;
3988d522f475Smrg
39890bd37d32Smrg	    if (my_targets != 0) {
39900bd37d32Smrg		while (*my_targets != None) {
39910bd37d32Smrg		    *targetP++ = *my_targets++;
39920bd37d32Smrg		}
3993956cc18dSsnj	    }
3994d522f475Smrg	    *targetP++ = XA_LENGTH(dpy);
3995d522f475Smrg	    *targetP++ = XA_LIST_LENGTH(dpy);
3996d522f475Smrg
39972eaa94a1Schristos	    *length = std_length + (unsigned long) (targetP - allocP);
3998d522f475Smrg
3999d522f475Smrg	    memcpy(targetP, std_targets, sizeof(Atom) * std_length);
4000d522f475Smrg	    XtFree((char *) std_targets);
4001d522f475Smrg	    *type = XA_ATOM;
4002d522f475Smrg	    *format = 32;
4003d522f475Smrg	    result = True;
4004956cc18dSsnj	} else {
4005956cc18dSsnj	    TRACE(("XmuConvertStandardSelection - failed\n"));
4006d522f475Smrg	}
4007d522f475Smrg    }
4008d522f475Smrg#if OPT_WIDE_CHARS
4009d522f475Smrg    else if (screen->wide_chars && *target == XA_STRING) {
4010d522f475Smrg	result =
4011d522f475Smrg	    _ConvertSelectionHelper(w,
4012d522f475Smrg				    type, value, length, format,
4013d522f475Smrg				    Xutf8TextListToTextProperty,
4014d522f475Smrg				    XStringStyle);
4015956cc18dSsnj	TRACE(("...Xutf8TextListToTextProperty:%d\n", result));
4016d522f475Smrg    } else if (screen->wide_chars && *target == XA_UTF8_STRING(dpy)) {
4017d522f475Smrg	result =
4018d522f475Smrg	    _ConvertSelectionHelper(w,
4019d522f475Smrg				    type, value, length, format,
4020d522f475Smrg				    Xutf8TextListToTextProperty,
4021d522f475Smrg				    XUTF8StringStyle);
4022956cc18dSsnj	TRACE(("...Xutf8TextListToTextProperty:%d\n", result));
4023d522f475Smrg    } else if (screen->wide_chars && *target == XA_TEXT(dpy)) {
4024d522f475Smrg	result =
4025d522f475Smrg	    _ConvertSelectionHelper(w,
4026d522f475Smrg				    type, value, length, format,
4027d522f475Smrg				    Xutf8TextListToTextProperty,
4028d522f475Smrg				    XStdICCTextStyle);
4029956cc18dSsnj	TRACE(("...Xutf8TextListToTextProperty:%d\n", result));
4030d522f475Smrg    } else if (screen->wide_chars && *target == XA_COMPOUND_TEXT(dpy)) {
4031d522f475Smrg	result =
4032d522f475Smrg	    _ConvertSelectionHelper(w,
4033d522f475Smrg				    type, value, length, format,
4034d522f475Smrg				    Xutf8TextListToTextProperty,
4035d522f475Smrg				    XCompoundTextStyle);
4036956cc18dSsnj	TRACE(("...Xutf8TextListToTextProperty:%d\n", result));
4037d522f475Smrg    }
4038d522f475Smrg#endif
4039d522f475Smrg
4040d522f475Smrg    else if (*target == XA_STRING) {	/* not wide_chars */
4041d522f475Smrg	/* We can only reach this point if the selection requestor
4042d522f475Smrg	   requested STRING before any of TEXT, COMPOUND_TEXT or
4043d522f475Smrg	   UTF8_STRING.  We therefore assume that the requestor is not
4044d522f475Smrg	   properly internationalised, and dump raw eight-bit data
4045d522f475Smrg	   with no conversion into the selection.  Yes, this breaks
4046d522f475Smrg	   the ICCCM in non-Latin-1 locales. */
4047d522f475Smrg	*type = XA_STRING;
4048d522f475Smrg	*value = (XtPointer) screen->selection_data;
4049d522f475Smrg	*length = screen->selection_length;
4050d522f475Smrg	*format = 8;
4051d522f475Smrg	result = True;
4052956cc18dSsnj	TRACE(("...raw 8-bit data:%d\n", result));
4053d522f475Smrg    } else if (*target == XA_TEXT(dpy)) {	/* not wide_chars */
4054d522f475Smrg	result =
4055d522f475Smrg	    _ConvertSelectionHelper(w,
4056d522f475Smrg				    type, value, length, format,
4057d522f475Smrg				    XmbTextListToTextProperty,
4058d522f475Smrg				    XStdICCTextStyle);
4059956cc18dSsnj	TRACE(("...XmbTextListToTextProperty(StdICC):%d\n", result));
4060d522f475Smrg    } else if (*target == XA_COMPOUND_TEXT(dpy)) {	/* not wide_chars */
4061d522f475Smrg	result =
4062d522f475Smrg	    _ConvertSelectionHelper(w,
4063d522f475Smrg				    type, value, length, format,
4064d522f475Smrg				    XmbTextListToTextProperty,
4065d522f475Smrg				    XCompoundTextStyle);
4066956cc18dSsnj	TRACE(("...XmbTextListToTextProperty(Compound):%d\n", result));
4067d522f475Smrg    }
4068d522f475Smrg#ifdef X_HAVE_UTF8_STRING
4069d522f475Smrg    else if (*target == XA_UTF8_STRING(dpy)) {	/* not wide_chars */
4070d522f475Smrg	result =
4071d522f475Smrg	    _ConvertSelectionHelper(w,
4072d522f475Smrg				    type, value, length, format,
4073d522f475Smrg				    XmbTextListToTextProperty,
4074d522f475Smrg				    XUTF8StringStyle);
4075956cc18dSsnj	TRACE(("...XmbTextListToTextProperty(UTF8):%d\n", result));
4076d522f475Smrg    }
4077d522f475Smrg#endif
4078d522f475Smrg    else if (*target == XA_LIST_LENGTH(dpy)) {
407920d2c4d2Smrg	result = SaveConvertedLength(value, (unsigned long) 1);
4080d522f475Smrg	*type = XA_INTEGER;
4081d522f475Smrg	*length = 1;
4082d522f475Smrg	*format = 32;
4083956cc18dSsnj	TRACE(("...list of values:%d\n", result));
4084d522f475Smrg    } else if (*target == XA_LENGTH(dpy)) {
4085d522f475Smrg	/* This value is wrong if we have UTF-8 text */
40862eaa94a1Schristos	result = SaveConvertedLength(value, screen->selection_length);
4087d522f475Smrg	*type = XA_INTEGER;
4088d522f475Smrg	*length = 1;
4089d522f475Smrg	*format = 32;
4090956cc18dSsnj	TRACE(("...list of values:%d\n", result));
4091d522f475Smrg    } else if (XmuConvertStandardSelection(w,
4092d522f475Smrg					   screen->selection_time, selection,
4093d522f475Smrg					   target, type, (XPointer *) value,
4094d522f475Smrg					   length, format)) {
4095d522f475Smrg	result = True;
4096956cc18dSsnj	TRACE(("...XmuConvertStandardSelection:%d\n", result));
4097d522f475Smrg    }
4098d522f475Smrg
4099d522f475Smrg    /* else */
41002eaa94a1Schristos    return (Boolean) result;
4101d522f475Smrg}
4102d522f475Smrg
4103d522f475Smrgstatic void
4104894e0ac8SmrgLoseSelection(Widget w, Atom *selection)
4105d522f475Smrg{
4106d522f475Smrg    TScreen *screen;
4107d522f475Smrg    Atom *atomP;
4108d522f475Smrg    Cardinal i;
4109d522f475Smrg
4110956cc18dSsnj    XtermWidget xw;
4111956cc18dSsnj
4112956cc18dSsnj    if ((xw = getXtermWidget(w)) == 0)
4113d522f475Smrg	return;
4114d522f475Smrg
4115956cc18dSsnj    screen = TScreenOf(xw);
411601037d57Smrg    TRACE(("LoseSelection %s\n", XGetAtomName(screen->display, *selection)));
411701037d57Smrg
4118d522f475Smrg    for (i = 0, atomP = screen->selection_atoms;
4119d522f475Smrg	 i < screen->selection_count; i++, atomP++) {
4120d522f475Smrg	if (*selection == *atomP)
4121d522f475Smrg	    *atomP = (Atom) 0;
4122d522f475Smrg	if (CutBuffer(*atomP) >= 0) {
4123d522f475Smrg	    *atomP = (Atom) 0;
4124d522f475Smrg	}
4125d522f475Smrg    }
4126d522f475Smrg
4127d522f475Smrg    for (i = screen->selection_count; i; i--) {
4128d522f475Smrg	if (screen->selection_atoms[i - 1] != 0)
4129d522f475Smrg	    break;
4130d522f475Smrg    }
4131d522f475Smrg    screen->selection_count = i;
4132d522f475Smrg
4133d522f475Smrg    for (i = 0, atomP = screen->selection_atoms;
4134d522f475Smrg	 i < screen->selection_count; i++, atomP++) {
4135d522f475Smrg	if (*atomP == (Atom) 0) {
4136d522f475Smrg	    *atomP = screen->selection_atoms[--screen->selection_count];
4137d522f475Smrg	}
4138d522f475Smrg    }
4139d522f475Smrg
4140d522f475Smrg    if (screen->selection_count == 0)
4141956cc18dSsnj	TrackText(xw, &zeroCELL, &zeroCELL);
4142d522f475Smrg}
4143d522f475Smrg
4144d522f475Smrg/* ARGSUSED */
4145d522f475Smrgstatic void
4146d522f475SmrgSelectionDone(Widget w GCC_UNUSED,
4147894e0ac8Smrg	      Atom *selection GCC_UNUSED,
4148894e0ac8Smrg	      Atom *target GCC_UNUSED)
4149d522f475Smrg{
4150d522f475Smrg    /* empty proc so Intrinsics know we want to keep storage */
415101037d57Smrg    TRACE(("SelectionDone\n"));
4152d522f475Smrg}
4153d522f475Smrg
4154d522f475Smrgstatic void
4155d522f475Smrg_OwnSelection(XtermWidget xw,
4156e0a2b6dfSmrg	      String *selections,
4157d522f475Smrg	      Cardinal count)
4158d522f475Smrg{
4159956cc18dSsnj    TScreen *screen = TScreenOf(xw);
4160d522f475Smrg    Atom *atoms = screen->selection_atoms;
4161d522f475Smrg    Cardinal i;
4162d522f475Smrg    Bool have_selection = False;
4163d522f475Smrg
416420d2c4d2Smrg    if (count == 0)
416520d2c4d2Smrg	return;
4166d522f475Smrg
416701037d57Smrg    TRACE(("_OwnSelection count %d, length %ld value %s\n", count,
416801037d57Smrg	   screen->selection_length,
416901037d57Smrg	   visibleChars(screen->selection_data, (unsigned) screen->selection_length)));
4170d522f475Smrg    selections = MapSelections(xw, selections, count);
4171d522f475Smrg
4172d522f475Smrg    if (count > screen->sel_atoms_size) {
4173d522f475Smrg	XtFree((char *) atoms);
4174a1f3da82Smrg	atoms = TypeXtMallocN(Atom, count);
4175d522f475Smrg	screen->selection_atoms = atoms;
4176d522f475Smrg	screen->sel_atoms_size = count;
4177d522f475Smrg    }
4178d522f475Smrg    XmuInternStrings(XtDisplay((Widget) xw), selections, count, atoms);
4179d522f475Smrg    for (i = 0; i < count; i++) {
4180d522f475Smrg	int cutbuffer = CutBuffer(atoms[i]);
4181d522f475Smrg	if (cutbuffer >= 0) {
41822eaa94a1Schristos	    unsigned long limit =
41832eaa94a1Schristos	    (unsigned long) (4 * XMaxRequestSize(XtDisplay((Widget) xw)) - 32);
41842eaa94a1Schristos	    if (screen->selection_length > limit) {
418520d2c4d2Smrg		TRACE(("selection too big (%lu bytes), not storing in CUT_BUFFER%d\n",
418620d2c4d2Smrg		       screen->selection_length, cutbuffer));
41870bd37d32Smrg		xtermWarning("selection too big (%lu bytes), not storing in CUT_BUFFER%d\n",
41880bd37d32Smrg			     screen->selection_length, cutbuffer);
4189d522f475Smrg	    } else {
4190d522f475Smrg		/* This used to just use the UTF-8 data, which was totally
4191894e0ac8Smrg		 * broken as not even the corresponding paste code in xterm
4192d522f475Smrg		 * understood this!  So now it converts to Latin1 first.
4193d522f475Smrg		 *   Robert Brady, 2000-09-05
4194d522f475Smrg		 */
4195d522f475Smrg		unsigned long length = screen->selection_length;
4196d522f475Smrg		Char *data = screen->selection_data;
4197d522f475Smrg		if_OPT_WIDE_CHARS((screen), {
4198956cc18dSsnj		    data = UTF8toLatin1(screen, data, length, &length);
4199d522f475Smrg		});
4200d522f475Smrg		TRACE(("XStoreBuffer(%d)\n", cutbuffer));
4201d522f475Smrg		XStoreBuffer(XtDisplay((Widget) xw),
4202d522f475Smrg			     (char *) data,
4203d522f475Smrg			     (int) length,
4204d522f475Smrg			     cutbuffer);
4205d522f475Smrg	    }
420601037d57Smrg	} else if (screen->selection_length == 0) {
420701037d57Smrg	    XtDisownSelection((Widget) xw, atoms[i], screen->selection_time);
4208d522f475Smrg	} else if (!screen->replyToEmacs) {
4209d522f475Smrg	    have_selection |=
4210d522f475Smrg		XtOwnSelection((Widget) xw, atoms[i],
4211d522f475Smrg			       screen->selection_time,
4212d522f475Smrg			       ConvertSelection, LoseSelection, SelectionDone);
4213d522f475Smrg	}
4214d522f475Smrg    }
4215d522f475Smrg    if (!screen->replyToEmacs)
4216d522f475Smrg	screen->selection_count = count;
4217d522f475Smrg    if (!have_selection)
4218d522f475Smrg	TrackText(xw, &zeroCELL, &zeroCELL);
4219d522f475Smrg}
4220d522f475Smrg
4221d522f475Smrgstatic void
4222e0a2b6dfSmrgResetSelectionState(TScreen *screen)
4223d522f475Smrg{
4224d522f475Smrg    screen->selection_count = 0;
4225d522f475Smrg    screen->startH = zeroCELL;
4226d522f475Smrg    screen->endH = zeroCELL;
4227d522f475Smrg}
4228d522f475Smrg
4229d522f475Smrgvoid
4230d522f475SmrgDisownSelection(XtermWidget xw)
4231d522f475Smrg{
4232956cc18dSsnj    TScreen *screen = TScreenOf(xw);
4233d522f475Smrg    Atom *atoms = screen->selection_atoms;
4234d522f475Smrg    Cardinal count = screen->selection_count;
4235d522f475Smrg    Cardinal i;
4236d522f475Smrg
4237d522f475Smrg    TRACE(("DisownSelection count %d, start %d.%d, end %d.%d\n",
4238d522f475Smrg	   count,
4239d522f475Smrg	   screen->startH.row,
4240d522f475Smrg	   screen->startH.col,
4241d522f475Smrg	   screen->endH.row,
4242d522f475Smrg	   screen->endH.col));
4243d522f475Smrg
4244d522f475Smrg    for (i = 0; i < count; i++) {
4245d522f475Smrg	int cutbuffer = CutBuffer(atoms[i]);
4246d522f475Smrg	if (cutbuffer < 0) {
4247d522f475Smrg	    XtDisownSelection((Widget) xw, atoms[i],
4248d522f475Smrg			      screen->selection_time);
4249d522f475Smrg	}
4250d522f475Smrg    }
4251d522f475Smrg    /*
4252d522f475Smrg     * If none of the callbacks via XtDisownSelection() reset highlighting
4253d522f475Smrg     * do it now.
4254d522f475Smrg     */
4255d522f475Smrg    if (ScrnHaveSelection(screen)) {
4256d522f475Smrg	/* save data which will be reset */
4257d522f475Smrg	CELL first = screen->startH;
4258d522f475Smrg	CELL last = screen->endH;
4259d522f475Smrg
4260d522f475Smrg	ResetSelectionState(screen);
4261d522f475Smrg	ReHiliteText(xw, &first, &last);
4262d522f475Smrg    } else {
4263d522f475Smrg	ResetSelectionState(screen);
4264d522f475Smrg    }
4265d522f475Smrg}
4266d522f475Smrg
4267d522f475Smrgvoid
4268d522f475SmrgUnhiliteSelection(XtermWidget xw)
4269d522f475Smrg{
4270956cc18dSsnj    TScreen *screen = TScreenOf(xw);
4271d522f475Smrg
4272d522f475Smrg    if (ScrnHaveSelection(screen)) {
4273d522f475Smrg	CELL first = screen->startH;
4274d522f475Smrg	CELL last = screen->endH;
4275d522f475Smrg
4276d522f475Smrg	screen->startH = zeroCELL;
4277d522f475Smrg	screen->endH = zeroCELL;
4278d522f475Smrg	ReHiliteText(xw, &first, &last);
4279d522f475Smrg    }
4280d522f475Smrg}
4281d522f475Smrg
4282d522f475Smrg/* returns number of chars in line from scol to ecol out */
4283d522f475Smrg/* ARGSUSED */
4284d522f475Smrgstatic int
4285e0a2b6dfSmrgLength(TScreen *screen,
4286d522f475Smrg       int row,
4287d522f475Smrg       int scol,
4288d522f475Smrg       int ecol)
4289d522f475Smrg{
429001037d57Smrg    CLineData *ld = GET_LINEDATA(screen, row);
429101037d57Smrg    const int lastcol = LastTextCol(screen, ld, row);
4292d522f475Smrg
4293d522f475Smrg    if (ecol > lastcol)
4294d522f475Smrg	ecol = lastcol;
4295d522f475Smrg    return (ecol - scol + 1);
4296d522f475Smrg}
4297d522f475Smrg
4298d522f475Smrg/* copies text into line, preallocated */
4299d522f475Smrgstatic Char *
4300e0a2b6dfSmrgSaveText(TScreen *screen,
4301d522f475Smrg	 int row,
4302d522f475Smrg	 int scol,
4303d522f475Smrg	 int ecol,
4304e0a2b6dfSmrg	 Char *lp,		/* pointer to where to put the text */
4305d522f475Smrg	 int *eol)
4306d522f475Smrg{
4307956cc18dSsnj    LineData *ld;
4308d522f475Smrg    int i = 0;
4309d522f475Smrg    unsigned c;
4310d522f475Smrg    Char *result = lp;
4311d522f475Smrg#if OPT_WIDE_CHARS
43122eaa94a1Schristos    unsigned previous = 0;
4313d522f475Smrg#endif
4314d522f475Smrg
4315956cc18dSsnj    ld = GET_LINEDATA(screen, row);
4316d522f475Smrg    i = Length(screen, row, scol, ecol);
4317d522f475Smrg    ecol = scol + i;
4318d522f475Smrg#if OPT_DEC_CHRSET
4319956cc18dSsnj    if (CSET_DOUBLE(GetLineDblCS(ld))) {
4320d522f475Smrg	scol = (scol + 0) / 2;
4321d522f475Smrg	ecol = (ecol + 1) / 2;
4322d522f475Smrg    }
4323d522f475Smrg#endif
4324956cc18dSsnj    *eol = !LineTstWrapped(ld);
4325d522f475Smrg    for (i = scol; i < ecol; i++) {
43260bd37d32Smrg	assert(i < (int) ld->lineSize);
4327956cc18dSsnj	c = E2A(ld->charData[i]);
4328d522f475Smrg#if OPT_WIDE_CHARS
4329d522f475Smrg	/* We want to strip out every occurrence of HIDDEN_CHAR AFTER a
4330d522f475Smrg	 * wide character.
4331d522f475Smrg	 */
4332894e0ac8Smrg	if (c == HIDDEN_CHAR) {
4333894e0ac8Smrg	    if (isWide((int) previous)) {
4334894e0ac8Smrg		previous = c;
4335894e0ac8Smrg		/* Combining characters attached to double-width characters
4336894e0ac8Smrg		   are in memory attached to the HIDDEN_CHAR */
4337894e0ac8Smrg		if_OPT_WIDE_CHARS(screen, {
4338894e0ac8Smrg		    if ((screen->utf8_nrc_mode | screen->utf8_mode) != uFalse) {
4339894e0ac8Smrg			unsigned ch;
4340894e0ac8Smrg			size_t off;
4341894e0ac8Smrg			for_each_combData(off, ld) {
4342894e0ac8Smrg			    ch = ld->combData[off][i];
4343894e0ac8Smrg			    if (ch == 0)
4344894e0ac8Smrg				break;
4345894e0ac8Smrg			    lp = convertToUTF8(lp, ch);
4346894e0ac8Smrg			}
4347d522f475Smrg		    }
4348894e0ac8Smrg		});
4349894e0ac8Smrg		continue;
4350894e0ac8Smrg	    } else {
4351894e0ac8Smrg		c = ' ';	/* should not happen, but just in case... */
4352894e0ac8Smrg	    }
4353d522f475Smrg	}
4354d522f475Smrg	previous = c;
4355e0a2b6dfSmrg	if ((screen->utf8_nrc_mode | screen->utf8_mode) != uFalse) {
4356d522f475Smrg	    lp = convertToUTF8(lp, (c != 0) ? c : ' ');
4357d522f475Smrg	    if_OPT_WIDE_CHARS(screen, {
4358d522f475Smrg		unsigned ch;
4359956cc18dSsnj		size_t off;
4360956cc18dSsnj		for_each_combData(off, ld) {
4361956cc18dSsnj		    ch = ld->combData[off][i];
4362956cc18dSsnj		    if (ch == 0)
4363d522f475Smrg			break;
4364d522f475Smrg		    lp = convertToUTF8(lp, ch);
4365d522f475Smrg		}
4366d522f475Smrg	    });
4367d522f475Smrg	} else
4368d522f475Smrg#endif
4369d522f475Smrg	{
4370d522f475Smrg	    if (c == 0) {
4371d522f475Smrg		c = E2A(' ');
4372d522f475Smrg	    } else if (c < E2A(' ')) {
4373d522f475Smrg		c = DECtoASCII(c);
4374d522f475Smrg	    } else if (c == 0x7f) {
4375d522f475Smrg		c = 0x5f;
4376d522f475Smrg	    }
43772eaa94a1Schristos	    *lp++ = CharOf(A2E(c));
4378d522f475Smrg	}
4379d522f475Smrg	if (c != E2A(' '))
4380d522f475Smrg	    result = lp;
4381d522f475Smrg    }
4382d522f475Smrg
4383d522f475Smrg    /*
4384d522f475Smrg     * If requested, trim trailing blanks from selected lines.  Do not do this
4385d522f475Smrg     * if the line is wrapped.
4386d522f475Smrg     */
4387d522f475Smrg    if (!*eol || !screen->trim_selection)
4388d522f475Smrg	result = lp;
4389d522f475Smrg
4390d522f475Smrg    return (result);
4391d522f475Smrg}
4392d522f475Smrg
4393d522f475Smrg/* 32 + following 7-bit word:
4394d522f475Smrg
4395d522f475Smrg   1:0  Button no: 0, 1, 2.  3=release.
4396d522f475Smrg     2  shift
4397d522f475Smrg     3  meta
4398d522f475Smrg     4  ctrl
4399d522f475Smrg     5  set for motion notify
4400d522f475Smrg     6  set for wheel
4401d522f475Smrg*/
4402d522f475Smrg
4403d522f475Smrg/* Position: 32 - 255. */
4404a1f3da82Smrgstatic int
4405894e0ac8SmrgBtnCode(XButtonEvent *event, int button)
4406d522f475Smrg{
44072eaa94a1Schristos    int result = (int) (32 + (KeyState(event->state) << 2));
4408d522f475Smrg
44090bd37d32Smrg    if (event->type == MotionNotify)
44100bd37d32Smrg	result += 32;
44110bd37d32Smrg
4412d522f475Smrg    if (button < 0 || button > 5) {
4413d522f475Smrg	result += 3;
4414d522f475Smrg    } else {
4415d522f475Smrg	if (button > 3)
4416d522f475Smrg	    result += (64 - 4);
4417d522f475Smrg	result += button;
4418d522f475Smrg    }
44190bd37d32Smrg    TRACE(("BtnCode button %d, %s state " FMT_MODIFIER_NAMES " ->%#x\n",
44200bd37d32Smrg	   button,
44210bd37d32Smrg	   visibleEventType(event->type),
44220bd37d32Smrg	   ARG_MODIFIER_NAMES(event->state),
44230bd37d32Smrg	   result));
4424a1f3da82Smrg    return result;
4425a1f3da82Smrg}
4426a1f3da82Smrg
4427a1f3da82Smrgstatic unsigned
4428e0a2b6dfSmrgEmitButtonCode(TScreen *screen,
4429e0a2b6dfSmrg	       Char *line,
44300bd37d32Smrg	       unsigned count,
4431894e0ac8Smrg	       XButtonEvent *event,
44320bd37d32Smrg	       int button)
4433a1f3da82Smrg{
44340bd37d32Smrg    int value;
4435a1f3da82Smrg
44360bd37d32Smrg    if (screen->send_mouse_pos == X10_MOUSE) {
44370bd37d32Smrg	value = CharOf(' ' + button);
4438a1f3da82Smrg    } else {
44390bd37d32Smrg	value = BtnCode(event, button);
44400bd37d32Smrg    }
44410bd37d32Smrg
44420bd37d32Smrg    switch (screen->extend_coords) {
44430bd37d32Smrg    default:
44440bd37d32Smrg	line[count++] = CharOf(value);
44450bd37d32Smrg	break;
44460bd37d32Smrg    case SET_SGR_EXT_MODE_MOUSE:
44470bd37d32Smrg	value -= 32;		/* encoding starts at zero */
44480bd37d32Smrg	/* FALLTHRU */
44490bd37d32Smrg    case SET_URXVT_EXT_MODE_MOUSE:
44500bd37d32Smrg	count += (unsigned) sprintf((char *) line + count, "%d", value);
44510bd37d32Smrg	break;
44520bd37d32Smrg    case SET_EXT_MODE_MOUSE:
44530bd37d32Smrg	if (value < 128) {
44540bd37d32Smrg	    line[count++] = CharOf(value);
44550bd37d32Smrg	} else {
44560bd37d32Smrg	    line[count++] = CharOf(0xC0 + (value >> 6));
44570bd37d32Smrg	    line[count++] = CharOf(0x80 + (value & 0x3F));
44580bd37d32Smrg	}
44590bd37d32Smrg	break;
4460a1f3da82Smrg    }
4461a1f3da82Smrg    return count;
4462d522f475Smrg}
4463d522f475Smrg
44640bd37d32Smrgstatic int
44650bd37d32SmrgFirstBitN(int bits)
44660bd37d32Smrg{
44670bd37d32Smrg    int result = -1;
44680bd37d32Smrg    if (bits > 0) {
44690bd37d32Smrg	result = 0;
44700bd37d32Smrg	while (!(bits & 1)) {
44710bd37d32Smrg	    bits /= 2;
44720bd37d32Smrg	    ++result;
44730bd37d32Smrg	}
44740bd37d32Smrg    }
44750bd37d32Smrg    return result;
44760bd37d32Smrg}
44770bd37d32Smrg
44780bd37d32Smrg#define ButtonBit(button) ((button >= 0) ? (1 << (button)) : 0)
44790bd37d32Smrg
44800bd37d32Smrg#define EMIT_BUTTON(button) EmitButtonCode(screen, line, count, event, button)
44810bd37d32Smrg
4482d522f475Smrgstatic void
4483894e0ac8SmrgEditorButton(XtermWidget xw, XButtonEvent *event)
4484d522f475Smrg{
4485956cc18dSsnj    TScreen *screen = TScreenOf(xw);
4486d522f475Smrg    int pty = screen->respond;
44870bd37d32Smrg    int mouse_limit = MouseLimit(screen);
44880bd37d32Smrg    Char line[32];
44890bd37d32Smrg    Char final = 'M';
4490d522f475Smrg    int row, col;
4491d522f475Smrg    int button;
4492d522f475Smrg    unsigned count = 0;
4493d522f475Smrg    Boolean changed = True;
4494d522f475Smrg
4495d522f475Smrg    /* If button event, get button # adjusted for DEC compatibility */
44962eaa94a1Schristos    button = (int) (event->button - 1);
4497d522f475Smrg    if (button >= 3)
4498d522f475Smrg	button++;
4499d522f475Smrg
4500d522f475Smrg    /* Compute character position of mouse pointer */
4501d522f475Smrg    row = (event->y - screen->border) / FontHeight(screen);
4502d522f475Smrg    col = (event->x - OriginX(screen)) / FontWidth(screen);
4503d522f475Smrg
4504d522f475Smrg    /* Limit to screen dimensions */
4505d522f475Smrg    if (row < 0)
4506d522f475Smrg	row = 0;
4507d522f475Smrg    else if (row > screen->max_row)
4508d522f475Smrg	row = screen->max_row;
4509d522f475Smrg
4510d522f475Smrg    if (col < 0)
4511d522f475Smrg	col = 0;
4512d522f475Smrg    else if (col > screen->max_col)
4513d522f475Smrg	col = screen->max_col;
4514492d43a5Smrg
45150bd37d32Smrg    if (mouse_limit > 0) {
45160bd37d32Smrg	/* Limit to representable mouse dimensions */
45170bd37d32Smrg	if (row > mouse_limit)
45180bd37d32Smrg	    row = mouse_limit;
45190bd37d32Smrg	if (col > mouse_limit)
45200bd37d32Smrg	    col = mouse_limit;
45210bd37d32Smrg    }
4522d522f475Smrg
4523d522f475Smrg    /* Build key sequence starting with \E[M */
4524d522f475Smrg    if (screen->control_eight_bits) {
4525d522f475Smrg	line[count++] = ANSI_CSI;
4526d522f475Smrg    } else {
4527d522f475Smrg	line[count++] = ANSI_ESC;
4528d522f475Smrg	line[count++] = '[';
4529d522f475Smrg    }
45300bd37d32Smrg    switch (screen->extend_coords) {
45310bd37d32Smrg    case 0:
45320bd37d32Smrg    case SET_EXT_MODE_MOUSE:
4533d522f475Smrg#if OPT_SCO_FUNC_KEYS
45340bd37d32Smrg	if (xw->keyboard.type == keyboardIsSCO) {
45350bd37d32Smrg	    /*
45360bd37d32Smrg	     * SCO function key F1 is \E[M, which would conflict with xterm's
45370bd37d32Smrg	     * normal kmous.
45380bd37d32Smrg	     */
45390bd37d32Smrg	    line[count++] = '>';
45400bd37d32Smrg	}
4541d522f475Smrg#endif
45420bd37d32Smrg	line[count++] = final;
45430bd37d32Smrg	break;
45440bd37d32Smrg    case SET_SGR_EXT_MODE_MOUSE:
45450bd37d32Smrg	line[count++] = '<';
45460bd37d32Smrg	break;
45470bd37d32Smrg    }
4548d522f475Smrg
4549d522f475Smrg    /* Add event code to key sequence */
4550d522f475Smrg    if (screen->send_mouse_pos == X10_MOUSE) {
45510bd37d32Smrg	count = EMIT_BUTTON(button);
4552d522f475Smrg    } else {
4553d522f475Smrg	/* Button-Motion events */
4554d522f475Smrg	switch (event->type) {
4555d522f475Smrg	case ButtonPress:
45560bd37d32Smrg	    screen->mouse_button |= ButtonBit(button);
45570bd37d32Smrg	    count = EMIT_BUTTON(button);
4558d522f475Smrg	    break;
4559d522f475Smrg	case ButtonRelease:
4560d522f475Smrg	    /*
4561d522f475Smrg	     * Wheel mouse interface generates release-events for buttons
4562d522f475Smrg	     * 4 and 5, coded here as 3 and 4 respectively.  We change the
45630bd37d32Smrg	     * release for buttons 1..3 to a -1, which will be later mapped
45640bd37d32Smrg	     * into a "0" (some button was released).
4565d522f475Smrg	     */
45660bd37d32Smrg	    screen->mouse_button &= ~ButtonBit(button);
45670bd37d32Smrg	    if (button < 3) {
45680bd37d32Smrg		switch (screen->extend_coords) {
45690bd37d32Smrg		case SET_SGR_EXT_MODE_MOUSE:
45700bd37d32Smrg		    final = 'm';
45710bd37d32Smrg		    break;
45720bd37d32Smrg		default:
45730bd37d32Smrg		    button = -1;
45740bd37d32Smrg		    break;
45750bd37d32Smrg		}
45760bd37d32Smrg	    }
45770bd37d32Smrg	    count = EMIT_BUTTON(button);
4578d522f475Smrg	    break;
4579d522f475Smrg	case MotionNotify:
4580d522f475Smrg	    /* BTN_EVENT_MOUSE and ANY_EVENT_MOUSE modes send motion
4581d522f475Smrg	     * events only if character cell has changed.
4582d522f475Smrg	     */
4583d522f475Smrg	    if ((row == screen->mouse_row)
4584d522f475Smrg		&& (col == screen->mouse_col)) {
4585d522f475Smrg		changed = False;
4586d522f475Smrg	    } else {
45870bd37d32Smrg		count = EMIT_BUTTON(FirstBitN(screen->mouse_button));
4588d522f475Smrg	    }
4589d522f475Smrg	    break;
4590d522f475Smrg	default:
4591d522f475Smrg	    changed = False;
4592d522f475Smrg	    break;
4593d522f475Smrg	}
4594d522f475Smrg    }
4595d522f475Smrg
4596d522f475Smrg    if (changed) {
4597d522f475Smrg	screen->mouse_row = row;
4598d522f475Smrg	screen->mouse_col = col;
4599d522f475Smrg
4600492d43a5Smrg	TRACE(("mouse at %d,%d button+mask = %#x\n", row, col, line[count - 1]));
4601d522f475Smrg
4602492d43a5Smrg	/* Add pointer position to key sequence */
46030bd37d32Smrg	count = EmitMousePositionSeparator(screen, line, count);
4604492d43a5Smrg	count = EmitMousePosition(screen, line, count, col);
46050bd37d32Smrg	count = EmitMousePositionSeparator(screen, line, count);
4606492d43a5Smrg	count = EmitMousePosition(screen, line, count, row);
4607d522f475Smrg
46080bd37d32Smrg	switch (screen->extend_coords) {
46090bd37d32Smrg	case SET_SGR_EXT_MODE_MOUSE:
46100bd37d32Smrg	case SET_URXVT_EXT_MODE_MOUSE:
46110bd37d32Smrg	    line[count++] = final;
46120bd37d32Smrg	    break;
46130bd37d32Smrg	}
46140bd37d32Smrg
4615d522f475Smrg	/* Transmit key sequence to process running under xterm */
4616d522f475Smrg	v_write(pty, line, count);
4617d522f475Smrg    }
4618d522f475Smrg    return;
4619d522f475Smrg}
4620d522f475Smrg
4621d522f475Smrg#if OPT_FOCUS_EVENT
4622d522f475Smrgvoid
4623894e0ac8SmrgSendFocusButton(XtermWidget xw, XFocusChangeEvent *event)
4624d522f475Smrg{
4625956cc18dSsnj    TScreen *screen = TScreenOf(xw);
4626d522f475Smrg
4627d522f475Smrg    if (screen->send_focus_pos) {
4628d522f475Smrg	ANSI reply;
4629d522f475Smrg
4630d522f475Smrg	memset(&reply, 0, sizeof(reply));
4631d522f475Smrg	reply.a_type = ANSI_CSI;
4632d522f475Smrg
4633d522f475Smrg#if OPT_SCO_FUNC_KEYS
4634d522f475Smrg	if (xw->keyboard.type == keyboardIsSCO) {
4635d522f475Smrg	    reply.a_pintro = '>';
4636d522f475Smrg	}
4637d522f475Smrg#endif
46382eaa94a1Schristos	reply.a_final = CharOf((event->type == FocusIn) ? 'I' : 'O');
4639d522f475Smrg	unparseseq(xw, &reply);
4640d522f475Smrg    }
4641d522f475Smrg    return;
4642d522f475Smrg}
4643d522f475Smrg#endif /* OPT_FOCUS_EVENT */
46440bd37d32Smrg
46450bd37d32Smrg#if OPT_SELECTION_OPS
46460bd37d32Smrg/*
46470bd37d32Smrg * Get the event-time, needed to process selections.
46480bd37d32Smrg */
46490bd37d32Smrgstatic Time
4650894e0ac8SmrggetEventTime(XEvent *event)
46510bd37d32Smrg{
46520bd37d32Smrg    Time result;
46530bd37d32Smrg
46540bd37d32Smrg    if (IsBtnEvent(event)) {
46550bd37d32Smrg	result = ((XButtonEvent *) event)->time;
46560bd37d32Smrg    } else if (IsKeyEvent(event)) {
46570bd37d32Smrg	result = ((XKeyEvent *) event)->time;
46580bd37d32Smrg    } else {
46590bd37d32Smrg	result = 0;
46600bd37d32Smrg    }
46610bd37d32Smrg
46620bd37d32Smrg    return result;
46630bd37d32Smrg}
46640bd37d32Smrg
46650bd37d32Smrg/* obtain the selection string, passing the endpoints to caller's parameters */
466601037d57Smrgstatic void
466701037d57SmrgdoSelectionFormat(XtermWidget xw,
466801037d57Smrg		  Widget w,
466901037d57Smrg		  XEvent *event,
467001037d57Smrg		  String *params,
467101037d57Smrg		  Cardinal *num_params,
467201037d57Smrg		  FormatSelect format_select)
46730bd37d32Smrg{
46740bd37d32Smrg    TScreen *screen = TScreenOf(xw);
467501037d57Smrg    InternalSelect *mydata = &(screen->internal_select);
467601037d57Smrg
467701037d57Smrg    memset(mydata, 0, sizeof(*mydata));
467801037d57Smrg    mydata->format = x_strdup(params[0]);
467901037d57Smrg    mydata->format_select = format_select;
46800bd37d32Smrg
46810bd37d32Smrg    /* override flags so that SelectionReceived only updates a buffer */
46820bd37d32Smrg#if OPT_PASTE64
468301037d57Smrg    mydata->base64_paste = screen->base64_paste;
46840bd37d32Smrg    screen->base64_paste = 0;
46850bd37d32Smrg#endif
46860bd37d32Smrg#if OPT_READLINE
468701037d57Smrg    mydata->paste_brackets = screen->paste_brackets;
46880bd37d32Smrg    SCREEN_FLAG_unset(screen, paste_brackets);
46890bd37d32Smrg#endif
46900bd37d32Smrg
46910bd37d32Smrg    screen->selectToBuffer = True;
46920bd37d32Smrg    xtermGetSelection(w, getEventTime(event), params + 1, *num_params - 1, NULL);
46930bd37d32Smrg}
46940bd37d32Smrg
46950bd37d32Smrg/* obtain data from the screen, passing the endpoints to caller's parameters */
46960bd37d32Smrgstatic char *
4697e0a2b6dfSmrggetDataFromScreen(XtermWidget xw, String method, CELL *start, CELL *finish)
46980bd37d32Smrg{
46990bd37d32Smrg    TScreen *screen = TScreenOf(xw);
47000bd37d32Smrg
47010bd37d32Smrg    CELL save_old_start = screen->startH;
47020bd37d32Smrg    CELL save_old_end = screen->endH;
47030bd37d32Smrg
47040bd37d32Smrg    CELL save_startSel = screen->startSel;
47050bd37d32Smrg    CELL save_startRaw = screen->startRaw;
47060bd37d32Smrg    CELL save_finishSel = screen->endSel;
47070bd37d32Smrg    CELL save_finishRaw = screen->endRaw;
47080bd37d32Smrg
47090bd37d32Smrg    int save_firstValidRow = screen->firstValidRow;
47100bd37d32Smrg    int save_lastValidRow = screen->lastValidRow;
47110bd37d32Smrg
471201037d57Smrg    const Cardinal noClick = 0;
471301037d57Smrg    int save_numberOfClicks = screen->numberOfClicks;
471401037d57Smrg
47150bd37d32Smrg    SelectUnit saveUnits = screen->selectUnit;
471601037d57Smrg    SelectUnit saveMap = screen->selectMap[noClick];
47170bd37d32Smrg#if OPT_SELECT_REGEX
471801037d57Smrg    char *saveExpr = screen->selectExpr[noClick];
47190bd37d32Smrg#endif
47200bd37d32Smrg
47210bd37d32Smrg    Char *save_selection_data = screen->selection_data;
47220bd37d32Smrg    int save_selection_size = screen->selection_size;
47230bd37d32Smrg    unsigned long save_selection_length = screen->selection_length;
47240bd37d32Smrg
47250bd37d32Smrg    char *result = 0;
47260bd37d32Smrg
47270bd37d32Smrg    TRACE(("getDataFromScreen %s\n", method));
47280bd37d32Smrg
47290bd37d32Smrg    screen->selection_data = 0;
47300bd37d32Smrg    screen->selection_size = 0;
47310bd37d32Smrg    screen->selection_length = 0;
47320bd37d32Smrg
473301037d57Smrg    screen->numberOfClicks = 1;
473401037d57Smrg    lookupSelectUnit(xw, noClick, method);
473501037d57Smrg    screen->selectUnit = screen->selectMap[noClick];
47360bd37d32Smrg
47370bd37d32Smrg    memset(start, 0, sizeof(*start));
47380bd37d32Smrg    start->row = screen->cur_row;
47390bd37d32Smrg    start->col = screen->cur_col;
474001037d57Smrg    finish->row = screen->cur_row;
474101037d57Smrg    finish->col = screen->max_col;
47420bd37d32Smrg
47430bd37d32Smrg    ComputeSelect(xw, start, finish, False);
47440bd37d32Smrg    SaltTextAway(xw, &(screen->startSel), &(screen->endSel));
47450bd37d32Smrg
47460bd37d32Smrg    if (screen->selection_length && screen->selection_data) {
47470bd37d32Smrg	TRACE(("...getDataFromScreen selection_data %.*s\n",
47480bd37d32Smrg	       (int) screen->selection_length,
47490bd37d32Smrg	       screen->selection_data));
47500bd37d32Smrg	result = malloc(screen->selection_length + 1);
47510bd37d32Smrg	if (result) {
47520bd37d32Smrg	    memcpy(result, screen->selection_data, screen->selection_length);
47530bd37d32Smrg	    result[screen->selection_length] = 0;
47540bd37d32Smrg	}
47550bd37d32Smrg	free(screen->selection_data);
47560bd37d32Smrg    }
47570bd37d32Smrg
47580bd37d32Smrg    TRACE(("...getDataFromScreen restoring previous selection\n"));
47590bd37d32Smrg
47600bd37d32Smrg    screen->startSel = save_startSel;
47610bd37d32Smrg    screen->startRaw = save_startRaw;
47620bd37d32Smrg    screen->endSel = save_finishSel;
47630bd37d32Smrg    screen->endRaw = save_finishRaw;
47640bd37d32Smrg
47650bd37d32Smrg    screen->firstValidRow = save_firstValidRow;
47660bd37d32Smrg    screen->lastValidRow = save_lastValidRow;
47670bd37d32Smrg
476801037d57Smrg    screen->numberOfClicks = save_numberOfClicks;
47690bd37d32Smrg    screen->selectUnit = saveUnits;
477001037d57Smrg    screen->selectMap[noClick] = saveMap;
47710bd37d32Smrg#if OPT_SELECT_REGEX
477201037d57Smrg    screen->selectExpr[noClick] = saveExpr;
47730bd37d32Smrg#endif
47740bd37d32Smrg
47750bd37d32Smrg    screen->selection_data = save_selection_data;
47760bd37d32Smrg    screen->selection_size = save_selection_size;
47770bd37d32Smrg    screen->selection_length = save_selection_length;
47780bd37d32Smrg
47790bd37d32Smrg    TrackText(xw, &save_old_start, &save_old_end);
47800bd37d32Smrg
47810bd37d32Smrg    TRACE(("...getDataFromScreen done\n"));
47820bd37d32Smrg    return result;
47830bd37d32Smrg}
47840bd37d32Smrg
47850bd37d32Smrg/*
47860bd37d32Smrg * Split-up the format before substituting data, to avoid quoting issues.
47870bd37d32Smrg * The resource mechanism has a limited ability to handle escapes.  We take
47880bd37d32Smrg * the result as if it were an sh-type string and parse it into a regular
47890bd37d32Smrg * argv array.
47900bd37d32Smrg */
47910bd37d32Smrgstatic char **
47920bd37d32SmrgtokenizeFormat(String format)
47930bd37d32Smrg{
47940bd37d32Smrg    char **result = 0;
47950bd37d32Smrg    int pass;
47960bd37d32Smrg    int argc;
47970bd37d32Smrg    int n;
47980bd37d32Smrg
47990bd37d32Smrg    format = x_skip_blanks(format);
48000bd37d32Smrg    if (*format != '\0') {
48010bd37d32Smrg	char *blob = x_strdup(format);
48020bd37d32Smrg
48030bd37d32Smrg	for (pass = 0; pass < 2; ++pass) {
48040bd37d32Smrg	    int used = 0;
48050bd37d32Smrg	    int first = 1;
48060bd37d32Smrg	    int escaped = 0;
48070bd37d32Smrg	    int squoted = 0;
48080bd37d32Smrg	    int dquoted = 0;
48090bd37d32Smrg
48100bd37d32Smrg	    argc = 0;
48110bd37d32Smrg	    for (n = 0; format[n] != '\0'; ++n) {
48120bd37d32Smrg		if (escaped) {
48130bd37d32Smrg		    blob[used++] = format[n];
48140bd37d32Smrg		    escaped = 0;
48150bd37d32Smrg		} else if (format[n] == '"') {
48160bd37d32Smrg		    if (!squoted) {
48170bd37d32Smrg			if (!dquoted)
48180bd37d32Smrg			    blob[used++] = format[n];
48190bd37d32Smrg			dquoted = !dquoted;
48200bd37d32Smrg		    }
48210bd37d32Smrg		} else if (format[n] == '\'') {
48220bd37d32Smrg		    if (!dquoted) {
48230bd37d32Smrg			if (!squoted)
48240bd37d32Smrg			    blob[used++] = format[n];
48250bd37d32Smrg			squoted = !squoted;
48260bd37d32Smrg		    }
48270bd37d32Smrg		} else if (format[n] == '\\') {
48280bd37d32Smrg		    blob[used++] = format[n];
48290bd37d32Smrg		    escaped = 1;
48300bd37d32Smrg		} else {
48310bd37d32Smrg		    if (first) {
48320bd37d32Smrg			first = 0;
48330bd37d32Smrg			if (pass) {
48340bd37d32Smrg			    result[argc] = &blob[n];
48350bd37d32Smrg			}
48360bd37d32Smrg			++argc;
48370bd37d32Smrg		    }
48380bd37d32Smrg		    if (isspace((Char) format[n])) {
48390bd37d32Smrg			first = !isspace((Char) format[n + 1]);
48400bd37d32Smrg			if (squoted || dquoted) {
48410bd37d32Smrg			    blob[used++] = format[n];
48420bd37d32Smrg			} else if (first) {
48430bd37d32Smrg			    blob[used++] = '\0';
48440bd37d32Smrg			}
48450bd37d32Smrg		    } else {
48460bd37d32Smrg			blob[used++] = format[n];
48470bd37d32Smrg		    }
48480bd37d32Smrg		}
48490bd37d32Smrg	    }
48500bd37d32Smrg	    blob[used] = '\0';
48510bd37d32Smrg	    assert(strlen(blob) <= strlen(format));
48520bd37d32Smrg	    if (!pass) {
48530bd37d32Smrg		result = TypeCallocN(char *, argc + 1);
48540bd37d32Smrg		if (result == 0) {
48550bd37d32Smrg		    free(blob);
48560bd37d32Smrg		    break;
48570bd37d32Smrg		}
48580bd37d32Smrg	    }
48590bd37d32Smrg	}
48600bd37d32Smrg    }
48610bd37d32Smrg#if OPT_TRACE
48620bd37d32Smrg    if (result) {
48630bd37d32Smrg	TRACE(("tokenizeFormat %s\n", format));
48640bd37d32Smrg	for (argc = 0; result[argc]; ++argc) {
48650bd37d32Smrg	    TRACE(("argv[%d] = %s\n", argc, result[argc]));
48660bd37d32Smrg	}
48670bd37d32Smrg    }
48680bd37d32Smrg#endif
48690bd37d32Smrg
48700bd37d32Smrg    return result;
48710bd37d32Smrg}
48720bd37d32Smrg
48730bd37d32Smrgstatic void
4874e0a2b6dfSmrgformatVideoAttrs(XtermWidget xw, char *buffer, CELL *cell)
48750bd37d32Smrg{
48760bd37d32Smrg    TScreen *screen = TScreenOf(xw);
48770bd37d32Smrg    LineData *ld = GET_LINEDATA(screen, cell->row);
48780bd37d32Smrg
48790bd37d32Smrg    *buffer = '\0';
48800bd37d32Smrg    if (ld != 0 && cell->col < (int) ld->lineSize) {
4881894e0ac8Smrg	IAttr attribs = ld->attribs[cell->col];
48820bd37d32Smrg	const char *delim = "";
48830bd37d32Smrg
48840bd37d32Smrg	if (attribs & INVERSE) {
48850bd37d32Smrg	    buffer += sprintf(buffer, "7");
48860bd37d32Smrg	    delim = ";";
48870bd37d32Smrg	}
48880bd37d32Smrg	if (attribs & UNDERLINE) {
48890bd37d32Smrg	    buffer += sprintf(buffer, "%s4", delim);
48900bd37d32Smrg	    delim = ";";
48910bd37d32Smrg	}
48920bd37d32Smrg	if (attribs & BOLD) {
48930bd37d32Smrg	    buffer += sprintf(buffer, "%s1", delim);
48940bd37d32Smrg	    delim = ";";
48950bd37d32Smrg	}
48960bd37d32Smrg	if (attribs & BLINK) {
48970bd37d32Smrg	    buffer += sprintf(buffer, "%s5", delim);
48980bd37d32Smrg	    delim = ";";
48990bd37d32Smrg	}
49000bd37d32Smrg#if OPT_ISO_COLORS
49010bd37d32Smrg	if (attribs & FG_COLOR) {
49020bd37d32Smrg	    unsigned fg = extract_fg(xw, ld->color[cell->col], attribs);
49030bd37d32Smrg	    if (fg < 8) {
49040bd37d32Smrg		fg += 30;
49050bd37d32Smrg	    } else if (fg < 16) {
49060bd37d32Smrg		fg += 90;
49070bd37d32Smrg	    } else {
49080bd37d32Smrg		buffer += sprintf(buffer, "%s38;5", delim);
49090bd37d32Smrg		delim = ";";
49100bd37d32Smrg	    }
49110bd37d32Smrg	    buffer += sprintf(buffer, "%s%u", delim, fg);
49120bd37d32Smrg	    delim = ";";
49130bd37d32Smrg	}
49140bd37d32Smrg	if (attribs & BG_COLOR) {
49150bd37d32Smrg	    unsigned bg = extract_bg(xw, ld->color[cell->col], attribs);
49160bd37d32Smrg	    if (bg < 8) {
49170bd37d32Smrg		bg += 40;
49180bd37d32Smrg	    } else if (bg < 16) {
49190bd37d32Smrg		bg += 100;
49200bd37d32Smrg	    } else {
49210bd37d32Smrg		buffer += sprintf(buffer, "%s48;5", delim);
49220bd37d32Smrg		delim = ";";
49230bd37d32Smrg	    }
49240bd37d32Smrg	    (void) sprintf(buffer, "%s%u", delim, bg);
49250bd37d32Smrg	}
49260bd37d32Smrg#endif
49270bd37d32Smrg    }
49280bd37d32Smrg}
49290bd37d32Smrg
49300bd37d32Smrg/* substitute data into format, reallocating the result */
49310bd37d32Smrgstatic char *
49320bd37d32SmrgexpandFormat(XtermWidget xw,
49330bd37d32Smrg	     const char *format,
49340bd37d32Smrg	     char *data,
4935e0a2b6dfSmrg	     CELL *start,
4936e0a2b6dfSmrg	     CELL *finish)
49370bd37d32Smrg{
49380bd37d32Smrg    char *result = 0;
49390bd37d32Smrg    if (!IsEmpty(format)) {
49400bd37d32Smrg	static char empty[1];
49410bd37d32Smrg	int pass;
49420bd37d32Smrg	int n;
49430bd37d32Smrg	char numbers[80];
49440bd37d32Smrg
49450bd37d32Smrg	if (data == 0)
49460bd37d32Smrg	    data = empty;
49470bd37d32Smrg
49480bd37d32Smrg	for (pass = 0; pass < 2; ++pass) {
49490bd37d32Smrg	    size_t need = 0;
49500bd37d32Smrg
49510bd37d32Smrg	    for (n = 0; format[n] != '\0'; ++n) {
49520bd37d32Smrg		char *value = 0;
49530bd37d32Smrg
49540bd37d32Smrg		if (format[n] == '%') {
49550bd37d32Smrg		    switch (format[++n]) {
49560bd37d32Smrg		    case '%':
49570bd37d32Smrg			if (pass) {
49580bd37d32Smrg			    result[need] = format[n];
49590bd37d32Smrg			}
49600bd37d32Smrg			++need;
49610bd37d32Smrg			break;
49620bd37d32Smrg		    case 'P':
49630bd37d32Smrg			sprintf(numbers, "%d;%d",
49640bd37d32Smrg				TScreenOf(xw)->topline + start->row + 1,
49650bd37d32Smrg				start->col + 1);
49660bd37d32Smrg			value = numbers;
49670bd37d32Smrg			break;
49680bd37d32Smrg		    case 'p':
49690bd37d32Smrg			sprintf(numbers, "%d;%d",
49700bd37d32Smrg				TScreenOf(xw)->topline + finish->row + 1,
49710bd37d32Smrg				finish->col + 1);
49720bd37d32Smrg			value = numbers;
49730bd37d32Smrg			break;
49740bd37d32Smrg		    case 'S':
49750bd37d32Smrg			sprintf(numbers, "%u", (unsigned) strlen(data));
49760bd37d32Smrg			value = numbers;
49770bd37d32Smrg			break;
49780bd37d32Smrg		    case 's':
49790bd37d32Smrg			value = data;
49800bd37d32Smrg			break;
49810bd37d32Smrg		    case 'T':
49820bd37d32Smrg			if ((value = x_strtrim(data)) != 0) {
49830bd37d32Smrg			    sprintf(numbers, "%u", (unsigned) strlen(value));
49840bd37d32Smrg			    free(value);
49850bd37d32Smrg			} else {
49860bd37d32Smrg			    strcpy(numbers, "0");
49870bd37d32Smrg			}
49880bd37d32Smrg			value = numbers;
49890bd37d32Smrg			break;
49900bd37d32Smrg		    case 't':
49910bd37d32Smrg			value = x_strtrim(data);
49920bd37d32Smrg			break;
49930bd37d32Smrg		    case 'V':
49940bd37d32Smrg			formatVideoAttrs(xw, numbers, start);
49950bd37d32Smrg			value = numbers;
49960bd37d32Smrg			break;
49970bd37d32Smrg		    case 'v':
49980bd37d32Smrg			formatVideoAttrs(xw, numbers, finish);
49990bd37d32Smrg			value = numbers;
50000bd37d32Smrg			break;
50010bd37d32Smrg		    default:
50020bd37d32Smrg			if (pass) {
50030bd37d32Smrg			    result[need] = format[n];
50040bd37d32Smrg			}
50050bd37d32Smrg			--n;
50060bd37d32Smrg			++need;
50070bd37d32Smrg			break;
50080bd37d32Smrg		    }
50090bd37d32Smrg		    if (value != 0) {
50100bd37d32Smrg			if (pass) {
50110bd37d32Smrg			    strcpy(result + need, value);
50120bd37d32Smrg			}
50130bd37d32Smrg			need += strlen(value);
50140bd37d32Smrg			if (value != numbers && value != data) {
50150bd37d32Smrg			    free(value);
50160bd37d32Smrg			}
50170bd37d32Smrg		    }
50180bd37d32Smrg		} else {
50190bd37d32Smrg		    if (pass) {
50200bd37d32Smrg			result[need] = format[n];
50210bd37d32Smrg		    }
50220bd37d32Smrg		    ++need;
50230bd37d32Smrg		}
50240bd37d32Smrg	    }
50250bd37d32Smrg	    if (pass) {
50260bd37d32Smrg		result[need] = '\0';
50270bd37d32Smrg	    } else {
50280bd37d32Smrg		++need;
50290bd37d32Smrg		result = malloc(need);
50300bd37d32Smrg		if (result == 0) {
50310bd37d32Smrg		    break;
50320bd37d32Smrg		}
50330bd37d32Smrg	    }
50340bd37d32Smrg	}
50350bd37d32Smrg    }
50360bd37d32Smrg    TRACE(("expandFormat(%s) = %s\n", NonNull(format), NonNull(result)));
50370bd37d32Smrg    return result;
50380bd37d32Smrg}
50390bd37d32Smrg
50400bd37d32Smrg/* execute the command after forking.  The main process frees its data */
50410bd37d32Smrgstatic void
50420bd37d32SmrgexecuteCommand(char **argv)
50430bd37d32Smrg{
50440bd37d32Smrg    if (argv != 0 && argv[0] != 0) {
50450bd37d32Smrg	if (fork() == 0) {
50460bd37d32Smrg	    execvp(argv[0], argv);
50470bd37d32Smrg	    exit(EXIT_FAILURE);
50480bd37d32Smrg	}
50490bd37d32Smrg    }
50500bd37d32Smrg}
50510bd37d32Smrg
50520bd37d32Smrgstatic void
50530bd37d32SmrgfreeArgv(char *blob, char **argv)
50540bd37d32Smrg{
50550bd37d32Smrg    int n;
50560bd37d32Smrg
50570bd37d32Smrg    if (blob) {
50580bd37d32Smrg	free(blob);
50590bd37d32Smrg	if (argv) {
50600bd37d32Smrg	    for (n = 0; argv[n]; ++n)
50610bd37d32Smrg		free(argv[n]);
50620bd37d32Smrg	    free(argv);
50630bd37d32Smrg	}
50640bd37d32Smrg    }
50650bd37d32Smrg}
50660bd37d32Smrg
506701037d57Smrgstatic void
506801037d57SmrgreallyExecFormatted(Widget w, char *format, char *data, CELL *start, CELL *finish)
506901037d57Smrg{
507001037d57Smrg    XtermWidget xw;
507101037d57Smrg
507201037d57Smrg    if ((xw = getXtermWidget(w)) != 0) {
507301037d57Smrg	char **argv;
507401037d57Smrg	char *blob;
507501037d57Smrg	int argc;
507601037d57Smrg
507701037d57Smrg	if ((argv = tokenizeFormat(format)) != 0) {
507801037d57Smrg	    blob = argv[0];
507901037d57Smrg	    for (argc = 0; argv[argc] != 0; ++argc) {
508001037d57Smrg		argv[argc] = expandFormat(xw, argv[argc], data, start, finish);
508101037d57Smrg	    }
508201037d57Smrg	    executeCommand(argv);
508301037d57Smrg	    freeArgv(blob, argv);
508401037d57Smrg	}
508501037d57Smrg    }
508601037d57Smrg}
508701037d57Smrg
50880bd37d32Smrgvoid
50890bd37d32SmrgHandleExecFormatted(Widget w,
509001037d57Smrg		    XEvent *event,
5091e0a2b6dfSmrg		    String *params,	/* selections */
50920bd37d32Smrg		    Cardinal *num_params)
50930bd37d32Smrg{
50940bd37d32Smrg    XtermWidget xw;
50950bd37d32Smrg
509601037d57Smrg    TRACE(("HandleExecFormatted(%d)\n", *num_params));
509701037d57Smrg    if ((xw = getXtermWidget(w)) != 0 &&
509801037d57Smrg	(*num_params > 1)) {
509901037d57Smrg	doSelectionFormat(xw, w, event, params, num_params, reallyExecFormatted);
51000bd37d32Smrg    }
51010bd37d32Smrg}
51020bd37d32Smrg
51030bd37d32Smrgvoid
51040bd37d32SmrgHandleExecSelectable(Widget w,
5105894e0ac8Smrg		     XEvent *event GCC_UNUSED,
5106e0a2b6dfSmrg		     String *params,	/* selections */
51070bd37d32Smrg		     Cardinal *num_params)
51080bd37d32Smrg{
51090bd37d32Smrg    XtermWidget xw;
51100bd37d32Smrg
51110bd37d32Smrg    if ((xw = getXtermWidget(w)) != 0) {
51120bd37d32Smrg	TRACE(("HandleExecSelectable(%d)\n", *num_params));
51130bd37d32Smrg
51140bd37d32Smrg	if (*num_params == 2) {
51150bd37d32Smrg	    CELL start, finish;
51160bd37d32Smrg	    char *data;
51170bd37d32Smrg	    char **argv;
51180bd37d32Smrg	    char *blob;
51190bd37d32Smrg	    int argc;
51200bd37d32Smrg
51210bd37d32Smrg	    data = getDataFromScreen(xw, params[1], &start, &finish);
51220bd37d32Smrg	    if (data != 0) {
51230bd37d32Smrg		if ((argv = tokenizeFormat(params[0])) != 0) {
51240bd37d32Smrg		    blob = argv[0];
51250bd37d32Smrg		    for (argc = 0; argv[argc] != 0; ++argc) {
51260bd37d32Smrg			argv[argc] = expandFormat(xw, argv[argc], data,
51270bd37d32Smrg						  &start, &finish);
51280bd37d32Smrg		    }
51290bd37d32Smrg		    executeCommand(argv);
51300bd37d32Smrg		    freeArgv(blob, argv);
51310bd37d32Smrg		}
5132894e0ac8Smrg		free(data);
51330bd37d32Smrg	    }
51340bd37d32Smrg	}
51350bd37d32Smrg    }
51360bd37d32Smrg}
51370bd37d32Smrg
513801037d57Smrgstatic void
513901037d57SmrgreallyInsertFormatted(Widget w, char *format, char *data, CELL *start, CELL *finish)
514001037d57Smrg{
514101037d57Smrg    XtermWidget xw;
514201037d57Smrg
514301037d57Smrg    if ((xw = getXtermWidget(w)) != 0) {
514401037d57Smrg	char *exps;
514501037d57Smrg
514601037d57Smrg	if ((exps = expandFormat(xw, format, data, start, finish)) != 0) {
514701037d57Smrg	    unparseputs(xw, exps);
514801037d57Smrg	    unparse_end(xw);
514901037d57Smrg	    free(exps);
515001037d57Smrg	}
515101037d57Smrg    }
515201037d57Smrg}
515301037d57Smrg
51540bd37d32Smrgvoid
51550bd37d32SmrgHandleInsertFormatted(Widget w,
515601037d57Smrg		      XEvent *event,
5157e0a2b6dfSmrg		      String *params,	/* selections */
51580bd37d32Smrg		      Cardinal *num_params)
51590bd37d32Smrg{
51600bd37d32Smrg    XtermWidget xw;
51610bd37d32Smrg
516201037d57Smrg    TRACE(("HandleInsertFormatted(%d)\n", *num_params));
516301037d57Smrg    if ((xw = getXtermWidget(w)) != 0 &&
516401037d57Smrg	(*num_params > 1)) {
516501037d57Smrg	doSelectionFormat(xw, w, event, params, num_params, reallyInsertFormatted);
51660bd37d32Smrg    }
51670bd37d32Smrg}
51680bd37d32Smrg
51690bd37d32Smrgvoid
51700bd37d32SmrgHandleInsertSelectable(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(("HandleInsertSelectable(%d)\n", *num_params));
51790bd37d32Smrg
51800bd37d32Smrg	if (*num_params == 2) {
51810bd37d32Smrg	    CELL start, finish;
51820bd37d32Smrg	    char *data;
51830bd37d32Smrg	    char *temp = x_strdup(params[0]);
51840bd37d32Smrg	    char *exps;
51850bd37d32Smrg
51860bd37d32Smrg	    data = getDataFromScreen(xw, params[1], &start, &finish);
51870bd37d32Smrg	    if (data != 0) {
51880bd37d32Smrg		exps = expandFormat(xw, temp, data, &start, &finish);
51890bd37d32Smrg		if (exps != 0) {
51900bd37d32Smrg		    unparseputs(xw, exps);
519101037d57Smrg		    unparse_end(xw);
51920bd37d32Smrg		    free(exps);
51930bd37d32Smrg		}
51940bd37d32Smrg		free(data);
51950bd37d32Smrg	    }
51960bd37d32Smrg	    free(temp);
51970bd37d32Smrg	}
51980bd37d32Smrg    }
51990bd37d32Smrg}
52000bd37d32Smrg#endif /* OPT_SELECTION_OPS */
5201