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