button.c revision d522f475
1d522f475Smrg/* $XTermId: button.c,v 1.285 2008/02/24 19:42:02 tom Exp $ */
2d522f475Smrg
3d522f475Smrg/*
4d522f475Smrg * Copyright 1999-2007,2008 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>
65d522f475Smrg
66d522f475Smrg#include <X11/Xatom.h>
67d522f475Smrg#include <X11/Xmu/Atoms.h>
68d522f475Smrg#include <X11/Xmu/StdSel.h>
69d522f475Smrg
70d522f475Smrg#include <xutf8.h>
71d522f475Smrg#include <fontutils.h>
72d522f475Smrg
73d522f475Smrg#include <data.h>
74d522f475Smrg#include <error.h>
75d522f475Smrg#include <menu.h>
76d522f475Smrg#include <xcharmouse.h>
77d522f475Smrg#include <charclass.h>
78d522f475Smrg#include <xstrings.h>
79d522f475Smrg
80d522f475Smrg#if OPT_SELECT_REGEX
81d522f475Smrg#ifdef HAVE_PCREPOSIX_H
82d522f475Smrg#include <pcreposix.h>
83d522f475Smrg#else /* POSIX regex.h */
84d522f475Smrg#include <sys/types.h>
85d522f475Smrg#include <regex.h>
86d522f475Smrg#endif
87d522f475Smrg#endif
88d522f475Smrg
89d522f475Smrg#if OPT_WIDE_CHARS
90d522f475Smrg#include <ctype.h>
91d522f475Smrg#include <wcwidth.h>
92d522f475Smrg#else
93d522f475Smrg#define CharacterClass(value) \
94d522f475Smrg	charClass[value & ((sizeof(charClass)/sizeof(charClass[0]))-1)]
95d522f475Smrg#endif
96d522f475Smrg
97d522f475Smrg      /*
98d522f475Smrg       * We reserve shift modifier for cut/paste operations.  In principle we
99d522f475Smrg       * can pass through control and meta modifiers, but in practice, the
100d522f475Smrg       * popup menu uses control, and the window manager is likely to use meta,
101d522f475Smrg       * so those events are not delivered to SendMousePosition.
102d522f475Smrg       */
103d522f475Smrg#define OurModifiers (ShiftMask | ControlMask | Mod1Mask)
104d522f475Smrg#define AllModifiers (ShiftMask | LockMask | ControlMask | Mod1Mask | \
105d522f475Smrg		      Mod2Mask | Mod3Mask | Mod4Mask | Mod5Mask)
106d522f475Smrg
107d522f475Smrg#define KeyModifiers (event->xbutton.state & OurModifiers)
108d522f475Smrg
109d522f475Smrg#define KeyState(x) (((x) & (ShiftMask|ControlMask)) + (((x) & Mod1Mask) ? 2 : 0))
110d522f475Smrg    /* adds together the bits:
111d522f475Smrg       shift key -> 1
112d522f475Smrg       meta key  -> 2
113d522f475Smrg       control key -> 4 */
114d522f475Smrg
115d522f475Smrg#define	Coordinate(s,c)	((c)->row * MaxCols(s) + (c)->col)
116d522f475Smrg
117d522f475Smrgstatic const CELL zeroCELL =
118d522f475Smrg{0, 0};
119d522f475Smrg
120d522f475Smrg#if OPT_DEC_LOCATOR
121d522f475Smrgstatic Bool SendLocatorPosition(XtermWidget xw, XEvent * event);
122d522f475Smrgstatic void CheckLocatorPosition(XtermWidget xw, XEvent * event);
123d522f475Smrg#endif /* OPT_DEC_LOCATOR */
124d522f475Smrg
125d522f475Smrg/* Multi-click handling */
126d522f475Smrg#if OPT_READLINE
127d522f475Smrgstatic Time lastButtonDownTime = 0;
128d522f475Smrgstatic int ExtendingSelection = 0;
129d522f475Smrgstatic Time lastButton3UpTime = 0;
130d522f475Smrgstatic Time lastButton3DoubleDownTime = 0;
131d522f475Smrgstatic CELL lastButton3;	/* At the release time */
132d522f475Smrg#endif /* OPT_READLINE */
133d522f475Smrg
134d522f475Smrgstatic Char *SaveText(TScreen * screen, int row, int scol, int ecol,
135d522f475Smrg		      Char * lp, int *eol);
136d522f475Smrgstatic int Length(TScreen * screen, int row, int scol, int ecol);
137d522f475Smrgstatic void ComputeSelect(XtermWidget xw, CELL * startc, CELL * endc, Bool extend);
138d522f475Smrgstatic void EditorButton(XtermWidget xw, XButtonEvent * event);
139d522f475Smrgstatic void EndExtend(XtermWidget w, XEvent * event, String * params, Cardinal
140d522f475Smrg		      num_params, Bool use_cursor_loc);
141d522f475Smrgstatic void ExtendExtend(XtermWidget xw, const CELL * cell);
142d522f475Smrgstatic void PointToCELL(TScreen * screen, int y, int x, CELL * cell);
143d522f475Smrgstatic void ReHiliteText(XtermWidget xw, CELL * first, CELL * last);
144d522f475Smrgstatic void SaltTextAway(XtermWidget xw, CELL * cellc, CELL * cell,
145d522f475Smrg			 String * params, Cardinal num_params);
146d522f475Smrgstatic void SelectSet(XtermWidget xw, XEvent * event, String * params, Cardinal num_params);
147d522f475Smrgstatic void SelectionReceived PROTO_XT_SEL_CB_ARGS;
148d522f475Smrgstatic void StartSelect(XtermWidget xw, const CELL * cell);
149d522f475Smrgstatic void TrackDown(XtermWidget xw, XButtonEvent * event);
150d522f475Smrgstatic void TrackText(XtermWidget xw, const CELL * first, const CELL * last);
151d522f475Smrgstatic void _OwnSelection(XtermWidget xw, String * selections, Cardinal count);
152d522f475Smrgstatic void do_select_end(XtermWidget xw, XEvent * event, String * params,
153d522f475Smrg			  Cardinal *num_params, Bool use_cursor_loc);
154d522f475Smrg
155d522f475SmrgBool
156d522f475SmrgSendMousePosition(XtermWidget xw, XEvent * event)
157d522f475Smrg{
158d522f475Smrg    TScreen *screen = &(xw->screen);
159d522f475Smrg
160d522f475Smrg    /* If send_mouse_pos mode isn't on, we shouldn't be here */
161d522f475Smrg    if (screen->send_mouse_pos == MOUSE_OFF)
162d522f475Smrg	return False;
163d522f475Smrg
164d522f475Smrg#if OPT_DEC_LOCATOR
165d522f475Smrg    if (screen->send_mouse_pos == DEC_LOCATOR) {
166d522f475Smrg	return (SendLocatorPosition(xw, event));
167d522f475Smrg    }
168d522f475Smrg#endif /* OPT_DEC_LOCATOR */
169d522f475Smrg
170d522f475Smrg    /* Make sure the event is an appropriate type */
171d522f475Smrg    if ((screen->send_mouse_pos != BTN_EVENT_MOUSE)
172d522f475Smrg	&& (screen->send_mouse_pos != ANY_EVENT_MOUSE)
173d522f475Smrg	&& event->type != ButtonPress
174d522f475Smrg	&& event->type != ButtonRelease)
175d522f475Smrg	return False;
176d522f475Smrg
177d522f475Smrg    switch (screen->send_mouse_pos) {
178d522f475Smrg    case X10_MOUSE:		/* X10 compatibility sequences */
179d522f475Smrg
180d522f475Smrg	if (KeyModifiers == 0) {
181d522f475Smrg	    if (event->type == ButtonPress)
182d522f475Smrg		EditorButton(xw, (XButtonEvent *) event);
183d522f475Smrg	    return True;
184d522f475Smrg	}
185d522f475Smrg	return False;
186d522f475Smrg
187d522f475Smrg    case VT200_HIGHLIGHT_MOUSE:	/* DEC vt200 hilite tracking */
188d522f475Smrg	if (event->type == ButtonPress &&
189d522f475Smrg	    KeyModifiers == 0 &&
190d522f475Smrg	    event->xbutton.button == Button1) {
191d522f475Smrg	    TrackDown(xw, (XButtonEvent *) event);
192d522f475Smrg	    return True;
193d522f475Smrg	}
194d522f475Smrg	if (KeyModifiers == 0 || KeyModifiers == ControlMask) {
195d522f475Smrg	    EditorButton(xw, (XButtonEvent *) event);
196d522f475Smrg	    return True;
197d522f475Smrg	}
198d522f475Smrg	return False;
199d522f475Smrg
200d522f475Smrg    case VT200_MOUSE:		/* DEC vt200 compatible */
201d522f475Smrg
202d522f475Smrg	/* xterm extension for motion reporting. June 1998 */
203d522f475Smrg	/* EditorButton() will distinguish between the modes */
204d522f475Smrg    case BTN_EVENT_MOUSE:
205d522f475Smrg    case ANY_EVENT_MOUSE:
206d522f475Smrg	if (KeyModifiers == 0 || KeyModifiers == ControlMask) {
207d522f475Smrg	    EditorButton(xw, (XButtonEvent *) event);
208d522f475Smrg	    return True;
209d522f475Smrg	}
210d522f475Smrg	return False;
211d522f475Smrg
212d522f475Smrg    default:
213d522f475Smrg	return False;
214d522f475Smrg    }
215d522f475Smrg}
216d522f475Smrg
217d522f475Smrg#if OPT_DEC_LOCATOR
218d522f475Smrg
219d522f475Smrg#define	LocatorCoords( row, col, x, y, oor )			\
220d522f475Smrg    if( screen->locator_pixels ) {				\
221d522f475Smrg	(oor)=False; (row) = (y)+1; (col) = (x)+1;		\
222d522f475Smrg	/* Limit to screen dimensions */			\
223d522f475Smrg	if ((row) < 1) (row) = 1,(oor)=True;			\
224d522f475Smrg	else if ((row) > screen->border*2+Height(screen))	\
225d522f475Smrg	    (row) = screen->border*2+Height(screen),(oor)=True;	\
226d522f475Smrg	if ((col) < 1) (col) = 1,(oor)=True;			\
227d522f475Smrg	else if ((col) > OriginX(screen)*2+Width(screen))	\
228d522f475Smrg	    (col) = OriginX(screen)*2+Width(screen),(oor)=True;	\
229d522f475Smrg    } else {							\
230d522f475Smrg	(oor)=False;						\
231d522f475Smrg	/* Compute character position of mouse pointer */	\
232d522f475Smrg	(row) = ((y) - screen->border) / FontHeight(screen);	\
233d522f475Smrg	(col) = ((x) - OriginX(screen)) / FontWidth(screen);	\
234d522f475Smrg	/* Limit to screen dimensions */			\
235d522f475Smrg	if ((row) < 0) (row) = 0,(oor)=True;			\
236d522f475Smrg	else if ((row) > screen->max_row)			\
237d522f475Smrg	    (row) = screen->max_row,(oor)=True;			\
238d522f475Smrg	if ((col) < 0) (col) = 0,(oor)=True;			\
239d522f475Smrg	else if ((col) > screen->max_col)			\
240d522f475Smrg	    (col) = screen->max_col,(oor)=True;			\
241d522f475Smrg	(row)++; (col)++;					\
242d522f475Smrg    }
243d522f475Smrg
244d522f475Smrgstatic Bool
245d522f475SmrgSendLocatorPosition(XtermWidget xw, XEvent * event)
246d522f475Smrg{
247d522f475Smrg    ANSI reply;
248d522f475Smrg    TScreen *screen = &(xw->screen);
249d522f475Smrg    int row, col;
250d522f475Smrg    Bool oor;
251d522f475Smrg    int button;
252d522f475Smrg    int state;
253d522f475Smrg
254d522f475Smrg    /* Make sure the event is an appropriate type */
255d522f475Smrg    if ((event->type != ButtonPress &&
256d522f475Smrg	 event->type != ButtonRelease &&
257d522f475Smrg	 !screen->loc_filter) ||
258d522f475Smrg	(KeyModifiers != 0 && KeyModifiers != ControlMask))
259d522f475Smrg	return (False);
260d522f475Smrg
261d522f475Smrg    if ((event->type == ButtonPress &&
262d522f475Smrg	 !(screen->locator_events & LOC_BTNS_DN)) ||
263d522f475Smrg	(event->type == ButtonRelease &&
264d522f475Smrg	 !(screen->locator_events & LOC_BTNS_UP)))
265d522f475Smrg	return (True);
266d522f475Smrg
267d522f475Smrg    if (event->type == MotionNotify) {
268d522f475Smrg	CheckLocatorPosition(xw, event);
269d522f475Smrg	return (True);
270d522f475Smrg    }
271d522f475Smrg
272d522f475Smrg    /* get button # */
273d522f475Smrg    button = event->xbutton.button - 1;
274d522f475Smrg
275d522f475Smrg    LocatorCoords(row, col, event->xbutton.x, event->xbutton.y, oor);
276d522f475Smrg
277d522f475Smrg    /*
278d522f475Smrg     * DECterm mouse:
279d522f475Smrg     *
280d522f475Smrg     * ESCAPE '[' event ; mask ; row ; column '&' 'w'
281d522f475Smrg     */
282d522f475Smrg    memset(&reply, 0, sizeof(reply));
283d522f475Smrg    reply.a_type = ANSI_CSI;
284d522f475Smrg
285d522f475Smrg    if (oor) {
286d522f475Smrg	reply.a_nparam = 1;
287d522f475Smrg	reply.a_param[0] = 0;	/* Event - 0 = locator unavailable */
288d522f475Smrg	reply.a_inters = '&';
289d522f475Smrg	reply.a_final = 'w';
290d522f475Smrg	unparseseq(xw, &reply);
291d522f475Smrg
292d522f475Smrg	if (screen->locator_reset) {
293d522f475Smrg	    MotionOff(screen, xw);
294d522f475Smrg	    screen->send_mouse_pos = MOUSE_OFF;
295d522f475Smrg	}
296d522f475Smrg	return (True);
297d522f475Smrg    }
298d522f475Smrg
299d522f475Smrg    /*
300d522f475Smrg     * event:
301d522f475Smrg     *        1       no buttons
302d522f475Smrg     *        2       left button down
303d522f475Smrg     *        3       left button up
304d522f475Smrg     *        4       middle button down
305d522f475Smrg     *        5       middle button up
306d522f475Smrg     *        6       right button down
307d522f475Smrg     *        7       right button up
308d522f475Smrg     *        8       M4 down
309d522f475Smrg     *        9       M4 up
310d522f475Smrg     */
311d522f475Smrg    reply.a_nparam = 4;
312d522f475Smrg    switch (event->type) {
313d522f475Smrg    case ButtonPress:
314d522f475Smrg	reply.a_param[0] = 2 + (button << 1);
315d522f475Smrg	break;
316d522f475Smrg    case ButtonRelease:
317d522f475Smrg	reply.a_param[0] = 3 + (button << 1);
318d522f475Smrg	break;
319d522f475Smrg    default:
320d522f475Smrg	return (True);
321d522f475Smrg    }
322d522f475Smrg    /*
323d522f475Smrg     * mask:
324d522f475Smrg     * bit 7   bit 6   bit 5   bit 4   bit 3   bit 2       bit 1         bit 0
325d522f475Smrg     *                                 M4 down left down   middle down   right down
326d522f475Smrg     *
327d522f475Smrg     * Notice that Button1 (left) and Button3 (right) are swapped in the mask.
328d522f475Smrg     * Also, mask should be the state after the button press/release,
329d522f475Smrg     * X provides the state not including the button press/release.
330d522f475Smrg     */
331d522f475Smrg    state = (event->xbutton.state
332d522f475Smrg	     & (Button1Mask | Button2Mask | Button3Mask | Button4Mask)) >> 8;
333d522f475Smrg    state ^= 1 << button;	/* update mask to "after" state */
334d522f475Smrg    state = (state & ~(4 | 1)) | ((state & 1) ? 4 : 0) | ((state & 4) ? 1 : 0);		/* swap Button1 & Button3 */
335d522f475Smrg
336d522f475Smrg    reply.a_param[1] = state;
337d522f475Smrg    reply.a_param[2] = row;
338d522f475Smrg    reply.a_param[3] = col;
339d522f475Smrg    reply.a_inters = '&';
340d522f475Smrg    reply.a_final = 'w';
341d522f475Smrg
342d522f475Smrg    unparseseq(xw, &reply);
343d522f475Smrg
344d522f475Smrg    if (screen->locator_reset) {
345d522f475Smrg	MotionOff(screen, xw);
346d522f475Smrg	screen->send_mouse_pos = MOUSE_OFF;
347d522f475Smrg    }
348d522f475Smrg
349d522f475Smrg    /*
350d522f475Smrg     * DECterm turns the Locator off if a button is pressed while a filter rectangle
351d522f475Smrg     * is active. This might be a bug, but I don't know, so I'll emulate it anyways.
352d522f475Smrg     */
353d522f475Smrg    if (screen->loc_filter) {
354d522f475Smrg	screen->send_mouse_pos = MOUSE_OFF;
355d522f475Smrg	screen->loc_filter = False;
356d522f475Smrg	screen->locator_events = 0;
357d522f475Smrg	MotionOff(screen, xw);
358d522f475Smrg    }
359d522f475Smrg
360d522f475Smrg    return (True);
361d522f475Smrg}
362d522f475Smrg
363d522f475Smrg/*
364d522f475Smrg * mask:
365d522f475Smrg * bit 7   bit 6   bit 5   bit 4   bit 3   bit 2       bit 1         bit 0
366d522f475Smrg *                                 M4 down left down   middle down   right down
367d522f475Smrg *
368d522f475Smrg * Button1 (left) and Button3 (right) are swapped in the mask relative to X.
369d522f475Smrg */
370d522f475Smrg#define	ButtonState(state, mask)	\
371d522f475Smrg{ (state) = ((mask) & (Button1Mask | Button2Mask | Button3Mask | Button4Mask)) >> 8;	\
372d522f475Smrg  /* swap Button1 & Button3 */								\
373d522f475Smrg  (state) = ((state) & ~(4|1)) | (((state)&1)?4:0) | (((state)&4)?1:0);			\
374d522f475Smrg}
375d522f475Smrg
376d522f475Smrgvoid
377d522f475SmrgGetLocatorPosition(XtermWidget xw)
378d522f475Smrg{
379d522f475Smrg    ANSI reply;
380d522f475Smrg    TScreen *screen = &xw->screen;
381d522f475Smrg    Window root, child;
382d522f475Smrg    int rx, ry, x, y;
383d522f475Smrg    unsigned int mask;
384d522f475Smrg    int row = 0, col = 0;
385d522f475Smrg    Bool oor = False;
386d522f475Smrg    Bool ret = False;
387d522f475Smrg    int state;
388d522f475Smrg
389d522f475Smrg    /*
390d522f475Smrg     * DECterm turns the Locator off if the position is requested while a filter rectangle
391d522f475Smrg     * is active.  This might be a bug, but I don't know, so I'll emulate it anyways.
392d522f475Smrg     */
393d522f475Smrg    if (screen->loc_filter) {
394d522f475Smrg	screen->send_mouse_pos = MOUSE_OFF;
395d522f475Smrg	screen->loc_filter = False;
396d522f475Smrg	screen->locator_events = 0;
397d522f475Smrg	MotionOff(screen, xw);
398d522f475Smrg    }
399d522f475Smrg
400d522f475Smrg    memset(&reply, 0, sizeof(reply));
401d522f475Smrg    reply.a_type = ANSI_CSI;
402d522f475Smrg
403d522f475Smrg    if (screen->send_mouse_pos == DEC_LOCATOR) {
404d522f475Smrg	ret = XQueryPointer(screen->display, VWindow(screen), &root,
405d522f475Smrg			    &child, &rx, &ry, &x, &y, &mask);
406d522f475Smrg	if (ret) {
407d522f475Smrg	    LocatorCoords(row, col, x, y, oor);
408d522f475Smrg	}
409d522f475Smrg    }
410d522f475Smrg    if (ret == False || oor) {
411d522f475Smrg	reply.a_nparam = 1;
412d522f475Smrg	reply.a_param[0] = 0;	/* Event - 0 = locator unavailable */
413d522f475Smrg	reply.a_inters = '&';
414d522f475Smrg	reply.a_final = 'w';
415d522f475Smrg	unparseseq(xw, &reply);
416d522f475Smrg
417d522f475Smrg	if (screen->locator_reset) {
418d522f475Smrg	    MotionOff(screen, xw);
419d522f475Smrg	    screen->send_mouse_pos = MOUSE_OFF;
420d522f475Smrg	}
421d522f475Smrg	return;
422d522f475Smrg    }
423d522f475Smrg
424d522f475Smrg    ButtonState(state, mask);
425d522f475Smrg
426d522f475Smrg    reply.a_nparam = 4;
427d522f475Smrg    reply.a_param[0] = 1;	/* Event - 1 = response to locator request */
428d522f475Smrg    reply.a_param[1] = state;
429d522f475Smrg    reply.a_param[2] = row;
430d522f475Smrg    reply.a_param[3] = col;
431d522f475Smrg    reply.a_inters = '&';
432d522f475Smrg    reply.a_final = 'w';
433d522f475Smrg    unparseseq(xw, &reply);
434d522f475Smrg
435d522f475Smrg    if (screen->locator_reset) {
436d522f475Smrg	MotionOff(screen, xw);
437d522f475Smrg	screen->send_mouse_pos = MOUSE_OFF;
438d522f475Smrg    }
439d522f475Smrg}
440d522f475Smrg
441d522f475Smrgvoid
442d522f475SmrgInitLocatorFilter(XtermWidget xw)
443d522f475Smrg{
444d522f475Smrg    ANSI reply;
445d522f475Smrg    TScreen *screen = &xw->screen;
446d522f475Smrg    Window root, child;
447d522f475Smrg    int rx, ry, x, y;
448d522f475Smrg    unsigned int mask;
449d522f475Smrg    int row = 0, col = 0;
450d522f475Smrg    Bool oor = 0;
451d522f475Smrg    Bool ret;
452d522f475Smrg    int state;
453d522f475Smrg
454d522f475Smrg    ret = XQueryPointer(screen->display, VWindow(screen),
455d522f475Smrg			&root, &child, &rx, &ry, &x, &y, &mask);
456d522f475Smrg    if (ret) {
457d522f475Smrg	LocatorCoords(row, col, x, y, oor);
458d522f475Smrg    }
459d522f475Smrg    if (ret == False || oor) {
460d522f475Smrg	/* Locator is unavailable */
461d522f475Smrg
462d522f475Smrg	if (screen->loc_filter_top != LOC_FILTER_POS ||
463d522f475Smrg	    screen->loc_filter_left != LOC_FILTER_POS ||
464d522f475Smrg	    screen->loc_filter_bottom != LOC_FILTER_POS ||
465d522f475Smrg	    screen->loc_filter_right != LOC_FILTER_POS) {
466d522f475Smrg	    /*
467d522f475Smrg	     * If any explicit coordinates were received,
468d522f475Smrg	     * report immediately with no coordinates.
469d522f475Smrg	     */
470d522f475Smrg	    memset(&reply, 0, sizeof(reply));
471d522f475Smrg	    reply.a_type = ANSI_CSI;
472d522f475Smrg	    reply.a_nparam = 1;
473d522f475Smrg	    reply.a_param[0] = 0;	/* Event - 0 = locator unavailable */
474d522f475Smrg	    reply.a_inters = '&';
475d522f475Smrg	    reply.a_final = 'w';
476d522f475Smrg	    unparseseq(xw, &reply);
477d522f475Smrg
478d522f475Smrg	    if (screen->locator_reset) {
479d522f475Smrg		MotionOff(screen, xw);
480d522f475Smrg		screen->send_mouse_pos = MOUSE_OFF;
481d522f475Smrg	    }
482d522f475Smrg	} else {
483d522f475Smrg	    /*
484d522f475Smrg	     * No explicit coordinates were received, and the pointer is
485d522f475Smrg	     * unavailable.  Report when the pointer re-enters the window.
486d522f475Smrg	     */
487d522f475Smrg	    screen->loc_filter = True;
488d522f475Smrg	    MotionOn(screen, xw);
489d522f475Smrg	}
490d522f475Smrg	return;
491d522f475Smrg    }
492d522f475Smrg
493d522f475Smrg    /*
494d522f475Smrg     * Adjust rectangle coordinates:
495d522f475Smrg     *  1. Replace "LOC_FILTER_POS" with current coordinates
496d522f475Smrg     *  2. Limit coordinates to screen size
497d522f475Smrg     *  3. make sure top and left are less than bottom and right, resp.
498d522f475Smrg     */
499d522f475Smrg    if (screen->locator_pixels) {
500d522f475Smrg	rx = OriginX(screen) * 2 + Width(screen);
501d522f475Smrg	ry = screen->border * 2 + Height(screen);
502d522f475Smrg    } else {
503d522f475Smrg	rx = screen->max_col;
504d522f475Smrg	ry = screen->max_row;
505d522f475Smrg    }
506d522f475Smrg
507d522f475Smrg#define	Adjust( coord, def, max )				\
508d522f475Smrg	if( (coord) == LOC_FILTER_POS )	(coord) = (def);	\
509d522f475Smrg	else if ((coord) < 1)		(coord) = 1;		\
510d522f475Smrg	else if ((coord) > (max))	(coord) = (max)
511d522f475Smrg
512d522f475Smrg    Adjust(screen->loc_filter_top, row, ry);
513d522f475Smrg    Adjust(screen->loc_filter_left, col, rx);
514d522f475Smrg    Adjust(screen->loc_filter_bottom, row, ry);
515d522f475Smrg    Adjust(screen->loc_filter_right, col, rx);
516d522f475Smrg
517d522f475Smrg    if (screen->loc_filter_top > screen->loc_filter_bottom) {
518d522f475Smrg	ry = screen->loc_filter_top;
519d522f475Smrg	screen->loc_filter_top = screen->loc_filter_bottom;
520d522f475Smrg	screen->loc_filter_bottom = ry;
521d522f475Smrg    }
522d522f475Smrg
523d522f475Smrg    if (screen->loc_filter_left > screen->loc_filter_right) {
524d522f475Smrg	rx = screen->loc_filter_left;
525d522f475Smrg	screen->loc_filter_left = screen->loc_filter_right;
526d522f475Smrg	screen->loc_filter_right = rx;
527d522f475Smrg    }
528d522f475Smrg
529d522f475Smrg    if ((col < screen->loc_filter_left) ||
530d522f475Smrg	(col > screen->loc_filter_right) ||
531d522f475Smrg	(row < screen->loc_filter_top) ||
532d522f475Smrg	(row > screen->loc_filter_bottom)) {
533d522f475Smrg	/* Pointer is already outside the rectangle - report immediately */
534d522f475Smrg	ButtonState(state, mask);
535d522f475Smrg
536d522f475Smrg	memset(&reply, 0, sizeof(reply));
537d522f475Smrg	reply.a_type = ANSI_CSI;
538d522f475Smrg	reply.a_nparam = 4;
539d522f475Smrg	reply.a_param[0] = 10;	/* Event - 10 = locator outside filter */
540d522f475Smrg	reply.a_param[1] = state;
541d522f475Smrg	reply.a_param[2] = row;
542d522f475Smrg	reply.a_param[3] = col;
543d522f475Smrg	reply.a_inters = '&';
544d522f475Smrg	reply.a_final = 'w';
545d522f475Smrg	unparseseq(xw, &reply);
546d522f475Smrg
547d522f475Smrg	if (screen->locator_reset) {
548d522f475Smrg	    MotionOff(screen, xw);
549d522f475Smrg	    screen->send_mouse_pos = MOUSE_OFF;
550d522f475Smrg	}
551d522f475Smrg	return;
552d522f475Smrg    }
553d522f475Smrg
554d522f475Smrg    /*
555d522f475Smrg     * Rectangle is set up.  Allow pointer tracking
556d522f475Smrg     * to detect if the mouse leaves the rectangle.
557d522f475Smrg     */
558d522f475Smrg    screen->loc_filter = True;
559d522f475Smrg    MotionOn(screen, xw);
560d522f475Smrg}
561d522f475Smrg
562d522f475Smrgstatic void
563d522f475SmrgCheckLocatorPosition(XtermWidget xw, XEvent * event)
564d522f475Smrg{
565d522f475Smrg    ANSI reply;
566d522f475Smrg    TScreen *screen = &(xw->screen);
567d522f475Smrg    int row, col;
568d522f475Smrg    Bool oor;
569d522f475Smrg    int state;
570d522f475Smrg
571d522f475Smrg    LocatorCoords(row, col, event->xbutton.x, event->xbutton.y, oor);
572d522f475Smrg
573d522f475Smrg    /*
574d522f475Smrg     * Send report if the pointer left the filter rectangle, if
575d522f475Smrg     * the pointer left the window, or if the filter rectangle
576d522f475Smrg     * had no coordinates and the pointer re-entered the window.
577d522f475Smrg     */
578d522f475Smrg    if (oor || (screen->loc_filter_top == LOC_FILTER_POS) ||
579d522f475Smrg	(col < screen->loc_filter_left) ||
580d522f475Smrg	(col > screen->loc_filter_right) ||
581d522f475Smrg	(row < screen->loc_filter_top) ||
582d522f475Smrg	(row > screen->loc_filter_bottom)) {
583d522f475Smrg	/* Filter triggered - disable it */
584d522f475Smrg	screen->loc_filter = False;
585d522f475Smrg	MotionOff(screen, xw);
586d522f475Smrg
587d522f475Smrg	memset(&reply, 0, sizeof(reply));
588d522f475Smrg	reply.a_type = ANSI_CSI;
589d522f475Smrg	if (oor) {
590d522f475Smrg	    reply.a_nparam = 1;
591d522f475Smrg	    reply.a_param[0] = 0;	/* Event - 0 = locator unavailable */
592d522f475Smrg	} else {
593d522f475Smrg	    ButtonState(state, event->xbutton.state);
594d522f475Smrg
595d522f475Smrg	    reply.a_nparam = 4;
596d522f475Smrg	    reply.a_param[0] = 10;	/* Event - 10 = locator outside filter */
597d522f475Smrg	    reply.a_param[1] = state;
598d522f475Smrg	    reply.a_param[2] = row;
599d522f475Smrg	    reply.a_param[3] = col;
600d522f475Smrg	}
601d522f475Smrg
602d522f475Smrg	reply.a_inters = '&';
603d522f475Smrg	reply.a_final = 'w';
604d522f475Smrg	unparseseq(xw, &reply);
605d522f475Smrg
606d522f475Smrg	if (screen->locator_reset) {
607d522f475Smrg	    MotionOff(screen, xw);
608d522f475Smrg	    screen->send_mouse_pos = MOUSE_OFF;
609d522f475Smrg	}
610d522f475Smrg    }
611d522f475Smrg}
612d522f475Smrg#endif /* OPT_DEC_LOCATOR */
613d522f475Smrg
614d522f475Smrg#if OPT_READLINE
615d522f475Smrgstatic int
616d522f475SmrgisClick1_clean(TScreen * screen, XEvent * event)
617d522f475Smrg{
618d522f475Smrg    int delta;
619d522f475Smrg
620d522f475Smrg    if (!(event->type == ButtonPress || event->type == ButtonRelease)
621d522f475Smrg    /* Disable on Shift-Click-1, including the application-mouse modes */
622d522f475Smrg	|| (KeyModifiers & ShiftMask)
623d522f475Smrg	|| (screen->send_mouse_pos != MOUSE_OFF)	/* Kinda duplicate... */
624d522f475Smrg	||ExtendingSelection)	/* Was moved */
625d522f475Smrg	return 0;
626d522f475Smrg
627d522f475Smrg    if (event->type != ButtonRelease)
628d522f475Smrg	return 0;
629d522f475Smrg
630d522f475Smrg    if (lastButtonDownTime == (Time) 0) {
631d522f475Smrg	/* first time or once in a blue moon */
632d522f475Smrg	delta = screen->multiClickTime + 1;
633d522f475Smrg    } else if (event->xbutton.time > lastButtonDownTime) {
634d522f475Smrg	/* most of the time */
635d522f475Smrg	delta = event->xbutton.time - lastButtonDownTime;
636d522f475Smrg    } else {
637d522f475Smrg	/* time has rolled over since lastButtonUpTime */
638d522f475Smrg	delta = (((Time) ~ 0) - lastButtonDownTime) + event->xbutton.time;
639d522f475Smrg    }
640d522f475Smrg
641d522f475Smrg    return delta <= screen->multiClickTime;
642d522f475Smrg}
643d522f475Smrg
644d522f475Smrgstatic int
645d522f475SmrgisDoubleClick3(TScreen * screen, XEvent * event)
646d522f475Smrg{
647d522f475Smrg    int delta;
648d522f475Smrg
649d522f475Smrg    if (event->type != ButtonRelease
650d522f475Smrg	|| (KeyModifiers & ShiftMask)
651d522f475Smrg	|| event->xbutton.button != Button3) {
652d522f475Smrg	lastButton3UpTime = 0;	/* Disable the cached info */
653d522f475Smrg	return 0;
654d522f475Smrg    }
655d522f475Smrg    /* Process Btn3Release. */
656d522f475Smrg    if (lastButton3DoubleDownTime == (Time) 0) {
657d522f475Smrg	/* No previous click or once in a blue moon */
658d522f475Smrg	delta = screen->multiClickTime + 1;
659d522f475Smrg    } else if (event->xbutton.time > lastButton3DoubleDownTime) {
660d522f475Smrg	/* most of the time */
661d522f475Smrg	delta = event->xbutton.time - lastButton3DoubleDownTime;
662d522f475Smrg    } else {
663d522f475Smrg	/* time has rolled over since lastButton3DoubleDownTime */
664d522f475Smrg	delta = (((Time) ~ 0) - lastButton3DoubleDownTime) + event->xbutton.time;
665d522f475Smrg    }
666d522f475Smrg    if (delta <= screen->multiClickTime) {
667d522f475Smrg	/* Double click */
668d522f475Smrg	CELL cell;
669d522f475Smrg
670d522f475Smrg	/* Cannot check ExtendingSelection, since mouse-3 always sets it */
671d522f475Smrg	PointToCELL(screen, event->xbutton.y, event->xbutton.x, &cell);
672d522f475Smrg	if (isSameCELL(&cell, &lastButton3)) {
673d522f475Smrg	    lastButton3DoubleDownTime = 0;	/* Disable the third click */
674d522f475Smrg	    return 1;
675d522f475Smrg	}
676d522f475Smrg    }
677d522f475Smrg    /* Not a double click, memorize for future check. */
678d522f475Smrg    lastButton3UpTime = event->xbutton.time;
679d522f475Smrg    PointToCELL(screen, event->xbutton.y, event->xbutton.x, &lastButton3);
680d522f475Smrg    return 0;
681d522f475Smrg}
682d522f475Smrg
683d522f475Smrgstatic int
684d522f475SmrgCheckSecondPress3(TScreen * screen, XEvent * event)
685d522f475Smrg{
686d522f475Smrg    int delta;
687d522f475Smrg
688d522f475Smrg    if (event->type != ButtonPress
689d522f475Smrg	|| (KeyModifiers & ShiftMask)
690d522f475Smrg	|| event->xbutton.button != Button3) {
691d522f475Smrg	lastButton3DoubleDownTime = 0;	/* Disable the cached info */
692d522f475Smrg	return 0;
693d522f475Smrg    }
694d522f475Smrg    /* Process Btn3Press. */
695d522f475Smrg    if (lastButton3UpTime == (Time) 0) {
696d522f475Smrg	/* No previous click or once in a blue moon */
697d522f475Smrg	delta = screen->multiClickTime + 1;
698d522f475Smrg    } else if (event->xbutton.time > lastButton3UpTime) {
699d522f475Smrg	/* most of the time */
700d522f475Smrg	delta = event->xbutton.time - lastButton3UpTime;
701d522f475Smrg    } else {
702d522f475Smrg	/* time has rolled over since lastButton3UpTime */
703d522f475Smrg	delta = (((Time) ~ 0) - lastButton3UpTime) + event->xbutton.time;
704d522f475Smrg    }
705d522f475Smrg    if (delta <= screen->multiClickTime) {
706d522f475Smrg	CELL cell;
707d522f475Smrg
708d522f475Smrg	PointToCELL(screen, event->xbutton.y, event->xbutton.x, &cell);
709d522f475Smrg	if (isSameCELL(&cell, &lastButton3)) {
710d522f475Smrg	    /* A candidate for a double-click */
711d522f475Smrg	    lastButton3DoubleDownTime = event->xbutton.time;
712d522f475Smrg	    PointToCELL(screen, event->xbutton.y, event->xbutton.x, &lastButton3);
713d522f475Smrg	    return 1;
714d522f475Smrg	}
715d522f475Smrg	lastButton3UpTime = 0;	/* Disable the info about the previous click */
716d522f475Smrg    }
717d522f475Smrg    /* Either too long, or moved, disable. */
718d522f475Smrg    lastButton3DoubleDownTime = 0;
719d522f475Smrg    return 0;
720d522f475Smrg}
721d522f475Smrg
722d522f475Smrgstatic int
723d522f475SmrgrowOnCurrentLine(TScreen * screen,
724d522f475Smrg		 int line,
725d522f475Smrg		 int *deltap)	/* must be XButtonEvent */
726d522f475Smrg{
727d522f475Smrg    int l1, l2;
728d522f475Smrg
729d522f475Smrg    *deltap = 0;
730d522f475Smrg    if (line == screen->cur_row)
731d522f475Smrg	return 1;
732d522f475Smrg
733d522f475Smrg    if (line < screen->cur_row)
734d522f475Smrg	l1 = line, l2 = screen->cur_row;
735d522f475Smrg    else
736d522f475Smrg	l2 = line, l1 = screen->cur_row;
737d522f475Smrg    l1--;
738d522f475Smrg    while (++l1 < l2)
739d522f475Smrg	if (!ScrnTstWrapped(screen, l1))
740d522f475Smrg	    return 0;
741d522f475Smrg    /* Everything is on one "wrapped line" now */
742d522f475Smrg    *deltap = line - screen->cur_row;
743d522f475Smrg    return 1;
744d522f475Smrg}
745d522f475Smrg
746d522f475Smrgstatic int
747d522f475SmrgeventRow(TScreen * screen, XEvent * event)	/* must be XButtonEvent */
748d522f475Smrg{
749d522f475Smrg    return (event->xbutton.y - screen->border) / FontHeight(screen);
750d522f475Smrg}
751d522f475Smrg
752d522f475Smrgstatic int
753d522f475SmrgeventColBetween(TScreen * screen, XEvent * event)	/* must be XButtonEvent */
754d522f475Smrg{
755d522f475Smrg    /* Correct by half a width - we are acting on a boundary, not on a cell. */
756d522f475Smrg    return ((event->xbutton.x - OriginX(screen) + (FontWidth(screen) - 1) / 2)
757d522f475Smrg	    / FontWidth(screen));
758d522f475Smrg}
759d522f475Smrg
760d522f475Smrgstatic int
761d522f475SmrgReadLineMovePoint(TScreen * screen, int col, int ldelta)
762d522f475Smrg{
763d522f475Smrg    Char line[6];
764d522f475Smrg    unsigned count = 0;
765d522f475Smrg
766d522f475Smrg    col += ldelta * MaxCols(screen) - screen->cur_col;
767d522f475Smrg    if (col == 0)
768d522f475Smrg	return 0;
769d522f475Smrg    if (screen->control_eight_bits) {
770d522f475Smrg	line[count++] = ANSI_CSI;
771d522f475Smrg    } else {
772d522f475Smrg	line[count++] = ANSI_ESC;
773d522f475Smrg	line[count++] = '[';	/* XXX maybe sometimes O is better? */
774d522f475Smrg    }
775d522f475Smrg    line[count++] = (col > 0 ? 'C' : 'D');
776d522f475Smrg    if (col < 0)
777d522f475Smrg	col = -col;
778d522f475Smrg    while (col--)
779d522f475Smrg	v_write(screen->respond, line, 3);
780d522f475Smrg    return 1;
781d522f475Smrg}
782d522f475Smrg
783d522f475Smrgstatic int
784d522f475SmrgReadLineDelete(TScreen * screen, CELL * cell1, CELL * cell2)
785d522f475Smrg{
786d522f475Smrg    int del;
787d522f475Smrg
788d522f475Smrg    del = (cell2->col - cell1->col) + ((cell2->row - cell1->row) * MaxCols(screen));
789d522f475Smrg    if (del <= 0)		/* Just in case... */
790d522f475Smrg	return 0;
791d522f475Smrg    while (del--)
792d522f475Smrg	v_write(screen->respond, (Char *) "\177", 1);
793d522f475Smrg    return 1;
794d522f475Smrg}
795d522f475Smrg#endif /* OPT_READLINE */
796d522f475Smrg
797d522f475Smrg/* ^XM-G<line+' '><col+' '> */
798d522f475Smrgvoid
799d522f475SmrgDiredButton(Widget w,
800d522f475Smrg	    XEvent * event,	/* must be XButtonEvent */
801d522f475Smrg	    String * params GCC_UNUSED,		/* selections */
802d522f475Smrg	    Cardinal *num_params GCC_UNUSED)
803d522f475Smrg{
804d522f475Smrg    if (IsXtermWidget(w)) {
805d522f475Smrg	XtermWidget xw = (XtermWidget) w;
806d522f475Smrg	TScreen *screen = &(xw->screen);
807d522f475Smrg	Char Line[6];
808d522f475Smrg	unsigned line, col;
809d522f475Smrg
810d522f475Smrg	if (event->type == ButtonPress || event->type == ButtonRelease) {
811d522f475Smrg	    line = (event->xbutton.y - screen->border) / FontHeight(screen);
812d522f475Smrg	    col = (event->xbutton.x - OriginX(screen)) / FontWidth(screen);
813d522f475Smrg	    Line[0] = CONTROL('X');
814d522f475Smrg	    Line[1] = ANSI_ESC;
815d522f475Smrg	    Line[2] = 'G';
816d522f475Smrg	    Line[3] = ' ' + col;
817d522f475Smrg	    Line[4] = ' ' + line;
818d522f475Smrg	    v_write(screen->respond, Line, 5);
819d522f475Smrg	}
820d522f475Smrg    }
821d522f475Smrg}
822d522f475Smrg
823d522f475Smrg#if OPT_READLINE
824d522f475Smrgvoid
825d522f475SmrgReadLineButton(Widget w,
826d522f475Smrg	       XEvent * event,	/* must be XButtonEvent */
827d522f475Smrg	       String * params GCC_UNUSED,	/* selections */
828d522f475Smrg	       Cardinal *num_params GCC_UNUSED)
829d522f475Smrg{
830d522f475Smrg    if (IsXtermWidget(w)) {
831d522f475Smrg	XtermWidget xw = (XtermWidget) w;
832d522f475Smrg	TScreen *screen = &(xw->screen);
833d522f475Smrg	Char Line[6];
834d522f475Smrg	int line, col, ldelta = 0;
835d522f475Smrg
836d522f475Smrg	if (!(event->type == ButtonPress || event->type == ButtonRelease)
837d522f475Smrg	    || (screen->send_mouse_pos != MOUSE_OFF) || ExtendingSelection)
838d522f475Smrg	    goto finish;
839d522f475Smrg	if (event->type == ButtonRelease) {
840d522f475Smrg	    int delta;
841d522f475Smrg
842d522f475Smrg	    if (lastButtonDownTime == (Time) 0) {
843d522f475Smrg		/* first time and once in a blue moon */
844d522f475Smrg		delta = screen->multiClickTime + 1;
845d522f475Smrg	    } else if (event->xbutton.time > lastButtonDownTime) {
846d522f475Smrg		/* most of the time */
847d522f475Smrg		delta = event->xbutton.time - lastButtonDownTime;
848d522f475Smrg	    } else {
849d522f475Smrg		/* time has rolled over since lastButtonUpTime */
850d522f475Smrg		delta = (((Time) ~ 0) - lastButtonDownTime) + event->xbutton.time;
851d522f475Smrg	    }
852d522f475Smrg	    if (delta > screen->multiClickTime)
853d522f475Smrg		goto finish;	/* All this work for this... */
854d522f475Smrg	}
855d522f475Smrg	line = (event->xbutton.y - screen->border) / FontHeight(screen);
856d522f475Smrg	if (line != screen->cur_row) {
857d522f475Smrg	    int l1, l2;
858d522f475Smrg
859d522f475Smrg	    if (line < screen->cur_row)
860d522f475Smrg		l1 = line, l2 = screen->cur_row;
861d522f475Smrg	    else
862d522f475Smrg		l2 = line, l1 = screen->cur_row;
863d522f475Smrg	    l1--;
864d522f475Smrg	    while (++l1 < l2)
865d522f475Smrg		if (!ScrnTstWrapped(screen, l1))
866d522f475Smrg		    goto finish;
867d522f475Smrg	    /* Everything is on one "wrapped line" now */
868d522f475Smrg	    ldelta = line - screen->cur_row;
869d522f475Smrg	}
870d522f475Smrg	/* Correct by half a width - we are acting on a boundary, not on a cell. */
871d522f475Smrg	col = (event->xbutton.x - OriginX(screen) + (FontWidth(screen) - 1)
872d522f475Smrg	       / 2)
873d522f475Smrg	    / FontWidth(screen) - screen->cur_col + ldelta * MaxCols(screen);
874d522f475Smrg	if (col == 0)
875d522f475Smrg	    goto finish;
876d522f475Smrg	Line[0] = ANSI_ESC;
877d522f475Smrg	/* XXX: sometimes it is better to send '['? */
878d522f475Smrg	Line[1] = 'O';
879d522f475Smrg	Line[2] = (col > 0 ? 'C' : 'D');
880d522f475Smrg	if (col < 0)
881d522f475Smrg	    col = -col;
882d522f475Smrg	while (col--)
883d522f475Smrg	    v_write(screen->respond, Line, 3);
884d522f475Smrg      finish:
885d522f475Smrg	if (event->type == ButtonRelease)
886d522f475Smrg	    do_select_end(xw, event, params, num_params, False);
887d522f475Smrg    }
888d522f475Smrg}
889d522f475Smrg#endif /* OPT_READLINE */
890d522f475Smrg
891d522f475Smrg/* repeats <ESC>n or <ESC>p */
892d522f475Smrgvoid
893d522f475SmrgViButton(Widget w,
894d522f475Smrg	 XEvent * event,	/* must be XButtonEvent */
895d522f475Smrg	 String * params GCC_UNUSED,	/* selections */
896d522f475Smrg	 Cardinal *num_params GCC_UNUSED)
897d522f475Smrg{
898d522f475Smrg    if (IsXtermWidget(w)) {
899d522f475Smrg	XtermWidget xw = (XtermWidget) w;
900d522f475Smrg	TScreen *screen = &(xw->screen);
901d522f475Smrg	int pty = screen->respond;
902d522f475Smrg	Char Line[6];
903d522f475Smrg	int line;
904d522f475Smrg
905d522f475Smrg	if (event->type == ButtonPress || event->type == ButtonRelease) {
906d522f475Smrg
907d522f475Smrg	    line = screen->cur_row -
908d522f475Smrg		((event->xbutton.y - screen->border) / FontHeight(screen));
909d522f475Smrg	    if (line != 0) {
910d522f475Smrg		Line[0] = ANSI_ESC;	/* force an exit from insert-mode */
911d522f475Smrg		v_write(pty, Line, 1);
912d522f475Smrg
913d522f475Smrg		if (line < 0) {
914d522f475Smrg		    line = -line;
915d522f475Smrg		    Line[0] = CONTROL('n');
916d522f475Smrg		} else {
917d522f475Smrg		    Line[0] = CONTROL('p');
918d522f475Smrg		}
919d522f475Smrg		while (--line >= 0)
920d522f475Smrg		    v_write(pty, Line, 1);
921d522f475Smrg	    }
922d522f475Smrg	}
923d522f475Smrg    }
924d522f475Smrg}
925d522f475Smrg
926d522f475Smrg/*
927d522f475Smrg * This function handles button-motion events
928d522f475Smrg */
929d522f475Smrg/*ARGSUSED*/
930d522f475Smrgvoid
931d522f475SmrgHandleSelectExtend(Widget w,
932d522f475Smrg		   XEvent * event,	/* must be XMotionEvent */
933d522f475Smrg		   String * params GCC_UNUSED,
934d522f475Smrg		   Cardinal *num_params GCC_UNUSED)
935d522f475Smrg{
936d522f475Smrg    if (IsXtermWidget(w)) {
937d522f475Smrg	XtermWidget xw = (XtermWidget) w;
938d522f475Smrg	TScreen *screen = &(xw->screen);
939d522f475Smrg	CELL cell;
940d522f475Smrg
941d522f475Smrg	screen->selection_time = event->xmotion.time;
942d522f475Smrg	switch (screen->eventMode) {
943d522f475Smrg	    /* If not in one of the DEC mouse-reporting modes */
944d522f475Smrg	case LEFTEXTENSION:
945d522f475Smrg	case RIGHTEXTENSION:
946d522f475Smrg	    PointToCELL(screen, event->xmotion.y, event->xmotion.x, &cell);
947d522f475Smrg	    ExtendExtend(xw, &cell);
948d522f475Smrg	    break;
949d522f475Smrg
950d522f475Smrg	    /* If in motion reporting mode, send mouse position to
951d522f475Smrg	       character process as a key sequence \E[M... */
952d522f475Smrg	case NORMAL:
953d522f475Smrg	    /* will get here if send_mouse_pos != MOUSE_OFF */
954d522f475Smrg	    if (screen->send_mouse_pos == BTN_EVENT_MOUSE
955d522f475Smrg		|| screen->send_mouse_pos == ANY_EVENT_MOUSE) {
956d522f475Smrg		(void) SendMousePosition(xw, event);
957d522f475Smrg	    }
958d522f475Smrg	    break;
959d522f475Smrg	}
960d522f475Smrg    }
961d522f475Smrg}
962d522f475Smrg
963d522f475Smrgvoid
964d522f475SmrgHandleKeyboardSelectExtend(Widget w,
965d522f475Smrg			   XEvent * event GCC_UNUSED,	/* must be XButtonEvent */
966d522f475Smrg			   String * params GCC_UNUSED,
967d522f475Smrg			   Cardinal *num_params GCC_UNUSED)
968d522f475Smrg{
969d522f475Smrg    if (IsXtermWidget(w)) {
970d522f475Smrg	XtermWidget xw = (XtermWidget) w;
971d522f475Smrg	TScreen *screen = &xw->screen;
972d522f475Smrg	ExtendExtend(xw, &screen->cursorp);
973d522f475Smrg    }
974d522f475Smrg}
975d522f475Smrg
976d522f475Smrgstatic void
977d522f475Smrgdo_select_end(XtermWidget xw,
978d522f475Smrg	      XEvent * event,	/* must be XButtonEvent */
979d522f475Smrg	      String * params,	/* selections */
980d522f475Smrg	      Cardinal *num_params,
981d522f475Smrg	      Bool use_cursor_loc)
982d522f475Smrg{
983d522f475Smrg#if OPT_READLINE
984d522f475Smrg    int ldelta1, ldelta2;
985d522f475Smrg#endif
986d522f475Smrg    TScreen *screen = &xw->screen;
987d522f475Smrg
988d522f475Smrg    screen->selection_time = event->xbutton.time;
989d522f475Smrg    switch (screen->eventMode) {
990d522f475Smrg    case NORMAL:
991d522f475Smrg	(void) SendMousePosition(xw, event);
992d522f475Smrg	break;
993d522f475Smrg    case LEFTEXTENSION:
994d522f475Smrg    case RIGHTEXTENSION:
995d522f475Smrg	EndExtend(xw, event, params, *num_params, use_cursor_loc);
996d522f475Smrg#if OPT_READLINE
997d522f475Smrg	if (isClick1_clean(screen, event)
998d522f475Smrg	    && SCREEN_FLAG(screen, click1_moves)
999d522f475Smrg	    && rowOnCurrentLine(screen, eventRow(screen, event), &ldelta1)) {
1000d522f475Smrg	    ReadLineMovePoint(screen, eventColBetween(screen, event), ldelta1);
1001d522f475Smrg	}
1002d522f475Smrg	if (isDoubleClick3(screen, event)
1003d522f475Smrg	    && SCREEN_FLAG(screen, dclick3_deletes)
1004d522f475Smrg	    && rowOnCurrentLine(screen, screen->startSel.row, &ldelta1)
1005d522f475Smrg	    && rowOnCurrentLine(screen, screen->endSel.row, &ldelta2)) {
1006d522f475Smrg	    ReadLineMovePoint(screen, screen->endSel.col, ldelta2);
1007d522f475Smrg	    ReadLineDelete(screen, &screen->startSel, &(screen->endSel));
1008d522f475Smrg	}
1009d522f475Smrg#endif /* OPT_READLINE */
1010d522f475Smrg	break;
1011d522f475Smrg    }
1012d522f475Smrg}
1013d522f475Smrg
1014d522f475Smrgvoid
1015d522f475SmrgHandleSelectEnd(Widget w,
1016d522f475Smrg		XEvent * event,	/* must be XButtonEvent */
1017d522f475Smrg		String * params,	/* selections */
1018d522f475Smrg		Cardinal *num_params)
1019d522f475Smrg{
1020d522f475Smrg    if (IsXtermWidget(w))
1021d522f475Smrg	do_select_end((XtermWidget) w, event, params, num_params, False);
1022d522f475Smrg}
1023d522f475Smrg
1024d522f475Smrgvoid
1025d522f475SmrgHandleKeyboardSelectEnd(Widget w,
1026d522f475Smrg			XEvent * event,		/* must be XButtonEvent */
1027d522f475Smrg			String * params,	/* selections */
1028d522f475Smrg			Cardinal *num_params)
1029d522f475Smrg{
1030d522f475Smrg    if (IsXtermWidget(w))
1031d522f475Smrg	do_select_end((XtermWidget) w, event, params, num_params, True);
1032d522f475Smrg}
1033d522f475Smrg
1034d522f475Smrgstruct _SelectionList {
1035d522f475Smrg    String *params;
1036d522f475Smrg    Cardinal count;
1037d522f475Smrg    Atom *targets;
1038d522f475Smrg    Time time;
1039d522f475Smrg};
1040d522f475Smrg
1041d522f475Smrgstatic unsigned
1042d522f475SmrgDECtoASCII(unsigned ch)
1043d522f475Smrg{
1044d522f475Smrg    if (xtermIsDecGraphic(ch)) {
1045d522f475Smrg	ch = "###########+++++##-##++++|######"[ch];
1046d522f475Smrg	/*    01234567890123456789012345678901 */
1047d522f475Smrg    }
1048d522f475Smrg    return ch;
1049d522f475Smrg}
1050d522f475Smrg/*
1051d522f475Smrg * Convert a UTF-8 string to Latin-1, replacing non Latin-1 characters by `#',
1052d522f475Smrg * or ASCII/Latin-1 equivalents for special cases.
1053d522f475Smrg */
1054d522f475Smrg#if OPT_WIDE_CHARS
1055d522f475Smrgstatic Char *
1056d522f475SmrgUTF8toLatin1(Char * s, unsigned len, unsigned long *result)
1057d522f475Smrg{
1058d522f475Smrg    static Char *buffer;
1059d522f475Smrg    static size_t used;
1060d522f475Smrg
1061d522f475Smrg    Char *q;
1062d522f475Smrg
1063d522f475Smrg    if (used == 0) {
1064d522f475Smrg	buffer = (Char *) XtMalloc(1 + (used = len));
1065d522f475Smrg    } else if (len > used) {
1066d522f475Smrg	buffer = (Char *) XtRealloc((char *) buffer, 1 + (used = len));
1067d522f475Smrg    }
1068d522f475Smrg
1069d522f475Smrg    if (buffer != 0) {
1070d522f475Smrg	PtyData data;
1071d522f475Smrg
1072d522f475Smrg	q = buffer;
1073d522f475Smrg	fakePtyData(&data, s, s + len);
1074d522f475Smrg	while (decodeUtf8(&data)) {
1075d522f475Smrg	    IChar value = skipPtyData(&data);
1076d522f475Smrg	    if (value == UCS_REPL) {
1077d522f475Smrg		*q++ = '#';
1078d522f475Smrg	    } else if (value < 256) {
1079d522f475Smrg		*q++ = value;
1080d522f475Smrg	    } else {
1081d522f475Smrg		unsigned eqv = ucs2dec(value);
1082d522f475Smrg		if (xtermIsDecGraphic(eqv)) {
1083d522f475Smrg		    *q++ = DECtoASCII(eqv);
1084d522f475Smrg		} else {
1085d522f475Smrg		    eqv = AsciiEquivs(value);
1086d522f475Smrg		    if (eqv == value)
1087d522f475Smrg			eqv = '#';
1088d522f475Smrg		    *q++ = eqv;
1089d522f475Smrg		    if (iswide((wchar_t) value))
1090d522f475Smrg			*q++ = ' ';
1091d522f475Smrg		}
1092d522f475Smrg	    }
1093d522f475Smrg	}
1094d522f475Smrg	*q = 0;
1095d522f475Smrg	*result = q - buffer;
1096d522f475Smrg    } else {
1097d522f475Smrg	*result = 0;
1098d522f475Smrg    }
1099d522f475Smrg    return buffer;
1100d522f475Smrg}
1101d522f475Smrg#endif /* OPT_WIDE_CHARS */
1102d522f475Smrg
1103d522f475Smrgstatic Atom *
1104d522f475Smrg_SelectionTargets(Widget w)
1105d522f475Smrg{
1106d522f475Smrg    static Atom *eightBitSelectionTargets = NULL;
1107d522f475Smrg    TScreen *screen;
1108d522f475Smrg    int n;
1109d522f475Smrg
1110d522f475Smrg    if (!IsXtermWidget(w))
1111d522f475Smrg	return NULL;
1112d522f475Smrg
1113d522f475Smrg    screen = TScreenOf((XtermWidget) w);
1114d522f475Smrg
1115d522f475Smrg#if OPT_WIDE_CHARS
1116d522f475Smrg    if (screen->wide_chars) {
1117d522f475Smrg	static Atom *utf8SelectionTargets = NULL;
1118d522f475Smrg
1119d522f475Smrg	if (utf8SelectionTargets == NULL) {
1120d522f475Smrg	    utf8SelectionTargets = (Atom *) XtMalloc(5 * sizeof(Atom));
1121d522f475Smrg	    if (utf8SelectionTargets == NULL) {
1122d522f475Smrg		TRACE(("Couldn't allocate utf8SelectionTargets\n"));
1123d522f475Smrg		return NULL;
1124d522f475Smrg	    }
1125d522f475Smrg	    n = 0;
1126d522f475Smrg	    utf8SelectionTargets[n++] = XA_UTF8_STRING(XtDisplay(w));
1127d522f475Smrg#ifdef X_HAVE_UTF8_STRING
1128d522f475Smrg	    if (screen->i18nSelections) {
1129d522f475Smrg		utf8SelectionTargets[n++] = XA_TEXT(XtDisplay(w));
1130d522f475Smrg		utf8SelectionTargets[n++] = XA_COMPOUND_TEXT(XtDisplay(w));
1131d522f475Smrg	    }
1132d522f475Smrg#endif
1133d522f475Smrg	    utf8SelectionTargets[n++] = XA_STRING;
1134d522f475Smrg	    utf8SelectionTargets[n] = None;
1135d522f475Smrg	}
1136d522f475Smrg	return utf8SelectionTargets;
1137d522f475Smrg    }
1138d522f475Smrg#endif
1139d522f475Smrg
1140d522f475Smrg    /* not screen->wide_chars */
1141d522f475Smrg    if (eightBitSelectionTargets == NULL) {
1142d522f475Smrg	eightBitSelectionTargets = (Atom *) XtMalloc(5 * sizeof(Atom));
1143d522f475Smrg	if (eightBitSelectionTargets == NULL) {
1144d522f475Smrg	    TRACE(("Couldn't allocate eightBitSelectionTargets\n"));
1145d522f475Smrg	    return NULL;
1146d522f475Smrg	}
1147d522f475Smrg	n = 0;
1148d522f475Smrg#ifdef X_HAVE_UTF8_STRING
1149d522f475Smrg	eightBitSelectionTargets[n++] = XA_UTF8_STRING(XtDisplay(w));
1150d522f475Smrg#endif
1151d522f475Smrg	if (screen->i18nSelections) {
1152d522f475Smrg	    eightBitSelectionTargets[n++] = XA_TEXT(XtDisplay(w));
1153d522f475Smrg	    eightBitSelectionTargets[n++] = XA_COMPOUND_TEXT(XtDisplay(w));
1154d522f475Smrg	}
1155d522f475Smrg	eightBitSelectionTargets[n++] = XA_STRING;
1156d522f475Smrg	eightBitSelectionTargets[n] = None;
1157d522f475Smrg    }
1158d522f475Smrg    return eightBitSelectionTargets;
1159d522f475Smrg}
1160d522f475Smrg
1161d522f475Smrg#define isSELECT(value) (!strcmp(value, "SELECT"))
1162d522f475Smrg
1163d522f475Smrgstatic void
1164d522f475SmrgUnmapSelections(XtermWidget xw)
1165d522f475Smrg{
1166d522f475Smrg    TScreen *screen = &(xw->screen);
1167d522f475Smrg    Cardinal n;
1168d522f475Smrg
1169d522f475Smrg    if (screen->mappedSelect) {
1170d522f475Smrg	for (n = 0; screen->mappedSelect[n] != 0; ++n)
1171d522f475Smrg	    free(screen->mappedSelect[n]);
1172d522f475Smrg	free(screen->mappedSelect);
1173d522f475Smrg	screen->mappedSelect = 0;
1174d522f475Smrg    }
1175d522f475Smrg}
1176d522f475Smrg
1177d522f475Smrg/*
1178d522f475Smrg * xterm generally uses the primary selection.  Some applications prefer
1179d522f475Smrg * (or are limited to) the clipboard.  Since the translations resource is
1180d522f475Smrg * complicated, users seldom change the way it affects selection.  But it
1181d522f475Smrg * is simple to remap the choice between primary and clipboard before the
1182d522f475Smrg * call to XmuInternStrings().
1183d522f475Smrg */
1184d522f475Smrgstatic String *
1185d522f475SmrgMapSelections(XtermWidget xw, String * params, Cardinal num_params)
1186d522f475Smrg{
1187d522f475Smrg    String *result = params;
1188d522f475Smrg
1189d522f475Smrg    if (num_params > 0) {
1190d522f475Smrg	Cardinal j;
1191d522f475Smrg	Boolean map = False;
1192d522f475Smrg
1193d522f475Smrg	for (j = 0; j < num_params; ++j) {
1194d522f475Smrg	    TRACE(("param[%d]:%s\n", j, params[j]));
1195d522f475Smrg	    if (isSELECT(params[j])) {
1196d522f475Smrg		map = True;
1197d522f475Smrg		break;
1198d522f475Smrg	    }
1199d522f475Smrg	}
1200d522f475Smrg	if (map) {
1201d522f475Smrg	    const char *mapTo = (xw->screen.selectToClipboard
1202d522f475Smrg				 ? "CLIPBOARD"
1203d522f475Smrg				 : "PRIMARY");
1204d522f475Smrg
1205d522f475Smrg	    UnmapSelections(xw);
1206d522f475Smrg	    if ((result = TypeMallocN(String, num_params + 1)) != 0) {
1207d522f475Smrg		result[num_params] = 0;
1208d522f475Smrg		for (j = 0; j < num_params; ++j) {
1209d522f475Smrg		    result[j] = x_strdup((isSELECT(params[j])
1210d522f475Smrg					  ? mapTo
1211d522f475Smrg					  : params[j]));
1212d522f475Smrg		    if (result[j] == 0) {
1213d522f475Smrg			UnmapSelections(xw);
1214d522f475Smrg			result = 0;
1215d522f475Smrg			break;
1216d522f475Smrg		    }
1217d522f475Smrg		}
1218d522f475Smrg		xw->screen.mappedSelect = result;
1219d522f475Smrg	    }
1220d522f475Smrg	}
1221d522f475Smrg    }
1222d522f475Smrg    return result;
1223d522f475Smrg}
1224d522f475Smrg
1225d522f475Smrg/*
1226d522f475Smrg * Lookup the cut-buffer number, which will be in the range 0-7.
1227d522f475Smrg * If it is not a cut-buffer, it is the primary selection (-1).
1228d522f475Smrg */
1229d522f475Smrgstatic int
1230d522f475SmrgCutBuffer(unsigned code)
1231d522f475Smrg{
1232d522f475Smrg    int cutbuffer;
1233d522f475Smrg    switch (code) {
1234d522f475Smrg    case XA_CUT_BUFFER0:
1235d522f475Smrg	cutbuffer = 0;
1236d522f475Smrg	break;
1237d522f475Smrg    case XA_CUT_BUFFER1:
1238d522f475Smrg	cutbuffer = 1;
1239d522f475Smrg	break;
1240d522f475Smrg    case XA_CUT_BUFFER2:
1241d522f475Smrg	cutbuffer = 2;
1242d522f475Smrg	break;
1243d522f475Smrg    case XA_CUT_BUFFER3:
1244d522f475Smrg	cutbuffer = 3;
1245d522f475Smrg	break;
1246d522f475Smrg    case XA_CUT_BUFFER4:
1247d522f475Smrg	cutbuffer = 4;
1248d522f475Smrg	break;
1249d522f475Smrg    case XA_CUT_BUFFER5:
1250d522f475Smrg	cutbuffer = 5;
1251d522f475Smrg	break;
1252d522f475Smrg    case XA_CUT_BUFFER6:
1253d522f475Smrg	cutbuffer = 6;
1254d522f475Smrg	break;
1255d522f475Smrg    case XA_CUT_BUFFER7:
1256d522f475Smrg	cutbuffer = 7;
1257d522f475Smrg	break;
1258d522f475Smrg    default:
1259d522f475Smrg	cutbuffer = -1;
1260d522f475Smrg	break;
1261d522f475Smrg    }
1262d522f475Smrg    return cutbuffer;
1263d522f475Smrg}
1264d522f475Smrg
1265d522f475Smrg#if OPT_PASTE64
1266d522f475Smrgstatic void
1267d522f475SmrgFinishPaste64(XtermWidget xw)
1268d522f475Smrg{
1269d522f475Smrg    TRACE(("FinishPaste64(%d)\n", xw->screen.base64_paste));
1270d522f475Smrg    if (xw->screen.base64_paste) {
1271d522f475Smrg	xw->screen.base64_paste = 0;
1272d522f475Smrg	unparseputc1(xw, xw->screen.base64_final);
1273d522f475Smrg	unparse_end(xw);
1274d522f475Smrg    }
1275d522f475Smrg}
1276d522f475Smrg#endif
1277d522f475Smrg
1278d522f475Smrg#if !OPT_PASTE64
1279d522f475Smrgstatic
1280d522f475Smrg#endif
1281d522f475Smrgvoid
1282d522f475SmrgxtermGetSelection(Widget w,
1283d522f475Smrg		  Time ev_time,
1284d522f475Smrg		  String * params,	/* selections in precedence order */
1285d522f475Smrg		  Cardinal num_params,
1286d522f475Smrg		  Atom * targets)
1287d522f475Smrg{
1288d522f475Smrg    Atom selection;
1289d522f475Smrg    int cutbuffer;
1290d522f475Smrg    Atom target;
1291d522f475Smrg
1292d522f475Smrg    if (!IsXtermWidget(w))
1293d522f475Smrg	return;
1294d522f475Smrg
1295d522f475Smrg    TRACE(("xtermGetSelection\n"));
1296d522f475Smrg    params = MapSelections((XtermWidget) w, params, num_params);
1297d522f475Smrg
1298d522f475Smrg    XmuInternStrings(XtDisplay(w), params, (Cardinal) 1, &selection);
1299d522f475Smrg    cutbuffer = CutBuffer(selection);
1300d522f475Smrg
1301d522f475Smrg    TRACE(("Cutbuffer: %d, target: %lu\n", cutbuffer,
1302d522f475Smrg	   targets ? (unsigned long) targets[0] : 0));
1303d522f475Smrg
1304d522f475Smrg    if (cutbuffer >= 0) {
1305d522f475Smrg	int inbytes;
1306d522f475Smrg	unsigned long nbytes;
1307d522f475Smrg	int fmt8 = 8;
1308d522f475Smrg	Atom type = XA_STRING;
1309d522f475Smrg	char *line;
1310d522f475Smrg
1311d522f475Smrg	/* 'line' is freed in SelectionReceived */
1312d522f475Smrg	line = XFetchBuffer(XtDisplay(w), &inbytes, cutbuffer);
1313d522f475Smrg	nbytes = (unsigned long) inbytes;
1314d522f475Smrg
1315d522f475Smrg	if (nbytes > 0)
1316d522f475Smrg	    SelectionReceived(w, NULL, &selection, &type, (XtPointer) line,
1317d522f475Smrg			      &nbytes, &fmt8);
1318d522f475Smrg	else if (num_params > 1) {
1319d522f475Smrg	    xtermGetSelection(w, ev_time, params + 1, num_params - 1, NULL);
1320d522f475Smrg	}
1321d522f475Smrg#if OPT_PASTE64
1322d522f475Smrg	else {
1323d522f475Smrg	    FinishPaste64((XtermWidget) w);
1324d522f475Smrg	}
1325d522f475Smrg#endif
1326d522f475Smrg	return;
1327d522f475Smrg    } else {
1328d522f475Smrg	struct _SelectionList *list;
1329d522f475Smrg
1330d522f475Smrg	if (targets == NULL || targets[0] == None) {
1331d522f475Smrg	    targets = _SelectionTargets(w);
1332d522f475Smrg	}
1333d522f475Smrg
1334d522f475Smrg	if (targets != 0) {
1335d522f475Smrg	    target = targets[0];
1336d522f475Smrg
1337d522f475Smrg	    if (targets[1] == None) {	/* last target in list */
1338d522f475Smrg		params++;
1339d522f475Smrg		num_params--;
1340d522f475Smrg		targets = _SelectionTargets(w);
1341d522f475Smrg	    } else {
1342d522f475Smrg		targets = &(targets[1]);
1343d522f475Smrg	    }
1344d522f475Smrg
1345d522f475Smrg	    if (num_params) {
1346d522f475Smrg		/* 'list' is freed in SelectionReceived */
1347d522f475Smrg		list = XtNew(struct _SelectionList);
1348d522f475Smrg		if (list != 0) {
1349d522f475Smrg		    list->params = params;
1350d522f475Smrg		    list->count = num_params;
1351d522f475Smrg		    list->targets = targets;
1352d522f475Smrg		    list->time = ev_time;
1353d522f475Smrg		}
1354d522f475Smrg	    } else {
1355d522f475Smrg		list = NULL;
1356d522f475Smrg	    }
1357d522f475Smrg
1358d522f475Smrg	    XtGetSelectionValue(w, selection,
1359d522f475Smrg				target,
1360d522f475Smrg				SelectionReceived,
1361d522f475Smrg				(XtPointer) list, ev_time);
1362d522f475Smrg	}
1363d522f475Smrg    }
1364d522f475Smrg}
1365d522f475Smrg
1366d522f475Smrg#if OPT_TRACE && OPT_WIDE_CHARS
1367d522f475Smrgstatic void
1368d522f475SmrgGettingSelection(Display * dpy, Atom type, Char * line, unsigned long len)
1369d522f475Smrg{
1370d522f475Smrg    Char *cp;
1371d522f475Smrg    char *name;
1372d522f475Smrg
1373d522f475Smrg    name = XGetAtomName(dpy, type);
1374d522f475Smrg
1375d522f475Smrg    TRACE(("Getting %s (%ld)\n", name, (long int) type));
1376d522f475Smrg    for (cp = line; cp < line + len; cp++) {
1377d522f475Smrg	TRACE(("[%d:%lu]", cp + 1 - line, len));
1378d522f475Smrg	if (isprint(*cp)) {
1379d522f475Smrg	    TRACE(("%c\n", *cp));
1380d522f475Smrg	} else {
1381d522f475Smrg	    TRACE(("\\x%02x\n", *cp));
1382d522f475Smrg	}
1383d522f475Smrg    }
1384d522f475Smrg}
1385d522f475Smrg#else
1386d522f475Smrg#define GettingSelection(dpy,type,line,len)	/* nothing */
1387d522f475Smrg#endif
1388d522f475Smrg
1389d522f475Smrg#ifdef VMS
1390d522f475Smrg#  define tty_vwrite(pty,lag,l)		tt_write(lag,l)
1391d522f475Smrg#else /* !( VMS ) */
1392d522f475Smrg#  define tty_vwrite(pty,lag,l)		v_write(pty,lag,l)
1393d522f475Smrg#endif /* defined VMS */
1394d522f475Smrg
1395d522f475Smrg#if OPT_PASTE64
1396d522f475Smrg/* Return base64 code character given 6-bit number */
1397d522f475Smrgstatic const char base64_code[] = "\
1398d522f475SmrgABCDEFGHIJKLMNOPQRSTUVWXYZ\
1399d522f475Smrgabcdefghijklmnopqrstuvwxyz\
1400d522f475Smrg0123456789+/";
1401d522f475Smrgstatic void
1402d522f475Smrgbase64_flush(TScreen * screen)
1403d522f475Smrg{
1404d522f475Smrg    Char x;
1405d522f475Smrg    switch (screen->base64_count) {
1406d522f475Smrg    case 0:
1407d522f475Smrg	break;
1408d522f475Smrg    case 2:
1409d522f475Smrg	x = base64_code[screen->base64_accu << 4];
1410d522f475Smrg	tty_vwrite(screen->respond, &x, 1);
1411d522f475Smrg	break;
1412d522f475Smrg    case 4:
1413d522f475Smrg	x = base64_code[screen->base64_accu << 2];
1414d522f475Smrg	tty_vwrite(screen->respond, &x, 1);
1415d522f475Smrg	break;
1416d522f475Smrg    }
1417d522f475Smrg    if (screen->base64_pad & 3)
1418d522f475Smrg	tty_vwrite(screen->respond,
1419d522f475Smrg		   (Char *) "===",
1420d522f475Smrg		   (unsigned) (4 - (screen->base64_pad & 3)));
1421d522f475Smrg    screen->base64_count = 0;
1422d522f475Smrg    screen->base64_accu = 0;
1423d522f475Smrg    screen->base64_pad = 0;
1424d522f475Smrg}
1425d522f475Smrg#endif /* OPT_PASTE64 */
1426d522f475Smrg
1427d522f475Smrgstatic void
1428d522f475Smrg_qWriteSelectionData(TScreen * screen, Char * lag, unsigned length)
1429d522f475Smrg{
1430d522f475Smrg#if OPT_PASTE64
1431d522f475Smrg    if (screen->base64_paste) {
1432d522f475Smrg	/* Send data as base64 */
1433d522f475Smrg	Char *p = lag;
1434d522f475Smrg	Char buf[64];
1435d522f475Smrg	unsigned x = 0;
1436d522f475Smrg	while (length--) {
1437d522f475Smrg	    switch (screen->base64_count) {
1438d522f475Smrg	    case 0:
1439d522f475Smrg		buf[x++] = base64_code[*p >> 2];
1440d522f475Smrg		screen->base64_accu = (*p & 0x3);
1441d522f475Smrg		screen->base64_count = 2;
1442d522f475Smrg		++p;
1443d522f475Smrg		break;
1444d522f475Smrg	    case 2:
1445d522f475Smrg		buf[x++] = base64_code[(screen->base64_accu << 4) + (*p >> 4)];
1446d522f475Smrg		screen->base64_accu = (*p & 0xF);
1447d522f475Smrg		screen->base64_count = 4;
1448d522f475Smrg		++p;
1449d522f475Smrg		break;
1450d522f475Smrg	    case 4:
1451d522f475Smrg		buf[x++] = base64_code[(screen->base64_accu << 2) + (*p >> 6)];
1452d522f475Smrg		buf[x++] = base64_code[*p & 0x3F];
1453d522f475Smrg		screen->base64_accu = 0;
1454d522f475Smrg		screen->base64_count = 0;
1455d522f475Smrg		++p;
1456d522f475Smrg		break;
1457d522f475Smrg	    }
1458d522f475Smrg	    if (x >= 63) {
1459d522f475Smrg		/* Write 63 or 64 characters */
1460d522f475Smrg		screen->base64_pad += x;
1461d522f475Smrg		tty_vwrite(screen->respond, buf, x);
1462d522f475Smrg		x = 0;
1463d522f475Smrg	    }
1464d522f475Smrg	}
1465d522f475Smrg	if (x != 0) {
1466d522f475Smrg	    screen->base64_pad += x;
1467d522f475Smrg	    tty_vwrite(screen->respond, buf, x);
1468d522f475Smrg	}
1469d522f475Smrg    } else
1470d522f475Smrg#endif /* OPT_PASTE64 */
1471d522f475Smrg#if OPT_READLINE
1472d522f475Smrg    if (SCREEN_FLAG(screen, paste_quotes)) {
1473d522f475Smrg	while (length--) {
1474d522f475Smrg	    tty_vwrite(screen->respond, (Char *) "\026", 1);	/* Control-V */
1475d522f475Smrg	    tty_vwrite(screen->respond, lag++, 1);
1476d522f475Smrg	}
1477d522f475Smrg    } else
1478d522f475Smrg#endif
1479d522f475Smrg	tty_vwrite(screen->respond, lag, length);
1480d522f475Smrg}
1481d522f475Smrg
1482d522f475Smrgstatic void
1483d522f475Smrg_WriteSelectionData(TScreen * screen, Char * line, int length)
1484d522f475Smrg{
1485d522f475Smrg    /* Write data to pty a line at a time. */
1486d522f475Smrg    /* Doing this one line at a time may no longer be necessary
1487d522f475Smrg       because v_write has been re-written. */
1488d522f475Smrg
1489d522f475Smrg    Char *lag, *end;
1490d522f475Smrg
1491d522f475Smrg    /* in the VMS version, if tt_pasting isn't set to True then qio
1492d522f475Smrg       reads aren't blocked and an infinite loop is entered, where the
1493d522f475Smrg       pasted text shows up as new input, goes in again, shows up
1494d522f475Smrg       again, ad nauseum. */
1495d522f475Smrg#ifdef VMS
1496d522f475Smrg    tt_pasting = True;
1497d522f475Smrg#endif
1498d522f475Smrg
1499d522f475Smrg    end = &line[length];
1500d522f475Smrg    lag = line;
1501d522f475Smrg
1502d522f475Smrg#if OPT_PASTE64
1503d522f475Smrg    if (screen->base64_paste) {
1504d522f475Smrg	_qWriteSelectionData(screen, lag, (unsigned) (end - lag));
1505d522f475Smrg	base64_flush(screen);
1506d522f475Smrg    } else
1507d522f475Smrg#endif
1508d522f475Smrg    {
1509d522f475Smrg	if (!SCREEN_FLAG(screen, paste_literal_nl)) {
1510d522f475Smrg	    Char *cp;
1511d522f475Smrg	    for (cp = line; cp != end; cp++) {
1512d522f475Smrg		if (*cp == '\n') {
1513d522f475Smrg		    *cp = '\r';
1514d522f475Smrg		    _qWriteSelectionData(screen, lag, (unsigned) (cp - lag + 1));
1515d522f475Smrg		    lag = cp + 1;
1516d522f475Smrg		}
1517d522f475Smrg	    }
1518d522f475Smrg	}
1519d522f475Smrg
1520d522f475Smrg	if (lag != end) {
1521d522f475Smrg	    _qWriteSelectionData(screen, lag, (unsigned) (end - lag));
1522d522f475Smrg	}
1523d522f475Smrg    }
1524d522f475Smrg#ifdef VMS
1525d522f475Smrg    tt_pasting = False;
1526d522f475Smrg    tt_start_read();		/* reenable reads or a character may be lost */
1527d522f475Smrg#endif
1528d522f475Smrg}
1529d522f475Smrg
1530d522f475Smrg#if OPT_READLINE
1531d522f475Smrgstatic void
1532d522f475Smrg_WriteKey(TScreen * screen, Char * in)
1533d522f475Smrg{
1534d522f475Smrg    Char line[16];
1535d522f475Smrg    unsigned count = 0;
1536d522f475Smrg    unsigned length = strlen((char *) in);
1537d522f475Smrg
1538d522f475Smrg    if (screen->control_eight_bits) {
1539d522f475Smrg	line[count++] = ANSI_CSI;
1540d522f475Smrg    } else {
1541d522f475Smrg	line[count++] = ANSI_ESC;
1542d522f475Smrg	line[count++] = '[';
1543d522f475Smrg    }
1544d522f475Smrg    while (length--)
1545d522f475Smrg	line[count++] = *in++;
1546d522f475Smrg    line[count++] = '~';
1547d522f475Smrg    tty_vwrite(screen->respond, line, count);
1548d522f475Smrg}
1549d522f475Smrg#endif /* OPT_READLINE */
1550d522f475Smrg
1551d522f475Smrg/* SelectionReceived: stuff received selection text into pty */
1552d522f475Smrg
1553d522f475Smrg/* ARGSUSED */
1554d522f475Smrgstatic void
1555d522f475SmrgSelectionReceived(Widget w,
1556d522f475Smrg		  XtPointer client_data,
1557d522f475Smrg		  Atom * selection GCC_UNUSED,
1558d522f475Smrg		  Atom * type,
1559d522f475Smrg		  XtPointer value,
1560d522f475Smrg		  unsigned long *length,
1561d522f475Smrg		  int *format)
1562d522f475Smrg{
1563d522f475Smrg    char **text_list = NULL;
1564d522f475Smrg    int text_list_count;
1565d522f475Smrg    XTextProperty text_prop;
1566d522f475Smrg    TScreen *screen;
1567d522f475Smrg    Display *dpy;
1568d522f475Smrg#if OPT_TRACE && OPT_WIDE_CHARS
1569d522f475Smrg    Char *line = (Char *) value;
1570d522f475Smrg#endif
1571d522f475Smrg
1572d522f475Smrg    if (!IsXtermWidget(w))
1573d522f475Smrg	return;
1574d522f475Smrg    screen = TScreenOf((XtermWidget) w);
1575d522f475Smrg    dpy = XtDisplay(w);
1576d522f475Smrg
1577d522f475Smrg    if (*type == 0		/*XT_CONVERT_FAIL */
1578d522f475Smrg	|| *length == 0
1579d522f475Smrg	|| value == NULL)
1580d522f475Smrg	goto fail;
1581d522f475Smrg
1582d522f475Smrg    text_prop.value = (unsigned char *) value;
1583d522f475Smrg    text_prop.encoding = *type;
1584d522f475Smrg    text_prop.format = *format;
1585d522f475Smrg    text_prop.nitems = *length;
1586d522f475Smrg
1587d522f475Smrg#if OPT_WIDE_CHARS
1588d522f475Smrg    if (screen->wide_chars) {
1589d522f475Smrg	if (*type == XA_UTF8_STRING(dpy) ||
1590d522f475Smrg	    *type == XA_STRING ||
1591d522f475Smrg	    *type == XA_COMPOUND_TEXT(dpy)) {
1592d522f475Smrg	    GettingSelection(dpy, *type, line, *length);
1593d522f475Smrg	    if (Xutf8TextPropertyToTextList(dpy, &text_prop,
1594d522f475Smrg					    &text_list,
1595d522f475Smrg					    &text_list_count) < 0) {
1596d522f475Smrg		TRACE(("Conversion failed\n"));
1597d522f475Smrg		text_list = NULL;
1598d522f475Smrg	    }
1599d522f475Smrg	}
1600d522f475Smrg    } else
1601d522f475Smrg#endif /* OPT_WIDE_CHARS */
1602d522f475Smrg    {
1603d522f475Smrg	/* Convert the selection to locale's multibyte encoding. */
1604d522f475Smrg
1605d522f475Smrg	if (*type == XA_UTF8_STRING(dpy) ||
1606d522f475Smrg	    *type == XA_STRING ||
1607d522f475Smrg	    *type == XA_COMPOUND_TEXT(dpy)) {
1608d522f475Smrg	    Status rc;
1609d522f475Smrg
1610d522f475Smrg	    GettingSelection(dpy, *type, line, *length);
1611d522f475Smrg
1612d522f475Smrg#if OPT_WIDE_CHARS
1613d522f475Smrg	    if (*type == XA_UTF8_STRING(dpy) &&
1614d522f475Smrg		!(screen->wide_chars || screen->c1_printable)) {
1615d522f475Smrg		rc = Xutf8TextPropertyToTextList(dpy, &text_prop,
1616d522f475Smrg						 &text_list, &text_list_count);
1617d522f475Smrg		if (text_list != NULL && text_list_count != 0) {
1618d522f475Smrg		    int i;
1619d522f475Smrg		    Char *data;
1620d522f475Smrg		    unsigned long size;
1621d522f475Smrg		    for (i = 0; i < text_list_count; ++i) {
1622d522f475Smrg			data = (Char *) text_list[i];
1623d522f475Smrg			size = strlen(text_list[i]);
1624d522f475Smrg			data = UTF8toLatin1(data, size, &size);
1625d522f475Smrg			XFree(text_list[i]);
1626d522f475Smrg			text_list[i] = XtMalloc(size + 1);
1627d522f475Smrg			memcpy(text_list[i], data, size + 1);
1628d522f475Smrg		    }
1629d522f475Smrg		}
1630d522f475Smrg	    } else
1631d522f475Smrg#endif
1632d522f475Smrg	    if (*type == XA_STRING && screen->brokenSelections) {
1633d522f475Smrg		rc = XTextPropertyToStringList(&text_prop,
1634d522f475Smrg					       &text_list, &text_list_count);
1635d522f475Smrg	    } else {
1636d522f475Smrg		rc = XmbTextPropertyToTextList(dpy, &text_prop,
1637d522f475Smrg					       &text_list,
1638d522f475Smrg					       &text_list_count);
1639d522f475Smrg	    }
1640d522f475Smrg	    if (rc < 0) {
1641d522f475Smrg		TRACE(("Conversion failed\n"));
1642d522f475Smrg		text_list = NULL;
1643d522f475Smrg	    }
1644d522f475Smrg	}
1645d522f475Smrg    }
1646d522f475Smrg
1647d522f475Smrg    if (text_list != NULL && text_list_count != 0) {
1648d522f475Smrg	int i;
1649d522f475Smrg
1650d522f475Smrg#if OPT_PASTE64
1651d522f475Smrg	if (screen->base64_paste) {
1652d522f475Smrg	    ;
1653d522f475Smrg	} else
1654d522f475Smrg#endif
1655d522f475Smrg#if OPT_READLINE
1656d522f475Smrg	if (SCREEN_FLAG(screen, paste_brackets)) {
1657d522f475Smrg	    _WriteKey(screen, (Char *) "200");
1658d522f475Smrg	}
1659d522f475Smrg#endif
1660d522f475Smrg	for (i = 0; i < text_list_count; i++) {
1661d522f475Smrg	    int len = strlen(text_list[i]);
1662d522f475Smrg	    _WriteSelectionData(screen, (Char *) text_list[i], len);
1663d522f475Smrg	}
1664d522f475Smrg#if OPT_PASTE64
1665d522f475Smrg	if (screen->base64_paste) {
1666d522f475Smrg	    FinishPaste64((XtermWidget) w);
1667d522f475Smrg	} else
1668d522f475Smrg#endif
1669d522f475Smrg#if OPT_READLINE
1670d522f475Smrg	if (SCREEN_FLAG(screen, paste_brackets)) {
1671d522f475Smrg	    _WriteKey(screen, (Char *) "201");
1672d522f475Smrg	}
1673d522f475Smrg#endif
1674d522f475Smrg	XFreeStringList(text_list);
1675d522f475Smrg    } else
1676d522f475Smrg	goto fail;
1677d522f475Smrg
1678d522f475Smrg    XtFree((char *) client_data);
1679d522f475Smrg    XtFree((char *) value);
1680d522f475Smrg
1681d522f475Smrg    return;
1682d522f475Smrg
1683d522f475Smrg  fail:
1684d522f475Smrg    if (client_data != 0) {
1685d522f475Smrg	struct _SelectionList *list = (struct _SelectionList *) client_data;
1686d522f475Smrg	xtermGetSelection(w, list->time,
1687d522f475Smrg			  list->params, list->count, list->targets);
1688d522f475Smrg	XtFree((char *) client_data);
1689d522f475Smrg#if OPT_PASTE64
1690d522f475Smrg    } else {
1691d522f475Smrg	FinishPaste64((XtermWidget) w);
1692d522f475Smrg#endif
1693d522f475Smrg    }
1694d522f475Smrg    return;
1695d522f475Smrg}
1696d522f475Smrg
1697d522f475Smrgvoid
1698d522f475SmrgHandleInsertSelection(Widget w,
1699d522f475Smrg		      XEvent * event,	/* assumed to be XButtonEvent* */
1700d522f475Smrg		      String * params,	/* selections in precedence order */
1701d522f475Smrg		      Cardinal *num_params)
1702d522f475Smrg{
1703d522f475Smrg    if (IsXtermWidget(w)) {
1704d522f475Smrg	XtermWidget xw = (XtermWidget) w;
1705d522f475Smrg
1706d522f475Smrg	if (!SendMousePosition(xw, event)) {
1707d522f475Smrg#if OPT_READLINE
1708d522f475Smrg	    int ldelta;
1709d522f475Smrg	    TScreen *screen = &(xw->screen);
1710d522f475Smrg	    if ((event->type == ButtonPress || event->type == ButtonRelease)
1711d522f475Smrg	    /* Disable on Shift-mouse, including the application-mouse modes */
1712d522f475Smrg		&& !(KeyModifiers & ShiftMask)
1713d522f475Smrg		&& (screen->send_mouse_pos == MOUSE_OFF)
1714d522f475Smrg		&& SCREEN_FLAG(screen, paste_moves)
1715d522f475Smrg		&& rowOnCurrentLine(screen, eventRow(screen, event), &ldelta))
1716d522f475Smrg		ReadLineMovePoint(screen, eventColBetween(screen, event), ldelta);
1717d522f475Smrg#endif /* OPT_READLINE */
1718d522f475Smrg
1719d522f475Smrg	    xtermGetSelection(w, event->xbutton.time, params, *num_params, NULL);
1720d522f475Smrg	}
1721d522f475Smrg    }
1722d522f475Smrg}
1723d522f475Smrg
1724d522f475Smrgstatic SelectUnit
1725d522f475SmrgEvalSelectUnit(TScreen * screen,
1726d522f475Smrg	       Time buttonDownTime,
1727d522f475Smrg	       SelectUnit defaultUnit,
1728d522f475Smrg	       unsigned int button)
1729d522f475Smrg{
1730d522f475Smrg    SelectUnit result;
1731d522f475Smrg    int delta;
1732d522f475Smrg
1733d522f475Smrg    if (button != screen->lastButton) {
1734d522f475Smrg	delta = term->screen.multiClickTime + 1;
1735d522f475Smrg    } else if (screen->lastButtonUpTime == (Time) 0) {
1736d522f475Smrg	/* first time and once in a blue moon */
1737d522f475Smrg	delta = screen->multiClickTime + 1;
1738d522f475Smrg    } else if (buttonDownTime > screen->lastButtonUpTime) {
1739d522f475Smrg	/* most of the time */
1740d522f475Smrg	delta = buttonDownTime - screen->lastButtonUpTime;
1741d522f475Smrg    } else {
1742d522f475Smrg	/* time has rolled over since lastButtonUpTime */
1743d522f475Smrg	delta = (((Time) ~ 0) - screen->lastButtonUpTime) + buttonDownTime;
1744d522f475Smrg    }
1745d522f475Smrg
1746d522f475Smrg    if (delta > screen->multiClickTime) {
1747d522f475Smrg	screen->numberOfClicks = 1;
1748d522f475Smrg	result = defaultUnit;
1749d522f475Smrg    } else {
1750d522f475Smrg	result = screen->selectMap[screen->numberOfClicks % screen->maxClicks];
1751d522f475Smrg	screen->numberOfClicks += 1;
1752d522f475Smrg    }
1753d522f475Smrg    TRACE(("EvalSelectUnit(%d) = %d\n", screen->numberOfClicks, result));
1754d522f475Smrg    return result;
1755d522f475Smrg}
1756d522f475Smrg
1757d522f475Smrgstatic void
1758d522f475Smrgdo_select_start(XtermWidget xw,
1759d522f475Smrg		XEvent * event,	/* must be XButtonEvent* */
1760d522f475Smrg		CELL * cell)
1761d522f475Smrg{
1762d522f475Smrg    TScreen *screen = &(xw->screen);
1763d522f475Smrg
1764d522f475Smrg    if (SendMousePosition(xw, event))
1765d522f475Smrg	return;
1766d522f475Smrg    screen->selectUnit = EvalSelectUnit(screen,
1767d522f475Smrg					event->xbutton.time,
1768d522f475Smrg					Select_CHAR,
1769d522f475Smrg					event->xbutton.button);
1770d522f475Smrg    screen->replyToEmacs = False;
1771d522f475Smrg
1772d522f475Smrg#if OPT_READLINE
1773d522f475Smrg    lastButtonDownTime = event->xbutton.time;
1774d522f475Smrg#endif
1775d522f475Smrg
1776d522f475Smrg    StartSelect(xw, cell);
1777d522f475Smrg}
1778d522f475Smrg
1779d522f475Smrg/* ARGSUSED */
1780d522f475Smrgvoid
1781d522f475SmrgHandleSelectStart(Widget w,
1782d522f475Smrg		  XEvent * event,	/* must be XButtonEvent* */
1783d522f475Smrg		  String * params GCC_UNUSED,
1784d522f475Smrg		  Cardinal *num_params GCC_UNUSED)
1785d522f475Smrg{
1786d522f475Smrg    if (IsXtermWidget(w)) {
1787d522f475Smrg	XtermWidget xw = (XtermWidget) w;
1788d522f475Smrg	TScreen *screen = &(xw->screen);
1789d522f475Smrg	CELL cell;
1790d522f475Smrg
1791d522f475Smrg	screen->firstValidRow = 0;
1792d522f475Smrg	screen->lastValidRow = screen->max_row;
1793d522f475Smrg	PointToCELL(screen, event->xbutton.y, event->xbutton.x, &cell);
1794d522f475Smrg
1795d522f475Smrg#if OPT_READLINE
1796d522f475Smrg	ExtendingSelection = 0;
1797d522f475Smrg#endif
1798d522f475Smrg
1799d522f475Smrg	do_select_start(xw, event, &cell);
1800d522f475Smrg    }
1801d522f475Smrg}
1802d522f475Smrg
1803d522f475Smrg/* ARGSUSED */
1804d522f475Smrgvoid
1805d522f475SmrgHandleKeyboardSelectStart(Widget w,
1806d522f475Smrg			  XEvent * event,	/* must be XButtonEvent* */
1807d522f475Smrg			  String * params GCC_UNUSED,
1808d522f475Smrg			  Cardinal *num_params GCC_UNUSED)
1809d522f475Smrg{
1810d522f475Smrg    if (IsXtermWidget(w)) {
1811d522f475Smrg	XtermWidget xw = (XtermWidget) w;
1812d522f475Smrg	TScreen *screen = &(xw->screen);
1813d522f475Smrg	do_select_start(xw, event, &screen->cursorp);
1814d522f475Smrg    }
1815d522f475Smrg}
1816d522f475Smrg
1817d522f475Smrgstatic void
1818d522f475SmrgTrackDown(XtermWidget xw, XButtonEvent * event)
1819d522f475Smrg{
1820d522f475Smrg    TScreen *screen = &(xw->screen);
1821d522f475Smrg    CELL cell;
1822d522f475Smrg
1823d522f475Smrg    screen->selectUnit = EvalSelectUnit(screen,
1824d522f475Smrg					event->time,
1825d522f475Smrg					Select_CHAR,
1826d522f475Smrg					event->button);
1827d522f475Smrg    if (screen->numberOfClicks > 1) {
1828d522f475Smrg	PointToCELL(screen, event->y, event->x, &cell);
1829d522f475Smrg	screen->replyToEmacs = True;
1830d522f475Smrg	StartSelect(xw, &cell);
1831d522f475Smrg    } else {
1832d522f475Smrg	screen->waitingForTrackInfo = True;
1833d522f475Smrg	EditorButton(xw, (XButtonEvent *) event);
1834d522f475Smrg    }
1835d522f475Smrg}
1836d522f475Smrg
1837d522f475Smrg#define boundsCheck(x)	if (x < 0) \
1838d522f475Smrg			    x = 0; \
1839d522f475Smrg			else if (x >= screen->max_row) \
1840d522f475Smrg			    x = screen->max_row
1841d522f475Smrg
1842d522f475Smrgvoid
1843d522f475SmrgTrackMouse(XtermWidget xw,
1844d522f475Smrg	   int func,
1845d522f475Smrg	   CELL * start,
1846d522f475Smrg	   int firstrow,
1847d522f475Smrg	   int lastrow)
1848d522f475Smrg{
1849d522f475Smrg    TScreen *screen = &(xw->screen);
1850d522f475Smrg
1851d522f475Smrg    if (screen->waitingForTrackInfo) {	/* if Timed, ignore */
1852d522f475Smrg	screen->waitingForTrackInfo = False;
1853d522f475Smrg
1854d522f475Smrg	if (func != 0) {
1855d522f475Smrg	    CELL first = *start;
1856d522f475Smrg
1857d522f475Smrg	    boundsCheck(first.row);
1858d522f475Smrg	    boundsCheck(firstrow);
1859d522f475Smrg	    boundsCheck(lastrow);
1860d522f475Smrg	    screen->firstValidRow = firstrow;
1861d522f475Smrg	    screen->lastValidRow = lastrow;
1862d522f475Smrg	    screen->replyToEmacs = True;
1863d522f475Smrg	    StartSelect(xw, &first);
1864d522f475Smrg	}
1865d522f475Smrg    }
1866d522f475Smrg}
1867d522f475Smrg
1868d522f475Smrgstatic void
1869d522f475SmrgStartSelect(XtermWidget xw, const CELL * cell)
1870d522f475Smrg{
1871d522f475Smrg    TScreen *screen = &(xw->screen);
1872d522f475Smrg
1873d522f475Smrg    TRACE(("StartSelect row=%d, col=%d\n", cell->row, cell->col));
1874d522f475Smrg    if (screen->cursor_state)
1875d522f475Smrg	HideCursor();
1876d522f475Smrg    if (screen->numberOfClicks == 1) {
1877d522f475Smrg	/* set start of selection */
1878d522f475Smrg	screen->rawPos = *cell;
1879d522f475Smrg    }
1880d522f475Smrg    /* else use old values in rawPos */
1881d522f475Smrg    screen->saveStartR = screen->startExt = screen->rawPos;
1882d522f475Smrg    screen->saveEndR = screen->endExt = screen->rawPos;
1883d522f475Smrg    if (Coordinate(screen, cell) < Coordinate(screen, &(screen->rawPos))) {
1884d522f475Smrg	screen->eventMode = LEFTEXTENSION;
1885d522f475Smrg	screen->startExt = *cell;
1886d522f475Smrg    } else {
1887d522f475Smrg	screen->eventMode = RIGHTEXTENSION;
1888d522f475Smrg	screen->endExt = *cell;
1889d522f475Smrg    }
1890d522f475Smrg    ComputeSelect(xw, &(screen->startExt), &(screen->endExt), False);
1891d522f475Smrg}
1892d522f475Smrg
1893d522f475Smrgstatic void
1894d522f475SmrgEndExtend(XtermWidget xw,
1895d522f475Smrg	  XEvent * event,	/* must be XButtonEvent */
1896d522f475Smrg	  String * params,	/* selections */
1897d522f475Smrg	  Cardinal num_params,
1898d522f475Smrg	  Bool use_cursor_loc)
1899d522f475Smrg{
1900d522f475Smrg    CELL cell;
1901d522f475Smrg    unsigned count;
1902d522f475Smrg    TScreen *screen = &xw->screen;
1903d522f475Smrg    Char line[9];
1904d522f475Smrg
1905d522f475Smrg    if (use_cursor_loc) {
1906d522f475Smrg	cell = screen->cursorp;
1907d522f475Smrg    } else {
1908d522f475Smrg	PointToCELL(screen, event->xbutton.y, event->xbutton.x, &cell);
1909d522f475Smrg    }
1910d522f475Smrg    ExtendExtend(xw, &cell);
1911d522f475Smrg    screen->lastButtonUpTime = event->xbutton.time;
1912d522f475Smrg    screen->lastButton = event->xbutton.button;
1913d522f475Smrg    if (!isSameCELL(&(screen->startSel), &(screen->endSel))) {
1914d522f475Smrg	if (screen->replyToEmacs) {
1915d522f475Smrg	    count = 0;
1916d522f475Smrg	    if (screen->control_eight_bits) {
1917d522f475Smrg		line[count++] = ANSI_CSI;
1918d522f475Smrg	    } else {
1919d522f475Smrg		line[count++] = ANSI_ESC;
1920d522f475Smrg		line[count++] = '[';
1921d522f475Smrg	    }
1922d522f475Smrg	    if (isSameCELL(&(screen->rawPos), &(screen->startSel))
1923d522f475Smrg		&& isSameCELL(&cell, &(screen->endSel))) {
1924d522f475Smrg		/* Use short-form emacs select */
1925d522f475Smrg		line[count++] = 't';
1926d522f475Smrg		line[count++] = ' ' + screen->endSel.col + 1;
1927d522f475Smrg		line[count++] = ' ' + screen->endSel.row + 1;
1928d522f475Smrg	    } else {
1929d522f475Smrg		/* long-form, specify everything */
1930d522f475Smrg		line[count++] = 'T';
1931d522f475Smrg		line[count++] = ' ' + screen->startSel.col + 1;
1932d522f475Smrg		line[count++] = ' ' + screen->startSel.row + 1;
1933d522f475Smrg		line[count++] = ' ' + screen->endSel.col + 1;
1934d522f475Smrg		line[count++] = ' ' + screen->endSel.row + 1;
1935d522f475Smrg		line[count++] = ' ' + cell.col + 1;
1936d522f475Smrg		line[count++] = ' ' + cell.row + 1;
1937d522f475Smrg	    }
1938d522f475Smrg	    v_write(screen->respond, line, count);
1939d522f475Smrg	    TrackText(xw, &zeroCELL, &zeroCELL);
1940d522f475Smrg	}
1941d522f475Smrg    }
1942d522f475Smrg    SelectSet(xw, event, params, num_params);
1943d522f475Smrg    screen->eventMode = NORMAL;
1944d522f475Smrg}
1945d522f475Smrg
1946d522f475Smrgvoid
1947d522f475SmrgHandleSelectSet(Widget w,
1948d522f475Smrg		XEvent * event,
1949d522f475Smrg		String * params,
1950d522f475Smrg		Cardinal *num_params)
1951d522f475Smrg{
1952d522f475Smrg    if (IsXtermWidget(w)) {
1953d522f475Smrg	SelectSet((XtermWidget) w, event, params, *num_params);
1954d522f475Smrg    }
1955d522f475Smrg}
1956d522f475Smrg
1957d522f475Smrg/* ARGSUSED */
1958d522f475Smrgstatic void
1959d522f475SmrgSelectSet(XtermWidget xw,
1960d522f475Smrg	  XEvent * event GCC_UNUSED,
1961d522f475Smrg	  String * params,
1962d522f475Smrg	  Cardinal num_params)
1963d522f475Smrg{
1964d522f475Smrg    TScreen *screen = &(xw->screen);
1965d522f475Smrg
1966d522f475Smrg    TRACE(("SelectSet\n"));
1967d522f475Smrg    /* Only do select stuff if non-null select */
1968d522f475Smrg    if (!isSameCELL(&(screen->startSel), &(screen->endSel))) {
1969d522f475Smrg	SaltTextAway(xw, &(screen->startSel), &(screen->endSel), params, num_params);
1970d522f475Smrg    } else {
1971d522f475Smrg	DisownSelection(xw);
1972d522f475Smrg    }
1973d522f475Smrg}
1974d522f475Smrg
1975d522f475Smrg#define Abs(x)		((x) < 0 ? -(x) : (x))
1976d522f475Smrg
1977d522f475Smrg/* ARGSUSED */
1978d522f475Smrgstatic void
1979d522f475Smrgdo_start_extend(XtermWidget xw,
1980d522f475Smrg		XEvent * event,	/* must be XButtonEvent* */
1981d522f475Smrg		String * params GCC_UNUSED,
1982d522f475Smrg		Cardinal *num_params GCC_UNUSED,
1983d522f475Smrg		Bool use_cursor_loc)
1984d522f475Smrg{
1985d522f475Smrg    TScreen *screen = &(xw->screen);
1986d522f475Smrg    int coord;
1987d522f475Smrg    CELL cell;
1988d522f475Smrg
1989d522f475Smrg    if (!IsXtermWidget(xw))
1990d522f475Smrg	return;
1991d522f475Smrg
1992d522f475Smrg    if (SendMousePosition(xw, event))
1993d522f475Smrg	return;
1994d522f475Smrg
1995d522f475Smrg    screen->firstValidRow = 0;
1996d522f475Smrg    screen->lastValidRow = screen->max_row;
1997d522f475Smrg#if OPT_READLINE
1998d522f475Smrg    if ((KeyModifiers & ShiftMask)
1999d522f475Smrg	|| event->xbutton.button != Button3
2000d522f475Smrg	|| !(SCREEN_FLAG(screen, dclick3_deletes)))
2001d522f475Smrg#endif
2002d522f475Smrg	screen->selectUnit = EvalSelectUnit(screen,
2003d522f475Smrg					    event->xbutton.time,
2004d522f475Smrg					    screen->selectUnit,
2005d522f475Smrg					    event->xbutton.button);
2006d522f475Smrg    screen->replyToEmacs = False;
2007d522f475Smrg
2008d522f475Smrg#if OPT_READLINE
2009d522f475Smrg    CheckSecondPress3(screen, event);
2010d522f475Smrg#endif
2011d522f475Smrg
2012d522f475Smrg    if (screen->numberOfClicks == 1
2013d522f475Smrg	|| (SCREEN_FLAG(screen, dclick3_deletes)	/* Dclick special */
2014d522f475Smrg	    &&!(KeyModifiers & ShiftMask))) {
2015d522f475Smrg	/* Save existing selection so we can reestablish it if the guy
2016d522f475Smrg	   extends past the other end of the selection */
2017d522f475Smrg	screen->saveStartR = screen->startExt = screen->startRaw;
2018d522f475Smrg	screen->saveEndR = screen->endExt = screen->endRaw;
2019d522f475Smrg    } else {
2020d522f475Smrg	/* He just needed the selection mode changed, use old values. */
2021d522f475Smrg	screen->startExt = screen->startRaw = screen->saveStartR;
2022d522f475Smrg	screen->endExt = screen->endRaw = screen->saveEndR;
2023d522f475Smrg    }
2024d522f475Smrg    if (use_cursor_loc) {
2025d522f475Smrg	cell = screen->cursorp;
2026d522f475Smrg    } else {
2027d522f475Smrg	PointToCELL(screen, event->xbutton.y, event->xbutton.x, &cell);
2028d522f475Smrg    }
2029d522f475Smrg    coord = Coordinate(screen, &cell);
2030d522f475Smrg
2031d522f475Smrg    if (Abs(coord - Coordinate(screen, &(screen->startSel)))
2032d522f475Smrg	< Abs(coord - Coordinate(screen, &(screen->endSel)))
2033d522f475Smrg	|| coord < Coordinate(screen, &(screen->startSel))) {
2034d522f475Smrg	/* point is close to left side of selection */
2035d522f475Smrg	screen->eventMode = LEFTEXTENSION;
2036d522f475Smrg	screen->startExt = cell;
2037d522f475Smrg    } else {
2038d522f475Smrg	/* point is close to left side of selection */
2039d522f475Smrg	screen->eventMode = RIGHTEXTENSION;
2040d522f475Smrg	screen->endExt = cell;
2041d522f475Smrg    }
2042d522f475Smrg    ComputeSelect(xw, &(screen->startExt), &(screen->endExt), True);
2043d522f475Smrg
2044d522f475Smrg#if OPT_READLINE
2045d522f475Smrg    if (!isSameCELL(&(screen->startSel), &(screen->endSel)))
2046d522f475Smrg	ExtendingSelection = 1;
2047d522f475Smrg#endif
2048d522f475Smrg}
2049d522f475Smrg
2050d522f475Smrgstatic void
2051d522f475SmrgExtendExtend(XtermWidget xw, const CELL * cell)
2052d522f475Smrg{
2053d522f475Smrg    TScreen *screen = &(xw->screen);
2054d522f475Smrg    int coord = Coordinate(screen, cell);
2055d522f475Smrg
2056d522f475Smrg    TRACE(("ExtendExtend row=%d, col=%d\n", cell->row, cell->col));
2057d522f475Smrg    if (screen->eventMode == LEFTEXTENSION
2058d522f475Smrg	&& ((coord + (screen->selectUnit != Select_CHAR))
2059d522f475Smrg	    > Coordinate(screen, &(screen->endSel)))) {
2060d522f475Smrg	/* Whoops, he's changed his mind.  Do RIGHTEXTENSION */
2061d522f475Smrg	screen->eventMode = RIGHTEXTENSION;
2062d522f475Smrg	screen->startExt = screen->saveStartR;
2063d522f475Smrg    } else if (screen->eventMode == RIGHTEXTENSION
2064d522f475Smrg	       && coord < Coordinate(screen, &(screen->startSel))) {
2065d522f475Smrg	/* Whoops, he's changed his mind.  Do LEFTEXTENSION */
2066d522f475Smrg	screen->eventMode = LEFTEXTENSION;
2067d522f475Smrg	screen->endExt = screen->saveEndR;
2068d522f475Smrg    }
2069d522f475Smrg    if (screen->eventMode == LEFTEXTENSION) {
2070d522f475Smrg	screen->startExt = *cell;
2071d522f475Smrg    } else {
2072d522f475Smrg	screen->endExt = *cell;
2073d522f475Smrg    }
2074d522f475Smrg    ComputeSelect(xw, &(screen->startExt), &(screen->endExt), False);
2075d522f475Smrg
2076d522f475Smrg#if OPT_READLINE
2077d522f475Smrg    if (!isSameCELL(&(screen->startSel), &(screen->endSel)))
2078d522f475Smrg	ExtendingSelection = 1;
2079d522f475Smrg#endif
2080d522f475Smrg}
2081d522f475Smrg
2082d522f475Smrgvoid
2083d522f475SmrgHandleStartExtend(Widget w,
2084d522f475Smrg		  XEvent * event,	/* must be XButtonEvent* */
2085d522f475Smrg		  String * params,	/* unused */
2086d522f475Smrg		  Cardinal *num_params)		/* unused */
2087d522f475Smrg{
2088d522f475Smrg    if (IsXtermWidget(w))
2089d522f475Smrg	do_start_extend((XtermWidget) w, event, params, num_params, False);
2090d522f475Smrg}
2091d522f475Smrg
2092d522f475Smrgvoid
2093d522f475SmrgHandleKeyboardStartExtend(Widget w,
2094d522f475Smrg			  XEvent * event,	/* must be XButtonEvent* */
2095d522f475Smrg			  String * params,	/* unused */
2096d522f475Smrg			  Cardinal *num_params)		/* unused */
2097d522f475Smrg{
2098d522f475Smrg    if (IsXtermWidget(w))
2099d522f475Smrg	do_start_extend((XtermWidget) w, event, params, num_params, True);
2100d522f475Smrg}
2101d522f475Smrg
2102d522f475Smrgvoid
2103d522f475SmrgScrollSelection(TScreen * screen, int amount, Bool always)
2104d522f475Smrg{
2105d522f475Smrg    int minrow = INX2ROW(screen, -screen->savedlines);
2106d522f475Smrg    int maxrow = INX2ROW(screen, screen->max_row);
2107d522f475Smrg    int maxcol = screen->max_col;
2108d522f475Smrg
2109d522f475Smrg#define scroll_update_one(cell) \
2110d522f475Smrg	(cell)->row += amount; \
2111d522f475Smrg	if ((cell)->row < minrow) { \
2112d522f475Smrg	    (cell)->row = minrow; \
2113d522f475Smrg	    (cell)->col = 0; \
2114d522f475Smrg	} \
2115d522f475Smrg	if ((cell)->row > maxrow) { \
2116d522f475Smrg	    (cell)->row = maxrow; \
2117d522f475Smrg	    (cell)->col = maxcol; \
2118d522f475Smrg	}
2119d522f475Smrg
2120d522f475Smrg    scroll_update_one(&(screen->startRaw));
2121d522f475Smrg    scroll_update_one(&(screen->endRaw));
2122d522f475Smrg    scroll_update_one(&(screen->startSel));
2123d522f475Smrg    scroll_update_one(&(screen->endSel));
2124d522f475Smrg
2125d522f475Smrg    scroll_update_one(&(screen->rawPos));
2126d522f475Smrg
2127d522f475Smrg    /*
2128d522f475Smrg     * If we are told to scroll the selection but it lies outside the scrolling
2129d522f475Smrg     * margins, then that could cause the selection to move (bad).  It is not
2130d522f475Smrg     * simple to fix, because this function is called both for the scrollbar
2131d522f475Smrg     * actions as well as application scrolling.  The 'always' flag is set in
2132d522f475Smrg     * the former case.  The rest of the logic handles the latter.
2133d522f475Smrg     */
2134d522f475Smrg    if (ScrnHaveSelection(screen)) {
2135d522f475Smrg	int adjust;
2136d522f475Smrg
2137d522f475Smrg	adjust = ROW2INX(screen, screen->startH.row);
2138d522f475Smrg	if (always
2139d522f475Smrg	    || !ScrnHaveLineMargins(screen)
2140d522f475Smrg	    || ScrnIsLineInMargins(screen, adjust)) {
2141d522f475Smrg	    scroll_update_one(&screen->startH);
2142d522f475Smrg	}
2143d522f475Smrg	adjust = ROW2INX(screen, screen->endH.row);
2144d522f475Smrg	if (always
2145d522f475Smrg	    || !ScrnHaveLineMargins(screen)
2146d522f475Smrg	    || ScrnIsLineInMargins(screen, adjust)) {
2147d522f475Smrg	    scroll_update_one(&screen->endH);
2148d522f475Smrg	}
2149d522f475Smrg    }
2150d522f475Smrg
2151d522f475Smrg    screen->startHCoord = Coordinate(screen, &screen->startH);
2152d522f475Smrg    screen->endHCoord = Coordinate(screen, &screen->endH);
2153d522f475Smrg}
2154d522f475Smrg
2155d522f475Smrg/*ARGSUSED*/
2156d522f475Smrgvoid
2157d522f475SmrgResizeSelection(TScreen * screen GCC_UNUSED, int rows, int cols)
2158d522f475Smrg{
2159d522f475Smrg    rows--;			/* decr to get 0-max */
2160d522f475Smrg    cols--;
2161d522f475Smrg
2162d522f475Smrg    if (screen->startRaw.row > rows)
2163d522f475Smrg	screen->startRaw.row = rows;
2164d522f475Smrg    if (screen->startSel.row > rows)
2165d522f475Smrg	screen->startSel.row = rows;
2166d522f475Smrg    if (screen->endRaw.row > rows)
2167d522f475Smrg	screen->endRaw.row = rows;
2168d522f475Smrg    if (screen->endSel.row > rows)
2169d522f475Smrg	screen->endSel.row = rows;
2170d522f475Smrg    if (screen->rawPos.row > rows)
2171d522f475Smrg	screen->rawPos.row = rows;
2172d522f475Smrg
2173d522f475Smrg    if (screen->startRaw.col > cols)
2174d522f475Smrg	screen->startRaw.col = cols;
2175d522f475Smrg    if (screen->startSel.col > cols)
2176d522f475Smrg	screen->startSel.col = cols;
2177d522f475Smrg    if (screen->endRaw.col > cols)
2178d522f475Smrg	screen->endRaw.col = cols;
2179d522f475Smrg    if (screen->endSel.col > cols)
2180d522f475Smrg	screen->endSel.col = cols;
2181d522f475Smrg    if (screen->rawPos.col > cols)
2182d522f475Smrg	screen->rawPos.col = cols;
2183d522f475Smrg}
2184d522f475Smrg
2185d522f475Smrg#if OPT_WIDE_CHARS
2186d522f475SmrgBool
2187d522f475Smrgiswide(int i)
2188d522f475Smrg{
2189d522f475Smrg    return (i == HIDDEN_CHAR) || (my_wcwidth(i) == 2);
2190d522f475Smrg}
2191d522f475Smrg
2192d522f475Smrg#define isWideCell(row, col) iswide((int)XTERM_CELL(row, col))
2193d522f475Smrg#endif
2194d522f475Smrg
2195d522f475Smrgstatic void
2196d522f475SmrgPointToCELL(TScreen * screen,
2197d522f475Smrg	    int y,
2198d522f475Smrg	    int x,
2199d522f475Smrg	    CELL * cell)
2200d522f475Smrg/* Convert pixel coordinates to character coordinates.
2201d522f475Smrg   Rows are clipped between firstValidRow and lastValidRow.
2202d522f475Smrg   Columns are clipped between to be 0 or greater, but are not clipped to some
2203d522f475Smrg       maximum value. */
2204d522f475Smrg{
2205d522f475Smrg    cell->row = (y - screen->border) / FontHeight(screen);
2206d522f475Smrg    if (cell->row < screen->firstValidRow)
2207d522f475Smrg	cell->row = screen->firstValidRow;
2208d522f475Smrg    else if (cell->row > screen->lastValidRow)
2209d522f475Smrg	cell->row = screen->lastValidRow;
2210d522f475Smrg    cell->col = (x - OriginX(screen)) / FontWidth(screen);
2211d522f475Smrg    if (cell->col < 0)
2212d522f475Smrg	cell->col = 0;
2213d522f475Smrg    else if (cell->col > MaxCols(screen)) {
2214d522f475Smrg	cell->col = MaxCols(screen);
2215d522f475Smrg    }
2216d522f475Smrg#if OPT_WIDE_CHARS
2217d522f475Smrg    /*
2218d522f475Smrg     * If we got a click on the right half of a doublewidth character,
2219d522f475Smrg     * pretend it happened on the left half.
2220d522f475Smrg     */
2221d522f475Smrg    if (cell->col > 0
2222d522f475Smrg	&& isWideCell(cell->row, cell->col - 1)
2223d522f475Smrg	&& (XTERM_CELL(cell->row, cell->col) == HIDDEN_CHAR)) {
2224d522f475Smrg	cell->col -= 1;
2225d522f475Smrg    }
2226d522f475Smrg#endif
2227d522f475Smrg}
2228d522f475Smrg
2229d522f475Smrg/*
2230d522f475Smrg * Find the last column at which text was drawn on the given row.
2231d522f475Smrg */
2232d522f475Smrgstatic int
2233d522f475SmrgLastTextCol(TScreen * screen, int row)
2234d522f475Smrg{
2235d522f475Smrg    int inx = ROW2INX(screen, row);
2236d522f475Smrg    int i;
2237d522f475Smrg    Char *ch;
2238d522f475Smrg
2239d522f475Smrg    if (inx + screen->savedlines >= 0) {
2240d522f475Smrg	for (i = screen->max_col,
2241d522f475Smrg	     ch = SCRN_BUF_ATTRS(screen, inx) + i;
2242d522f475Smrg	     i >= 0 && !(*ch & CHARDRAWN);
2243d522f475Smrg	     ch--, i--) ;
2244d522f475Smrg#if OPT_DEC_CHRSET
2245d522f475Smrg	if (CSET_DOUBLE(SCRN_BUF_CSETS(screen, inx)[0])) {
2246d522f475Smrg	    i *= 2;
2247d522f475Smrg	}
2248d522f475Smrg#endif
2249d522f475Smrg    } else {
2250d522f475Smrg	i = -1;
2251d522f475Smrg    }
2252d522f475Smrg    return (i);
2253d522f475Smrg}
2254d522f475Smrg
2255d522f475Smrg#if !OPT_WIDE_CHARS
2256d522f475Smrg/*
2257d522f475Smrg** double click table for cut and paste in 8 bits
2258d522f475Smrg**
2259d522f475Smrg** This table is divided in four parts :
2260d522f475Smrg**
2261d522f475Smrg**	- control characters	[0,0x1f] U [0x80,0x9f]
2262d522f475Smrg**	- separators		[0x20,0x3f] U [0xa0,0xb9]
2263d522f475Smrg**	- binding characters	[0x40,0x7f] U [0xc0,0xff]
2264d522f475Smrg**	- exceptions
2265d522f475Smrg*/
2266d522f475Smrg/* *INDENT-OFF* */
2267d522f475Smrgstatic int charClass[256] =
2268d522f475Smrg{
2269d522f475Smrg/* NUL  SOH  STX  ETX  EOT  ENQ  ACK  BEL */
2270d522f475Smrg    32,  1,    1,   1,   1,   1,   1,   1,
2271d522f475Smrg/*  BS   HT   NL   VT   NP   CR   SO   SI */
2272d522f475Smrg     1,  32,   1,   1,   1,   1,   1,   1,
2273d522f475Smrg/* DLE  DC1  DC2  DC3  DC4  NAK  SYN  ETB */
2274d522f475Smrg     1,   1,   1,   1,   1,   1,   1,   1,
2275d522f475Smrg/* CAN   EM  SUB  ESC   FS   GS   RS   US */
2276d522f475Smrg     1,   1,   1,   1,   1,   1,   1,   1,
2277d522f475Smrg/*  SP    !    "    #    $    %    &    ' */
2278d522f475Smrg    32,  33,  34,  35,  36,  37,  38,  39,
2279d522f475Smrg/*   (    )    *    +    ,    -    .    / */
2280d522f475Smrg    40,  41,  42,  43,  44,  45,  46,  47,
2281d522f475Smrg/*   0    1    2    3    4    5    6    7 */
2282d522f475Smrg    48,  48,  48,  48,  48,  48,  48,  48,
2283d522f475Smrg/*   8    9    :    ;    <    =    >    ? */
2284d522f475Smrg    48,  48,  58,  59,  60,  61,  62,  63,
2285d522f475Smrg/*   @    A    B    C    D    E    F    G */
2286d522f475Smrg    64,  48,  48,  48,  48,  48,  48,  48,
2287d522f475Smrg/*   H    I    J    K    L    M    N    O */
2288d522f475Smrg    48,  48,  48,  48,  48,  48,  48,  48,
2289d522f475Smrg/*   P    Q    R    S    T    U    V    W */
2290d522f475Smrg    48,  48,  48,  48,  48,  48,  48,  48,
2291d522f475Smrg/*   X    Y    Z    [    \    ]    ^    _ */
2292d522f475Smrg    48,  48,  48,  91,  92,  93,  94,  48,
2293d522f475Smrg/*   `    a    b    c    d    e    f    g */
2294d522f475Smrg    96,  48,  48,  48,  48,  48,  48,  48,
2295d522f475Smrg/*   h    i    j    k    l    m    n    o */
2296d522f475Smrg    48,  48,  48,  48,  48,  48,  48,  48,
2297d522f475Smrg/*   p    q    r    s    t    u    v    w */
2298d522f475Smrg    48,  48,  48,  48,  48,  48,  48,  48,
2299d522f475Smrg/*   x    y    z    {    |    }    ~  DEL */
2300d522f475Smrg    48,  48,  48, 123, 124, 125, 126,   1,
2301d522f475Smrg/* x80  x81  x82  x83  IND  NEL  SSA  ESA */
2302d522f475Smrg    1,    1,   1,   1,   1,   1,   1,   1,
2303d522f475Smrg/* HTS  HTJ  VTS  PLD  PLU   RI  SS2  SS3 */
2304d522f475Smrg    1,    1,   1,   1,   1,   1,   1,   1,
2305d522f475Smrg/* DCS  PU1  PU2  STS  CCH   MW  SPA  EPA */
2306d522f475Smrg    1,    1,   1,   1,   1,   1,   1,   1,
2307d522f475Smrg/* x98  x99  x9A  CSI   ST  OSC   PM  APC */
2308d522f475Smrg    1,    1,   1,   1,   1,   1,   1,   1,
2309d522f475Smrg/*   -    i   c/    L   ox   Y-    |   So */
2310d522f475Smrg    160, 161, 162, 163, 164, 165, 166, 167,
2311d522f475Smrg/*  ..   c0   ip   <<    _        R0    - */
2312d522f475Smrg    168, 169, 170, 171, 172, 173, 174, 175,
2313d522f475Smrg/*   o   +-    2    3    '    u   q|    . */
2314d522f475Smrg    176, 177, 178, 179, 180, 181, 182, 183,
2315d522f475Smrg/*   ,    1    2   >>  1/4  1/2  3/4    ? */
2316d522f475Smrg    184, 185, 186, 187, 188, 189, 190, 191,
2317d522f475Smrg/*  A`   A'   A^   A~   A:   Ao   AE   C, */
2318d522f475Smrg     48,  48,  48,  48,  48,  48,  48,  48,
2319d522f475Smrg/*  E`   E'   E^   E:   I`   I'   I^   I: */
2320d522f475Smrg     48,  48,  48,  48,  48,  48,  48,  48,
2321d522f475Smrg/*  D-   N~   O`   O'   O^   O~   O:    X */
2322d522f475Smrg     48,  48,  48,  48,  48,  48,  48, 215,
2323d522f475Smrg/*  O/   U`   U'   U^   U:   Y'    P    B */
2324d522f475Smrg     48,  48,  48,  48,  48,  48,  48,  48,
2325d522f475Smrg/*  a`   a'   a^   a~   a:   ao   ae   c, */
2326d522f475Smrg     48,  48,  48,  48,  48,  48,  48,  48,
2327d522f475Smrg/*  e`   e'   e^   e:    i`  i'   i^   i: */
2328d522f475Smrg     48,  48,  48,  48,  48,  48,  48,  48,
2329d522f475Smrg/*   d   n~   o`   o'   o^   o~   o:   -: */
2330d522f475Smrg     48,  48,  48,  48,  48,  48,  48, 247,
2331d522f475Smrg/*  o/   u`   u'   u^   u:   y'    P   y: */
2332d522f475Smrg     48,  48,  48,  48,  48,  48,  48,  48};
2333d522f475Smrg/* *INDENT-ON* */
2334d522f475Smrg
2335d522f475Smrgint
2336d522f475SmrgSetCharacterClassRange(int low,	/* in range of [0..255] */
2337d522f475Smrg		       int high,
2338d522f475Smrg		       int value)	/* arbitrary */
2339d522f475Smrg{
2340d522f475Smrg
2341d522f475Smrg    if (low < 0 || high > 255 || high < low)
2342d522f475Smrg	return (-1);
2343d522f475Smrg
2344d522f475Smrg    for (; low <= high; low++)
2345d522f475Smrg	charClass[low] = value;
2346d522f475Smrg
2347d522f475Smrg    return (0);
2348d522f475Smrg}
2349d522f475Smrg#endif
2350d522f475Smrg
2351d522f475Smrg#if OPT_WIDE_CHARS
2352d522f475Smrgstatic int
2353d522f475Smrgclass_of(TScreen * screen, CELL * cell)
2354d522f475Smrg{
2355d522f475Smrg    CELL temp = *cell;
2356d522f475Smrg    int value;
2357d522f475Smrg
2358d522f475Smrg#if OPT_DEC_CHRSET
2359d522f475Smrg    if (CSET_DOUBLE(SCRN_BUF_CSETS(screen, ROW2INX(screen, temp.row))[0])) {
2360d522f475Smrg	temp.col /= 2;
2361d522f475Smrg    }
2362d522f475Smrg#endif
2363d522f475Smrg
2364d522f475Smrg    value = XTERM_CELL(temp.row, temp.col);
2365d522f475Smrg    if_OPT_WIDE_CHARS(screen, {
2366d522f475Smrg	return CharacterClass(value);
2367d522f475Smrg    });
2368d522f475Smrg    return CharacterClass(value);
2369d522f475Smrg}
2370d522f475Smrg#define ClassSelects(screen, cell, cclass) \
2371d522f475Smrg	 (class_of(screen, cell) == cclass \
2372d522f475Smrg	 || XTERM_CELL((cell)->row, (cell)->col) == HIDDEN_CHAR)
2373d522f475Smrg#else
2374d522f475Smrg#define class_of(screen, cell) charClass[XTERM_CELL((cell)->row, (cell)->col)]
2375d522f475Smrg#define ClassSelects(screen, cell, cclass) \
2376d522f475Smrg	 (class_of(screen, (cell)) == cclass)
2377d522f475Smrg#endif
2378d522f475Smrg
2379d522f475Smrg/*
2380d522f475Smrg * If the given column is past the end of text on the given row, bump to the
2381d522f475Smrg * beginning of the next line.
2382d522f475Smrg */
2383d522f475Smrgstatic Boolean
2384d522f475SmrgokPosition(TScreen * screen,
2385d522f475Smrg	   CELL * cell)
2386d522f475Smrg{
2387d522f475Smrg    if (cell->col > (LastTextCol(screen, cell->row) + 1)) {
2388d522f475Smrg	cell->col = 0;
2389d522f475Smrg	cell->row += 1;
2390d522f475Smrg	return False;
2391d522f475Smrg    }
2392d522f475Smrg    return True;
2393d522f475Smrg}
2394d522f475Smrg
2395d522f475Smrgstatic void
2396d522f475SmrgtrimLastLine(TScreen * screen, CELL * last)
2397d522f475Smrg{
2398d522f475Smrg    if (screen->cutNewline) {
2399d522f475Smrg	last->col = 0;
2400d522f475Smrg	++last->row;
2401d522f475Smrg    } else {
2402d522f475Smrg	last->col = LastTextCol(screen, last->row) + 1;
2403d522f475Smrg    }
2404d522f475Smrg}
2405d522f475Smrg
2406d522f475Smrg#if OPT_SELECT_REGEX
2407d522f475Smrg/*
2408d522f475Smrg * Returns the first row of a wrapped line.
2409d522f475Smrg */
2410d522f475Smrgstatic int
2411d522f475SmrgfirstRowOfLine(TScreen * screen, int row, Bool visible)
2412d522f475Smrg{
2413d522f475Smrg    int limit = visible ? 0 : -screen->savedlines;
2414d522f475Smrg
2415d522f475Smrg    while (row > limit &&
2416d522f475Smrg	   ScrnTstWrapped(screen, row - 1))
2417d522f475Smrg	--row;
2418d522f475Smrg    return row;
2419d522f475Smrg}
2420d522f475Smrg
2421d522f475Smrg/*
2422d522f475Smrg * Returns the last row of a wrapped line.
2423d522f475Smrg */
2424d522f475Smrgstatic int
2425d522f475SmrglastRowOfLine(TScreen * screen, int row)
2426d522f475Smrg{
2427d522f475Smrg    while (row < screen->max_row &&
2428d522f475Smrg	   ScrnTstWrapped(screen, row))
2429d522f475Smrg	++row;
2430d522f475Smrg    return row;
2431d522f475Smrg}
2432d522f475Smrg
2433d522f475Smrg/*
2434d522f475Smrg * Returns the number of cells on the range of rows.
2435d522f475Smrg */
2436d522f475Smrgstatic unsigned
2437d522f475SmrglengthOfLines(TScreen * screen, int firstRow, int lastRow)
2438d522f475Smrg{
2439d522f475Smrg    unsigned length = 0;
2440d522f475Smrg    int n;
2441d522f475Smrg
2442d522f475Smrg    for (n = firstRow; n <= lastRow; ++n) {
2443d522f475Smrg	int value = LastTextCol(screen, n);
2444d522f475Smrg	if (value >= 0)
2445d522f475Smrg	    length += (value + 1);
2446d522f475Smrg    }
2447d522f475Smrg    return length;
2448d522f475Smrg}
2449d522f475Smrg
2450d522f475Smrg/*
2451d522f475Smrg * Make a copy of the wrapped-line which corresponds to the given row as a
2452d522f475Smrg * string of bytes.  Construct an index for the columns from the beginning of
2453d522f475Smrg * the line.
2454d522f475Smrg */
2455d522f475Smrgstatic char *
2456d522f475Smrgmake_indexed_text(TScreen * screen, int row, unsigned length, int *indexed)
2457d522f475Smrg{
2458d522f475Smrg    Char *result = 0;
2459d522f475Smrg    unsigned need = (length + 1);
2460d522f475Smrg
2461d522f475Smrg    /*
2462d522f475Smrg     * Get a quick upper bound to the number of bytes needed, if the whole
2463d522f475Smrg     * string were UTF-8.
2464d522f475Smrg     */
2465d522f475Smrg    if_OPT_WIDE_CHARS(screen, {
2466d522f475Smrg	need *= (MAX_PTRS * 6);
2467d522f475Smrg    });
2468d522f475Smrg
2469d522f475Smrg    if ((result = TypeCallocN(Char, need + 1)) != 0) {
2470d522f475Smrg	unsigned used = 0;
2471d522f475Smrg	Char *last = result;
2472d522f475Smrg
2473d522f475Smrg	do {
2474d522f475Smrg	    int col = 0;
2475d522f475Smrg	    int limit = LastTextCol(screen, row);
2476d522f475Smrg
2477d522f475Smrg	    while (col <= limit) {
2478d522f475Smrg		Char *next = last;
2479d522f475Smrg		unsigned data = XTERM_CELL(row, col);
2480d522f475Smrg
2481d522f475Smrg		/* some internal points may not be drawn */
2482d522f475Smrg		if (data == 0)
2483d522f475Smrg		    data = ' ';
2484d522f475Smrg
2485d522f475Smrg		if_WIDE_OR_NARROW(screen, {
2486d522f475Smrg		    next = convertToUTF8(last, data);
2487d522f475Smrg		}
2488d522f475Smrg		, {
2489d522f475Smrg		    *next++ = CharOf(data);
2490d522f475Smrg		});
2491d522f475Smrg
2492d522f475Smrg		if_OPT_WIDE_CHARS(screen, {
2493d522f475Smrg		    int off;
2494d522f475Smrg		    for (off = OFF_FINAL; off < MAX_PTRS; off += 2) {
2495d522f475Smrg			if ((data = XTERM_CELLC(row, col, off)) == 0)
2496d522f475Smrg			    break;
2497d522f475Smrg			next = convertToUTF8(next, data);
2498d522f475Smrg		    }
2499d522f475Smrg		});
2500d522f475Smrg
2501d522f475Smrg		indexed[used] = last - result;
2502d522f475Smrg		*next = 0;
2503d522f475Smrg		/* TRACE(("index[%d.%d] %d:%s\n", row, used, indexed[used], last)); */
2504d522f475Smrg		last = next;
2505d522f475Smrg		++used;
2506d522f475Smrg		++col;
2507d522f475Smrg		indexed[used] = next - result;
2508d522f475Smrg	    }
2509d522f475Smrg	} while (used < length &&
2510d522f475Smrg		 ScrnTstWrapped(screen, row) &&
2511d522f475Smrg		 ++row < screen->max_row);
2512d522f475Smrg    }
2513d522f475Smrg    /* TRACE(("result:%s\n", result)); */
2514d522f475Smrg    return (char *) result;
2515d522f475Smrg}
2516d522f475Smrg
2517d522f475Smrg/*
2518d522f475Smrg * Find the column given an offset into the character string by using the
2519d522f475Smrg * index constructed in make_indexed_text().
2520d522f475Smrg */
2521d522f475Smrgstatic int
2522d522f475SmrgindexToCol(int *indexed, int len, int off)
2523d522f475Smrg{
2524d522f475Smrg    int col = 0;
2525d522f475Smrg    while (indexed[col] < len) {
2526d522f475Smrg	if (indexed[col] >= off)
2527d522f475Smrg	    break;
2528d522f475Smrg	++col;
2529d522f475Smrg    }
2530d522f475Smrg    return col;
2531d522f475Smrg}
2532d522f475Smrg
2533d522f475Smrg/*
2534d522f475Smrg * Given a row number, and a column offset from that (which may be wrapped),
2535d522f475Smrg * set the cell to the actual row/column values.
2536d522f475Smrg */
2537d522f475Smrgstatic void
2538d522f475SmrgcolumnToCell(TScreen * screen, int row, int col, CELL * cell)
2539d522f475Smrg{
2540d522f475Smrg    while (row < screen->max_row) {
2541d522f475Smrg	int last = LastTextCol(screen, row);
2542d522f475Smrg
2543d522f475Smrg	/* TRACE(("last(%d) = %d, have %d\n", row, last, col)); */
2544d522f475Smrg	if (col <= last) {
2545d522f475Smrg	    break;
2546d522f475Smrg	}
2547d522f475Smrg	/*
2548d522f475Smrg	 * Stop if the current row does not wrap (does not continue the current
2549d522f475Smrg	 * line).
2550d522f475Smrg	 */
2551d522f475Smrg	if (!ScrnTstWrapped(screen, row)) {
2552d522f475Smrg	    col = last + 1;
2553d522f475Smrg	    break;
2554d522f475Smrg	}
2555d522f475Smrg	col -= (last + 1);
2556d522f475Smrg	++row;
2557d522f475Smrg    }
2558d522f475Smrg    if (col < 0)
2559d522f475Smrg	col = 0;
2560d522f475Smrg    cell->row = row;
2561d522f475Smrg    cell->col = col;
2562d522f475Smrg}
2563d522f475Smrg
2564d522f475Smrg/*
2565d522f475Smrg * Given a cell, find the corresponding column offset.
2566d522f475Smrg */
2567d522f475Smrgstatic int
2568d522f475SmrgcellToColumn(TScreen * screen, CELL * cell)
2569d522f475Smrg{
2570d522f475Smrg    int col = cell->col;
2571d522f475Smrg    int row = firstRowOfLine(screen, cell->row, False);
2572d522f475Smrg    while (row < cell->row) {
2573d522f475Smrg	col += LastTextCol(screen, row++);
2574d522f475Smrg    }
2575d522f475Smrg    return col;
2576d522f475Smrg}
2577d522f475Smrg
2578d522f475Smrgstatic void
2579d522f475Smrgdo_select_regex(TScreen * screen, CELL * startc, CELL * endc)
2580d522f475Smrg{
2581d522f475Smrg    int inx = ((screen->numberOfClicks - 1) % screen->maxClicks);
2582d522f475Smrg    char *expr = screen->selectExpr[inx];
2583d522f475Smrg    regex_t preg;
2584d522f475Smrg    regmatch_t match;
2585d522f475Smrg    char *search;
2586d522f475Smrg    int *indexed;
2587d522f475Smrg
2588d522f475Smrg    TRACE(("Select_REGEX:%s\n", NonNull(expr)));
2589d522f475Smrg    if (okPosition(screen, startc) && expr != 0) {
2590d522f475Smrg	if (regcomp(&preg, expr, REG_EXTENDED) == 0) {
2591d522f475Smrg	    int firstRow = firstRowOfLine(screen, startc->row, True);
2592d522f475Smrg	    int lastRow = lastRowOfLine(screen, firstRow);
2593d522f475Smrg	    unsigned size = lengthOfLines(screen, firstRow, lastRow);
2594d522f475Smrg	    int actual = cellToColumn(screen, startc);
2595d522f475Smrg
2596d522f475Smrg	    TRACE(("regcomp ok rows %d..%d bytes %d\n",
2597d522f475Smrg		   firstRow, lastRow, size));
2598d522f475Smrg
2599d522f475Smrg	    if ((indexed = TypeCallocN(int, size + 1)) != 0) {
2600d522f475Smrg		if ((search = make_indexed_text(screen,
2601d522f475Smrg						firstRow,
2602d522f475Smrg						size,
2603d522f475Smrg						indexed)) != 0) {
2604d522f475Smrg		    int len = strlen(search);
2605d522f475Smrg		    int col;
2606d522f475Smrg		    int best_col = -1;
2607d522f475Smrg		    int best_len = -1;
2608d522f475Smrg
2609d522f475Smrg		    for (col = 0; indexed[col] < len; ++col) {
2610d522f475Smrg			if (regexec(&preg,
2611d522f475Smrg				    search + indexed[col],
2612d522f475Smrg				    1, &match, 0) == 0) {
2613d522f475Smrg			    int start_inx = match.rm_so + indexed[col];
2614d522f475Smrg			    int finis_inx = match.rm_eo + indexed[col];
2615d522f475Smrg			    int start_col = indexToCol(indexed, len, start_inx);
2616d522f475Smrg			    int finis_col = indexToCol(indexed, len, finis_inx);
2617d522f475Smrg
2618d522f475Smrg			    if (start_col <= actual &&
2619d522f475Smrg				actual < finis_col) {
2620d522f475Smrg				int test = finis_col - start_col;
2621d522f475Smrg				if (best_len < test) {
2622d522f475Smrg				    best_len = test;
2623d522f475Smrg				    best_col = start_col;
2624d522f475Smrg				    TRACE(("match column %d len %d\n",
2625d522f475Smrg					   best_col,
2626d522f475Smrg					   best_len));
2627d522f475Smrg				}
2628d522f475Smrg			    }
2629d522f475Smrg			}
2630d522f475Smrg		    }
2631d522f475Smrg		    if (best_col >= 0) {
2632d522f475Smrg			int best_nxt = best_col + best_len;
2633d522f475Smrg			columnToCell(screen, firstRow, best_col, startc);
2634d522f475Smrg			columnToCell(screen, firstRow, best_nxt, endc);
2635d522f475Smrg			TRACE(("search::%s\n", search));
2636d522f475Smrg			TRACE(("indexed:%d..%d -> %d..%d\n",
2637d522f475Smrg			       best_col, best_nxt,
2638d522f475Smrg			       indexed[best_col],
2639d522f475Smrg			       indexed[best_nxt]));
2640d522f475Smrg			TRACE(("matched:%d:%s\n",
2641d522f475Smrg			       indexed[best_nxt] + 1 -
2642d522f475Smrg			       indexed[best_col],
2643d522f475Smrg			       visibleChars(PAIRED_CHARS((Char *) (search +
2644d522f475Smrg								   indexed[best_col]),
2645d522f475Smrg							 0),
2646d522f475Smrg					    (unsigned) (indexed[best_nxt] +
2647d522f475Smrg							1 -
2648d522f475Smrg							indexed[best_col]))));
2649d522f475Smrg		    }
2650d522f475Smrg		    free(search);
2651d522f475Smrg		}
2652d522f475Smrg		free(indexed);
2653d522f475Smrg	    }
2654d522f475Smrg	    regfree(&preg);
2655d522f475Smrg	}
2656d522f475Smrg    }
2657d522f475Smrg}
2658d522f475Smrg#endif /* OPT_SELECT_REGEX */
2659d522f475Smrg
2660d522f475Smrg/*
2661d522f475Smrg * sets startSel endSel
2662d522f475Smrg * ensuring that they have legal values
2663d522f475Smrg */
2664d522f475Smrgstatic void
2665d522f475SmrgComputeSelect(XtermWidget xw,
2666d522f475Smrg	      CELL * startc,
2667d522f475Smrg	      CELL * endc,
2668d522f475Smrg	      Bool extend)
2669d522f475Smrg{
2670d522f475Smrg    TScreen *screen = &(xw->screen);
2671d522f475Smrg    int length;
2672d522f475Smrg    int cclass;
2673d522f475Smrg    CELL first = *startc;
2674d522f475Smrg    CELL last = *endc;
2675d522f475Smrg
2676d522f475Smrg    TRACE(("ComputeSelect(startRow=%d, startCol=%d, endRow=%d, endCol=%d, %sextend)\n",
2677d522f475Smrg	   first.row, first.col,
2678d522f475Smrg	   last.row, last.col,
2679d522f475Smrg	   extend ? "" : "no"));
2680d522f475Smrg
2681d522f475Smrg#if OPT_WIDE_CHARS
2682d522f475Smrg    if (first.col > 1
2683d522f475Smrg	&& isWideCell(first.row, first.col - 1)
2684d522f475Smrg	&& XTERM_CELL(first.row, first.col - 0) == HIDDEN_CHAR) {
2685d522f475Smrg	fprintf(stderr, "Adjusting start. Changing downwards from %i.\n", first.col);
2686d522f475Smrg	first.col -= 1;
2687d522f475Smrg	if (last.col == (first.col + 1))
2688d522f475Smrg	    last.col--;
2689d522f475Smrg    }
2690d522f475Smrg
2691d522f475Smrg    if (last.col > 1
2692d522f475Smrg	&& isWideCell(last.row, last.col - 1)
2693d522f475Smrg	&& XTERM_CELL(last.row, last.col) == HIDDEN_CHAR) {
2694d522f475Smrg	last.col += 1;
2695d522f475Smrg    }
2696d522f475Smrg#endif
2697d522f475Smrg
2698d522f475Smrg    if (Coordinate(screen, &first) <= Coordinate(screen, &last)) {
2699d522f475Smrg	screen->startSel = screen->startRaw = first;
2700d522f475Smrg	screen->endSel = screen->endRaw = last;
2701d522f475Smrg    } else {			/* Swap them */
2702d522f475Smrg	screen->startSel = screen->startRaw = last;
2703d522f475Smrg	screen->endSel = screen->endRaw = first;
2704d522f475Smrg    }
2705d522f475Smrg
2706d522f475Smrg    switch (screen->selectUnit) {
2707d522f475Smrg    case Select_CHAR:
2708d522f475Smrg	(void) okPosition(screen, &(screen->startSel));
2709d522f475Smrg	(void) okPosition(screen, &(screen->endSel));
2710d522f475Smrg	break;
2711d522f475Smrg
2712d522f475Smrg    case Select_WORD:
2713d522f475Smrg	TRACE(("Select_WORD\n"));
2714d522f475Smrg	if (okPosition(screen, &(screen->startSel))) {
2715d522f475Smrg	    cclass = class_of(screen, &(screen->startSel));
2716d522f475Smrg	    do {
2717d522f475Smrg		--screen->startSel.col;
2718d522f475Smrg		if (screen->startSel.row > 0
2719d522f475Smrg		    && screen->startSel.col < 0
2720d522f475Smrg		    && ScrnTstWrapped(screen, screen->startSel.row - 1)) {
2721d522f475Smrg		    --screen->startSel.row;
2722d522f475Smrg		    screen->startSel.col = LastTextCol(screen, screen->startSel.row);
2723d522f475Smrg		}
2724d522f475Smrg	    } while (screen->startSel.col >= 0
2725d522f475Smrg		     && ClassSelects(screen, &(screen->startSel), cclass));
2726d522f475Smrg	    ++screen->startSel.col;
2727d522f475Smrg	}
2728d522f475Smrg#if OPT_WIDE_CHARS
2729d522f475Smrg	if (screen->startSel.col
2730d522f475Smrg	    && XTERM_CELL(screen->startSel.row,
2731d522f475Smrg			  screen->startSel.col) == HIDDEN_CHAR)
2732d522f475Smrg	    screen->startSel.col++;
2733d522f475Smrg#endif
2734d522f475Smrg
2735d522f475Smrg	if (okPosition(screen, &(screen->endSel))) {
2736d522f475Smrg	    length = LastTextCol(screen, screen->endSel.row);
2737d522f475Smrg	    cclass = class_of(screen, &(screen->endSel));
2738d522f475Smrg	    do {
2739d522f475Smrg		++screen->endSel.col;
2740d522f475Smrg		if (screen->endSel.col > length
2741d522f475Smrg		    && ScrnTstWrapped(screen, screen->endSel.row)) {
2742d522f475Smrg		    screen->endSel.col = 0;
2743d522f475Smrg		    ++screen->endSel.row;
2744d522f475Smrg		    length = LastTextCol(screen, screen->endSel.row);
2745d522f475Smrg		}
2746d522f475Smrg	    } while (screen->endSel.col <= length
2747d522f475Smrg		     && ClassSelects(screen, &(screen->endSel), cclass));
2748d522f475Smrg	    /* Word-select selects if pointing to any char in "word",
2749d522f475Smrg	     * especially note that it includes the last character in a word.
2750d522f475Smrg	     * So we do no --endSel.col and do special eol handling.
2751d522f475Smrg	     */
2752d522f475Smrg	    if (screen->endSel.col > length + 1) {
2753d522f475Smrg		screen->endSel.col = 0;
2754d522f475Smrg		++screen->endSel.row;
2755d522f475Smrg	    }
2756d522f475Smrg	}
2757d522f475Smrg#if OPT_WIDE_CHARS
2758d522f475Smrg	if (screen->endSel.col
2759d522f475Smrg	    && XTERM_CELL(screen->endSel.row,
2760d522f475Smrg			  screen->endSel.col) == HIDDEN_CHAR)
2761d522f475Smrg	    screen->endSel.col++;
2762d522f475Smrg#endif
2763d522f475Smrg
2764d522f475Smrg	screen->saveStartW = screen->startSel;
2765d522f475Smrg	break;
2766d522f475Smrg
2767d522f475Smrg    case Select_LINE:
2768d522f475Smrg	TRACE(("Select_LINE\n"));
2769d522f475Smrg	while (ScrnTstWrapped(screen, screen->endSel.row)) {
2770d522f475Smrg	    ++screen->endSel.row;
2771d522f475Smrg	}
2772d522f475Smrg	if (screen->cutToBeginningOfLine
2773d522f475Smrg	    || screen->startSel.row < screen->saveStartW.row) {
2774d522f475Smrg	    screen->startSel.col = 0;
2775d522f475Smrg	    while (screen->startSel.row > 0
2776d522f475Smrg		   && ScrnTstWrapped(screen, screen->startSel.row - 1)) {
2777d522f475Smrg		--screen->startSel.row;
2778d522f475Smrg	    }
2779d522f475Smrg	} else if (!extend) {
2780d522f475Smrg	    if ((first.row < screen->saveStartW.row)
2781d522f475Smrg		|| (isSameRow(&first, &(screen->saveStartW))
2782d522f475Smrg		    && first.col < screen->saveStartW.col)) {
2783d522f475Smrg		screen->startSel.col = 0;
2784d522f475Smrg		while (screen->startSel.row > 0
2785d522f475Smrg		       && ScrnTstWrapped(screen, screen->startSel.row - 1)) {
2786d522f475Smrg		    --screen->startSel.row;
2787d522f475Smrg		}
2788d522f475Smrg	    } else {
2789d522f475Smrg		screen->startSel = screen->saveStartW;
2790d522f475Smrg	    }
2791d522f475Smrg	}
2792d522f475Smrg	trimLastLine(screen, &(screen->endSel));
2793d522f475Smrg	break;
2794d522f475Smrg
2795d522f475Smrg    case Select_GROUP:		/* paragraph */
2796d522f475Smrg	TRACE(("Select_GROUP\n"));
2797d522f475Smrg	if (okPosition(screen, &(screen->startSel))) {
2798d522f475Smrg	    /* scan backward for beginning of group */
2799d522f475Smrg	    while (screen->startSel.row > 0 &&
2800d522f475Smrg		   (LastTextCol(screen, screen->startSel.row - 1) > 0 ||
2801d522f475Smrg		    ScrnTstWrapped(screen, screen->startSel.row - 1))) {
2802d522f475Smrg		--screen->startSel.row;
2803d522f475Smrg	    }
2804d522f475Smrg	    screen->startSel.col = 0;
2805d522f475Smrg	    /* scan forward for end of group */
2806d522f475Smrg	    while (screen->endSel.row < screen->max_row &&
2807d522f475Smrg		   (LastTextCol(screen, screen->endSel.row + 1) > 0 ||
2808d522f475Smrg		    ScrnTstWrapped(screen, screen->endSel.row))) {
2809d522f475Smrg		++screen->endSel.row;
2810d522f475Smrg	    }
2811d522f475Smrg	    trimLastLine(screen, &(screen->endSel));
2812d522f475Smrg	}
2813d522f475Smrg	break;
2814d522f475Smrg
2815d522f475Smrg    case Select_PAGE:		/* everything one can see */
2816d522f475Smrg	TRACE(("Select_PAGE\n"));
2817d522f475Smrg	screen->startSel.row = 0;
2818d522f475Smrg	screen->startSel.col = 0;
2819d522f475Smrg	screen->endSel.row = screen->max_row + 1;
2820d522f475Smrg	screen->endSel.col = 0;
2821d522f475Smrg	break;
2822d522f475Smrg
2823d522f475Smrg    case Select_ALL:		/* counts scrollback if in normal screen */
2824d522f475Smrg	TRACE(("Select_ALL\n"));
2825d522f475Smrg	screen->startSel.row = -screen->savedlines;
2826d522f475Smrg	screen->startSel.col = 0;
2827d522f475Smrg	screen->endSel.row = screen->max_row + 1;
2828d522f475Smrg	screen->endSel.col = 0;
2829d522f475Smrg	break;
2830d522f475Smrg
2831d522f475Smrg#if OPT_SELECT_REGEX
2832d522f475Smrg    case Select_REGEX:
2833d522f475Smrg	do_select_regex(screen, &(screen->startSel), &(screen->endSel));
2834d522f475Smrg	break;
2835d522f475Smrg#endif
2836d522f475Smrg
2837d522f475Smrg    case NSELECTUNITS:		/* always ignore */
2838d522f475Smrg	return;
2839d522f475Smrg    }
2840d522f475Smrg
2841d522f475Smrg    /* check boundaries */
2842d522f475Smrg    ScrollSelection(screen, 0, False);
2843d522f475Smrg
2844d522f475Smrg    TrackText(xw, &(screen->startSel), &(screen->endSel));
2845d522f475Smrg    return;
2846d522f475Smrg}
2847d522f475Smrg
2848d522f475Smrg/* Guaranteed (first.row, first.col) <= (last.row, last.col) */
2849d522f475Smrgstatic void
2850d522f475SmrgTrackText(XtermWidget xw,
2851d522f475Smrg	  const CELL * firstp,
2852d522f475Smrg	  const CELL * lastp)
2853d522f475Smrg{
2854d522f475Smrg    TScreen *screen = &(xw->screen);
2855d522f475Smrg    int from, to;
2856d522f475Smrg    CELL old_start, old_end;
2857d522f475Smrg    CELL first = *firstp;
2858d522f475Smrg    CELL last = *lastp;
2859d522f475Smrg
2860d522f475Smrg    TRACE(("TrackText(first=%d,%d, last=%d,%d)\n",
2861d522f475Smrg	   first.row, first.col, last.row, last.col));
2862d522f475Smrg
2863d522f475Smrg    old_start = screen->startH;
2864d522f475Smrg    old_end = screen->endH;
2865d522f475Smrg    if (isSameCELL(&first, &old_start) &&
2866d522f475Smrg	isSameCELL(&last, &old_end))
2867d522f475Smrg	return;
2868d522f475Smrg    screen->startH = first;
2869d522f475Smrg    screen->endH = last;
2870d522f475Smrg    from = Coordinate(screen, &screen->startH);
2871d522f475Smrg    to = Coordinate(screen, &screen->endH);
2872d522f475Smrg    if (to <= screen->startHCoord || from > screen->endHCoord) {
2873d522f475Smrg	/* No overlap whatsoever between old and new hilite */
2874d522f475Smrg	ReHiliteText(xw, &old_start, &old_end);
2875d522f475Smrg	ReHiliteText(xw, &first, &last);
2876d522f475Smrg    } else {
2877d522f475Smrg	if (from < screen->startHCoord) {
2878d522f475Smrg	    /* Extend left end */
2879d522f475Smrg	    ReHiliteText(xw, &first, &old_start);
2880d522f475Smrg	} else if (from > screen->startHCoord) {
2881d522f475Smrg	    /* Shorten left end */
2882d522f475Smrg	    ReHiliteText(xw, &old_start, &first);
2883d522f475Smrg	}
2884d522f475Smrg	if (to > screen->endHCoord) {
2885d522f475Smrg	    /* Extend right end */
2886d522f475Smrg	    ReHiliteText(xw, &old_end, &last);
2887d522f475Smrg	} else if (to < screen->endHCoord) {
2888d522f475Smrg	    /* Shorten right end */
2889d522f475Smrg	    ReHiliteText(xw, &last, &old_end);
2890d522f475Smrg	}
2891d522f475Smrg    }
2892d522f475Smrg    screen->startHCoord = from;
2893d522f475Smrg    screen->endHCoord = to;
2894d522f475Smrg}
2895d522f475Smrg
2896d522f475Smrg/* Guaranteed that (first->row, first->col) <= (last->row, last->col) */
2897d522f475Smrgstatic void
2898d522f475SmrgReHiliteText(XtermWidget xw,
2899d522f475Smrg	     CELL * firstp,
2900d522f475Smrg	     CELL * lastp)
2901d522f475Smrg{
2902d522f475Smrg    TScreen *screen = &(xw->screen);
2903d522f475Smrg    int i;
2904d522f475Smrg    CELL first = *firstp;
2905d522f475Smrg    CELL last = *lastp;
2906d522f475Smrg
2907d522f475Smrg    TRACE(("ReHiliteText from %d.%d to %d.%d\n",
2908d522f475Smrg	   first.row, first.col, last.row, last.col));
2909d522f475Smrg
2910d522f475Smrg    if (first.row < 0)
2911d522f475Smrg	first.row = first.col = 0;
2912d522f475Smrg    else if (first.row > screen->max_row)
2913d522f475Smrg	return;			/* nothing to do, since last.row >= first.row */
2914d522f475Smrg
2915d522f475Smrg    if (last.row < 0)
2916d522f475Smrg	return;			/* nothing to do, since first.row <= last.row */
2917d522f475Smrg    else if (last.row > screen->max_row) {
2918d522f475Smrg	last.row = screen->max_row;
2919d522f475Smrg	last.col = MaxCols(screen);
2920d522f475Smrg    }
2921d522f475Smrg    if (isSameCELL(&first, &last))
2922d522f475Smrg	return;
2923d522f475Smrg
2924d522f475Smrg    if (!isSameRow(&first, &last)) {	/* do multiple rows */
2925d522f475Smrg	if ((i = screen->max_col - first.col + 1) > 0) {	/* first row */
2926d522f475Smrg	    ScrnRefresh(xw, first.row, first.col, 1, i, True);
2927d522f475Smrg	}
2928d522f475Smrg	if ((i = last.row - first.row - 1) > 0) {	/* middle rows */
2929d522f475Smrg	    ScrnRefresh(xw, first.row + 1, 0, i, MaxCols(screen), True);
2930d522f475Smrg	}
2931d522f475Smrg	if (last.col > 0 && last.row <= screen->max_row) {	/* last row */
2932d522f475Smrg	    ScrnRefresh(xw, last.row, 0, 1, last.col, True);
2933d522f475Smrg	}
2934d522f475Smrg    } else {			/* do single row */
2935d522f475Smrg	ScrnRefresh(xw, first.row, first.col, 1, last.col - first.col, True);
2936d522f475Smrg    }
2937d522f475Smrg}
2938d522f475Smrg
2939d522f475Smrg/*
2940d522f475Smrg * Guaranteed that (cellc->row, cellc->col) <= (cell->row, cell->col), and that both points are valid
2941d522f475Smrg * (may have cell->row = screen->max_row+1, cell->col = 0).
2942d522f475Smrg */
2943d522f475Smrgstatic void
2944d522f475SmrgSaltTextAway(XtermWidget xw,
2945d522f475Smrg	     CELL * cellc,
2946d522f475Smrg	     CELL * cell,
2947d522f475Smrg	     String * params,	/* selections */
2948d522f475Smrg	     Cardinal num_params)
2949d522f475Smrg{
2950d522f475Smrg    TScreen *screen = &(xw->screen);
2951d522f475Smrg    int i, j = 0;
2952d522f475Smrg    int eol;
2953d522f475Smrg    Char *line;
2954d522f475Smrg    Char *lp;
2955d522f475Smrg    CELL first = *cellc;
2956d522f475Smrg    CELL last = *cell;
2957d522f475Smrg
2958d522f475Smrg    if (isSameRow(&first, &last) && first.col > last.col) {
2959d522f475Smrg	int tmp = first.col;
2960d522f475Smrg	first.col = last.col;
2961d522f475Smrg	last.col = tmp;
2962d522f475Smrg    }
2963d522f475Smrg
2964d522f475Smrg    --last.col;
2965d522f475Smrg    /* first we need to know how long the string is before we can save it */
2966d522f475Smrg
2967d522f475Smrg    if (isSameRow(&last, &first)) {
2968d522f475Smrg	j = Length(screen, first.row, first.col, last.col);
2969d522f475Smrg    } else {			/* two cases, cut is on same line, cut spans multiple lines */
2970d522f475Smrg	j += Length(screen, first.row, first.col, screen->max_col) + 1;
2971d522f475Smrg	for (i = first.row + 1; i < last.row; i++)
2972d522f475Smrg	    j += Length(screen, i, 0, screen->max_col) + 1;
2973d522f475Smrg	if (last.col >= 0)
2974d522f475Smrg	    j += Length(screen, last.row, 0, last.col);
2975d522f475Smrg    }
2976d522f475Smrg
2977d522f475Smrg    /* UTF-8 may require more space */
2978d522f475Smrg    if_OPT_WIDE_CHARS(screen, {
2979d522f475Smrg	j *= 4;
2980d522f475Smrg    });
2981d522f475Smrg
2982d522f475Smrg    /* now get some memory to save it in */
2983d522f475Smrg
2984d522f475Smrg    if (screen->selection_size <= j) {
2985d522f475Smrg	if ((line = (Char *) malloc((unsigned) j + 1)) == 0)
2986d522f475Smrg	    SysError(ERROR_BMALLOC2);
2987d522f475Smrg	XtFree((char *) screen->selection_data);
2988d522f475Smrg	screen->selection_data = line;
2989d522f475Smrg	screen->selection_size = j + 1;
2990d522f475Smrg    } else {
2991d522f475Smrg	line = screen->selection_data;
2992d522f475Smrg    }
2993d522f475Smrg
2994d522f475Smrg    if ((line == 0)
2995d522f475Smrg	|| (j < 0))
2996d522f475Smrg	return;
2997d522f475Smrg
2998d522f475Smrg    line[j] = '\0';		/* make sure it is null terminated */
2999d522f475Smrg    lp = line;			/* lp points to where to save the text */
3000d522f475Smrg    if (isSameRow(&last, &first)) {
3001d522f475Smrg	lp = SaveText(screen, last.row, first.col, last.col, lp, &eol);
3002d522f475Smrg    } else {
3003d522f475Smrg	lp = SaveText(screen, first.row, first.col, screen->max_col, lp, &eol);
3004d522f475Smrg	if (eol)
3005d522f475Smrg	    *lp++ = '\n';	/* put in newline at end of line */
3006d522f475Smrg	for (i = first.row + 1; i < last.row; i++) {
3007d522f475Smrg	    lp = SaveText(screen, i, 0, screen->max_col, lp, &eol);
3008d522f475Smrg	    if (eol)
3009d522f475Smrg		*lp++ = '\n';
3010d522f475Smrg	}
3011d522f475Smrg	if (last.col >= 0)
3012d522f475Smrg	    lp = SaveText(screen, last.row, 0, last.col, lp, &eol);
3013d522f475Smrg    }
3014d522f475Smrg    *lp = '\0';			/* make sure we have end marked */
3015d522f475Smrg
3016d522f475Smrg    TRACE(("Salted TEXT:%d:%s\n", lp - line,
3017d522f475Smrg	   visibleChars(PAIRED_CHARS(line, 0), (unsigned) (lp - line))));
3018d522f475Smrg
3019d522f475Smrg    screen->selection_length = (lp - line);
3020d522f475Smrg    _OwnSelection(xw, params, num_params);
3021d522f475Smrg}
3022d522f475Smrg
3023d522f475Smrg#if OPT_PASTE64
3024d522f475Smrgvoid
3025d522f475SmrgClearSelectionBuffer(TScreen * screen)
3026d522f475Smrg{
3027d522f475Smrg    screen->selection_length = 0;
3028d522f475Smrg    screen->base64_count = 0;
3029d522f475Smrg}
3030d522f475Smrg
3031d522f475Smrgstatic void
3032d522f475SmrgAppendStrToSelectionBuffer(TScreen * screen, Char * text, unsigned len)
3033d522f475Smrg{
3034d522f475Smrg    if (len != 0) {
3035d522f475Smrg	int j = screen->selection_length + len;		/* New length */
3036d522f475Smrg	int k = j + (j >> 2) + 80;	/* New size if we grow buffer: grow by ~50% */
3037d522f475Smrg	if (j + 1 >= screen->selection_size) {
3038d522f475Smrg	    if (!screen->selection_length) {
3039d522f475Smrg		/* New buffer */
3040d522f475Smrg		Char *line;
3041d522f475Smrg		if ((line = (Char *) malloc((unsigned) k)) == 0)
3042d522f475Smrg		    SysError(ERROR_BMALLOC2);
3043d522f475Smrg		XtFree((char *) screen->selection_data);
3044d522f475Smrg		screen->selection_data = line;
3045d522f475Smrg	    } else {
3046d522f475Smrg		/* Realloc buffer */
3047d522f475Smrg		screen->selection_data = (Char *)
3048d522f475Smrg		    realloc(screen->selection_data,
3049d522f475Smrg			    (unsigned) k);
3050d522f475Smrg		if (screen->selection_data == 0)
3051d522f475Smrg		    SysError(ERROR_BMALLOC2);
3052d522f475Smrg	    }
3053d522f475Smrg	    screen->selection_size = k;
3054d522f475Smrg	}
3055d522f475Smrg	memcpy(screen->selection_data + screen->selection_length, text, len);
3056d522f475Smrg	screen->selection_length += len;
3057d522f475Smrg	screen->selection_data[screen->selection_length] = 0;
3058d522f475Smrg    }
3059d522f475Smrg}
3060d522f475Smrg
3061d522f475Smrgvoid
3062d522f475SmrgAppendToSelectionBuffer(TScreen * screen, unsigned c)
3063d522f475Smrg{
3064d522f475Smrg    int six;
3065d522f475Smrg    Char ch;
3066d522f475Smrg
3067d522f475Smrg    /* Decode base64 character */
3068d522f475Smrg    if (c >= 'A' && c <= 'Z')
3069d522f475Smrg	six = c - 'A';
3070d522f475Smrg    else if (c >= 'a' && c <= 'z')
3071d522f475Smrg	six = c - 'a' + 26;
3072d522f475Smrg    else if (c >= '0' && c <= '9')
3073d522f475Smrg	six = c - '0' + 52;
3074d522f475Smrg    else if (c == '+')
3075d522f475Smrg	six = 62;
3076d522f475Smrg    else if (c == '/')
3077d522f475Smrg	six = 63;
3078d522f475Smrg    else
3079d522f475Smrg	return;
3080d522f475Smrg
3081d522f475Smrg    /* Accumulate bytes */
3082d522f475Smrg    switch (screen->base64_count) {
3083d522f475Smrg    case 0:
3084d522f475Smrg	screen->base64_accu = six;
3085d522f475Smrg	screen->base64_count = 6;
3086d522f475Smrg	break;
3087d522f475Smrg
3088d522f475Smrg    case 2:
3089d522f475Smrg	ch = (screen->base64_accu << 6) + six;
3090d522f475Smrg	screen->base64_count = 0;
3091d522f475Smrg	AppendStrToSelectionBuffer(screen, &ch, 1);
3092d522f475Smrg	break;
3093d522f475Smrg
3094d522f475Smrg    case 4:
3095d522f475Smrg	ch = (screen->base64_accu << 4) + (six >> 2);
3096d522f475Smrg	screen->base64_accu = (six & 0x3);
3097d522f475Smrg	screen->base64_count = 2;
3098d522f475Smrg	AppendStrToSelectionBuffer(screen, &ch, 1);
3099d522f475Smrg	break;
3100d522f475Smrg
3101d522f475Smrg    case 6:
3102d522f475Smrg	ch = (screen->base64_accu << 2) + (six >> 4);
3103d522f475Smrg	screen->base64_accu = (six & 0xF);
3104d522f475Smrg	screen->base64_count = 4;
3105d522f475Smrg	AppendStrToSelectionBuffer(screen, &ch, 1);
3106d522f475Smrg	break;
3107d522f475Smrg    }
3108d522f475Smrg}
3109d522f475Smrg
3110d522f475Smrgvoid
3111d522f475SmrgCompleteSelection(XtermWidget xw, char **args, Cardinal len)
3112d522f475Smrg{
3113d522f475Smrg    xw->screen.base64_count = 0;
3114d522f475Smrg    xw->screen.base64_accu = 0;
3115d522f475Smrg    _OwnSelection(xw, args, len);
3116d522f475Smrg}
3117d522f475Smrg#endif /* OPT_PASTE64 */
3118d522f475Smrg
3119d522f475Smrgstatic Bool
3120d522f475Smrg_ConvertSelectionHelper(Widget w,
3121d522f475Smrg			Atom * type,
3122d522f475Smrg			XtPointer *value,
3123d522f475Smrg			unsigned long *length,
3124d522f475Smrg			int *format,
3125d522f475Smrg			int (*conversion_function) (Display *,
3126d522f475Smrg						    char **, int,
3127d522f475Smrg						    XICCEncodingStyle,
3128d522f475Smrg						    XTextProperty *),
3129d522f475Smrg			XICCEncodingStyle conversion_style)
3130d522f475Smrg{
3131d522f475Smrg    if (IsXtermWidget(w)) {
3132d522f475Smrg	Display *dpy = XtDisplay(w);
3133d522f475Smrg	TScreen *screen = TScreenOf((XtermWidget) w);
3134d522f475Smrg	XTextProperty textprop;
3135d522f475Smrg	char *the_data = (char *) screen->selection_data;
3136d522f475Smrg
3137d522f475Smrg	if (conversion_function(dpy, &the_data, 1,
3138d522f475Smrg				conversion_style,
3139d522f475Smrg				&textprop) >= Success) {
3140d522f475Smrg	    *value = (XtPointer) textprop.value;
3141d522f475Smrg	    *length = textprop.nitems;
3142d522f475Smrg	    *type = textprop.encoding;
3143d522f475Smrg	    *format = textprop.format;
3144d522f475Smrg	    return True;
3145d522f475Smrg	}
3146d522f475Smrg    }
3147d522f475Smrg    return False;
3148d522f475Smrg}
3149d522f475Smrg
3150d522f475Smrgstatic Boolean
3151d522f475SmrgConvertSelection(Widget w,
3152d522f475Smrg		 Atom * selection,
3153d522f475Smrg		 Atom * target,
3154d522f475Smrg		 Atom * type,
3155d522f475Smrg		 XtPointer *value,
3156d522f475Smrg		 unsigned long *length,
3157d522f475Smrg		 int *format)
3158d522f475Smrg{
3159d522f475Smrg    Display *dpy = XtDisplay(w);
3160d522f475Smrg    TScreen *screen;
3161d522f475Smrg    Bool result = False;
3162d522f475Smrg
3163d522f475Smrg    if (!IsXtermWidget(w))
3164d522f475Smrg	return False;
3165d522f475Smrg
3166d522f475Smrg    screen = TScreenOf((XtermWidget) w);
3167d522f475Smrg
3168d522f475Smrg    if (screen->selection_data == NULL)
3169d522f475Smrg	return False;		/* can this happen? */
3170d522f475Smrg
3171d522f475Smrg    if (*target == XA_TARGETS(dpy)) {
3172d522f475Smrg	Atom *allocP;
3173d522f475Smrg	Atom *targetP;
3174d522f475Smrg	Atom *std_targets;
3175d522f475Smrg	XPointer std_return = 0;
3176d522f475Smrg	unsigned long std_length;
3177d522f475Smrg
3178d522f475Smrg	TRACE(("ConvertSelection XA_TARGETS(dpy)\n"));
3179d522f475Smrg	if (XmuConvertStandardSelection(w, screen->selection_time, selection,
3180d522f475Smrg					target, type, &std_return,
3181d522f475Smrg					&std_length, format)) {
3182d522f475Smrg	    std_targets = (Atom *) (std_return);
3183d522f475Smrg	    *length = std_length + 6;
3184d522f475Smrg
3185d522f475Smrg	    targetP = (Atom *) XtMalloc(sizeof(Atom) * (*length));
3186d522f475Smrg	    allocP = targetP;
3187d522f475Smrg
3188d522f475Smrg	    *value = (XtPointer) targetP;
3189d522f475Smrg
3190d522f475Smrg	    *targetP++ = XA_STRING;
3191d522f475Smrg	    *targetP++ = XA_TEXT(dpy);
3192d522f475Smrg#ifdef X_HAVE_UTF8_STRING
3193d522f475Smrg	    *targetP++ = XA_COMPOUND_TEXT(dpy);
3194d522f475Smrg	    *targetP++ = XA_UTF8_STRING(dpy);
3195d522f475Smrg#else
3196d522f475Smrg	    *targetP = XA_COMPOUND_TEXT(dpy);
3197d522f475Smrg	    if_OPT_WIDE_CHARS(screen, {
3198d522f475Smrg		*targetP = XA_UTF8_STRING(dpy);
3199d522f475Smrg	    });
3200d522f475Smrg	    targetP++;
3201d522f475Smrg#endif
3202d522f475Smrg	    *targetP++ = XA_LENGTH(dpy);
3203d522f475Smrg	    *targetP++ = XA_LIST_LENGTH(dpy);
3204d522f475Smrg
3205d522f475Smrg	    *length = std_length + (targetP - allocP);
3206d522f475Smrg
3207d522f475Smrg	    memcpy(targetP, std_targets, sizeof(Atom) * std_length);
3208d522f475Smrg	    XtFree((char *) std_targets);
3209d522f475Smrg	    *type = XA_ATOM;
3210d522f475Smrg	    *format = 32;
3211d522f475Smrg	    result = True;
3212d522f475Smrg	}
3213d522f475Smrg    }
3214d522f475Smrg#if OPT_WIDE_CHARS
3215d522f475Smrg    else if (screen->wide_chars && *target == XA_STRING) {
3216d522f475Smrg	TRACE(("ConvertSelection XA_STRING - wide\n"));
3217d522f475Smrg	result =
3218d522f475Smrg	    _ConvertSelectionHelper(w,
3219d522f475Smrg				    type, value, length, format,
3220d522f475Smrg				    Xutf8TextListToTextProperty,
3221d522f475Smrg				    XStringStyle);
3222d522f475Smrg    } else if (screen->wide_chars && *target == XA_UTF8_STRING(dpy)) {
3223d522f475Smrg	TRACE(("ConvertSelection XA_UTF8_STRING(dpy) - wide\n"));
3224d522f475Smrg	result =
3225d522f475Smrg	    _ConvertSelectionHelper(w,
3226d522f475Smrg				    type, value, length, format,
3227d522f475Smrg				    Xutf8TextListToTextProperty,
3228d522f475Smrg				    XUTF8StringStyle);
3229d522f475Smrg    } else if (screen->wide_chars && *target == XA_TEXT(dpy)) {
3230d522f475Smrg	TRACE(("ConvertSelection XA_TEXT(dpy) - wide\n"));
3231d522f475Smrg	result =
3232d522f475Smrg	    _ConvertSelectionHelper(w,
3233d522f475Smrg				    type, value, length, format,
3234d522f475Smrg				    Xutf8TextListToTextProperty,
3235d522f475Smrg				    XStdICCTextStyle);
3236d522f475Smrg    } else if (screen->wide_chars && *target == XA_COMPOUND_TEXT(dpy)) {
3237d522f475Smrg	TRACE(("ConvertSelection XA_COMPOUND_TEXT(dpy) - wide\n"));
3238d522f475Smrg	result =
3239d522f475Smrg	    _ConvertSelectionHelper(w,
3240d522f475Smrg				    type, value, length, format,
3241d522f475Smrg				    Xutf8TextListToTextProperty,
3242d522f475Smrg				    XCompoundTextStyle);
3243d522f475Smrg    }
3244d522f475Smrg#endif
3245d522f475Smrg
3246d522f475Smrg    else if (*target == XA_STRING) {	/* not wide_chars */
3247d522f475Smrg	/* We can only reach this point if the selection requestor
3248d522f475Smrg	   requested STRING before any of TEXT, COMPOUND_TEXT or
3249d522f475Smrg	   UTF8_STRING.  We therefore assume that the requestor is not
3250d522f475Smrg	   properly internationalised, and dump raw eight-bit data
3251d522f475Smrg	   with no conversion into the selection.  Yes, this breaks
3252d522f475Smrg	   the ICCCM in non-Latin-1 locales. */
3253d522f475Smrg	TRACE(("ConvertSelection XA_STRING\n"));
3254d522f475Smrg	*type = XA_STRING;
3255d522f475Smrg	*value = (XtPointer) screen->selection_data;
3256d522f475Smrg	*length = screen->selection_length;
3257d522f475Smrg	*format = 8;
3258d522f475Smrg	result = True;
3259d522f475Smrg    } else if (*target == XA_TEXT(dpy)) {	/* not wide_chars */
3260d522f475Smrg	TRACE(("ConvertSelection XA_TEXT(dpy)\n"));
3261d522f475Smrg	result =
3262d522f475Smrg	    _ConvertSelectionHelper(w,
3263d522f475Smrg				    type, value, length, format,
3264d522f475Smrg				    XmbTextListToTextProperty,
3265d522f475Smrg				    XStdICCTextStyle);
3266d522f475Smrg    } else if (*target == XA_COMPOUND_TEXT(dpy)) {	/* not wide_chars */
3267d522f475Smrg	TRACE(("ConvertSelection XA_COMPOUND_TEXT(dpy)\n"));
3268d522f475Smrg	result =
3269d522f475Smrg	    _ConvertSelectionHelper(w,
3270d522f475Smrg				    type, value, length, format,
3271d522f475Smrg				    XmbTextListToTextProperty,
3272d522f475Smrg				    XCompoundTextStyle);
3273d522f475Smrg    }
3274d522f475Smrg#ifdef X_HAVE_UTF8_STRING
3275d522f475Smrg    else if (*target == XA_UTF8_STRING(dpy)) {	/* not wide_chars */
3276d522f475Smrg	TRACE(("ConvertSelection XA_UTF8_STRING(dpy)\n"));
3277d522f475Smrg	result =
3278d522f475Smrg	    _ConvertSelectionHelper(w,
3279d522f475Smrg				    type, value, length, format,
3280d522f475Smrg				    XmbTextListToTextProperty,
3281d522f475Smrg				    XUTF8StringStyle);
3282d522f475Smrg    }
3283d522f475Smrg#endif
3284d522f475Smrg    else if (*target == XA_LIST_LENGTH(dpy)) {
3285d522f475Smrg	TRACE(("ConvertSelection XA_LIST_LENGTH(dpy)\n"));
3286d522f475Smrg	*value = XtMalloc(4);
3287d522f475Smrg	if (sizeof(long) == 4)
3288d522f475Smrg	     *(long *) *value = 1;
3289d522f475Smrg	else {
3290d522f475Smrg	    long temp = 1;
3291d522f475Smrg	    memcpy((char *) *value, ((char *) &temp) + sizeof(long) - 4, 4);
3292d522f475Smrg	}
3293d522f475Smrg	*type = XA_INTEGER;
3294d522f475Smrg	*length = 1;
3295d522f475Smrg	*format = 32;
3296d522f475Smrg	result = True;
3297d522f475Smrg    } else if (*target == XA_LENGTH(dpy)) {
3298d522f475Smrg	TRACE(("ConvertSelection XA_LENGTH(dpy)\n"));
3299d522f475Smrg	/* This value is wrong if we have UTF-8 text */
3300d522f475Smrg	*value = XtMalloc(4);
3301d522f475Smrg	if (sizeof(long) == 4) {
3302d522f475Smrg	    *(long *) *value = screen->selection_length;
3303d522f475Smrg	} else {
3304d522f475Smrg	    long temp = screen->selection_length;
3305d522f475Smrg	    memcpy((char *) *value, ((char *) &temp) + sizeof(long) - 4, 4);
3306d522f475Smrg	}
3307d522f475Smrg	*type = XA_INTEGER;
3308d522f475Smrg	*length = 1;
3309d522f475Smrg	*format = 32;
3310d522f475Smrg	result = True;
3311d522f475Smrg    } else if (XmuConvertStandardSelection(w,
3312d522f475Smrg					   screen->selection_time, selection,
3313d522f475Smrg					   target, type, (XPointer *) value,
3314d522f475Smrg					   length, format)) {
3315d522f475Smrg	TRACE(("ConvertSelection XmuConvertStandardSelection\n"));
3316d522f475Smrg	result = True;
3317d522f475Smrg    }
3318d522f475Smrg
3319d522f475Smrg    /* else */
3320d522f475Smrg    return result;
3321d522f475Smrg}
3322d522f475Smrg
3323d522f475Smrgstatic void
3324d522f475SmrgLoseSelection(Widget w, Atom * selection)
3325d522f475Smrg{
3326d522f475Smrg    TScreen *screen;
3327d522f475Smrg    Atom *atomP;
3328d522f475Smrg    Cardinal i;
3329d522f475Smrg
3330d522f475Smrg    if (!IsXtermWidget(w))
3331d522f475Smrg	return;
3332d522f475Smrg
3333d522f475Smrg    screen = TScreenOf((XtermWidget) w);
3334d522f475Smrg    for (i = 0, atomP = screen->selection_atoms;
3335d522f475Smrg	 i < screen->selection_count; i++, atomP++) {
3336d522f475Smrg	if (*selection == *atomP)
3337d522f475Smrg	    *atomP = (Atom) 0;
3338d522f475Smrg	if (CutBuffer(*atomP) >= 0) {
3339d522f475Smrg	    *atomP = (Atom) 0;
3340d522f475Smrg	}
3341d522f475Smrg    }
3342d522f475Smrg
3343d522f475Smrg    for (i = screen->selection_count; i; i--) {
3344d522f475Smrg	if (screen->selection_atoms[i - 1] != 0)
3345d522f475Smrg	    break;
3346d522f475Smrg    }
3347d522f475Smrg    screen->selection_count = i;
3348d522f475Smrg
3349d522f475Smrg    for (i = 0, atomP = screen->selection_atoms;
3350d522f475Smrg	 i < screen->selection_count; i++, atomP++) {
3351d522f475Smrg	if (*atomP == (Atom) 0) {
3352d522f475Smrg	    *atomP = screen->selection_atoms[--screen->selection_count];
3353d522f475Smrg	}
3354d522f475Smrg    }
3355d522f475Smrg
3356d522f475Smrg    if (screen->selection_count == 0)
3357d522f475Smrg	TrackText((XtermWidget) w, &zeroCELL, &zeroCELL);
3358d522f475Smrg}
3359d522f475Smrg
3360d522f475Smrg/* ARGSUSED */
3361d522f475Smrgstatic void
3362d522f475SmrgSelectionDone(Widget w GCC_UNUSED,
3363d522f475Smrg	      Atom * selection GCC_UNUSED,
3364d522f475Smrg	      Atom * target GCC_UNUSED)
3365d522f475Smrg{
3366d522f475Smrg    /* empty proc so Intrinsics know we want to keep storage */
3367d522f475Smrg}
3368d522f475Smrg
3369d522f475Smrgstatic void
3370d522f475Smrg_OwnSelection(XtermWidget xw,
3371d522f475Smrg	      String * selections,
3372d522f475Smrg	      Cardinal count)
3373d522f475Smrg{
3374d522f475Smrg    TScreen *screen = &(xw->screen);
3375d522f475Smrg    Atom *atoms = screen->selection_atoms;
3376d522f475Smrg    Cardinal i;
3377d522f475Smrg    Bool have_selection = False;
3378d522f475Smrg
3379d522f475Smrg    if (screen->selection_length < 0)
3380d522f475Smrg	return;
3381d522f475Smrg
3382d522f475Smrg    TRACE(("_OwnSelection\n"));
3383d522f475Smrg    selections = MapSelections(xw, selections, count);
3384d522f475Smrg
3385d522f475Smrg    if (count > screen->sel_atoms_size) {
3386d522f475Smrg	XtFree((char *) atoms);
3387d522f475Smrg	atoms = (Atom *) XtMalloc(count * sizeof(Atom));
3388d522f475Smrg	screen->selection_atoms = atoms;
3389d522f475Smrg	screen->sel_atoms_size = count;
3390d522f475Smrg    }
3391d522f475Smrg    XmuInternStrings(XtDisplay((Widget) xw), selections, count, atoms);
3392d522f475Smrg    for (i = 0; i < count; i++) {
3393d522f475Smrg	int cutbuffer = CutBuffer(atoms[i]);
3394d522f475Smrg	if (cutbuffer >= 0) {
3395d522f475Smrg	    if (screen->selection_length >
3396d522f475Smrg		4 * XMaxRequestSize(XtDisplay((Widget) xw)) - 32) {
3397d522f475Smrg		fprintf(stderr,
3398d522f475Smrg			"%s: selection too big (%d bytes), not storing in CUT_BUFFER%d\n",
3399d522f475Smrg			xterm_name, screen->selection_length, cutbuffer);
3400d522f475Smrg	    } else {
3401d522f475Smrg		/* This used to just use the UTF-8 data, which was totally
3402d522f475Smrg		 * broken as not even the corresponding paste code in Xterm
3403d522f475Smrg		 * understood this!  So now it converts to Latin1 first.
3404d522f475Smrg		 *   Robert Brady, 2000-09-05
3405d522f475Smrg		 */
3406d522f475Smrg		unsigned long length = screen->selection_length;
3407d522f475Smrg		Char *data = screen->selection_data;
3408d522f475Smrg		if_OPT_WIDE_CHARS((screen), {
3409d522f475Smrg		    data = UTF8toLatin1(data, length, &length);
3410d522f475Smrg		});
3411d522f475Smrg		TRACE(("XStoreBuffer(%d)\n", cutbuffer));
3412d522f475Smrg		XStoreBuffer(XtDisplay((Widget) xw),
3413d522f475Smrg			     (char *) data,
3414d522f475Smrg			     (int) length,
3415d522f475Smrg			     cutbuffer);
3416d522f475Smrg	    }
3417d522f475Smrg	} else if (!screen->replyToEmacs) {
3418d522f475Smrg	    have_selection |=
3419d522f475Smrg		XtOwnSelection((Widget) xw, atoms[i],
3420d522f475Smrg			       screen->selection_time,
3421d522f475Smrg			       ConvertSelection, LoseSelection, SelectionDone);
3422d522f475Smrg	}
3423d522f475Smrg    }
3424d522f475Smrg    if (!screen->replyToEmacs)
3425d522f475Smrg	screen->selection_count = count;
3426d522f475Smrg    if (!have_selection)
3427d522f475Smrg	TrackText(xw, &zeroCELL, &zeroCELL);
3428d522f475Smrg}
3429d522f475Smrg
3430d522f475Smrgstatic void
3431d522f475SmrgResetSelectionState(TScreen * screen)
3432d522f475Smrg{
3433d522f475Smrg    screen->selection_count = 0;
3434d522f475Smrg    screen->startH = zeroCELL;
3435d522f475Smrg    screen->endH = zeroCELL;
3436d522f475Smrg}
3437d522f475Smrg
3438d522f475Smrgvoid
3439d522f475SmrgDisownSelection(XtermWidget xw)
3440d522f475Smrg{
3441d522f475Smrg    TScreen *screen = &(xw->screen);
3442d522f475Smrg    Atom *atoms = screen->selection_atoms;
3443d522f475Smrg    Cardinal count = screen->selection_count;
3444d522f475Smrg    Cardinal i;
3445d522f475Smrg
3446d522f475Smrg    TRACE(("DisownSelection count %d, start %d.%d, end %d.%d\n",
3447d522f475Smrg	   count,
3448d522f475Smrg	   screen->startH.row,
3449d522f475Smrg	   screen->startH.col,
3450d522f475Smrg	   screen->endH.row,
3451d522f475Smrg	   screen->endH.col));
3452d522f475Smrg
3453d522f475Smrg    for (i = 0; i < count; i++) {
3454d522f475Smrg	int cutbuffer = CutBuffer(atoms[i]);
3455d522f475Smrg	if (cutbuffer < 0) {
3456d522f475Smrg	    XtDisownSelection((Widget) xw, atoms[i],
3457d522f475Smrg			      screen->selection_time);
3458d522f475Smrg	}
3459d522f475Smrg    }
3460d522f475Smrg    /*
3461d522f475Smrg     * If none of the callbacks via XtDisownSelection() reset highlighting
3462d522f475Smrg     * do it now.
3463d522f475Smrg     */
3464d522f475Smrg    if (ScrnHaveSelection(screen)) {
3465d522f475Smrg	/* save data which will be reset */
3466d522f475Smrg	CELL first = screen->startH;
3467d522f475Smrg	CELL last = screen->endH;
3468d522f475Smrg
3469d522f475Smrg	ResetSelectionState(screen);
3470d522f475Smrg	ReHiliteText(xw, &first, &last);
3471d522f475Smrg    } else {
3472d522f475Smrg	ResetSelectionState(screen);
3473d522f475Smrg    }
3474d522f475Smrg}
3475d522f475Smrg
3476d522f475Smrgvoid
3477d522f475SmrgUnhiliteSelection(XtermWidget xw)
3478d522f475Smrg{
3479d522f475Smrg    TScreen *screen = &(xw->screen);
3480d522f475Smrg
3481d522f475Smrg    if (ScrnHaveSelection(screen)) {
3482d522f475Smrg	CELL first = screen->startH;
3483d522f475Smrg	CELL last = screen->endH;
3484d522f475Smrg
3485d522f475Smrg	screen->startH = zeroCELL;
3486d522f475Smrg	screen->endH = zeroCELL;
3487d522f475Smrg	ReHiliteText(xw, &first, &last);
3488d522f475Smrg    }
3489d522f475Smrg}
3490d522f475Smrg
3491d522f475Smrg/* returns number of chars in line from scol to ecol out */
3492d522f475Smrg/* ARGSUSED */
3493d522f475Smrgstatic int
3494d522f475SmrgLength(TScreen * screen GCC_UNUSED,
3495d522f475Smrg       int row,
3496d522f475Smrg       int scol,
3497d522f475Smrg       int ecol)
3498d522f475Smrg{
3499d522f475Smrg    int lastcol = LastTextCol(screen, row);
3500d522f475Smrg
3501d522f475Smrg    if (ecol > lastcol)
3502d522f475Smrg	ecol = lastcol;
3503d522f475Smrg    return (ecol - scol + 1);
3504d522f475Smrg}
3505d522f475Smrg
3506d522f475Smrg/* copies text into line, preallocated */
3507d522f475Smrgstatic Char *
3508d522f475SmrgSaveText(TScreen * screen,
3509d522f475Smrg	 int row,
3510d522f475Smrg	 int scol,
3511d522f475Smrg	 int ecol,
3512d522f475Smrg	 Char * lp,		/* pointer to where to put the text */
3513d522f475Smrg	 int *eol)
3514d522f475Smrg{
3515d522f475Smrg    int i = 0;
3516d522f475Smrg    unsigned c;
3517d522f475Smrg    Char *result = lp;
3518d522f475Smrg#if OPT_WIDE_CHARS
3519d522f475Smrg    int previous = 0;
3520d522f475Smrg#endif
3521d522f475Smrg
3522d522f475Smrg    i = Length(screen, row, scol, ecol);
3523d522f475Smrg    ecol = scol + i;
3524d522f475Smrg#if OPT_DEC_CHRSET
3525d522f475Smrg    if (CSET_DOUBLE(SCRN_BUF_CSETS(screen, ROW2INX(screen, row))[0])) {
3526d522f475Smrg	scol = (scol + 0) / 2;
3527d522f475Smrg	ecol = (ecol + 1) / 2;
3528d522f475Smrg    }
3529d522f475Smrg#endif
3530d522f475Smrg    *eol = !ScrnTstWrapped(screen, row);
3531d522f475Smrg    for (i = scol; i < ecol; i++) {
3532d522f475Smrg	c = E2A(XTERM_CELL(row, i));
3533d522f475Smrg#if OPT_WIDE_CHARS
3534d522f475Smrg	/* We want to strip out every occurrence of HIDDEN_CHAR AFTER a
3535d522f475Smrg	 * wide character.
3536d522f475Smrg	 */
3537d522f475Smrg	if (c == HIDDEN_CHAR && iswide(previous)) {
3538d522f475Smrg	    previous = c;
3539d522f475Smrg	    /* Combining characters attached to double-width characters
3540d522f475Smrg	       are in memory attached to the HIDDEN_CHAR */
3541d522f475Smrg	    if_OPT_WIDE_CHARS(screen, {
3542d522f475Smrg		if (screen->utf8_mode != uFalse) {
3543d522f475Smrg		    unsigned ch;
3544d522f475Smrg		    int off;
3545d522f475Smrg		    for (off = OFF_FINAL; off < MAX_PTRS; off += 2) {
3546d522f475Smrg			if ((ch = XTERM_CELLC(row, i, off)) == 0)
3547d522f475Smrg			    break;
3548d522f475Smrg			lp = convertToUTF8(lp, ch);
3549d522f475Smrg		    }
3550d522f475Smrg		}
3551d522f475Smrg	    });
3552d522f475Smrg	    continue;
3553d522f475Smrg	}
3554d522f475Smrg	previous = c;
3555d522f475Smrg	if (screen->utf8_mode != uFalse) {
3556d522f475Smrg	    lp = convertToUTF8(lp, (c != 0) ? c : ' ');
3557d522f475Smrg	    if_OPT_WIDE_CHARS(screen, {
3558d522f475Smrg		unsigned ch;
3559d522f475Smrg		int off;
3560d522f475Smrg		for (off = OFF_FINAL; off < MAX_PTRS; off += 2) {
3561d522f475Smrg		    if ((ch = XTERM_CELLC(row, i, off)) == 0)
3562d522f475Smrg			break;
3563d522f475Smrg		    lp = convertToUTF8(lp, ch);
3564d522f475Smrg		}
3565d522f475Smrg	    });
3566d522f475Smrg	} else
3567d522f475Smrg#endif
3568d522f475Smrg	{
3569d522f475Smrg	    if (c == 0) {
3570d522f475Smrg		c = E2A(' ');
3571d522f475Smrg	    } else if (c < E2A(' ')) {
3572d522f475Smrg		c = DECtoASCII(c);
3573d522f475Smrg	    } else if (c == 0x7f) {
3574d522f475Smrg		c = 0x5f;
3575d522f475Smrg	    }
3576d522f475Smrg	    *lp++ = A2E(c);
3577d522f475Smrg	}
3578d522f475Smrg	if (c != E2A(' '))
3579d522f475Smrg	    result = lp;
3580d522f475Smrg    }
3581d522f475Smrg
3582d522f475Smrg    /*
3583d522f475Smrg     * If requested, trim trailing blanks from selected lines.  Do not do this
3584d522f475Smrg     * if the line is wrapped.
3585d522f475Smrg     */
3586d522f475Smrg    if (!*eol || !screen->trim_selection)
3587d522f475Smrg	result = lp;
3588d522f475Smrg
3589d522f475Smrg    return (result);
3590d522f475Smrg}
3591d522f475Smrg
3592d522f475Smrg/* 32 + following 7-bit word:
3593d522f475Smrg
3594d522f475Smrg   1:0  Button no: 0, 1, 2.  3=release.
3595d522f475Smrg     2  shift
3596d522f475Smrg     3  meta
3597d522f475Smrg     4  ctrl
3598d522f475Smrg     5  set for motion notify
3599d522f475Smrg     6  set for wheel
3600d522f475Smrg*/
3601d522f475Smrg
3602d522f475Smrg/* Position: 32 - 255. */
3603d522f475Smrg
3604d522f475Smrgstatic int
3605d522f475SmrgBtnCode(XButtonEvent * event, int button)
3606d522f475Smrg{
3607d522f475Smrg    int result = 32 + (KeyState(event->state) << 2);
3608d522f475Smrg
3609d522f475Smrg    if (button < 0 || button > 5) {
3610d522f475Smrg	result += 3;
3611d522f475Smrg    } else {
3612d522f475Smrg	if (button > 3)
3613d522f475Smrg	    result += (64 - 4);
3614d522f475Smrg	if (event->type == MotionNotify)
3615d522f475Smrg	    result += 32;
3616d522f475Smrg	result += button;
3617d522f475Smrg    }
3618d522f475Smrg    return result;
3619d522f475Smrg}
3620d522f475Smrg
3621d522f475Smrg#define MOUSE_LIMIT (255 - 32)
3622d522f475Smrg
3623d522f475Smrgstatic void
3624d522f475SmrgEditorButton(XtermWidget xw, XButtonEvent * event)
3625d522f475Smrg{
3626d522f475Smrg    TScreen *screen = &(xw->screen);
3627d522f475Smrg    int pty = screen->respond;
3628d522f475Smrg    Char line[6];
3629d522f475Smrg    int row, col;
3630d522f475Smrg    int button;
3631d522f475Smrg    unsigned count = 0;
3632d522f475Smrg    Boolean changed = True;
3633d522f475Smrg
3634d522f475Smrg    /* If button event, get button # adjusted for DEC compatibility */
3635d522f475Smrg    button = event->button - 1;
3636d522f475Smrg    if (button >= 3)
3637d522f475Smrg	button++;
3638d522f475Smrg
3639d522f475Smrg    /* Compute character position of mouse pointer */
3640d522f475Smrg    row = (event->y - screen->border) / FontHeight(screen);
3641d522f475Smrg    col = (event->x - OriginX(screen)) / FontWidth(screen);
3642d522f475Smrg
3643d522f475Smrg    /* Limit to screen dimensions */
3644d522f475Smrg    if (row < 0)
3645d522f475Smrg	row = 0;
3646d522f475Smrg    else if (row > screen->max_row)
3647d522f475Smrg	row = screen->max_row;
3648d522f475Smrg    else if (row > MOUSE_LIMIT)
3649d522f475Smrg	row = MOUSE_LIMIT;
3650d522f475Smrg
3651d522f475Smrg    if (col < 0)
3652d522f475Smrg	col = 0;
3653d522f475Smrg    else if (col > screen->max_col)
3654d522f475Smrg	col = screen->max_col;
3655d522f475Smrg    else if (col > MOUSE_LIMIT)
3656d522f475Smrg	col = MOUSE_LIMIT;
3657d522f475Smrg
3658d522f475Smrg    /* Build key sequence starting with \E[M */
3659d522f475Smrg    if (screen->control_eight_bits) {
3660d522f475Smrg	line[count++] = ANSI_CSI;
3661d522f475Smrg    } else {
3662d522f475Smrg	line[count++] = ANSI_ESC;
3663d522f475Smrg	line[count++] = '[';
3664d522f475Smrg    }
3665d522f475Smrg#if OPT_SCO_FUNC_KEYS
3666d522f475Smrg    if (xw->keyboard.type == keyboardIsSCO) {
3667d522f475Smrg	/*
3668d522f475Smrg	 * SCO function key F1 is \E[M, which would conflict with xterm's
3669d522f475Smrg	 * normal kmous.
3670d522f475Smrg	 */
3671d522f475Smrg	line[count++] = '>';
3672d522f475Smrg    }
3673d522f475Smrg#endif
3674d522f475Smrg    line[count++] = 'M';
3675d522f475Smrg
3676d522f475Smrg    /* Add event code to key sequence */
3677d522f475Smrg    if (screen->send_mouse_pos == X10_MOUSE) {
3678d522f475Smrg	line[count++] = ' ' + button;
3679d522f475Smrg    } else {
3680d522f475Smrg	/* Button-Motion events */
3681d522f475Smrg	switch (event->type) {
3682d522f475Smrg	case ButtonPress:
3683d522f475Smrg	    line[count++] = BtnCode(event, screen->mouse_button = button);
3684d522f475Smrg	    break;
3685d522f475Smrg	case ButtonRelease:
3686d522f475Smrg	    /*
3687d522f475Smrg	     * Wheel mouse interface generates release-events for buttons
3688d522f475Smrg	     * 4 and 5, coded here as 3 and 4 respectively.  We change the
3689d522f475Smrg	     * release for buttons 1..3 to a -1.
3690d522f475Smrg	     */
3691d522f475Smrg	    if (button < 3)
3692d522f475Smrg		button = -1;
3693d522f475Smrg	    line[count++] = BtnCode(event, screen->mouse_button = button);
3694d522f475Smrg	    break;
3695d522f475Smrg	case MotionNotify:
3696d522f475Smrg	    /* BTN_EVENT_MOUSE and ANY_EVENT_MOUSE modes send motion
3697d522f475Smrg	     * events only if character cell has changed.
3698d522f475Smrg	     */
3699d522f475Smrg	    if ((row == screen->mouse_row)
3700d522f475Smrg		&& (col == screen->mouse_col)) {
3701d522f475Smrg		changed = False;
3702d522f475Smrg	    } else {
3703d522f475Smrg		line[count++] = BtnCode(event, screen->mouse_button);
3704d522f475Smrg	    }
3705d522f475Smrg	    break;
3706d522f475Smrg	default:
3707d522f475Smrg	    changed = False;
3708d522f475Smrg	    break;
3709d522f475Smrg	}
3710d522f475Smrg    }
3711d522f475Smrg
3712d522f475Smrg    if (changed) {
3713d522f475Smrg	screen->mouse_row = row;
3714d522f475Smrg	screen->mouse_col = col;
3715d522f475Smrg
3716d522f475Smrg	/* Add pointer position to key sequence */
3717d522f475Smrg	line[count++] = ' ' + col + 1;
3718d522f475Smrg	line[count++] = ' ' + row + 1;
3719d522f475Smrg
3720d522f475Smrg	TRACE(("mouse at %d,%d button+mask = %#x\n", row, col,
3721d522f475Smrg	       (screen->control_eight_bits) ? line[2] : line[3]));
3722d522f475Smrg
3723d522f475Smrg	/* Transmit key sequence to process running under xterm */
3724d522f475Smrg	v_write(pty, line, count);
3725d522f475Smrg    }
3726d522f475Smrg    return;
3727d522f475Smrg}
3728d522f475Smrg
3729d522f475Smrg#if OPT_FOCUS_EVENT
3730d522f475Smrgvoid
3731d522f475SmrgSendFocusButton(XtermWidget xw, XFocusChangeEvent * event)
3732d522f475Smrg{
3733d522f475Smrg    TScreen *screen = &(xw->screen);
3734d522f475Smrg
3735d522f475Smrg    if (screen->send_focus_pos) {
3736d522f475Smrg	ANSI reply;
3737d522f475Smrg
3738d522f475Smrg	memset(&reply, 0, sizeof(reply));
3739d522f475Smrg	reply.a_type = ANSI_CSI;
3740d522f475Smrg
3741d522f475Smrg#if OPT_SCO_FUNC_KEYS
3742d522f475Smrg	if (xw->keyboard.type == keyboardIsSCO) {
3743d522f475Smrg	    reply.a_pintro = '>';
3744d522f475Smrg	}
3745d522f475Smrg#endif
3746d522f475Smrg	reply.a_final = (event->type == FocusIn) ? 'I' : 'O';
3747d522f475Smrg	unparseseq(xw, &reply);
3748d522f475Smrg    }
3749d522f475Smrg    return;
3750d522f475Smrg}
3751d522f475Smrg#endif /* OPT_FOCUS_EVENT */
3752