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