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