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