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