button.c revision e39b573c
1e39b573cSmrg/* $XTermId: button.c,v 1.398 2011/07/04 21:49:39 tom Exp $ */
2d522f475Smrg
3d522f475Smrg/*
4a1f3da82Smrg * Copyright 1999-2010,2011 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>
6520d2c4d2Smrg#include <assert.h>
66d522f475Smrg
67d522f475Smrg#include <X11/Xatom.h>
68d522f475Smrg#include <X11/Xmu/Atoms.h>
69d522f475Smrg#include <X11/Xmu/StdSel.h>
70d522f475Smrg
71d522f475Smrg#include <xutf8.h>
72d522f475Smrg#include <fontutils.h>
73d522f475Smrg
74d522f475Smrg#include <data.h>
75d522f475Smrg#include <error.h>
76d522f475Smrg#include <menu.h>
77d522f475Smrg#include <xcharmouse.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)
122d522f475Smrg
1232eaa94a1Schristos#define KeyState(x) (((int) ((x) & (ShiftMask|ControlMask))) \
1242eaa94a1Schristos			  + (((x) & Mod1Mask) ? 2 : 0))
125d522f475Smrg    /* adds together the bits:
126d522f475Smrg       shift key -> 1
127d522f475Smrg       meta key  -> 2
128d522f475Smrg       control key -> 4 */
129d522f475Smrg
130d522f475Smrg#define	Coordinate(s,c)	((c)->row * MaxCols(s) + (c)->col)
131d522f475Smrg
132d522f475Smrgstatic const CELL zeroCELL =
133d522f475Smrg{0, 0};
134d522f475Smrg
135d522f475Smrg#if OPT_DEC_LOCATOR
136492d43a5Smrgstatic Bool SendLocatorPosition(XtermWidget xw, XButtonEvent * event);
137492d43a5Smrgstatic void CheckLocatorPosition(XtermWidget xw, XButtonEvent * event);
138d522f475Smrg#endif /* OPT_DEC_LOCATOR */
139d522f475Smrg
140d522f475Smrg/* Multi-click handling */
141d522f475Smrg#if OPT_READLINE
142d522f475Smrgstatic Time lastButtonDownTime = 0;
143d522f475Smrgstatic int ExtendingSelection = 0;
144d522f475Smrgstatic Time lastButton3UpTime = 0;
145d522f475Smrgstatic Time lastButton3DoubleDownTime = 0;
146d522f475Smrgstatic CELL lastButton3;	/* At the release time */
147d522f475Smrg#endif /* OPT_READLINE */
148d522f475Smrg
149d522f475Smrgstatic Char *SaveText(TScreen * screen, int row, int scol, int ecol,
150d522f475Smrg		      Char * lp, int *eol);
151d522f475Smrgstatic int Length(TScreen * screen, int row, int scol, int ecol);
152d522f475Smrgstatic void ComputeSelect(XtermWidget xw, CELL * startc, CELL * endc, Bool extend);
153d522f475Smrgstatic void EditorButton(XtermWidget xw, XButtonEvent * event);
154d522f475Smrgstatic void EndExtend(XtermWidget w, XEvent * event, String * params, Cardinal
155d522f475Smrg		      num_params, Bool use_cursor_loc);
156d522f475Smrgstatic void ExtendExtend(XtermWidget xw, const CELL * cell);
157d522f475Smrgstatic void PointToCELL(TScreen * screen, int y, int x, CELL * cell);
158d522f475Smrgstatic void ReHiliteText(XtermWidget xw, CELL * first, CELL * last);
159d522f475Smrgstatic void SaltTextAway(XtermWidget xw, CELL * cellc, CELL * cell,
160d522f475Smrg			 String * params, Cardinal num_params);
161d522f475Smrgstatic void SelectSet(XtermWidget xw, XEvent * event, String * params, Cardinal num_params);
162d522f475Smrgstatic void SelectionReceived PROTO_XT_SEL_CB_ARGS;
163d522f475Smrgstatic void StartSelect(XtermWidget xw, const CELL * cell);
164d522f475Smrgstatic void TrackDown(XtermWidget xw, XButtonEvent * event);
165d522f475Smrgstatic void TrackText(XtermWidget xw, const CELL * first, const CELL * last);
166d522f475Smrgstatic void _OwnSelection(XtermWidget xw, String * selections, Cardinal count);
167d522f475Smrgstatic 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
176492d43a5Smrgstatic unsigned
177492d43a5SmrgEmitMousePosition(TScreen * screen, Char line[], unsigned count, int value)
178492d43a5Smrg{
179492d43a5Smrg    int mouse_limit = (screen->ext_mode_mouse
180492d43a5Smrg		       ? EXT_MOUSE_LIMIT
181492d43a5Smrg		       : MOUSE_LIMIT);
182492d43a5Smrg
183492d43a5Smrg    /*
184492d43a5Smrg     * Add pointer position to key sequence
185492d43a5Smrg     *
186492d43a5Smrg     * In extended mode we encode large positions as two-byte UTF-8.
187492d43a5Smrg     *
188492d43a5Smrg     * NOTE: historically, it was possible to emit 256, which became
189492d43a5Smrg     * zero by truncation to 8 bits. While this was arguably a bug,
190492d43a5Smrg     * it's also somewhat useful as a past-end marker. We preserve
191492d43a5Smrg     * this behavior for both normal and extended mouse modes.
192492d43a5Smrg     */
193492d43a5Smrg    if (value == mouse_limit) {
194492d43a5Smrg	line[count++] = CharOf(0);
195492d43a5Smrg    } else if (!screen->ext_mode_mouse || value < EXT_MOUSE_START) {
196492d43a5Smrg	line[count++] = CharOf(' ' + value + 1);
197492d43a5Smrg    } else {
198492d43a5Smrg	value += ' ' + 1;
199492d43a5Smrg	line[count++] = CharOf(0xC0 + (value >> 6));
200492d43a5Smrg	line[count++] = CharOf(0x80 + (value & 0x3F));
201d522f475Smrg    }
202492d43a5Smrg    return count;
203492d43a5Smrg}
204d522f475Smrg
205492d43a5SmrgBool
206492d43a5SmrgSendMousePosition(XtermWidget xw, XEvent * event)
207492d43a5Smrg{
208492d43a5Smrg    TScreen *screen = TScreenOf(xw);
209492d43a5Smrg    XButtonEvent *my_event = (XButtonEvent *) event;
210492d43a5Smrg    Bool result = False;
211d522f475Smrg
212d522f475Smrg    switch (screen->send_mouse_pos) {
213492d43a5Smrg    case MOUSE_OFF:
214492d43a5Smrg	/* If send_mouse_pos mode isn't on, we shouldn't be here */
215492d43a5Smrg	break;
216d522f475Smrg
217d522f475Smrg    case BTN_EVENT_MOUSE:
218d522f475Smrg    case ANY_EVENT_MOUSE:
219492d43a5Smrg	if (KeyModifiers(event) == 0 || KeyModifiers(event) == ControlMask) {
220492d43a5Smrg	    /* xterm extension for motion reporting. June 1998 */
221492d43a5Smrg	    /* EditorButton() will distinguish between the modes */
222492d43a5Smrg	    switch (event->type) {
223492d43a5Smrg	    case MotionNotify:
224492d43a5Smrg		my_event->button = 0;
225492d43a5Smrg		/* FALLTHRU */
226492d43a5Smrg	    case ButtonPress:
227492d43a5Smrg		/* FALLTHRU */
228492d43a5Smrg	    case ButtonRelease:
229492d43a5Smrg		EditorButton(xw, my_event);
230492d43a5Smrg		result = True;
231492d43a5Smrg		break;
2322eaa94a1Schristos	    }
233d522f475Smrg	}
234492d43a5Smrg	break;
235d522f475Smrg
236d522f475Smrg    default:
237492d43a5Smrg	/* Make sure the event is an appropriate type */
238492d43a5Smrg	if (IsBtnEvent(event)) {
239492d43a5Smrg	    switch (screen->send_mouse_pos) {
240492d43a5Smrg	    case X10_MOUSE:	/* X10 compatibility sequences */
241492d43a5Smrg
242492d43a5Smrg		if (BtnModifiers(my_event) == 0) {
243492d43a5Smrg		    if (my_event->type == ButtonPress)
244492d43a5Smrg			EditorButton(xw, my_event);
245492d43a5Smrg		    result = True;
246492d43a5Smrg		}
247492d43a5Smrg		break;
248492d43a5Smrg
249492d43a5Smrg	    case VT200_HIGHLIGHT_MOUSE:	/* DEC vt200 hilite tracking */
250492d43a5Smrg		if (my_event->type == ButtonPress &&
251492d43a5Smrg		    BtnModifiers(my_event) == 0 &&
252492d43a5Smrg		    my_event->button == Button1) {
253492d43a5Smrg		    TrackDown(xw, my_event);
254492d43a5Smrg		    result = True;
255492d43a5Smrg		} else if (BtnModifiers(my_event) == 0
256492d43a5Smrg			   || BtnModifiers(my_event) == ControlMask) {
257492d43a5Smrg		    EditorButton(xw, my_event);
258492d43a5Smrg		    result = True;
259492d43a5Smrg		}
260492d43a5Smrg		break;
261492d43a5Smrg
262492d43a5Smrg	    case VT200_MOUSE:	/* DEC vt200 compatible */
263492d43a5Smrg		if (BtnModifiers(my_event) == 0
264492d43a5Smrg		    || BtnModifiers(my_event) == ControlMask) {
265492d43a5Smrg		    EditorButton(xw, my_event);
266492d43a5Smrg		    result = True;
267492d43a5Smrg		}
268492d43a5Smrg		break;
269492d43a5Smrg
270492d43a5Smrg#if OPT_DEC_LOCATOR
271492d43a5Smrg	    case DEC_LOCATOR:
272492d43a5Smrg		result = SendLocatorPosition(xw, my_event);
273492d43a5Smrg		break;
274492d43a5Smrg#endif /* OPT_DEC_LOCATOR */
275492d43a5Smrg	    }
276492d43a5Smrg	}
277d522f475Smrg    }
278492d43a5Smrg    return result;
279d522f475Smrg}
280d522f475Smrg
281d522f475Smrg#if OPT_DEC_LOCATOR
282d522f475Smrg
283d522f475Smrg#define	LocatorCoords( row, col, x, y, oor )			\
284d522f475Smrg    if( screen->locator_pixels ) {				\
285d522f475Smrg	(oor)=False; (row) = (y)+1; (col) = (x)+1;		\
286d522f475Smrg	/* Limit to screen dimensions */			\
287d522f475Smrg	if ((row) < 1) (row) = 1,(oor)=True;			\
288d522f475Smrg	else if ((row) > screen->border*2+Height(screen))	\
289d522f475Smrg	    (row) = screen->border*2+Height(screen),(oor)=True;	\
290d522f475Smrg	if ((col) < 1) (col) = 1,(oor)=True;			\
291d522f475Smrg	else if ((col) > OriginX(screen)*2+Width(screen))	\
292d522f475Smrg	    (col) = OriginX(screen)*2+Width(screen),(oor)=True;	\
293d522f475Smrg    } else {							\
294d522f475Smrg	(oor)=False;						\
295d522f475Smrg	/* Compute character position of mouse pointer */	\
296d522f475Smrg	(row) = ((y) - screen->border) / FontHeight(screen);	\
297d522f475Smrg	(col) = ((x) - OriginX(screen)) / FontWidth(screen);	\
298d522f475Smrg	/* Limit to screen dimensions */			\
299d522f475Smrg	if ((row) < 0) (row) = 0,(oor)=True;			\
300d522f475Smrg	else if ((row) > screen->max_row)			\
301d522f475Smrg	    (row) = screen->max_row,(oor)=True;			\
302d522f475Smrg	if ((col) < 0) (col) = 0,(oor)=True;			\
303d522f475Smrg	else if ((col) > screen->max_col)			\
304d522f475Smrg	    (col) = screen->max_col,(oor)=True;			\
305d522f475Smrg	(row)++; (col)++;					\
306d522f475Smrg    }
307d522f475Smrg
308d522f475Smrgstatic Bool
309492d43a5SmrgSendLocatorPosition(XtermWidget xw, XButtonEvent * event)
310d522f475Smrg{
311d522f475Smrg    ANSI reply;
312956cc18dSsnj    TScreen *screen = TScreenOf(xw);
313d522f475Smrg    int row, col;
314d522f475Smrg    Bool oor;
315d522f475Smrg    int button;
3162eaa94a1Schristos    unsigned state;
317d522f475Smrg
318d522f475Smrg    /* Make sure the event is an appropriate type */
319492d43a5Smrg    if ((!IsBtnEvent(event) &&
320d522f475Smrg	 !screen->loc_filter) ||
321492d43a5Smrg	(BtnModifiers(event) != 0 && BtnModifiers(event) != ControlMask))
322d522f475Smrg	return (False);
323d522f475Smrg
324d522f475Smrg    if ((event->type == ButtonPress &&
325d522f475Smrg	 !(screen->locator_events & LOC_BTNS_DN)) ||
326d522f475Smrg	(event->type == ButtonRelease &&
327d522f475Smrg	 !(screen->locator_events & LOC_BTNS_UP)))
328d522f475Smrg	return (True);
329d522f475Smrg
330d522f475Smrg    if (event->type == MotionNotify) {
331d522f475Smrg	CheckLocatorPosition(xw, event);
332d522f475Smrg	return (True);
333d522f475Smrg    }
334d522f475Smrg
335d522f475Smrg    /* get button # */
336492d43a5Smrg    button = (int) event->button - 1;
337d522f475Smrg
338492d43a5Smrg    LocatorCoords(row, col, event->x, event->y, oor);
339d522f475Smrg
340d522f475Smrg    /*
341d522f475Smrg     * DECterm mouse:
342d522f475Smrg     *
343d522f475Smrg     * ESCAPE '[' event ; mask ; row ; column '&' 'w'
344d522f475Smrg     */
345d522f475Smrg    memset(&reply, 0, sizeof(reply));
346d522f475Smrg    reply.a_type = ANSI_CSI;
347d522f475Smrg
348d522f475Smrg    if (oor) {
349d522f475Smrg	reply.a_nparam = 1;
350d522f475Smrg	reply.a_param[0] = 0;	/* Event - 0 = locator unavailable */
351d522f475Smrg	reply.a_inters = '&';
352d522f475Smrg	reply.a_final = 'w';
353d522f475Smrg	unparseseq(xw, &reply);
354d522f475Smrg
355d522f475Smrg	if (screen->locator_reset) {
356d522f475Smrg	    MotionOff(screen, xw);
357d522f475Smrg	    screen->send_mouse_pos = MOUSE_OFF;
358d522f475Smrg	}
359d522f475Smrg	return (True);
360d522f475Smrg    }
361d522f475Smrg
362d522f475Smrg    /*
363d522f475Smrg     * event:
364d522f475Smrg     *        1       no buttons
365d522f475Smrg     *        2       left button down
366d522f475Smrg     *        3       left button up
367d522f475Smrg     *        4       middle button down
368d522f475Smrg     *        5       middle button up
369d522f475Smrg     *        6       right button down
370d522f475Smrg     *        7       right button up
371d522f475Smrg     *        8       M4 down
372d522f475Smrg     *        9       M4 up
373d522f475Smrg     */
374d522f475Smrg    reply.a_nparam = 4;
375d522f475Smrg    switch (event->type) {
376d522f475Smrg    case ButtonPress:
3772eaa94a1Schristos	reply.a_param[0] = (ParmType) (2 + (button << 1));
378d522f475Smrg	break;
379d522f475Smrg    case ButtonRelease:
3802eaa94a1Schristos	reply.a_param[0] = (ParmType) (3 + (button << 1));
381d522f475Smrg	break;
382d522f475Smrg    default:
383d522f475Smrg	return (True);
384d522f475Smrg    }
385d522f475Smrg    /*
386d522f475Smrg     * mask:
387d522f475Smrg     * bit 7   bit 6   bit 5   bit 4   bit 3   bit 2       bit 1         bit 0
388d522f475Smrg     *                                 M4 down left down   middle down   right down
389d522f475Smrg     *
390d522f475Smrg     * Notice that Button1 (left) and Button3 (right) are swapped in the mask.
391d522f475Smrg     * Also, mask should be the state after the button press/release,
392d522f475Smrg     * X provides the state not including the button press/release.
393d522f475Smrg     */
394492d43a5Smrg    state = (event->state
395d522f475Smrg	     & (Button1Mask | Button2Mask | Button3Mask | Button4Mask)) >> 8;
3964e40088cSchristos    /* update mask to "after" state */
39720d2c4d2Smrg    state ^= ((unsigned) (1 << button));
3984e40088cSchristos    /* swap Button1 & Button3 */
399956cc18dSsnj    state = ((state & (unsigned) ~(4 | 1))
400956cc18dSsnj	     | ((state & 1) ? 4 : 0)
401956cc18dSsnj	     | ((state & 4) ? 1 : 0));
402d522f475Smrg
4032eaa94a1Schristos    reply.a_param[1] = (ParmType) state;
4042eaa94a1Schristos    reply.a_param[2] = (ParmType) row;
4052eaa94a1Schristos    reply.a_param[3] = (ParmType) col;
406d522f475Smrg    reply.a_inters = '&';
407d522f475Smrg    reply.a_final = 'w';
408d522f475Smrg
409d522f475Smrg    unparseseq(xw, &reply);
410d522f475Smrg
411d522f475Smrg    if (screen->locator_reset) {
412d522f475Smrg	MotionOff(screen, xw);
413d522f475Smrg	screen->send_mouse_pos = MOUSE_OFF;
414d522f475Smrg    }
415d522f475Smrg
416d522f475Smrg    /*
417d522f475Smrg     * DECterm turns the Locator off if a button is pressed while a filter rectangle
418d522f475Smrg     * is active. This might be a bug, but I don't know, so I'll emulate it anyways.
419d522f475Smrg     */
420d522f475Smrg    if (screen->loc_filter) {
421d522f475Smrg	screen->send_mouse_pos = MOUSE_OFF;
422d522f475Smrg	screen->loc_filter = False;
423d522f475Smrg	screen->locator_events = 0;
424d522f475Smrg	MotionOff(screen, xw);
425d522f475Smrg    }
426d522f475Smrg
427d522f475Smrg    return (True);
428d522f475Smrg}
429d522f475Smrg
430d522f475Smrg/*
431d522f475Smrg * mask:
432d522f475Smrg * bit 7   bit 6   bit 5   bit 4   bit 3   bit 2       bit 1         bit 0
433d522f475Smrg *                                 M4 down left down   middle down   right down
434d522f475Smrg *
435d522f475Smrg * Button1 (left) and Button3 (right) are swapped in the mask relative to X.
436d522f475Smrg */
437d522f475Smrg#define	ButtonState(state, mask)	\
4382eaa94a1Schristos{ (state) = (int) (((mask) & (Button1Mask | Button2Mask | Button3Mask | Button4Mask)) >> 8);	\
439d522f475Smrg  /* swap Button1 & Button3 */								\
440d522f475Smrg  (state) = ((state) & ~(4|1)) | (((state)&1)?4:0) | (((state)&4)?1:0);			\
441d522f475Smrg}
442d522f475Smrg
443d522f475Smrgvoid
444d522f475SmrgGetLocatorPosition(XtermWidget xw)
445d522f475Smrg{
446d522f475Smrg    ANSI reply;
447956cc18dSsnj    TScreen *screen = TScreenOf(xw);
448d522f475Smrg    Window root, child;
449d522f475Smrg    int rx, ry, x, y;
450d522f475Smrg    unsigned int mask;
451d522f475Smrg    int row = 0, col = 0;
452d522f475Smrg    Bool oor = False;
453d522f475Smrg    Bool ret = False;
454d522f475Smrg    int state;
455d522f475Smrg
456d522f475Smrg    /*
457d522f475Smrg     * DECterm turns the Locator off if the position is requested while a filter rectangle
458d522f475Smrg     * is active.  This might be a bug, but I don't know, so I'll emulate it anyways.
459d522f475Smrg     */
460d522f475Smrg    if (screen->loc_filter) {
461d522f475Smrg	screen->send_mouse_pos = MOUSE_OFF;
462d522f475Smrg	screen->loc_filter = False;
463d522f475Smrg	screen->locator_events = 0;
464d522f475Smrg	MotionOff(screen, xw);
465d522f475Smrg    }
466d522f475Smrg
467d522f475Smrg    memset(&reply, 0, sizeof(reply));
468d522f475Smrg    reply.a_type = ANSI_CSI;
469d522f475Smrg
470d522f475Smrg    if (screen->send_mouse_pos == DEC_LOCATOR) {
471d522f475Smrg	ret = XQueryPointer(screen->display, VWindow(screen), &root,
472d522f475Smrg			    &child, &rx, &ry, &x, &y, &mask);
473d522f475Smrg	if (ret) {
474d522f475Smrg	    LocatorCoords(row, col, x, y, oor);
475d522f475Smrg	}
476d522f475Smrg    }
477d522f475Smrg    if (ret == False || oor) {
478d522f475Smrg	reply.a_nparam = 1;
479d522f475Smrg	reply.a_param[0] = 0;	/* Event - 0 = locator unavailable */
480d522f475Smrg	reply.a_inters = '&';
481d522f475Smrg	reply.a_final = 'w';
482d522f475Smrg	unparseseq(xw, &reply);
483d522f475Smrg
484d522f475Smrg	if (screen->locator_reset) {
485d522f475Smrg	    MotionOff(screen, xw);
486d522f475Smrg	    screen->send_mouse_pos = MOUSE_OFF;
487d522f475Smrg	}
488d522f475Smrg	return;
489d522f475Smrg    }
490d522f475Smrg
491d522f475Smrg    ButtonState(state, mask);
492d522f475Smrg
493d522f475Smrg    reply.a_nparam = 4;
494d522f475Smrg    reply.a_param[0] = 1;	/* Event - 1 = response to locator request */
4952eaa94a1Schristos    reply.a_param[1] = (ParmType) state;
4962eaa94a1Schristos    reply.a_param[2] = (ParmType) row;
4972eaa94a1Schristos    reply.a_param[3] = (ParmType) col;
498d522f475Smrg    reply.a_inters = '&';
499d522f475Smrg    reply.a_final = 'w';
500d522f475Smrg    unparseseq(xw, &reply);
501d522f475Smrg
502d522f475Smrg    if (screen->locator_reset) {
503d522f475Smrg	MotionOff(screen, xw);
504d522f475Smrg	screen->send_mouse_pos = MOUSE_OFF;
505d522f475Smrg    }
506d522f475Smrg}
507d522f475Smrg
508d522f475Smrgvoid
509d522f475SmrgInitLocatorFilter(XtermWidget xw)
510d522f475Smrg{
511d522f475Smrg    ANSI reply;
512956cc18dSsnj    TScreen *screen = TScreenOf(xw);
513d522f475Smrg    Window root, child;
514d522f475Smrg    int rx, ry, x, y;
515d522f475Smrg    unsigned int mask;
516d522f475Smrg    int row = 0, col = 0;
517d522f475Smrg    Bool oor = 0;
518d522f475Smrg    Bool ret;
519d522f475Smrg    int state;
520d522f475Smrg
521d522f475Smrg    ret = XQueryPointer(screen->display, VWindow(screen),
522d522f475Smrg			&root, &child, &rx, &ry, &x, &y, &mask);
523d522f475Smrg    if (ret) {
524d522f475Smrg	LocatorCoords(row, col, x, y, oor);
525d522f475Smrg    }
526d522f475Smrg    if (ret == False || oor) {
527d522f475Smrg	/* Locator is unavailable */
528d522f475Smrg
529d522f475Smrg	if (screen->loc_filter_top != LOC_FILTER_POS ||
530d522f475Smrg	    screen->loc_filter_left != LOC_FILTER_POS ||
531d522f475Smrg	    screen->loc_filter_bottom != LOC_FILTER_POS ||
532d522f475Smrg	    screen->loc_filter_right != LOC_FILTER_POS) {
533d522f475Smrg	    /*
534d522f475Smrg	     * If any explicit coordinates were received,
535d522f475Smrg	     * report immediately with no coordinates.
536d522f475Smrg	     */
537d522f475Smrg	    memset(&reply, 0, sizeof(reply));
538d522f475Smrg	    reply.a_type = ANSI_CSI;
539d522f475Smrg	    reply.a_nparam = 1;
540d522f475Smrg	    reply.a_param[0] = 0;	/* Event - 0 = locator unavailable */
541d522f475Smrg	    reply.a_inters = '&';
542d522f475Smrg	    reply.a_final = 'w';
543d522f475Smrg	    unparseseq(xw, &reply);
544d522f475Smrg
545d522f475Smrg	    if (screen->locator_reset) {
546d522f475Smrg		MotionOff(screen, xw);
547d522f475Smrg		screen->send_mouse_pos = MOUSE_OFF;
548d522f475Smrg	    }
549d522f475Smrg	} else {
550d522f475Smrg	    /*
551d522f475Smrg	     * No explicit coordinates were received, and the pointer is
552d522f475Smrg	     * unavailable.  Report when the pointer re-enters the window.
553d522f475Smrg	     */
554d522f475Smrg	    screen->loc_filter = True;
555d522f475Smrg	    MotionOn(screen, xw);
556d522f475Smrg	}
557d522f475Smrg	return;
558d522f475Smrg    }
559d522f475Smrg
560d522f475Smrg    /*
561d522f475Smrg     * Adjust rectangle coordinates:
562d522f475Smrg     *  1. Replace "LOC_FILTER_POS" with current coordinates
563d522f475Smrg     *  2. Limit coordinates to screen size
564d522f475Smrg     *  3. make sure top and left are less than bottom and right, resp.
565d522f475Smrg     */
566d522f475Smrg    if (screen->locator_pixels) {
567d522f475Smrg	rx = OriginX(screen) * 2 + Width(screen);
568d522f475Smrg	ry = screen->border * 2 + Height(screen);
569d522f475Smrg    } else {
570d522f475Smrg	rx = screen->max_col;
571d522f475Smrg	ry = screen->max_row;
572d522f475Smrg    }
573d522f475Smrg
574d522f475Smrg#define	Adjust( coord, def, max )				\
575d522f475Smrg	if( (coord) == LOC_FILTER_POS )	(coord) = (def);	\
576d522f475Smrg	else if ((coord) < 1)		(coord) = 1;		\
577d522f475Smrg	else if ((coord) > (max))	(coord) = (max)
578d522f475Smrg
579d522f475Smrg    Adjust(screen->loc_filter_top, row, ry);
580d522f475Smrg    Adjust(screen->loc_filter_left, col, rx);
581d522f475Smrg    Adjust(screen->loc_filter_bottom, row, ry);
582d522f475Smrg    Adjust(screen->loc_filter_right, col, rx);
583d522f475Smrg
584d522f475Smrg    if (screen->loc_filter_top > screen->loc_filter_bottom) {
585d522f475Smrg	ry = screen->loc_filter_top;
586d522f475Smrg	screen->loc_filter_top = screen->loc_filter_bottom;
587d522f475Smrg	screen->loc_filter_bottom = ry;
588d522f475Smrg    }
589d522f475Smrg
590d522f475Smrg    if (screen->loc_filter_left > screen->loc_filter_right) {
591d522f475Smrg	rx = screen->loc_filter_left;
592d522f475Smrg	screen->loc_filter_left = screen->loc_filter_right;
593d522f475Smrg	screen->loc_filter_right = rx;
594d522f475Smrg    }
595d522f475Smrg
596d522f475Smrg    if ((col < screen->loc_filter_left) ||
597d522f475Smrg	(col > screen->loc_filter_right) ||
598d522f475Smrg	(row < screen->loc_filter_top) ||
599d522f475Smrg	(row > screen->loc_filter_bottom)) {
600d522f475Smrg	/* Pointer is already outside the rectangle - report immediately */
601d522f475Smrg	ButtonState(state, mask);
602d522f475Smrg
603d522f475Smrg	memset(&reply, 0, sizeof(reply));
604d522f475Smrg	reply.a_type = ANSI_CSI;
605d522f475Smrg	reply.a_nparam = 4;
606d522f475Smrg	reply.a_param[0] = 10;	/* Event - 10 = locator outside filter */
6072eaa94a1Schristos	reply.a_param[1] = (ParmType) state;
6082eaa94a1Schristos	reply.a_param[2] = (ParmType) row;
6092eaa94a1Schristos	reply.a_param[3] = (ParmType) col;
610d522f475Smrg	reply.a_inters = '&';
611d522f475Smrg	reply.a_final = 'w';
612d522f475Smrg	unparseseq(xw, &reply);
613d522f475Smrg
614d522f475Smrg	if (screen->locator_reset) {
615d522f475Smrg	    MotionOff(screen, xw);
616d522f475Smrg	    screen->send_mouse_pos = MOUSE_OFF;
617d522f475Smrg	}
618d522f475Smrg	return;
619d522f475Smrg    }
620d522f475Smrg
621d522f475Smrg    /*
622d522f475Smrg     * Rectangle is set up.  Allow pointer tracking
623d522f475Smrg     * to detect if the mouse leaves the rectangle.
624d522f475Smrg     */
625d522f475Smrg    screen->loc_filter = True;
626d522f475Smrg    MotionOn(screen, xw);
627d522f475Smrg}
628d522f475Smrg
629d522f475Smrgstatic void
630492d43a5SmrgCheckLocatorPosition(XtermWidget xw, XButtonEvent * event)
631d522f475Smrg{
632d522f475Smrg    ANSI reply;
633956cc18dSsnj    TScreen *screen = TScreenOf(xw);
634d522f475Smrg    int row, col;
635d522f475Smrg    Bool oor;
636d522f475Smrg    int state;
637d522f475Smrg
638492d43a5Smrg    LocatorCoords(row, col, event->x, event->y, oor);
639d522f475Smrg
640d522f475Smrg    /*
641d522f475Smrg     * Send report if the pointer left the filter rectangle, if
642d522f475Smrg     * the pointer left the window, or if the filter rectangle
643d522f475Smrg     * had no coordinates and the pointer re-entered the window.
644d522f475Smrg     */
645d522f475Smrg    if (oor || (screen->loc_filter_top == LOC_FILTER_POS) ||
646d522f475Smrg	(col < screen->loc_filter_left) ||
647d522f475Smrg	(col > screen->loc_filter_right) ||
648d522f475Smrg	(row < screen->loc_filter_top) ||
649d522f475Smrg	(row > screen->loc_filter_bottom)) {
650d522f475Smrg	/* Filter triggered - disable it */
651d522f475Smrg	screen->loc_filter = False;
652d522f475Smrg	MotionOff(screen, xw);
653d522f475Smrg
654d522f475Smrg	memset(&reply, 0, sizeof(reply));
655d522f475Smrg	reply.a_type = ANSI_CSI;
656d522f475Smrg	if (oor) {
657d522f475Smrg	    reply.a_nparam = 1;
658d522f475Smrg	    reply.a_param[0] = 0;	/* Event - 0 = locator unavailable */
659d522f475Smrg	} else {
660492d43a5Smrg	    ButtonState(state, event->state);
661d522f475Smrg
662d522f475Smrg	    reply.a_nparam = 4;
663d522f475Smrg	    reply.a_param[0] = 10;	/* Event - 10 = locator outside filter */
6642eaa94a1Schristos	    reply.a_param[1] = (ParmType) state;
6652eaa94a1Schristos	    reply.a_param[2] = (ParmType) row;
6662eaa94a1Schristos	    reply.a_param[3] = (ParmType) col;
667d522f475Smrg	}
668d522f475Smrg
669d522f475Smrg	reply.a_inters = '&';
670d522f475Smrg	reply.a_final = 'w';
671d522f475Smrg	unparseseq(xw, &reply);
672d522f475Smrg
673d522f475Smrg	if (screen->locator_reset) {
674d522f475Smrg	    MotionOff(screen, xw);
675d522f475Smrg	    screen->send_mouse_pos = MOUSE_OFF;
676d522f475Smrg	}
677d522f475Smrg    }
678d522f475Smrg}
679d522f475Smrg#endif /* OPT_DEC_LOCATOR */
680d522f475Smrg
681d522f475Smrg#if OPT_READLINE
682d522f475Smrgstatic int
683492d43a5SmrgisClick1_clean(TScreen * screen, XButtonEvent * event)
684d522f475Smrg{
685d522f475Smrg    int delta;
686d522f475Smrg
687492d43a5Smrg    if (!IsBtnEvent(event)
688d522f475Smrg    /* Disable on Shift-Click-1, including the application-mouse modes */
689492d43a5Smrg	|| (BtnModifiers(event) & ShiftMask)
690d522f475Smrg	|| (screen->send_mouse_pos != MOUSE_OFF)	/* Kinda duplicate... */
691d522f475Smrg	||ExtendingSelection)	/* Was moved */
692d522f475Smrg	return 0;
693d522f475Smrg
694d522f475Smrg    if (event->type != ButtonRelease)
695d522f475Smrg	return 0;
696d522f475Smrg
697d522f475Smrg    if (lastButtonDownTime == (Time) 0) {
698d522f475Smrg	/* first time or once in a blue moon */
699d522f475Smrg	delta = screen->multiClickTime + 1;
700492d43a5Smrg    } else if (event->time > lastButtonDownTime) {
701d522f475Smrg	/* most of the time */
702492d43a5Smrg	delta = (int) (event->time - lastButtonDownTime);
703d522f475Smrg    } else {
704d522f475Smrg	/* time has rolled over since lastButtonUpTime */
705492d43a5Smrg	delta = (int) ((((Time) ~ 0) - lastButtonDownTime) + event->time);
706d522f475Smrg    }
707d522f475Smrg
708d522f475Smrg    return delta <= screen->multiClickTime;
709d522f475Smrg}
710d522f475Smrg
711d522f475Smrgstatic int
712492d43a5SmrgisDoubleClick3(TScreen * screen, XButtonEvent * event)
713d522f475Smrg{
714d522f475Smrg    int delta;
715d522f475Smrg
716d522f475Smrg    if (event->type != ButtonRelease
717492d43a5Smrg	|| (BtnModifiers(event) & ShiftMask)
718492d43a5Smrg	|| event->button != Button3) {
719d522f475Smrg	lastButton3UpTime = 0;	/* Disable the cached info */
720d522f475Smrg	return 0;
721d522f475Smrg    }
722d522f475Smrg    /* Process Btn3Release. */
723d522f475Smrg    if (lastButton3DoubleDownTime == (Time) 0) {
724d522f475Smrg	/* No previous click or once in a blue moon */
725d522f475Smrg	delta = screen->multiClickTime + 1;
726492d43a5Smrg    } else if (event->time > lastButton3DoubleDownTime) {
727d522f475Smrg	/* most of the time */
728492d43a5Smrg	delta = (int) (event->time - lastButton3DoubleDownTime);
729d522f475Smrg    } else {
730d522f475Smrg	/* time has rolled over since lastButton3DoubleDownTime */
731492d43a5Smrg	delta = (int) ((((Time) ~ 0) - lastButton3DoubleDownTime) + event->time);
732d522f475Smrg    }
733d522f475Smrg    if (delta <= screen->multiClickTime) {
734d522f475Smrg	/* Double click */
735d522f475Smrg	CELL cell;
736d522f475Smrg
737d522f475Smrg	/* Cannot check ExtendingSelection, since mouse-3 always sets it */
738492d43a5Smrg	PointToCELL(screen, event->y, event->x, &cell);
739d522f475Smrg	if (isSameCELL(&cell, &lastButton3)) {
740d522f475Smrg	    lastButton3DoubleDownTime = 0;	/* Disable the third click */
741d522f475Smrg	    return 1;
742d522f475Smrg	}
743d522f475Smrg    }
744d522f475Smrg    /* Not a double click, memorize for future check. */
745492d43a5Smrg    lastButton3UpTime = event->time;
746492d43a5Smrg    PointToCELL(screen, event->y, event->x, &lastButton3);
747d522f475Smrg    return 0;
748d522f475Smrg}
749d522f475Smrg
750d522f475Smrgstatic int
751d522f475SmrgCheckSecondPress3(TScreen * screen, XEvent * event)
752d522f475Smrg{
753d522f475Smrg    int delta;
754d522f475Smrg
755d522f475Smrg    if (event->type != ButtonPress
756492d43a5Smrg	|| (KeyModifiers(event) & ShiftMask)
757d522f475Smrg	|| event->xbutton.button != Button3) {
758d522f475Smrg	lastButton3DoubleDownTime = 0;	/* Disable the cached info */
759d522f475Smrg	return 0;
760d522f475Smrg    }
761d522f475Smrg    /* Process Btn3Press. */
762d522f475Smrg    if (lastButton3UpTime == (Time) 0) {
763d522f475Smrg	/* No previous click or once in a blue moon */
764d522f475Smrg	delta = screen->multiClickTime + 1;
765d522f475Smrg    } else if (event->xbutton.time > lastButton3UpTime) {
766d522f475Smrg	/* most of the time */
7672eaa94a1Schristos	delta = (int) (event->xbutton.time - lastButton3UpTime);
768d522f475Smrg    } else {
769d522f475Smrg	/* time has rolled over since lastButton3UpTime */
7702eaa94a1Schristos	delta = (int) ((((Time) ~ 0) - lastButton3UpTime) + event->xbutton.time);
771d522f475Smrg    }
772d522f475Smrg    if (delta <= screen->multiClickTime) {
773d522f475Smrg	CELL cell;
774d522f475Smrg
775d522f475Smrg	PointToCELL(screen, event->xbutton.y, event->xbutton.x, &cell);
776d522f475Smrg	if (isSameCELL(&cell, &lastButton3)) {
777d522f475Smrg	    /* A candidate for a double-click */
778d522f475Smrg	    lastButton3DoubleDownTime = event->xbutton.time;
779d522f475Smrg	    PointToCELL(screen, event->xbutton.y, event->xbutton.x, &lastButton3);
780d522f475Smrg	    return 1;
781d522f475Smrg	}
782d522f475Smrg	lastButton3UpTime = 0;	/* Disable the info about the previous click */
783d522f475Smrg    }
784d522f475Smrg    /* Either too long, or moved, disable. */
785d522f475Smrg    lastButton3DoubleDownTime = 0;
786d522f475Smrg    return 0;
787d522f475Smrg}
788d522f475Smrg
789d522f475Smrgstatic int
790d522f475SmrgrowOnCurrentLine(TScreen * screen,
791d522f475Smrg		 int line,
792d522f475Smrg		 int *deltap)	/* must be XButtonEvent */
793d522f475Smrg{
794956cc18dSsnj    int result = 1;
795d522f475Smrg    int l1, l2;
796d522f475Smrg
797d522f475Smrg    *deltap = 0;
798956cc18dSsnj    if (line != screen->cur_row) {
799956cc18dSsnj	if (line < screen->cur_row)
800956cc18dSsnj	    l1 = line, l2 = screen->cur_row;
801956cc18dSsnj	else
802956cc18dSsnj	    l2 = line, l1 = screen->cur_row;
803956cc18dSsnj	l1--;
804956cc18dSsnj	while (++l1 < l2) {
805956cc18dSsnj	    LineData *ld = GET_LINEDATA(screen, l1);
806956cc18dSsnj	    if (!LineTstWrapped(ld)) {
807956cc18dSsnj		result = 0;
808956cc18dSsnj		break;
809956cc18dSsnj	    }
810956cc18dSsnj	}
811956cc18dSsnj	if (result) {
812956cc18dSsnj	    /* Everything is on one "wrapped line" now */
813956cc18dSsnj	    *deltap = line - screen->cur_row;
814956cc18dSsnj	}
815956cc18dSsnj    }
816956cc18dSsnj    return result;
817d522f475Smrg}
818d522f475Smrg
819d522f475Smrgstatic int
820d522f475SmrgeventRow(TScreen * screen, XEvent * event)	/* must be XButtonEvent */
821d522f475Smrg{
822d522f475Smrg    return (event->xbutton.y - screen->border) / FontHeight(screen);
823d522f475Smrg}
824d522f475Smrg
825d522f475Smrgstatic int
826d522f475SmrgeventColBetween(TScreen * screen, XEvent * event)	/* must be XButtonEvent */
827d522f475Smrg{
828d522f475Smrg    /* Correct by half a width - we are acting on a boundary, not on a cell. */
829d522f475Smrg    return ((event->xbutton.x - OriginX(screen) + (FontWidth(screen) - 1) / 2)
830d522f475Smrg	    / FontWidth(screen));
831d522f475Smrg}
832d522f475Smrg
833d522f475Smrgstatic int
834d522f475SmrgReadLineMovePoint(TScreen * screen, int col, int ldelta)
835d522f475Smrg{
836d522f475Smrg    Char line[6];
837d522f475Smrg    unsigned count = 0;
838d522f475Smrg
839d522f475Smrg    col += ldelta * MaxCols(screen) - screen->cur_col;
840d522f475Smrg    if (col == 0)
841d522f475Smrg	return 0;
842d522f475Smrg    if (screen->control_eight_bits) {
843d522f475Smrg	line[count++] = ANSI_CSI;
844d522f475Smrg    } else {
845d522f475Smrg	line[count++] = ANSI_ESC;
846d522f475Smrg	line[count++] = '[';	/* XXX maybe sometimes O is better? */
847d522f475Smrg    }
84820d2c4d2Smrg    line[count] = CharOf(col > 0 ? 'C' : 'D');
849d522f475Smrg    if (col < 0)
850d522f475Smrg	col = -col;
851d522f475Smrg    while (col--)
852d522f475Smrg	v_write(screen->respond, line, 3);
853d522f475Smrg    return 1;
854d522f475Smrg}
855d522f475Smrg
856d522f475Smrgstatic int
857d522f475SmrgReadLineDelete(TScreen * screen, CELL * cell1, CELL * cell2)
858d522f475Smrg{
859d522f475Smrg    int del;
860d522f475Smrg
861d522f475Smrg    del = (cell2->col - cell1->col) + ((cell2->row - cell1->row) * MaxCols(screen));
862d522f475Smrg    if (del <= 0)		/* Just in case... */
863d522f475Smrg	return 0;
864d522f475Smrg    while (del--)
865492d43a5Smrg	v_write(screen->respond, (const Char *) "\177", 1);
866d522f475Smrg    return 1;
867d522f475Smrg}
868492d43a5Smrg
869492d43a5Smrgstatic void
870492d43a5SmrgreadlineExtend(TScreen * screen, XEvent * event)
871492d43a5Smrg{
872492d43a5Smrg    int ldelta1, ldelta2;
873492d43a5Smrg
874492d43a5Smrg    if (IsBtnEvent(event)) {
875492d43a5Smrg	XButtonEvent *my_event = (XButtonEvent *) event;
876492d43a5Smrg	if (isClick1_clean(screen, my_event)
877492d43a5Smrg	    && SCREEN_FLAG(screen, click1_moves)
878492d43a5Smrg	    && rowOnCurrentLine(screen, eventRow(screen, event), &ldelta1)) {
879492d43a5Smrg	    ReadLineMovePoint(screen, eventColBetween(screen, event), ldelta1);
880492d43a5Smrg	}
881492d43a5Smrg	if (isDoubleClick3(screen, my_event)
882492d43a5Smrg	    && SCREEN_FLAG(screen, dclick3_deletes)
883492d43a5Smrg	    && rowOnCurrentLine(screen, screen->startSel.row, &ldelta1)
884492d43a5Smrg	    && rowOnCurrentLine(screen, screen->endSel.row, &ldelta2)) {
885492d43a5Smrg	    ReadLineMovePoint(screen, screen->endSel.col, ldelta2);
886492d43a5Smrg	    ReadLineDelete(screen, &screen->startSel, &(screen->endSel));
887492d43a5Smrg	}
888492d43a5Smrg    }
889492d43a5Smrg}
890492d43a5Smrg
891d522f475Smrg#endif /* OPT_READLINE */
892d522f475Smrg
893d522f475Smrg/* ^XM-G<line+' '><col+' '> */
894d522f475Smrgvoid
895d522f475SmrgDiredButton(Widget w,
896d522f475Smrg	    XEvent * event,	/* must be XButtonEvent */
897d522f475Smrg	    String * params GCC_UNUSED,		/* selections */
898d522f475Smrg	    Cardinal *num_params GCC_UNUSED)
899d522f475Smrg{
900956cc18dSsnj    XtermWidget xw;
901956cc18dSsnj
902956cc18dSsnj    if ((xw = getXtermWidget(w)) != 0) {
903956cc18dSsnj	TScreen *screen = TScreenOf(xw);
904d522f475Smrg	Char Line[6];
905d522f475Smrg	unsigned line, col;
906d522f475Smrg
907492d43a5Smrg	if (IsBtnEvent(event)
9082eaa94a1Schristos	    && (event->xbutton.y >= screen->border)
9092eaa94a1Schristos	    && (event->xbutton.x >= OriginX(screen))) {
91020d2c4d2Smrg	    line = (unsigned) ((event->xbutton.y - screen->border)
91120d2c4d2Smrg			       / FontHeight(screen));
91220d2c4d2Smrg	    col = (unsigned) ((event->xbutton.x - OriginX(screen))
91320d2c4d2Smrg			      / FontWidth(screen));
914d522f475Smrg	    Line[0] = CONTROL('X');
915d522f475Smrg	    Line[1] = ANSI_ESC;
916d522f475Smrg	    Line[2] = 'G';
9172eaa94a1Schristos	    Line[3] = CharOf(' ' + col);
9182eaa94a1Schristos	    Line[4] = CharOf(' ' + line);
919d522f475Smrg	    v_write(screen->respond, Line, 5);
920d522f475Smrg	}
921d522f475Smrg    }
922d522f475Smrg}
923d522f475Smrg
924d522f475Smrg#if OPT_READLINE
925d522f475Smrgvoid
926d522f475SmrgReadLineButton(Widget w,
927d522f475Smrg	       XEvent * event,	/* must be XButtonEvent */
928d522f475Smrg	       String * params GCC_UNUSED,	/* selections */
929d522f475Smrg	       Cardinal *num_params GCC_UNUSED)
930d522f475Smrg{
931956cc18dSsnj    XtermWidget xw;
932956cc18dSsnj
933956cc18dSsnj    if ((xw = getXtermWidget(w)) != 0) {
934956cc18dSsnj	TScreen *screen = TScreenOf(xw);
935d522f475Smrg	Char Line[6];
936d522f475Smrg	int line, col, ldelta = 0;
937d522f475Smrg
938492d43a5Smrg	if (!IsBtnEvent(event)
939d522f475Smrg	    || (screen->send_mouse_pos != MOUSE_OFF) || ExtendingSelection)
940d522f475Smrg	    goto finish;
941d522f475Smrg	if (event->type == ButtonRelease) {
942d522f475Smrg	    int delta;
943d522f475Smrg
944d522f475Smrg	    if (lastButtonDownTime == (Time) 0) {
945d522f475Smrg		/* first time and once in a blue moon */
946d522f475Smrg		delta = screen->multiClickTime + 1;
947d522f475Smrg	    } else if (event->xbutton.time > lastButtonDownTime) {
948d522f475Smrg		/* most of the time */
9492eaa94a1Schristos		delta = (int) (event->xbutton.time - lastButtonDownTime);
950d522f475Smrg	    } else {
951d522f475Smrg		/* time has rolled over since lastButtonUpTime */
9522eaa94a1Schristos		delta = (int) ((((Time) ~ 0) - lastButtonDownTime) + event->xbutton.time);
953d522f475Smrg	    }
954d522f475Smrg	    if (delta > screen->multiClickTime)
955d522f475Smrg		goto finish;	/* All this work for this... */
956d522f475Smrg	}
957d522f475Smrg	line = (event->xbutton.y - screen->border) / FontHeight(screen);
958956cc18dSsnj	if (!rowOnCurrentLine(screen, line, &ldelta))
959956cc18dSsnj	    goto finish;
960d522f475Smrg	/* Correct by half a width - we are acting on a boundary, not on a cell. */
961d522f475Smrg	col = (event->xbutton.x - OriginX(screen) + (FontWidth(screen) - 1)
962d522f475Smrg	       / 2)
963d522f475Smrg	    / FontWidth(screen) - screen->cur_col + ldelta * MaxCols(screen);
964d522f475Smrg	if (col == 0)
965d522f475Smrg	    goto finish;
966d522f475Smrg	Line[0] = ANSI_ESC;
967d522f475Smrg	/* XXX: sometimes it is better to send '['? */
968d522f475Smrg	Line[1] = 'O';
9692eaa94a1Schristos	Line[2] = CharOf(col > 0 ? 'C' : 'D');
970d522f475Smrg	if (col < 0)
971d522f475Smrg	    col = -col;
972d522f475Smrg	while (col--)
973d522f475Smrg	    v_write(screen->respond, Line, 3);
974d522f475Smrg      finish:
975d522f475Smrg	if (event->type == ButtonRelease)
976d522f475Smrg	    do_select_end(xw, event, params, num_params, False);
977d522f475Smrg    }
978d522f475Smrg}
979d522f475Smrg#endif /* OPT_READLINE */
980d522f475Smrg
981d522f475Smrg/* repeats <ESC>n or <ESC>p */
982d522f475Smrgvoid
983d522f475SmrgViButton(Widget w,
984d522f475Smrg	 XEvent * event,	/* must be XButtonEvent */
985d522f475Smrg	 String * params GCC_UNUSED,	/* selections */
986d522f475Smrg	 Cardinal *num_params GCC_UNUSED)
987d522f475Smrg{
988956cc18dSsnj    XtermWidget xw;
989956cc18dSsnj
990956cc18dSsnj    if ((xw = getXtermWidget(w)) != 0) {
991956cc18dSsnj	TScreen *screen = TScreenOf(xw);
992d522f475Smrg	int pty = screen->respond;
993d522f475Smrg	Char Line[6];
994d522f475Smrg	int line;
995d522f475Smrg
996492d43a5Smrg	if (IsBtnEvent(event)) {
997d522f475Smrg
998d522f475Smrg	    line = screen->cur_row -
999d522f475Smrg		((event->xbutton.y - screen->border) / FontHeight(screen));
1000d522f475Smrg	    if (line != 0) {
1001d522f475Smrg		Line[0] = ANSI_ESC;	/* force an exit from insert-mode */
1002d522f475Smrg		v_write(pty, Line, 1);
1003d522f475Smrg
1004d522f475Smrg		if (line < 0) {
1005d522f475Smrg		    line = -line;
1006d522f475Smrg		    Line[0] = CONTROL('n');
1007d522f475Smrg		} else {
1008d522f475Smrg		    Line[0] = CONTROL('p');
1009d522f475Smrg		}
1010d522f475Smrg		while (--line >= 0)
1011d522f475Smrg		    v_write(pty, Line, 1);
1012d522f475Smrg	    }
1013d522f475Smrg	}
1014d522f475Smrg    }
1015d522f475Smrg}
1016d522f475Smrg
1017d522f475Smrg/*
1018d522f475Smrg * This function handles button-motion events
1019d522f475Smrg */
1020d522f475Smrg/*ARGSUSED*/
1021d522f475Smrgvoid
1022d522f475SmrgHandleSelectExtend(Widget w,
1023d522f475Smrg		   XEvent * event,	/* must be XMotionEvent */
1024d522f475Smrg		   String * params GCC_UNUSED,
1025d522f475Smrg		   Cardinal *num_params GCC_UNUSED)
1026d522f475Smrg{
1027956cc18dSsnj    XtermWidget xw;
1028956cc18dSsnj
1029956cc18dSsnj    if ((xw = getXtermWidget(w)) != 0) {
1030956cc18dSsnj	TScreen *screen = TScreenOf(xw);
1031d522f475Smrg	CELL cell;
1032d522f475Smrg
1033d522f475Smrg	screen->selection_time = event->xmotion.time;
1034d522f475Smrg	switch (screen->eventMode) {
1035d522f475Smrg	    /* If not in one of the DEC mouse-reporting modes */
1036d522f475Smrg	case LEFTEXTENSION:
1037d522f475Smrg	case RIGHTEXTENSION:
1038d522f475Smrg	    PointToCELL(screen, event->xmotion.y, event->xmotion.x, &cell);
1039d522f475Smrg	    ExtendExtend(xw, &cell);
1040d522f475Smrg	    break;
1041d522f475Smrg
1042d522f475Smrg	    /* If in motion reporting mode, send mouse position to
1043d522f475Smrg	       character process as a key sequence \E[M... */
1044d522f475Smrg	case NORMAL:
1045d522f475Smrg	    /* will get here if send_mouse_pos != MOUSE_OFF */
1046d522f475Smrg	    if (screen->send_mouse_pos == BTN_EVENT_MOUSE
1047d522f475Smrg		|| screen->send_mouse_pos == ANY_EVENT_MOUSE) {
1048d522f475Smrg		(void) SendMousePosition(xw, event);
1049d522f475Smrg	    }
1050d522f475Smrg	    break;
1051d522f475Smrg	}
1052d522f475Smrg    }
1053d522f475Smrg}
1054d522f475Smrg
1055d522f475Smrgvoid
1056d522f475SmrgHandleKeyboardSelectExtend(Widget w,
1057d522f475Smrg			   XEvent * event GCC_UNUSED,	/* must be XButtonEvent */
1058d522f475Smrg			   String * params GCC_UNUSED,
1059d522f475Smrg			   Cardinal *num_params GCC_UNUSED)
1060d522f475Smrg{
1061956cc18dSsnj    XtermWidget xw;
1062956cc18dSsnj
1063956cc18dSsnj    if ((xw = getXtermWidget(w)) != 0) {
1064956cc18dSsnj	TScreen *screen = TScreenOf(xw);
1065d522f475Smrg	ExtendExtend(xw, &screen->cursorp);
1066d522f475Smrg    }
1067d522f475Smrg}
1068d522f475Smrg
1069d522f475Smrgstatic void
1070d522f475Smrgdo_select_end(XtermWidget xw,
1071d522f475Smrg	      XEvent * event,	/* must be XButtonEvent */
1072d522f475Smrg	      String * params,	/* selections */
1073d522f475Smrg	      Cardinal *num_params,
1074d522f475Smrg	      Bool use_cursor_loc)
1075d522f475Smrg{
1076956cc18dSsnj    TScreen *screen = TScreenOf(xw);
1077d522f475Smrg
1078d522f475Smrg    screen->selection_time = event->xbutton.time;
1079d522f475Smrg    switch (screen->eventMode) {
1080d522f475Smrg    case NORMAL:
1081d522f475Smrg	(void) SendMousePosition(xw, event);
1082d522f475Smrg	break;
1083d522f475Smrg    case LEFTEXTENSION:
1084d522f475Smrg    case RIGHTEXTENSION:
1085d522f475Smrg	EndExtend(xw, event, params, *num_params, use_cursor_loc);
1086d522f475Smrg#if OPT_READLINE
1087492d43a5Smrg	readlineExtend(screen, event);
1088d522f475Smrg#endif /* OPT_READLINE */
1089d522f475Smrg	break;
1090d522f475Smrg    }
1091d522f475Smrg}
1092d522f475Smrg
1093d522f475Smrgvoid
1094d522f475SmrgHandleSelectEnd(Widget w,
1095d522f475Smrg		XEvent * event,	/* must be XButtonEvent */
1096d522f475Smrg		String * params,	/* selections */
1097d522f475Smrg		Cardinal *num_params)
1098d522f475Smrg{
1099956cc18dSsnj    XtermWidget xw;
1100956cc18dSsnj
1101956cc18dSsnj    if ((xw = getXtermWidget(w)) != 0) {
1102956cc18dSsnj	do_select_end(xw, event, params, num_params, False);
1103956cc18dSsnj    }
1104d522f475Smrg}
1105d522f475Smrg
1106d522f475Smrgvoid
1107d522f475SmrgHandleKeyboardSelectEnd(Widget w,
1108d522f475Smrg			XEvent * event,		/* must be XButtonEvent */
1109d522f475Smrg			String * params,	/* selections */
1110d522f475Smrg			Cardinal *num_params)
1111d522f475Smrg{
1112956cc18dSsnj    XtermWidget xw;
1113956cc18dSsnj
1114956cc18dSsnj    if ((xw = getXtermWidget(w)) != 0) {
1115956cc18dSsnj	do_select_end(xw, event, params, num_params, True);
1116956cc18dSsnj    }
1117d522f475Smrg}
1118d522f475Smrg
1119492d43a5Smrg/*
11206879286fSmrg * Copy the selection data to the given target(s).
1121492d43a5Smrg */
1122492d43a5Smrgvoid
11236879286fSmrgHandleCopySelection(Widget w,
11246879286fSmrg		    XEvent * event,
11256879286fSmrg		    String * params,	/* list of targets */
11266879286fSmrg		    Cardinal *num_params)
1127492d43a5Smrg{
1128492d43a5Smrg    XtermWidget xw;
1129492d43a5Smrg
1130492d43a5Smrg    if ((xw = getXtermWidget(w)) != 0) {
11316879286fSmrg	SelectSet(xw, event, params, *num_params);
1132492d43a5Smrg    }
1133492d43a5Smrg}
1134492d43a5Smrg
1135d522f475Smrgstruct _SelectionList {
1136d522f475Smrg    String *params;
1137d522f475Smrg    Cardinal count;
1138d522f475Smrg    Atom *targets;
1139d522f475Smrg    Time time;
1140d522f475Smrg};
1141d522f475Smrg
1142d522f475Smrgstatic unsigned
1143d522f475SmrgDECtoASCII(unsigned ch)
1144d522f475Smrg{
1145d522f475Smrg    if (xtermIsDecGraphic(ch)) {
11462eaa94a1Schristos	ch = CharOf("###########+++++##-##++++|######"[ch]);
11472eaa94a1Schristos	/*           01234567890123456789012345678901 */
1148d522f475Smrg    }
1149d522f475Smrg    return ch;
1150d522f475Smrg}
115120d2c4d2Smrg
115220d2c4d2Smrg#if OPT_WIDE_CHARS
115320d2c4d2Smrgstatic Cardinal
115420d2c4d2SmrgaddXtermChar(Char ** buffer, Cardinal *used, Cardinal offset, unsigned value)
115520d2c4d2Smrg{
115620d2c4d2Smrg    if (offset + 1 >= *used) {
115720d2c4d2Smrg	*used = 1 + (2 * (offset + 1));
115820d2c4d2Smrg	allocXtermChars(buffer, *used);
115920d2c4d2Smrg    }
116020d2c4d2Smrg    (*buffer)[offset++] = (Char) value;
116120d2c4d2Smrg    return offset;
116220d2c4d2Smrg}
116320d2c4d2Smrg#define AddChar(buffer, used, offset, value) \
116420d2c4d2Smrg	offset = addXtermChar(buffer, used, offset, (unsigned) value)
116520d2c4d2Smrg
1166d522f475Smrg/*
1167d522f475Smrg * Convert a UTF-8 string to Latin-1, replacing non Latin-1 characters by `#',
1168d522f475Smrg * or ASCII/Latin-1 equivalents for special cases.
1169d522f475Smrg */
1170d522f475Smrgstatic Char *
117120d2c4d2SmrgUTF8toLatin1(TScreen * screen, Char * s, unsigned long len, unsigned long *result)
1172d522f475Smrg{
1173d522f475Smrg    static Char *buffer;
1174956cc18dSsnj    static Cardinal used;
1175d522f475Smrg
117620d2c4d2Smrg    Cardinal offset = 0;
1177d522f475Smrg
1178492d43a5Smrg    const Char *p;
1179d522f475Smrg
118020d2c4d2Smrg    if (len != 0) {
1181d522f475Smrg	PtyData data;
1182d522f475Smrg
1183d522f475Smrg	fakePtyData(&data, s, s + len);
1184d522f475Smrg	while (decodeUtf8(&data)) {
1185956cc18dSsnj	    Bool fails = False;
1186956cc18dSsnj	    Bool extra = False;
1187d522f475Smrg	    IChar value = skipPtyData(&data);
1188d522f475Smrg	    if (value == UCS_REPL) {
1189956cc18dSsnj		fails = True;
1190d522f475Smrg	    } else if (value < 256) {
119120d2c4d2Smrg		AddChar(&buffer, &used, offset, CharOf(value));
1192d522f475Smrg	    } else {
1193d522f475Smrg		unsigned eqv = ucs2dec(value);
1194d522f475Smrg		if (xtermIsDecGraphic(eqv)) {
119520d2c4d2Smrg		    AddChar(&buffer, &used, offset, DECtoASCII(eqv));
1196d522f475Smrg		} else {
1197d522f475Smrg		    eqv = AsciiEquivs(value);
1198956cc18dSsnj		    if (eqv == value) {
1199956cc18dSsnj			fails = True;
1200956cc18dSsnj		    } else {
120120d2c4d2Smrg			AddChar(&buffer, &used, offset, eqv);
1202956cc18dSsnj		    }
1203956cc18dSsnj		    if (isWide((wchar_t) value))
1204956cc18dSsnj			extra = True;
1205956cc18dSsnj		}
1206956cc18dSsnj	    }
1207956cc18dSsnj
1208956cc18dSsnj	    /*
1209956cc18dSsnj	     * If we're not able to plug in a single-byte result, insert the
1210956cc18dSsnj	     * defaultString (which normally is a single "#", but could be
1211956cc18dSsnj	     * whatever the user wants).
1212956cc18dSsnj	     */
1213956cc18dSsnj	    if (fails) {
1214492d43a5Smrg		for (p = (const Char *) screen->default_string; *p != '\0'; ++p) {
121520d2c4d2Smrg		    AddChar(&buffer, &used, offset, *p);
1216d522f475Smrg		}
1217d522f475Smrg	    }
1218956cc18dSsnj	    if (extra)
121920d2c4d2Smrg		AddChar(&buffer, &used, offset, ' ');
1220d522f475Smrg	}
122120d2c4d2Smrg	AddChar(&buffer, &used, offset, '\0');
122220d2c4d2Smrg	*result = (unsigned long) (offset - 1);
1223d522f475Smrg    } else {
1224d522f475Smrg	*result = 0;
1225d522f475Smrg    }
1226d522f475Smrg    return buffer;
1227d522f475Smrg}
122820d2c4d2Smrg
122920d2c4d2Smrgint
123020d2c4d2SmrgxtermUtf8ToTextList(XtermWidget xw,
123120d2c4d2Smrg		    XTextProperty * text_prop,
123220d2c4d2Smrg		    char ***text_list,
123320d2c4d2Smrg		    int *text_list_count)
123420d2c4d2Smrg{
123520d2c4d2Smrg    TScreen *screen = TScreenOf(xw);
123620d2c4d2Smrg    Display *dpy = screen->display;
123720d2c4d2Smrg    int rc = -1;
123820d2c4d2Smrg
123920d2c4d2Smrg    if (text_prop->format == 8
124020d2c4d2Smrg	&& (rc = Xutf8TextPropertyToTextList(dpy, text_prop,
124120d2c4d2Smrg					     text_list,
124220d2c4d2Smrg					     text_list_count)) >= 0) {
124320d2c4d2Smrg	if (*text_list != NULL && *text_list_count != 0) {
124420d2c4d2Smrg	    int i;
124520d2c4d2Smrg	    Char *data;
124620d2c4d2Smrg	    char **new_text_list, *tmp;
124720d2c4d2Smrg	    unsigned long size, new_size;
124820d2c4d2Smrg
124920d2c4d2Smrg	    TRACE(("xtermUtf8ToTextList size %d\n", *text_list_count));
125020d2c4d2Smrg
125120d2c4d2Smrg	    /*
125220d2c4d2Smrg	     * XLib StringList actually uses only two pointers, one for the
125320d2c4d2Smrg	     * list itself, and one for the data.  Pointer to the data is the
125420d2c4d2Smrg	     * first element of the list, the rest (if any) list elements point
125520d2c4d2Smrg	     * to the same memory block as the first element
125620d2c4d2Smrg	     */
125720d2c4d2Smrg	    new_size = 0;
125820d2c4d2Smrg	    for (i = 0; i < *text_list_count; ++i) {
125920d2c4d2Smrg		data = (Char *) (*text_list)[i];
126020d2c4d2Smrg		size = strlen((*text_list)[i]) + 1;
126120d2c4d2Smrg		(void) UTF8toLatin1(screen, data, size, &size);
126220d2c4d2Smrg		new_size += size + 1;
126320d2c4d2Smrg	    }
1264a1f3da82Smrg	    new_text_list = TypeXtMallocN(char *, *text_list_count);
126520d2c4d2Smrg	    new_text_list[0] = tmp = XtMalloc((Cardinal) new_size);
126620d2c4d2Smrg	    for (i = 0; i < (*text_list_count); ++i) {
126720d2c4d2Smrg		data = (Char *) (*text_list)[i];
126820d2c4d2Smrg		size = strlen((*text_list)[i]) + 1;
126920d2c4d2Smrg		data = UTF8toLatin1(screen, data, size, &size);
127020d2c4d2Smrg		memcpy(tmp, data, size + 1);
127120d2c4d2Smrg		new_text_list[i] = tmp;
127220d2c4d2Smrg		tmp += size + 1;
127320d2c4d2Smrg	    }
127420d2c4d2Smrg	    XFreeStringList((*text_list));
127520d2c4d2Smrg	    *text_list = new_text_list;
127620d2c4d2Smrg	} else {
127720d2c4d2Smrg	    rc = -1;
127820d2c4d2Smrg	}
127920d2c4d2Smrg    }
128020d2c4d2Smrg    return rc;
128120d2c4d2Smrg}
1282d522f475Smrg#endif /* OPT_WIDE_CHARS */
1283d522f475Smrg
1284956cc18dSsnjstatic char *
1285956cc18dSsnjparseItem(char *value, char *nextc)
1286d522f475Smrg{
1287956cc18dSsnj    char *nextp = value;
1288956cc18dSsnj    while (*nextp != '\0' && *nextp != ',') {
1289956cc18dSsnj	*nextp = x_toupper(*nextp);
1290956cc18dSsnj	++nextp;
1291956cc18dSsnj    }
1292956cc18dSsnj    *nextc = *nextp;
1293956cc18dSsnj    *nextp = '\0';
1294956cc18dSsnj    x_strtrim(value);
1295d522f475Smrg
1296956cc18dSsnj    return nextp;
1297956cc18dSsnj}
1298d522f475Smrg
1299956cc18dSsnj/*
1300956cc18dSsnj * All of the wanted strings are unique in the first character, so we can
1301956cc18dSsnj * use simple abbreviations.
1302956cc18dSsnj */
1303956cc18dSsnjstatic Bool
1304956cc18dSsnjsameItem(const char *actual, const char *wanted)
1305956cc18dSsnj{
1306956cc18dSsnj    Bool result = False;
1307956cc18dSsnj    size_t have = strlen(actual);
1308956cc18dSsnj    size_t need = strlen(wanted);
1309d522f475Smrg
1310956cc18dSsnj    if (have != 0 && have <= need) {
1311956cc18dSsnj	if (!strncmp(actual, wanted, have)) {
1312956cc18dSsnj	    TRACE(("...matched \"%s\"\n", wanted));
1313956cc18dSsnj	    result = True;
1314956cc18dSsnj	}
1315956cc18dSsnj    }
1316956cc18dSsnj
1317956cc18dSsnj    return result;
1318956cc18dSsnj}
1319956cc18dSsnj
1320956cc18dSsnj/*
1321956cc18dSsnj * Handle the eightBitSelectTypes or utf8SelectTypes resource values.
1322956cc18dSsnj */
1323956cc18dSsnjstatic Bool
1324956cc18dSsnjoverrideTargets(Widget w, String value, Atom ** resultp)
1325956cc18dSsnj{
1326956cc18dSsnj    Bool override = False;
1327956cc18dSsnj    XtermWidget xw;
1328956cc18dSsnj
1329956cc18dSsnj    if ((xw = getXtermWidget(w)) != 0) {
1330956cc18dSsnj	TScreen *screen = TScreenOf(xw);
1331956cc18dSsnj
133220d2c4d2Smrg	if (!IsEmpty(value)) {
1333492d43a5Smrg	    char *copied = x_strdup(value);
1334956cc18dSsnj	    if (copied != 0) {
1335956cc18dSsnj		Atom *result = 0;
1336956cc18dSsnj		Cardinal count = 1;
1337956cc18dSsnj		int n;
1338d522f475Smrg
1339956cc18dSsnj		TRACE(("decoding SelectTypes \"%s\"\n", value));
1340956cc18dSsnj		for (n = 0; copied[n] != '\0'; ++n) {
1341956cc18dSsnj		    if (copied[n] == ',')
1342956cc18dSsnj			++count;
1343956cc18dSsnj		}
1344a1f3da82Smrg		result = TypeXtMallocN(Atom, (2 * count) + 1);
1345956cc18dSsnj		if (result == NULL) {
1346956cc18dSsnj		    TRACE(("Couldn't allocate selection types\n"));
1347956cc18dSsnj		} else {
1348956cc18dSsnj		    char nextc = '?';
134920d2c4d2Smrg		    char *listp = (char *) copied;
1350956cc18dSsnj		    count = 0;
1351956cc18dSsnj		    do {
1352956cc18dSsnj			char *nextp = parseItem(listp, &nextc);
1353956cc18dSsnj			size_t len = strlen(listp);
1354956cc18dSsnj
1355956cc18dSsnj			if (len == 0) {
1356a1f3da82Smrg			    /* EMPTY */ ;
1357956cc18dSsnj			}
1358956cc18dSsnj#if OPT_WIDE_CHARS
1359956cc18dSsnj			else if (sameItem(listp, "UTF8")) {
1360956cc18dSsnj			    result[count++] = XA_UTF8_STRING(XtDisplay(w));
1361956cc18dSsnj			}
1362956cc18dSsnj#endif
1363956cc18dSsnj			else if (sameItem(listp, "I18N")) {
1364956cc18dSsnj			    if (screen->i18nSelections) {
1365956cc18dSsnj				result[count++] = XA_TEXT(XtDisplay(w));
1366956cc18dSsnj				result[count++] = XA_COMPOUND_TEXT(XtDisplay(w));
1367956cc18dSsnj			    }
1368956cc18dSsnj			} else if (sameItem(listp, "TEXT")) {
1369956cc18dSsnj			    result[count++] = XA_TEXT(XtDisplay(w));
1370956cc18dSsnj			} else if (sameItem(listp, "COMPOUND_TEXT")) {
1371956cc18dSsnj			    result[count++] = XA_COMPOUND_TEXT(XtDisplay(w));
1372956cc18dSsnj			} else if (sameItem(listp, "STRING")) {
1373956cc18dSsnj			    result[count++] = XA_STRING;
1374956cc18dSsnj			}
1375956cc18dSsnj			*nextp++ = nextc;
1376956cc18dSsnj			listp = nextp;
1377956cc18dSsnj		    } while (nextc != '\0');
1378956cc18dSsnj		    if (count) {
1379956cc18dSsnj			result[count] = None;
1380956cc18dSsnj			override = True;
1381956cc18dSsnj			*resultp = result;
1382956cc18dSsnj		    } else {
1383956cc18dSsnj			XtFree((char *) result);
1384956cc18dSsnj		    }
1385956cc18dSsnj		}
1386956cc18dSsnj	    } else {
1387956cc18dSsnj		TRACE(("Couldn't allocate copy of selection types\n"));
1388d522f475Smrg	    }
1389956cc18dSsnj	}
1390956cc18dSsnj    }
1391956cc18dSsnj    return override;
1392956cc18dSsnj}
1393956cc18dSsnj
1394956cc18dSsnj#if OPT_WIDE_CHARS
1395956cc18dSsnjstatic Atom *
1396956cc18dSsnjallocUtf8Targets(Widget w, TScreen * screen)
1397956cc18dSsnj{
1398956cc18dSsnj    Atom **resultp = &(screen->selection_targets_utf8);
1399956cc18dSsnj
1400956cc18dSsnj    if (*resultp == 0) {
1401956cc18dSsnj	Atom *result;
1402956cc18dSsnj
1403956cc18dSsnj	if (!overrideTargets(w, screen->utf8_select_types, &result)) {
1404a1f3da82Smrg	    result = TypeXtMallocN(Atom, 5);
1405956cc18dSsnj	    if (result == NULL) {
1406956cc18dSsnj		TRACE(("Couldn't allocate utf-8 selection targets\n"));
1407956cc18dSsnj	    } else {
1408956cc18dSsnj		int n = 0;
1409956cc18dSsnj
1410e39b573cSmrg		if (XSupportsLocale()) {
1411e39b573cSmrg		    result[n++] = XA_UTF8_STRING(XtDisplay(w));
1412d522f475Smrg#ifdef X_HAVE_UTF8_STRING
1413e39b573cSmrg		    if (screen->i18nSelections) {
1414e39b573cSmrg			result[n++] = XA_TEXT(XtDisplay(w));
1415e39b573cSmrg			result[n++] = XA_COMPOUND_TEXT(XtDisplay(w));
1416e39b573cSmrg		    }
1417d522f475Smrg#endif
1418e39b573cSmrg		}
1419956cc18dSsnj		result[n++] = XA_STRING;
1420956cc18dSsnj		result[n] = None;
1421956cc18dSsnj	    }
1422d522f475Smrg	}
1423956cc18dSsnj
1424956cc18dSsnj	*resultp = result;
1425d522f475Smrg    }
1426956cc18dSsnj
1427956cc18dSsnj    return *resultp;
1428956cc18dSsnj}
1429d522f475Smrg#endif
1430d522f475Smrg
1431956cc18dSsnjstatic Atom *
1432956cc18dSsnjalloc8bitTargets(Widget w, TScreen * screen)
1433956cc18dSsnj{
1434956cc18dSsnj    Atom **resultp = &(screen->selection_targets_8bit);
1435956cc18dSsnj
1436956cc18dSsnj    if (*resultp == 0) {
1437956cc18dSsnj	Atom *result = 0;
1438956cc18dSsnj
1439956cc18dSsnj	if (!overrideTargets(w, screen->eightbit_select_types, &result)) {
1440a1f3da82Smrg	    result = TypeXtMallocN(Atom, 5);
1441956cc18dSsnj	    if (result == NULL) {
1442956cc18dSsnj		TRACE(("Couldn't allocate 8bit selection targets\n"));
1443956cc18dSsnj	    } else {
1444956cc18dSsnj		int n = 0;
1445956cc18dSsnj
1446e39b573cSmrg		if (XSupportsLocale()) {
1447d522f475Smrg#ifdef X_HAVE_UTF8_STRING
1448e39b573cSmrg		    result[n++] = XA_UTF8_STRING(XtDisplay(w));
1449956cc18dSsnj#endif
1450e39b573cSmrg		    if (screen->i18nSelections) {
1451e39b573cSmrg			result[n++] = XA_TEXT(XtDisplay(w));
1452e39b573cSmrg			result[n++] = XA_COMPOUND_TEXT(XtDisplay(w));
1453e39b573cSmrg		    }
1454956cc18dSsnj		}
1455956cc18dSsnj		result[n++] = XA_STRING;
1456956cc18dSsnj		result[n] = None;
1457956cc18dSsnj	    }
1458956cc18dSsnj	}
1459956cc18dSsnj
1460956cc18dSsnj	*resultp = result;
1461956cc18dSsnj    }
1462956cc18dSsnj
1463956cc18dSsnj    return *resultp;
1464956cc18dSsnj}
1465956cc18dSsnj
1466956cc18dSsnjstatic Atom *
1467956cc18dSsnj_SelectionTargets(Widget w)
1468956cc18dSsnj{
1469956cc18dSsnj    Atom *result;
1470956cc18dSsnj    TScreen *screen;
1471956cc18dSsnj    XtermWidget xw;
1472956cc18dSsnj
1473956cc18dSsnj    if ((xw = getXtermWidget(w)) == 0) {
1474956cc18dSsnj	result = NULL;
1475956cc18dSsnj    } else {
1476956cc18dSsnj	screen = TScreenOf(xw);
1477956cc18dSsnj
1478956cc18dSsnj#if OPT_WIDE_CHARS
1479956cc18dSsnj	if (screen->wide_chars) {
1480956cc18dSsnj	    result = allocUtf8Targets(w, screen);
1481956cc18dSsnj	} else
1482d522f475Smrg#endif
1483956cc18dSsnj	{
1484956cc18dSsnj	    /* not screen->wide_chars */
1485956cc18dSsnj	    result = alloc8bitTargets(w, screen);
1486d522f475Smrg	}
1487d522f475Smrg    }
1488956cc18dSsnj
1489956cc18dSsnj    return result;
1490d522f475Smrg}
1491d522f475Smrg
1492d522f475Smrg#define isSELECT(value) (!strcmp(value, "SELECT"))
1493d522f475Smrg
1494d522f475Smrgstatic void
1495d522f475SmrgUnmapSelections(XtermWidget xw)
1496d522f475Smrg{
1497956cc18dSsnj    TScreen *screen = TScreenOf(xw);
1498d522f475Smrg    Cardinal n;
1499d522f475Smrg
1500d522f475Smrg    if (screen->mappedSelect) {
1501d522f475Smrg	for (n = 0; screen->mappedSelect[n] != 0; ++n)
150220d2c4d2Smrg	    free((void *) screen->mappedSelect[n]);
1503d522f475Smrg	free(screen->mappedSelect);
1504d522f475Smrg	screen->mappedSelect = 0;
1505d522f475Smrg    }
1506d522f475Smrg}
1507d522f475Smrg
1508d522f475Smrg/*
1509d522f475Smrg * xterm generally uses the primary selection.  Some applications prefer
1510d522f475Smrg * (or are limited to) the clipboard.  Since the translations resource is
1511d522f475Smrg * complicated, users seldom change the way it affects selection.  But it
1512d522f475Smrg * is simple to remap the choice between primary and clipboard before the
1513d522f475Smrg * call to XmuInternStrings().
1514d522f475Smrg */
1515d522f475Smrgstatic String *
1516d522f475SmrgMapSelections(XtermWidget xw, String * params, Cardinal num_params)
1517d522f475Smrg{
1518d522f475Smrg    String *result = params;
1519d522f475Smrg
1520d522f475Smrg    if (num_params > 0) {
1521d522f475Smrg	Cardinal j;
1522d522f475Smrg	Boolean map = False;
1523d522f475Smrg
1524d522f475Smrg	for (j = 0; j < num_params; ++j) {
1525d522f475Smrg	    TRACE(("param[%d]:%s\n", j, params[j]));
1526d522f475Smrg	    if (isSELECT(params[j])) {
1527d522f475Smrg		map = True;
1528d522f475Smrg		break;
1529d522f475Smrg	    }
1530d522f475Smrg	}
1531d522f475Smrg	if (map) {
1532956cc18dSsnj	    TScreen *screen = TScreenOf(xw);
1533956cc18dSsnj	    const char *mapTo = (screen->selectToClipboard
1534d522f475Smrg				 ? "CLIPBOARD"
1535d522f475Smrg				 : "PRIMARY");
1536d522f475Smrg
1537d522f475Smrg	    UnmapSelections(xw);
1538d522f475Smrg	    if ((result = TypeMallocN(String, num_params + 1)) != 0) {
1539d522f475Smrg		result[num_params] = 0;
1540d522f475Smrg		for (j = 0; j < num_params; ++j) {
1541d522f475Smrg		    result[j] = x_strdup((isSELECT(params[j])
1542d522f475Smrg					  ? mapTo
1543d522f475Smrg					  : params[j]));
1544d522f475Smrg		    if (result[j] == 0) {
1545d522f475Smrg			UnmapSelections(xw);
1546d522f475Smrg			result = 0;
1547d522f475Smrg			break;
1548d522f475Smrg		    }
1549d522f475Smrg		}
1550956cc18dSsnj		screen->mappedSelect = result;
1551d522f475Smrg	    }
1552d522f475Smrg	}
1553d522f475Smrg    }
1554d522f475Smrg    return result;
1555d522f475Smrg}
1556d522f475Smrg
1557d522f475Smrg/*
1558d522f475Smrg * Lookup the cut-buffer number, which will be in the range 0-7.
1559d522f475Smrg * If it is not a cut-buffer, it is the primary selection (-1).
1560d522f475Smrg */
1561d522f475Smrgstatic int
156220d2c4d2SmrgCutBuffer(Atom code)
1563d522f475Smrg{
1564d522f475Smrg    int cutbuffer;
156520d2c4d2Smrg    switch ((unsigned) code) {
1566d522f475Smrg    case XA_CUT_BUFFER0:
1567d522f475Smrg	cutbuffer = 0;
1568d522f475Smrg	break;
1569d522f475Smrg    case XA_CUT_BUFFER1:
1570d522f475Smrg	cutbuffer = 1;
1571d522f475Smrg	break;
1572d522f475Smrg    case XA_CUT_BUFFER2:
1573d522f475Smrg	cutbuffer = 2;
1574d522f475Smrg	break;
1575d522f475Smrg    case XA_CUT_BUFFER3:
1576d522f475Smrg	cutbuffer = 3;
1577d522f475Smrg	break;
1578d522f475Smrg    case XA_CUT_BUFFER4:
1579d522f475Smrg	cutbuffer = 4;
1580d522f475Smrg	break;
1581d522f475Smrg    case XA_CUT_BUFFER5:
1582d522f475Smrg	cutbuffer = 5;
1583d522f475Smrg	break;
1584d522f475Smrg    case XA_CUT_BUFFER6:
1585d522f475Smrg	cutbuffer = 6;
1586d522f475Smrg	break;
1587d522f475Smrg    case XA_CUT_BUFFER7:
1588d522f475Smrg	cutbuffer = 7;
1589d522f475Smrg	break;
1590d522f475Smrg    default:
1591d522f475Smrg	cutbuffer = -1;
1592d522f475Smrg	break;
1593d522f475Smrg    }
1594d522f475Smrg    return cutbuffer;
1595d522f475Smrg}
1596d522f475Smrg
1597d522f475Smrg#if OPT_PASTE64
1598d522f475Smrgstatic void
1599d522f475SmrgFinishPaste64(XtermWidget xw)
1600d522f475Smrg{
1601956cc18dSsnj    TScreen *screen = TScreenOf(xw);
1602956cc18dSsnj
1603956cc18dSsnj    TRACE(("FinishPaste64(%d)\n", screen->base64_paste));
1604956cc18dSsnj    if (screen->base64_paste) {
1605956cc18dSsnj	screen->base64_paste = 0;
1606956cc18dSsnj	unparseputc1(xw, screen->base64_final);
1607d522f475Smrg	unparse_end(xw);
1608d522f475Smrg    }
1609d522f475Smrg}
1610d522f475Smrg#endif
1611d522f475Smrg
1612d522f475Smrg#if !OPT_PASTE64
1613d522f475Smrgstatic
1614d522f475Smrg#endif
1615d522f475Smrgvoid
1616d522f475SmrgxtermGetSelection(Widget w,
1617d522f475Smrg		  Time ev_time,
1618d522f475Smrg		  String * params,	/* selections in precedence order */
1619d522f475Smrg		  Cardinal num_params,
1620d522f475Smrg		  Atom * targets)
1621d522f475Smrg{
1622d522f475Smrg    Atom selection;
1623d522f475Smrg    int cutbuffer;
1624d522f475Smrg    Atom target;
1625d522f475Smrg
1626956cc18dSsnj    XtermWidget xw;
1627956cc18dSsnj
162820d2c4d2Smrg    if (num_params == 0)
162920d2c4d2Smrg	return;
1630956cc18dSsnj    if ((xw = getXtermWidget(w)) == 0)
1631d522f475Smrg	return;
1632d522f475Smrg
1633956cc18dSsnj    TRACE(("xtermGetSelection num_params %d\n", num_params));
1634956cc18dSsnj    params = MapSelections(xw, params, num_params);
1635d522f475Smrg
1636d522f475Smrg    XmuInternStrings(XtDisplay(w), params, (Cardinal) 1, &selection);
1637d522f475Smrg    cutbuffer = CutBuffer(selection);
1638d522f475Smrg
1639956cc18dSsnj    TRACE(("Cutbuffer: %d, target: %s\n", cutbuffer,
1640956cc18dSsnj	   (targets
1641956cc18dSsnj	    ? visibleSelectionTarget(XtDisplay(w), targets[0])
1642956cc18dSsnj	    : "None")));
1643d522f475Smrg
1644d522f475Smrg    if (cutbuffer >= 0) {
1645d522f475Smrg	int inbytes;
1646d522f475Smrg	unsigned long nbytes;
1647d522f475Smrg	int fmt8 = 8;
1648d522f475Smrg	Atom type = XA_STRING;
1649d522f475Smrg	char *line;
1650d522f475Smrg
1651d522f475Smrg	/* 'line' is freed in SelectionReceived */
1652d522f475Smrg	line = XFetchBuffer(XtDisplay(w), &inbytes, cutbuffer);
1653d522f475Smrg	nbytes = (unsigned long) inbytes;
1654d522f475Smrg
1655d522f475Smrg	if (nbytes > 0)
1656d522f475Smrg	    SelectionReceived(w, NULL, &selection, &type, (XtPointer) line,
1657d522f475Smrg			      &nbytes, &fmt8);
1658d522f475Smrg	else if (num_params > 1) {
1659d522f475Smrg	    xtermGetSelection(w, ev_time, params + 1, num_params - 1, NULL);
1660d522f475Smrg	}
1661d522f475Smrg#if OPT_PASTE64
1662d522f475Smrg	else {
1663956cc18dSsnj	    FinishPaste64(xw);
1664d522f475Smrg	}
1665d522f475Smrg#endif
1666d522f475Smrg	return;
1667d522f475Smrg    } else {
1668d522f475Smrg	struct _SelectionList *list;
1669d522f475Smrg
1670d522f475Smrg	if (targets == NULL || targets[0] == None) {
1671d522f475Smrg	    targets = _SelectionTargets(w);
1672d522f475Smrg	}
1673d522f475Smrg
1674d522f475Smrg	if (targets != 0) {
1675d522f475Smrg	    target = targets[0];
1676d522f475Smrg
1677d522f475Smrg	    if (targets[1] == None) {	/* last target in list */
1678d522f475Smrg		params++;
1679d522f475Smrg		num_params--;
1680d522f475Smrg		targets = _SelectionTargets(w);
1681d522f475Smrg	    } else {
1682d522f475Smrg		targets = &(targets[1]);
1683d522f475Smrg	    }
1684d522f475Smrg
1685d522f475Smrg	    if (num_params) {
1686d522f475Smrg		/* 'list' is freed in SelectionReceived */
1687a1f3da82Smrg		list = TypeXtMalloc(struct _SelectionList);
1688d522f475Smrg		if (list != 0) {
1689d522f475Smrg		    list->params = params;
1690d522f475Smrg		    list->count = num_params;
1691d522f475Smrg		    list->targets = targets;
1692d522f475Smrg		    list->time = ev_time;
1693d522f475Smrg		}
1694d522f475Smrg	    } else {
1695d522f475Smrg		list = NULL;
1696d522f475Smrg	    }
1697d522f475Smrg
1698d522f475Smrg	    XtGetSelectionValue(w, selection,
1699d522f475Smrg				target,
1700d522f475Smrg				SelectionReceived,
1701d522f475Smrg				(XtPointer) list, ev_time);
1702d522f475Smrg	}
1703d522f475Smrg    }
1704d522f475Smrg}
1705d522f475Smrg
1706d522f475Smrg#if OPT_TRACE && OPT_WIDE_CHARS
1707d522f475Smrgstatic void
1708d522f475SmrgGettingSelection(Display * dpy, Atom type, Char * line, unsigned long len)
1709d522f475Smrg{
1710d522f475Smrg    Char *cp;
1711d522f475Smrg    char *name;
1712d522f475Smrg
1713d522f475Smrg    name = XGetAtomName(dpy, type);
1714d522f475Smrg
1715d522f475Smrg    TRACE(("Getting %s (%ld)\n", name, (long int) type));
1716d522f475Smrg    for (cp = line; cp < line + len; cp++) {
1717956cc18dSsnj	TRACE(("[%d:%lu]", (int) (cp + 1 - line), len));
1718d522f475Smrg	if (isprint(*cp)) {
1719d522f475Smrg	    TRACE(("%c\n", *cp));
1720d522f475Smrg	} else {
1721d522f475Smrg	    TRACE(("\\x%02x\n", *cp));
1722d522f475Smrg	}
1723d522f475Smrg    }
1724d522f475Smrg}
1725d522f475Smrg#else
1726d522f475Smrg#define GettingSelection(dpy,type,line,len)	/* nothing */
1727d522f475Smrg#endif
1728d522f475Smrg
1729d522f475Smrg#ifdef VMS
1730d522f475Smrg#  define tty_vwrite(pty,lag,l)		tt_write(lag,l)
1731d522f475Smrg#else /* !( VMS ) */
1732d522f475Smrg#  define tty_vwrite(pty,lag,l)		v_write(pty,lag,l)
1733d522f475Smrg#endif /* defined VMS */
1734d522f475Smrg
1735d522f475Smrg#if OPT_PASTE64
1736d522f475Smrg/* Return base64 code character given 6-bit number */
1737d522f475Smrgstatic const char base64_code[] = "\
1738d522f475SmrgABCDEFGHIJKLMNOPQRSTUVWXYZ\
1739d522f475Smrgabcdefghijklmnopqrstuvwxyz\
1740d522f475Smrg0123456789+/";
1741d522f475Smrgstatic void
1742d522f475Smrgbase64_flush(TScreen * screen)
1743d522f475Smrg{
1744d522f475Smrg    Char x;
1745d522f475Smrg    switch (screen->base64_count) {
1746d522f475Smrg    case 0:
1747d522f475Smrg	break;
1748d522f475Smrg    case 2:
17492eaa94a1Schristos	x = CharOf(base64_code[screen->base64_accu << 4]);
1750d522f475Smrg	tty_vwrite(screen->respond, &x, 1);
1751d522f475Smrg	break;
1752d522f475Smrg    case 4:
17532eaa94a1Schristos	x = CharOf(base64_code[screen->base64_accu << 2]);
1754d522f475Smrg	tty_vwrite(screen->respond, &x, 1);
1755d522f475Smrg	break;
1756d522f475Smrg    }
1757d522f475Smrg    if (screen->base64_pad & 3)
1758d522f475Smrg	tty_vwrite(screen->respond,
1759492d43a5Smrg		   (const Char *) "===",
1760d522f475Smrg		   (unsigned) (4 - (screen->base64_pad & 3)));
1761d522f475Smrg    screen->base64_count = 0;
1762d522f475Smrg    screen->base64_accu = 0;
1763d522f475Smrg    screen->base64_pad = 0;
1764d522f475Smrg}
1765d522f475Smrg#endif /* OPT_PASTE64 */
1766d522f475Smrg
1767d522f475Smrgstatic void
1768d522f475Smrg_qWriteSelectionData(TScreen * screen, Char * lag, unsigned length)
1769d522f475Smrg{
1770d522f475Smrg#if OPT_PASTE64
1771d522f475Smrg    if (screen->base64_paste) {
1772d522f475Smrg	/* Send data as base64 */
1773d522f475Smrg	Char *p = lag;
1774d522f475Smrg	Char buf[64];
1775d522f475Smrg	unsigned x = 0;
1776d522f475Smrg	while (length--) {
1777d522f475Smrg	    switch (screen->base64_count) {
1778d522f475Smrg	    case 0:
17792eaa94a1Schristos		buf[x++] = CharOf(base64_code[*p >> 2]);
17802eaa94a1Schristos		screen->base64_accu = (unsigned) (*p & 0x3);
1781d522f475Smrg		screen->base64_count = 2;
1782d522f475Smrg		++p;
1783d522f475Smrg		break;
1784d522f475Smrg	    case 2:
17852eaa94a1Schristos		buf[x++] = CharOf(base64_code[(screen->base64_accu << 4) +
17862eaa94a1Schristos					      (*p >> 4)]);
17872eaa94a1Schristos		screen->base64_accu = (unsigned) (*p & 0xF);
1788d522f475Smrg		screen->base64_count = 4;
1789d522f475Smrg		++p;
1790d522f475Smrg		break;
1791d522f475Smrg	    case 4:
17922eaa94a1Schristos		buf[x++] = CharOf(base64_code[(screen->base64_accu << 2) +
17932eaa94a1Schristos					      (*p >> 6)]);
17942eaa94a1Schristos		buf[x++] = CharOf(base64_code[*p & 0x3F]);
1795d522f475Smrg		screen->base64_accu = 0;
1796d522f475Smrg		screen->base64_count = 0;
1797d522f475Smrg		++p;
1798d522f475Smrg		break;
1799d522f475Smrg	    }
1800d522f475Smrg	    if (x >= 63) {
1801d522f475Smrg		/* Write 63 or 64 characters */
1802d522f475Smrg		screen->base64_pad += x;
1803d522f475Smrg		tty_vwrite(screen->respond, buf, x);
1804d522f475Smrg		x = 0;
1805d522f475Smrg	    }
1806d522f475Smrg	}
1807d522f475Smrg	if (x != 0) {
1808d522f475Smrg	    screen->base64_pad += x;
1809d522f475Smrg	    tty_vwrite(screen->respond, buf, x);
1810d522f475Smrg	}
1811d522f475Smrg    } else
1812d522f475Smrg#endif /* OPT_PASTE64 */
1813d522f475Smrg#if OPT_READLINE
1814d522f475Smrg    if (SCREEN_FLAG(screen, paste_quotes)) {
1815d522f475Smrg	while (length--) {
1816492d43a5Smrg	    tty_vwrite(screen->respond, (const Char *) "\026", 1);	/* Control-V */
1817d522f475Smrg	    tty_vwrite(screen->respond, lag++, 1);
1818d522f475Smrg	}
1819d522f475Smrg    } else
1820d522f475Smrg#endif
1821d522f475Smrg	tty_vwrite(screen->respond, lag, length);
1822d522f475Smrg}
1823d522f475Smrg
1824d522f475Smrgstatic void
182520d2c4d2Smrg_WriteSelectionData(TScreen * screen, Char * line, size_t length)
1826d522f475Smrg{
1827d522f475Smrg    /* Write data to pty a line at a time. */
1828d522f475Smrg    /* Doing this one line at a time may no longer be necessary
1829d522f475Smrg       because v_write has been re-written. */
1830d522f475Smrg
1831d522f475Smrg    Char *lag, *end;
1832d522f475Smrg
1833d522f475Smrg    /* in the VMS version, if tt_pasting isn't set to True then qio
1834d522f475Smrg       reads aren't blocked and an infinite loop is entered, where the
1835d522f475Smrg       pasted text shows up as new input, goes in again, shows up
1836d522f475Smrg       again, ad nauseum. */
1837d522f475Smrg#ifdef VMS
1838d522f475Smrg    tt_pasting = True;
1839d522f475Smrg#endif
1840d522f475Smrg
1841d522f475Smrg    end = &line[length];
1842d522f475Smrg    lag = line;
1843d522f475Smrg
1844d522f475Smrg#if OPT_PASTE64
1845d522f475Smrg    if (screen->base64_paste) {
1846d522f475Smrg	_qWriteSelectionData(screen, lag, (unsigned) (end - lag));
1847d522f475Smrg	base64_flush(screen);
1848d522f475Smrg    } else
1849d522f475Smrg#endif
1850d522f475Smrg    {
1851d522f475Smrg	if (!SCREEN_FLAG(screen, paste_literal_nl)) {
1852d522f475Smrg	    Char *cp;
1853d522f475Smrg	    for (cp = line; cp != end; cp++) {
1854d522f475Smrg		if (*cp == '\n') {
1855d522f475Smrg		    *cp = '\r';
1856d522f475Smrg		    _qWriteSelectionData(screen, lag, (unsigned) (cp - lag + 1));
1857d522f475Smrg		    lag = cp + 1;
1858d522f475Smrg		}
1859d522f475Smrg	    }
1860d522f475Smrg	}
1861d522f475Smrg
1862d522f475Smrg	if (lag != end) {
1863d522f475Smrg	    _qWriteSelectionData(screen, lag, (unsigned) (end - lag));
1864d522f475Smrg	}
1865d522f475Smrg    }
1866d522f475Smrg#ifdef VMS
1867d522f475Smrg    tt_pasting = False;
1868d522f475Smrg    tt_start_read();		/* reenable reads or a character may be lost */
1869d522f475Smrg#endif
1870d522f475Smrg}
1871d522f475Smrg
1872d522f475Smrg#if OPT_READLINE
1873d522f475Smrgstatic void
1874492d43a5Smrg_WriteKey(TScreen * screen, const Char * in)
1875d522f475Smrg{
1876d522f475Smrg    Char line[16];
1877d522f475Smrg    unsigned count = 0;
1878492d43a5Smrg    size_t length = strlen((const char *) in);
1879d522f475Smrg
1880d522f475Smrg    if (screen->control_eight_bits) {
1881d522f475Smrg	line[count++] = ANSI_CSI;
1882d522f475Smrg    } else {
1883d522f475Smrg	line[count++] = ANSI_ESC;
1884d522f475Smrg	line[count++] = '[';
1885d522f475Smrg    }
1886d522f475Smrg    while (length--)
1887d522f475Smrg	line[count++] = *in++;
1888d522f475Smrg    line[count++] = '~';
1889d522f475Smrg    tty_vwrite(screen->respond, line, count);
1890d522f475Smrg}
1891d522f475Smrg#endif /* OPT_READLINE */
1892d522f475Smrg
1893d522f475Smrg/* SelectionReceived: stuff received selection text into pty */
1894d522f475Smrg
1895d522f475Smrg/* ARGSUSED */
1896d522f475Smrgstatic void
1897d522f475SmrgSelectionReceived(Widget w,
1898d522f475Smrg		  XtPointer client_data,
1899d522f475Smrg		  Atom * selection GCC_UNUSED,
1900d522f475Smrg		  Atom * type,
1901d522f475Smrg		  XtPointer value,
1902d522f475Smrg		  unsigned long *length,
1903d522f475Smrg		  int *format)
1904d522f475Smrg{
1905d522f475Smrg    char **text_list = NULL;
1906d522f475Smrg    int text_list_count;
1907d522f475Smrg    XTextProperty text_prop;
1908d522f475Smrg    TScreen *screen;
1909d522f475Smrg    Display *dpy;
1910d522f475Smrg#if OPT_TRACE && OPT_WIDE_CHARS
1911d522f475Smrg    Char *line = (Char *) value;
1912d522f475Smrg#endif
1913d522f475Smrg
1914956cc18dSsnj    XtermWidget xw;
1915956cc18dSsnj
1916956cc18dSsnj    if ((xw = getXtermWidget(w)) == 0)
1917d522f475Smrg	return;
1918956cc18dSsnj
1919956cc18dSsnj    screen = TScreenOf(xw);
1920d522f475Smrg    dpy = XtDisplay(w);
1921d522f475Smrg
1922d522f475Smrg    if (*type == 0		/*XT_CONVERT_FAIL */
1923d522f475Smrg	|| *length == 0
1924d522f475Smrg	|| value == NULL)
1925d522f475Smrg	goto fail;
1926d522f475Smrg
1927d522f475Smrg    text_prop.value = (unsigned char *) value;
1928d522f475Smrg    text_prop.encoding = *type;
1929d522f475Smrg    text_prop.format = *format;
1930d522f475Smrg    text_prop.nitems = *length;
1931d522f475Smrg
1932956cc18dSsnj    TRACE(("SelectionReceived %s format %d, nitems %ld\n",
1933956cc18dSsnj	   visibleSelectionTarget(dpy, text_prop.encoding),
1934956cc18dSsnj	   text_prop.format,
1935956cc18dSsnj	   text_prop.nitems));
1936956cc18dSsnj
1937d522f475Smrg#if OPT_WIDE_CHARS
1938e39b573cSmrg    if (XSupportsLocale() && screen->wide_chars) {
1939d522f475Smrg	if (*type == XA_UTF8_STRING(dpy) ||
1940d522f475Smrg	    *type == XA_STRING ||
1941d522f475Smrg	    *type == XA_COMPOUND_TEXT(dpy)) {
1942d522f475Smrg	    GettingSelection(dpy, *type, line, *length);
1943d522f475Smrg	    if (Xutf8TextPropertyToTextList(dpy, &text_prop,
1944d522f475Smrg					    &text_list,
1945d522f475Smrg					    &text_list_count) < 0) {
1946e39b573cSmrg		TRACE(("default Xutf8 Conversion failed\n"));
1947d522f475Smrg		text_list = NULL;
1948d522f475Smrg	    }
1949d522f475Smrg	}
1950d522f475Smrg    } else
1951d522f475Smrg#endif /* OPT_WIDE_CHARS */
1952d522f475Smrg    {
1953d522f475Smrg	/* Convert the selection to locale's multibyte encoding. */
1954d522f475Smrg
1955d522f475Smrg	if (*type == XA_UTF8_STRING(dpy) ||
1956d522f475Smrg	    *type == XA_STRING ||
1957d522f475Smrg	    *type == XA_COMPOUND_TEXT(dpy)) {
1958d522f475Smrg	    Status rc;
1959d522f475Smrg
1960d522f475Smrg	    GettingSelection(dpy, *type, line, *length);
1961d522f475Smrg
1962d522f475Smrg#if OPT_WIDE_CHARS
1963d522f475Smrg	    if (*type == XA_UTF8_STRING(dpy) &&
1964d522f475Smrg		!(screen->wide_chars || screen->c1_printable)) {
196520d2c4d2Smrg		rc = xtermUtf8ToTextList(xw, &text_prop,
196620d2c4d2Smrg					 &text_list, &text_list_count);
1967d522f475Smrg	    } else
1968d522f475Smrg#endif
1969e39b573cSmrg	    if (*type == XA_STRING && (!XSupportsLocale() || screen->brokenSelections)) {
1970d522f475Smrg		rc = XTextPropertyToStringList(&text_prop,
1971d522f475Smrg					       &text_list, &text_list_count);
1972d522f475Smrg	    } else {
1973d522f475Smrg		rc = XmbTextPropertyToTextList(dpy, &text_prop,
1974d522f475Smrg					       &text_list,
1975d522f475Smrg					       &text_list_count);
1976d522f475Smrg	    }
1977d522f475Smrg	    if (rc < 0) {
1978d522f475Smrg		TRACE(("Conversion failed\n"));
1979d522f475Smrg		text_list = NULL;
1980d522f475Smrg	    }
1981d522f475Smrg	}
1982d522f475Smrg    }
1983d522f475Smrg
1984d522f475Smrg    if (text_list != NULL && text_list_count != 0) {
1985d522f475Smrg	int i;
1986d522f475Smrg
1987d522f475Smrg#if OPT_PASTE64
1988d522f475Smrg	if (screen->base64_paste) {
1989a1f3da82Smrg	    /* EMPTY */ ;
1990d522f475Smrg	} else
1991d522f475Smrg#endif
1992d522f475Smrg#if OPT_READLINE
1993d522f475Smrg	if (SCREEN_FLAG(screen, paste_brackets)) {
1994492d43a5Smrg	    _WriteKey(screen, (const Char *) "200");
1995d522f475Smrg	}
1996d522f475Smrg#endif
1997d522f475Smrg	for (i = 0; i < text_list_count; i++) {
199820d2c4d2Smrg	    size_t len = strlen(text_list[i]);
1999d522f475Smrg	    _WriteSelectionData(screen, (Char *) text_list[i], len);
2000d522f475Smrg	}
2001d522f475Smrg#if OPT_PASTE64
2002d522f475Smrg	if (screen->base64_paste) {
2003956cc18dSsnj	    FinishPaste64(xw);
2004d522f475Smrg	} else
2005d522f475Smrg#endif
2006d522f475Smrg#if OPT_READLINE
2007d522f475Smrg	if (SCREEN_FLAG(screen, paste_brackets)) {
2008492d43a5Smrg	    _WriteKey(screen, (const Char *) "201");
2009d522f475Smrg	}
2010d522f475Smrg#endif
2011d522f475Smrg	XFreeStringList(text_list);
2012d522f475Smrg    } else
2013d522f475Smrg	goto fail;
2014d522f475Smrg
2015d522f475Smrg    XtFree((char *) client_data);
2016d522f475Smrg    XtFree((char *) value);
2017d522f475Smrg
2018d522f475Smrg    return;
2019d522f475Smrg
2020d522f475Smrg  fail:
2021d522f475Smrg    if (client_data != 0) {
2022d522f475Smrg	struct _SelectionList *list = (struct _SelectionList *) client_data;
2023956cc18dSsnj
2024956cc18dSsnj	TRACE(("SelectionReceived ->xtermGetSelection\n"));
2025d522f475Smrg	xtermGetSelection(w, list->time,
2026d522f475Smrg			  list->params, list->count, list->targets);
2027d522f475Smrg	XtFree((char *) client_data);
2028d522f475Smrg#if OPT_PASTE64
2029d522f475Smrg    } else {
2030956cc18dSsnj	FinishPaste64(xw);
2031d522f475Smrg#endif
2032d522f475Smrg    }
2033d522f475Smrg    return;
2034d522f475Smrg}
2035d522f475Smrg
2036d522f475Smrgvoid
2037d522f475SmrgHandleInsertSelection(Widget w,
2038d522f475Smrg		      XEvent * event,	/* assumed to be XButtonEvent* */
2039d522f475Smrg		      String * params,	/* selections in precedence order */
2040d522f475Smrg		      Cardinal *num_params)
2041d522f475Smrg{
2042956cc18dSsnj    XtermWidget xw;
2043d522f475Smrg
2044956cc18dSsnj    if ((xw = getXtermWidget(w)) != 0) {
2045d522f475Smrg	if (!SendMousePosition(xw, event)) {
2046d522f475Smrg#if OPT_READLINE
2047d522f475Smrg	    int ldelta;
2048956cc18dSsnj	    TScreen *screen = TScreenOf(xw);
2049492d43a5Smrg	    if (IsBtnEvent(event)
2050d522f475Smrg	    /* Disable on Shift-mouse, including the application-mouse modes */
2051492d43a5Smrg		&& !(KeyModifiers(event) & ShiftMask)
2052d522f475Smrg		&& (screen->send_mouse_pos == MOUSE_OFF)
2053d522f475Smrg		&& SCREEN_FLAG(screen, paste_moves)
2054d522f475Smrg		&& rowOnCurrentLine(screen, eventRow(screen, event), &ldelta))
2055d522f475Smrg		ReadLineMovePoint(screen, eventColBetween(screen, event), ldelta);
2056d522f475Smrg#endif /* OPT_READLINE */
2057d522f475Smrg
2058d522f475Smrg	    xtermGetSelection(w, event->xbutton.time, params, *num_params, NULL);
2059d522f475Smrg	}
2060d522f475Smrg    }
2061d522f475Smrg}
2062d522f475Smrg
2063d522f475Smrgstatic SelectUnit
2064956cc18dSsnjEvalSelectUnit(XtermWidget xw,
2065d522f475Smrg	       Time buttonDownTime,
2066d522f475Smrg	       SelectUnit defaultUnit,
2067d522f475Smrg	       unsigned int button)
2068d522f475Smrg{
2069956cc18dSsnj    TScreen *screen = TScreenOf(xw);
2070d522f475Smrg    SelectUnit result;
2071d522f475Smrg    int delta;
2072d522f475Smrg
2073d522f475Smrg    if (button != screen->lastButton) {
207420d2c4d2Smrg	delta = screen->multiClickTime + 1;
2075d522f475Smrg    } else if (screen->lastButtonUpTime == (Time) 0) {
2076d522f475Smrg	/* first time and once in a blue moon */
2077d522f475Smrg	delta = screen->multiClickTime + 1;
2078d522f475Smrg    } else if (buttonDownTime > screen->lastButtonUpTime) {
2079d522f475Smrg	/* most of the time */
20802eaa94a1Schristos	delta = (int) (buttonDownTime - screen->lastButtonUpTime);
2081d522f475Smrg    } else {
2082d522f475Smrg	/* time has rolled over since lastButtonUpTime */
20832eaa94a1Schristos	delta = (int) ((((Time) ~ 0) - screen->lastButtonUpTime) + buttonDownTime);
2084d522f475Smrg    }
2085d522f475Smrg
2086d522f475Smrg    if (delta > screen->multiClickTime) {
2087d522f475Smrg	screen->numberOfClicks = 1;
2088d522f475Smrg	result = defaultUnit;
2089d522f475Smrg    } else {
2090d522f475Smrg	result = screen->selectMap[screen->numberOfClicks % screen->maxClicks];
2091d522f475Smrg	screen->numberOfClicks += 1;
2092d522f475Smrg    }
2093d522f475Smrg    TRACE(("EvalSelectUnit(%d) = %d\n", screen->numberOfClicks, result));
2094d522f475Smrg    return result;
2095d522f475Smrg}
2096d522f475Smrg
2097d522f475Smrgstatic void
2098d522f475Smrgdo_select_start(XtermWidget xw,
2099d522f475Smrg		XEvent * event,	/* must be XButtonEvent* */
2100d522f475Smrg		CELL * cell)
2101d522f475Smrg{
2102956cc18dSsnj    TScreen *screen = TScreenOf(xw);
2103d522f475Smrg
2104d522f475Smrg    if (SendMousePosition(xw, event))
2105d522f475Smrg	return;
2106956cc18dSsnj    screen->selectUnit = EvalSelectUnit(xw,
2107d522f475Smrg					event->xbutton.time,
2108d522f475Smrg					Select_CHAR,
2109d522f475Smrg					event->xbutton.button);
2110d522f475Smrg    screen->replyToEmacs = False;
2111d522f475Smrg
2112d522f475Smrg#if OPT_READLINE
2113d522f475Smrg    lastButtonDownTime = event->xbutton.time;
2114d522f475Smrg#endif
2115d522f475Smrg
2116d522f475Smrg    StartSelect(xw, cell);
2117d522f475Smrg}
2118d522f475Smrg
2119d522f475Smrg/* ARGSUSED */
2120d522f475Smrgvoid
2121d522f475SmrgHandleSelectStart(Widget w,
2122d522f475Smrg		  XEvent * event,	/* must be XButtonEvent* */
2123d522f475Smrg		  String * params GCC_UNUSED,
2124d522f475Smrg		  Cardinal *num_params GCC_UNUSED)
2125d522f475Smrg{
2126956cc18dSsnj    XtermWidget xw;
2127956cc18dSsnj
2128956cc18dSsnj    if ((xw = getXtermWidget(w)) != 0) {
2129956cc18dSsnj	TScreen *screen = TScreenOf(xw);
2130d522f475Smrg	CELL cell;
2131d522f475Smrg
2132d522f475Smrg	screen->firstValidRow = 0;
2133d522f475Smrg	screen->lastValidRow = screen->max_row;
2134d522f475Smrg	PointToCELL(screen, event->xbutton.y, event->xbutton.x, &cell);
2135d522f475Smrg
2136d522f475Smrg#if OPT_READLINE
2137d522f475Smrg	ExtendingSelection = 0;
2138d522f475Smrg#endif
2139d522f475Smrg
2140d522f475Smrg	do_select_start(xw, event, &cell);
2141d522f475Smrg    }
2142d522f475Smrg}
2143d522f475Smrg
2144d522f475Smrg/* ARGSUSED */
2145d522f475Smrgvoid
2146d522f475SmrgHandleKeyboardSelectStart(Widget w,
2147d522f475Smrg			  XEvent * event,	/* must be XButtonEvent* */
2148d522f475Smrg			  String * params GCC_UNUSED,
2149d522f475Smrg			  Cardinal *num_params GCC_UNUSED)
2150d522f475Smrg{
2151956cc18dSsnj    XtermWidget xw;
2152956cc18dSsnj
2153956cc18dSsnj    if ((xw = getXtermWidget(w)) != 0) {
2154956cc18dSsnj	TScreen *screen = TScreenOf(xw);
2155d522f475Smrg	do_select_start(xw, event, &screen->cursorp);
2156d522f475Smrg    }
2157d522f475Smrg}
2158d522f475Smrg
2159d522f475Smrgstatic void
2160d522f475SmrgTrackDown(XtermWidget xw, XButtonEvent * event)
2161d522f475Smrg{
2162956cc18dSsnj    TScreen *screen = TScreenOf(xw);
2163d522f475Smrg    CELL cell;
2164d522f475Smrg
2165956cc18dSsnj    screen->selectUnit = EvalSelectUnit(xw,
2166d522f475Smrg					event->time,
2167d522f475Smrg					Select_CHAR,
2168d522f475Smrg					event->button);
2169d522f475Smrg    if (screen->numberOfClicks > 1) {
2170d522f475Smrg	PointToCELL(screen, event->y, event->x, &cell);
2171d522f475Smrg	screen->replyToEmacs = True;
2172d522f475Smrg	StartSelect(xw, &cell);
2173d522f475Smrg    } else {
2174d522f475Smrg	screen->waitingForTrackInfo = True;
2175492d43a5Smrg	EditorButton(xw, event);
2176d522f475Smrg    }
2177d522f475Smrg}
2178d522f475Smrg
2179d522f475Smrg#define boundsCheck(x)	if (x < 0) \
2180d522f475Smrg			    x = 0; \
2181d522f475Smrg			else if (x >= screen->max_row) \
2182d522f475Smrg			    x = screen->max_row
2183d522f475Smrg
2184d522f475Smrgvoid
2185d522f475SmrgTrackMouse(XtermWidget xw,
2186d522f475Smrg	   int func,
2187d522f475Smrg	   CELL * start,
2188d522f475Smrg	   int firstrow,
2189d522f475Smrg	   int lastrow)
2190d522f475Smrg{
2191956cc18dSsnj    TScreen *screen = TScreenOf(xw);
2192d522f475Smrg
2193d522f475Smrg    if (screen->waitingForTrackInfo) {	/* if Timed, ignore */
2194d522f475Smrg	screen->waitingForTrackInfo = False;
2195d522f475Smrg
2196d522f475Smrg	if (func != 0) {
2197d522f475Smrg	    CELL first = *start;
2198d522f475Smrg
2199d522f475Smrg	    boundsCheck(first.row);
2200d522f475Smrg	    boundsCheck(firstrow);
2201d522f475Smrg	    boundsCheck(lastrow);
2202d522f475Smrg	    screen->firstValidRow = firstrow;
2203d522f475Smrg	    screen->lastValidRow = lastrow;
2204d522f475Smrg	    screen->replyToEmacs = True;
2205d522f475Smrg	    StartSelect(xw, &first);
2206d522f475Smrg	}
2207d522f475Smrg    }
2208d522f475Smrg}
2209d522f475Smrg
2210d522f475Smrgstatic void
2211d522f475SmrgStartSelect(XtermWidget xw, const CELL * cell)
2212d522f475Smrg{
2213956cc18dSsnj    TScreen *screen = TScreenOf(xw);
2214d522f475Smrg
2215d522f475Smrg    TRACE(("StartSelect row=%d, col=%d\n", cell->row, cell->col));
2216d522f475Smrg    if (screen->cursor_state)
2217d522f475Smrg	HideCursor();
2218d522f475Smrg    if (screen->numberOfClicks == 1) {
2219d522f475Smrg	/* set start of selection */
2220d522f475Smrg	screen->rawPos = *cell;
2221d522f475Smrg    }
2222d522f475Smrg    /* else use old values in rawPos */
2223d522f475Smrg    screen->saveStartR = screen->startExt = screen->rawPos;
2224d522f475Smrg    screen->saveEndR = screen->endExt = screen->rawPos;
2225d522f475Smrg    if (Coordinate(screen, cell) < Coordinate(screen, &(screen->rawPos))) {
2226d522f475Smrg	screen->eventMode = LEFTEXTENSION;
2227d522f475Smrg	screen->startExt = *cell;
2228d522f475Smrg    } else {
2229d522f475Smrg	screen->eventMode = RIGHTEXTENSION;
2230d522f475Smrg	screen->endExt = *cell;
2231d522f475Smrg    }
2232d522f475Smrg    ComputeSelect(xw, &(screen->startExt), &(screen->endExt), False);
2233d522f475Smrg}
2234d522f475Smrg
2235d522f475Smrgstatic void
2236d522f475SmrgEndExtend(XtermWidget xw,
2237d522f475Smrg	  XEvent * event,	/* must be XButtonEvent */
2238d522f475Smrg	  String * params,	/* selections */
2239d522f475Smrg	  Cardinal num_params,
2240d522f475Smrg	  Bool use_cursor_loc)
2241d522f475Smrg{
2242d522f475Smrg    CELL cell;
2243d522f475Smrg    unsigned count;
2244956cc18dSsnj    TScreen *screen = TScreenOf(xw);
2245492d43a5Smrg    Char line[20];
2246d522f475Smrg
2247d522f475Smrg    if (use_cursor_loc) {
2248d522f475Smrg	cell = screen->cursorp;
2249d522f475Smrg    } else {
2250d522f475Smrg	PointToCELL(screen, event->xbutton.y, event->xbutton.x, &cell);
2251d522f475Smrg    }
2252d522f475Smrg    ExtendExtend(xw, &cell);
2253d522f475Smrg    screen->lastButtonUpTime = event->xbutton.time;
2254d522f475Smrg    screen->lastButton = event->xbutton.button;
2255d522f475Smrg    if (!isSameCELL(&(screen->startSel), &(screen->endSel))) {
2256d522f475Smrg	if (screen->replyToEmacs) {
2257d522f475Smrg	    count = 0;
2258d522f475Smrg	    if (screen->control_eight_bits) {
2259d522f475Smrg		line[count++] = ANSI_CSI;
2260d522f475Smrg	    } else {
2261d522f475Smrg		line[count++] = ANSI_ESC;
2262d522f475Smrg		line[count++] = '[';
2263d522f475Smrg	    }
2264d522f475Smrg	    if (isSameCELL(&(screen->rawPos), &(screen->startSel))
2265d522f475Smrg		&& isSameCELL(&cell, &(screen->endSel))) {
2266d522f475Smrg		/* Use short-form emacs select */
2267d522f475Smrg		line[count++] = 't';
2268492d43a5Smrg		count = EmitMousePosition(screen, line, count, screen->endSel.col);
2269492d43a5Smrg		count = EmitMousePosition(screen, line, count, screen->endSel.row);
2270d522f475Smrg	    } else {
2271d522f475Smrg		/* long-form, specify everything */
2272d522f475Smrg		line[count++] = 'T';
2273492d43a5Smrg		count = EmitMousePosition(screen, line, count, screen->startSel.col);
2274492d43a5Smrg		count = EmitMousePosition(screen, line, count, screen->startSel.row);
2275492d43a5Smrg		count = EmitMousePosition(screen, line, count, screen->endSel.col);
2276492d43a5Smrg		count = EmitMousePosition(screen, line, count, screen->endSel.row);
2277492d43a5Smrg		count = EmitMousePosition(screen, line, count, cell.col);
2278492d43a5Smrg		count = EmitMousePosition(screen, line, count, cell.row);
2279d522f475Smrg	    }
2280d522f475Smrg	    v_write(screen->respond, line, count);
2281d522f475Smrg	    TrackText(xw, &zeroCELL, &zeroCELL);
2282d522f475Smrg	}
2283d522f475Smrg    }
2284d522f475Smrg    SelectSet(xw, event, params, num_params);
2285d522f475Smrg    screen->eventMode = NORMAL;
2286d522f475Smrg}
2287d522f475Smrg
2288d522f475Smrgvoid
2289d522f475SmrgHandleSelectSet(Widget w,
2290d522f475Smrg		XEvent * event,
2291d522f475Smrg		String * params,
2292d522f475Smrg		Cardinal *num_params)
2293d522f475Smrg{
2294956cc18dSsnj    XtermWidget xw;
2295956cc18dSsnj
2296956cc18dSsnj    if ((xw = getXtermWidget(w)) != 0) {
2297956cc18dSsnj	SelectSet(xw, event, params, *num_params);
2298d522f475Smrg    }
2299d522f475Smrg}
2300d522f475Smrg
2301d522f475Smrg/* ARGSUSED */
2302d522f475Smrgstatic void
2303d522f475SmrgSelectSet(XtermWidget xw,
2304d522f475Smrg	  XEvent * event GCC_UNUSED,
2305d522f475Smrg	  String * params,
2306d522f475Smrg	  Cardinal num_params)
2307d522f475Smrg{
2308956cc18dSsnj    TScreen *screen = TScreenOf(xw);
2309d522f475Smrg
2310d522f475Smrg    TRACE(("SelectSet\n"));
2311d522f475Smrg    /* Only do select stuff if non-null select */
2312d522f475Smrg    if (!isSameCELL(&(screen->startSel), &(screen->endSel))) {
2313d522f475Smrg	SaltTextAway(xw, &(screen->startSel), &(screen->endSel), params, num_params);
2314d522f475Smrg    } else {
2315d522f475Smrg	DisownSelection(xw);
2316d522f475Smrg    }
2317d522f475Smrg}
2318d522f475Smrg
2319d522f475Smrg#define Abs(x)		((x) < 0 ? -(x) : (x))
2320d522f475Smrg
2321d522f475Smrg/* ARGSUSED */
2322d522f475Smrgstatic void
2323d522f475Smrgdo_start_extend(XtermWidget xw,
2324d522f475Smrg		XEvent * event,	/* must be XButtonEvent* */
2325d522f475Smrg		String * params GCC_UNUSED,
2326d522f475Smrg		Cardinal *num_params GCC_UNUSED,
2327d522f475Smrg		Bool use_cursor_loc)
2328d522f475Smrg{
2329956cc18dSsnj    TScreen *screen = TScreenOf(xw);
2330d522f475Smrg    int coord;
2331d522f475Smrg    CELL cell;
2332d522f475Smrg
2333d522f475Smrg    if (SendMousePosition(xw, event))
2334d522f475Smrg	return;
2335d522f475Smrg
2336d522f475Smrg    screen->firstValidRow = 0;
2337d522f475Smrg    screen->lastValidRow = screen->max_row;
2338d522f475Smrg#if OPT_READLINE
2339492d43a5Smrg    if ((KeyModifiers(event) & ShiftMask)
2340d522f475Smrg	|| event->xbutton.button != Button3
2341d522f475Smrg	|| !(SCREEN_FLAG(screen, dclick3_deletes)))
2342d522f475Smrg#endif
2343956cc18dSsnj	screen->selectUnit = EvalSelectUnit(xw,
2344d522f475Smrg					    event->xbutton.time,
2345d522f475Smrg					    screen->selectUnit,
2346d522f475Smrg					    event->xbutton.button);
2347d522f475Smrg    screen->replyToEmacs = False;
2348d522f475Smrg
2349d522f475Smrg#if OPT_READLINE
2350d522f475Smrg    CheckSecondPress3(screen, event);
2351d522f475Smrg#endif
2352d522f475Smrg
2353d522f475Smrg    if (screen->numberOfClicks == 1
2354d522f475Smrg	|| (SCREEN_FLAG(screen, dclick3_deletes)	/* Dclick special */
2355492d43a5Smrg	    &&!(KeyModifiers(event) & ShiftMask))) {
2356d522f475Smrg	/* Save existing selection so we can reestablish it if the guy
2357d522f475Smrg	   extends past the other end of the selection */
2358d522f475Smrg	screen->saveStartR = screen->startExt = screen->startRaw;
2359d522f475Smrg	screen->saveEndR = screen->endExt = screen->endRaw;
2360d522f475Smrg    } else {
2361d522f475Smrg	/* He just needed the selection mode changed, use old values. */
2362d522f475Smrg	screen->startExt = screen->startRaw = screen->saveStartR;
2363d522f475Smrg	screen->endExt = screen->endRaw = screen->saveEndR;
2364d522f475Smrg    }
2365d522f475Smrg    if (use_cursor_loc) {
2366d522f475Smrg	cell = screen->cursorp;
2367d522f475Smrg    } else {
2368d522f475Smrg	PointToCELL(screen, event->xbutton.y, event->xbutton.x, &cell);
2369d522f475Smrg    }
2370d522f475Smrg    coord = Coordinate(screen, &cell);
2371d522f475Smrg
2372d522f475Smrg    if (Abs(coord - Coordinate(screen, &(screen->startSel)))
2373d522f475Smrg	< Abs(coord - Coordinate(screen, &(screen->endSel)))
2374d522f475Smrg	|| coord < Coordinate(screen, &(screen->startSel))) {
2375d522f475Smrg	/* point is close to left side of selection */
2376d522f475Smrg	screen->eventMode = LEFTEXTENSION;
2377d522f475Smrg	screen->startExt = cell;
2378d522f475Smrg    } else {
2379d522f475Smrg	/* point is close to left side of selection */
2380d522f475Smrg	screen->eventMode = RIGHTEXTENSION;
2381d522f475Smrg	screen->endExt = cell;
2382d522f475Smrg    }
2383d522f475Smrg    ComputeSelect(xw, &(screen->startExt), &(screen->endExt), True);
2384d522f475Smrg
2385d522f475Smrg#if OPT_READLINE
2386d522f475Smrg    if (!isSameCELL(&(screen->startSel), &(screen->endSel)))
2387d522f475Smrg	ExtendingSelection = 1;
2388d522f475Smrg#endif
2389d522f475Smrg}
2390d522f475Smrg
2391d522f475Smrgstatic void
2392d522f475SmrgExtendExtend(XtermWidget xw, const CELL * cell)
2393d522f475Smrg{
2394956cc18dSsnj    TScreen *screen = TScreenOf(xw);
2395d522f475Smrg    int coord = Coordinate(screen, cell);
2396d522f475Smrg
2397d522f475Smrg    TRACE(("ExtendExtend row=%d, col=%d\n", cell->row, cell->col));
2398d522f475Smrg    if (screen->eventMode == LEFTEXTENSION
2399d522f475Smrg	&& ((coord + (screen->selectUnit != Select_CHAR))
2400d522f475Smrg	    > Coordinate(screen, &(screen->endSel)))) {
2401d522f475Smrg	/* Whoops, he's changed his mind.  Do RIGHTEXTENSION */
2402d522f475Smrg	screen->eventMode = RIGHTEXTENSION;
2403d522f475Smrg	screen->startExt = screen->saveStartR;
2404d522f475Smrg    } else if (screen->eventMode == RIGHTEXTENSION
2405d522f475Smrg	       && coord < Coordinate(screen, &(screen->startSel))) {
2406d522f475Smrg	/* Whoops, he's changed his mind.  Do LEFTEXTENSION */
2407d522f475Smrg	screen->eventMode = LEFTEXTENSION;
2408d522f475Smrg	screen->endExt = screen->saveEndR;
2409d522f475Smrg    }
2410d522f475Smrg    if (screen->eventMode == LEFTEXTENSION) {
2411d522f475Smrg	screen->startExt = *cell;
2412d522f475Smrg    } else {
2413d522f475Smrg	screen->endExt = *cell;
2414d522f475Smrg    }
2415d522f475Smrg    ComputeSelect(xw, &(screen->startExt), &(screen->endExt), False);
2416d522f475Smrg
2417d522f475Smrg#if OPT_READLINE
2418d522f475Smrg    if (!isSameCELL(&(screen->startSel), &(screen->endSel)))
2419d522f475Smrg	ExtendingSelection = 1;
2420d522f475Smrg#endif
2421d522f475Smrg}
2422d522f475Smrg
2423d522f475Smrgvoid
2424d522f475SmrgHandleStartExtend(Widget w,
2425d522f475Smrg		  XEvent * event,	/* must be XButtonEvent* */
2426d522f475Smrg		  String * params,	/* unused */
2427d522f475Smrg		  Cardinal *num_params)		/* unused */
2428d522f475Smrg{
2429956cc18dSsnj    XtermWidget xw;
2430956cc18dSsnj
2431956cc18dSsnj    if ((xw = getXtermWidget(w)) != 0) {
2432956cc18dSsnj	do_start_extend(xw, event, params, num_params, False);
2433956cc18dSsnj    }
2434d522f475Smrg}
2435d522f475Smrg
2436d522f475Smrgvoid
2437d522f475SmrgHandleKeyboardStartExtend(Widget w,
2438d522f475Smrg			  XEvent * event,	/* must be XButtonEvent* */
2439d522f475Smrg			  String * params,	/* unused */
2440d522f475Smrg			  Cardinal *num_params)		/* unused */
2441d522f475Smrg{
2442956cc18dSsnj    XtermWidget xw;
2443956cc18dSsnj
2444956cc18dSsnj    if ((xw = getXtermWidget(w)) != 0) {
2445956cc18dSsnj	do_start_extend(xw, event, params, num_params, True);
2446956cc18dSsnj    }
2447d522f475Smrg}
2448d522f475Smrg
2449d522f475Smrgvoid
2450d522f475SmrgScrollSelection(TScreen * screen, int amount, Bool always)
2451d522f475Smrg{
2452d522f475Smrg    int minrow = INX2ROW(screen, -screen->savedlines);
2453d522f475Smrg    int maxrow = INX2ROW(screen, screen->max_row);
2454d522f475Smrg    int maxcol = screen->max_col;
2455d522f475Smrg
2456d522f475Smrg#define scroll_update_one(cell) \
2457d522f475Smrg	(cell)->row += amount; \
2458d522f475Smrg	if ((cell)->row < minrow) { \
2459d522f475Smrg	    (cell)->row = minrow; \
2460d522f475Smrg	    (cell)->col = 0; \
2461d522f475Smrg	} \
2462d522f475Smrg	if ((cell)->row > maxrow) { \
2463d522f475Smrg	    (cell)->row = maxrow; \
2464d522f475Smrg	    (cell)->col = maxcol; \
2465d522f475Smrg	}
2466d522f475Smrg
2467d522f475Smrg    scroll_update_one(&(screen->startRaw));
2468d522f475Smrg    scroll_update_one(&(screen->endRaw));
2469d522f475Smrg    scroll_update_one(&(screen->startSel));
2470d522f475Smrg    scroll_update_one(&(screen->endSel));
2471d522f475Smrg
2472d522f475Smrg    scroll_update_one(&(screen->rawPos));
2473d522f475Smrg
2474d522f475Smrg    /*
2475d522f475Smrg     * If we are told to scroll the selection but it lies outside the scrolling
2476d522f475Smrg     * margins, then that could cause the selection to move (bad).  It is not
2477d522f475Smrg     * simple to fix, because this function is called both for the scrollbar
2478d522f475Smrg     * actions as well as application scrolling.  The 'always' flag is set in
2479d522f475Smrg     * the former case.  The rest of the logic handles the latter.
2480d522f475Smrg     */
2481d522f475Smrg    if (ScrnHaveSelection(screen)) {
2482d522f475Smrg	int adjust;
2483d522f475Smrg
2484d522f475Smrg	adjust = ROW2INX(screen, screen->startH.row);
2485d522f475Smrg	if (always
2486d522f475Smrg	    || !ScrnHaveLineMargins(screen)
2487d522f475Smrg	    || ScrnIsLineInMargins(screen, adjust)) {
2488d522f475Smrg	    scroll_update_one(&screen->startH);
2489d522f475Smrg	}
2490d522f475Smrg	adjust = ROW2INX(screen, screen->endH.row);
2491d522f475Smrg	if (always
2492d522f475Smrg	    || !ScrnHaveLineMargins(screen)
2493d522f475Smrg	    || ScrnIsLineInMargins(screen, adjust)) {
2494d522f475Smrg	    scroll_update_one(&screen->endH);
2495d522f475Smrg	}
2496d522f475Smrg    }
2497d522f475Smrg
2498d522f475Smrg    screen->startHCoord = Coordinate(screen, &screen->startH);
2499d522f475Smrg    screen->endHCoord = Coordinate(screen, &screen->endH);
2500d522f475Smrg}
2501d522f475Smrg
2502d522f475Smrg/*ARGSUSED*/
2503d522f475Smrgvoid
2504d522f475SmrgResizeSelection(TScreen * screen GCC_UNUSED, int rows, int cols)
2505d522f475Smrg{
2506d522f475Smrg    rows--;			/* decr to get 0-max */
2507d522f475Smrg    cols--;
2508d522f475Smrg
2509d522f475Smrg    if (screen->startRaw.row > rows)
2510d522f475Smrg	screen->startRaw.row = rows;
2511d522f475Smrg    if (screen->startSel.row > rows)
2512d522f475Smrg	screen->startSel.row = rows;
2513d522f475Smrg    if (screen->endRaw.row > rows)
2514d522f475Smrg	screen->endRaw.row = rows;
2515d522f475Smrg    if (screen->endSel.row > rows)
2516d522f475Smrg	screen->endSel.row = rows;
2517d522f475Smrg    if (screen->rawPos.row > rows)
2518d522f475Smrg	screen->rawPos.row = rows;
2519d522f475Smrg
2520d522f475Smrg    if (screen->startRaw.col > cols)
2521d522f475Smrg	screen->startRaw.col = cols;
2522d522f475Smrg    if (screen->startSel.col > cols)
2523d522f475Smrg	screen->startSel.col = cols;
2524d522f475Smrg    if (screen->endRaw.col > cols)
2525d522f475Smrg	screen->endRaw.col = cols;
2526d522f475Smrg    if (screen->endSel.col > cols)
2527d522f475Smrg	screen->endSel.col = cols;
2528d522f475Smrg    if (screen->rawPos.col > cols)
2529d522f475Smrg	screen->rawPos.col = cols;
2530d522f475Smrg}
2531d522f475Smrg
2532d522f475Smrg#if OPT_WIDE_CHARS
2533d522f475SmrgBool
2534d522f475Smrgiswide(int i)
2535d522f475Smrg{
253620d2c4d2Smrg    return (i == HIDDEN_CHAR) || (WideCells(i) == 2);
2537d522f475Smrg}
2538d522f475Smrg
2539d522f475Smrg#define isWideCell(row, col) iswide((int)XTERM_CELL(row, col))
2540d522f475Smrg#endif
2541d522f475Smrg
2542d522f475Smrgstatic void
2543d522f475SmrgPointToCELL(TScreen * screen,
2544d522f475Smrg	    int y,
2545d522f475Smrg	    int x,
2546d522f475Smrg	    CELL * cell)
2547d522f475Smrg/* Convert pixel coordinates to character coordinates.
2548d522f475Smrg   Rows are clipped between firstValidRow and lastValidRow.
2549d522f475Smrg   Columns are clipped between to be 0 or greater, but are not clipped to some
2550d522f475Smrg       maximum value. */
2551d522f475Smrg{
2552d522f475Smrg    cell->row = (y - screen->border) / FontHeight(screen);
2553d522f475Smrg    if (cell->row < screen->firstValidRow)
2554d522f475Smrg	cell->row = screen->firstValidRow;
2555d522f475Smrg    else if (cell->row > screen->lastValidRow)
2556d522f475Smrg	cell->row = screen->lastValidRow;
2557d522f475Smrg    cell->col = (x - OriginX(screen)) / FontWidth(screen);
2558d522f475Smrg    if (cell->col < 0)
2559d522f475Smrg	cell->col = 0;
2560d522f475Smrg    else if (cell->col > MaxCols(screen)) {
2561d522f475Smrg	cell->col = MaxCols(screen);
2562d522f475Smrg    }
2563d522f475Smrg#if OPT_WIDE_CHARS
2564d522f475Smrg    /*
2565d522f475Smrg     * If we got a click on the right half of a doublewidth character,
2566d522f475Smrg     * pretend it happened on the left half.
2567d522f475Smrg     */
2568d522f475Smrg    if (cell->col > 0
2569d522f475Smrg	&& isWideCell(cell->row, cell->col - 1)
2570d522f475Smrg	&& (XTERM_CELL(cell->row, cell->col) == HIDDEN_CHAR)) {
2571d522f475Smrg	cell->col -= 1;
2572d522f475Smrg    }
2573d522f475Smrg#endif
2574d522f475Smrg}
2575d522f475Smrg
2576d522f475Smrg/*
2577d522f475Smrg * Find the last column at which text was drawn on the given row.
2578d522f475Smrg */
2579d522f475Smrgstatic int
2580956cc18dSsnjLastTextCol(TScreen * screen, LineData * ld, int row)
2581d522f475Smrg{
258220d2c4d2Smrg    int i = -1;
2583d522f475Smrg    Char *ch;
2584d522f475Smrg
258520d2c4d2Smrg    if (ld != 0) {
258620d2c4d2Smrg	if (okScrnRow(screen, row)) {
258720d2c4d2Smrg	    for (i = screen->max_col,
258820d2c4d2Smrg		 ch = ld->attribs + i;
258920d2c4d2Smrg		 i >= 0 && !(*ch & CHARDRAWN);
259020d2c4d2Smrg		 ch--, i--) {
259120d2c4d2Smrg		;
259220d2c4d2Smrg	    }
2593d522f475Smrg#if OPT_DEC_CHRSET
259420d2c4d2Smrg	    if (CSET_DOUBLE(GetLineDblCS(ld))) {
259520d2c4d2Smrg		i *= 2;
259620d2c4d2Smrg	    }
2597d522f475Smrg#endif
259820d2c4d2Smrg	}
2599d522f475Smrg    }
2600d522f475Smrg    return (i);
2601d522f475Smrg}
2602d522f475Smrg
2603d522f475Smrg#if !OPT_WIDE_CHARS
2604d522f475Smrg/*
2605d522f475Smrg** double click table for cut and paste in 8 bits
2606d522f475Smrg**
2607d522f475Smrg** This table is divided in four parts :
2608d522f475Smrg**
2609d522f475Smrg**	- control characters	[0,0x1f] U [0x80,0x9f]
2610d522f475Smrg**	- separators		[0x20,0x3f] U [0xa0,0xb9]
2611d522f475Smrg**	- binding characters	[0x40,0x7f] U [0xc0,0xff]
2612d522f475Smrg**	- exceptions
2613d522f475Smrg*/
2614d522f475Smrg/* *INDENT-OFF* */
2615d522f475Smrgstatic int charClass[256] =
2616d522f475Smrg{
2617d522f475Smrg/* NUL  SOH  STX  ETX  EOT  ENQ  ACK  BEL */
2618d522f475Smrg    32,  1,    1,   1,   1,   1,   1,   1,
2619d522f475Smrg/*  BS   HT   NL   VT   NP   CR   SO   SI */
2620d522f475Smrg     1,  32,   1,   1,   1,   1,   1,   1,
2621d522f475Smrg/* DLE  DC1  DC2  DC3  DC4  NAK  SYN  ETB */
2622d522f475Smrg     1,   1,   1,   1,   1,   1,   1,   1,
2623d522f475Smrg/* CAN   EM  SUB  ESC   FS   GS   RS   US */
2624d522f475Smrg     1,   1,   1,   1,   1,   1,   1,   1,
2625d522f475Smrg/*  SP    !    "    #    $    %    &    ' */
2626d522f475Smrg    32,  33,  34,  35,  36,  37,  38,  39,
2627d522f475Smrg/*   (    )    *    +    ,    -    .    / */
2628d522f475Smrg    40,  41,  42,  43,  44,  45,  46,  47,
2629d522f475Smrg/*   0    1    2    3    4    5    6    7 */
2630d522f475Smrg    48,  48,  48,  48,  48,  48,  48,  48,
2631d522f475Smrg/*   8    9    :    ;    <    =    >    ? */
2632d522f475Smrg    48,  48,  58,  59,  60,  61,  62,  63,
2633d522f475Smrg/*   @    A    B    C    D    E    F    G */
2634d522f475Smrg    64,  48,  48,  48,  48,  48,  48,  48,
2635d522f475Smrg/*   H    I    J    K    L    M    N    O */
2636d522f475Smrg    48,  48,  48,  48,  48,  48,  48,  48,
2637d522f475Smrg/*   P    Q    R    S    T    U    V    W */
2638d522f475Smrg    48,  48,  48,  48,  48,  48,  48,  48,
2639d522f475Smrg/*   X    Y    Z    [    \    ]    ^    _ */
2640d522f475Smrg    48,  48,  48,  91,  92,  93,  94,  48,
2641d522f475Smrg/*   `    a    b    c    d    e    f    g */
2642d522f475Smrg    96,  48,  48,  48,  48,  48,  48,  48,
2643d522f475Smrg/*   h    i    j    k    l    m    n    o */
2644d522f475Smrg    48,  48,  48,  48,  48,  48,  48,  48,
2645d522f475Smrg/*   p    q    r    s    t    u    v    w */
2646d522f475Smrg    48,  48,  48,  48,  48,  48,  48,  48,
2647d522f475Smrg/*   x    y    z    {    |    }    ~  DEL */
2648d522f475Smrg    48,  48,  48, 123, 124, 125, 126,   1,
2649d522f475Smrg/* x80  x81  x82  x83  IND  NEL  SSA  ESA */
2650d522f475Smrg    1,    1,   1,   1,   1,   1,   1,   1,
2651d522f475Smrg/* HTS  HTJ  VTS  PLD  PLU   RI  SS2  SS3 */
2652d522f475Smrg    1,    1,   1,   1,   1,   1,   1,   1,
2653d522f475Smrg/* DCS  PU1  PU2  STS  CCH   MW  SPA  EPA */
2654d522f475Smrg    1,    1,   1,   1,   1,   1,   1,   1,
2655d522f475Smrg/* x98  x99  x9A  CSI   ST  OSC   PM  APC */
2656d522f475Smrg    1,    1,   1,   1,   1,   1,   1,   1,
2657d522f475Smrg/*   -    i   c/    L   ox   Y-    |   So */
2658d522f475Smrg    160, 161, 162, 163, 164, 165, 166, 167,
2659d522f475Smrg/*  ..   c0   ip   <<    _        R0    - */
2660d522f475Smrg    168, 169, 170, 171, 172, 173, 174, 175,
2661d522f475Smrg/*   o   +-    2    3    '    u   q|    . */
2662d522f475Smrg    176, 177, 178, 179, 180, 181, 182, 183,
2663d522f475Smrg/*   ,    1    2   >>  1/4  1/2  3/4    ? */
2664d522f475Smrg    184, 185, 186, 187, 188, 189, 190, 191,
2665d522f475Smrg/*  A`   A'   A^   A~   A:   Ao   AE   C, */
2666d522f475Smrg     48,  48,  48,  48,  48,  48,  48,  48,
2667d522f475Smrg/*  E`   E'   E^   E:   I`   I'   I^   I: */
2668d522f475Smrg     48,  48,  48,  48,  48,  48,  48,  48,
2669d522f475Smrg/*  D-   N~   O`   O'   O^   O~   O:    X */
2670d522f475Smrg     48,  48,  48,  48,  48,  48,  48, 215,
2671d522f475Smrg/*  O/   U`   U'   U^   U:   Y'    P    B */
2672d522f475Smrg     48,  48,  48,  48,  48,  48,  48,  48,
2673d522f475Smrg/*  a`   a'   a^   a~   a:   ao   ae   c, */
2674d522f475Smrg     48,  48,  48,  48,  48,  48,  48,  48,
2675d522f475Smrg/*  e`   e'   e^   e:    i`  i'   i^   i: */
2676d522f475Smrg     48,  48,  48,  48,  48,  48,  48,  48,
2677d522f475Smrg/*   d   n~   o`   o'   o^   o~   o:   -: */
2678d522f475Smrg     48,  48,  48,  48,  48,  48,  48, 247,
2679d522f475Smrg/*  o/   u`   u'   u^   u:   y'    P   y: */
2680d522f475Smrg     48,  48,  48,  48,  48,  48,  48,  48};
2681d522f475Smrg/* *INDENT-ON* */
2682d522f475Smrg
2683d522f475Smrgint
2684d522f475SmrgSetCharacterClassRange(int low,	/* in range of [0..255] */
2685d522f475Smrg		       int high,
2686d522f475Smrg		       int value)	/* arbitrary */
2687d522f475Smrg{
2688d522f475Smrg
2689d522f475Smrg    if (low < 0 || high > 255 || high < low)
2690d522f475Smrg	return (-1);
2691d522f475Smrg
2692d522f475Smrg    for (; low <= high; low++)
2693d522f475Smrg	charClass[low] = value;
2694d522f475Smrg
2695d522f475Smrg    return (0);
2696d522f475Smrg}
2697d522f475Smrg#endif
2698d522f475Smrg
2699d522f475Smrgstatic int
2700956cc18dSsnjclass_of(LineData * ld, CELL * cell)
2701d522f475Smrg{
2702d522f475Smrg    CELL temp = *cell;
2703d522f475Smrg
2704d522f475Smrg#if OPT_DEC_CHRSET
2705956cc18dSsnj    if (CSET_DOUBLE(GetLineDblCS(ld))) {
2706d522f475Smrg	temp.col /= 2;
2707d522f475Smrg    }
2708d522f475Smrg#endif
2709d522f475Smrg
271020d2c4d2Smrg    assert(temp.col < ld->lineSize);
2711956cc18dSsnj    return CharacterClass((int) (ld->charData[temp.col]));
2712d522f475Smrg}
2713956cc18dSsnj
2714956cc18dSsnj#if OPT_WIDE_CHARS
2715956cc18dSsnj#define CClassSelects(name, cclass) \
2716956cc18dSsnj	 (CClassOf(name) == cclass \
2717956cc18dSsnj	 || XTERM_CELL(screen->name.row, screen->name.col) == HIDDEN_CHAR)
2718d522f475Smrg#else
2719956cc18dSsnj#define CClassSelects(name, cclass) \
2720956cc18dSsnj	 (class_of(ld.name, &((screen->name))) == cclass)
2721d522f475Smrg#endif
2722d522f475Smrg
2723956cc18dSsnj#define CClassOf(name) class_of(ld.name, &((screen->name)))
2724956cc18dSsnj
2725d522f475Smrg/*
2726d522f475Smrg * If the given column is past the end of text on the given row, bump to the
2727d522f475Smrg * beginning of the next line.
2728d522f475Smrg */
2729d522f475Smrgstatic Boolean
2730d522f475SmrgokPosition(TScreen * screen,
2731956cc18dSsnj	   LineData ** ld,
2732d522f475Smrg	   CELL * cell)
2733d522f475Smrg{
273420d2c4d2Smrg    Boolean result = True;
273520d2c4d2Smrg
273620d2c4d2Smrg    if (cell->row > screen->max_row) {
273720d2c4d2Smrg	result = False;
273820d2c4d2Smrg    } else if (cell->col > (LastTextCol(screen, *ld, cell->row) + 1)) {
273920d2c4d2Smrg	if (cell->row < screen->max_row) {
274020d2c4d2Smrg	    cell->col = 0;
274120d2c4d2Smrg	    *ld = GET_LINEDATA(screen, ++cell->row);
274220d2c4d2Smrg	    result = False;
274320d2c4d2Smrg	}
2744d522f475Smrg    }
274520d2c4d2Smrg    return result;
2746d522f475Smrg}
2747d522f475Smrg
2748d522f475Smrgstatic void
2749956cc18dSsnjtrimLastLine(TScreen * screen,
2750956cc18dSsnj	     LineData ** ld,
2751956cc18dSsnj	     CELL * last)
2752d522f475Smrg{
275320d2c4d2Smrg    if (screen->cutNewline && last->row < screen->max_row) {
2754d522f475Smrg	last->col = 0;
2755956cc18dSsnj	*ld = GET_LINEDATA(screen, ++last->row);
2756d522f475Smrg    } else {
2757956cc18dSsnj	last->col = LastTextCol(screen, *ld, last->row) + 1;
2758d522f475Smrg    }
2759d522f475Smrg}
2760d522f475Smrg
2761d522f475Smrg#if OPT_SELECT_REGEX
2762d522f475Smrg/*
2763d522f475Smrg * Returns the first row of a wrapped line.
2764d522f475Smrg */
2765d522f475Smrgstatic int
2766d522f475SmrgfirstRowOfLine(TScreen * screen, int row, Bool visible)
2767d522f475Smrg{
2768956cc18dSsnj    LineData *ld = 0;
2769d522f475Smrg    int limit = visible ? 0 : -screen->savedlines;
2770d522f475Smrg
2771d522f475Smrg    while (row > limit &&
2772956cc18dSsnj	   (ld = GET_LINEDATA(screen, row - 1)) != 0 &&
2773956cc18dSsnj	   LineTstWrapped(ld)) {
2774d522f475Smrg	--row;
2775956cc18dSsnj    }
2776d522f475Smrg    return row;
2777d522f475Smrg}
2778d522f475Smrg
2779d522f475Smrg/*
2780d522f475Smrg * Returns the last row of a wrapped line.
2781d522f475Smrg */
2782d522f475Smrgstatic int
2783d522f475SmrglastRowOfLine(TScreen * screen, int row)
2784d522f475Smrg{
2785956cc18dSsnj    LineData *ld;
2786956cc18dSsnj
2787d522f475Smrg    while (row < screen->max_row &&
2788956cc18dSsnj	   (ld = GET_LINEDATA(screen, row)) != 0 &&
2789956cc18dSsnj	   LineTstWrapped(ld)) {
2790d522f475Smrg	++row;
2791956cc18dSsnj    }
2792d522f475Smrg    return row;
2793d522f475Smrg}
2794d522f475Smrg
2795d522f475Smrg/*
2796d522f475Smrg * Returns the number of cells on the range of rows.
2797d522f475Smrg */
2798d522f475Smrgstatic unsigned
2799d522f475SmrglengthOfLines(TScreen * screen, int firstRow, int lastRow)
2800d522f475Smrg{
2801d522f475Smrg    unsigned length = 0;
2802d522f475Smrg    int n;
2803d522f475Smrg
2804d522f475Smrg    for (n = firstRow; n <= lastRow; ++n) {
2805956cc18dSsnj	LineData *ld = GET_LINEDATA(screen, n);
2806956cc18dSsnj	int value = LastTextCol(screen, ld, n);
2807d522f475Smrg	if (value >= 0)
28082eaa94a1Schristos	    length += (unsigned) (value + 1);
2809d522f475Smrg    }
2810d522f475Smrg    return length;
2811d522f475Smrg}
2812d522f475Smrg
2813d522f475Smrg/*
2814d522f475Smrg * Make a copy of the wrapped-line which corresponds to the given row as a
2815d522f475Smrg * string of bytes.  Construct an index for the columns from the beginning of
2816d522f475Smrg * the line.
2817d522f475Smrg */
2818d522f475Smrgstatic char *
2819d522f475Smrgmake_indexed_text(TScreen * screen, int row, unsigned length, int *indexed)
2820d522f475Smrg{
2821d522f475Smrg    Char *result = 0;
282220d2c4d2Smrg    size_t need = (length + 1);
2823d522f475Smrg
2824d522f475Smrg    /*
2825d522f475Smrg     * Get a quick upper bound to the number of bytes needed, if the whole
2826d522f475Smrg     * string were UTF-8.
2827d522f475Smrg     */
2828d522f475Smrg    if_OPT_WIDE_CHARS(screen, {
2829956cc18dSsnj	need *= ((screen->lineExtra + 1) * 6);
2830d522f475Smrg    });
2831d522f475Smrg
2832d522f475Smrg    if ((result = TypeCallocN(Char, need + 1)) != 0) {
2833956cc18dSsnj	LineData *ld = GET_LINEDATA(screen, row);
2834d522f475Smrg	unsigned used = 0;
2835d522f475Smrg	Char *last = result;
2836d522f475Smrg
2837d522f475Smrg	do {
2838d522f475Smrg	    int col = 0;
2839956cc18dSsnj	    int limit = LastTextCol(screen, ld, row);
2840d522f475Smrg
2841d522f475Smrg	    while (col <= limit) {
2842d522f475Smrg		Char *next = last;
2843956cc18dSsnj		unsigned data = ld->charData[col];
2844d522f475Smrg
284520d2c4d2Smrg		assert(col < ld->lineSize);
2846d522f475Smrg		/* some internal points may not be drawn */
2847d522f475Smrg		if (data == 0)
2848d522f475Smrg		    data = ' ';
2849d522f475Smrg
2850d522f475Smrg		if_WIDE_OR_NARROW(screen, {
2851d522f475Smrg		    next = convertToUTF8(last, data);
2852d522f475Smrg		}
2853d522f475Smrg		, {
2854d522f475Smrg		    *next++ = CharOf(data);
2855d522f475Smrg		});
2856d522f475Smrg
2857d522f475Smrg		if_OPT_WIDE_CHARS(screen, {
2858956cc18dSsnj		    size_t off;
2859956cc18dSsnj		    for_each_combData(off, ld) {
2860956cc18dSsnj			data = ld->combData[off][col];
2861956cc18dSsnj			if (data == 0)
2862d522f475Smrg			    break;
2863d522f475Smrg			next = convertToUTF8(next, data);
2864d522f475Smrg		    }
2865d522f475Smrg		});
2866d522f475Smrg
286720d2c4d2Smrg		indexed[used] = (int) (last - result);
2868d522f475Smrg		*next = 0;
2869d522f475Smrg		/* TRACE(("index[%d.%d] %d:%s\n", row, used, indexed[used], last)); */
2870d522f475Smrg		last = next;
2871d522f475Smrg		++used;
2872d522f475Smrg		++col;
287320d2c4d2Smrg		indexed[used] = (int) (next - result);
2874d522f475Smrg	    }
2875d522f475Smrg	} while (used < length &&
2876956cc18dSsnj		 LineTstWrapped(ld) &&
2877956cc18dSsnj		 (ld = GET_LINEDATA(screen, ++row)) != 0 &&
2878956cc18dSsnj		 row < screen->max_row);
2879d522f475Smrg    }
2880d522f475Smrg    /* TRACE(("result:%s\n", result)); */
2881d522f475Smrg    return (char *) result;
2882d522f475Smrg}
2883d522f475Smrg
2884d522f475Smrg/*
2885d522f475Smrg * Find the column given an offset into the character string by using the
2886d522f475Smrg * index constructed in make_indexed_text().
2887d522f475Smrg */
2888d522f475Smrgstatic int
2889d522f475SmrgindexToCol(int *indexed, int len, int off)
2890d522f475Smrg{
2891d522f475Smrg    int col = 0;
2892d522f475Smrg    while (indexed[col] < len) {
2893d522f475Smrg	if (indexed[col] >= off)
2894d522f475Smrg	    break;
2895d522f475Smrg	++col;
2896d522f475Smrg    }
2897d522f475Smrg    return col;
2898d522f475Smrg}
2899d522f475Smrg
2900d522f475Smrg/*
2901d522f475Smrg * Given a row number, and a column offset from that (which may be wrapped),
2902d522f475Smrg * set the cell to the actual row/column values.
2903d522f475Smrg */
2904d522f475Smrgstatic void
2905d522f475SmrgcolumnToCell(TScreen * screen, int row, int col, CELL * cell)
2906d522f475Smrg{
2907d522f475Smrg    while (row < screen->max_row) {
2908956cc18dSsnj	LineData *ld = GET_LINEDATA(screen, row);
2909956cc18dSsnj	int last = LastTextCol(screen, ld, row);
2910d522f475Smrg
2911d522f475Smrg	/* TRACE(("last(%d) = %d, have %d\n", row, last, col)); */
2912d522f475Smrg	if (col <= last) {
2913d522f475Smrg	    break;
2914d522f475Smrg	}
2915d522f475Smrg	/*
2916d522f475Smrg	 * Stop if the current row does not wrap (does not continue the current
2917d522f475Smrg	 * line).
2918d522f475Smrg	 */
2919956cc18dSsnj	if (!LineTstWrapped(ld)) {
2920d522f475Smrg	    col = last + 1;
2921d522f475Smrg	    break;
2922d522f475Smrg	}
2923d522f475Smrg	col -= (last + 1);
2924d522f475Smrg	++row;
2925d522f475Smrg    }
2926d522f475Smrg    if (col < 0)
2927d522f475Smrg	col = 0;
2928d522f475Smrg    cell->row = row;
2929d522f475Smrg    cell->col = col;
2930d522f475Smrg}
2931d522f475Smrg
2932d522f475Smrg/*
2933d522f475Smrg * Given a cell, find the corresponding column offset.
2934d522f475Smrg */
2935d522f475Smrgstatic int
2936d522f475SmrgcellToColumn(TScreen * screen, CELL * cell)
2937d522f475Smrg{
2938956cc18dSsnj    LineData *ld = 0;
2939d522f475Smrg    int col = cell->col;
2940d522f475Smrg    int row = firstRowOfLine(screen, cell->row, False);
2941d522f475Smrg    while (row < cell->row) {
2942956cc18dSsnj	ld = GET_LINEDATA(screen, row);
2943956cc18dSsnj	col += LastTextCol(screen, ld, row++);
2944d522f475Smrg    }
2945956cc18dSsnj#if OPT_DEC_CHRSET
2946956cc18dSsnj    if (ld == 0)
2947956cc18dSsnj	ld = GET_LINEDATA(screen, row);
2948956cc18dSsnj    if (CSET_DOUBLE(GetLineDblCS(ld)))
2949956cc18dSsnj	col /= 2;
2950956cc18dSsnj#endif
2951d522f475Smrg    return col;
2952d522f475Smrg}
2953d522f475Smrg
2954d522f475Smrgstatic void
2955d522f475Smrgdo_select_regex(TScreen * screen, CELL * startc, CELL * endc)
2956d522f475Smrg{
2957956cc18dSsnj    LineData *ld = GET_LINEDATA(screen, startc->row);
2958d522f475Smrg    int inx = ((screen->numberOfClicks - 1) % screen->maxClicks);
2959d522f475Smrg    char *expr = screen->selectExpr[inx];
2960d522f475Smrg    regex_t preg;
2961d522f475Smrg    regmatch_t match;
2962d522f475Smrg    char *search;
2963d522f475Smrg    int *indexed;
2964d522f475Smrg
2965d522f475Smrg    TRACE(("Select_REGEX:%s\n", NonNull(expr)));
2966956cc18dSsnj    if (okPosition(screen, &ld, startc) && expr != 0) {
2967d522f475Smrg	if (regcomp(&preg, expr, REG_EXTENDED) == 0) {
2968d522f475Smrg	    int firstRow = firstRowOfLine(screen, startc->row, True);
2969d522f475Smrg	    int lastRow = lastRowOfLine(screen, firstRow);
2970d522f475Smrg	    unsigned size = lengthOfLines(screen, firstRow, lastRow);
2971d522f475Smrg	    int actual = cellToColumn(screen, startc);
2972d522f475Smrg
2973d522f475Smrg	    TRACE(("regcomp ok rows %d..%d bytes %d\n",
2974d522f475Smrg		   firstRow, lastRow, size));
2975d522f475Smrg
2976d522f475Smrg	    if ((indexed = TypeCallocN(int, size + 1)) != 0) {
2977d522f475Smrg		if ((search = make_indexed_text(screen,
2978d522f475Smrg						firstRow,
2979d522f475Smrg						size,
2980d522f475Smrg						indexed)) != 0) {
29812eaa94a1Schristos		    int len = (int) strlen(search);
2982d522f475Smrg		    int col;
2983d522f475Smrg		    int best_col = -1;
2984d522f475Smrg		    int best_len = -1;
2985d522f475Smrg
2986d522f475Smrg		    for (col = 0; indexed[col] < len; ++col) {
2987d522f475Smrg			if (regexec(&preg,
2988d522f475Smrg				    search + indexed[col],
298920d2c4d2Smrg				    (size_t) 1, &match, 0) == 0) {
2990d522f475Smrg			    int start_inx = match.rm_so + indexed[col];
2991d522f475Smrg			    int finis_inx = match.rm_eo + indexed[col];
2992d522f475Smrg			    int start_col = indexToCol(indexed, len, start_inx);
2993d522f475Smrg			    int finis_col = indexToCol(indexed, len, finis_inx);
2994d522f475Smrg
2995d522f475Smrg			    if (start_col <= actual &&
2996d522f475Smrg				actual < finis_col) {
2997d522f475Smrg				int test = finis_col - start_col;
2998d522f475Smrg				if (best_len < test) {
2999d522f475Smrg				    best_len = test;
3000d522f475Smrg				    best_col = start_col;
3001d522f475Smrg				    TRACE(("match column %d len %d\n",
3002d522f475Smrg					   best_col,
3003d522f475Smrg					   best_len));
3004d522f475Smrg				}
3005d522f475Smrg			    }
3006d522f475Smrg			}
3007d522f475Smrg		    }
3008d522f475Smrg		    if (best_col >= 0) {
3009d522f475Smrg			int best_nxt = best_col + best_len;
3010d522f475Smrg			columnToCell(screen, firstRow, best_col, startc);
3011d522f475Smrg			columnToCell(screen, firstRow, best_nxt, endc);
3012d522f475Smrg			TRACE(("search::%s\n", search));
3013d522f475Smrg			TRACE(("indexed:%d..%d -> %d..%d\n",
3014d522f475Smrg			       best_col, best_nxt,
3015d522f475Smrg			       indexed[best_col],
3016d522f475Smrg			       indexed[best_nxt]));
3017d522f475Smrg			TRACE(("matched:%d:%s\n",
3018d522f475Smrg			       indexed[best_nxt] + 1 -
3019d522f475Smrg			       indexed[best_col],
3020956cc18dSsnj			       visibleChars((Char *) (search + indexed[best_col]),
3021d522f475Smrg					    (unsigned) (indexed[best_nxt] +
3022d522f475Smrg							1 -
3023d522f475Smrg							indexed[best_col]))));
3024d522f475Smrg		    }
3025d522f475Smrg		    free(search);
3026d522f475Smrg		}
3027d522f475Smrg		free(indexed);
3028956cc18dSsnj#if OPT_DEC_CHRSET
3029956cc18dSsnj		if ((ld = GET_LINEDATA(screen, startc->row)) != 0) {
3030956cc18dSsnj		    if (CSET_DOUBLE(GetLineDblCS(ld)))
3031956cc18dSsnj			startc->col *= 2;
3032956cc18dSsnj		}
3033956cc18dSsnj		if ((ld = GET_LINEDATA(screen, endc->row)) != 0) {
3034956cc18dSsnj		    if (CSET_DOUBLE(GetLineDblCS(ld)))
3035956cc18dSsnj			endc->col *= 2;
3036956cc18dSsnj		}
3037956cc18dSsnj#endif
3038d522f475Smrg	    }
3039d522f475Smrg	    regfree(&preg);
3040d522f475Smrg	}
3041d522f475Smrg    }
3042d522f475Smrg}
3043d522f475Smrg#endif /* OPT_SELECT_REGEX */
3044d522f475Smrg
3045956cc18dSsnj#define InitRow(name) \
3046956cc18dSsnj	ld.name = GET_LINEDATA(screen, screen->name.row)
3047956cc18dSsnj
3048956cc18dSsnj#define NextRow(name) \
3049956cc18dSsnj	ld.name = GET_LINEDATA(screen, ++screen->name.row)
3050956cc18dSsnj
3051956cc18dSsnj#define PrevRow(name) \
3052956cc18dSsnj	ld.name = GET_LINEDATA(screen, --screen->name.row)
3053956cc18dSsnj
305420d2c4d2Smrg#define MoreRows(name) \
305520d2c4d2Smrg	(screen->name.row < screen->max_row)
305620d2c4d2Smrg
3057956cc18dSsnj#define isPrevWrapped(name) \
3058956cc18dSsnj	(screen->name.row > 0 \
3059956cc18dSsnj	   && (ltmp = GET_LINEDATA(screen, screen->name.row - 1)) != 0 \
3060956cc18dSsnj	   && LineTstWrapped(ltmp))
3061956cc18dSsnj
3062d522f475Smrg/*
3063d522f475Smrg * sets startSel endSel
3064d522f475Smrg * ensuring that they have legal values
3065d522f475Smrg */
3066d522f475Smrgstatic void
3067d522f475SmrgComputeSelect(XtermWidget xw,
3068d522f475Smrg	      CELL * startc,
3069d522f475Smrg	      CELL * endc,
3070d522f475Smrg	      Bool extend)
3071d522f475Smrg{
3072956cc18dSsnj    TScreen *screen = TScreenOf(xw);
3073956cc18dSsnj
3074d522f475Smrg    int length;
3075d522f475Smrg    int cclass;
3076d522f475Smrg    CELL first = *startc;
3077d522f475Smrg    CELL last = *endc;
3078956cc18dSsnj    Boolean ignored = False;
3079956cc18dSsnj
3080956cc18dSsnj    struct {
3081956cc18dSsnj	LineData *startSel;
3082956cc18dSsnj	LineData *endSel;
3083956cc18dSsnj    } ld;
3084956cc18dSsnj    LineData *ltmp;
3085d522f475Smrg
3086d522f475Smrg    TRACE(("ComputeSelect(startRow=%d, startCol=%d, endRow=%d, endCol=%d, %sextend)\n",
3087d522f475Smrg	   first.row, first.col,
3088d522f475Smrg	   last.row, last.col,
3089d522f475Smrg	   extend ? "" : "no"));
3090d522f475Smrg
3091d522f475Smrg#if OPT_WIDE_CHARS
3092d522f475Smrg    if (first.col > 1
3093d522f475Smrg	&& isWideCell(first.row, first.col - 1)
3094d522f475Smrg	&& XTERM_CELL(first.row, first.col - 0) == HIDDEN_CHAR) {
309520d2c4d2Smrg	TRACE(("Adjusting start. Changing downwards from %i.\n", first.col));
3096d522f475Smrg	first.col -= 1;
3097d522f475Smrg	if (last.col == (first.col + 1))
3098d522f475Smrg	    last.col--;
3099d522f475Smrg    }
3100d522f475Smrg
3101d522f475Smrg    if (last.col > 1
3102d522f475Smrg	&& isWideCell(last.row, last.col - 1)
3103d522f475Smrg	&& XTERM_CELL(last.row, last.col) == HIDDEN_CHAR) {
3104d522f475Smrg	last.col += 1;
3105d522f475Smrg    }
3106d522f475Smrg#endif
3107d522f475Smrg
3108d522f475Smrg    if (Coordinate(screen, &first) <= Coordinate(screen, &last)) {
3109d522f475Smrg	screen->startSel = screen->startRaw = first;
3110d522f475Smrg	screen->endSel = screen->endRaw = last;
3111d522f475Smrg    } else {			/* Swap them */
3112d522f475Smrg	screen->startSel = screen->startRaw = last;
3113d522f475Smrg	screen->endSel = screen->endRaw = first;
3114d522f475Smrg    }
3115d522f475Smrg
3116956cc18dSsnj    InitRow(startSel);
3117956cc18dSsnj    InitRow(endSel);
3118956cc18dSsnj
3119d522f475Smrg    switch (screen->selectUnit) {
3120d522f475Smrg    case Select_CHAR:
3121956cc18dSsnj	(void) okPosition(screen, &(ld.startSel), &(screen->startSel));
3122956cc18dSsnj	(void) okPosition(screen, &(ld.endSel), &(screen->endSel));
3123d522f475Smrg	break;
3124d522f475Smrg
3125d522f475Smrg    case Select_WORD:
3126d522f475Smrg	TRACE(("Select_WORD\n"));
3127956cc18dSsnj	if (okPosition(screen, &(ld.startSel), &(screen->startSel))) {
3128956cc18dSsnj	    cclass = CClassOf(startSel);
3129d522f475Smrg	    do {
3130d522f475Smrg		--screen->startSel.col;
3131956cc18dSsnj		if (screen->startSel.col < 0
3132956cc18dSsnj		    && isPrevWrapped(startSel)) {
3133956cc18dSsnj		    PrevRow(startSel);
3134956cc18dSsnj		    screen->startSel.col = LastTextCol(screen, ld.startSel, screen->startSel.row);
3135d522f475Smrg		}
3136d522f475Smrg	    } while (screen->startSel.col >= 0
3137956cc18dSsnj		     && CClassSelects(startSel, cclass));
3138d522f475Smrg	    ++screen->startSel.col;
3139d522f475Smrg	}
3140d522f475Smrg#if OPT_WIDE_CHARS
3141d522f475Smrg	if (screen->startSel.col
3142d522f475Smrg	    && XTERM_CELL(screen->startSel.row,
3143d522f475Smrg			  screen->startSel.col) == HIDDEN_CHAR)
3144d522f475Smrg	    screen->startSel.col++;
3145d522f475Smrg#endif
3146d522f475Smrg
3147956cc18dSsnj	if (okPosition(screen, &(ld.endSel), &(screen->endSel))) {
3148956cc18dSsnj	    length = LastTextCol(screen, ld.endSel, screen->endSel.row);
3149956cc18dSsnj	    cclass = CClassOf(endSel);
3150d522f475Smrg	    do {
3151d522f475Smrg		++screen->endSel.col;
3152d522f475Smrg		if (screen->endSel.col > length
3153956cc18dSsnj		    && LineTstWrapped(ld.endSel)) {
315420d2c4d2Smrg		    if (!MoreRows(endSel))
315520d2c4d2Smrg			break;
3156d522f475Smrg		    screen->endSel.col = 0;
3157956cc18dSsnj		    NextRow(endSel);
3158956cc18dSsnj		    length = LastTextCol(screen, ld.endSel, screen->endSel.row);
3159d522f475Smrg		}
3160d522f475Smrg	    } while (screen->endSel.col <= length
3161956cc18dSsnj		     && CClassSelects(endSel, cclass));
3162d522f475Smrg	    /* Word-select selects if pointing to any char in "word",
3163d522f475Smrg	     * especially note that it includes the last character in a word.
3164d522f475Smrg	     * So we do no --endSel.col and do special eol handling.
3165d522f475Smrg	     */
316620d2c4d2Smrg	    if (screen->endSel.col > length + 1
316720d2c4d2Smrg		&& MoreRows(endSel)) {
3168d522f475Smrg		screen->endSel.col = 0;
3169956cc18dSsnj		NextRow(endSel);
3170d522f475Smrg	    }
3171d522f475Smrg	}
3172d522f475Smrg#if OPT_WIDE_CHARS
3173d522f475Smrg	if (screen->endSel.col
3174d522f475Smrg	    && XTERM_CELL(screen->endSel.row,
3175d522f475Smrg			  screen->endSel.col) == HIDDEN_CHAR)
3176d522f475Smrg	    screen->endSel.col++;
3177d522f475Smrg#endif
3178d522f475Smrg
3179d522f475Smrg	screen->saveStartW = screen->startSel;
3180d522f475Smrg	break;
3181d522f475Smrg
3182d522f475Smrg    case Select_LINE:
3183d522f475Smrg	TRACE(("Select_LINE\n"));
318420d2c4d2Smrg	while (LineTstWrapped(ld.endSel)
318520d2c4d2Smrg	       && MoreRows(endSel)) {
3186956cc18dSsnj	    NextRow(endSel);
3187d522f475Smrg	}
3188d522f475Smrg	if (screen->cutToBeginningOfLine
3189d522f475Smrg	    || screen->startSel.row < screen->saveStartW.row) {
3190d522f475Smrg	    screen->startSel.col = 0;
3191956cc18dSsnj	    while (isPrevWrapped(startSel)) {
3192956cc18dSsnj		PrevRow(startSel);
3193d522f475Smrg	    }
3194d522f475Smrg	} else if (!extend) {
3195d522f475Smrg	    if ((first.row < screen->saveStartW.row)
3196d522f475Smrg		|| (isSameRow(&first, &(screen->saveStartW))
3197d522f475Smrg		    && first.col < screen->saveStartW.col)) {
3198d522f475Smrg		screen->startSel.col = 0;
3199956cc18dSsnj		while (isPrevWrapped(startSel)) {
3200956cc18dSsnj		    PrevRow(startSel);
3201d522f475Smrg		}
3202d522f475Smrg	    } else {
3203d522f475Smrg		screen->startSel = screen->saveStartW;
3204d522f475Smrg	    }
3205d522f475Smrg	}
3206956cc18dSsnj	trimLastLine(screen, &(ld.endSel), &(screen->endSel));
3207d522f475Smrg	break;
3208d522f475Smrg
3209d522f475Smrg    case Select_GROUP:		/* paragraph */
3210d522f475Smrg	TRACE(("Select_GROUP\n"));
3211956cc18dSsnj	if (okPosition(screen, &(ld.startSel), &(screen->startSel))) {
3212d522f475Smrg	    /* scan backward for beginning of group */
3213d522f475Smrg	    while (screen->startSel.row > 0 &&
3214956cc18dSsnj		   (LastTextCol(screen, ld.startSel, screen->startSel.row -
3215956cc18dSsnj				1) > 0 ||
3216956cc18dSsnj		    isPrevWrapped(startSel))) {
3217956cc18dSsnj		PrevRow(startSel);
3218d522f475Smrg	    }
3219d522f475Smrg	    screen->startSel.col = 0;
3220d522f475Smrg	    /* scan forward for end of group */
322120d2c4d2Smrg	    while (MoreRows(endSel) &&
3222956cc18dSsnj		   (LastTextCol(screen, ld.endSel, screen->endSel.row + 1) >
3223956cc18dSsnj		    0 ||
3224956cc18dSsnj		    LineTstWrapped(ld.endSel))) {
3225956cc18dSsnj		NextRow(endSel);
3226d522f475Smrg	    }
3227956cc18dSsnj	    trimLastLine(screen, &(ld.endSel), &(screen->endSel));
3228d522f475Smrg	}
3229d522f475Smrg	break;
3230d522f475Smrg
3231d522f475Smrg    case Select_PAGE:		/* everything one can see */
3232d522f475Smrg	TRACE(("Select_PAGE\n"));
3233d522f475Smrg	screen->startSel.row = 0;
3234d522f475Smrg	screen->startSel.col = 0;
323520d2c4d2Smrg	screen->endSel.row = MaxRows(screen);
3236d522f475Smrg	screen->endSel.col = 0;
3237d522f475Smrg	break;
3238d522f475Smrg
3239d522f475Smrg    case Select_ALL:		/* counts scrollback if in normal screen */
3240d522f475Smrg	TRACE(("Select_ALL\n"));
3241d522f475Smrg	screen->startSel.row = -screen->savedlines;
3242d522f475Smrg	screen->startSel.col = 0;
324320d2c4d2Smrg	screen->endSel.row = MaxRows(screen);
3244d522f475Smrg	screen->endSel.col = 0;
3245d522f475Smrg	break;
3246d522f475Smrg
3247d522f475Smrg#if OPT_SELECT_REGEX
3248d522f475Smrg    case Select_REGEX:
3249d522f475Smrg	do_select_regex(screen, &(screen->startSel), &(screen->endSel));
3250d522f475Smrg	break;
3251d522f475Smrg#endif
3252d522f475Smrg
3253d522f475Smrg    case NSELECTUNITS:		/* always ignore */
3254956cc18dSsnj	ignored = True;
3255956cc18dSsnj	break;
3256d522f475Smrg    }
3257d522f475Smrg
3258956cc18dSsnj    if (!ignored) {
3259956cc18dSsnj	/* check boundaries */
3260956cc18dSsnj	ScrollSelection(screen, 0, False);
3261956cc18dSsnj	TrackText(xw, &(screen->startSel), &(screen->endSel));
3262956cc18dSsnj    }
3263d522f475Smrg
3264d522f475Smrg    return;
3265d522f475Smrg}
3266d522f475Smrg
3267d522f475Smrg/* Guaranteed (first.row, first.col) <= (last.row, last.col) */
3268d522f475Smrgstatic void
3269d522f475SmrgTrackText(XtermWidget xw,
3270d522f475Smrg	  const CELL * firstp,
3271d522f475Smrg	  const CELL * lastp)
3272d522f475Smrg{
3273956cc18dSsnj    TScreen *screen = TScreenOf(xw);
3274d522f475Smrg    int from, to;
3275d522f475Smrg    CELL old_start, old_end;
3276d522f475Smrg    CELL first = *firstp;
3277d522f475Smrg    CELL last = *lastp;
3278d522f475Smrg
3279d522f475Smrg    TRACE(("TrackText(first=%d,%d, last=%d,%d)\n",
3280d522f475Smrg	   first.row, first.col, last.row, last.col));
3281d522f475Smrg
3282d522f475Smrg    old_start = screen->startH;
3283d522f475Smrg    old_end = screen->endH;
3284d522f475Smrg    if (isSameCELL(&first, &old_start) &&
3285d522f475Smrg	isSameCELL(&last, &old_end))
3286d522f475Smrg	return;
3287d522f475Smrg    screen->startH = first;
3288d522f475Smrg    screen->endH = last;
3289d522f475Smrg    from = Coordinate(screen, &screen->startH);
3290d522f475Smrg    to = Coordinate(screen, &screen->endH);
3291d522f475Smrg    if (to <= screen->startHCoord || from > screen->endHCoord) {
3292d522f475Smrg	/* No overlap whatsoever between old and new hilite */
3293d522f475Smrg	ReHiliteText(xw, &old_start, &old_end);
3294d522f475Smrg	ReHiliteText(xw, &first, &last);
3295d522f475Smrg    } else {
3296d522f475Smrg	if (from < screen->startHCoord) {
3297d522f475Smrg	    /* Extend left end */
3298d522f475Smrg	    ReHiliteText(xw, &first, &old_start);
3299d522f475Smrg	} else if (from > screen->startHCoord) {
3300d522f475Smrg	    /* Shorten left end */
3301d522f475Smrg	    ReHiliteText(xw, &old_start, &first);
3302d522f475Smrg	}
3303d522f475Smrg	if (to > screen->endHCoord) {
3304d522f475Smrg	    /* Extend right end */
3305d522f475Smrg	    ReHiliteText(xw, &old_end, &last);
3306d522f475Smrg	} else if (to < screen->endHCoord) {
3307d522f475Smrg	    /* Shorten right end */
3308d522f475Smrg	    ReHiliteText(xw, &last, &old_end);
3309d522f475Smrg	}
3310d522f475Smrg    }
3311d522f475Smrg    screen->startHCoord = from;
3312d522f475Smrg    screen->endHCoord = to;
3313d522f475Smrg}
3314d522f475Smrg
3315d522f475Smrg/* Guaranteed that (first->row, first->col) <= (last->row, last->col) */
3316d522f475Smrgstatic void
3317d522f475SmrgReHiliteText(XtermWidget xw,
3318d522f475Smrg	     CELL * firstp,
3319d522f475Smrg	     CELL * lastp)
3320d522f475Smrg{
3321956cc18dSsnj    TScreen *screen = TScreenOf(xw);
3322d522f475Smrg    int i;
3323d522f475Smrg    CELL first = *firstp;
3324d522f475Smrg    CELL last = *lastp;
3325d522f475Smrg
3326d522f475Smrg    TRACE(("ReHiliteText from %d.%d to %d.%d\n",
3327d522f475Smrg	   first.row, first.col, last.row, last.col));
3328d522f475Smrg
3329d522f475Smrg    if (first.row < 0)
3330d522f475Smrg	first.row = first.col = 0;
3331d522f475Smrg    else if (first.row > screen->max_row)
3332d522f475Smrg	return;			/* nothing to do, since last.row >= first.row */
3333d522f475Smrg
3334d522f475Smrg    if (last.row < 0)
3335d522f475Smrg	return;			/* nothing to do, since first.row <= last.row */
3336d522f475Smrg    else if (last.row > screen->max_row) {
3337d522f475Smrg	last.row = screen->max_row;
3338d522f475Smrg	last.col = MaxCols(screen);
3339d522f475Smrg    }
3340d522f475Smrg    if (isSameCELL(&first, &last))
3341d522f475Smrg	return;
3342d522f475Smrg
3343d522f475Smrg    if (!isSameRow(&first, &last)) {	/* do multiple rows */
3344d522f475Smrg	if ((i = screen->max_col - first.col + 1) > 0) {	/* first row */
3345d522f475Smrg	    ScrnRefresh(xw, first.row, first.col, 1, i, True);
3346d522f475Smrg	}
3347d522f475Smrg	if ((i = last.row - first.row - 1) > 0) {	/* middle rows */
3348d522f475Smrg	    ScrnRefresh(xw, first.row + 1, 0, i, MaxCols(screen), True);
3349d522f475Smrg	}
3350d522f475Smrg	if (last.col > 0 && last.row <= screen->max_row) {	/* last row */
3351d522f475Smrg	    ScrnRefresh(xw, last.row, 0, 1, last.col, True);
3352d522f475Smrg	}
3353d522f475Smrg    } else {			/* do single row */
3354d522f475Smrg	ScrnRefresh(xw, first.row, first.col, 1, last.col - first.col, True);
3355d522f475Smrg    }
3356d522f475Smrg}
3357d522f475Smrg
3358d522f475Smrg/*
3359d522f475Smrg * Guaranteed that (cellc->row, cellc->col) <= (cell->row, cell->col), and that both points are valid
3360d522f475Smrg * (may have cell->row = screen->max_row+1, cell->col = 0).
3361d522f475Smrg */
3362d522f475Smrgstatic void
3363d522f475SmrgSaltTextAway(XtermWidget xw,
3364d522f475Smrg	     CELL * cellc,
3365d522f475Smrg	     CELL * cell,
3366d522f475Smrg	     String * params,	/* selections */
3367d522f475Smrg	     Cardinal num_params)
3368d522f475Smrg{
3369956cc18dSsnj    TScreen *screen = TScreenOf(xw);
3370d522f475Smrg    int i, j = 0;
3371d522f475Smrg    int eol;
3372956cc18dSsnj    int tmp;
3373d522f475Smrg    Char *line;
3374d522f475Smrg    Char *lp;
3375d522f475Smrg    CELL first = *cellc;
3376d522f475Smrg    CELL last = *cell;
3377d522f475Smrg
3378d522f475Smrg    if (isSameRow(&first, &last) && first.col > last.col) {
3379956cc18dSsnj	EXCHANGE(first.col, last.col, tmp);
3380d522f475Smrg    }
3381d522f475Smrg
3382d522f475Smrg    --last.col;
3383d522f475Smrg    /* first we need to know how long the string is before we can save it */
3384d522f475Smrg
3385d522f475Smrg    if (isSameRow(&last, &first)) {
3386d522f475Smrg	j = Length(screen, first.row, first.col, last.col);
3387d522f475Smrg    } else {			/* two cases, cut is on same line, cut spans multiple lines */
3388d522f475Smrg	j += Length(screen, first.row, first.col, screen->max_col) + 1;
3389d522f475Smrg	for (i = first.row + 1; i < last.row; i++)
3390d522f475Smrg	    j += Length(screen, i, 0, screen->max_col) + 1;
3391d522f475Smrg	if (last.col >= 0)
3392d522f475Smrg	    j += Length(screen, last.row, 0, last.col);
3393d522f475Smrg    }
3394d522f475Smrg
3395d522f475Smrg    /* UTF-8 may require more space */
3396d522f475Smrg    if_OPT_WIDE_CHARS(screen, {
3397d522f475Smrg	j *= 4;
3398d522f475Smrg    });
3399d522f475Smrg
3400d522f475Smrg    /* now get some memory to save it in */
3401d522f475Smrg
3402d522f475Smrg    if (screen->selection_size <= j) {
340320d2c4d2Smrg	if ((line = (Char *) malloc((size_t) j + 1)) == 0)
3404d522f475Smrg	    SysError(ERROR_BMALLOC2);
3405d522f475Smrg	XtFree((char *) screen->selection_data);
3406d522f475Smrg	screen->selection_data = line;
3407d522f475Smrg	screen->selection_size = j + 1;
3408d522f475Smrg    } else {
3409d522f475Smrg	line = screen->selection_data;
3410d522f475Smrg    }
3411d522f475Smrg
3412d522f475Smrg    if ((line == 0)
3413d522f475Smrg	|| (j < 0))
3414d522f475Smrg	return;
3415d522f475Smrg
3416d522f475Smrg    line[j] = '\0';		/* make sure it is null terminated */
3417d522f475Smrg    lp = line;			/* lp points to where to save the text */
3418d522f475Smrg    if (isSameRow(&last, &first)) {
3419d522f475Smrg	lp = SaveText(screen, last.row, first.col, last.col, lp, &eol);
3420d522f475Smrg    } else {
3421d522f475Smrg	lp = SaveText(screen, first.row, first.col, screen->max_col, lp, &eol);
3422d522f475Smrg	if (eol)
3423d522f475Smrg	    *lp++ = '\n';	/* put in newline at end of line */
3424d522f475Smrg	for (i = first.row + 1; i < last.row; i++) {
3425d522f475Smrg	    lp = SaveText(screen, i, 0, screen->max_col, lp, &eol);
3426d522f475Smrg	    if (eol)
3427d522f475Smrg		*lp++ = '\n';
3428d522f475Smrg	}
3429d522f475Smrg	if (last.col >= 0)
3430d522f475Smrg	    lp = SaveText(screen, last.row, 0, last.col, lp, &eol);
3431d522f475Smrg    }
3432d522f475Smrg    *lp = '\0';			/* make sure we have end marked */
3433d522f475Smrg
3434956cc18dSsnj    TRACE(("Salted TEXT:%d:%s\n", (int) (lp - line),
3435956cc18dSsnj	   visibleChars(line, (unsigned) (lp - line))));
3436d522f475Smrg
34372eaa94a1Schristos    screen->selection_length = (unsigned long) (lp - line);
3438d522f475Smrg    _OwnSelection(xw, params, num_params);
3439d522f475Smrg}
3440d522f475Smrg
3441d522f475Smrg#if OPT_PASTE64
3442d522f475Smrgvoid
3443d522f475SmrgClearSelectionBuffer(TScreen * screen)
3444d522f475Smrg{
3445d522f475Smrg    screen->selection_length = 0;
3446d522f475Smrg    screen->base64_count = 0;
3447d522f475Smrg}
3448d522f475Smrg
3449d522f475Smrgstatic void
345020d2c4d2SmrgAppendStrToSelectionBuffer(TScreen * screen, Char * text, size_t len)
3451d522f475Smrg{
3452d522f475Smrg    if (len != 0) {
34532eaa94a1Schristos	int j = (int) (screen->selection_length + len);		/* New length */
3454d522f475Smrg	int k = j + (j >> 2) + 80;	/* New size if we grow buffer: grow by ~50% */
3455d522f475Smrg	if (j + 1 >= screen->selection_size) {
3456d522f475Smrg	    if (!screen->selection_length) {
3457d522f475Smrg		/* New buffer */
3458d522f475Smrg		Char *line;
345920d2c4d2Smrg		if ((line = (Char *) malloc((size_t) k)) == 0)
3460d522f475Smrg		    SysError(ERROR_BMALLOC2);
3461d522f475Smrg		XtFree((char *) screen->selection_data);
3462d522f475Smrg		screen->selection_data = line;
3463d522f475Smrg	    } else {
3464d522f475Smrg		/* Realloc buffer */
3465d522f475Smrg		screen->selection_data = (Char *)
3466d522f475Smrg		    realloc(screen->selection_data,
346720d2c4d2Smrg			    (size_t) k);
3468d522f475Smrg		if (screen->selection_data == 0)
3469d522f475Smrg		    SysError(ERROR_BMALLOC2);
3470d522f475Smrg	    }
3471d522f475Smrg	    screen->selection_size = k;
3472d522f475Smrg	}
347320d2c4d2Smrg	if (screen->selection_data != 0) {
347420d2c4d2Smrg	    memcpy(screen->selection_data + screen->selection_length, text, len);
347520d2c4d2Smrg	    screen->selection_length += len;
347620d2c4d2Smrg	    screen->selection_data[screen->selection_length] = 0;
347720d2c4d2Smrg	}
3478d522f475Smrg    }
3479d522f475Smrg}
3480d522f475Smrg
3481d522f475Smrgvoid
3482d522f475SmrgAppendToSelectionBuffer(TScreen * screen, unsigned c)
3483d522f475Smrg{
34842eaa94a1Schristos    unsigned six;
3485d522f475Smrg    Char ch;
3486d522f475Smrg
3487d522f475Smrg    /* Decode base64 character */
3488d522f475Smrg    if (c >= 'A' && c <= 'Z')
3489d522f475Smrg	six = c - 'A';
3490d522f475Smrg    else if (c >= 'a' && c <= 'z')
3491d522f475Smrg	six = c - 'a' + 26;
3492d522f475Smrg    else if (c >= '0' && c <= '9')
3493d522f475Smrg	six = c - '0' + 52;
3494d522f475Smrg    else if (c == '+')
3495d522f475Smrg	six = 62;
3496d522f475Smrg    else if (c == '/')
3497d522f475Smrg	six = 63;
3498d522f475Smrg    else
3499d522f475Smrg	return;
3500d522f475Smrg
3501d522f475Smrg    /* Accumulate bytes */
3502d522f475Smrg    switch (screen->base64_count) {
3503d522f475Smrg    case 0:
3504d522f475Smrg	screen->base64_accu = six;
3505d522f475Smrg	screen->base64_count = 6;
3506d522f475Smrg	break;
3507d522f475Smrg
3508d522f475Smrg    case 2:
35092eaa94a1Schristos	ch = CharOf((screen->base64_accu << 6) + six);
3510d522f475Smrg	screen->base64_count = 0;
351120d2c4d2Smrg	AppendStrToSelectionBuffer(screen, &ch, (size_t) 1);
3512d522f475Smrg	break;
3513d522f475Smrg
3514d522f475Smrg    case 4:
35152eaa94a1Schristos	ch = CharOf((screen->base64_accu << 4) + (six >> 2));
3516d522f475Smrg	screen->base64_accu = (six & 0x3);
3517d522f475Smrg	screen->base64_count = 2;
351820d2c4d2Smrg	AppendStrToSelectionBuffer(screen, &ch, (size_t) 1);
3519d522f475Smrg	break;
3520d522f475Smrg
3521d522f475Smrg    case 6:
35222eaa94a1Schristos	ch = CharOf((screen->base64_accu << 2) + (six >> 4));
3523d522f475Smrg	screen->base64_accu = (six & 0xF);
3524d522f475Smrg	screen->base64_count = 4;
352520d2c4d2Smrg	AppendStrToSelectionBuffer(screen, &ch, (size_t) 1);
3526d522f475Smrg	break;
3527d522f475Smrg    }
3528d522f475Smrg}
3529d522f475Smrg
3530d522f475Smrgvoid
353120d2c4d2SmrgCompleteSelection(XtermWidget xw, String * args, Cardinal len)
3532d522f475Smrg{
3533956cc18dSsnj    TScreen *screen = TScreenOf(xw);
3534956cc18dSsnj
3535956cc18dSsnj    screen->base64_count = 0;
3536956cc18dSsnj    screen->base64_accu = 0;
3537d522f475Smrg    _OwnSelection(xw, args, len);
3538d522f475Smrg}
3539d522f475Smrg#endif /* OPT_PASTE64 */
3540d522f475Smrg
3541d522f475Smrgstatic Bool
3542d522f475Smrg_ConvertSelectionHelper(Widget w,
3543d522f475Smrg			Atom * type,
3544d522f475Smrg			XtPointer *value,
3545d522f475Smrg			unsigned long *length,
3546d522f475Smrg			int *format,
3547d522f475Smrg			int (*conversion_function) (Display *,
3548d522f475Smrg						    char **, int,
3549d522f475Smrg						    XICCEncodingStyle,
3550d522f475Smrg						    XTextProperty *),
3551d522f475Smrg			XICCEncodingStyle conversion_style)
3552d522f475Smrg{
3553956cc18dSsnj    XtermWidget xw;
3554956cc18dSsnj
3555956cc18dSsnj    if ((xw = getXtermWidget(w)) != 0) {
3556956cc18dSsnj	TScreen *screen = TScreenOf(xw);
3557d522f475Smrg	Display *dpy = XtDisplay(w);
3558d522f475Smrg	XTextProperty textprop;
3559d522f475Smrg	char *the_data = (char *) screen->selection_data;
3560d522f475Smrg
3561d522f475Smrg	if (conversion_function(dpy, &the_data, 1,
3562d522f475Smrg				conversion_style,
3563d522f475Smrg				&textprop) >= Success) {
3564d522f475Smrg	    *value = (XtPointer) textprop.value;
3565d522f475Smrg	    *length = textprop.nitems;
3566d522f475Smrg	    *type = textprop.encoding;
3567d522f475Smrg	    *format = textprop.format;
3568d522f475Smrg	    return True;
3569d522f475Smrg	}
3570d522f475Smrg    }
3571d522f475Smrg    return False;
3572d522f475Smrg}
3573d522f475Smrg
35742eaa94a1Schristosstatic Boolean
35752eaa94a1SchristosSaveConvertedLength(XtPointer *target, unsigned long source)
35762eaa94a1Schristos{
35772eaa94a1Schristos    Boolean result = False;
35782eaa94a1Schristos
35792eaa94a1Schristos    *target = XtMalloc(4);
35802eaa94a1Schristos    if (*target != 0) {
35812eaa94a1Schristos	result = True;
35822eaa94a1Schristos	if (sizeof(unsigned long) == 4) {
35832eaa94a1Schristos	    *(unsigned long *) *target = source;
35842eaa94a1Schristos	} else if (sizeof(unsigned) == 4) {
358520d2c4d2Smrg	    *(unsigned *) *target = (unsigned) source;
35862eaa94a1Schristos	} else if (sizeof(unsigned short) == 4) {
35872eaa94a1Schristos	    *(unsigned short *) *target = (unsigned short) source;
35882eaa94a1Schristos	} else {
35892eaa94a1Schristos	    /* FIXME - does this depend on byte-order? */
35902eaa94a1Schristos	    unsigned long temp = source;
359120d2c4d2Smrg	    memcpy((char *) *target,
359220d2c4d2Smrg		   ((char *) &temp) + sizeof(temp) - 4,
359320d2c4d2Smrg		   (size_t) 4);
35942eaa94a1Schristos	}
35952eaa94a1Schristos    }
35962eaa94a1Schristos    return result;
35972eaa94a1Schristos}
35982eaa94a1Schristos
3599d522f475Smrgstatic Boolean
3600d522f475SmrgConvertSelection(Widget w,
3601d522f475Smrg		 Atom * selection,
3602d522f475Smrg		 Atom * target,
3603d522f475Smrg		 Atom * type,
3604d522f475Smrg		 XtPointer *value,
3605d522f475Smrg		 unsigned long *length,
3606d522f475Smrg		 int *format)
3607d522f475Smrg{
3608d522f475Smrg    Display *dpy = XtDisplay(w);
3609d522f475Smrg    TScreen *screen;
3610d522f475Smrg    Bool result = False;
3611d522f475Smrg
3612956cc18dSsnj    XtermWidget xw;
3613956cc18dSsnj
3614956cc18dSsnj    if ((xw = getXtermWidget(w)) == 0)
3615d522f475Smrg	return False;
3616d522f475Smrg
3617956cc18dSsnj    screen = TScreenOf(xw);
3618d522f475Smrg
3619d522f475Smrg    if (screen->selection_data == NULL)
3620d522f475Smrg	return False;		/* can this happen? */
3621d522f475Smrg
3622956cc18dSsnj    TRACE(("ConvertSelection %s\n",
3623956cc18dSsnj	   visibleSelectionTarget(dpy, *target)));
3624956cc18dSsnj
3625d522f475Smrg    if (*target == XA_TARGETS(dpy)) {
3626d522f475Smrg	Atom *allocP;
3627d522f475Smrg	Atom *targetP;
3628d522f475Smrg	Atom *std_targets;
3629d522f475Smrg	XPointer std_return = 0;
3630d522f475Smrg	unsigned long std_length;
3631d522f475Smrg
3632d522f475Smrg	if (XmuConvertStandardSelection(w, screen->selection_time, selection,
3633d522f475Smrg					target, type, &std_return,
3634d522f475Smrg					&std_length, format)) {
3635956cc18dSsnj	    Atom *my_targets = _SelectionTargets(w);
3636956cc18dSsnj
3637956cc18dSsnj	    TRACE(("XmuConvertStandardSelection - success\n"));
3638a1f3da82Smrg	    std_targets = (Atom *) (void *) (std_return);
3639d522f475Smrg	    *length = std_length + 6;
3640d522f475Smrg
3641a1f3da82Smrg	    targetP = TypeXtMallocN(Atom, *length);
3642d522f475Smrg	    allocP = targetP;
3643d522f475Smrg
3644d522f475Smrg	    *value = (XtPointer) targetP;
3645d522f475Smrg
3646956cc18dSsnj	    while (*my_targets != None) {
3647956cc18dSsnj		*targetP++ = *my_targets++;
3648956cc18dSsnj	    }
3649d522f475Smrg	    *targetP++ = XA_LENGTH(dpy);
3650d522f475Smrg	    *targetP++ = XA_LIST_LENGTH(dpy);
3651d522f475Smrg
36522eaa94a1Schristos	    *length = std_length + (unsigned long) (targetP - allocP);
3653d522f475Smrg
3654d522f475Smrg	    memcpy(targetP, std_targets, sizeof(Atom) * std_length);
3655d522f475Smrg	    XtFree((char *) std_targets);
3656d522f475Smrg	    *type = XA_ATOM;
3657d522f475Smrg	    *format = 32;
3658d522f475Smrg	    result = True;
3659956cc18dSsnj	} else {
3660956cc18dSsnj	    TRACE(("XmuConvertStandardSelection - failed\n"));
3661d522f475Smrg	}
3662d522f475Smrg    }
3663d522f475Smrg#if OPT_WIDE_CHARS
3664d522f475Smrg    else if (screen->wide_chars && *target == XA_STRING) {
3665d522f475Smrg	result =
3666d522f475Smrg	    _ConvertSelectionHelper(w,
3667d522f475Smrg				    type, value, length, format,
3668d522f475Smrg				    Xutf8TextListToTextProperty,
3669d522f475Smrg				    XStringStyle);
3670956cc18dSsnj	TRACE(("...Xutf8TextListToTextProperty:%d\n", result));
3671d522f475Smrg    } else if (screen->wide_chars && *target == XA_UTF8_STRING(dpy)) {
3672d522f475Smrg	result =
3673d522f475Smrg	    _ConvertSelectionHelper(w,
3674d522f475Smrg				    type, value, length, format,
3675d522f475Smrg				    Xutf8TextListToTextProperty,
3676d522f475Smrg				    XUTF8StringStyle);
3677956cc18dSsnj	TRACE(("...Xutf8TextListToTextProperty:%d\n", result));
3678d522f475Smrg    } else if (screen->wide_chars && *target == XA_TEXT(dpy)) {
3679d522f475Smrg	result =
3680d522f475Smrg	    _ConvertSelectionHelper(w,
3681d522f475Smrg				    type, value, length, format,
3682d522f475Smrg				    Xutf8TextListToTextProperty,
3683d522f475Smrg				    XStdICCTextStyle);
3684956cc18dSsnj	TRACE(("...Xutf8TextListToTextProperty:%d\n", result));
3685d522f475Smrg    } else if (screen->wide_chars && *target == XA_COMPOUND_TEXT(dpy)) {
3686d522f475Smrg	result =
3687d522f475Smrg	    _ConvertSelectionHelper(w,
3688d522f475Smrg				    type, value, length, format,
3689d522f475Smrg				    Xutf8TextListToTextProperty,
3690d522f475Smrg				    XCompoundTextStyle);
3691956cc18dSsnj	TRACE(("...Xutf8TextListToTextProperty:%d\n", result));
3692d522f475Smrg    }
3693d522f475Smrg#endif
3694d522f475Smrg
3695d522f475Smrg    else if (*target == XA_STRING) {	/* not wide_chars */
3696d522f475Smrg	/* We can only reach this point if the selection requestor
3697d522f475Smrg	   requested STRING before any of TEXT, COMPOUND_TEXT or
3698d522f475Smrg	   UTF8_STRING.  We therefore assume that the requestor is not
3699d522f475Smrg	   properly internationalised, and dump raw eight-bit data
3700d522f475Smrg	   with no conversion into the selection.  Yes, this breaks
3701d522f475Smrg	   the ICCCM in non-Latin-1 locales. */
3702d522f475Smrg	*type = XA_STRING;
3703d522f475Smrg	*value = (XtPointer) screen->selection_data;
3704d522f475Smrg	*length = screen->selection_length;
3705d522f475Smrg	*format = 8;
3706d522f475Smrg	result = True;
3707956cc18dSsnj	TRACE(("...raw 8-bit data:%d\n", result));
3708d522f475Smrg    } else if (*target == XA_TEXT(dpy)) {	/* not wide_chars */
3709d522f475Smrg	result =
3710d522f475Smrg	    _ConvertSelectionHelper(w,
3711d522f475Smrg				    type, value, length, format,
3712d522f475Smrg				    XmbTextListToTextProperty,
3713d522f475Smrg				    XStdICCTextStyle);
3714956cc18dSsnj	TRACE(("...XmbTextListToTextProperty(StdICC):%d\n", result));
3715d522f475Smrg    } else if (*target == XA_COMPOUND_TEXT(dpy)) {	/* not wide_chars */
3716d522f475Smrg	result =
3717d522f475Smrg	    _ConvertSelectionHelper(w,
3718d522f475Smrg				    type, value, length, format,
3719d522f475Smrg				    XmbTextListToTextProperty,
3720d522f475Smrg				    XCompoundTextStyle);
3721956cc18dSsnj	TRACE(("...XmbTextListToTextProperty(Compound):%d\n", result));
3722d522f475Smrg    }
3723d522f475Smrg#ifdef X_HAVE_UTF8_STRING
3724d522f475Smrg    else if (*target == XA_UTF8_STRING(dpy)) {	/* not wide_chars */
3725d522f475Smrg	result =
3726d522f475Smrg	    _ConvertSelectionHelper(w,
3727d522f475Smrg				    type, value, length, format,
3728d522f475Smrg				    XmbTextListToTextProperty,
3729d522f475Smrg				    XUTF8StringStyle);
3730956cc18dSsnj	TRACE(("...XmbTextListToTextProperty(UTF8):%d\n", result));
3731d522f475Smrg    }
3732d522f475Smrg#endif
3733d522f475Smrg    else if (*target == XA_LIST_LENGTH(dpy)) {
373420d2c4d2Smrg	result = SaveConvertedLength(value, (unsigned long) 1);
3735d522f475Smrg	*type = XA_INTEGER;
3736d522f475Smrg	*length = 1;
3737d522f475Smrg	*format = 32;
3738956cc18dSsnj	TRACE(("...list of values:%d\n", result));
3739d522f475Smrg    } else if (*target == XA_LENGTH(dpy)) {
3740d522f475Smrg	/* This value is wrong if we have UTF-8 text */
37412eaa94a1Schristos	result = SaveConvertedLength(value, screen->selection_length);
3742d522f475Smrg	*type = XA_INTEGER;
3743d522f475Smrg	*length = 1;
3744d522f475Smrg	*format = 32;
3745956cc18dSsnj	TRACE(("...list of values:%d\n", result));
3746d522f475Smrg    } else if (XmuConvertStandardSelection(w,
3747d522f475Smrg					   screen->selection_time, selection,
3748d522f475Smrg					   target, type, (XPointer *) value,
3749d522f475Smrg					   length, format)) {
3750d522f475Smrg	result = True;
3751956cc18dSsnj	TRACE(("...XmuConvertStandardSelection:%d\n", result));
3752d522f475Smrg    }
3753d522f475Smrg
3754d522f475Smrg    /* else */
37552eaa94a1Schristos    return (Boolean) result;
3756d522f475Smrg}
3757d522f475Smrg
3758d522f475Smrgstatic void
3759d522f475SmrgLoseSelection(Widget w, Atom * selection)
3760d522f475Smrg{
3761d522f475Smrg    TScreen *screen;
3762d522f475Smrg    Atom *atomP;
3763d522f475Smrg    Cardinal i;
3764d522f475Smrg
3765956cc18dSsnj    XtermWidget xw;
3766956cc18dSsnj
3767956cc18dSsnj    if ((xw = getXtermWidget(w)) == 0)
3768d522f475Smrg	return;
3769d522f475Smrg
3770956cc18dSsnj    screen = TScreenOf(xw);
3771d522f475Smrg    for (i = 0, atomP = screen->selection_atoms;
3772d522f475Smrg	 i < screen->selection_count; i++, atomP++) {
3773d522f475Smrg	if (*selection == *atomP)
3774d522f475Smrg	    *atomP = (Atom) 0;
3775d522f475Smrg	if (CutBuffer(*atomP) >= 0) {
3776d522f475Smrg	    *atomP = (Atom) 0;
3777d522f475Smrg	}
3778d522f475Smrg    }
3779d522f475Smrg
3780d522f475Smrg    for (i = screen->selection_count; i; i--) {
3781d522f475Smrg	if (screen->selection_atoms[i - 1] != 0)
3782d522f475Smrg	    break;
3783d522f475Smrg    }
3784d522f475Smrg    screen->selection_count = i;
3785d522f475Smrg
3786d522f475Smrg    for (i = 0, atomP = screen->selection_atoms;
3787d522f475Smrg	 i < screen->selection_count; i++, atomP++) {
3788d522f475Smrg	if (*atomP == (Atom) 0) {
3789d522f475Smrg	    *atomP = screen->selection_atoms[--screen->selection_count];
3790d522f475Smrg	}
3791d522f475Smrg    }
3792d522f475Smrg
3793d522f475Smrg    if (screen->selection_count == 0)
3794956cc18dSsnj	TrackText(xw, &zeroCELL, &zeroCELL);
3795d522f475Smrg}
3796d522f475Smrg
3797d522f475Smrg/* ARGSUSED */
3798d522f475Smrgstatic void
3799d522f475SmrgSelectionDone(Widget w GCC_UNUSED,
3800d522f475Smrg	      Atom * selection GCC_UNUSED,
3801d522f475Smrg	      Atom * target GCC_UNUSED)
3802d522f475Smrg{
3803d522f475Smrg    /* empty proc so Intrinsics know we want to keep storage */
3804d522f475Smrg}
3805d522f475Smrg
3806d522f475Smrgstatic void
3807d522f475Smrg_OwnSelection(XtermWidget xw,
3808d522f475Smrg	      String * selections,
3809d522f475Smrg	      Cardinal count)
3810d522f475Smrg{
3811956cc18dSsnj    TScreen *screen = TScreenOf(xw);
3812d522f475Smrg    Atom *atoms = screen->selection_atoms;
3813d522f475Smrg    Cardinal i;
3814d522f475Smrg    Bool have_selection = False;
3815d522f475Smrg
381620d2c4d2Smrg    if (count == 0)
381720d2c4d2Smrg	return;
38182eaa94a1Schristos    if (screen->selection_length == 0)
3819d522f475Smrg	return;
3820d522f475Smrg
3821956cc18dSsnj    TRACE(("_OwnSelection count %d\n", count));
3822d522f475Smrg    selections = MapSelections(xw, selections, count);
3823d522f475Smrg
3824d522f475Smrg    if (count > screen->sel_atoms_size) {
3825d522f475Smrg	XtFree((char *) atoms);
3826a1f3da82Smrg	atoms = TypeXtMallocN(Atom, count);
3827d522f475Smrg	screen->selection_atoms = atoms;
3828d522f475Smrg	screen->sel_atoms_size = count;
3829d522f475Smrg    }
3830d522f475Smrg    XmuInternStrings(XtDisplay((Widget) xw), selections, count, atoms);
3831d522f475Smrg    for (i = 0; i < count; i++) {
3832d522f475Smrg	int cutbuffer = CutBuffer(atoms[i]);
3833d522f475Smrg	if (cutbuffer >= 0) {
38342eaa94a1Schristos	    unsigned long limit =
38352eaa94a1Schristos	    (unsigned long) (4 * XMaxRequestSize(XtDisplay((Widget) xw)) - 32);
38362eaa94a1Schristos	    if (screen->selection_length > limit) {
383720d2c4d2Smrg		TRACE(("selection too big (%lu bytes), not storing in CUT_BUFFER%d\n",
383820d2c4d2Smrg		       screen->selection_length, cutbuffer));
3839d522f475Smrg		fprintf(stderr,
3840956cc18dSsnj			"%s: selection too big (%lu bytes), not storing in CUT_BUFFER%d\n",
3841a1f3da82Smrg			ProgramName, screen->selection_length, cutbuffer);
3842d522f475Smrg	    } else {
3843d522f475Smrg		/* This used to just use the UTF-8 data, which was totally
3844d522f475Smrg		 * broken as not even the corresponding paste code in Xterm
3845d522f475Smrg		 * understood this!  So now it converts to Latin1 first.
3846d522f475Smrg		 *   Robert Brady, 2000-09-05
3847d522f475Smrg		 */
3848d522f475Smrg		unsigned long length = screen->selection_length;
3849d522f475Smrg		Char *data = screen->selection_data;
3850d522f475Smrg		if_OPT_WIDE_CHARS((screen), {
3851956cc18dSsnj		    data = UTF8toLatin1(screen, data, length, &length);
3852d522f475Smrg		});
3853d522f475Smrg		TRACE(("XStoreBuffer(%d)\n", cutbuffer));
3854d522f475Smrg		XStoreBuffer(XtDisplay((Widget) xw),
3855d522f475Smrg			     (char *) data,
3856d522f475Smrg			     (int) length,
3857d522f475Smrg			     cutbuffer);
3858d522f475Smrg	    }
3859d522f475Smrg	} else if (!screen->replyToEmacs) {
3860d522f475Smrg	    have_selection |=
3861d522f475Smrg		XtOwnSelection((Widget) xw, atoms[i],
3862d522f475Smrg			       screen->selection_time,
3863d522f475Smrg			       ConvertSelection, LoseSelection, SelectionDone);
3864d522f475Smrg	}
3865d522f475Smrg    }
3866d522f475Smrg    if (!screen->replyToEmacs)
3867d522f475Smrg	screen->selection_count = count;
3868d522f475Smrg    if (!have_selection)
3869d522f475Smrg	TrackText(xw, &zeroCELL, &zeroCELL);
3870d522f475Smrg}
3871d522f475Smrg
3872d522f475Smrgstatic void
3873d522f475SmrgResetSelectionState(TScreen * screen)
3874d522f475Smrg{
3875d522f475Smrg    screen->selection_count = 0;
3876d522f475Smrg    screen->startH = zeroCELL;
3877d522f475Smrg    screen->endH = zeroCELL;
3878d522f475Smrg}
3879d522f475Smrg
3880d522f475Smrgvoid
3881d522f475SmrgDisownSelection(XtermWidget xw)
3882d522f475Smrg{
3883956cc18dSsnj    TScreen *screen = TScreenOf(xw);
3884d522f475Smrg    Atom *atoms = screen->selection_atoms;
3885d522f475Smrg    Cardinal count = screen->selection_count;
3886d522f475Smrg    Cardinal i;
3887d522f475Smrg
3888d522f475Smrg    TRACE(("DisownSelection count %d, start %d.%d, end %d.%d\n",
3889d522f475Smrg	   count,
3890d522f475Smrg	   screen->startH.row,
3891d522f475Smrg	   screen->startH.col,
3892d522f475Smrg	   screen->endH.row,
3893d522f475Smrg	   screen->endH.col));
3894d522f475Smrg
3895d522f475Smrg    for (i = 0; i < count; i++) {
3896d522f475Smrg	int cutbuffer = CutBuffer(atoms[i]);
3897d522f475Smrg	if (cutbuffer < 0) {
3898d522f475Smrg	    XtDisownSelection((Widget) xw, atoms[i],
3899d522f475Smrg			      screen->selection_time);
3900d522f475Smrg	}
3901d522f475Smrg    }
3902d522f475Smrg    /*
3903d522f475Smrg     * If none of the callbacks via XtDisownSelection() reset highlighting
3904d522f475Smrg     * do it now.
3905d522f475Smrg     */
3906d522f475Smrg    if (ScrnHaveSelection(screen)) {
3907d522f475Smrg	/* save data which will be reset */
3908d522f475Smrg	CELL first = screen->startH;
3909d522f475Smrg	CELL last = screen->endH;
3910d522f475Smrg
3911d522f475Smrg	ResetSelectionState(screen);
3912d522f475Smrg	ReHiliteText(xw, &first, &last);
3913d522f475Smrg    } else {
3914d522f475Smrg	ResetSelectionState(screen);
3915d522f475Smrg    }
3916d522f475Smrg}
3917d522f475Smrg
3918d522f475Smrgvoid
3919d522f475SmrgUnhiliteSelection(XtermWidget xw)
3920d522f475Smrg{
3921956cc18dSsnj    TScreen *screen = TScreenOf(xw);
3922d522f475Smrg
3923d522f475Smrg    if (ScrnHaveSelection(screen)) {
3924d522f475Smrg	CELL first = screen->startH;
3925d522f475Smrg	CELL last = screen->endH;
3926d522f475Smrg
3927d522f475Smrg	screen->startH = zeroCELL;
3928d522f475Smrg	screen->endH = zeroCELL;
3929d522f475Smrg	ReHiliteText(xw, &first, &last);
3930d522f475Smrg    }
3931d522f475Smrg}
3932d522f475Smrg
3933d522f475Smrg/* returns number of chars in line from scol to ecol out */
3934d522f475Smrg/* ARGSUSED */
3935d522f475Smrgstatic int
3936956cc18dSsnjLength(TScreen * screen,
3937d522f475Smrg       int row,
3938d522f475Smrg       int scol,
3939d522f475Smrg       int ecol)
3940d522f475Smrg{
3941956cc18dSsnj    LineData *ld = GET_LINEDATA(screen, row);
3942956cc18dSsnj    int lastcol = LastTextCol(screen, ld, row);
3943d522f475Smrg
3944d522f475Smrg    if (ecol > lastcol)
3945d522f475Smrg	ecol = lastcol;
3946d522f475Smrg    return (ecol - scol + 1);
3947d522f475Smrg}
3948d522f475Smrg
3949d522f475Smrg/* copies text into line, preallocated */
3950d522f475Smrgstatic Char *
3951d522f475SmrgSaveText(TScreen * screen,
3952d522f475Smrg	 int row,
3953d522f475Smrg	 int scol,
3954d522f475Smrg	 int ecol,
3955d522f475Smrg	 Char * lp,		/* pointer to where to put the text */
3956d522f475Smrg	 int *eol)
3957d522f475Smrg{
3958956cc18dSsnj    LineData *ld;
3959d522f475Smrg    int i = 0;
3960d522f475Smrg    unsigned c;
3961d522f475Smrg    Char *result = lp;
3962d522f475Smrg#if OPT_WIDE_CHARS
39632eaa94a1Schristos    unsigned previous = 0;
3964d522f475Smrg#endif
3965d522f475Smrg
3966956cc18dSsnj    ld = GET_LINEDATA(screen, row);
3967d522f475Smrg    i = Length(screen, row, scol, ecol);
3968d522f475Smrg    ecol = scol + i;
3969d522f475Smrg#if OPT_DEC_CHRSET
3970956cc18dSsnj    if (CSET_DOUBLE(GetLineDblCS(ld))) {
3971d522f475Smrg	scol = (scol + 0) / 2;
3972d522f475Smrg	ecol = (ecol + 1) / 2;
3973d522f475Smrg    }
3974d522f475Smrg#endif
3975956cc18dSsnj    *eol = !LineTstWrapped(ld);
3976d522f475Smrg    for (i = scol; i < ecol; i++) {
397720d2c4d2Smrg	assert(i < ld->lineSize);
3978956cc18dSsnj	c = E2A(ld->charData[i]);
3979d522f475Smrg#if OPT_WIDE_CHARS
3980d522f475Smrg	/* We want to strip out every occurrence of HIDDEN_CHAR AFTER a
3981d522f475Smrg	 * wide character.
3982d522f475Smrg	 */
3983956cc18dSsnj	if (c == HIDDEN_CHAR && isWide((int) previous)) {
3984d522f475Smrg	    previous = c;
3985d522f475Smrg	    /* Combining characters attached to double-width characters
3986d522f475Smrg	       are in memory attached to the HIDDEN_CHAR */
3987d522f475Smrg	    if_OPT_WIDE_CHARS(screen, {
3988d522f475Smrg		if (screen->utf8_mode != uFalse) {
3989d522f475Smrg		    unsigned ch;
3990956cc18dSsnj		    size_t off;
3991956cc18dSsnj		    for_each_combData(off, ld) {
3992956cc18dSsnj			ch = ld->combData[off][i];
3993956cc18dSsnj			if (ch == 0)
3994d522f475Smrg			    break;
3995d522f475Smrg			lp = convertToUTF8(lp, ch);
3996d522f475Smrg		    }
3997d522f475Smrg		}
3998d522f475Smrg	    });
3999d522f475Smrg	    continue;
4000d522f475Smrg	}
4001d522f475Smrg	previous = c;
4002d522f475Smrg	if (screen->utf8_mode != uFalse) {
4003d522f475Smrg	    lp = convertToUTF8(lp, (c != 0) ? c : ' ');
4004d522f475Smrg	    if_OPT_WIDE_CHARS(screen, {
4005d522f475Smrg		unsigned ch;
4006956cc18dSsnj		size_t off;
4007956cc18dSsnj		for_each_combData(off, ld) {
4008956cc18dSsnj		    ch = ld->combData[off][i];
4009956cc18dSsnj		    if (ch == 0)
4010d522f475Smrg			break;
4011d522f475Smrg		    lp = convertToUTF8(lp, ch);
4012d522f475Smrg		}
4013d522f475Smrg	    });
4014d522f475Smrg	} else
4015d522f475Smrg#endif
4016d522f475Smrg	{
4017d522f475Smrg	    if (c == 0) {
4018d522f475Smrg		c = E2A(' ');
4019d522f475Smrg	    } else if (c < E2A(' ')) {
4020d522f475Smrg		c = DECtoASCII(c);
4021d522f475Smrg	    } else if (c == 0x7f) {
4022d522f475Smrg		c = 0x5f;
4023d522f475Smrg	    }
40242eaa94a1Schristos	    *lp++ = CharOf(A2E(c));
4025d522f475Smrg	}
4026d522f475Smrg	if (c != E2A(' '))
4027d522f475Smrg	    result = lp;
4028d522f475Smrg    }
4029d522f475Smrg
4030d522f475Smrg    /*
4031d522f475Smrg     * If requested, trim trailing blanks from selected lines.  Do not do this
4032d522f475Smrg     * if the line is wrapped.
4033d522f475Smrg     */
4034d522f475Smrg    if (!*eol || !screen->trim_selection)
4035d522f475Smrg	result = lp;
4036d522f475Smrg
4037d522f475Smrg    return (result);
4038d522f475Smrg}
4039d522f475Smrg
4040d522f475Smrg/* 32 + following 7-bit word:
4041d522f475Smrg
4042d522f475Smrg   1:0  Button no: 0, 1, 2.  3=release.
4043d522f475Smrg     2  shift
4044d522f475Smrg     3  meta
4045d522f475Smrg     4  ctrl
4046d522f475Smrg     5  set for motion notify
4047d522f475Smrg     6  set for wheel
4048d522f475Smrg*/
4049d522f475Smrg
4050d522f475Smrg/* Position: 32 - 255. */
4051a1f3da82Smrgstatic int
4052d522f475SmrgBtnCode(XButtonEvent * event, int button)
4053d522f475Smrg{
40542eaa94a1Schristos    int result = (int) (32 + (KeyState(event->state) << 2));
4055d522f475Smrg
4056d522f475Smrg    if (button < 0 || button > 5) {
4057d522f475Smrg	result += 3;
4058d522f475Smrg    } else {
4059d522f475Smrg	if (button > 3)
4060d522f475Smrg	    result += (64 - 4);
4061d522f475Smrg	if (event->type == MotionNotify)
4062d522f475Smrg	    result += 32;
4063d522f475Smrg	result += button;
4064d522f475Smrg    }
4065a1f3da82Smrg    return result;
4066a1f3da82Smrg}
4067a1f3da82Smrg
4068a1f3da82Smrgstatic unsigned
4069a1f3da82SmrgEmitButtonCode(TScreen * screen, Char * line, unsigned count, XButtonEvent * event)
4070a1f3da82Smrg{
4071a1f3da82Smrg    int value = BtnCode(event, screen->mouse_button);
4072a1f3da82Smrg
4073a1f3da82Smrg    if (!screen->ext_mode_mouse || value < 128) {
4074a1f3da82Smrg	line[count++] = CharOf(value);
4075a1f3da82Smrg    } else {
4076a1f3da82Smrg	line[count++] = CharOf(0xC0 + (value >> 6));
4077a1f3da82Smrg	line[count++] = CharOf(0x80 + (value & 0x3F));
4078a1f3da82Smrg    }
4079a1f3da82Smrg    return count;
4080d522f475Smrg}
4081d522f475Smrg
4082d522f475Smrgstatic void
4083d522f475SmrgEditorButton(XtermWidget xw, XButtonEvent * event)
4084d522f475Smrg{
4085956cc18dSsnj    TScreen *screen = TScreenOf(xw);
4086d522f475Smrg    int pty = screen->respond;
4087492d43a5Smrg    int mouse_limit = screen->ext_mode_mouse ? EXT_MOUSE_LIMIT : MOUSE_LIMIT;
4088492d43a5Smrg    Char line[10];
4089d522f475Smrg    int row, col;
4090d522f475Smrg    int button;
4091d522f475Smrg    unsigned count = 0;
4092d522f475Smrg    Boolean changed = True;
4093d522f475Smrg
4094d522f475Smrg    /* If button event, get button # adjusted for DEC compatibility */
40952eaa94a1Schristos    button = (int) (event->button - 1);
4096d522f475Smrg    if (button >= 3)
4097d522f475Smrg	button++;
4098d522f475Smrg
4099d522f475Smrg    /* Compute character position of mouse pointer */
4100d522f475Smrg    row = (event->y - screen->border) / FontHeight(screen);
4101d522f475Smrg    col = (event->x - OriginX(screen)) / FontWidth(screen);
4102d522f475Smrg
4103d522f475Smrg    /* Limit to screen dimensions */
4104d522f475Smrg    if (row < 0)
4105d522f475Smrg	row = 0;
4106d522f475Smrg    else if (row > screen->max_row)
4107d522f475Smrg	row = screen->max_row;
4108d522f475Smrg
4109d522f475Smrg    if (col < 0)
4110d522f475Smrg	col = 0;
4111d522f475Smrg    else if (col > screen->max_col)
4112d522f475Smrg	col = screen->max_col;
4113492d43a5Smrg
4114492d43a5Smrg    /* Limit to representable mouse dimensions */
4115492d43a5Smrg    if (row > mouse_limit)
4116492d43a5Smrg	row = mouse_limit;
4117492d43a5Smrg    if (col > mouse_limit)
4118492d43a5Smrg	col = mouse_limit;
4119d522f475Smrg
4120d522f475Smrg    /* Build key sequence starting with \E[M */
4121d522f475Smrg    if (screen->control_eight_bits) {
4122d522f475Smrg	line[count++] = ANSI_CSI;
4123d522f475Smrg    } else {
4124d522f475Smrg	line[count++] = ANSI_ESC;
4125d522f475Smrg	line[count++] = '[';
4126d522f475Smrg    }
4127d522f475Smrg#if OPT_SCO_FUNC_KEYS
4128d522f475Smrg    if (xw->keyboard.type == keyboardIsSCO) {
4129d522f475Smrg	/*
4130d522f475Smrg	 * SCO function key F1 is \E[M, which would conflict with xterm's
4131d522f475Smrg	 * normal kmous.
4132d522f475Smrg	 */
4133d522f475Smrg	line[count++] = '>';
4134d522f475Smrg    }
4135d522f475Smrg#endif
4136d522f475Smrg    line[count++] = 'M';
4137d522f475Smrg
4138d522f475Smrg    /* Add event code to key sequence */
4139d522f475Smrg    if (screen->send_mouse_pos == X10_MOUSE) {
41402eaa94a1Schristos	line[count++] = CharOf(' ' + button);
4141d522f475Smrg    } else {
4142d522f475Smrg	/* Button-Motion events */
4143d522f475Smrg	switch (event->type) {
4144d522f475Smrg	case ButtonPress:
4145a1f3da82Smrg	    screen->mouse_button = button;
4146a1f3da82Smrg	    count = EmitButtonCode(screen, line, count, event);
4147d522f475Smrg	    break;
4148d522f475Smrg	case ButtonRelease:
4149d522f475Smrg	    /*
4150d522f475Smrg	     * Wheel mouse interface generates release-events for buttons
4151d522f475Smrg	     * 4 and 5, coded here as 3 and 4 respectively.  We change the
4152d522f475Smrg	     * release for buttons 1..3 to a -1.
4153d522f475Smrg	     */
4154d522f475Smrg	    if (button < 3)
4155d522f475Smrg		button = -1;
4156a1f3da82Smrg	    screen->mouse_button = button;
4157a1f3da82Smrg	    count = EmitButtonCode(screen, line, count, event);
4158d522f475Smrg	    break;
4159d522f475Smrg	case MotionNotify:
4160d522f475Smrg	    /* BTN_EVENT_MOUSE and ANY_EVENT_MOUSE modes send motion
4161d522f475Smrg	     * events only if character cell has changed.
4162d522f475Smrg	     */
4163d522f475Smrg	    if ((row == screen->mouse_row)
4164d522f475Smrg		&& (col == screen->mouse_col)) {
4165d522f475Smrg		changed = False;
4166d522f475Smrg	    } else {
4167a1f3da82Smrg		count = EmitButtonCode(screen, line, count, event);
4168d522f475Smrg	    }
4169d522f475Smrg	    break;
4170d522f475Smrg	default:
4171d522f475Smrg	    changed = False;
4172d522f475Smrg	    break;
4173d522f475Smrg	}
4174d522f475Smrg    }
4175d522f475Smrg
4176d522f475Smrg    if (changed) {
4177d522f475Smrg	screen->mouse_row = row;
4178d522f475Smrg	screen->mouse_col = col;
4179d522f475Smrg
4180492d43a5Smrg	TRACE(("mouse at %d,%d button+mask = %#x\n", row, col, line[count - 1]));
4181d522f475Smrg
4182492d43a5Smrg	/* Add pointer position to key sequence */
4183492d43a5Smrg	count = EmitMousePosition(screen, line, count, col);
4184492d43a5Smrg	count = EmitMousePosition(screen, line, count, row);
4185d522f475Smrg
4186d522f475Smrg	/* Transmit key sequence to process running under xterm */
4187d522f475Smrg	v_write(pty, line, count);
4188d522f475Smrg    }
4189d522f475Smrg    return;
4190d522f475Smrg}
4191d522f475Smrg
4192d522f475Smrg#if OPT_FOCUS_EVENT
4193d522f475Smrgvoid
4194d522f475SmrgSendFocusButton(XtermWidget xw, XFocusChangeEvent * event)
4195d522f475Smrg{
4196956cc18dSsnj    TScreen *screen = TScreenOf(xw);
4197d522f475Smrg
4198d522f475Smrg    if (screen->send_focus_pos) {
4199d522f475Smrg	ANSI reply;
4200d522f475Smrg
4201d522f475Smrg	memset(&reply, 0, sizeof(reply));
4202d522f475Smrg	reply.a_type = ANSI_CSI;
4203d522f475Smrg
4204d522f475Smrg#if OPT_SCO_FUNC_KEYS
4205d522f475Smrg	if (xw->keyboard.type == keyboardIsSCO) {
4206d522f475Smrg	    reply.a_pintro = '>';
4207d522f475Smrg	}
4208d522f475Smrg#endif
42092eaa94a1Schristos	reply.a_final = CharOf((event->type == FocusIn) ? 'I' : 'O');
4210d522f475Smrg	unparseseq(xw, &reply);
4211d522f475Smrg    }
4212d522f475Smrg    return;
4213d522f475Smrg}
4214d522f475Smrg#endif /* OPT_FOCUS_EVENT */
4215