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