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