button.c revision 20d2c4d2
120d2c4d2Smrg/* $XTermId: button.c,v 1.377 2010/06/04 09:27:07 tom Exp $ */
2d522f475Smrg
3d522f475Smrg/*
420d2c4d2Smrg * Copyright 1999-2009,2010 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
118d522f475Smrg#define KeyModifiers (event->xbutton.state & OurModifiers)
119d522f475Smrg
1202eaa94a1Schristos#define KeyState(x) (((int) ((x) & (ShiftMask|ControlMask))) \
1212eaa94a1Schristos			  + (((x) & Mod1Mask) ? 2 : 0))
122d522f475Smrg    /* adds together the bits:
123d522f475Smrg       shift key -> 1
124d522f475Smrg       meta key  -> 2
125d522f475Smrg       control key -> 4 */
126d522f475Smrg
127d522f475Smrg#define	Coordinate(s,c)	((c)->row * MaxCols(s) + (c)->col)
128d522f475Smrg
129d522f475Smrgstatic const CELL zeroCELL =
130d522f475Smrg{0, 0};
131d522f475Smrg
132d522f475Smrg#if OPT_DEC_LOCATOR
133d522f475Smrgstatic Bool SendLocatorPosition(XtermWidget xw, XEvent * event);
134d522f475Smrgstatic void CheckLocatorPosition(XtermWidget xw, XEvent * event);
135d522f475Smrg#endif /* OPT_DEC_LOCATOR */
136d522f475Smrg
137d522f475Smrg/* Multi-click handling */
138d522f475Smrg#if OPT_READLINE
139d522f475Smrgstatic Time lastButtonDownTime = 0;
140d522f475Smrgstatic int ExtendingSelection = 0;
141d522f475Smrgstatic Time lastButton3UpTime = 0;
142d522f475Smrgstatic Time lastButton3DoubleDownTime = 0;
143d522f475Smrgstatic CELL lastButton3;	/* At the release time */
144d522f475Smrg#endif /* OPT_READLINE */
145d522f475Smrg
146d522f475Smrgstatic Char *SaveText(TScreen * screen, int row, int scol, int ecol,
147d522f475Smrg		      Char * lp, int *eol);
148d522f475Smrgstatic int Length(TScreen * screen, int row, int scol, int ecol);
149d522f475Smrgstatic void ComputeSelect(XtermWidget xw, CELL * startc, CELL * endc, Bool extend);
150d522f475Smrgstatic void EditorButton(XtermWidget xw, XButtonEvent * event);
151d522f475Smrgstatic void EndExtend(XtermWidget w, XEvent * event, String * params, Cardinal
152d522f475Smrg		      num_params, Bool use_cursor_loc);
153d522f475Smrgstatic void ExtendExtend(XtermWidget xw, const CELL * cell);
154d522f475Smrgstatic void PointToCELL(TScreen * screen, int y, int x, CELL * cell);
155d522f475Smrgstatic void ReHiliteText(XtermWidget xw, CELL * first, CELL * last);
156d522f475Smrgstatic void SaltTextAway(XtermWidget xw, CELL * cellc, CELL * cell,
157d522f475Smrg			 String * params, Cardinal num_params);
158d522f475Smrgstatic void SelectSet(XtermWidget xw, XEvent * event, String * params, Cardinal num_params);
159d522f475Smrgstatic void SelectionReceived PROTO_XT_SEL_CB_ARGS;
160d522f475Smrgstatic void StartSelect(XtermWidget xw, const CELL * cell);
161d522f475Smrgstatic void TrackDown(XtermWidget xw, XButtonEvent * event);
162d522f475Smrgstatic void TrackText(XtermWidget xw, const CELL * first, const CELL * last);
163d522f475Smrgstatic void _OwnSelection(XtermWidget xw, String * selections, Cardinal count);
164d522f475Smrgstatic void do_select_end(XtermWidget xw, XEvent * event, String * params,
165d522f475Smrg			  Cardinal *num_params, Bool use_cursor_loc);
166d522f475Smrg
167d522f475SmrgBool
168d522f475SmrgSendMousePosition(XtermWidget xw, XEvent * event)
169d522f475Smrg{
170956cc18dSsnj    TScreen *screen = TScreenOf(xw);
171d522f475Smrg
172d522f475Smrg    /* If send_mouse_pos mode isn't on, we shouldn't be here */
173d522f475Smrg    if (screen->send_mouse_pos == MOUSE_OFF)
174d522f475Smrg	return False;
175d522f475Smrg
176d522f475Smrg#if OPT_DEC_LOCATOR
177d522f475Smrg    if (screen->send_mouse_pos == DEC_LOCATOR) {
178d522f475Smrg	return (SendLocatorPosition(xw, event));
179d522f475Smrg    }
180d522f475Smrg#endif /* OPT_DEC_LOCATOR */
181d522f475Smrg
182d522f475Smrg    /* Make sure the event is an appropriate type */
183d522f475Smrg    if ((screen->send_mouse_pos != BTN_EVENT_MOUSE)
184d522f475Smrg	&& (screen->send_mouse_pos != ANY_EVENT_MOUSE)
185d522f475Smrg	&& event->type != ButtonPress
186d522f475Smrg	&& event->type != ButtonRelease)
187d522f475Smrg	return False;
188d522f475Smrg
189d522f475Smrg    switch (screen->send_mouse_pos) {
190d522f475Smrg    case X10_MOUSE:		/* X10 compatibility sequences */
191d522f475Smrg
192d522f475Smrg	if (KeyModifiers == 0) {
193d522f475Smrg	    if (event->type == ButtonPress)
194d522f475Smrg		EditorButton(xw, (XButtonEvent *) event);
195d522f475Smrg	    return True;
196d522f475Smrg	}
197d522f475Smrg	return False;
198d522f475Smrg
199d522f475Smrg    case VT200_HIGHLIGHT_MOUSE:	/* DEC vt200 hilite tracking */
200d522f475Smrg	if (event->type == ButtonPress &&
201d522f475Smrg	    KeyModifiers == 0 &&
202d522f475Smrg	    event->xbutton.button == Button1) {
203d522f475Smrg	    TrackDown(xw, (XButtonEvent *) event);
204d522f475Smrg	    return True;
205d522f475Smrg	}
206d522f475Smrg	if (KeyModifiers == 0 || KeyModifiers == ControlMask) {
207d522f475Smrg	    EditorButton(xw, (XButtonEvent *) event);
208d522f475Smrg	    return True;
209d522f475Smrg	}
210d522f475Smrg	return False;
211d522f475Smrg
212d522f475Smrg    case VT200_MOUSE:		/* DEC vt200 compatible */
213d522f475Smrg
214d522f475Smrg	/* xterm extension for motion reporting. June 1998 */
215d522f475Smrg	/* EditorButton() will distinguish between the modes */
216d522f475Smrg    case BTN_EVENT_MOUSE:
217d522f475Smrg    case ANY_EVENT_MOUSE:
218d522f475Smrg	if (KeyModifiers == 0 || KeyModifiers == ControlMask) {
2192eaa94a1Schristos	    if (event->type == MotionNotify) {
2202eaa94a1Schristos		((XButtonEvent *) event)->button = 0;
2212eaa94a1Schristos	    }
222d522f475Smrg	    EditorButton(xw, (XButtonEvent *) event);
223d522f475Smrg	    return True;
224d522f475Smrg	}
225d522f475Smrg	return False;
226d522f475Smrg
227d522f475Smrg    default:
228d522f475Smrg	return False;
229d522f475Smrg    }
230d522f475Smrg}
231d522f475Smrg
232d522f475Smrg#if OPT_DEC_LOCATOR
233d522f475Smrg
234d522f475Smrg#define	LocatorCoords( row, col, x, y, oor )			\
235d522f475Smrg    if( screen->locator_pixels ) {				\
236d522f475Smrg	(oor)=False; (row) = (y)+1; (col) = (x)+1;		\
237d522f475Smrg	/* Limit to screen dimensions */			\
238d522f475Smrg	if ((row) < 1) (row) = 1,(oor)=True;			\
239d522f475Smrg	else if ((row) > screen->border*2+Height(screen))	\
240d522f475Smrg	    (row) = screen->border*2+Height(screen),(oor)=True;	\
241d522f475Smrg	if ((col) < 1) (col) = 1,(oor)=True;			\
242d522f475Smrg	else if ((col) > OriginX(screen)*2+Width(screen))	\
243d522f475Smrg	    (col) = OriginX(screen)*2+Width(screen),(oor)=True;	\
244d522f475Smrg    } else {							\
245d522f475Smrg	(oor)=False;						\
246d522f475Smrg	/* Compute character position of mouse pointer */	\
247d522f475Smrg	(row) = ((y) - screen->border) / FontHeight(screen);	\
248d522f475Smrg	(col) = ((x) - OriginX(screen)) / FontWidth(screen);	\
249d522f475Smrg	/* Limit to screen dimensions */			\
250d522f475Smrg	if ((row) < 0) (row) = 0,(oor)=True;			\
251d522f475Smrg	else if ((row) > screen->max_row)			\
252d522f475Smrg	    (row) = screen->max_row,(oor)=True;			\
253d522f475Smrg	if ((col) < 0) (col) = 0,(oor)=True;			\
254d522f475Smrg	else if ((col) > screen->max_col)			\
255d522f475Smrg	    (col) = screen->max_col,(oor)=True;			\
256d522f475Smrg	(row)++; (col)++;					\
257d522f475Smrg    }
258d522f475Smrg
259d522f475Smrgstatic Bool
260d522f475SmrgSendLocatorPosition(XtermWidget xw, XEvent * event)
261d522f475Smrg{
262d522f475Smrg    ANSI reply;
263956cc18dSsnj    TScreen *screen = TScreenOf(xw);
264d522f475Smrg    int row, col;
265d522f475Smrg    Bool oor;
266d522f475Smrg    int button;
2672eaa94a1Schristos    unsigned state;
268d522f475Smrg
269d522f475Smrg    /* Make sure the event is an appropriate type */
270d522f475Smrg    if ((event->type != ButtonPress &&
271d522f475Smrg	 event->type != ButtonRelease &&
272d522f475Smrg	 !screen->loc_filter) ||
273d522f475Smrg	(KeyModifiers != 0 && KeyModifiers != ControlMask))
274d522f475Smrg	return (False);
275d522f475Smrg
276d522f475Smrg    if ((event->type == ButtonPress &&
277d522f475Smrg	 !(screen->locator_events & LOC_BTNS_DN)) ||
278d522f475Smrg	(event->type == ButtonRelease &&
279d522f475Smrg	 !(screen->locator_events & LOC_BTNS_UP)))
280d522f475Smrg	return (True);
281d522f475Smrg
282d522f475Smrg    if (event->type == MotionNotify) {
283d522f475Smrg	CheckLocatorPosition(xw, event);
284d522f475Smrg	return (True);
285d522f475Smrg    }
286d522f475Smrg
287d522f475Smrg    /* get button # */
2882eaa94a1Schristos    button = (int) event->xbutton.button - 1;
289d522f475Smrg
290d522f475Smrg    LocatorCoords(row, col, event->xbutton.x, event->xbutton.y, oor);
291d522f475Smrg
292d522f475Smrg    /*
293d522f475Smrg     * DECterm mouse:
294d522f475Smrg     *
295d522f475Smrg     * ESCAPE '[' event ; mask ; row ; column '&' 'w'
296d522f475Smrg     */
297d522f475Smrg    memset(&reply, 0, sizeof(reply));
298d522f475Smrg    reply.a_type = ANSI_CSI;
299d522f475Smrg
300d522f475Smrg    if (oor) {
301d522f475Smrg	reply.a_nparam = 1;
302d522f475Smrg	reply.a_param[0] = 0;	/* Event - 0 = locator unavailable */
303d522f475Smrg	reply.a_inters = '&';
304d522f475Smrg	reply.a_final = 'w';
305d522f475Smrg	unparseseq(xw, &reply);
306d522f475Smrg
307d522f475Smrg	if (screen->locator_reset) {
308d522f475Smrg	    MotionOff(screen, xw);
309d522f475Smrg	    screen->send_mouse_pos = MOUSE_OFF;
310d522f475Smrg	}
311d522f475Smrg	return (True);
312d522f475Smrg    }
313d522f475Smrg
314d522f475Smrg    /*
315d522f475Smrg     * event:
316d522f475Smrg     *        1       no buttons
317d522f475Smrg     *        2       left button down
318d522f475Smrg     *        3       left button up
319d522f475Smrg     *        4       middle button down
320d522f475Smrg     *        5       middle button up
321d522f475Smrg     *        6       right button down
322d522f475Smrg     *        7       right button up
323d522f475Smrg     *        8       M4 down
324d522f475Smrg     *        9       M4 up
325d522f475Smrg     */
326d522f475Smrg    reply.a_nparam = 4;
327d522f475Smrg    switch (event->type) {
328d522f475Smrg    case ButtonPress:
3292eaa94a1Schristos	reply.a_param[0] = (ParmType) (2 + (button << 1));
330d522f475Smrg	break;
331d522f475Smrg    case ButtonRelease:
3322eaa94a1Schristos	reply.a_param[0] = (ParmType) (3 + (button << 1));
333d522f475Smrg	break;
334d522f475Smrg    default:
335d522f475Smrg	return (True);
336d522f475Smrg    }
337d522f475Smrg    /*
338d522f475Smrg     * mask:
339d522f475Smrg     * bit 7   bit 6   bit 5   bit 4   bit 3   bit 2       bit 1         bit 0
340d522f475Smrg     *                                 M4 down left down   middle down   right down
341d522f475Smrg     *
342d522f475Smrg     * Notice that Button1 (left) and Button3 (right) are swapped in the mask.
343d522f475Smrg     * Also, mask should be the state after the button press/release,
344d522f475Smrg     * X provides the state not including the button press/release.
345d522f475Smrg     */
346d522f475Smrg    state = (event->xbutton.state
347d522f475Smrg	     & (Button1Mask | Button2Mask | Button3Mask | Button4Mask)) >> 8;
3484e40088cSchristos    /* update mask to "after" state */
34920d2c4d2Smrg    state ^= ((unsigned) (1 << button));
3504e40088cSchristos    /* swap Button1 & Button3 */
351956cc18dSsnj    state = ((state & (unsigned) ~(4 | 1))
352956cc18dSsnj	     | ((state & 1) ? 4 : 0)
353956cc18dSsnj	     | ((state & 4) ? 1 : 0));
354d522f475Smrg
3552eaa94a1Schristos    reply.a_param[1] = (ParmType) state;
3562eaa94a1Schristos    reply.a_param[2] = (ParmType) row;
3572eaa94a1Schristos    reply.a_param[3] = (ParmType) col;
358d522f475Smrg    reply.a_inters = '&';
359d522f475Smrg    reply.a_final = 'w';
360d522f475Smrg
361d522f475Smrg    unparseseq(xw, &reply);
362d522f475Smrg
363d522f475Smrg    if (screen->locator_reset) {
364d522f475Smrg	MotionOff(screen, xw);
365d522f475Smrg	screen->send_mouse_pos = MOUSE_OFF;
366d522f475Smrg    }
367d522f475Smrg
368d522f475Smrg    /*
369d522f475Smrg     * DECterm turns the Locator off if a button is pressed while a filter rectangle
370d522f475Smrg     * is active. This might be a bug, but I don't know, so I'll emulate it anyways.
371d522f475Smrg     */
372d522f475Smrg    if (screen->loc_filter) {
373d522f475Smrg	screen->send_mouse_pos = MOUSE_OFF;
374d522f475Smrg	screen->loc_filter = False;
375d522f475Smrg	screen->locator_events = 0;
376d522f475Smrg	MotionOff(screen, xw);
377d522f475Smrg    }
378d522f475Smrg
379d522f475Smrg    return (True);
380d522f475Smrg}
381d522f475Smrg
382d522f475Smrg/*
383d522f475Smrg * mask:
384d522f475Smrg * bit 7   bit 6   bit 5   bit 4   bit 3   bit 2       bit 1         bit 0
385d522f475Smrg *                                 M4 down left down   middle down   right down
386d522f475Smrg *
387d522f475Smrg * Button1 (left) and Button3 (right) are swapped in the mask relative to X.
388d522f475Smrg */
389d522f475Smrg#define	ButtonState(state, mask)	\
3902eaa94a1Schristos{ (state) = (int) (((mask) & (Button1Mask | Button2Mask | Button3Mask | Button4Mask)) >> 8);	\
391d522f475Smrg  /* swap Button1 & Button3 */								\
392d522f475Smrg  (state) = ((state) & ~(4|1)) | (((state)&1)?4:0) | (((state)&4)?1:0);			\
393d522f475Smrg}
394d522f475Smrg
395d522f475Smrgvoid
396d522f475SmrgGetLocatorPosition(XtermWidget xw)
397d522f475Smrg{
398d522f475Smrg    ANSI reply;
399956cc18dSsnj    TScreen *screen = TScreenOf(xw);
400d522f475Smrg    Window root, child;
401d522f475Smrg    int rx, ry, x, y;
402d522f475Smrg    unsigned int mask;
403d522f475Smrg    int row = 0, col = 0;
404d522f475Smrg    Bool oor = False;
405d522f475Smrg    Bool ret = False;
406d522f475Smrg    int state;
407d522f475Smrg
408d522f475Smrg    /*
409d522f475Smrg     * DECterm turns the Locator off if the position is requested while a filter rectangle
410d522f475Smrg     * is active.  This might be a bug, but I don't know, so I'll emulate it anyways.
411d522f475Smrg     */
412d522f475Smrg    if (screen->loc_filter) {
413d522f475Smrg	screen->send_mouse_pos = MOUSE_OFF;
414d522f475Smrg	screen->loc_filter = False;
415d522f475Smrg	screen->locator_events = 0;
416d522f475Smrg	MotionOff(screen, xw);
417d522f475Smrg    }
418d522f475Smrg
419d522f475Smrg    memset(&reply, 0, sizeof(reply));
420d522f475Smrg    reply.a_type = ANSI_CSI;
421d522f475Smrg
422d522f475Smrg    if (screen->send_mouse_pos == DEC_LOCATOR) {
423d522f475Smrg	ret = XQueryPointer(screen->display, VWindow(screen), &root,
424d522f475Smrg			    &child, &rx, &ry, &x, &y, &mask);
425d522f475Smrg	if (ret) {
426d522f475Smrg	    LocatorCoords(row, col, x, y, oor);
427d522f475Smrg	}
428d522f475Smrg    }
429d522f475Smrg    if (ret == False || oor) {
430d522f475Smrg	reply.a_nparam = 1;
431d522f475Smrg	reply.a_param[0] = 0;	/* Event - 0 = locator unavailable */
432d522f475Smrg	reply.a_inters = '&';
433d522f475Smrg	reply.a_final = 'w';
434d522f475Smrg	unparseseq(xw, &reply);
435d522f475Smrg
436d522f475Smrg	if (screen->locator_reset) {
437d522f475Smrg	    MotionOff(screen, xw);
438d522f475Smrg	    screen->send_mouse_pos = MOUSE_OFF;
439d522f475Smrg	}
440d522f475Smrg	return;
441d522f475Smrg    }
442d522f475Smrg
443d522f475Smrg    ButtonState(state, mask);
444d522f475Smrg
445d522f475Smrg    reply.a_nparam = 4;
446d522f475Smrg    reply.a_param[0] = 1;	/* Event - 1 = response to locator request */
4472eaa94a1Schristos    reply.a_param[1] = (ParmType) state;
4482eaa94a1Schristos    reply.a_param[2] = (ParmType) row;
4492eaa94a1Schristos    reply.a_param[3] = (ParmType) col;
450d522f475Smrg    reply.a_inters = '&';
451d522f475Smrg    reply.a_final = 'w';
452d522f475Smrg    unparseseq(xw, &reply);
453d522f475Smrg
454d522f475Smrg    if (screen->locator_reset) {
455d522f475Smrg	MotionOff(screen, xw);
456d522f475Smrg	screen->send_mouse_pos = MOUSE_OFF;
457d522f475Smrg    }
458d522f475Smrg}
459d522f475Smrg
460d522f475Smrgvoid
461d522f475SmrgInitLocatorFilter(XtermWidget xw)
462d522f475Smrg{
463d522f475Smrg    ANSI reply;
464956cc18dSsnj    TScreen *screen = TScreenOf(xw);
465d522f475Smrg    Window root, child;
466d522f475Smrg    int rx, ry, x, y;
467d522f475Smrg    unsigned int mask;
468d522f475Smrg    int row = 0, col = 0;
469d522f475Smrg    Bool oor = 0;
470d522f475Smrg    Bool ret;
471d522f475Smrg    int state;
472d522f475Smrg
473d522f475Smrg    ret = XQueryPointer(screen->display, VWindow(screen),
474d522f475Smrg			&root, &child, &rx, &ry, &x, &y, &mask);
475d522f475Smrg    if (ret) {
476d522f475Smrg	LocatorCoords(row, col, x, y, oor);
477d522f475Smrg    }
478d522f475Smrg    if (ret == False || oor) {
479d522f475Smrg	/* Locator is unavailable */
480d522f475Smrg
481d522f475Smrg	if (screen->loc_filter_top != LOC_FILTER_POS ||
482d522f475Smrg	    screen->loc_filter_left != LOC_FILTER_POS ||
483d522f475Smrg	    screen->loc_filter_bottom != LOC_FILTER_POS ||
484d522f475Smrg	    screen->loc_filter_right != LOC_FILTER_POS) {
485d522f475Smrg	    /*
486d522f475Smrg	     * If any explicit coordinates were received,
487d522f475Smrg	     * report immediately with no coordinates.
488d522f475Smrg	     */
489d522f475Smrg	    memset(&reply, 0, sizeof(reply));
490d522f475Smrg	    reply.a_type = ANSI_CSI;
491d522f475Smrg	    reply.a_nparam = 1;
492d522f475Smrg	    reply.a_param[0] = 0;	/* Event - 0 = locator unavailable */
493d522f475Smrg	    reply.a_inters = '&';
494d522f475Smrg	    reply.a_final = 'w';
495d522f475Smrg	    unparseseq(xw, &reply);
496d522f475Smrg
497d522f475Smrg	    if (screen->locator_reset) {
498d522f475Smrg		MotionOff(screen, xw);
499d522f475Smrg		screen->send_mouse_pos = MOUSE_OFF;
500d522f475Smrg	    }
501d522f475Smrg	} else {
502d522f475Smrg	    /*
503d522f475Smrg	     * No explicit coordinates were received, and the pointer is
504d522f475Smrg	     * unavailable.  Report when the pointer re-enters the window.
505d522f475Smrg	     */
506d522f475Smrg	    screen->loc_filter = True;
507d522f475Smrg	    MotionOn(screen, xw);
508d522f475Smrg	}
509d522f475Smrg	return;
510d522f475Smrg    }
511d522f475Smrg
512d522f475Smrg    /*
513d522f475Smrg     * Adjust rectangle coordinates:
514d522f475Smrg     *  1. Replace "LOC_FILTER_POS" with current coordinates
515d522f475Smrg     *  2. Limit coordinates to screen size
516d522f475Smrg     *  3. make sure top and left are less than bottom and right, resp.
517d522f475Smrg     */
518d522f475Smrg    if (screen->locator_pixels) {
519d522f475Smrg	rx = OriginX(screen) * 2 + Width(screen);
520d522f475Smrg	ry = screen->border * 2 + Height(screen);
521d522f475Smrg    } else {
522d522f475Smrg	rx = screen->max_col;
523d522f475Smrg	ry = screen->max_row;
524d522f475Smrg    }
525d522f475Smrg
526d522f475Smrg#define	Adjust( coord, def, max )				\
527d522f475Smrg	if( (coord) == LOC_FILTER_POS )	(coord) = (def);	\
528d522f475Smrg	else if ((coord) < 1)		(coord) = 1;		\
529d522f475Smrg	else if ((coord) > (max))	(coord) = (max)
530d522f475Smrg
531d522f475Smrg    Adjust(screen->loc_filter_top, row, ry);
532d522f475Smrg    Adjust(screen->loc_filter_left, col, rx);
533d522f475Smrg    Adjust(screen->loc_filter_bottom, row, ry);
534d522f475Smrg    Adjust(screen->loc_filter_right, col, rx);
535d522f475Smrg
536d522f475Smrg    if (screen->loc_filter_top > screen->loc_filter_bottom) {
537d522f475Smrg	ry = screen->loc_filter_top;
538d522f475Smrg	screen->loc_filter_top = screen->loc_filter_bottom;
539d522f475Smrg	screen->loc_filter_bottom = ry;
540d522f475Smrg    }
541d522f475Smrg
542d522f475Smrg    if (screen->loc_filter_left > screen->loc_filter_right) {
543d522f475Smrg	rx = screen->loc_filter_left;
544d522f475Smrg	screen->loc_filter_left = screen->loc_filter_right;
545d522f475Smrg	screen->loc_filter_right = rx;
546d522f475Smrg    }
547d522f475Smrg
548d522f475Smrg    if ((col < screen->loc_filter_left) ||
549d522f475Smrg	(col > screen->loc_filter_right) ||
550d522f475Smrg	(row < screen->loc_filter_top) ||
551d522f475Smrg	(row > screen->loc_filter_bottom)) {
552d522f475Smrg	/* Pointer is already outside the rectangle - report immediately */
553d522f475Smrg	ButtonState(state, mask);
554d522f475Smrg
555d522f475Smrg	memset(&reply, 0, sizeof(reply));
556d522f475Smrg	reply.a_type = ANSI_CSI;
557d522f475Smrg	reply.a_nparam = 4;
558d522f475Smrg	reply.a_param[0] = 10;	/* Event - 10 = locator outside filter */
5592eaa94a1Schristos	reply.a_param[1] = (ParmType) state;
5602eaa94a1Schristos	reply.a_param[2] = (ParmType) row;
5612eaa94a1Schristos	reply.a_param[3] = (ParmType) col;
562d522f475Smrg	reply.a_inters = '&';
563d522f475Smrg	reply.a_final = 'w';
564d522f475Smrg	unparseseq(xw, &reply);
565d522f475Smrg
566d522f475Smrg	if (screen->locator_reset) {
567d522f475Smrg	    MotionOff(screen, xw);
568d522f475Smrg	    screen->send_mouse_pos = MOUSE_OFF;
569d522f475Smrg	}
570d522f475Smrg	return;
571d522f475Smrg    }
572d522f475Smrg
573d522f475Smrg    /*
574d522f475Smrg     * Rectangle is set up.  Allow pointer tracking
575d522f475Smrg     * to detect if the mouse leaves the rectangle.
576d522f475Smrg     */
577d522f475Smrg    screen->loc_filter = True;
578d522f475Smrg    MotionOn(screen, xw);
579d522f475Smrg}
580d522f475Smrg
581d522f475Smrgstatic void
582d522f475SmrgCheckLocatorPosition(XtermWidget xw, XEvent * event)
583d522f475Smrg{
584d522f475Smrg    ANSI reply;
585956cc18dSsnj    TScreen *screen = TScreenOf(xw);
586d522f475Smrg    int row, col;
587d522f475Smrg    Bool oor;
588d522f475Smrg    int state;
589d522f475Smrg
590d522f475Smrg    LocatorCoords(row, col, event->xbutton.x, event->xbutton.y, oor);
591d522f475Smrg
592d522f475Smrg    /*
593d522f475Smrg     * Send report if the pointer left the filter rectangle, if
594d522f475Smrg     * the pointer left the window, or if the filter rectangle
595d522f475Smrg     * had no coordinates and the pointer re-entered the window.
596d522f475Smrg     */
597d522f475Smrg    if (oor || (screen->loc_filter_top == LOC_FILTER_POS) ||
598d522f475Smrg	(col < screen->loc_filter_left) ||
599d522f475Smrg	(col > screen->loc_filter_right) ||
600d522f475Smrg	(row < screen->loc_filter_top) ||
601d522f475Smrg	(row > screen->loc_filter_bottom)) {
602d522f475Smrg	/* Filter triggered - disable it */
603d522f475Smrg	screen->loc_filter = False;
604d522f475Smrg	MotionOff(screen, xw);
605d522f475Smrg
606d522f475Smrg	memset(&reply, 0, sizeof(reply));
607d522f475Smrg	reply.a_type = ANSI_CSI;
608d522f475Smrg	if (oor) {
609d522f475Smrg	    reply.a_nparam = 1;
610d522f475Smrg	    reply.a_param[0] = 0;	/* Event - 0 = locator unavailable */
611d522f475Smrg	} else {
612d522f475Smrg	    ButtonState(state, event->xbutton.state);
613d522f475Smrg
614d522f475Smrg	    reply.a_nparam = 4;
615d522f475Smrg	    reply.a_param[0] = 10;	/* Event - 10 = locator outside filter */
6162eaa94a1Schristos	    reply.a_param[1] = (ParmType) state;
6172eaa94a1Schristos	    reply.a_param[2] = (ParmType) row;
6182eaa94a1Schristos	    reply.a_param[3] = (ParmType) col;
619d522f475Smrg	}
620d522f475Smrg
621d522f475Smrg	reply.a_inters = '&';
622d522f475Smrg	reply.a_final = 'w';
623d522f475Smrg	unparseseq(xw, &reply);
624d522f475Smrg
625d522f475Smrg	if (screen->locator_reset) {
626d522f475Smrg	    MotionOff(screen, xw);
627d522f475Smrg	    screen->send_mouse_pos = MOUSE_OFF;
628d522f475Smrg	}
629d522f475Smrg    }
630d522f475Smrg}
631d522f475Smrg#endif /* OPT_DEC_LOCATOR */
632d522f475Smrg
633d522f475Smrg#if OPT_READLINE
634d522f475Smrgstatic int
635d522f475SmrgisClick1_clean(TScreen * screen, XEvent * event)
636d522f475Smrg{
637d522f475Smrg    int delta;
638d522f475Smrg
639d522f475Smrg    if (!(event->type == ButtonPress || event->type == ButtonRelease)
640d522f475Smrg    /* Disable on Shift-Click-1, including the application-mouse modes */
641d522f475Smrg	|| (KeyModifiers & ShiftMask)
642d522f475Smrg	|| (screen->send_mouse_pos != MOUSE_OFF)	/* Kinda duplicate... */
643d522f475Smrg	||ExtendingSelection)	/* Was moved */
644d522f475Smrg	return 0;
645d522f475Smrg
646d522f475Smrg    if (event->type != ButtonRelease)
647d522f475Smrg	return 0;
648d522f475Smrg
649d522f475Smrg    if (lastButtonDownTime == (Time) 0) {
650d522f475Smrg	/* first time or once in a blue moon */
651d522f475Smrg	delta = screen->multiClickTime + 1;
652d522f475Smrg    } else if (event->xbutton.time > lastButtonDownTime) {
653d522f475Smrg	/* most of the time */
6542eaa94a1Schristos	delta = (int) (event->xbutton.time - lastButtonDownTime);
655d522f475Smrg    } else {
656d522f475Smrg	/* time has rolled over since lastButtonUpTime */
6572eaa94a1Schristos	delta = (int) ((((Time) ~ 0) - lastButtonDownTime) + event->xbutton.time);
658d522f475Smrg    }
659d522f475Smrg
660d522f475Smrg    return delta <= screen->multiClickTime;
661d522f475Smrg}
662d522f475Smrg
663d522f475Smrgstatic int
664d522f475SmrgisDoubleClick3(TScreen * screen, XEvent * event)
665d522f475Smrg{
666d522f475Smrg    int delta;
667d522f475Smrg
668d522f475Smrg    if (event->type != ButtonRelease
669d522f475Smrg	|| (KeyModifiers & ShiftMask)
670d522f475Smrg	|| event->xbutton.button != Button3) {
671d522f475Smrg	lastButton3UpTime = 0;	/* Disable the cached info */
672d522f475Smrg	return 0;
673d522f475Smrg    }
674d522f475Smrg    /* Process Btn3Release. */
675d522f475Smrg    if (lastButton3DoubleDownTime == (Time) 0) {
676d522f475Smrg	/* No previous click or once in a blue moon */
677d522f475Smrg	delta = screen->multiClickTime + 1;
678d522f475Smrg    } else if (event->xbutton.time > lastButton3DoubleDownTime) {
679d522f475Smrg	/* most of the time */
6802eaa94a1Schristos	delta = (int) (event->xbutton.time - lastButton3DoubleDownTime);
681d522f475Smrg    } else {
682d522f475Smrg	/* time has rolled over since lastButton3DoubleDownTime */
6832eaa94a1Schristos	delta = (int) ((((Time) ~ 0) - lastButton3DoubleDownTime) + event->xbutton.time);
684d522f475Smrg    }
685d522f475Smrg    if (delta <= screen->multiClickTime) {
686d522f475Smrg	/* Double click */
687d522f475Smrg	CELL cell;
688d522f475Smrg
689d522f475Smrg	/* Cannot check ExtendingSelection, since mouse-3 always sets it */
690d522f475Smrg	PointToCELL(screen, event->xbutton.y, event->xbutton.x, &cell);
691d522f475Smrg	if (isSameCELL(&cell, &lastButton3)) {
692d522f475Smrg	    lastButton3DoubleDownTime = 0;	/* Disable the third click */
693d522f475Smrg	    return 1;
694d522f475Smrg	}
695d522f475Smrg    }
696d522f475Smrg    /* Not a double click, memorize for future check. */
697d522f475Smrg    lastButton3UpTime = event->xbutton.time;
698d522f475Smrg    PointToCELL(screen, event->xbutton.y, event->xbutton.x, &lastButton3);
699d522f475Smrg    return 0;
700d522f475Smrg}
701d522f475Smrg
702d522f475Smrgstatic int
703d522f475SmrgCheckSecondPress3(TScreen * screen, XEvent * event)
704d522f475Smrg{
705d522f475Smrg    int delta;
706d522f475Smrg
707d522f475Smrg    if (event->type != ButtonPress
708d522f475Smrg	|| (KeyModifiers & ShiftMask)
709d522f475Smrg	|| event->xbutton.button != Button3) {
710d522f475Smrg	lastButton3DoubleDownTime = 0;	/* Disable the cached info */
711d522f475Smrg	return 0;
712d522f475Smrg    }
713d522f475Smrg    /* Process Btn3Press. */
714d522f475Smrg    if (lastButton3UpTime == (Time) 0) {
715d522f475Smrg	/* No previous click or once in a blue moon */
716d522f475Smrg	delta = screen->multiClickTime + 1;
717d522f475Smrg    } else if (event->xbutton.time > lastButton3UpTime) {
718d522f475Smrg	/* most of the time */
7192eaa94a1Schristos	delta = (int) (event->xbutton.time - lastButton3UpTime);
720d522f475Smrg    } else {
721d522f475Smrg	/* time has rolled over since lastButton3UpTime */
7222eaa94a1Schristos	delta = (int) ((((Time) ~ 0) - lastButton3UpTime) + event->xbutton.time);
723d522f475Smrg    }
724d522f475Smrg    if (delta <= screen->multiClickTime) {
725d522f475Smrg	CELL cell;
726d522f475Smrg
727d522f475Smrg	PointToCELL(screen, event->xbutton.y, event->xbutton.x, &cell);
728d522f475Smrg	if (isSameCELL(&cell, &lastButton3)) {
729d522f475Smrg	    /* A candidate for a double-click */
730d522f475Smrg	    lastButton3DoubleDownTime = event->xbutton.time;
731d522f475Smrg	    PointToCELL(screen, event->xbutton.y, event->xbutton.x, &lastButton3);
732d522f475Smrg	    return 1;
733d522f475Smrg	}
734d522f475Smrg	lastButton3UpTime = 0;	/* Disable the info about the previous click */
735d522f475Smrg    }
736d522f475Smrg    /* Either too long, or moved, disable. */
737d522f475Smrg    lastButton3DoubleDownTime = 0;
738d522f475Smrg    return 0;
739d522f475Smrg}
740d522f475Smrg
741d522f475Smrgstatic int
742d522f475SmrgrowOnCurrentLine(TScreen * screen,
743d522f475Smrg		 int line,
744d522f475Smrg		 int *deltap)	/* must be XButtonEvent */
745d522f475Smrg{
746956cc18dSsnj    int result = 1;
747d522f475Smrg    int l1, l2;
748d522f475Smrg
749d522f475Smrg    *deltap = 0;
750956cc18dSsnj    if (line != screen->cur_row) {
751956cc18dSsnj	if (line < screen->cur_row)
752956cc18dSsnj	    l1 = line, l2 = screen->cur_row;
753956cc18dSsnj	else
754956cc18dSsnj	    l2 = line, l1 = screen->cur_row;
755956cc18dSsnj	l1--;
756956cc18dSsnj	while (++l1 < l2) {
757956cc18dSsnj	    LineData *ld = GET_LINEDATA(screen, l1);
758956cc18dSsnj	    if (!LineTstWrapped(ld)) {
759956cc18dSsnj		result = 0;
760956cc18dSsnj		break;
761956cc18dSsnj	    }
762956cc18dSsnj	}
763956cc18dSsnj	if (result) {
764956cc18dSsnj	    /* Everything is on one "wrapped line" now */
765956cc18dSsnj	    *deltap = line - screen->cur_row;
766956cc18dSsnj	}
767956cc18dSsnj    }
768956cc18dSsnj    return result;
769d522f475Smrg}
770d522f475Smrg
771d522f475Smrgstatic int
772d522f475SmrgeventRow(TScreen * screen, XEvent * event)	/* must be XButtonEvent */
773d522f475Smrg{
774d522f475Smrg    return (event->xbutton.y - screen->border) / FontHeight(screen);
775d522f475Smrg}
776d522f475Smrg
777d522f475Smrgstatic int
778d522f475SmrgeventColBetween(TScreen * screen, XEvent * event)	/* must be XButtonEvent */
779d522f475Smrg{
780d522f475Smrg    /* Correct by half a width - we are acting on a boundary, not on a cell. */
781d522f475Smrg    return ((event->xbutton.x - OriginX(screen) + (FontWidth(screen) - 1) / 2)
782d522f475Smrg	    / FontWidth(screen));
783d522f475Smrg}
784d522f475Smrg
785d522f475Smrgstatic int
786d522f475SmrgReadLineMovePoint(TScreen * screen, int col, int ldelta)
787d522f475Smrg{
788d522f475Smrg    Char line[6];
789d522f475Smrg    unsigned count = 0;
790d522f475Smrg
791d522f475Smrg    col += ldelta * MaxCols(screen) - screen->cur_col;
792d522f475Smrg    if (col == 0)
793d522f475Smrg	return 0;
794d522f475Smrg    if (screen->control_eight_bits) {
795d522f475Smrg	line[count++] = ANSI_CSI;
796d522f475Smrg    } else {
797d522f475Smrg	line[count++] = ANSI_ESC;
798d522f475Smrg	line[count++] = '[';	/* XXX maybe sometimes O is better? */
799d522f475Smrg    }
80020d2c4d2Smrg    line[count] = CharOf(col > 0 ? 'C' : 'D');
801d522f475Smrg    if (col < 0)
802d522f475Smrg	col = -col;
803d522f475Smrg    while (col--)
804d522f475Smrg	v_write(screen->respond, line, 3);
805d522f475Smrg    return 1;
806d522f475Smrg}
807d522f475Smrg
808d522f475Smrgstatic int
809d522f475SmrgReadLineDelete(TScreen * screen, CELL * cell1, CELL * cell2)
810d522f475Smrg{
811d522f475Smrg    int del;
812d522f475Smrg
813d522f475Smrg    del = (cell2->col - cell1->col) + ((cell2->row - cell1->row) * MaxCols(screen));
814d522f475Smrg    if (del <= 0)		/* Just in case... */
815d522f475Smrg	return 0;
816d522f475Smrg    while (del--)
817d522f475Smrg	v_write(screen->respond, (Char *) "\177", 1);
818d522f475Smrg    return 1;
819d522f475Smrg}
820d522f475Smrg#endif /* OPT_READLINE */
821d522f475Smrg
822d522f475Smrg/* ^XM-G<line+' '><col+' '> */
823d522f475Smrgvoid
824d522f475SmrgDiredButton(Widget w,
825d522f475Smrg	    XEvent * event,	/* must be XButtonEvent */
826d522f475Smrg	    String * params GCC_UNUSED,		/* selections */
827d522f475Smrg	    Cardinal *num_params GCC_UNUSED)
828d522f475Smrg{
829956cc18dSsnj    XtermWidget xw;
830956cc18dSsnj
831956cc18dSsnj    if ((xw = getXtermWidget(w)) != 0) {
832956cc18dSsnj	TScreen *screen = TScreenOf(xw);
833d522f475Smrg	Char Line[6];
834d522f475Smrg	unsigned line, col;
835d522f475Smrg
8362eaa94a1Schristos	if ((event->type == ButtonPress || event->type == ButtonRelease)
8372eaa94a1Schristos	    && (event->xbutton.y >= screen->border)
8382eaa94a1Schristos	    && (event->xbutton.x >= OriginX(screen))) {
83920d2c4d2Smrg	    line = (unsigned) ((event->xbutton.y - screen->border)
84020d2c4d2Smrg			       / FontHeight(screen));
84120d2c4d2Smrg	    col = (unsigned) ((event->xbutton.x - OriginX(screen))
84220d2c4d2Smrg			      / FontWidth(screen));
843d522f475Smrg	    Line[0] = CONTROL('X');
844d522f475Smrg	    Line[1] = ANSI_ESC;
845d522f475Smrg	    Line[2] = 'G';
8462eaa94a1Schristos	    Line[3] = CharOf(' ' + col);
8472eaa94a1Schristos	    Line[4] = CharOf(' ' + line);
848d522f475Smrg	    v_write(screen->respond, Line, 5);
849d522f475Smrg	}
850d522f475Smrg    }
851d522f475Smrg}
852d522f475Smrg
853d522f475Smrg#if OPT_READLINE
854d522f475Smrgvoid
855d522f475SmrgReadLineButton(Widget w,
856d522f475Smrg	       XEvent * event,	/* must be XButtonEvent */
857d522f475Smrg	       String * params GCC_UNUSED,	/* selections */
858d522f475Smrg	       Cardinal *num_params GCC_UNUSED)
859d522f475Smrg{
860956cc18dSsnj    XtermWidget xw;
861956cc18dSsnj
862956cc18dSsnj    if ((xw = getXtermWidget(w)) != 0) {
863956cc18dSsnj	TScreen *screen = TScreenOf(xw);
864d522f475Smrg	Char Line[6];
865d522f475Smrg	int line, col, ldelta = 0;
866d522f475Smrg
867d522f475Smrg	if (!(event->type == ButtonPress || event->type == ButtonRelease)
868d522f475Smrg	    || (screen->send_mouse_pos != MOUSE_OFF) || ExtendingSelection)
869d522f475Smrg	    goto finish;
870d522f475Smrg	if (event->type == ButtonRelease) {
871d522f475Smrg	    int delta;
872d522f475Smrg
873d522f475Smrg	    if (lastButtonDownTime == (Time) 0) {
874d522f475Smrg		/* first time and once in a blue moon */
875d522f475Smrg		delta = screen->multiClickTime + 1;
876d522f475Smrg	    } else if (event->xbutton.time > lastButtonDownTime) {
877d522f475Smrg		/* most of the time */
8782eaa94a1Schristos		delta = (int) (event->xbutton.time - lastButtonDownTime);
879d522f475Smrg	    } else {
880d522f475Smrg		/* time has rolled over since lastButtonUpTime */
8812eaa94a1Schristos		delta = (int) ((((Time) ~ 0) - lastButtonDownTime) + event->xbutton.time);
882d522f475Smrg	    }
883d522f475Smrg	    if (delta > screen->multiClickTime)
884d522f475Smrg		goto finish;	/* All this work for this... */
885d522f475Smrg	}
886d522f475Smrg	line = (event->xbutton.y - screen->border) / FontHeight(screen);
887956cc18dSsnj	if (!rowOnCurrentLine(screen, line, &ldelta))
888956cc18dSsnj	    goto finish;
889d522f475Smrg	/* Correct by half a width - we are acting on a boundary, not on a cell. */
890d522f475Smrg	col = (event->xbutton.x - OriginX(screen) + (FontWidth(screen) - 1)
891d522f475Smrg	       / 2)
892d522f475Smrg	    / FontWidth(screen) - screen->cur_col + ldelta * MaxCols(screen);
893d522f475Smrg	if (col == 0)
894d522f475Smrg	    goto finish;
895d522f475Smrg	Line[0] = ANSI_ESC;
896d522f475Smrg	/* XXX: sometimes it is better to send '['? */
897d522f475Smrg	Line[1] = 'O';
8982eaa94a1Schristos	Line[2] = CharOf(col > 0 ? 'C' : 'D');
899d522f475Smrg	if (col < 0)
900d522f475Smrg	    col = -col;
901d522f475Smrg	while (col--)
902d522f475Smrg	    v_write(screen->respond, Line, 3);
903d522f475Smrg      finish:
904d522f475Smrg	if (event->type == ButtonRelease)
905d522f475Smrg	    do_select_end(xw, event, params, num_params, False);
906d522f475Smrg    }
907d522f475Smrg}
908d522f475Smrg#endif /* OPT_READLINE */
909d522f475Smrg
910d522f475Smrg/* repeats <ESC>n or <ESC>p */
911d522f475Smrgvoid
912d522f475SmrgViButton(Widget w,
913d522f475Smrg	 XEvent * event,	/* must be XButtonEvent */
914d522f475Smrg	 String * params GCC_UNUSED,	/* selections */
915d522f475Smrg	 Cardinal *num_params GCC_UNUSED)
916d522f475Smrg{
917956cc18dSsnj    XtermWidget xw;
918956cc18dSsnj
919956cc18dSsnj    if ((xw = getXtermWidget(w)) != 0) {
920956cc18dSsnj	TScreen *screen = TScreenOf(xw);
921d522f475Smrg	int pty = screen->respond;
922d522f475Smrg	Char Line[6];
923d522f475Smrg	int line;
924d522f475Smrg
925d522f475Smrg	if (event->type == ButtonPress || event->type == ButtonRelease) {
926d522f475Smrg
927d522f475Smrg	    line = screen->cur_row -
928d522f475Smrg		((event->xbutton.y - screen->border) / FontHeight(screen));
929d522f475Smrg	    if (line != 0) {
930d522f475Smrg		Line[0] = ANSI_ESC;	/* force an exit from insert-mode */
931d522f475Smrg		v_write(pty, Line, 1);
932d522f475Smrg
933d522f475Smrg		if (line < 0) {
934d522f475Smrg		    line = -line;
935d522f475Smrg		    Line[0] = CONTROL('n');
936d522f475Smrg		} else {
937d522f475Smrg		    Line[0] = CONTROL('p');
938d522f475Smrg		}
939d522f475Smrg		while (--line >= 0)
940d522f475Smrg		    v_write(pty, Line, 1);
941d522f475Smrg	    }
942d522f475Smrg	}
943d522f475Smrg    }
944d522f475Smrg}
945d522f475Smrg
946d522f475Smrg/*
947d522f475Smrg * This function handles button-motion events
948d522f475Smrg */
949d522f475Smrg/*ARGSUSED*/
950d522f475Smrgvoid
951d522f475SmrgHandleSelectExtend(Widget w,
952d522f475Smrg		   XEvent * event,	/* must be XMotionEvent */
953d522f475Smrg		   String * params GCC_UNUSED,
954d522f475Smrg		   Cardinal *num_params GCC_UNUSED)
955d522f475Smrg{
956956cc18dSsnj    XtermWidget xw;
957956cc18dSsnj
958956cc18dSsnj    if ((xw = getXtermWidget(w)) != 0) {
959956cc18dSsnj	TScreen *screen = TScreenOf(xw);
960d522f475Smrg	CELL cell;
961d522f475Smrg
962d522f475Smrg	screen->selection_time = event->xmotion.time;
963d522f475Smrg	switch (screen->eventMode) {
964d522f475Smrg	    /* If not in one of the DEC mouse-reporting modes */
965d522f475Smrg	case LEFTEXTENSION:
966d522f475Smrg	case RIGHTEXTENSION:
967d522f475Smrg	    PointToCELL(screen, event->xmotion.y, event->xmotion.x, &cell);
968d522f475Smrg	    ExtendExtend(xw, &cell);
969d522f475Smrg	    break;
970d522f475Smrg
971d522f475Smrg	    /* If in motion reporting mode, send mouse position to
972d522f475Smrg	       character process as a key sequence \E[M... */
973d522f475Smrg	case NORMAL:
974d522f475Smrg	    /* will get here if send_mouse_pos != MOUSE_OFF */
975d522f475Smrg	    if (screen->send_mouse_pos == BTN_EVENT_MOUSE
976d522f475Smrg		|| screen->send_mouse_pos == ANY_EVENT_MOUSE) {
977d522f475Smrg		(void) SendMousePosition(xw, event);
978d522f475Smrg	    }
979d522f475Smrg	    break;
980d522f475Smrg	}
981d522f475Smrg    }
982d522f475Smrg}
983d522f475Smrg
984d522f475Smrgvoid
985d522f475SmrgHandleKeyboardSelectExtend(Widget w,
986d522f475Smrg			   XEvent * event GCC_UNUSED,	/* must be XButtonEvent */
987d522f475Smrg			   String * params GCC_UNUSED,
988d522f475Smrg			   Cardinal *num_params GCC_UNUSED)
989d522f475Smrg{
990956cc18dSsnj    XtermWidget xw;
991956cc18dSsnj
992956cc18dSsnj    if ((xw = getXtermWidget(w)) != 0) {
993956cc18dSsnj	TScreen *screen = TScreenOf(xw);
994d522f475Smrg	ExtendExtend(xw, &screen->cursorp);
995d522f475Smrg    }
996d522f475Smrg}
997d522f475Smrg
998d522f475Smrgstatic void
999d522f475Smrgdo_select_end(XtermWidget xw,
1000d522f475Smrg	      XEvent * event,	/* must be XButtonEvent */
1001d522f475Smrg	      String * params,	/* selections */
1002d522f475Smrg	      Cardinal *num_params,
1003d522f475Smrg	      Bool use_cursor_loc)
1004d522f475Smrg{
1005d522f475Smrg#if OPT_READLINE
1006d522f475Smrg    int ldelta1, ldelta2;
1007d522f475Smrg#endif
1008956cc18dSsnj    TScreen *screen = TScreenOf(xw);
1009d522f475Smrg
1010d522f475Smrg    screen->selection_time = event->xbutton.time;
1011d522f475Smrg    switch (screen->eventMode) {
1012d522f475Smrg    case NORMAL:
1013d522f475Smrg	(void) SendMousePosition(xw, event);
1014d522f475Smrg	break;
1015d522f475Smrg    case LEFTEXTENSION:
1016d522f475Smrg    case RIGHTEXTENSION:
1017d522f475Smrg	EndExtend(xw, event, params, *num_params, use_cursor_loc);
1018d522f475Smrg#if OPT_READLINE
1019d522f475Smrg	if (isClick1_clean(screen, event)
1020d522f475Smrg	    && SCREEN_FLAG(screen, click1_moves)
1021d522f475Smrg	    && rowOnCurrentLine(screen, eventRow(screen, event), &ldelta1)) {
1022d522f475Smrg	    ReadLineMovePoint(screen, eventColBetween(screen, event), ldelta1);
1023d522f475Smrg	}
1024d522f475Smrg	if (isDoubleClick3(screen, event)
1025d522f475Smrg	    && SCREEN_FLAG(screen, dclick3_deletes)
1026d522f475Smrg	    && rowOnCurrentLine(screen, screen->startSel.row, &ldelta1)
1027d522f475Smrg	    && rowOnCurrentLine(screen, screen->endSel.row, &ldelta2)) {
1028d522f475Smrg	    ReadLineMovePoint(screen, screen->endSel.col, ldelta2);
1029d522f475Smrg	    ReadLineDelete(screen, &screen->startSel, &(screen->endSel));
1030d522f475Smrg	}
1031d522f475Smrg#endif /* OPT_READLINE */
1032d522f475Smrg	break;
1033d522f475Smrg    }
1034d522f475Smrg}
1035d522f475Smrg
1036d522f475Smrgvoid
1037d522f475SmrgHandleSelectEnd(Widget w,
1038d522f475Smrg		XEvent * event,	/* must be XButtonEvent */
1039d522f475Smrg		String * params,	/* selections */
1040d522f475Smrg		Cardinal *num_params)
1041d522f475Smrg{
1042956cc18dSsnj    XtermWidget xw;
1043956cc18dSsnj
1044956cc18dSsnj    if ((xw = getXtermWidget(w)) != 0) {
1045956cc18dSsnj	do_select_end(xw, event, params, num_params, False);
1046956cc18dSsnj    }
1047d522f475Smrg}
1048d522f475Smrg
1049d522f475Smrgvoid
1050d522f475SmrgHandleKeyboardSelectEnd(Widget w,
1051d522f475Smrg			XEvent * event,		/* must be XButtonEvent */
1052d522f475Smrg			String * params,	/* selections */
1053d522f475Smrg			Cardinal *num_params)
1054d522f475Smrg{
1055956cc18dSsnj    XtermWidget xw;
1056956cc18dSsnj
1057956cc18dSsnj    if ((xw = getXtermWidget(w)) != 0) {
1058956cc18dSsnj	do_select_end(xw, event, params, num_params, True);
1059956cc18dSsnj    }
1060d522f475Smrg}
1061d522f475Smrg
1062d522f475Smrgstruct _SelectionList {
1063d522f475Smrg    String *params;
1064d522f475Smrg    Cardinal count;
1065d522f475Smrg    Atom *targets;
1066d522f475Smrg    Time time;
1067d522f475Smrg};
1068d522f475Smrg
1069d522f475Smrgstatic unsigned
1070d522f475SmrgDECtoASCII(unsigned ch)
1071d522f475Smrg{
1072d522f475Smrg    if (xtermIsDecGraphic(ch)) {
10732eaa94a1Schristos	ch = CharOf("###########+++++##-##++++|######"[ch]);
10742eaa94a1Schristos	/*           01234567890123456789012345678901 */
1075d522f475Smrg    }
1076d522f475Smrg    return ch;
1077d522f475Smrg}
107820d2c4d2Smrg
107920d2c4d2Smrg#if OPT_WIDE_CHARS
108020d2c4d2Smrgstatic Cardinal
108120d2c4d2SmrgaddXtermChar(Char ** buffer, Cardinal *used, Cardinal offset, unsigned value)
108220d2c4d2Smrg{
108320d2c4d2Smrg    if (offset + 1 >= *used) {
108420d2c4d2Smrg	*used = 1 + (2 * (offset + 1));
108520d2c4d2Smrg	allocXtermChars(buffer, *used);
108620d2c4d2Smrg    }
108720d2c4d2Smrg    (*buffer)[offset++] = (Char) value;
108820d2c4d2Smrg    return offset;
108920d2c4d2Smrg}
109020d2c4d2Smrg#define AddChar(buffer, used, offset, value) \
109120d2c4d2Smrg	offset = addXtermChar(buffer, used, offset, (unsigned) value)
109220d2c4d2Smrg
1093d522f475Smrg/*
1094d522f475Smrg * Convert a UTF-8 string to Latin-1, replacing non Latin-1 characters by `#',
1095d522f475Smrg * or ASCII/Latin-1 equivalents for special cases.
1096d522f475Smrg */
1097d522f475Smrgstatic Char *
109820d2c4d2SmrgUTF8toLatin1(TScreen * screen, Char * s, unsigned long len, unsigned long *result)
1099d522f475Smrg{
1100d522f475Smrg    static Char *buffer;
1101956cc18dSsnj    static Cardinal used;
1102d522f475Smrg
110320d2c4d2Smrg    Cardinal offset = 0;
1104d522f475Smrg
110520d2c4d2Smrg    Char *p;
1106d522f475Smrg
110720d2c4d2Smrg    if (len != 0) {
1108d522f475Smrg	PtyData data;
1109d522f475Smrg
1110d522f475Smrg	fakePtyData(&data, s, s + len);
1111d522f475Smrg	while (decodeUtf8(&data)) {
1112956cc18dSsnj	    Bool fails = False;
1113956cc18dSsnj	    Bool extra = False;
1114d522f475Smrg	    IChar value = skipPtyData(&data);
1115d522f475Smrg	    if (value == UCS_REPL) {
1116956cc18dSsnj		fails = True;
1117d522f475Smrg	    } else if (value < 256) {
111820d2c4d2Smrg		AddChar(&buffer, &used, offset, CharOf(value));
1119d522f475Smrg	    } else {
1120d522f475Smrg		unsigned eqv = ucs2dec(value);
1121d522f475Smrg		if (xtermIsDecGraphic(eqv)) {
112220d2c4d2Smrg		    AddChar(&buffer, &used, offset, DECtoASCII(eqv));
1123d522f475Smrg		} else {
1124d522f475Smrg		    eqv = AsciiEquivs(value);
1125956cc18dSsnj		    if (eqv == value) {
1126956cc18dSsnj			fails = True;
1127956cc18dSsnj		    } else {
112820d2c4d2Smrg			AddChar(&buffer, &used, offset, eqv);
1129956cc18dSsnj		    }
1130956cc18dSsnj		    if (isWide((wchar_t) value))
1131956cc18dSsnj			extra = True;
1132956cc18dSsnj		}
1133956cc18dSsnj	    }
1134956cc18dSsnj
1135956cc18dSsnj	    /*
1136956cc18dSsnj	     * If we're not able to plug in a single-byte result, insert the
1137956cc18dSsnj	     * defaultString (which normally is a single "#", but could be
1138956cc18dSsnj	     * whatever the user wants).
1139956cc18dSsnj	     */
1140956cc18dSsnj	    if (fails) {
1141956cc18dSsnj		for (p = (Char *) screen->default_string; *p != '\0'; ++p) {
114220d2c4d2Smrg		    AddChar(&buffer, &used, offset, *p);
1143d522f475Smrg		}
1144d522f475Smrg	    }
1145956cc18dSsnj	    if (extra)
114620d2c4d2Smrg		AddChar(&buffer, &used, offset, ' ');
1147d522f475Smrg	}
114820d2c4d2Smrg	AddChar(&buffer, &used, offset, '\0');
114920d2c4d2Smrg	*result = (unsigned long) (offset - 1);
1150d522f475Smrg    } else {
1151d522f475Smrg	*result = 0;
1152d522f475Smrg    }
1153d522f475Smrg    return buffer;
1154d522f475Smrg}
115520d2c4d2Smrg
115620d2c4d2Smrgint
115720d2c4d2SmrgxtermUtf8ToTextList(XtermWidget xw,
115820d2c4d2Smrg		    XTextProperty * text_prop,
115920d2c4d2Smrg		    char ***text_list,
116020d2c4d2Smrg		    int *text_list_count)
116120d2c4d2Smrg{
116220d2c4d2Smrg    TScreen *screen = TScreenOf(xw);
116320d2c4d2Smrg    Display *dpy = screen->display;
116420d2c4d2Smrg    int rc = -1;
116520d2c4d2Smrg
116620d2c4d2Smrg    if (text_prop->format == 8
116720d2c4d2Smrg	&& (rc = Xutf8TextPropertyToTextList(dpy, text_prop,
116820d2c4d2Smrg					     text_list,
116920d2c4d2Smrg					     text_list_count)) >= 0) {
117020d2c4d2Smrg	if (*text_list != NULL && *text_list_count != 0) {
117120d2c4d2Smrg	    int i;
117220d2c4d2Smrg	    Char *data;
117320d2c4d2Smrg	    char **new_text_list, *tmp;
117420d2c4d2Smrg	    unsigned long size, new_size;
117520d2c4d2Smrg
117620d2c4d2Smrg	    TRACE(("xtermUtf8ToTextList size %d\n", *text_list_count));
117720d2c4d2Smrg
117820d2c4d2Smrg	    /*
117920d2c4d2Smrg	     * XLib StringList actually uses only two pointers, one for the
118020d2c4d2Smrg	     * list itself, and one for the data.  Pointer to the data is the
118120d2c4d2Smrg	     * first element of the list, the rest (if any) list elements point
118220d2c4d2Smrg	     * to the same memory block as the first element
118320d2c4d2Smrg	     */
118420d2c4d2Smrg	    new_size = 0;
118520d2c4d2Smrg	    for (i = 0; i < *text_list_count; ++i) {
118620d2c4d2Smrg		data = (Char *) (*text_list)[i];
118720d2c4d2Smrg		size = strlen((*text_list)[i]) + 1;
118820d2c4d2Smrg		(void) UTF8toLatin1(screen, data, size, &size);
118920d2c4d2Smrg		new_size += size + 1;
119020d2c4d2Smrg	    }
119120d2c4d2Smrg	    new_text_list =
119220d2c4d2Smrg		(char **) XtMalloc((Cardinal) sizeof(char *) * (unsigned) *text_list_count);
119320d2c4d2Smrg	    new_text_list[0] = tmp = XtMalloc((Cardinal) new_size);
119420d2c4d2Smrg	    for (i = 0; i < (*text_list_count); ++i) {
119520d2c4d2Smrg		data = (Char *) (*text_list)[i];
119620d2c4d2Smrg		size = strlen((*text_list)[i]) + 1;
119720d2c4d2Smrg		data = UTF8toLatin1(screen, data, size, &size);
119820d2c4d2Smrg		memcpy(tmp, data, size + 1);
119920d2c4d2Smrg		new_text_list[i] = tmp;
120020d2c4d2Smrg		tmp += size + 1;
120120d2c4d2Smrg	    }
120220d2c4d2Smrg	    XFreeStringList((*text_list));
120320d2c4d2Smrg	    *text_list = new_text_list;
120420d2c4d2Smrg	} else {
120520d2c4d2Smrg	    rc = -1;
120620d2c4d2Smrg	}
120720d2c4d2Smrg    }
120820d2c4d2Smrg    return rc;
120920d2c4d2Smrg}
1210d522f475Smrg#endif /* OPT_WIDE_CHARS */
1211d522f475Smrg
1212956cc18dSsnjstatic char *
1213956cc18dSsnjparseItem(char *value, char *nextc)
1214d522f475Smrg{
1215956cc18dSsnj    char *nextp = value;
1216956cc18dSsnj    while (*nextp != '\0' && *nextp != ',') {
1217956cc18dSsnj	*nextp = x_toupper(*nextp);
1218956cc18dSsnj	++nextp;
1219956cc18dSsnj    }
1220956cc18dSsnj    *nextc = *nextp;
1221956cc18dSsnj    *nextp = '\0';
1222956cc18dSsnj    x_strtrim(value);
1223d522f475Smrg
1224956cc18dSsnj    return nextp;
1225956cc18dSsnj}
1226d522f475Smrg
1227956cc18dSsnj/*
1228956cc18dSsnj * All of the wanted strings are unique in the first character, so we can
1229956cc18dSsnj * use simple abbreviations.
1230956cc18dSsnj */
1231956cc18dSsnjstatic Bool
1232956cc18dSsnjsameItem(const char *actual, const char *wanted)
1233956cc18dSsnj{
1234956cc18dSsnj    Bool result = False;
1235956cc18dSsnj    size_t have = strlen(actual);
1236956cc18dSsnj    size_t need = strlen(wanted);
1237d522f475Smrg
1238956cc18dSsnj    if (have != 0 && have <= need) {
1239956cc18dSsnj	if (!strncmp(actual, wanted, have)) {
1240956cc18dSsnj	    TRACE(("...matched \"%s\"\n", wanted));
1241956cc18dSsnj	    result = True;
1242956cc18dSsnj	}
1243956cc18dSsnj    }
1244956cc18dSsnj
1245956cc18dSsnj    return result;
1246956cc18dSsnj}
1247956cc18dSsnj
1248956cc18dSsnj/*
1249956cc18dSsnj * Handle the eightBitSelectTypes or utf8SelectTypes resource values.
1250956cc18dSsnj */
1251956cc18dSsnjstatic Bool
1252956cc18dSsnjoverrideTargets(Widget w, String value, Atom ** resultp)
1253956cc18dSsnj{
1254956cc18dSsnj    Bool override = False;
1255956cc18dSsnj    XtermWidget xw;
1256956cc18dSsnj
1257956cc18dSsnj    if ((xw = getXtermWidget(w)) != 0) {
1258956cc18dSsnj	TScreen *screen = TScreenOf(xw);
1259956cc18dSsnj
126020d2c4d2Smrg	if (!IsEmpty(value)) {
1261956cc18dSsnj	    String copied = x_strdup(value);
1262956cc18dSsnj	    if (copied != 0) {
1263956cc18dSsnj		Atom *result = 0;
1264956cc18dSsnj		Cardinal count = 1;
1265956cc18dSsnj		int n;
1266d522f475Smrg
1267956cc18dSsnj		TRACE(("decoding SelectTypes \"%s\"\n", value));
1268956cc18dSsnj		for (n = 0; copied[n] != '\0'; ++n) {
1269956cc18dSsnj		    if (copied[n] == ',')
1270956cc18dSsnj			++count;
1271956cc18dSsnj		}
127220d2c4d2Smrg		result = (Atom *) XtMalloc(((2 * count) + 1)
127320d2c4d2Smrg					   * (Cardinal) sizeof(Atom));
1274956cc18dSsnj		if (result == NULL) {
1275956cc18dSsnj		    TRACE(("Couldn't allocate selection types\n"));
1276956cc18dSsnj		} else {
1277956cc18dSsnj		    char nextc = '?';
127820d2c4d2Smrg		    char *listp = (char *) copied;
1279956cc18dSsnj		    count = 0;
1280956cc18dSsnj		    do {
1281956cc18dSsnj			char *nextp = parseItem(listp, &nextc);
1282956cc18dSsnj			size_t len = strlen(listp);
1283956cc18dSsnj
1284956cc18dSsnj			if (len == 0) {
1285956cc18dSsnj			    ;
1286956cc18dSsnj			}
1287956cc18dSsnj#if OPT_WIDE_CHARS
1288956cc18dSsnj			else if (sameItem(listp, "UTF8")) {
1289956cc18dSsnj			    result[count++] = XA_UTF8_STRING(XtDisplay(w));
1290956cc18dSsnj			}
1291956cc18dSsnj#endif
1292956cc18dSsnj			else if (sameItem(listp, "I18N")) {
1293956cc18dSsnj			    if (screen->i18nSelections) {
1294956cc18dSsnj				result[count++] = XA_TEXT(XtDisplay(w));
1295956cc18dSsnj				result[count++] = XA_COMPOUND_TEXT(XtDisplay(w));
1296956cc18dSsnj			    }
1297956cc18dSsnj			} else if (sameItem(listp, "TEXT")) {
1298956cc18dSsnj			    result[count++] = XA_TEXT(XtDisplay(w));
1299956cc18dSsnj			} else if (sameItem(listp, "COMPOUND_TEXT")) {
1300956cc18dSsnj			    result[count++] = XA_COMPOUND_TEXT(XtDisplay(w));
1301956cc18dSsnj			} else if (sameItem(listp, "STRING")) {
1302956cc18dSsnj			    result[count++] = XA_STRING;
1303956cc18dSsnj			}
1304956cc18dSsnj			*nextp++ = nextc;
1305956cc18dSsnj			listp = nextp;
1306956cc18dSsnj		    } while (nextc != '\0');
1307956cc18dSsnj		    if (count) {
1308956cc18dSsnj			result[count] = None;
1309956cc18dSsnj			override = True;
1310956cc18dSsnj			*resultp = result;
1311956cc18dSsnj		    } else {
1312956cc18dSsnj			XtFree((char *) result);
1313956cc18dSsnj		    }
1314956cc18dSsnj		}
1315956cc18dSsnj	    } else {
1316956cc18dSsnj		TRACE(("Couldn't allocate copy of selection types\n"));
1317d522f475Smrg	    }
1318956cc18dSsnj	}
1319956cc18dSsnj    }
1320956cc18dSsnj    return override;
1321956cc18dSsnj}
1322956cc18dSsnj
1323956cc18dSsnj#if OPT_WIDE_CHARS
1324956cc18dSsnjstatic Atom *
1325956cc18dSsnjallocUtf8Targets(Widget w, TScreen * screen)
1326956cc18dSsnj{
1327956cc18dSsnj    Atom **resultp = &(screen->selection_targets_utf8);
1328956cc18dSsnj
1329956cc18dSsnj    if (*resultp == 0) {
1330956cc18dSsnj	Atom *result;
1331956cc18dSsnj
1332956cc18dSsnj	if (!overrideTargets(w, screen->utf8_select_types, &result)) {
1333956cc18dSsnj	    result = (Atom *) XtMalloc((Cardinal) (5 * sizeof(Atom)));
1334956cc18dSsnj	    if (result == NULL) {
1335956cc18dSsnj		TRACE(("Couldn't allocate utf-8 selection targets\n"));
1336956cc18dSsnj	    } else {
1337956cc18dSsnj		int n = 0;
1338956cc18dSsnj
1339956cc18dSsnj		result[n++] = XA_UTF8_STRING(XtDisplay(w));
1340d522f475Smrg#ifdef X_HAVE_UTF8_STRING
1341956cc18dSsnj		if (screen->i18nSelections) {
1342956cc18dSsnj		    result[n++] = XA_TEXT(XtDisplay(w));
1343956cc18dSsnj		    result[n++] = XA_COMPOUND_TEXT(XtDisplay(w));
1344956cc18dSsnj		}
1345d522f475Smrg#endif
1346956cc18dSsnj		result[n++] = XA_STRING;
1347956cc18dSsnj		result[n] = None;
1348956cc18dSsnj	    }
1349d522f475Smrg	}
1350956cc18dSsnj
1351956cc18dSsnj	*resultp = result;
1352d522f475Smrg    }
1353956cc18dSsnj
1354956cc18dSsnj    return *resultp;
1355956cc18dSsnj}
1356d522f475Smrg#endif
1357d522f475Smrg
1358956cc18dSsnjstatic Atom *
1359956cc18dSsnjalloc8bitTargets(Widget w, TScreen * screen)
1360956cc18dSsnj{
1361956cc18dSsnj    Atom **resultp = &(screen->selection_targets_8bit);
1362956cc18dSsnj
1363956cc18dSsnj    if (*resultp == 0) {
1364956cc18dSsnj	Atom *result = 0;
1365956cc18dSsnj
1366956cc18dSsnj	if (!overrideTargets(w, screen->eightbit_select_types, &result)) {
1367956cc18dSsnj	    result = (Atom *) XtMalloc((Cardinal) (5 * sizeof(Atom)));
1368956cc18dSsnj	    if (result == NULL) {
1369956cc18dSsnj		TRACE(("Couldn't allocate 8bit selection targets\n"));
1370956cc18dSsnj	    } else {
1371956cc18dSsnj		int n = 0;
1372956cc18dSsnj
1373d522f475Smrg#ifdef X_HAVE_UTF8_STRING
1374956cc18dSsnj		result[n++] = XA_UTF8_STRING(XtDisplay(w));
1375956cc18dSsnj#endif
1376956cc18dSsnj		if (screen->i18nSelections) {
1377956cc18dSsnj		    result[n++] = XA_TEXT(XtDisplay(w));
1378956cc18dSsnj		    result[n++] = XA_COMPOUND_TEXT(XtDisplay(w));
1379956cc18dSsnj		}
1380956cc18dSsnj		result[n++] = XA_STRING;
1381956cc18dSsnj		result[n] = None;
1382956cc18dSsnj	    }
1383956cc18dSsnj	}
1384956cc18dSsnj
1385956cc18dSsnj	*resultp = result;
1386956cc18dSsnj    }
1387956cc18dSsnj
1388956cc18dSsnj    return *resultp;
1389956cc18dSsnj}
1390956cc18dSsnj
1391956cc18dSsnjstatic Atom *
1392956cc18dSsnj_SelectionTargets(Widget w)
1393956cc18dSsnj{
1394956cc18dSsnj    Atom *result;
1395956cc18dSsnj    TScreen *screen;
1396956cc18dSsnj    XtermWidget xw;
1397956cc18dSsnj
1398956cc18dSsnj    if ((xw = getXtermWidget(w)) == 0) {
1399956cc18dSsnj	result = NULL;
1400956cc18dSsnj    } else {
1401956cc18dSsnj	screen = TScreenOf(xw);
1402956cc18dSsnj
1403956cc18dSsnj#if OPT_WIDE_CHARS
1404956cc18dSsnj	if (screen->wide_chars) {
1405956cc18dSsnj	    result = allocUtf8Targets(w, screen);
1406956cc18dSsnj	} else
1407d522f475Smrg#endif
1408956cc18dSsnj	{
1409956cc18dSsnj	    /* not screen->wide_chars */
1410956cc18dSsnj	    result = alloc8bitTargets(w, screen);
1411d522f475Smrg	}
1412d522f475Smrg    }
1413956cc18dSsnj
1414956cc18dSsnj    return result;
1415d522f475Smrg}
1416d522f475Smrg
1417d522f475Smrg#define isSELECT(value) (!strcmp(value, "SELECT"))
1418d522f475Smrg
1419d522f475Smrgstatic void
1420d522f475SmrgUnmapSelections(XtermWidget xw)
1421d522f475Smrg{
1422956cc18dSsnj    TScreen *screen = TScreenOf(xw);
1423d522f475Smrg    Cardinal n;
1424d522f475Smrg
1425d522f475Smrg    if (screen->mappedSelect) {
1426d522f475Smrg	for (n = 0; screen->mappedSelect[n] != 0; ++n)
142720d2c4d2Smrg	    free((void *) screen->mappedSelect[n]);
1428d522f475Smrg	free(screen->mappedSelect);
1429d522f475Smrg	screen->mappedSelect = 0;
1430d522f475Smrg    }
1431d522f475Smrg}
1432d522f475Smrg
1433d522f475Smrg/*
1434d522f475Smrg * xterm generally uses the primary selection.  Some applications prefer
1435d522f475Smrg * (or are limited to) the clipboard.  Since the translations resource is
1436d522f475Smrg * complicated, users seldom change the way it affects selection.  But it
1437d522f475Smrg * is simple to remap the choice between primary and clipboard before the
1438d522f475Smrg * call to XmuInternStrings().
1439d522f475Smrg */
1440d522f475Smrgstatic String *
1441d522f475SmrgMapSelections(XtermWidget xw, String * params, Cardinal num_params)
1442d522f475Smrg{
1443d522f475Smrg    String *result = params;
1444d522f475Smrg
1445d522f475Smrg    if (num_params > 0) {
1446d522f475Smrg	Cardinal j;
1447d522f475Smrg	Boolean map = False;
1448d522f475Smrg
1449d522f475Smrg	for (j = 0; j < num_params; ++j) {
1450d522f475Smrg	    TRACE(("param[%d]:%s\n", j, params[j]));
1451d522f475Smrg	    if (isSELECT(params[j])) {
1452d522f475Smrg		map = True;
1453d522f475Smrg		break;
1454d522f475Smrg	    }
1455d522f475Smrg	}
1456d522f475Smrg	if (map) {
1457956cc18dSsnj	    TScreen *screen = TScreenOf(xw);
1458956cc18dSsnj	    const char *mapTo = (screen->selectToClipboard
1459d522f475Smrg				 ? "CLIPBOARD"
1460d522f475Smrg				 : "PRIMARY");
1461d522f475Smrg
1462d522f475Smrg	    UnmapSelections(xw);
1463d522f475Smrg	    if ((result = TypeMallocN(String, num_params + 1)) != 0) {
1464d522f475Smrg		result[num_params] = 0;
1465d522f475Smrg		for (j = 0; j < num_params; ++j) {
1466d522f475Smrg		    result[j] = x_strdup((isSELECT(params[j])
1467d522f475Smrg					  ? mapTo
1468d522f475Smrg					  : params[j]));
1469d522f475Smrg		    if (result[j] == 0) {
1470d522f475Smrg			UnmapSelections(xw);
1471d522f475Smrg			result = 0;
1472d522f475Smrg			break;
1473d522f475Smrg		    }
1474d522f475Smrg		}
1475956cc18dSsnj		screen->mappedSelect = result;
1476d522f475Smrg	    }
1477d522f475Smrg	}
1478d522f475Smrg    }
1479d522f475Smrg    return result;
1480d522f475Smrg}
1481d522f475Smrg
1482d522f475Smrg/*
1483d522f475Smrg * Lookup the cut-buffer number, which will be in the range 0-7.
1484d522f475Smrg * If it is not a cut-buffer, it is the primary selection (-1).
1485d522f475Smrg */
1486d522f475Smrgstatic int
148720d2c4d2SmrgCutBuffer(Atom code)
1488d522f475Smrg{
1489d522f475Smrg    int cutbuffer;
149020d2c4d2Smrg    switch ((unsigned) code) {
1491d522f475Smrg    case XA_CUT_BUFFER0:
1492d522f475Smrg	cutbuffer = 0;
1493d522f475Smrg	break;
1494d522f475Smrg    case XA_CUT_BUFFER1:
1495d522f475Smrg	cutbuffer = 1;
1496d522f475Smrg	break;
1497d522f475Smrg    case XA_CUT_BUFFER2:
1498d522f475Smrg	cutbuffer = 2;
1499d522f475Smrg	break;
1500d522f475Smrg    case XA_CUT_BUFFER3:
1501d522f475Smrg	cutbuffer = 3;
1502d522f475Smrg	break;
1503d522f475Smrg    case XA_CUT_BUFFER4:
1504d522f475Smrg	cutbuffer = 4;
1505d522f475Smrg	break;
1506d522f475Smrg    case XA_CUT_BUFFER5:
1507d522f475Smrg	cutbuffer = 5;
1508d522f475Smrg	break;
1509d522f475Smrg    case XA_CUT_BUFFER6:
1510d522f475Smrg	cutbuffer = 6;
1511d522f475Smrg	break;
1512d522f475Smrg    case XA_CUT_BUFFER7:
1513d522f475Smrg	cutbuffer = 7;
1514d522f475Smrg	break;
1515d522f475Smrg    default:
1516d522f475Smrg	cutbuffer = -1;
1517d522f475Smrg	break;
1518d522f475Smrg    }
1519d522f475Smrg    return cutbuffer;
1520d522f475Smrg}
1521d522f475Smrg
1522d522f475Smrg#if OPT_PASTE64
1523d522f475Smrgstatic void
1524d522f475SmrgFinishPaste64(XtermWidget xw)
1525d522f475Smrg{
1526956cc18dSsnj    TScreen *screen = TScreenOf(xw);
1527956cc18dSsnj
1528956cc18dSsnj    TRACE(("FinishPaste64(%d)\n", screen->base64_paste));
1529956cc18dSsnj    if (screen->base64_paste) {
1530956cc18dSsnj	screen->base64_paste = 0;
1531956cc18dSsnj	unparseputc1(xw, screen->base64_final);
1532d522f475Smrg	unparse_end(xw);
1533d522f475Smrg    }
1534d522f475Smrg}
1535d522f475Smrg#endif
1536d522f475Smrg
1537d522f475Smrg#if !OPT_PASTE64
1538d522f475Smrgstatic
1539d522f475Smrg#endif
1540d522f475Smrgvoid
1541d522f475SmrgxtermGetSelection(Widget w,
1542d522f475Smrg		  Time ev_time,
1543d522f475Smrg		  String * params,	/* selections in precedence order */
1544d522f475Smrg		  Cardinal num_params,
1545d522f475Smrg		  Atom * targets)
1546d522f475Smrg{
1547d522f475Smrg    Atom selection;
1548d522f475Smrg    int cutbuffer;
1549d522f475Smrg    Atom target;
1550d522f475Smrg
1551956cc18dSsnj    XtermWidget xw;
1552956cc18dSsnj
155320d2c4d2Smrg    if (num_params == 0)
155420d2c4d2Smrg	return;
1555956cc18dSsnj    if ((xw = getXtermWidget(w)) == 0)
1556d522f475Smrg	return;
1557d522f475Smrg
1558956cc18dSsnj    TRACE(("xtermGetSelection num_params %d\n", num_params));
1559956cc18dSsnj    params = MapSelections(xw, params, num_params);
1560d522f475Smrg
1561d522f475Smrg    XmuInternStrings(XtDisplay(w), params, (Cardinal) 1, &selection);
1562d522f475Smrg    cutbuffer = CutBuffer(selection);
1563d522f475Smrg
1564956cc18dSsnj    TRACE(("Cutbuffer: %d, target: %s\n", cutbuffer,
1565956cc18dSsnj	   (targets
1566956cc18dSsnj	    ? visibleSelectionTarget(XtDisplay(w), targets[0])
1567956cc18dSsnj	    : "None")));
1568d522f475Smrg
1569d522f475Smrg    if (cutbuffer >= 0) {
1570d522f475Smrg	int inbytes;
1571d522f475Smrg	unsigned long nbytes;
1572d522f475Smrg	int fmt8 = 8;
1573d522f475Smrg	Atom type = XA_STRING;
1574d522f475Smrg	char *line;
1575d522f475Smrg
1576d522f475Smrg	/* 'line' is freed in SelectionReceived */
1577d522f475Smrg	line = XFetchBuffer(XtDisplay(w), &inbytes, cutbuffer);
1578d522f475Smrg	nbytes = (unsigned long) inbytes;
1579d522f475Smrg
1580d522f475Smrg	if (nbytes > 0)
1581d522f475Smrg	    SelectionReceived(w, NULL, &selection, &type, (XtPointer) line,
1582d522f475Smrg			      &nbytes, &fmt8);
1583d522f475Smrg	else if (num_params > 1) {
1584d522f475Smrg	    xtermGetSelection(w, ev_time, params + 1, num_params - 1, NULL);
1585d522f475Smrg	}
1586d522f475Smrg#if OPT_PASTE64
1587d522f475Smrg	else {
1588956cc18dSsnj	    FinishPaste64(xw);
1589d522f475Smrg	}
1590d522f475Smrg#endif
1591d522f475Smrg	return;
1592d522f475Smrg    } else {
1593d522f475Smrg	struct _SelectionList *list;
1594d522f475Smrg
1595d522f475Smrg	if (targets == NULL || targets[0] == None) {
1596d522f475Smrg	    targets = _SelectionTargets(w);
1597d522f475Smrg	}
1598d522f475Smrg
1599d522f475Smrg	if (targets != 0) {
1600d522f475Smrg	    target = targets[0];
1601d522f475Smrg
1602d522f475Smrg	    if (targets[1] == None) {	/* last target in list */
1603d522f475Smrg		params++;
1604d522f475Smrg		num_params--;
1605d522f475Smrg		targets = _SelectionTargets(w);
1606d522f475Smrg	    } else {
1607d522f475Smrg		targets = &(targets[1]);
1608d522f475Smrg	    }
1609d522f475Smrg
1610d522f475Smrg	    if (num_params) {
1611d522f475Smrg		/* 'list' is freed in SelectionReceived */
1612d522f475Smrg		list = XtNew(struct _SelectionList);
1613d522f475Smrg		if (list != 0) {
1614d522f475Smrg		    list->params = params;
1615d522f475Smrg		    list->count = num_params;
1616d522f475Smrg		    list->targets = targets;
1617d522f475Smrg		    list->time = ev_time;
1618d522f475Smrg		}
1619d522f475Smrg	    } else {
1620d522f475Smrg		list = NULL;
1621d522f475Smrg	    }
1622d522f475Smrg
1623d522f475Smrg	    XtGetSelectionValue(w, selection,
1624d522f475Smrg				target,
1625d522f475Smrg				SelectionReceived,
1626d522f475Smrg				(XtPointer) list, ev_time);
1627d522f475Smrg	}
1628d522f475Smrg    }
1629d522f475Smrg}
1630d522f475Smrg
1631d522f475Smrg#if OPT_TRACE && OPT_WIDE_CHARS
1632d522f475Smrgstatic void
1633d522f475SmrgGettingSelection(Display * dpy, Atom type, Char * line, unsigned long len)
1634d522f475Smrg{
1635d522f475Smrg    Char *cp;
1636d522f475Smrg    char *name;
1637d522f475Smrg
1638d522f475Smrg    name = XGetAtomName(dpy, type);
1639d522f475Smrg
1640d522f475Smrg    TRACE(("Getting %s (%ld)\n", name, (long int) type));
1641d522f475Smrg    for (cp = line; cp < line + len; cp++) {
1642956cc18dSsnj	TRACE(("[%d:%lu]", (int) (cp + 1 - line), len));
1643d522f475Smrg	if (isprint(*cp)) {
1644d522f475Smrg	    TRACE(("%c\n", *cp));
1645d522f475Smrg	} else {
1646d522f475Smrg	    TRACE(("\\x%02x\n", *cp));
1647d522f475Smrg	}
1648d522f475Smrg    }
1649d522f475Smrg}
1650d522f475Smrg#else
1651d522f475Smrg#define GettingSelection(dpy,type,line,len)	/* nothing */
1652d522f475Smrg#endif
1653d522f475Smrg
1654d522f475Smrg#ifdef VMS
1655d522f475Smrg#  define tty_vwrite(pty,lag,l)		tt_write(lag,l)
1656d522f475Smrg#else /* !( VMS ) */
1657d522f475Smrg#  define tty_vwrite(pty,lag,l)		v_write(pty,lag,l)
1658d522f475Smrg#endif /* defined VMS */
1659d522f475Smrg
1660d522f475Smrg#if OPT_PASTE64
1661d522f475Smrg/* Return base64 code character given 6-bit number */
1662d522f475Smrgstatic const char base64_code[] = "\
1663d522f475SmrgABCDEFGHIJKLMNOPQRSTUVWXYZ\
1664d522f475Smrgabcdefghijklmnopqrstuvwxyz\
1665d522f475Smrg0123456789+/";
1666d522f475Smrgstatic void
1667d522f475Smrgbase64_flush(TScreen * screen)
1668d522f475Smrg{
1669d522f475Smrg    Char x;
1670d522f475Smrg    switch (screen->base64_count) {
1671d522f475Smrg    case 0:
1672d522f475Smrg	break;
1673d522f475Smrg    case 2:
16742eaa94a1Schristos	x = CharOf(base64_code[screen->base64_accu << 4]);
1675d522f475Smrg	tty_vwrite(screen->respond, &x, 1);
1676d522f475Smrg	break;
1677d522f475Smrg    case 4:
16782eaa94a1Schristos	x = CharOf(base64_code[screen->base64_accu << 2]);
1679d522f475Smrg	tty_vwrite(screen->respond, &x, 1);
1680d522f475Smrg	break;
1681d522f475Smrg    }
1682d522f475Smrg    if (screen->base64_pad & 3)
1683d522f475Smrg	tty_vwrite(screen->respond,
1684d522f475Smrg		   (Char *) "===",
1685d522f475Smrg		   (unsigned) (4 - (screen->base64_pad & 3)));
1686d522f475Smrg    screen->base64_count = 0;
1687d522f475Smrg    screen->base64_accu = 0;
1688d522f475Smrg    screen->base64_pad = 0;
1689d522f475Smrg}
1690d522f475Smrg#endif /* OPT_PASTE64 */
1691d522f475Smrg
1692d522f475Smrgstatic void
1693d522f475Smrg_qWriteSelectionData(TScreen * screen, Char * lag, unsigned length)
1694d522f475Smrg{
1695d522f475Smrg#if OPT_PASTE64
1696d522f475Smrg    if (screen->base64_paste) {
1697d522f475Smrg	/* Send data as base64 */
1698d522f475Smrg	Char *p = lag;
1699d522f475Smrg	Char buf[64];
1700d522f475Smrg	unsigned x = 0;
1701d522f475Smrg	while (length--) {
1702d522f475Smrg	    switch (screen->base64_count) {
1703d522f475Smrg	    case 0:
17042eaa94a1Schristos		buf[x++] = CharOf(base64_code[*p >> 2]);
17052eaa94a1Schristos		screen->base64_accu = (unsigned) (*p & 0x3);
1706d522f475Smrg		screen->base64_count = 2;
1707d522f475Smrg		++p;
1708d522f475Smrg		break;
1709d522f475Smrg	    case 2:
17102eaa94a1Schristos		buf[x++] = CharOf(base64_code[(screen->base64_accu << 4) +
17112eaa94a1Schristos					      (*p >> 4)]);
17122eaa94a1Schristos		screen->base64_accu = (unsigned) (*p & 0xF);
1713d522f475Smrg		screen->base64_count = 4;
1714d522f475Smrg		++p;
1715d522f475Smrg		break;
1716d522f475Smrg	    case 4:
17172eaa94a1Schristos		buf[x++] = CharOf(base64_code[(screen->base64_accu << 2) +
17182eaa94a1Schristos					      (*p >> 6)]);
17192eaa94a1Schristos		buf[x++] = CharOf(base64_code[*p & 0x3F]);
1720d522f475Smrg		screen->base64_accu = 0;
1721d522f475Smrg		screen->base64_count = 0;
1722d522f475Smrg		++p;
1723d522f475Smrg		break;
1724d522f475Smrg	    }
1725d522f475Smrg	    if (x >= 63) {
1726d522f475Smrg		/* Write 63 or 64 characters */
1727d522f475Smrg		screen->base64_pad += x;
1728d522f475Smrg		tty_vwrite(screen->respond, buf, x);
1729d522f475Smrg		x = 0;
1730d522f475Smrg	    }
1731d522f475Smrg	}
1732d522f475Smrg	if (x != 0) {
1733d522f475Smrg	    screen->base64_pad += x;
1734d522f475Smrg	    tty_vwrite(screen->respond, buf, x);
1735d522f475Smrg	}
1736d522f475Smrg    } else
1737d522f475Smrg#endif /* OPT_PASTE64 */
1738d522f475Smrg#if OPT_READLINE
1739d522f475Smrg    if (SCREEN_FLAG(screen, paste_quotes)) {
1740d522f475Smrg	while (length--) {
1741d522f475Smrg	    tty_vwrite(screen->respond, (Char *) "\026", 1);	/* Control-V */
1742d522f475Smrg	    tty_vwrite(screen->respond, lag++, 1);
1743d522f475Smrg	}
1744d522f475Smrg    } else
1745d522f475Smrg#endif
1746d522f475Smrg	tty_vwrite(screen->respond, lag, length);
1747d522f475Smrg}
1748d522f475Smrg
1749d522f475Smrgstatic void
175020d2c4d2Smrg_WriteSelectionData(TScreen * screen, Char * line, size_t length)
1751d522f475Smrg{
1752d522f475Smrg    /* Write data to pty a line at a time. */
1753d522f475Smrg    /* Doing this one line at a time may no longer be necessary
1754d522f475Smrg       because v_write has been re-written. */
1755d522f475Smrg
1756d522f475Smrg    Char *lag, *end;
1757d522f475Smrg
1758d522f475Smrg    /* in the VMS version, if tt_pasting isn't set to True then qio
1759d522f475Smrg       reads aren't blocked and an infinite loop is entered, where the
1760d522f475Smrg       pasted text shows up as new input, goes in again, shows up
1761d522f475Smrg       again, ad nauseum. */
1762d522f475Smrg#ifdef VMS
1763d522f475Smrg    tt_pasting = True;
1764d522f475Smrg#endif
1765d522f475Smrg
1766d522f475Smrg    end = &line[length];
1767d522f475Smrg    lag = line;
1768d522f475Smrg
1769d522f475Smrg#if OPT_PASTE64
1770d522f475Smrg    if (screen->base64_paste) {
1771d522f475Smrg	_qWriteSelectionData(screen, lag, (unsigned) (end - lag));
1772d522f475Smrg	base64_flush(screen);
1773d522f475Smrg    } else
1774d522f475Smrg#endif
1775d522f475Smrg    {
1776d522f475Smrg	if (!SCREEN_FLAG(screen, paste_literal_nl)) {
1777d522f475Smrg	    Char *cp;
1778d522f475Smrg	    for (cp = line; cp != end; cp++) {
1779d522f475Smrg		if (*cp == '\n') {
1780d522f475Smrg		    *cp = '\r';
1781d522f475Smrg		    _qWriteSelectionData(screen, lag, (unsigned) (cp - lag + 1));
1782d522f475Smrg		    lag = cp + 1;
1783d522f475Smrg		}
1784d522f475Smrg	    }
1785d522f475Smrg	}
1786d522f475Smrg
1787d522f475Smrg	if (lag != end) {
1788d522f475Smrg	    _qWriteSelectionData(screen, lag, (unsigned) (end - lag));
1789d522f475Smrg	}
1790d522f475Smrg    }
1791d522f475Smrg#ifdef VMS
1792d522f475Smrg    tt_pasting = False;
1793d522f475Smrg    tt_start_read();		/* reenable reads or a character may be lost */
1794d522f475Smrg#endif
1795d522f475Smrg}
1796d522f475Smrg
1797d522f475Smrg#if OPT_READLINE
1798d522f475Smrgstatic void
1799d522f475Smrg_WriteKey(TScreen * screen, Char * in)
1800d522f475Smrg{
1801d522f475Smrg    Char line[16];
1802d522f475Smrg    unsigned count = 0;
180320d2c4d2Smrg    size_t length = strlen((char *) in);
1804d522f475Smrg
1805d522f475Smrg    if (screen->control_eight_bits) {
1806d522f475Smrg	line[count++] = ANSI_CSI;
1807d522f475Smrg    } else {
1808d522f475Smrg	line[count++] = ANSI_ESC;
1809d522f475Smrg	line[count++] = '[';
1810d522f475Smrg    }
1811d522f475Smrg    while (length--)
1812d522f475Smrg	line[count++] = *in++;
1813d522f475Smrg    line[count++] = '~';
1814d522f475Smrg    tty_vwrite(screen->respond, line, count);
1815d522f475Smrg}
1816d522f475Smrg#endif /* OPT_READLINE */
1817d522f475Smrg
1818d522f475Smrg/* SelectionReceived: stuff received selection text into pty */
1819d522f475Smrg
1820d522f475Smrg/* ARGSUSED */
1821d522f475Smrgstatic void
1822d522f475SmrgSelectionReceived(Widget w,
1823d522f475Smrg		  XtPointer client_data,
1824d522f475Smrg		  Atom * selection GCC_UNUSED,
1825d522f475Smrg		  Atom * type,
1826d522f475Smrg		  XtPointer value,
1827d522f475Smrg		  unsigned long *length,
1828d522f475Smrg		  int *format)
1829d522f475Smrg{
1830d522f475Smrg    char **text_list = NULL;
1831d522f475Smrg    int text_list_count;
1832d522f475Smrg    XTextProperty text_prop;
1833d522f475Smrg    TScreen *screen;
1834d522f475Smrg    Display *dpy;
1835d522f475Smrg#if OPT_TRACE && OPT_WIDE_CHARS
1836d522f475Smrg    Char *line = (Char *) value;
1837d522f475Smrg#endif
1838d522f475Smrg
1839956cc18dSsnj    XtermWidget xw;
1840956cc18dSsnj
1841956cc18dSsnj    if ((xw = getXtermWidget(w)) == 0)
1842d522f475Smrg	return;
1843956cc18dSsnj
1844956cc18dSsnj    screen = TScreenOf(xw);
1845d522f475Smrg    dpy = XtDisplay(w);
1846d522f475Smrg
1847d522f475Smrg    if (*type == 0		/*XT_CONVERT_FAIL */
1848d522f475Smrg	|| *length == 0
1849d522f475Smrg	|| value == NULL)
1850d522f475Smrg	goto fail;
1851d522f475Smrg
1852d522f475Smrg    text_prop.value = (unsigned char *) value;
1853d522f475Smrg    text_prop.encoding = *type;
1854d522f475Smrg    text_prop.format = *format;
1855d522f475Smrg    text_prop.nitems = *length;
1856d522f475Smrg
1857956cc18dSsnj    TRACE(("SelectionReceived %s format %d, nitems %ld\n",
1858956cc18dSsnj	   visibleSelectionTarget(dpy, text_prop.encoding),
1859956cc18dSsnj	   text_prop.format,
1860956cc18dSsnj	   text_prop.nitems));
1861956cc18dSsnj
1862d522f475Smrg#if OPT_WIDE_CHARS
1863d522f475Smrg    if (screen->wide_chars) {
1864d522f475Smrg	if (*type == XA_UTF8_STRING(dpy) ||
1865d522f475Smrg	    *type == XA_STRING ||
1866d522f475Smrg	    *type == XA_COMPOUND_TEXT(dpy)) {
1867d522f475Smrg	    GettingSelection(dpy, *type, line, *length);
1868d522f475Smrg	    if (Xutf8TextPropertyToTextList(dpy, &text_prop,
1869d522f475Smrg					    &text_list,
1870d522f475Smrg					    &text_list_count) < 0) {
1871d522f475Smrg		TRACE(("Conversion failed\n"));
1872d522f475Smrg		text_list = NULL;
1873d522f475Smrg	    }
1874d522f475Smrg	}
1875d522f475Smrg    } else
1876d522f475Smrg#endif /* OPT_WIDE_CHARS */
1877d522f475Smrg    {
1878d522f475Smrg	/* Convert the selection to locale's multibyte encoding. */
1879d522f475Smrg
1880d522f475Smrg	if (*type == XA_UTF8_STRING(dpy) ||
1881d522f475Smrg	    *type == XA_STRING ||
1882d522f475Smrg	    *type == XA_COMPOUND_TEXT(dpy)) {
1883d522f475Smrg	    Status rc;
1884d522f475Smrg
1885d522f475Smrg	    GettingSelection(dpy, *type, line, *length);
1886d522f475Smrg
1887d522f475Smrg#if OPT_WIDE_CHARS
1888d522f475Smrg	    if (*type == XA_UTF8_STRING(dpy) &&
1889d522f475Smrg		!(screen->wide_chars || screen->c1_printable)) {
189020d2c4d2Smrg		rc = xtermUtf8ToTextList(xw, &text_prop,
189120d2c4d2Smrg					 &text_list, &text_list_count);
1892d522f475Smrg	    } else
1893d522f475Smrg#endif
1894d522f475Smrg	    if (*type == XA_STRING && screen->brokenSelections) {
1895d522f475Smrg		rc = XTextPropertyToStringList(&text_prop,
1896d522f475Smrg					       &text_list, &text_list_count);
1897d522f475Smrg	    } else {
1898d522f475Smrg		rc = XmbTextPropertyToTextList(dpy, &text_prop,
1899d522f475Smrg					       &text_list,
1900d522f475Smrg					       &text_list_count);
1901d522f475Smrg	    }
1902d522f475Smrg	    if (rc < 0) {
1903d522f475Smrg		TRACE(("Conversion failed\n"));
1904d522f475Smrg		text_list = NULL;
1905d522f475Smrg	    }
1906d522f475Smrg	}
1907d522f475Smrg    }
1908d522f475Smrg
1909d522f475Smrg    if (text_list != NULL && text_list_count != 0) {
1910d522f475Smrg	int i;
1911d522f475Smrg
1912d522f475Smrg#if OPT_PASTE64
1913d522f475Smrg	if (screen->base64_paste) {
1914d522f475Smrg	    ;
1915d522f475Smrg	} else
1916d522f475Smrg#endif
1917d522f475Smrg#if OPT_READLINE
1918d522f475Smrg	if (SCREEN_FLAG(screen, paste_brackets)) {
1919d522f475Smrg	    _WriteKey(screen, (Char *) "200");
1920d522f475Smrg	}
1921d522f475Smrg#endif
1922d522f475Smrg	for (i = 0; i < text_list_count; i++) {
192320d2c4d2Smrg	    size_t len = strlen(text_list[i]);
1924d522f475Smrg	    _WriteSelectionData(screen, (Char *) text_list[i], len);
1925d522f475Smrg	}
1926d522f475Smrg#if OPT_PASTE64
1927d522f475Smrg	if (screen->base64_paste) {
1928956cc18dSsnj	    FinishPaste64(xw);
1929d522f475Smrg	} else
1930d522f475Smrg#endif
1931d522f475Smrg#if OPT_READLINE
1932d522f475Smrg	if (SCREEN_FLAG(screen, paste_brackets)) {
1933d522f475Smrg	    _WriteKey(screen, (Char *) "201");
1934d522f475Smrg	}
1935d522f475Smrg#endif
1936d522f475Smrg	XFreeStringList(text_list);
1937d522f475Smrg    } else
1938d522f475Smrg	goto fail;
1939d522f475Smrg
1940d522f475Smrg    XtFree((char *) client_data);
1941d522f475Smrg    XtFree((char *) value);
1942d522f475Smrg
1943d522f475Smrg    return;
1944d522f475Smrg
1945d522f475Smrg  fail:
1946d522f475Smrg    if (client_data != 0) {
1947d522f475Smrg	struct _SelectionList *list = (struct _SelectionList *) client_data;
1948956cc18dSsnj
1949956cc18dSsnj	TRACE(("SelectionReceived ->xtermGetSelection\n"));
1950d522f475Smrg	xtermGetSelection(w, list->time,
1951d522f475Smrg			  list->params, list->count, list->targets);
1952d522f475Smrg	XtFree((char *) client_data);
1953d522f475Smrg#if OPT_PASTE64
1954d522f475Smrg    } else {
1955956cc18dSsnj	FinishPaste64(xw);
1956d522f475Smrg#endif
1957d522f475Smrg    }
1958d522f475Smrg    return;
1959d522f475Smrg}
1960d522f475Smrg
1961d522f475Smrgvoid
1962d522f475SmrgHandleInsertSelection(Widget w,
1963d522f475Smrg		      XEvent * event,	/* assumed to be XButtonEvent* */
1964d522f475Smrg		      String * params,	/* selections in precedence order */
1965d522f475Smrg		      Cardinal *num_params)
1966d522f475Smrg{
1967956cc18dSsnj    XtermWidget xw;
1968d522f475Smrg
1969956cc18dSsnj    if ((xw = getXtermWidget(w)) != 0) {
1970d522f475Smrg	if (!SendMousePosition(xw, event)) {
1971d522f475Smrg#if OPT_READLINE
1972d522f475Smrg	    int ldelta;
1973956cc18dSsnj	    TScreen *screen = TScreenOf(xw);
1974d522f475Smrg	    if ((event->type == ButtonPress || event->type == ButtonRelease)
1975d522f475Smrg	    /* Disable on Shift-mouse, including the application-mouse modes */
1976d522f475Smrg		&& !(KeyModifiers & ShiftMask)
1977d522f475Smrg		&& (screen->send_mouse_pos == MOUSE_OFF)
1978d522f475Smrg		&& SCREEN_FLAG(screen, paste_moves)
1979d522f475Smrg		&& rowOnCurrentLine(screen, eventRow(screen, event), &ldelta))
1980d522f475Smrg		ReadLineMovePoint(screen, eventColBetween(screen, event), ldelta);
1981d522f475Smrg#endif /* OPT_READLINE */
1982d522f475Smrg
1983d522f475Smrg	    xtermGetSelection(w, event->xbutton.time, params, *num_params, NULL);
1984d522f475Smrg	}
1985d522f475Smrg    }
1986d522f475Smrg}
1987d522f475Smrg
1988d522f475Smrgstatic SelectUnit
1989956cc18dSsnjEvalSelectUnit(XtermWidget xw,
1990d522f475Smrg	       Time buttonDownTime,
1991d522f475Smrg	       SelectUnit defaultUnit,
1992d522f475Smrg	       unsigned int button)
1993d522f475Smrg{
1994956cc18dSsnj    TScreen *screen = TScreenOf(xw);
1995d522f475Smrg    SelectUnit result;
1996d522f475Smrg    int delta;
1997d522f475Smrg
1998d522f475Smrg    if (button != screen->lastButton) {
199920d2c4d2Smrg	delta = screen->multiClickTime + 1;
2000d522f475Smrg    } else if (screen->lastButtonUpTime == (Time) 0) {
2001d522f475Smrg	/* first time and once in a blue moon */
2002d522f475Smrg	delta = screen->multiClickTime + 1;
2003d522f475Smrg    } else if (buttonDownTime > screen->lastButtonUpTime) {
2004d522f475Smrg	/* most of the time */
20052eaa94a1Schristos	delta = (int) (buttonDownTime - screen->lastButtonUpTime);
2006d522f475Smrg    } else {
2007d522f475Smrg	/* time has rolled over since lastButtonUpTime */
20082eaa94a1Schristos	delta = (int) ((((Time) ~ 0) - screen->lastButtonUpTime) + buttonDownTime);
2009d522f475Smrg    }
2010d522f475Smrg
2011d522f475Smrg    if (delta > screen->multiClickTime) {
2012d522f475Smrg	screen->numberOfClicks = 1;
2013d522f475Smrg	result = defaultUnit;
2014d522f475Smrg    } else {
2015d522f475Smrg	result = screen->selectMap[screen->numberOfClicks % screen->maxClicks];
2016d522f475Smrg	screen->numberOfClicks += 1;
2017d522f475Smrg    }
2018d522f475Smrg    TRACE(("EvalSelectUnit(%d) = %d\n", screen->numberOfClicks, result));
2019d522f475Smrg    return result;
2020d522f475Smrg}
2021d522f475Smrg
2022d522f475Smrgstatic void
2023d522f475Smrgdo_select_start(XtermWidget xw,
2024d522f475Smrg		XEvent * event,	/* must be XButtonEvent* */
2025d522f475Smrg		CELL * cell)
2026d522f475Smrg{
2027956cc18dSsnj    TScreen *screen = TScreenOf(xw);
2028d522f475Smrg
2029d522f475Smrg    if (SendMousePosition(xw, event))
2030d522f475Smrg	return;
2031956cc18dSsnj    screen->selectUnit = EvalSelectUnit(xw,
2032d522f475Smrg					event->xbutton.time,
2033d522f475Smrg					Select_CHAR,
2034d522f475Smrg					event->xbutton.button);
2035d522f475Smrg    screen->replyToEmacs = False;
2036d522f475Smrg
2037d522f475Smrg#if OPT_READLINE
2038d522f475Smrg    lastButtonDownTime = event->xbutton.time;
2039d522f475Smrg#endif
2040d522f475Smrg
2041d522f475Smrg    StartSelect(xw, cell);
2042d522f475Smrg}
2043d522f475Smrg
2044d522f475Smrg/* ARGSUSED */
2045d522f475Smrgvoid
2046d522f475SmrgHandleSelectStart(Widget w,
2047d522f475Smrg		  XEvent * event,	/* must be XButtonEvent* */
2048d522f475Smrg		  String * params GCC_UNUSED,
2049d522f475Smrg		  Cardinal *num_params GCC_UNUSED)
2050d522f475Smrg{
2051956cc18dSsnj    XtermWidget xw;
2052956cc18dSsnj
2053956cc18dSsnj    if ((xw = getXtermWidget(w)) != 0) {
2054956cc18dSsnj	TScreen *screen = TScreenOf(xw);
2055d522f475Smrg	CELL cell;
2056d522f475Smrg
2057d522f475Smrg	screen->firstValidRow = 0;
2058d522f475Smrg	screen->lastValidRow = screen->max_row;
2059d522f475Smrg	PointToCELL(screen, event->xbutton.y, event->xbutton.x, &cell);
2060d522f475Smrg
2061d522f475Smrg#if OPT_READLINE
2062d522f475Smrg	ExtendingSelection = 0;
2063d522f475Smrg#endif
2064d522f475Smrg
2065d522f475Smrg	do_select_start(xw, event, &cell);
2066d522f475Smrg    }
2067d522f475Smrg}
2068d522f475Smrg
2069d522f475Smrg/* ARGSUSED */
2070d522f475Smrgvoid
2071d522f475SmrgHandleKeyboardSelectStart(Widget w,
2072d522f475Smrg			  XEvent * event,	/* must be XButtonEvent* */
2073d522f475Smrg			  String * params GCC_UNUSED,
2074d522f475Smrg			  Cardinal *num_params GCC_UNUSED)
2075d522f475Smrg{
2076956cc18dSsnj    XtermWidget xw;
2077956cc18dSsnj
2078956cc18dSsnj    if ((xw = getXtermWidget(w)) != 0) {
2079956cc18dSsnj	TScreen *screen = TScreenOf(xw);
2080d522f475Smrg	do_select_start(xw, event, &screen->cursorp);
2081d522f475Smrg    }
2082d522f475Smrg}
2083d522f475Smrg
2084d522f475Smrgstatic void
2085d522f475SmrgTrackDown(XtermWidget xw, XButtonEvent * event)
2086d522f475Smrg{
2087956cc18dSsnj    TScreen *screen = TScreenOf(xw);
2088d522f475Smrg    CELL cell;
2089d522f475Smrg
2090956cc18dSsnj    screen->selectUnit = EvalSelectUnit(xw,
2091d522f475Smrg					event->time,
2092d522f475Smrg					Select_CHAR,
2093d522f475Smrg					event->button);
2094d522f475Smrg    if (screen->numberOfClicks > 1) {
2095d522f475Smrg	PointToCELL(screen, event->y, event->x, &cell);
2096d522f475Smrg	screen->replyToEmacs = True;
2097d522f475Smrg	StartSelect(xw, &cell);
2098d522f475Smrg    } else {
2099d522f475Smrg	screen->waitingForTrackInfo = True;
2100d522f475Smrg	EditorButton(xw, (XButtonEvent *) event);
2101d522f475Smrg    }
2102d522f475Smrg}
2103d522f475Smrg
2104d522f475Smrg#define boundsCheck(x)	if (x < 0) \
2105d522f475Smrg			    x = 0; \
2106d522f475Smrg			else if (x >= screen->max_row) \
2107d522f475Smrg			    x = screen->max_row
2108d522f475Smrg
2109d522f475Smrgvoid
2110d522f475SmrgTrackMouse(XtermWidget xw,
2111d522f475Smrg	   int func,
2112d522f475Smrg	   CELL * start,
2113d522f475Smrg	   int firstrow,
2114d522f475Smrg	   int lastrow)
2115d522f475Smrg{
2116956cc18dSsnj    TScreen *screen = TScreenOf(xw);
2117d522f475Smrg
2118d522f475Smrg    if (screen->waitingForTrackInfo) {	/* if Timed, ignore */
2119d522f475Smrg	screen->waitingForTrackInfo = False;
2120d522f475Smrg
2121d522f475Smrg	if (func != 0) {
2122d522f475Smrg	    CELL first = *start;
2123d522f475Smrg
2124d522f475Smrg	    boundsCheck(first.row);
2125d522f475Smrg	    boundsCheck(firstrow);
2126d522f475Smrg	    boundsCheck(lastrow);
2127d522f475Smrg	    screen->firstValidRow = firstrow;
2128d522f475Smrg	    screen->lastValidRow = lastrow;
2129d522f475Smrg	    screen->replyToEmacs = True;
2130d522f475Smrg	    StartSelect(xw, &first);
2131d522f475Smrg	}
2132d522f475Smrg    }
2133d522f475Smrg}
2134d522f475Smrg
2135d522f475Smrgstatic void
2136d522f475SmrgStartSelect(XtermWidget xw, const CELL * cell)
2137d522f475Smrg{
2138956cc18dSsnj    TScreen *screen = TScreenOf(xw);
2139d522f475Smrg
2140d522f475Smrg    TRACE(("StartSelect row=%d, col=%d\n", cell->row, cell->col));
2141d522f475Smrg    if (screen->cursor_state)
2142d522f475Smrg	HideCursor();
2143d522f475Smrg    if (screen->numberOfClicks == 1) {
2144d522f475Smrg	/* set start of selection */
2145d522f475Smrg	screen->rawPos = *cell;
2146d522f475Smrg    }
2147d522f475Smrg    /* else use old values in rawPos */
2148d522f475Smrg    screen->saveStartR = screen->startExt = screen->rawPos;
2149d522f475Smrg    screen->saveEndR = screen->endExt = screen->rawPos;
2150d522f475Smrg    if (Coordinate(screen, cell) < Coordinate(screen, &(screen->rawPos))) {
2151d522f475Smrg	screen->eventMode = LEFTEXTENSION;
2152d522f475Smrg	screen->startExt = *cell;
2153d522f475Smrg    } else {
2154d522f475Smrg	screen->eventMode = RIGHTEXTENSION;
2155d522f475Smrg	screen->endExt = *cell;
2156d522f475Smrg    }
2157d522f475Smrg    ComputeSelect(xw, &(screen->startExt), &(screen->endExt), False);
2158d522f475Smrg}
2159d522f475Smrg
2160d522f475Smrgstatic void
2161d522f475SmrgEndExtend(XtermWidget xw,
2162d522f475Smrg	  XEvent * event,	/* must be XButtonEvent */
2163d522f475Smrg	  String * params,	/* selections */
2164d522f475Smrg	  Cardinal num_params,
2165d522f475Smrg	  Bool use_cursor_loc)
2166d522f475Smrg{
2167d522f475Smrg    CELL cell;
2168d522f475Smrg    unsigned count;
2169956cc18dSsnj    TScreen *screen = TScreenOf(xw);
2170d522f475Smrg    Char line[9];
2171d522f475Smrg
2172d522f475Smrg    if (use_cursor_loc) {
2173d522f475Smrg	cell = screen->cursorp;
2174d522f475Smrg    } else {
2175d522f475Smrg	PointToCELL(screen, event->xbutton.y, event->xbutton.x, &cell);
2176d522f475Smrg    }
2177d522f475Smrg    ExtendExtend(xw, &cell);
2178d522f475Smrg    screen->lastButtonUpTime = event->xbutton.time;
2179d522f475Smrg    screen->lastButton = event->xbutton.button;
2180d522f475Smrg    if (!isSameCELL(&(screen->startSel), &(screen->endSel))) {
2181d522f475Smrg	if (screen->replyToEmacs) {
2182d522f475Smrg	    count = 0;
2183d522f475Smrg	    if (screen->control_eight_bits) {
2184d522f475Smrg		line[count++] = ANSI_CSI;
2185d522f475Smrg	    } else {
2186d522f475Smrg		line[count++] = ANSI_ESC;
2187d522f475Smrg		line[count++] = '[';
2188d522f475Smrg	    }
2189d522f475Smrg	    if (isSameCELL(&(screen->rawPos), &(screen->startSel))
2190d522f475Smrg		&& isSameCELL(&cell, &(screen->endSel))) {
2191d522f475Smrg		/* Use short-form emacs select */
2192d522f475Smrg		line[count++] = 't';
21932eaa94a1Schristos		line[count++] = CharOf(' ' + screen->endSel.col + 1);
21942eaa94a1Schristos		line[count++] = CharOf(' ' + screen->endSel.row + 1);
2195d522f475Smrg	    } else {
2196d522f475Smrg		/* long-form, specify everything */
2197d522f475Smrg		line[count++] = 'T';
21982eaa94a1Schristos		line[count++] = CharOf(' ' + screen->startSel.col + 1);
21992eaa94a1Schristos		line[count++] = CharOf(' ' + screen->startSel.row + 1);
22002eaa94a1Schristos		line[count++] = CharOf(' ' + screen->endSel.col + 1);
22012eaa94a1Schristos		line[count++] = CharOf(' ' + screen->endSel.row + 1);
22022eaa94a1Schristos		line[count++] = CharOf(' ' + cell.col + 1);
22032eaa94a1Schristos		line[count++] = CharOf(' ' + cell.row + 1);
2204d522f475Smrg	    }
2205d522f475Smrg	    v_write(screen->respond, line, count);
2206d522f475Smrg	    TrackText(xw, &zeroCELL, &zeroCELL);
2207d522f475Smrg	}
2208d522f475Smrg    }
2209d522f475Smrg    SelectSet(xw, event, params, num_params);
2210d522f475Smrg    screen->eventMode = NORMAL;
2211d522f475Smrg}
2212d522f475Smrg
2213d522f475Smrgvoid
2214d522f475SmrgHandleSelectSet(Widget w,
2215d522f475Smrg		XEvent * event,
2216d522f475Smrg		String * params,
2217d522f475Smrg		Cardinal *num_params)
2218d522f475Smrg{
2219956cc18dSsnj    XtermWidget xw;
2220956cc18dSsnj
2221956cc18dSsnj    if ((xw = getXtermWidget(w)) != 0) {
2222956cc18dSsnj	SelectSet(xw, event, params, *num_params);
2223d522f475Smrg    }
2224d522f475Smrg}
2225d522f475Smrg
2226d522f475Smrg/* ARGSUSED */
2227d522f475Smrgstatic void
2228d522f475SmrgSelectSet(XtermWidget xw,
2229d522f475Smrg	  XEvent * event GCC_UNUSED,
2230d522f475Smrg	  String * params,
2231d522f475Smrg	  Cardinal num_params)
2232d522f475Smrg{
2233956cc18dSsnj    TScreen *screen = TScreenOf(xw);
2234d522f475Smrg
2235d522f475Smrg    TRACE(("SelectSet\n"));
2236d522f475Smrg    /* Only do select stuff if non-null select */
2237d522f475Smrg    if (!isSameCELL(&(screen->startSel), &(screen->endSel))) {
2238d522f475Smrg	SaltTextAway(xw, &(screen->startSel), &(screen->endSel), params, num_params);
2239d522f475Smrg    } else {
2240d522f475Smrg	DisownSelection(xw);
2241d522f475Smrg    }
2242d522f475Smrg}
2243d522f475Smrg
2244d522f475Smrg#define Abs(x)		((x) < 0 ? -(x) : (x))
2245d522f475Smrg
2246d522f475Smrg/* ARGSUSED */
2247d522f475Smrgstatic void
2248d522f475Smrgdo_start_extend(XtermWidget xw,
2249d522f475Smrg		XEvent * event,	/* must be XButtonEvent* */
2250d522f475Smrg		String * params GCC_UNUSED,
2251d522f475Smrg		Cardinal *num_params GCC_UNUSED,
2252d522f475Smrg		Bool use_cursor_loc)
2253d522f475Smrg{
2254956cc18dSsnj    TScreen *screen = TScreenOf(xw);
2255d522f475Smrg    int coord;
2256d522f475Smrg    CELL cell;
2257d522f475Smrg
2258d522f475Smrg    if (SendMousePosition(xw, event))
2259d522f475Smrg	return;
2260d522f475Smrg
2261d522f475Smrg    screen->firstValidRow = 0;
2262d522f475Smrg    screen->lastValidRow = screen->max_row;
2263d522f475Smrg#if OPT_READLINE
2264d522f475Smrg    if ((KeyModifiers & ShiftMask)
2265d522f475Smrg	|| event->xbutton.button != Button3
2266d522f475Smrg	|| !(SCREEN_FLAG(screen, dclick3_deletes)))
2267d522f475Smrg#endif
2268956cc18dSsnj	screen->selectUnit = EvalSelectUnit(xw,
2269d522f475Smrg					    event->xbutton.time,
2270d522f475Smrg					    screen->selectUnit,
2271d522f475Smrg					    event->xbutton.button);
2272d522f475Smrg    screen->replyToEmacs = False;
2273d522f475Smrg
2274d522f475Smrg#if OPT_READLINE
2275d522f475Smrg    CheckSecondPress3(screen, event);
2276d522f475Smrg#endif
2277d522f475Smrg
2278d522f475Smrg    if (screen->numberOfClicks == 1
2279d522f475Smrg	|| (SCREEN_FLAG(screen, dclick3_deletes)	/* Dclick special */
2280d522f475Smrg	    &&!(KeyModifiers & ShiftMask))) {
2281d522f475Smrg	/* Save existing selection so we can reestablish it if the guy
2282d522f475Smrg	   extends past the other end of the selection */
2283d522f475Smrg	screen->saveStartR = screen->startExt = screen->startRaw;
2284d522f475Smrg	screen->saveEndR = screen->endExt = screen->endRaw;
2285d522f475Smrg    } else {
2286d522f475Smrg	/* He just needed the selection mode changed, use old values. */
2287d522f475Smrg	screen->startExt = screen->startRaw = screen->saveStartR;
2288d522f475Smrg	screen->endExt = screen->endRaw = screen->saveEndR;
2289d522f475Smrg    }
2290d522f475Smrg    if (use_cursor_loc) {
2291d522f475Smrg	cell = screen->cursorp;
2292d522f475Smrg    } else {
2293d522f475Smrg	PointToCELL(screen, event->xbutton.y, event->xbutton.x, &cell);
2294d522f475Smrg    }
2295d522f475Smrg    coord = Coordinate(screen, &cell);
2296d522f475Smrg
2297d522f475Smrg    if (Abs(coord - Coordinate(screen, &(screen->startSel)))
2298d522f475Smrg	< Abs(coord - Coordinate(screen, &(screen->endSel)))
2299d522f475Smrg	|| coord < Coordinate(screen, &(screen->startSel))) {
2300d522f475Smrg	/* point is close to left side of selection */
2301d522f475Smrg	screen->eventMode = LEFTEXTENSION;
2302d522f475Smrg	screen->startExt = cell;
2303d522f475Smrg    } else {
2304d522f475Smrg	/* point is close to left side of selection */
2305d522f475Smrg	screen->eventMode = RIGHTEXTENSION;
2306d522f475Smrg	screen->endExt = cell;
2307d522f475Smrg    }
2308d522f475Smrg    ComputeSelect(xw, &(screen->startExt), &(screen->endExt), True);
2309d522f475Smrg
2310d522f475Smrg#if OPT_READLINE
2311d522f475Smrg    if (!isSameCELL(&(screen->startSel), &(screen->endSel)))
2312d522f475Smrg	ExtendingSelection = 1;
2313d522f475Smrg#endif
2314d522f475Smrg}
2315d522f475Smrg
2316d522f475Smrgstatic void
2317d522f475SmrgExtendExtend(XtermWidget xw, const CELL * cell)
2318d522f475Smrg{
2319956cc18dSsnj    TScreen *screen = TScreenOf(xw);
2320d522f475Smrg    int coord = Coordinate(screen, cell);
2321d522f475Smrg
2322d522f475Smrg    TRACE(("ExtendExtend row=%d, col=%d\n", cell->row, cell->col));
2323d522f475Smrg    if (screen->eventMode == LEFTEXTENSION
2324d522f475Smrg	&& ((coord + (screen->selectUnit != Select_CHAR))
2325d522f475Smrg	    > Coordinate(screen, &(screen->endSel)))) {
2326d522f475Smrg	/* Whoops, he's changed his mind.  Do RIGHTEXTENSION */
2327d522f475Smrg	screen->eventMode = RIGHTEXTENSION;
2328d522f475Smrg	screen->startExt = screen->saveStartR;
2329d522f475Smrg    } else if (screen->eventMode == RIGHTEXTENSION
2330d522f475Smrg	       && coord < Coordinate(screen, &(screen->startSel))) {
2331d522f475Smrg	/* Whoops, he's changed his mind.  Do LEFTEXTENSION */
2332d522f475Smrg	screen->eventMode = LEFTEXTENSION;
2333d522f475Smrg	screen->endExt = screen->saveEndR;
2334d522f475Smrg    }
2335d522f475Smrg    if (screen->eventMode == LEFTEXTENSION) {
2336d522f475Smrg	screen->startExt = *cell;
2337d522f475Smrg    } else {
2338d522f475Smrg	screen->endExt = *cell;
2339d522f475Smrg    }
2340d522f475Smrg    ComputeSelect(xw, &(screen->startExt), &(screen->endExt), False);
2341d522f475Smrg
2342d522f475Smrg#if OPT_READLINE
2343d522f475Smrg    if (!isSameCELL(&(screen->startSel), &(screen->endSel)))
2344d522f475Smrg	ExtendingSelection = 1;
2345d522f475Smrg#endif
2346d522f475Smrg}
2347d522f475Smrg
2348d522f475Smrgvoid
2349d522f475SmrgHandleStartExtend(Widget w,
2350d522f475Smrg		  XEvent * event,	/* must be XButtonEvent* */
2351d522f475Smrg		  String * params,	/* unused */
2352d522f475Smrg		  Cardinal *num_params)		/* unused */
2353d522f475Smrg{
2354956cc18dSsnj    XtermWidget xw;
2355956cc18dSsnj
2356956cc18dSsnj    if ((xw = getXtermWidget(w)) != 0) {
2357956cc18dSsnj	do_start_extend(xw, event, params, num_params, False);
2358956cc18dSsnj    }
2359d522f475Smrg}
2360d522f475Smrg
2361d522f475Smrgvoid
2362d522f475SmrgHandleKeyboardStartExtend(Widget w,
2363d522f475Smrg			  XEvent * event,	/* must be XButtonEvent* */
2364d522f475Smrg			  String * params,	/* unused */
2365d522f475Smrg			  Cardinal *num_params)		/* unused */
2366d522f475Smrg{
2367956cc18dSsnj    XtermWidget xw;
2368956cc18dSsnj
2369956cc18dSsnj    if ((xw = getXtermWidget(w)) != 0) {
2370956cc18dSsnj	do_start_extend(xw, event, params, num_params, True);
2371956cc18dSsnj    }
2372d522f475Smrg}
2373d522f475Smrg
2374d522f475Smrgvoid
2375d522f475SmrgScrollSelection(TScreen * screen, int amount, Bool always)
2376d522f475Smrg{
2377d522f475Smrg    int minrow = INX2ROW(screen, -screen->savedlines);
2378d522f475Smrg    int maxrow = INX2ROW(screen, screen->max_row);
2379d522f475Smrg    int maxcol = screen->max_col;
2380d522f475Smrg
2381d522f475Smrg#define scroll_update_one(cell) \
2382d522f475Smrg	(cell)->row += amount; \
2383d522f475Smrg	if ((cell)->row < minrow) { \
2384d522f475Smrg	    (cell)->row = minrow; \
2385d522f475Smrg	    (cell)->col = 0; \
2386d522f475Smrg	} \
2387d522f475Smrg	if ((cell)->row > maxrow) { \
2388d522f475Smrg	    (cell)->row = maxrow; \
2389d522f475Smrg	    (cell)->col = maxcol; \
2390d522f475Smrg	}
2391d522f475Smrg
2392d522f475Smrg    scroll_update_one(&(screen->startRaw));
2393d522f475Smrg    scroll_update_one(&(screen->endRaw));
2394d522f475Smrg    scroll_update_one(&(screen->startSel));
2395d522f475Smrg    scroll_update_one(&(screen->endSel));
2396d522f475Smrg
2397d522f475Smrg    scroll_update_one(&(screen->rawPos));
2398d522f475Smrg
2399d522f475Smrg    /*
2400d522f475Smrg     * If we are told to scroll the selection but it lies outside the scrolling
2401d522f475Smrg     * margins, then that could cause the selection to move (bad).  It is not
2402d522f475Smrg     * simple to fix, because this function is called both for the scrollbar
2403d522f475Smrg     * actions as well as application scrolling.  The 'always' flag is set in
2404d522f475Smrg     * the former case.  The rest of the logic handles the latter.
2405d522f475Smrg     */
2406d522f475Smrg    if (ScrnHaveSelection(screen)) {
2407d522f475Smrg	int adjust;
2408d522f475Smrg
2409d522f475Smrg	adjust = ROW2INX(screen, screen->startH.row);
2410d522f475Smrg	if (always
2411d522f475Smrg	    || !ScrnHaveLineMargins(screen)
2412d522f475Smrg	    || ScrnIsLineInMargins(screen, adjust)) {
2413d522f475Smrg	    scroll_update_one(&screen->startH);
2414d522f475Smrg	}
2415d522f475Smrg	adjust = ROW2INX(screen, screen->endH.row);
2416d522f475Smrg	if (always
2417d522f475Smrg	    || !ScrnHaveLineMargins(screen)
2418d522f475Smrg	    || ScrnIsLineInMargins(screen, adjust)) {
2419d522f475Smrg	    scroll_update_one(&screen->endH);
2420d522f475Smrg	}
2421d522f475Smrg    }
2422d522f475Smrg
2423d522f475Smrg    screen->startHCoord = Coordinate(screen, &screen->startH);
2424d522f475Smrg    screen->endHCoord = Coordinate(screen, &screen->endH);
2425d522f475Smrg}
2426d522f475Smrg
2427d522f475Smrg/*ARGSUSED*/
2428d522f475Smrgvoid
2429d522f475SmrgResizeSelection(TScreen * screen GCC_UNUSED, int rows, int cols)
2430d522f475Smrg{
2431d522f475Smrg    rows--;			/* decr to get 0-max */
2432d522f475Smrg    cols--;
2433d522f475Smrg
2434d522f475Smrg    if (screen->startRaw.row > rows)
2435d522f475Smrg	screen->startRaw.row = rows;
2436d522f475Smrg    if (screen->startSel.row > rows)
2437d522f475Smrg	screen->startSel.row = rows;
2438d522f475Smrg    if (screen->endRaw.row > rows)
2439d522f475Smrg	screen->endRaw.row = rows;
2440d522f475Smrg    if (screen->endSel.row > rows)
2441d522f475Smrg	screen->endSel.row = rows;
2442d522f475Smrg    if (screen->rawPos.row > rows)
2443d522f475Smrg	screen->rawPos.row = rows;
2444d522f475Smrg
2445d522f475Smrg    if (screen->startRaw.col > cols)
2446d522f475Smrg	screen->startRaw.col = cols;
2447d522f475Smrg    if (screen->startSel.col > cols)
2448d522f475Smrg	screen->startSel.col = cols;
2449d522f475Smrg    if (screen->endRaw.col > cols)
2450d522f475Smrg	screen->endRaw.col = cols;
2451d522f475Smrg    if (screen->endSel.col > cols)
2452d522f475Smrg	screen->endSel.col = cols;
2453d522f475Smrg    if (screen->rawPos.col > cols)
2454d522f475Smrg	screen->rawPos.col = cols;
2455d522f475Smrg}
2456d522f475Smrg
2457d522f475Smrg#if OPT_WIDE_CHARS
2458d522f475SmrgBool
2459d522f475Smrgiswide(int i)
2460d522f475Smrg{
246120d2c4d2Smrg    return (i == HIDDEN_CHAR) || (WideCells(i) == 2);
2462d522f475Smrg}
2463d522f475Smrg
2464d522f475Smrg#define isWideCell(row, col) iswide((int)XTERM_CELL(row, col))
2465d522f475Smrg#endif
2466d522f475Smrg
2467d522f475Smrgstatic void
2468d522f475SmrgPointToCELL(TScreen * screen,
2469d522f475Smrg	    int y,
2470d522f475Smrg	    int x,
2471d522f475Smrg	    CELL * cell)
2472d522f475Smrg/* Convert pixel coordinates to character coordinates.
2473d522f475Smrg   Rows are clipped between firstValidRow and lastValidRow.
2474d522f475Smrg   Columns are clipped between to be 0 or greater, but are not clipped to some
2475d522f475Smrg       maximum value. */
2476d522f475Smrg{
2477d522f475Smrg    cell->row = (y - screen->border) / FontHeight(screen);
2478d522f475Smrg    if (cell->row < screen->firstValidRow)
2479d522f475Smrg	cell->row = screen->firstValidRow;
2480d522f475Smrg    else if (cell->row > screen->lastValidRow)
2481d522f475Smrg	cell->row = screen->lastValidRow;
2482d522f475Smrg    cell->col = (x - OriginX(screen)) / FontWidth(screen);
2483d522f475Smrg    if (cell->col < 0)
2484d522f475Smrg	cell->col = 0;
2485d522f475Smrg    else if (cell->col > MaxCols(screen)) {
2486d522f475Smrg	cell->col = MaxCols(screen);
2487d522f475Smrg    }
2488d522f475Smrg#if OPT_WIDE_CHARS
2489d522f475Smrg    /*
2490d522f475Smrg     * If we got a click on the right half of a doublewidth character,
2491d522f475Smrg     * pretend it happened on the left half.
2492d522f475Smrg     */
2493d522f475Smrg    if (cell->col > 0
2494d522f475Smrg	&& isWideCell(cell->row, cell->col - 1)
2495d522f475Smrg	&& (XTERM_CELL(cell->row, cell->col) == HIDDEN_CHAR)) {
2496d522f475Smrg	cell->col -= 1;
2497d522f475Smrg    }
2498d522f475Smrg#endif
2499d522f475Smrg}
2500d522f475Smrg
2501d522f475Smrg/*
2502d522f475Smrg * Find the last column at which text was drawn on the given row.
2503d522f475Smrg */
2504d522f475Smrgstatic int
2505956cc18dSsnjLastTextCol(TScreen * screen, LineData * ld, int row)
2506d522f475Smrg{
250720d2c4d2Smrg    int i = -1;
2508d522f475Smrg    Char *ch;
2509d522f475Smrg
251020d2c4d2Smrg    if (ld != 0) {
251120d2c4d2Smrg	if (okScrnRow(screen, row)) {
251220d2c4d2Smrg	    for (i = screen->max_col,
251320d2c4d2Smrg		 ch = ld->attribs + i;
251420d2c4d2Smrg		 i >= 0 && !(*ch & CHARDRAWN);
251520d2c4d2Smrg		 ch--, i--) {
251620d2c4d2Smrg		;
251720d2c4d2Smrg	    }
2518d522f475Smrg#if OPT_DEC_CHRSET
251920d2c4d2Smrg	    if (CSET_DOUBLE(GetLineDblCS(ld))) {
252020d2c4d2Smrg		i *= 2;
252120d2c4d2Smrg	    }
2522d522f475Smrg#endif
252320d2c4d2Smrg	}
2524d522f475Smrg    }
2525d522f475Smrg    return (i);
2526d522f475Smrg}
2527d522f475Smrg
2528d522f475Smrg#if !OPT_WIDE_CHARS
2529d522f475Smrg/*
2530d522f475Smrg** double click table for cut and paste in 8 bits
2531d522f475Smrg**
2532d522f475Smrg** This table is divided in four parts :
2533d522f475Smrg**
2534d522f475Smrg**	- control characters	[0,0x1f] U [0x80,0x9f]
2535d522f475Smrg**	- separators		[0x20,0x3f] U [0xa0,0xb9]
2536d522f475Smrg**	- binding characters	[0x40,0x7f] U [0xc0,0xff]
2537d522f475Smrg**	- exceptions
2538d522f475Smrg*/
2539d522f475Smrg/* *INDENT-OFF* */
2540d522f475Smrgstatic int charClass[256] =
2541d522f475Smrg{
2542d522f475Smrg/* NUL  SOH  STX  ETX  EOT  ENQ  ACK  BEL */
2543d522f475Smrg    32,  1,    1,   1,   1,   1,   1,   1,
2544d522f475Smrg/*  BS   HT   NL   VT   NP   CR   SO   SI */
2545d522f475Smrg     1,  32,   1,   1,   1,   1,   1,   1,
2546d522f475Smrg/* DLE  DC1  DC2  DC3  DC4  NAK  SYN  ETB */
2547d522f475Smrg     1,   1,   1,   1,   1,   1,   1,   1,
2548d522f475Smrg/* CAN   EM  SUB  ESC   FS   GS   RS   US */
2549d522f475Smrg     1,   1,   1,   1,   1,   1,   1,   1,
2550d522f475Smrg/*  SP    !    "    #    $    %    &    ' */
2551d522f475Smrg    32,  33,  34,  35,  36,  37,  38,  39,
2552d522f475Smrg/*   (    )    *    +    ,    -    .    / */
2553d522f475Smrg    40,  41,  42,  43,  44,  45,  46,  47,
2554d522f475Smrg/*   0    1    2    3    4    5    6    7 */
2555d522f475Smrg    48,  48,  48,  48,  48,  48,  48,  48,
2556d522f475Smrg/*   8    9    :    ;    <    =    >    ? */
2557d522f475Smrg    48,  48,  58,  59,  60,  61,  62,  63,
2558d522f475Smrg/*   @    A    B    C    D    E    F    G */
2559d522f475Smrg    64,  48,  48,  48,  48,  48,  48,  48,
2560d522f475Smrg/*   H    I    J    K    L    M    N    O */
2561d522f475Smrg    48,  48,  48,  48,  48,  48,  48,  48,
2562d522f475Smrg/*   P    Q    R    S    T    U    V    W */
2563d522f475Smrg    48,  48,  48,  48,  48,  48,  48,  48,
2564d522f475Smrg/*   X    Y    Z    [    \    ]    ^    _ */
2565d522f475Smrg    48,  48,  48,  91,  92,  93,  94,  48,
2566d522f475Smrg/*   `    a    b    c    d    e    f    g */
2567d522f475Smrg    96,  48,  48,  48,  48,  48,  48,  48,
2568d522f475Smrg/*   h    i    j    k    l    m    n    o */
2569d522f475Smrg    48,  48,  48,  48,  48,  48,  48,  48,
2570d522f475Smrg/*   p    q    r    s    t    u    v    w */
2571d522f475Smrg    48,  48,  48,  48,  48,  48,  48,  48,
2572d522f475Smrg/*   x    y    z    {    |    }    ~  DEL */
2573d522f475Smrg    48,  48,  48, 123, 124, 125, 126,   1,
2574d522f475Smrg/* x80  x81  x82  x83  IND  NEL  SSA  ESA */
2575d522f475Smrg    1,    1,   1,   1,   1,   1,   1,   1,
2576d522f475Smrg/* HTS  HTJ  VTS  PLD  PLU   RI  SS2  SS3 */
2577d522f475Smrg    1,    1,   1,   1,   1,   1,   1,   1,
2578d522f475Smrg/* DCS  PU1  PU2  STS  CCH   MW  SPA  EPA */
2579d522f475Smrg    1,    1,   1,   1,   1,   1,   1,   1,
2580d522f475Smrg/* x98  x99  x9A  CSI   ST  OSC   PM  APC */
2581d522f475Smrg    1,    1,   1,   1,   1,   1,   1,   1,
2582d522f475Smrg/*   -    i   c/    L   ox   Y-    |   So */
2583d522f475Smrg    160, 161, 162, 163, 164, 165, 166, 167,
2584d522f475Smrg/*  ..   c0   ip   <<    _        R0    - */
2585d522f475Smrg    168, 169, 170, 171, 172, 173, 174, 175,
2586d522f475Smrg/*   o   +-    2    3    '    u   q|    . */
2587d522f475Smrg    176, 177, 178, 179, 180, 181, 182, 183,
2588d522f475Smrg/*   ,    1    2   >>  1/4  1/2  3/4    ? */
2589d522f475Smrg    184, 185, 186, 187, 188, 189, 190, 191,
2590d522f475Smrg/*  A`   A'   A^   A~   A:   Ao   AE   C, */
2591d522f475Smrg     48,  48,  48,  48,  48,  48,  48,  48,
2592d522f475Smrg/*  E`   E'   E^   E:   I`   I'   I^   I: */
2593d522f475Smrg     48,  48,  48,  48,  48,  48,  48,  48,
2594d522f475Smrg/*  D-   N~   O`   O'   O^   O~   O:    X */
2595d522f475Smrg     48,  48,  48,  48,  48,  48,  48, 215,
2596d522f475Smrg/*  O/   U`   U'   U^   U:   Y'    P    B */
2597d522f475Smrg     48,  48,  48,  48,  48,  48,  48,  48,
2598d522f475Smrg/*  a`   a'   a^   a~   a:   ao   ae   c, */
2599d522f475Smrg     48,  48,  48,  48,  48,  48,  48,  48,
2600d522f475Smrg/*  e`   e'   e^   e:    i`  i'   i^   i: */
2601d522f475Smrg     48,  48,  48,  48,  48,  48,  48,  48,
2602d522f475Smrg/*   d   n~   o`   o'   o^   o~   o:   -: */
2603d522f475Smrg     48,  48,  48,  48,  48,  48,  48, 247,
2604d522f475Smrg/*  o/   u`   u'   u^   u:   y'    P   y: */
2605d522f475Smrg     48,  48,  48,  48,  48,  48,  48,  48};
2606d522f475Smrg/* *INDENT-ON* */
2607d522f475Smrg
2608d522f475Smrgint
2609d522f475SmrgSetCharacterClassRange(int low,	/* in range of [0..255] */
2610d522f475Smrg		       int high,
2611d522f475Smrg		       int value)	/* arbitrary */
2612d522f475Smrg{
2613d522f475Smrg
2614d522f475Smrg    if (low < 0 || high > 255 || high < low)
2615d522f475Smrg	return (-1);
2616d522f475Smrg
2617d522f475Smrg    for (; low <= high; low++)
2618d522f475Smrg	charClass[low] = value;
2619d522f475Smrg
2620d522f475Smrg    return (0);
2621d522f475Smrg}
2622d522f475Smrg#endif
2623d522f475Smrg
2624d522f475Smrgstatic int
2625956cc18dSsnjclass_of(LineData * ld, CELL * cell)
2626d522f475Smrg{
2627d522f475Smrg    CELL temp = *cell;
2628d522f475Smrg
2629d522f475Smrg#if OPT_DEC_CHRSET
2630956cc18dSsnj    if (CSET_DOUBLE(GetLineDblCS(ld))) {
2631d522f475Smrg	temp.col /= 2;
2632d522f475Smrg    }
2633d522f475Smrg#endif
2634d522f475Smrg
263520d2c4d2Smrg    assert(temp.col < ld->lineSize);
2636956cc18dSsnj    return CharacterClass((int) (ld->charData[temp.col]));
2637d522f475Smrg}
2638956cc18dSsnj
2639956cc18dSsnj#if OPT_WIDE_CHARS
2640956cc18dSsnj#define CClassSelects(name, cclass) \
2641956cc18dSsnj	 (CClassOf(name) == cclass \
2642956cc18dSsnj	 || XTERM_CELL(screen->name.row, screen->name.col) == HIDDEN_CHAR)
2643d522f475Smrg#else
2644956cc18dSsnj#define CClassSelects(name, cclass) \
2645956cc18dSsnj	 (class_of(ld.name, &((screen->name))) == cclass)
2646d522f475Smrg#endif
2647d522f475Smrg
2648956cc18dSsnj#define CClassOf(name) class_of(ld.name, &((screen->name)))
2649956cc18dSsnj
2650d522f475Smrg/*
2651d522f475Smrg * If the given column is past the end of text on the given row, bump to the
2652d522f475Smrg * beginning of the next line.
2653d522f475Smrg */
2654d522f475Smrgstatic Boolean
2655d522f475SmrgokPosition(TScreen * screen,
2656956cc18dSsnj	   LineData ** ld,
2657d522f475Smrg	   CELL * cell)
2658d522f475Smrg{
265920d2c4d2Smrg    Boolean result = True;
266020d2c4d2Smrg
266120d2c4d2Smrg    if (cell->row > screen->max_row) {
266220d2c4d2Smrg	result = False;
266320d2c4d2Smrg    } else if (cell->col > (LastTextCol(screen, *ld, cell->row) + 1)) {
266420d2c4d2Smrg	if (cell->row < screen->max_row) {
266520d2c4d2Smrg	    cell->col = 0;
266620d2c4d2Smrg	    *ld = GET_LINEDATA(screen, ++cell->row);
266720d2c4d2Smrg	    result = False;
266820d2c4d2Smrg	}
2669d522f475Smrg    }
267020d2c4d2Smrg    return result;
2671d522f475Smrg}
2672d522f475Smrg
2673d522f475Smrgstatic void
2674956cc18dSsnjtrimLastLine(TScreen * screen,
2675956cc18dSsnj	     LineData ** ld,
2676956cc18dSsnj	     CELL * last)
2677d522f475Smrg{
267820d2c4d2Smrg    if (screen->cutNewline && last->row < screen->max_row) {
2679d522f475Smrg	last->col = 0;
2680956cc18dSsnj	*ld = GET_LINEDATA(screen, ++last->row);
2681d522f475Smrg    } else {
2682956cc18dSsnj	last->col = LastTextCol(screen, *ld, last->row) + 1;
2683d522f475Smrg    }
2684d522f475Smrg}
2685d522f475Smrg
2686d522f475Smrg#if OPT_SELECT_REGEX
2687d522f475Smrg/*
2688d522f475Smrg * Returns the first row of a wrapped line.
2689d522f475Smrg */
2690d522f475Smrgstatic int
2691d522f475SmrgfirstRowOfLine(TScreen * screen, int row, Bool visible)
2692d522f475Smrg{
2693956cc18dSsnj    LineData *ld = 0;
2694d522f475Smrg    int limit = visible ? 0 : -screen->savedlines;
2695d522f475Smrg
2696d522f475Smrg    while (row > limit &&
2697956cc18dSsnj	   (ld = GET_LINEDATA(screen, row - 1)) != 0 &&
2698956cc18dSsnj	   LineTstWrapped(ld)) {
2699d522f475Smrg	--row;
2700956cc18dSsnj    }
2701d522f475Smrg    return row;
2702d522f475Smrg}
2703d522f475Smrg
2704d522f475Smrg/*
2705d522f475Smrg * Returns the last row of a wrapped line.
2706d522f475Smrg */
2707d522f475Smrgstatic int
2708d522f475SmrglastRowOfLine(TScreen * screen, int row)
2709d522f475Smrg{
2710956cc18dSsnj    LineData *ld;
2711956cc18dSsnj
2712d522f475Smrg    while (row < screen->max_row &&
2713956cc18dSsnj	   (ld = GET_LINEDATA(screen, row)) != 0 &&
2714956cc18dSsnj	   LineTstWrapped(ld)) {
2715d522f475Smrg	++row;
2716956cc18dSsnj    }
2717d522f475Smrg    return row;
2718d522f475Smrg}
2719d522f475Smrg
2720d522f475Smrg/*
2721d522f475Smrg * Returns the number of cells on the range of rows.
2722d522f475Smrg */
2723d522f475Smrgstatic unsigned
2724d522f475SmrglengthOfLines(TScreen * screen, int firstRow, int lastRow)
2725d522f475Smrg{
2726d522f475Smrg    unsigned length = 0;
2727d522f475Smrg    int n;
2728d522f475Smrg
2729d522f475Smrg    for (n = firstRow; n <= lastRow; ++n) {
2730956cc18dSsnj	LineData *ld = GET_LINEDATA(screen, n);
2731956cc18dSsnj	int value = LastTextCol(screen, ld, n);
2732d522f475Smrg	if (value >= 0)
27332eaa94a1Schristos	    length += (unsigned) (value + 1);
2734d522f475Smrg    }
2735d522f475Smrg    return length;
2736d522f475Smrg}
2737d522f475Smrg
2738d522f475Smrg/*
2739d522f475Smrg * Make a copy of the wrapped-line which corresponds to the given row as a
2740d522f475Smrg * string of bytes.  Construct an index for the columns from the beginning of
2741d522f475Smrg * the line.
2742d522f475Smrg */
2743d522f475Smrgstatic char *
2744d522f475Smrgmake_indexed_text(TScreen * screen, int row, unsigned length, int *indexed)
2745d522f475Smrg{
2746d522f475Smrg    Char *result = 0;
274720d2c4d2Smrg    size_t need = (length + 1);
2748d522f475Smrg
2749d522f475Smrg    /*
2750d522f475Smrg     * Get a quick upper bound to the number of bytes needed, if the whole
2751d522f475Smrg     * string were UTF-8.
2752d522f475Smrg     */
2753d522f475Smrg    if_OPT_WIDE_CHARS(screen, {
2754956cc18dSsnj	need *= ((screen->lineExtra + 1) * 6);
2755d522f475Smrg    });
2756d522f475Smrg
2757d522f475Smrg    if ((result = TypeCallocN(Char, need + 1)) != 0) {
2758956cc18dSsnj	LineData *ld = GET_LINEDATA(screen, row);
2759d522f475Smrg	unsigned used = 0;
2760d522f475Smrg	Char *last = result;
2761d522f475Smrg
2762d522f475Smrg	do {
2763d522f475Smrg	    int col = 0;
2764956cc18dSsnj	    int limit = LastTextCol(screen, ld, row);
2765d522f475Smrg
2766d522f475Smrg	    while (col <= limit) {
2767d522f475Smrg		Char *next = last;
2768956cc18dSsnj		unsigned data = ld->charData[col];
2769d522f475Smrg
277020d2c4d2Smrg		assert(col < ld->lineSize);
2771d522f475Smrg		/* some internal points may not be drawn */
2772d522f475Smrg		if (data == 0)
2773d522f475Smrg		    data = ' ';
2774d522f475Smrg
2775d522f475Smrg		if_WIDE_OR_NARROW(screen, {
2776d522f475Smrg		    next = convertToUTF8(last, data);
2777d522f475Smrg		}
2778d522f475Smrg		, {
2779d522f475Smrg		    *next++ = CharOf(data);
2780d522f475Smrg		});
2781d522f475Smrg
2782d522f475Smrg		if_OPT_WIDE_CHARS(screen, {
2783956cc18dSsnj		    size_t off;
2784956cc18dSsnj		    for_each_combData(off, ld) {
2785956cc18dSsnj			data = ld->combData[off][col];
2786956cc18dSsnj			if (data == 0)
2787d522f475Smrg			    break;
2788d522f475Smrg			next = convertToUTF8(next, data);
2789d522f475Smrg		    }
2790d522f475Smrg		});
2791d522f475Smrg
279220d2c4d2Smrg		indexed[used] = (int) (last - result);
2793d522f475Smrg		*next = 0;
2794d522f475Smrg		/* TRACE(("index[%d.%d] %d:%s\n", row, used, indexed[used], last)); */
2795d522f475Smrg		last = next;
2796d522f475Smrg		++used;
2797d522f475Smrg		++col;
279820d2c4d2Smrg		indexed[used] = (int) (next - result);
2799d522f475Smrg	    }
2800d522f475Smrg	} while (used < length &&
2801956cc18dSsnj		 LineTstWrapped(ld) &&
2802956cc18dSsnj		 (ld = GET_LINEDATA(screen, ++row)) != 0 &&
2803956cc18dSsnj		 row < screen->max_row);
2804d522f475Smrg    }
2805d522f475Smrg    /* TRACE(("result:%s\n", result)); */
2806d522f475Smrg    return (char *) result;
2807d522f475Smrg}
2808d522f475Smrg
2809d522f475Smrg/*
2810d522f475Smrg * Find the column given an offset into the character string by using the
2811d522f475Smrg * index constructed in make_indexed_text().
2812d522f475Smrg */
2813d522f475Smrgstatic int
2814d522f475SmrgindexToCol(int *indexed, int len, int off)
2815d522f475Smrg{
2816d522f475Smrg    int col = 0;
2817d522f475Smrg    while (indexed[col] < len) {
2818d522f475Smrg	if (indexed[col] >= off)
2819d522f475Smrg	    break;
2820d522f475Smrg	++col;
2821d522f475Smrg    }
2822d522f475Smrg    return col;
2823d522f475Smrg}
2824d522f475Smrg
2825d522f475Smrg/*
2826d522f475Smrg * Given a row number, and a column offset from that (which may be wrapped),
2827d522f475Smrg * set the cell to the actual row/column values.
2828d522f475Smrg */
2829d522f475Smrgstatic void
2830d522f475SmrgcolumnToCell(TScreen * screen, int row, int col, CELL * cell)
2831d522f475Smrg{
2832d522f475Smrg    while (row < screen->max_row) {
2833956cc18dSsnj	LineData *ld = GET_LINEDATA(screen, row);
2834956cc18dSsnj	int last = LastTextCol(screen, ld, row);
2835d522f475Smrg
2836d522f475Smrg	/* TRACE(("last(%d) = %d, have %d\n", row, last, col)); */
2837d522f475Smrg	if (col <= last) {
2838d522f475Smrg	    break;
2839d522f475Smrg	}
2840d522f475Smrg	/*
2841d522f475Smrg	 * Stop if the current row does not wrap (does not continue the current
2842d522f475Smrg	 * line).
2843d522f475Smrg	 */
2844956cc18dSsnj	if (!LineTstWrapped(ld)) {
2845d522f475Smrg	    col = last + 1;
2846d522f475Smrg	    break;
2847d522f475Smrg	}
2848d522f475Smrg	col -= (last + 1);
2849d522f475Smrg	++row;
2850d522f475Smrg    }
2851d522f475Smrg    if (col < 0)
2852d522f475Smrg	col = 0;
2853d522f475Smrg    cell->row = row;
2854d522f475Smrg    cell->col = col;
2855d522f475Smrg}
2856d522f475Smrg
2857d522f475Smrg/*
2858d522f475Smrg * Given a cell, find the corresponding column offset.
2859d522f475Smrg */
2860d522f475Smrgstatic int
2861d522f475SmrgcellToColumn(TScreen * screen, CELL * cell)
2862d522f475Smrg{
2863956cc18dSsnj    LineData *ld = 0;
2864d522f475Smrg    int col = cell->col;
2865d522f475Smrg    int row = firstRowOfLine(screen, cell->row, False);
2866d522f475Smrg    while (row < cell->row) {
2867956cc18dSsnj	ld = GET_LINEDATA(screen, row);
2868956cc18dSsnj	col += LastTextCol(screen, ld, row++);
2869d522f475Smrg    }
2870956cc18dSsnj#if OPT_DEC_CHRSET
2871956cc18dSsnj    if (ld == 0)
2872956cc18dSsnj	ld = GET_LINEDATA(screen, row);
2873956cc18dSsnj    if (CSET_DOUBLE(GetLineDblCS(ld)))
2874956cc18dSsnj	col /= 2;
2875956cc18dSsnj#endif
2876d522f475Smrg    return col;
2877d522f475Smrg}
2878d522f475Smrg
2879d522f475Smrgstatic void
2880d522f475Smrgdo_select_regex(TScreen * screen, CELL * startc, CELL * endc)
2881d522f475Smrg{
2882956cc18dSsnj    LineData *ld = GET_LINEDATA(screen, startc->row);
2883d522f475Smrg    int inx = ((screen->numberOfClicks - 1) % screen->maxClicks);
2884d522f475Smrg    char *expr = screen->selectExpr[inx];
2885d522f475Smrg    regex_t preg;
2886d522f475Smrg    regmatch_t match;
2887d522f475Smrg    char *search;
2888d522f475Smrg    int *indexed;
2889d522f475Smrg
2890d522f475Smrg    TRACE(("Select_REGEX:%s\n", NonNull(expr)));
2891956cc18dSsnj    if (okPosition(screen, &ld, startc) && expr != 0) {
2892d522f475Smrg	if (regcomp(&preg, expr, REG_EXTENDED) == 0) {
2893d522f475Smrg	    int firstRow = firstRowOfLine(screen, startc->row, True);
2894d522f475Smrg	    int lastRow = lastRowOfLine(screen, firstRow);
2895d522f475Smrg	    unsigned size = lengthOfLines(screen, firstRow, lastRow);
2896d522f475Smrg	    int actual = cellToColumn(screen, startc);
2897d522f475Smrg
2898d522f475Smrg	    TRACE(("regcomp ok rows %d..%d bytes %d\n",
2899d522f475Smrg		   firstRow, lastRow, size));
2900d522f475Smrg
2901d522f475Smrg	    if ((indexed = TypeCallocN(int, size + 1)) != 0) {
2902d522f475Smrg		if ((search = make_indexed_text(screen,
2903d522f475Smrg						firstRow,
2904d522f475Smrg						size,
2905d522f475Smrg						indexed)) != 0) {
29062eaa94a1Schristos		    int len = (int) strlen(search);
2907d522f475Smrg		    int col;
2908d522f475Smrg		    int best_col = -1;
2909d522f475Smrg		    int best_len = -1;
2910d522f475Smrg
2911d522f475Smrg		    for (col = 0; indexed[col] < len; ++col) {
2912d522f475Smrg			if (regexec(&preg,
2913d522f475Smrg				    search + indexed[col],
291420d2c4d2Smrg				    (size_t) 1, &match, 0) == 0) {
2915d522f475Smrg			    int start_inx = match.rm_so + indexed[col];
2916d522f475Smrg			    int finis_inx = match.rm_eo + indexed[col];
2917d522f475Smrg			    int start_col = indexToCol(indexed, len, start_inx);
2918d522f475Smrg			    int finis_col = indexToCol(indexed, len, finis_inx);
2919d522f475Smrg
2920d522f475Smrg			    if (start_col <= actual &&
2921d522f475Smrg				actual < finis_col) {
2922d522f475Smrg				int test = finis_col - start_col;
2923d522f475Smrg				if (best_len < test) {
2924d522f475Smrg				    best_len = test;
2925d522f475Smrg				    best_col = start_col;
2926d522f475Smrg				    TRACE(("match column %d len %d\n",
2927d522f475Smrg					   best_col,
2928d522f475Smrg					   best_len));
2929d522f475Smrg				}
2930d522f475Smrg			    }
2931d522f475Smrg			}
2932d522f475Smrg		    }
2933d522f475Smrg		    if (best_col >= 0) {
2934d522f475Smrg			int best_nxt = best_col + best_len;
2935d522f475Smrg			columnToCell(screen, firstRow, best_col, startc);
2936d522f475Smrg			columnToCell(screen, firstRow, best_nxt, endc);
2937d522f475Smrg			TRACE(("search::%s\n", search));
2938d522f475Smrg			TRACE(("indexed:%d..%d -> %d..%d\n",
2939d522f475Smrg			       best_col, best_nxt,
2940d522f475Smrg			       indexed[best_col],
2941d522f475Smrg			       indexed[best_nxt]));
2942d522f475Smrg			TRACE(("matched:%d:%s\n",
2943d522f475Smrg			       indexed[best_nxt] + 1 -
2944d522f475Smrg			       indexed[best_col],
2945956cc18dSsnj			       visibleChars((Char *) (search + indexed[best_col]),
2946d522f475Smrg					    (unsigned) (indexed[best_nxt] +
2947d522f475Smrg							1 -
2948d522f475Smrg							indexed[best_col]))));
2949d522f475Smrg		    }
2950d522f475Smrg		    free(search);
2951d522f475Smrg		}
2952d522f475Smrg		free(indexed);
2953956cc18dSsnj#if OPT_DEC_CHRSET
2954956cc18dSsnj		if ((ld = GET_LINEDATA(screen, startc->row)) != 0) {
2955956cc18dSsnj		    if (CSET_DOUBLE(GetLineDblCS(ld)))
2956956cc18dSsnj			startc->col *= 2;
2957956cc18dSsnj		}
2958956cc18dSsnj		if ((ld = GET_LINEDATA(screen, endc->row)) != 0) {
2959956cc18dSsnj		    if (CSET_DOUBLE(GetLineDblCS(ld)))
2960956cc18dSsnj			endc->col *= 2;
2961956cc18dSsnj		}
2962956cc18dSsnj#endif
2963d522f475Smrg	    }
2964d522f475Smrg	    regfree(&preg);
2965d522f475Smrg	}
2966d522f475Smrg    }
2967d522f475Smrg}
2968d522f475Smrg#endif /* OPT_SELECT_REGEX */
2969d522f475Smrg
2970956cc18dSsnj#define InitRow(name) \
2971956cc18dSsnj	ld.name = GET_LINEDATA(screen, screen->name.row)
2972956cc18dSsnj
2973956cc18dSsnj#define NextRow(name) \
2974956cc18dSsnj	ld.name = GET_LINEDATA(screen, ++screen->name.row)
2975956cc18dSsnj
2976956cc18dSsnj#define PrevRow(name) \
2977956cc18dSsnj	ld.name = GET_LINEDATA(screen, --screen->name.row)
2978956cc18dSsnj
297920d2c4d2Smrg#define MoreRows(name) \
298020d2c4d2Smrg	(screen->name.row < screen->max_row)
298120d2c4d2Smrg
2982956cc18dSsnj#define isPrevWrapped(name) \
2983956cc18dSsnj	(screen->name.row > 0 \
2984956cc18dSsnj	   && (ltmp = GET_LINEDATA(screen, screen->name.row - 1)) != 0 \
2985956cc18dSsnj	   && LineTstWrapped(ltmp))
2986956cc18dSsnj
2987d522f475Smrg/*
2988d522f475Smrg * sets startSel endSel
2989d522f475Smrg * ensuring that they have legal values
2990d522f475Smrg */
2991d522f475Smrgstatic void
2992d522f475SmrgComputeSelect(XtermWidget xw,
2993d522f475Smrg	      CELL * startc,
2994d522f475Smrg	      CELL * endc,
2995d522f475Smrg	      Bool extend)
2996d522f475Smrg{
2997956cc18dSsnj    TScreen *screen = TScreenOf(xw);
2998956cc18dSsnj
2999d522f475Smrg    int length;
3000d522f475Smrg    int cclass;
3001d522f475Smrg    CELL first = *startc;
3002d522f475Smrg    CELL last = *endc;
3003956cc18dSsnj    Boolean ignored = False;
3004956cc18dSsnj
3005956cc18dSsnj    struct {
3006956cc18dSsnj	LineData *startSel;
3007956cc18dSsnj	LineData *endSel;
3008956cc18dSsnj    } ld;
3009956cc18dSsnj    LineData *ltmp;
3010d522f475Smrg
3011d522f475Smrg    TRACE(("ComputeSelect(startRow=%d, startCol=%d, endRow=%d, endCol=%d, %sextend)\n",
3012d522f475Smrg	   first.row, first.col,
3013d522f475Smrg	   last.row, last.col,
3014d522f475Smrg	   extend ? "" : "no"));
3015d522f475Smrg
3016d522f475Smrg#if OPT_WIDE_CHARS
3017d522f475Smrg    if (first.col > 1
3018d522f475Smrg	&& isWideCell(first.row, first.col - 1)
3019d522f475Smrg	&& XTERM_CELL(first.row, first.col - 0) == HIDDEN_CHAR) {
302020d2c4d2Smrg	TRACE(("Adjusting start. Changing downwards from %i.\n", first.col));
3021d522f475Smrg	first.col -= 1;
3022d522f475Smrg	if (last.col == (first.col + 1))
3023d522f475Smrg	    last.col--;
3024d522f475Smrg    }
3025d522f475Smrg
3026d522f475Smrg    if (last.col > 1
3027d522f475Smrg	&& isWideCell(last.row, last.col - 1)
3028d522f475Smrg	&& XTERM_CELL(last.row, last.col) == HIDDEN_CHAR) {
3029d522f475Smrg	last.col += 1;
3030d522f475Smrg    }
3031d522f475Smrg#endif
3032d522f475Smrg
3033d522f475Smrg    if (Coordinate(screen, &first) <= Coordinate(screen, &last)) {
3034d522f475Smrg	screen->startSel = screen->startRaw = first;
3035d522f475Smrg	screen->endSel = screen->endRaw = last;
3036d522f475Smrg    } else {			/* Swap them */
3037d522f475Smrg	screen->startSel = screen->startRaw = last;
3038d522f475Smrg	screen->endSel = screen->endRaw = first;
3039d522f475Smrg    }
3040d522f475Smrg
3041956cc18dSsnj    InitRow(startSel);
3042956cc18dSsnj    InitRow(endSel);
3043956cc18dSsnj
3044d522f475Smrg    switch (screen->selectUnit) {
3045d522f475Smrg    case Select_CHAR:
3046956cc18dSsnj	(void) okPosition(screen, &(ld.startSel), &(screen->startSel));
3047956cc18dSsnj	(void) okPosition(screen, &(ld.endSel), &(screen->endSel));
3048d522f475Smrg	break;
3049d522f475Smrg
3050d522f475Smrg    case Select_WORD:
3051d522f475Smrg	TRACE(("Select_WORD\n"));
3052956cc18dSsnj	if (okPosition(screen, &(ld.startSel), &(screen->startSel))) {
3053956cc18dSsnj	    cclass = CClassOf(startSel);
3054d522f475Smrg	    do {
3055d522f475Smrg		--screen->startSel.col;
3056956cc18dSsnj		if (screen->startSel.col < 0
3057956cc18dSsnj		    && isPrevWrapped(startSel)) {
3058956cc18dSsnj		    PrevRow(startSel);
3059956cc18dSsnj		    screen->startSel.col = LastTextCol(screen, ld.startSel, screen->startSel.row);
3060d522f475Smrg		}
3061d522f475Smrg	    } while (screen->startSel.col >= 0
3062956cc18dSsnj		     && CClassSelects(startSel, cclass));
3063d522f475Smrg	    ++screen->startSel.col;
3064d522f475Smrg	}
3065d522f475Smrg#if OPT_WIDE_CHARS
3066d522f475Smrg	if (screen->startSel.col
3067d522f475Smrg	    && XTERM_CELL(screen->startSel.row,
3068d522f475Smrg			  screen->startSel.col) == HIDDEN_CHAR)
3069d522f475Smrg	    screen->startSel.col++;
3070d522f475Smrg#endif
3071d522f475Smrg
3072956cc18dSsnj	if (okPosition(screen, &(ld.endSel), &(screen->endSel))) {
3073956cc18dSsnj	    length = LastTextCol(screen, ld.endSel, screen->endSel.row);
3074956cc18dSsnj	    cclass = CClassOf(endSel);
3075d522f475Smrg	    do {
3076d522f475Smrg		++screen->endSel.col;
3077d522f475Smrg		if (screen->endSel.col > length
3078956cc18dSsnj		    && LineTstWrapped(ld.endSel)) {
307920d2c4d2Smrg		    if (!MoreRows(endSel))
308020d2c4d2Smrg			break;
3081d522f475Smrg		    screen->endSel.col = 0;
3082956cc18dSsnj		    NextRow(endSel);
3083956cc18dSsnj		    length = LastTextCol(screen, ld.endSel, screen->endSel.row);
3084d522f475Smrg		}
3085d522f475Smrg	    } while (screen->endSel.col <= length
3086956cc18dSsnj		     && CClassSelects(endSel, cclass));
3087d522f475Smrg	    /* Word-select selects if pointing to any char in "word",
3088d522f475Smrg	     * especially note that it includes the last character in a word.
3089d522f475Smrg	     * So we do no --endSel.col and do special eol handling.
3090d522f475Smrg	     */
309120d2c4d2Smrg	    if (screen->endSel.col > length + 1
309220d2c4d2Smrg		&& MoreRows(endSel)) {
3093d522f475Smrg		screen->endSel.col = 0;
3094956cc18dSsnj		NextRow(endSel);
3095d522f475Smrg	    }
3096d522f475Smrg	}
3097d522f475Smrg#if OPT_WIDE_CHARS
3098d522f475Smrg	if (screen->endSel.col
3099d522f475Smrg	    && XTERM_CELL(screen->endSel.row,
3100d522f475Smrg			  screen->endSel.col) == HIDDEN_CHAR)
3101d522f475Smrg	    screen->endSel.col++;
3102d522f475Smrg#endif
3103d522f475Smrg
3104d522f475Smrg	screen->saveStartW = screen->startSel;
3105d522f475Smrg	break;
3106d522f475Smrg
3107d522f475Smrg    case Select_LINE:
3108d522f475Smrg	TRACE(("Select_LINE\n"));
310920d2c4d2Smrg	while (LineTstWrapped(ld.endSel)
311020d2c4d2Smrg	       && MoreRows(endSel)) {
3111956cc18dSsnj	    NextRow(endSel);
3112d522f475Smrg	}
3113d522f475Smrg	if (screen->cutToBeginningOfLine
3114d522f475Smrg	    || screen->startSel.row < screen->saveStartW.row) {
3115d522f475Smrg	    screen->startSel.col = 0;
3116956cc18dSsnj	    while (isPrevWrapped(startSel)) {
3117956cc18dSsnj		PrevRow(startSel);
3118d522f475Smrg	    }
3119d522f475Smrg	} else if (!extend) {
3120d522f475Smrg	    if ((first.row < screen->saveStartW.row)
3121d522f475Smrg		|| (isSameRow(&first, &(screen->saveStartW))
3122d522f475Smrg		    && first.col < screen->saveStartW.col)) {
3123d522f475Smrg		screen->startSel.col = 0;
3124956cc18dSsnj		while (isPrevWrapped(startSel)) {
3125956cc18dSsnj		    PrevRow(startSel);
3126d522f475Smrg		}
3127d522f475Smrg	    } else {
3128d522f475Smrg		screen->startSel = screen->saveStartW;
3129d522f475Smrg	    }
3130d522f475Smrg	}
3131956cc18dSsnj	trimLastLine(screen, &(ld.endSel), &(screen->endSel));
3132d522f475Smrg	break;
3133d522f475Smrg
3134d522f475Smrg    case Select_GROUP:		/* paragraph */
3135d522f475Smrg	TRACE(("Select_GROUP\n"));
3136956cc18dSsnj	if (okPosition(screen, &(ld.startSel), &(screen->startSel))) {
3137d522f475Smrg	    /* scan backward for beginning of group */
3138d522f475Smrg	    while (screen->startSel.row > 0 &&
3139956cc18dSsnj		   (LastTextCol(screen, ld.startSel, screen->startSel.row -
3140956cc18dSsnj				1) > 0 ||
3141956cc18dSsnj		    isPrevWrapped(startSel))) {
3142956cc18dSsnj		PrevRow(startSel);
3143d522f475Smrg	    }
3144d522f475Smrg	    screen->startSel.col = 0;
3145d522f475Smrg	    /* scan forward for end of group */
314620d2c4d2Smrg	    while (MoreRows(endSel) &&
3147956cc18dSsnj		   (LastTextCol(screen, ld.endSel, screen->endSel.row + 1) >
3148956cc18dSsnj		    0 ||
3149956cc18dSsnj		    LineTstWrapped(ld.endSel))) {
3150956cc18dSsnj		NextRow(endSel);
3151d522f475Smrg	    }
3152956cc18dSsnj	    trimLastLine(screen, &(ld.endSel), &(screen->endSel));
3153d522f475Smrg	}
3154d522f475Smrg	break;
3155d522f475Smrg
3156d522f475Smrg    case Select_PAGE:		/* everything one can see */
3157d522f475Smrg	TRACE(("Select_PAGE\n"));
3158d522f475Smrg	screen->startSel.row = 0;
3159d522f475Smrg	screen->startSel.col = 0;
316020d2c4d2Smrg	screen->endSel.row = MaxRows(screen);
3161d522f475Smrg	screen->endSel.col = 0;
3162d522f475Smrg	break;
3163d522f475Smrg
3164d522f475Smrg    case Select_ALL:		/* counts scrollback if in normal screen */
3165d522f475Smrg	TRACE(("Select_ALL\n"));
3166d522f475Smrg	screen->startSel.row = -screen->savedlines;
3167d522f475Smrg	screen->startSel.col = 0;
316820d2c4d2Smrg	screen->endSel.row = MaxRows(screen);
3169d522f475Smrg	screen->endSel.col = 0;
3170d522f475Smrg	break;
3171d522f475Smrg
3172d522f475Smrg#if OPT_SELECT_REGEX
3173d522f475Smrg    case Select_REGEX:
3174d522f475Smrg	do_select_regex(screen, &(screen->startSel), &(screen->endSel));
3175d522f475Smrg	break;
3176d522f475Smrg#endif
3177d522f475Smrg
3178d522f475Smrg    case NSELECTUNITS:		/* always ignore */
3179956cc18dSsnj	ignored = True;
3180956cc18dSsnj	break;
3181d522f475Smrg    }
3182d522f475Smrg
3183956cc18dSsnj    if (!ignored) {
3184956cc18dSsnj	/* check boundaries */
3185956cc18dSsnj	ScrollSelection(screen, 0, False);
3186956cc18dSsnj	TrackText(xw, &(screen->startSel), &(screen->endSel));
3187956cc18dSsnj    }
3188d522f475Smrg
3189d522f475Smrg    return;
3190d522f475Smrg}
3191d522f475Smrg
3192d522f475Smrg/* Guaranteed (first.row, first.col) <= (last.row, last.col) */
3193d522f475Smrgstatic void
3194d522f475SmrgTrackText(XtermWidget xw,
3195d522f475Smrg	  const CELL * firstp,
3196d522f475Smrg	  const CELL * lastp)
3197d522f475Smrg{
3198956cc18dSsnj    TScreen *screen = TScreenOf(xw);
3199d522f475Smrg    int from, to;
3200d522f475Smrg    CELL old_start, old_end;
3201d522f475Smrg    CELL first = *firstp;
3202d522f475Smrg    CELL last = *lastp;
3203d522f475Smrg
3204d522f475Smrg    TRACE(("TrackText(first=%d,%d, last=%d,%d)\n",
3205d522f475Smrg	   first.row, first.col, last.row, last.col));
3206d522f475Smrg
3207d522f475Smrg    old_start = screen->startH;
3208d522f475Smrg    old_end = screen->endH;
3209d522f475Smrg    if (isSameCELL(&first, &old_start) &&
3210d522f475Smrg	isSameCELL(&last, &old_end))
3211d522f475Smrg	return;
3212d522f475Smrg    screen->startH = first;
3213d522f475Smrg    screen->endH = last;
3214d522f475Smrg    from = Coordinate(screen, &screen->startH);
3215d522f475Smrg    to = Coordinate(screen, &screen->endH);
3216d522f475Smrg    if (to <= screen->startHCoord || from > screen->endHCoord) {
3217d522f475Smrg	/* No overlap whatsoever between old and new hilite */
3218d522f475Smrg	ReHiliteText(xw, &old_start, &old_end);
3219d522f475Smrg	ReHiliteText(xw, &first, &last);
3220d522f475Smrg    } else {
3221d522f475Smrg	if (from < screen->startHCoord) {
3222d522f475Smrg	    /* Extend left end */
3223d522f475Smrg	    ReHiliteText(xw, &first, &old_start);
3224d522f475Smrg	} else if (from > screen->startHCoord) {
3225d522f475Smrg	    /* Shorten left end */
3226d522f475Smrg	    ReHiliteText(xw, &old_start, &first);
3227d522f475Smrg	}
3228d522f475Smrg	if (to > screen->endHCoord) {
3229d522f475Smrg	    /* Extend right end */
3230d522f475Smrg	    ReHiliteText(xw, &old_end, &last);
3231d522f475Smrg	} else if (to < screen->endHCoord) {
3232d522f475Smrg	    /* Shorten right end */
3233d522f475Smrg	    ReHiliteText(xw, &last, &old_end);
3234d522f475Smrg	}
3235d522f475Smrg    }
3236d522f475Smrg    screen->startHCoord = from;
3237d522f475Smrg    screen->endHCoord = to;
3238d522f475Smrg}
3239d522f475Smrg
3240d522f475Smrg/* Guaranteed that (first->row, first->col) <= (last->row, last->col) */
3241d522f475Smrgstatic void
3242d522f475SmrgReHiliteText(XtermWidget xw,
3243d522f475Smrg	     CELL * firstp,
3244d522f475Smrg	     CELL * lastp)
3245d522f475Smrg{
3246956cc18dSsnj    TScreen *screen = TScreenOf(xw);
3247d522f475Smrg    int i;
3248d522f475Smrg    CELL first = *firstp;
3249d522f475Smrg    CELL last = *lastp;
3250d522f475Smrg
3251d522f475Smrg    TRACE(("ReHiliteText from %d.%d to %d.%d\n",
3252d522f475Smrg	   first.row, first.col, last.row, last.col));
3253d522f475Smrg
3254d522f475Smrg    if (first.row < 0)
3255d522f475Smrg	first.row = first.col = 0;
3256d522f475Smrg    else if (first.row > screen->max_row)
3257d522f475Smrg	return;			/* nothing to do, since last.row >= first.row */
3258d522f475Smrg
3259d522f475Smrg    if (last.row < 0)
3260d522f475Smrg	return;			/* nothing to do, since first.row <= last.row */
3261d522f475Smrg    else if (last.row > screen->max_row) {
3262d522f475Smrg	last.row = screen->max_row;
3263d522f475Smrg	last.col = MaxCols(screen);
3264d522f475Smrg    }
3265d522f475Smrg    if (isSameCELL(&first, &last))
3266d522f475Smrg	return;
3267d522f475Smrg
3268d522f475Smrg    if (!isSameRow(&first, &last)) {	/* do multiple rows */
3269d522f475Smrg	if ((i = screen->max_col - first.col + 1) > 0) {	/* first row */
3270d522f475Smrg	    ScrnRefresh(xw, first.row, first.col, 1, i, True);
3271d522f475Smrg	}
3272d522f475Smrg	if ((i = last.row - first.row - 1) > 0) {	/* middle rows */
3273d522f475Smrg	    ScrnRefresh(xw, first.row + 1, 0, i, MaxCols(screen), True);
3274d522f475Smrg	}
3275d522f475Smrg	if (last.col > 0 && last.row <= screen->max_row) {	/* last row */
3276d522f475Smrg	    ScrnRefresh(xw, last.row, 0, 1, last.col, True);
3277d522f475Smrg	}
3278d522f475Smrg    } else {			/* do single row */
3279d522f475Smrg	ScrnRefresh(xw, first.row, first.col, 1, last.col - first.col, True);
3280d522f475Smrg    }
3281d522f475Smrg}
3282d522f475Smrg
3283d522f475Smrg/*
3284d522f475Smrg * Guaranteed that (cellc->row, cellc->col) <= (cell->row, cell->col), and that both points are valid
3285d522f475Smrg * (may have cell->row = screen->max_row+1, cell->col = 0).
3286d522f475Smrg */
3287d522f475Smrgstatic void
3288d522f475SmrgSaltTextAway(XtermWidget xw,
3289d522f475Smrg	     CELL * cellc,
3290d522f475Smrg	     CELL * cell,
3291d522f475Smrg	     String * params,	/* selections */
3292d522f475Smrg	     Cardinal num_params)
3293d522f475Smrg{
3294956cc18dSsnj    TScreen *screen = TScreenOf(xw);
3295d522f475Smrg    int i, j = 0;
3296d522f475Smrg    int eol;
3297956cc18dSsnj    int tmp;
3298d522f475Smrg    Char *line;
3299d522f475Smrg    Char *lp;
3300d522f475Smrg    CELL first = *cellc;
3301d522f475Smrg    CELL last = *cell;
3302d522f475Smrg
3303d522f475Smrg    if (isSameRow(&first, &last) && first.col > last.col) {
3304956cc18dSsnj	EXCHANGE(first.col, last.col, tmp);
3305d522f475Smrg    }
3306d522f475Smrg
3307d522f475Smrg    --last.col;
3308d522f475Smrg    /* first we need to know how long the string is before we can save it */
3309d522f475Smrg
3310d522f475Smrg    if (isSameRow(&last, &first)) {
3311d522f475Smrg	j = Length(screen, first.row, first.col, last.col);
3312d522f475Smrg    } else {			/* two cases, cut is on same line, cut spans multiple lines */
3313d522f475Smrg	j += Length(screen, first.row, first.col, screen->max_col) + 1;
3314d522f475Smrg	for (i = first.row + 1; i < last.row; i++)
3315d522f475Smrg	    j += Length(screen, i, 0, screen->max_col) + 1;
3316d522f475Smrg	if (last.col >= 0)
3317d522f475Smrg	    j += Length(screen, last.row, 0, last.col);
3318d522f475Smrg    }
3319d522f475Smrg
3320d522f475Smrg    /* UTF-8 may require more space */
3321d522f475Smrg    if_OPT_WIDE_CHARS(screen, {
3322d522f475Smrg	j *= 4;
3323d522f475Smrg    });
3324d522f475Smrg
3325d522f475Smrg    /* now get some memory to save it in */
3326d522f475Smrg
3327d522f475Smrg    if (screen->selection_size <= j) {
332820d2c4d2Smrg	if ((line = (Char *) malloc((size_t) j + 1)) == 0)
3329d522f475Smrg	    SysError(ERROR_BMALLOC2);
3330d522f475Smrg	XtFree((char *) screen->selection_data);
3331d522f475Smrg	screen->selection_data = line;
3332d522f475Smrg	screen->selection_size = j + 1;
3333d522f475Smrg    } else {
3334d522f475Smrg	line = screen->selection_data;
3335d522f475Smrg    }
3336d522f475Smrg
3337d522f475Smrg    if ((line == 0)
3338d522f475Smrg	|| (j < 0))
3339d522f475Smrg	return;
3340d522f475Smrg
3341d522f475Smrg    line[j] = '\0';		/* make sure it is null terminated */
3342d522f475Smrg    lp = line;			/* lp points to where to save the text */
3343d522f475Smrg    if (isSameRow(&last, &first)) {
3344d522f475Smrg	lp = SaveText(screen, last.row, first.col, last.col, lp, &eol);
3345d522f475Smrg    } else {
3346d522f475Smrg	lp = SaveText(screen, first.row, first.col, screen->max_col, lp, &eol);
3347d522f475Smrg	if (eol)
3348d522f475Smrg	    *lp++ = '\n';	/* put in newline at end of line */
3349d522f475Smrg	for (i = first.row + 1; i < last.row; i++) {
3350d522f475Smrg	    lp = SaveText(screen, i, 0, screen->max_col, lp, &eol);
3351d522f475Smrg	    if (eol)
3352d522f475Smrg		*lp++ = '\n';
3353d522f475Smrg	}
3354d522f475Smrg	if (last.col >= 0)
3355d522f475Smrg	    lp = SaveText(screen, last.row, 0, last.col, lp, &eol);
3356d522f475Smrg    }
3357d522f475Smrg    *lp = '\0';			/* make sure we have end marked */
3358d522f475Smrg
3359956cc18dSsnj    TRACE(("Salted TEXT:%d:%s\n", (int) (lp - line),
3360956cc18dSsnj	   visibleChars(line, (unsigned) (lp - line))));
3361d522f475Smrg
33622eaa94a1Schristos    screen->selection_length = (unsigned long) (lp - line);
3363d522f475Smrg    _OwnSelection(xw, params, num_params);
3364d522f475Smrg}
3365d522f475Smrg
3366d522f475Smrg#if OPT_PASTE64
3367d522f475Smrgvoid
3368d522f475SmrgClearSelectionBuffer(TScreen * screen)
3369d522f475Smrg{
3370d522f475Smrg    screen->selection_length = 0;
3371d522f475Smrg    screen->base64_count = 0;
3372d522f475Smrg}
3373d522f475Smrg
3374d522f475Smrgstatic void
337520d2c4d2SmrgAppendStrToSelectionBuffer(TScreen * screen, Char * text, size_t len)
3376d522f475Smrg{
3377d522f475Smrg    if (len != 0) {
33782eaa94a1Schristos	int j = (int) (screen->selection_length + len);		/* New length */
3379d522f475Smrg	int k = j + (j >> 2) + 80;	/* New size if we grow buffer: grow by ~50% */
3380d522f475Smrg	if (j + 1 >= screen->selection_size) {
3381d522f475Smrg	    if (!screen->selection_length) {
3382d522f475Smrg		/* New buffer */
3383d522f475Smrg		Char *line;
338420d2c4d2Smrg		if ((line = (Char *) malloc((size_t) k)) == 0)
3385d522f475Smrg		    SysError(ERROR_BMALLOC2);
3386d522f475Smrg		XtFree((char *) screen->selection_data);
3387d522f475Smrg		screen->selection_data = line;
3388d522f475Smrg	    } else {
3389d522f475Smrg		/* Realloc buffer */
3390d522f475Smrg		screen->selection_data = (Char *)
3391d522f475Smrg		    realloc(screen->selection_data,
339220d2c4d2Smrg			    (size_t) k);
3393d522f475Smrg		if (screen->selection_data == 0)
3394d522f475Smrg		    SysError(ERROR_BMALLOC2);
3395d522f475Smrg	    }
3396d522f475Smrg	    screen->selection_size = k;
3397d522f475Smrg	}
339820d2c4d2Smrg	if (screen->selection_data != 0) {
339920d2c4d2Smrg	    memcpy(screen->selection_data + screen->selection_length, text, len);
340020d2c4d2Smrg	    screen->selection_length += len;
340120d2c4d2Smrg	    screen->selection_data[screen->selection_length] = 0;
340220d2c4d2Smrg	}
3403d522f475Smrg    }
3404d522f475Smrg}
3405d522f475Smrg
3406d522f475Smrgvoid
3407d522f475SmrgAppendToSelectionBuffer(TScreen * screen, unsigned c)
3408d522f475Smrg{
34092eaa94a1Schristos    unsigned six;
3410d522f475Smrg    Char ch;
3411d522f475Smrg
3412d522f475Smrg    /* Decode base64 character */
3413d522f475Smrg    if (c >= 'A' && c <= 'Z')
3414d522f475Smrg	six = c - 'A';
3415d522f475Smrg    else if (c >= 'a' && c <= 'z')
3416d522f475Smrg	six = c - 'a' + 26;
3417d522f475Smrg    else if (c >= '0' && c <= '9')
3418d522f475Smrg	six = c - '0' + 52;
3419d522f475Smrg    else if (c == '+')
3420d522f475Smrg	six = 62;
3421d522f475Smrg    else if (c == '/')
3422d522f475Smrg	six = 63;
3423d522f475Smrg    else
3424d522f475Smrg	return;
3425d522f475Smrg
3426d522f475Smrg    /* Accumulate bytes */
3427d522f475Smrg    switch (screen->base64_count) {
3428d522f475Smrg    case 0:
3429d522f475Smrg	screen->base64_accu = six;
3430d522f475Smrg	screen->base64_count = 6;
3431d522f475Smrg	break;
3432d522f475Smrg
3433d522f475Smrg    case 2:
34342eaa94a1Schristos	ch = CharOf((screen->base64_accu << 6) + six);
3435d522f475Smrg	screen->base64_count = 0;
343620d2c4d2Smrg	AppendStrToSelectionBuffer(screen, &ch, (size_t) 1);
3437d522f475Smrg	break;
3438d522f475Smrg
3439d522f475Smrg    case 4:
34402eaa94a1Schristos	ch = CharOf((screen->base64_accu << 4) + (six >> 2));
3441d522f475Smrg	screen->base64_accu = (six & 0x3);
3442d522f475Smrg	screen->base64_count = 2;
344320d2c4d2Smrg	AppendStrToSelectionBuffer(screen, &ch, (size_t) 1);
3444d522f475Smrg	break;
3445d522f475Smrg
3446d522f475Smrg    case 6:
34472eaa94a1Schristos	ch = CharOf((screen->base64_accu << 2) + (six >> 4));
3448d522f475Smrg	screen->base64_accu = (six & 0xF);
3449d522f475Smrg	screen->base64_count = 4;
345020d2c4d2Smrg	AppendStrToSelectionBuffer(screen, &ch, (size_t) 1);
3451d522f475Smrg	break;
3452d522f475Smrg    }
3453d522f475Smrg}
3454d522f475Smrg
3455d522f475Smrgvoid
345620d2c4d2SmrgCompleteSelection(XtermWidget xw, String * args, Cardinal len)
3457d522f475Smrg{
3458956cc18dSsnj    TScreen *screen = TScreenOf(xw);
3459956cc18dSsnj
3460956cc18dSsnj    screen->base64_count = 0;
3461956cc18dSsnj    screen->base64_accu = 0;
3462d522f475Smrg    _OwnSelection(xw, args, len);
3463d522f475Smrg}
3464d522f475Smrg#endif /* OPT_PASTE64 */
3465d522f475Smrg
3466d522f475Smrgstatic Bool
3467d522f475Smrg_ConvertSelectionHelper(Widget w,
3468d522f475Smrg			Atom * type,
3469d522f475Smrg			XtPointer *value,
3470d522f475Smrg			unsigned long *length,
3471d522f475Smrg			int *format,
3472d522f475Smrg			int (*conversion_function) (Display *,
3473d522f475Smrg						    char **, int,
3474d522f475Smrg						    XICCEncodingStyle,
3475d522f475Smrg						    XTextProperty *),
3476d522f475Smrg			XICCEncodingStyle conversion_style)
3477d522f475Smrg{
3478956cc18dSsnj    XtermWidget xw;
3479956cc18dSsnj
3480956cc18dSsnj    if ((xw = getXtermWidget(w)) != 0) {
3481956cc18dSsnj	TScreen *screen = TScreenOf(xw);
3482d522f475Smrg	Display *dpy = XtDisplay(w);
3483d522f475Smrg	XTextProperty textprop;
3484d522f475Smrg	char *the_data = (char *) screen->selection_data;
3485d522f475Smrg
3486d522f475Smrg	if (conversion_function(dpy, &the_data, 1,
3487d522f475Smrg				conversion_style,
3488d522f475Smrg				&textprop) >= Success) {
3489d522f475Smrg	    *value = (XtPointer) textprop.value;
3490d522f475Smrg	    *length = textprop.nitems;
3491d522f475Smrg	    *type = textprop.encoding;
3492d522f475Smrg	    *format = textprop.format;
3493d522f475Smrg	    return True;
3494d522f475Smrg	}
3495d522f475Smrg    }
3496d522f475Smrg    return False;
3497d522f475Smrg}
3498d522f475Smrg
34992eaa94a1Schristosstatic Boolean
35002eaa94a1SchristosSaveConvertedLength(XtPointer *target, unsigned long source)
35012eaa94a1Schristos{
35022eaa94a1Schristos    Boolean result = False;
35032eaa94a1Schristos
35042eaa94a1Schristos    *target = XtMalloc(4);
35052eaa94a1Schristos    if (*target != 0) {
35062eaa94a1Schristos	result = True;
35072eaa94a1Schristos	if (sizeof(unsigned long) == 4) {
35082eaa94a1Schristos	    *(unsigned long *) *target = source;
35092eaa94a1Schristos	} else if (sizeof(unsigned) == 4) {
351020d2c4d2Smrg	    *(unsigned *) *target = (unsigned) source;
35112eaa94a1Schristos	} else if (sizeof(unsigned short) == 4) {
35122eaa94a1Schristos	    *(unsigned short *) *target = (unsigned short) source;
35132eaa94a1Schristos	} else {
35142eaa94a1Schristos	    /* FIXME - does this depend on byte-order? */
35152eaa94a1Schristos	    unsigned long temp = source;
351620d2c4d2Smrg	    memcpy((char *) *target,
351720d2c4d2Smrg		   ((char *) &temp) + sizeof(temp) - 4,
351820d2c4d2Smrg		   (size_t) 4);
35192eaa94a1Schristos	}
35202eaa94a1Schristos    }
35212eaa94a1Schristos    return result;
35222eaa94a1Schristos}
35232eaa94a1Schristos
3524d522f475Smrgstatic Boolean
3525d522f475SmrgConvertSelection(Widget w,
3526d522f475Smrg		 Atom * selection,
3527d522f475Smrg		 Atom * target,
3528d522f475Smrg		 Atom * type,
3529d522f475Smrg		 XtPointer *value,
3530d522f475Smrg		 unsigned long *length,
3531d522f475Smrg		 int *format)
3532d522f475Smrg{
3533d522f475Smrg    Display *dpy = XtDisplay(w);
3534d522f475Smrg    TScreen *screen;
3535d522f475Smrg    Bool result = False;
3536d522f475Smrg
3537956cc18dSsnj    XtermWidget xw;
3538956cc18dSsnj
3539956cc18dSsnj    if ((xw = getXtermWidget(w)) == 0)
3540d522f475Smrg	return False;
3541d522f475Smrg
3542956cc18dSsnj    screen = TScreenOf(xw);
3543d522f475Smrg
3544d522f475Smrg    if (screen->selection_data == NULL)
3545d522f475Smrg	return False;		/* can this happen? */
3546d522f475Smrg
3547956cc18dSsnj    TRACE(("ConvertSelection %s\n",
3548956cc18dSsnj	   visibleSelectionTarget(dpy, *target)));
3549956cc18dSsnj
3550d522f475Smrg    if (*target == XA_TARGETS(dpy)) {
3551d522f475Smrg	Atom *allocP;
3552d522f475Smrg	Atom *targetP;
3553d522f475Smrg	Atom *std_targets;
3554d522f475Smrg	XPointer std_return = 0;
3555d522f475Smrg	unsigned long std_length;
3556d522f475Smrg
3557d522f475Smrg	if (XmuConvertStandardSelection(w, screen->selection_time, selection,
3558d522f475Smrg					target, type, &std_return,
3559d522f475Smrg					&std_length, format)) {
3560956cc18dSsnj	    Atom *my_targets = _SelectionTargets(w);
3561956cc18dSsnj
3562956cc18dSsnj	    TRACE(("XmuConvertStandardSelection - success\n"));
3563d522f475Smrg	    std_targets = (Atom *) (std_return);
3564d522f475Smrg	    *length = std_length + 6;
3565d522f475Smrg
3566956cc18dSsnj	    targetP = (Atom *) XtMalloc((Cardinal) (sizeof(Atom) * (*length)));
3567d522f475Smrg	    allocP = targetP;
3568d522f475Smrg
3569d522f475Smrg	    *value = (XtPointer) targetP;
3570d522f475Smrg
3571956cc18dSsnj	    while (*my_targets != None) {
3572956cc18dSsnj		*targetP++ = *my_targets++;
3573956cc18dSsnj	    }
3574d522f475Smrg	    *targetP++ = XA_LENGTH(dpy);
3575d522f475Smrg	    *targetP++ = XA_LIST_LENGTH(dpy);
3576d522f475Smrg
35772eaa94a1Schristos	    *length = std_length + (unsigned long) (targetP - allocP);
3578d522f475Smrg
3579d522f475Smrg	    memcpy(targetP, std_targets, sizeof(Atom) * std_length);
3580d522f475Smrg	    XtFree((char *) std_targets);
3581d522f475Smrg	    *type = XA_ATOM;
3582d522f475Smrg	    *format = 32;
3583d522f475Smrg	    result = True;
3584956cc18dSsnj	} else {
3585956cc18dSsnj	    TRACE(("XmuConvertStandardSelection - failed\n"));
3586d522f475Smrg	}
3587d522f475Smrg    }
3588d522f475Smrg#if OPT_WIDE_CHARS
3589d522f475Smrg    else if (screen->wide_chars && *target == XA_STRING) {
3590d522f475Smrg	result =
3591d522f475Smrg	    _ConvertSelectionHelper(w,
3592d522f475Smrg				    type, value, length, format,
3593d522f475Smrg				    Xutf8TextListToTextProperty,
3594d522f475Smrg				    XStringStyle);
3595956cc18dSsnj	TRACE(("...Xutf8TextListToTextProperty:%d\n", result));
3596d522f475Smrg    } else if (screen->wide_chars && *target == XA_UTF8_STRING(dpy)) {
3597d522f475Smrg	result =
3598d522f475Smrg	    _ConvertSelectionHelper(w,
3599d522f475Smrg				    type, value, length, format,
3600d522f475Smrg				    Xutf8TextListToTextProperty,
3601d522f475Smrg				    XUTF8StringStyle);
3602956cc18dSsnj	TRACE(("...Xutf8TextListToTextProperty:%d\n", result));
3603d522f475Smrg    } else if (screen->wide_chars && *target == XA_TEXT(dpy)) {
3604d522f475Smrg	result =
3605d522f475Smrg	    _ConvertSelectionHelper(w,
3606d522f475Smrg				    type, value, length, format,
3607d522f475Smrg				    Xutf8TextListToTextProperty,
3608d522f475Smrg				    XStdICCTextStyle);
3609956cc18dSsnj	TRACE(("...Xutf8TextListToTextProperty:%d\n", result));
3610d522f475Smrg    } else if (screen->wide_chars && *target == XA_COMPOUND_TEXT(dpy)) {
3611d522f475Smrg	result =
3612d522f475Smrg	    _ConvertSelectionHelper(w,
3613d522f475Smrg				    type, value, length, format,
3614d522f475Smrg				    Xutf8TextListToTextProperty,
3615d522f475Smrg				    XCompoundTextStyle);
3616956cc18dSsnj	TRACE(("...Xutf8TextListToTextProperty:%d\n", result));
3617d522f475Smrg    }
3618d522f475Smrg#endif
3619d522f475Smrg
3620d522f475Smrg    else if (*target == XA_STRING) {	/* not wide_chars */
3621d522f475Smrg	/* We can only reach this point if the selection requestor
3622d522f475Smrg	   requested STRING before any of TEXT, COMPOUND_TEXT or
3623d522f475Smrg	   UTF8_STRING.  We therefore assume that the requestor is not
3624d522f475Smrg	   properly internationalised, and dump raw eight-bit data
3625d522f475Smrg	   with no conversion into the selection.  Yes, this breaks
3626d522f475Smrg	   the ICCCM in non-Latin-1 locales. */
3627d522f475Smrg	*type = XA_STRING;
3628d522f475Smrg	*value = (XtPointer) screen->selection_data;
3629d522f475Smrg	*length = screen->selection_length;
3630d522f475Smrg	*format = 8;
3631d522f475Smrg	result = True;
3632956cc18dSsnj	TRACE(("...raw 8-bit data:%d\n", result));
3633d522f475Smrg    } else if (*target == XA_TEXT(dpy)) {	/* not wide_chars */
3634d522f475Smrg	result =
3635d522f475Smrg	    _ConvertSelectionHelper(w,
3636d522f475Smrg				    type, value, length, format,
3637d522f475Smrg				    XmbTextListToTextProperty,
3638d522f475Smrg				    XStdICCTextStyle);
3639956cc18dSsnj	TRACE(("...XmbTextListToTextProperty(StdICC):%d\n", result));
3640d522f475Smrg    } else if (*target == XA_COMPOUND_TEXT(dpy)) {	/* not wide_chars */
3641d522f475Smrg	result =
3642d522f475Smrg	    _ConvertSelectionHelper(w,
3643d522f475Smrg				    type, value, length, format,
3644d522f475Smrg				    XmbTextListToTextProperty,
3645d522f475Smrg				    XCompoundTextStyle);
3646956cc18dSsnj	TRACE(("...XmbTextListToTextProperty(Compound):%d\n", result));
3647d522f475Smrg    }
3648d522f475Smrg#ifdef X_HAVE_UTF8_STRING
3649d522f475Smrg    else if (*target == XA_UTF8_STRING(dpy)) {	/* not wide_chars */
3650d522f475Smrg	result =
3651d522f475Smrg	    _ConvertSelectionHelper(w,
3652d522f475Smrg				    type, value, length, format,
3653d522f475Smrg				    XmbTextListToTextProperty,
3654d522f475Smrg				    XUTF8StringStyle);
3655956cc18dSsnj	TRACE(("...XmbTextListToTextProperty(UTF8):%d\n", result));
3656d522f475Smrg    }
3657d522f475Smrg#endif
3658d522f475Smrg    else if (*target == XA_LIST_LENGTH(dpy)) {
365920d2c4d2Smrg	result = SaveConvertedLength(value, (unsigned long) 1);
3660d522f475Smrg	*type = XA_INTEGER;
3661d522f475Smrg	*length = 1;
3662d522f475Smrg	*format = 32;
3663956cc18dSsnj	TRACE(("...list of values:%d\n", result));
3664d522f475Smrg    } else if (*target == XA_LENGTH(dpy)) {
3665d522f475Smrg	/* This value is wrong if we have UTF-8 text */
36662eaa94a1Schristos	result = SaveConvertedLength(value, screen->selection_length);
3667d522f475Smrg	*type = XA_INTEGER;
3668d522f475Smrg	*length = 1;
3669d522f475Smrg	*format = 32;
3670956cc18dSsnj	TRACE(("...list of values:%d\n", result));
3671d522f475Smrg    } else if (XmuConvertStandardSelection(w,
3672d522f475Smrg					   screen->selection_time, selection,
3673d522f475Smrg					   target, type, (XPointer *) value,
3674d522f475Smrg					   length, format)) {
3675d522f475Smrg	result = True;
3676956cc18dSsnj	TRACE(("...XmuConvertStandardSelection:%d\n", result));
3677d522f475Smrg    }
3678d522f475Smrg
3679d522f475Smrg    /* else */
36802eaa94a1Schristos    return (Boolean) result;
3681d522f475Smrg}
3682d522f475Smrg
3683d522f475Smrgstatic void
3684d522f475SmrgLoseSelection(Widget w, Atom * selection)
3685d522f475Smrg{
3686d522f475Smrg    TScreen *screen;
3687d522f475Smrg    Atom *atomP;
3688d522f475Smrg    Cardinal i;
3689d522f475Smrg
3690956cc18dSsnj    XtermWidget xw;
3691956cc18dSsnj
3692956cc18dSsnj    if ((xw = getXtermWidget(w)) == 0)
3693d522f475Smrg	return;
3694d522f475Smrg
3695956cc18dSsnj    screen = TScreenOf(xw);
3696d522f475Smrg    for (i = 0, atomP = screen->selection_atoms;
3697d522f475Smrg	 i < screen->selection_count; i++, atomP++) {
3698d522f475Smrg	if (*selection == *atomP)
3699d522f475Smrg	    *atomP = (Atom) 0;
3700d522f475Smrg	if (CutBuffer(*atomP) >= 0) {
3701d522f475Smrg	    *atomP = (Atom) 0;
3702d522f475Smrg	}
3703d522f475Smrg    }
3704d522f475Smrg
3705d522f475Smrg    for (i = screen->selection_count; i; i--) {
3706d522f475Smrg	if (screen->selection_atoms[i - 1] != 0)
3707d522f475Smrg	    break;
3708d522f475Smrg    }
3709d522f475Smrg    screen->selection_count = i;
3710d522f475Smrg
3711d522f475Smrg    for (i = 0, atomP = screen->selection_atoms;
3712d522f475Smrg	 i < screen->selection_count; i++, atomP++) {
3713d522f475Smrg	if (*atomP == (Atom) 0) {
3714d522f475Smrg	    *atomP = screen->selection_atoms[--screen->selection_count];
3715d522f475Smrg	}
3716d522f475Smrg    }
3717d522f475Smrg
3718d522f475Smrg    if (screen->selection_count == 0)
3719956cc18dSsnj	TrackText(xw, &zeroCELL, &zeroCELL);
3720d522f475Smrg}
3721d522f475Smrg
3722d522f475Smrg/* ARGSUSED */
3723d522f475Smrgstatic void
3724d522f475SmrgSelectionDone(Widget w GCC_UNUSED,
3725d522f475Smrg	      Atom * selection GCC_UNUSED,
3726d522f475Smrg	      Atom * target GCC_UNUSED)
3727d522f475Smrg{
3728d522f475Smrg    /* empty proc so Intrinsics know we want to keep storage */
3729d522f475Smrg}
3730d522f475Smrg
3731d522f475Smrgstatic void
3732d522f475Smrg_OwnSelection(XtermWidget xw,
3733d522f475Smrg	      String * selections,
3734d522f475Smrg	      Cardinal count)
3735d522f475Smrg{
3736956cc18dSsnj    TScreen *screen = TScreenOf(xw);
3737d522f475Smrg    Atom *atoms = screen->selection_atoms;
3738d522f475Smrg    Cardinal i;
3739d522f475Smrg    Bool have_selection = False;
3740d522f475Smrg
374120d2c4d2Smrg    if (count == 0)
374220d2c4d2Smrg	return;
37432eaa94a1Schristos    if (screen->selection_length == 0)
3744d522f475Smrg	return;
3745d522f475Smrg
3746956cc18dSsnj    TRACE(("_OwnSelection count %d\n", count));
3747d522f475Smrg    selections = MapSelections(xw, selections, count);
3748d522f475Smrg
3749d522f475Smrg    if (count > screen->sel_atoms_size) {
3750d522f475Smrg	XtFree((char *) atoms);
3751956cc18dSsnj	atoms = (Atom *) XtMalloc((Cardinal) (count * sizeof(Atom)));
3752d522f475Smrg	screen->selection_atoms = atoms;
3753d522f475Smrg	screen->sel_atoms_size = count;
3754d522f475Smrg    }
3755d522f475Smrg    XmuInternStrings(XtDisplay((Widget) xw), selections, count, atoms);
3756d522f475Smrg    for (i = 0; i < count; i++) {
3757d522f475Smrg	int cutbuffer = CutBuffer(atoms[i]);
3758d522f475Smrg	if (cutbuffer >= 0) {
37592eaa94a1Schristos	    unsigned long limit =
37602eaa94a1Schristos	    (unsigned long) (4 * XMaxRequestSize(XtDisplay((Widget) xw)) - 32);
37612eaa94a1Schristos	    if (screen->selection_length > limit) {
376220d2c4d2Smrg		TRACE(("selection too big (%lu bytes), not storing in CUT_BUFFER%d\n",
376320d2c4d2Smrg		       screen->selection_length, cutbuffer));
3764d522f475Smrg		fprintf(stderr,
3765956cc18dSsnj			"%s: selection too big (%lu bytes), not storing in CUT_BUFFER%d\n",
3766d522f475Smrg			xterm_name, screen->selection_length, cutbuffer);
3767d522f475Smrg	    } else {
3768d522f475Smrg		/* This used to just use the UTF-8 data, which was totally
3769d522f475Smrg		 * broken as not even the corresponding paste code in Xterm
3770d522f475Smrg		 * understood this!  So now it converts to Latin1 first.
3771d522f475Smrg		 *   Robert Brady, 2000-09-05
3772d522f475Smrg		 */
3773d522f475Smrg		unsigned long length = screen->selection_length;
3774d522f475Smrg		Char *data = screen->selection_data;
3775d522f475Smrg		if_OPT_WIDE_CHARS((screen), {
3776956cc18dSsnj		    data = UTF8toLatin1(screen, data, length, &length);
3777d522f475Smrg		});
3778d522f475Smrg		TRACE(("XStoreBuffer(%d)\n", cutbuffer));
3779d522f475Smrg		XStoreBuffer(XtDisplay((Widget) xw),
3780d522f475Smrg			     (char *) data,
3781d522f475Smrg			     (int) length,
3782d522f475Smrg			     cutbuffer);
3783d522f475Smrg	    }
3784d522f475Smrg	} else if (!screen->replyToEmacs) {
3785d522f475Smrg	    have_selection |=
3786d522f475Smrg		XtOwnSelection((Widget) xw, atoms[i],
3787d522f475Smrg			       screen->selection_time,
3788d522f475Smrg			       ConvertSelection, LoseSelection, SelectionDone);
3789d522f475Smrg	}
3790d522f475Smrg    }
3791d522f475Smrg    if (!screen->replyToEmacs)
3792d522f475Smrg	screen->selection_count = count;
3793d522f475Smrg    if (!have_selection)
3794d522f475Smrg	TrackText(xw, &zeroCELL, &zeroCELL);
3795d522f475Smrg}
3796d522f475Smrg
3797d522f475Smrgstatic void
3798d522f475SmrgResetSelectionState(TScreen * screen)
3799d522f475Smrg{
3800d522f475Smrg    screen->selection_count = 0;
3801d522f475Smrg    screen->startH = zeroCELL;
3802d522f475Smrg    screen->endH = zeroCELL;
3803d522f475Smrg}
3804d522f475Smrg
3805d522f475Smrgvoid
3806d522f475SmrgDisownSelection(XtermWidget xw)
3807d522f475Smrg{
3808956cc18dSsnj    TScreen *screen = TScreenOf(xw);
3809d522f475Smrg    Atom *atoms = screen->selection_atoms;
3810d522f475Smrg    Cardinal count = screen->selection_count;
3811d522f475Smrg    Cardinal i;
3812d522f475Smrg
3813d522f475Smrg    TRACE(("DisownSelection count %d, start %d.%d, end %d.%d\n",
3814d522f475Smrg	   count,
3815d522f475Smrg	   screen->startH.row,
3816d522f475Smrg	   screen->startH.col,
3817d522f475Smrg	   screen->endH.row,
3818d522f475Smrg	   screen->endH.col));
3819d522f475Smrg
3820d522f475Smrg    for (i = 0; i < count; i++) {
3821d522f475Smrg	int cutbuffer = CutBuffer(atoms[i]);
3822d522f475Smrg	if (cutbuffer < 0) {
3823d522f475Smrg	    XtDisownSelection((Widget) xw, atoms[i],
3824d522f475Smrg			      screen->selection_time);
3825d522f475Smrg	}
3826d522f475Smrg    }
3827d522f475Smrg    /*
3828d522f475Smrg     * If none of the callbacks via XtDisownSelection() reset highlighting
3829d522f475Smrg     * do it now.
3830d522f475Smrg     */
3831d522f475Smrg    if (ScrnHaveSelection(screen)) {
3832d522f475Smrg	/* save data which will be reset */
3833d522f475Smrg	CELL first = screen->startH;
3834d522f475Smrg	CELL last = screen->endH;
3835d522f475Smrg
3836d522f475Smrg	ResetSelectionState(screen);
3837d522f475Smrg	ReHiliteText(xw, &first, &last);
3838d522f475Smrg    } else {
3839d522f475Smrg	ResetSelectionState(screen);
3840d522f475Smrg    }
3841d522f475Smrg}
3842d522f475Smrg
3843d522f475Smrgvoid
3844d522f475SmrgUnhiliteSelection(XtermWidget xw)
3845d522f475Smrg{
3846956cc18dSsnj    TScreen *screen = TScreenOf(xw);
3847d522f475Smrg
3848d522f475Smrg    if (ScrnHaveSelection(screen)) {
3849d522f475Smrg	CELL first = screen->startH;
3850d522f475Smrg	CELL last = screen->endH;
3851d522f475Smrg
3852d522f475Smrg	screen->startH = zeroCELL;
3853d522f475Smrg	screen->endH = zeroCELL;
3854d522f475Smrg	ReHiliteText(xw, &first, &last);
3855d522f475Smrg    }
3856d522f475Smrg}
3857d522f475Smrg
3858d522f475Smrg/* returns number of chars in line from scol to ecol out */
3859d522f475Smrg/* ARGSUSED */
3860d522f475Smrgstatic int
3861956cc18dSsnjLength(TScreen * screen,
3862d522f475Smrg       int row,
3863d522f475Smrg       int scol,
3864d522f475Smrg       int ecol)
3865d522f475Smrg{
3866956cc18dSsnj    LineData *ld = GET_LINEDATA(screen, row);
3867956cc18dSsnj    int lastcol = LastTextCol(screen, ld, row);
3868d522f475Smrg
3869d522f475Smrg    if (ecol > lastcol)
3870d522f475Smrg	ecol = lastcol;
3871d522f475Smrg    return (ecol - scol + 1);
3872d522f475Smrg}
3873d522f475Smrg
3874d522f475Smrg/* copies text into line, preallocated */
3875d522f475Smrgstatic Char *
3876d522f475SmrgSaveText(TScreen * screen,
3877d522f475Smrg	 int row,
3878d522f475Smrg	 int scol,
3879d522f475Smrg	 int ecol,
3880d522f475Smrg	 Char * lp,		/* pointer to where to put the text */
3881d522f475Smrg	 int *eol)
3882d522f475Smrg{
3883956cc18dSsnj    LineData *ld;
3884d522f475Smrg    int i = 0;
3885d522f475Smrg    unsigned c;
3886d522f475Smrg    Char *result = lp;
3887d522f475Smrg#if OPT_WIDE_CHARS
38882eaa94a1Schristos    unsigned previous = 0;
3889d522f475Smrg#endif
3890d522f475Smrg
3891956cc18dSsnj    ld = GET_LINEDATA(screen, row);
3892d522f475Smrg    i = Length(screen, row, scol, ecol);
3893d522f475Smrg    ecol = scol + i;
3894d522f475Smrg#if OPT_DEC_CHRSET
3895956cc18dSsnj    if (CSET_DOUBLE(GetLineDblCS(ld))) {
3896d522f475Smrg	scol = (scol + 0) / 2;
3897d522f475Smrg	ecol = (ecol + 1) / 2;
3898d522f475Smrg    }
3899d522f475Smrg#endif
3900956cc18dSsnj    *eol = !LineTstWrapped(ld);
3901d522f475Smrg    for (i = scol; i < ecol; i++) {
390220d2c4d2Smrg	assert(i < ld->lineSize);
3903956cc18dSsnj	c = E2A(ld->charData[i]);
3904d522f475Smrg#if OPT_WIDE_CHARS
3905d522f475Smrg	/* We want to strip out every occurrence of HIDDEN_CHAR AFTER a
3906d522f475Smrg	 * wide character.
3907d522f475Smrg	 */
3908956cc18dSsnj	if (c == HIDDEN_CHAR && isWide((int) previous)) {
3909d522f475Smrg	    previous = c;
3910d522f475Smrg	    /* Combining characters attached to double-width characters
3911d522f475Smrg	       are in memory attached to the HIDDEN_CHAR */
3912d522f475Smrg	    if_OPT_WIDE_CHARS(screen, {
3913d522f475Smrg		if (screen->utf8_mode != uFalse) {
3914d522f475Smrg		    unsigned ch;
3915956cc18dSsnj		    size_t off;
3916956cc18dSsnj		    for_each_combData(off, ld) {
3917956cc18dSsnj			ch = ld->combData[off][i];
3918956cc18dSsnj			if (ch == 0)
3919d522f475Smrg			    break;
3920d522f475Smrg			lp = convertToUTF8(lp, ch);
3921d522f475Smrg		    }
3922d522f475Smrg		}
3923d522f475Smrg	    });
3924d522f475Smrg	    continue;
3925d522f475Smrg	}
3926d522f475Smrg	previous = c;
3927d522f475Smrg	if (screen->utf8_mode != uFalse) {
3928d522f475Smrg	    lp = convertToUTF8(lp, (c != 0) ? c : ' ');
3929d522f475Smrg	    if_OPT_WIDE_CHARS(screen, {
3930d522f475Smrg		unsigned ch;
3931956cc18dSsnj		size_t off;
3932956cc18dSsnj		for_each_combData(off, ld) {
3933956cc18dSsnj		    ch = ld->combData[off][i];
3934956cc18dSsnj		    if (ch == 0)
3935d522f475Smrg			break;
3936d522f475Smrg		    lp = convertToUTF8(lp, ch);
3937d522f475Smrg		}
3938d522f475Smrg	    });
3939d522f475Smrg	} else
3940d522f475Smrg#endif
3941d522f475Smrg	{
3942d522f475Smrg	    if (c == 0) {
3943d522f475Smrg		c = E2A(' ');
3944d522f475Smrg	    } else if (c < E2A(' ')) {
3945d522f475Smrg		c = DECtoASCII(c);
3946d522f475Smrg	    } else if (c == 0x7f) {
3947d522f475Smrg		c = 0x5f;
3948d522f475Smrg	    }
39492eaa94a1Schristos	    *lp++ = CharOf(A2E(c));
3950d522f475Smrg	}
3951d522f475Smrg	if (c != E2A(' '))
3952d522f475Smrg	    result = lp;
3953d522f475Smrg    }
3954d522f475Smrg
3955d522f475Smrg    /*
3956d522f475Smrg     * If requested, trim trailing blanks from selected lines.  Do not do this
3957d522f475Smrg     * if the line is wrapped.
3958d522f475Smrg     */
3959d522f475Smrg    if (!*eol || !screen->trim_selection)
3960d522f475Smrg	result = lp;
3961d522f475Smrg
3962d522f475Smrg    return (result);
3963d522f475Smrg}
3964d522f475Smrg
3965d522f475Smrg/* 32 + following 7-bit word:
3966d522f475Smrg
3967d522f475Smrg   1:0  Button no: 0, 1, 2.  3=release.
3968d522f475Smrg     2  shift
3969d522f475Smrg     3  meta
3970d522f475Smrg     4  ctrl
3971d522f475Smrg     5  set for motion notify
3972d522f475Smrg     6  set for wheel
3973d522f475Smrg*/
3974d522f475Smrg
3975d522f475Smrg/* Position: 32 - 255. */
3976d522f475Smrg
39772eaa94a1Schristosstatic Char
3978d522f475SmrgBtnCode(XButtonEvent * event, int button)
3979d522f475Smrg{
39802eaa94a1Schristos    int result = (int) (32 + (KeyState(event->state) << 2));
3981d522f475Smrg
3982d522f475Smrg    if (button < 0 || button > 5) {
3983d522f475Smrg	result += 3;
3984d522f475Smrg    } else {
3985d522f475Smrg	if (button > 3)
3986d522f475Smrg	    result += (64 - 4);
3987d522f475Smrg	if (event->type == MotionNotify)
3988d522f475Smrg	    result += 32;
3989d522f475Smrg	result += button;
3990d522f475Smrg    }
39912eaa94a1Schristos    return CharOf(result);
3992d522f475Smrg}
3993d522f475Smrg
3994d522f475Smrg#define MOUSE_LIMIT (255 - 32)
3995d522f475Smrg
3996d522f475Smrgstatic void
3997d522f475SmrgEditorButton(XtermWidget xw, XButtonEvent * event)
3998d522f475Smrg{
3999956cc18dSsnj    TScreen *screen = TScreenOf(xw);
4000d522f475Smrg    int pty = screen->respond;
4001d522f475Smrg    Char line[6];
4002d522f475Smrg    int row, col;
4003d522f475Smrg    int button;
4004d522f475Smrg    unsigned count = 0;
4005d522f475Smrg    Boolean changed = True;
4006d522f475Smrg
4007d522f475Smrg    /* If button event, get button # adjusted for DEC compatibility */
40082eaa94a1Schristos    button = (int) (event->button - 1);
4009d522f475Smrg    if (button >= 3)
4010d522f475Smrg	button++;
4011d522f475Smrg
4012d522f475Smrg    /* Compute character position of mouse pointer */
4013d522f475Smrg    row = (event->y - screen->border) / FontHeight(screen);
4014d522f475Smrg    col = (event->x - OriginX(screen)) / FontWidth(screen);
4015d522f475Smrg
4016d522f475Smrg    /* Limit to screen dimensions */
4017d522f475Smrg    if (row < 0)
4018d522f475Smrg	row = 0;
4019d522f475Smrg    else if (row > screen->max_row)
4020d522f475Smrg	row = screen->max_row;
4021d522f475Smrg    else if (row > MOUSE_LIMIT)
4022d522f475Smrg	row = MOUSE_LIMIT;
4023d522f475Smrg
4024d522f475Smrg    if (col < 0)
4025d522f475Smrg	col = 0;
4026d522f475Smrg    else if (col > screen->max_col)
4027d522f475Smrg	col = screen->max_col;
4028d522f475Smrg    else if (col > MOUSE_LIMIT)
4029d522f475Smrg	col = MOUSE_LIMIT;
4030d522f475Smrg
4031d522f475Smrg    /* Build key sequence starting with \E[M */
4032d522f475Smrg    if (screen->control_eight_bits) {
4033d522f475Smrg	line[count++] = ANSI_CSI;
4034d522f475Smrg    } else {
4035d522f475Smrg	line[count++] = ANSI_ESC;
4036d522f475Smrg	line[count++] = '[';
4037d522f475Smrg    }
4038d522f475Smrg#if OPT_SCO_FUNC_KEYS
4039d522f475Smrg    if (xw->keyboard.type == keyboardIsSCO) {
4040d522f475Smrg	/*
4041d522f475Smrg	 * SCO function key F1 is \E[M, which would conflict with xterm's
4042d522f475Smrg	 * normal kmous.
4043d522f475Smrg	 */
4044d522f475Smrg	line[count++] = '>';
4045d522f475Smrg    }
4046d522f475Smrg#endif
4047d522f475Smrg    line[count++] = 'M';
4048d522f475Smrg
4049d522f475Smrg    /* Add event code to key sequence */
4050d522f475Smrg    if (screen->send_mouse_pos == X10_MOUSE) {
40512eaa94a1Schristos	line[count++] = CharOf(' ' + button);
4052d522f475Smrg    } else {
4053d522f475Smrg	/* Button-Motion events */
4054d522f475Smrg	switch (event->type) {
4055d522f475Smrg	case ButtonPress:
4056d522f475Smrg	    line[count++] = BtnCode(event, screen->mouse_button = button);
4057d522f475Smrg	    break;
4058d522f475Smrg	case ButtonRelease:
4059d522f475Smrg	    /*
4060d522f475Smrg	     * Wheel mouse interface generates release-events for buttons
4061d522f475Smrg	     * 4 and 5, coded here as 3 and 4 respectively.  We change the
4062d522f475Smrg	     * release for buttons 1..3 to a -1.
4063d522f475Smrg	     */
4064d522f475Smrg	    if (button < 3)
4065d522f475Smrg		button = -1;
4066d522f475Smrg	    line[count++] = BtnCode(event, screen->mouse_button = button);
4067d522f475Smrg	    break;
4068d522f475Smrg	case MotionNotify:
4069d522f475Smrg	    /* BTN_EVENT_MOUSE and ANY_EVENT_MOUSE modes send motion
4070d522f475Smrg	     * events only if character cell has changed.
4071d522f475Smrg	     */
4072d522f475Smrg	    if ((row == screen->mouse_row)
4073d522f475Smrg		&& (col == screen->mouse_col)) {
4074d522f475Smrg		changed = False;
4075d522f475Smrg	    } else {
4076d522f475Smrg		line[count++] = BtnCode(event, screen->mouse_button);
4077d522f475Smrg	    }
4078d522f475Smrg	    break;
4079d522f475Smrg	default:
4080d522f475Smrg	    changed = False;
4081d522f475Smrg	    break;
4082d522f475Smrg	}
4083d522f475Smrg    }
4084d522f475Smrg
4085d522f475Smrg    if (changed) {
4086d522f475Smrg	screen->mouse_row = row;
4087d522f475Smrg	screen->mouse_col = col;
4088d522f475Smrg
4089d522f475Smrg	/* Add pointer position to key sequence */
40902eaa94a1Schristos	line[count++] = CharOf(' ' + col + 1);
40912eaa94a1Schristos	line[count++] = CharOf(' ' + row + 1);
4092d522f475Smrg
4093d522f475Smrg	TRACE(("mouse at %d,%d button+mask = %#x\n", row, col,
4094d522f475Smrg	       (screen->control_eight_bits) ? line[2] : line[3]));
4095d522f475Smrg
4096d522f475Smrg	/* Transmit key sequence to process running under xterm */
4097d522f475Smrg	v_write(pty, line, count);
4098d522f475Smrg    }
4099d522f475Smrg    return;
4100d522f475Smrg}
4101d522f475Smrg
4102d522f475Smrg#if OPT_FOCUS_EVENT
4103d522f475Smrgvoid
4104d522f475SmrgSendFocusButton(XtermWidget xw, XFocusChangeEvent * event)
4105d522f475Smrg{
4106956cc18dSsnj    TScreen *screen = TScreenOf(xw);
4107d522f475Smrg
4108d522f475Smrg    if (screen->send_focus_pos) {
4109d522f475Smrg	ANSI reply;
4110d522f475Smrg
4111d522f475Smrg	memset(&reply, 0, sizeof(reply));
4112d522f475Smrg	reply.a_type = ANSI_CSI;
4113d522f475Smrg
4114d522f475Smrg#if OPT_SCO_FUNC_KEYS
4115d522f475Smrg	if (xw->keyboard.type == keyboardIsSCO) {
4116d522f475Smrg	    reply.a_pintro = '>';
4117d522f475Smrg	}
4118d522f475Smrg#endif
41192eaa94a1Schristos	reply.a_final = CharOf((event->type == FocusIn) ? 'I' : 'O');
4120d522f475Smrg	unparseseq(xw, &reply);
4121d522f475Smrg    }
4122d522f475Smrg    return;
4123d522f475Smrg}
4124d522f475Smrg#endif /* OPT_FOCUS_EVENT */
4125