button.c revision 6879286f
1/* $XTermId: button.c,v 1.391 2010/11/11 11:50:13 tom Exp $ */
2
3/*
4 * Copyright 1999-2009,2010 by Thomas E. Dickey
5 *
6 *                         All Rights Reserved
7 *
8 * Permission is hereby granted, free of charge, to any person obtaining a
9 * copy of this software and associated documentation files (the
10 * "Software"), to deal in the Software without restriction, including
11 * without limitation the rights to use, copy, modify, merge, publish,
12 * distribute, sublicense, and/or sell copies of the Software, and to
13 * permit persons to whom the Software is furnished to do so, subject to
14 * the following conditions:
15 *
16 * The above copyright notice and this permission notice shall be included
17 * in all copies or substantial portions of the Software.
18 *
19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
20 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
21 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
22 * IN NO EVENT SHALL THE ABOVE LISTED COPYRIGHT HOLDER(S) BE LIABLE FOR ANY
23 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
24 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
25 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
26 *
27 * Except as contained in this notice, the name(s) of the above copyright
28 * holders shall not be used in advertising or otherwise to promote the
29 * sale, use or other dealings in this Software without prior written
30 * authorization.
31 *
32 *
33 * Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts.
34 *
35 *                         All Rights Reserved
36 *
37 * Permission to use, copy, modify, and distribute this software and its
38 * documentation for any purpose and without fee is hereby granted,
39 * provided that the above copyright notice appear in all copies and that
40 * both that copyright notice and this permission notice appear in
41 * supporting documentation, and that the name of Digital Equipment
42 * Corporation not be used in advertising or publicity pertaining to
43 * distribution of the software without specific, written prior permission.
44 *
45 *
46 * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
47 * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
48 * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
49 * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
50 * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
51 * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
52 * SOFTWARE.
53 */
54
55/*
56button.c	Handles button events in the terminal emulator.
57		does cut/paste operations, change modes via menu,
58		passes button events through to some applications.
59				J. Gettys.
60*/
61
62#include <xterm.h>
63
64#include <stdio.h>
65#include <assert.h>
66
67#include <X11/Xatom.h>
68#include <X11/Xmu/Atoms.h>
69#include <X11/Xmu/StdSel.h>
70
71#include <xutf8.h>
72#include <fontutils.h>
73
74#include <data.h>
75#include <error.h>
76#include <menu.h>
77#include <xcharmouse.h>
78#include <charclass.h>
79#include <xstrings.h>
80
81#if OPT_SELECT_REGEX
82#ifdef HAVE_PCREPOSIX_H
83#include <pcreposix.h>
84#else /* POSIX regex.h */
85#include <sys/types.h>
86#include <regex.h>
87#endif
88#endif
89
90#if OPT_WIDE_CHARS
91#include <ctype.h>
92#include <wcwidth.h>
93#else
94#define CharacterClass(value) \
95	charClass[value & ((sizeof(charClass)/sizeof(charClass[0]))-1)]
96#endif
97
98    /*
99     * We'll generally map rows to indices when doing selection.
100     * Simplify that with a macro.
101     *
102     * Note that ROW2INX() is safe to use with auto increment/decrement for
103     * the row expression since that is evaluated once.
104     */
105#define GET_LINEDATA(screen, row) \
106	getLineData(screen, ROW2INX(screen, row))
107
108    /*
109     * We reserve shift modifier for cut/paste operations.  In principle we
110     * can pass through control and meta modifiers, but in practice, the
111     * popup menu uses control, and the window manager is likely to use meta,
112     * so those events are not delivered to SendMousePosition.
113     */
114#define OurModifiers (ShiftMask | ControlMask | Mod1Mask)
115#define AllModifiers (ShiftMask | LockMask | ControlMask | Mod1Mask | \
116		      Mod2Mask | Mod3Mask | Mod4Mask | Mod5Mask)
117
118#define 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 =
1265		(char **) XtMalloc((Cardinal) sizeof(char *) * (unsigned) *text_list_count);
1266	    new_text_list[0] = tmp = XtMalloc((Cardinal) new_size);
1267	    for (i = 0; i < (*text_list_count); ++i) {
1268		data = (Char *) (*text_list)[i];
1269		size = strlen((*text_list)[i]) + 1;
1270		data = UTF8toLatin1(screen, data, size, &size);
1271		memcpy(tmp, data, size + 1);
1272		new_text_list[i] = tmp;
1273		tmp += size + 1;
1274	    }
1275	    XFreeStringList((*text_list));
1276	    *text_list = new_text_list;
1277	} else {
1278	    rc = -1;
1279	}
1280    }
1281    return rc;
1282}
1283#endif /* OPT_WIDE_CHARS */
1284
1285static char *
1286parseItem(char *value, char *nextc)
1287{
1288    char *nextp = value;
1289    while (*nextp != '\0' && *nextp != ',') {
1290	*nextp = x_toupper(*nextp);
1291	++nextp;
1292    }
1293    *nextc = *nextp;
1294    *nextp = '\0';
1295    x_strtrim(value);
1296
1297    return nextp;
1298}
1299
1300/*
1301 * All of the wanted strings are unique in the first character, so we can
1302 * use simple abbreviations.
1303 */
1304static Bool
1305sameItem(const char *actual, const char *wanted)
1306{
1307    Bool result = False;
1308    size_t have = strlen(actual);
1309    size_t need = strlen(wanted);
1310
1311    if (have != 0 && have <= need) {
1312	if (!strncmp(actual, wanted, have)) {
1313	    TRACE(("...matched \"%s\"\n", wanted));
1314	    result = True;
1315	}
1316    }
1317
1318    return result;
1319}
1320
1321/*
1322 * Handle the eightBitSelectTypes or utf8SelectTypes resource values.
1323 */
1324static Bool
1325overrideTargets(Widget w, String value, Atom ** resultp)
1326{
1327    Bool override = False;
1328    XtermWidget xw;
1329
1330    if ((xw = getXtermWidget(w)) != 0) {
1331	TScreen *screen = TScreenOf(xw);
1332
1333	if (!IsEmpty(value)) {
1334	    char *copied = x_strdup(value);
1335	    if (copied != 0) {
1336		Atom *result = 0;
1337		Cardinal count = 1;
1338		int n;
1339
1340		TRACE(("decoding SelectTypes \"%s\"\n", value));
1341		for (n = 0; copied[n] != '\0'; ++n) {
1342		    if (copied[n] == ',')
1343			++count;
1344		}
1345		result = (Atom *) XtMalloc(((2 * count) + 1)
1346					   * (Cardinal) sizeof(Atom));
1347		if (result == NULL) {
1348		    TRACE(("Couldn't allocate selection types\n"));
1349		} else {
1350		    char nextc = '?';
1351		    char *listp = (char *) copied;
1352		    count = 0;
1353		    do {
1354			char *nextp = parseItem(listp, &nextc);
1355			size_t len = strlen(listp);
1356
1357			if (len == 0) {
1358			    ;
1359			}
1360#if OPT_WIDE_CHARS
1361			else if (sameItem(listp, "UTF8")) {
1362			    result[count++] = XA_UTF8_STRING(XtDisplay(w));
1363			}
1364#endif
1365			else if (sameItem(listp, "I18N")) {
1366			    if (screen->i18nSelections) {
1367				result[count++] = XA_TEXT(XtDisplay(w));
1368				result[count++] = XA_COMPOUND_TEXT(XtDisplay(w));
1369			    }
1370			} else if (sameItem(listp, "TEXT")) {
1371			    result[count++] = XA_TEXT(XtDisplay(w));
1372			} else if (sameItem(listp, "COMPOUND_TEXT")) {
1373			    result[count++] = XA_COMPOUND_TEXT(XtDisplay(w));
1374			} else if (sameItem(listp, "STRING")) {
1375			    result[count++] = XA_STRING;
1376			}
1377			*nextp++ = nextc;
1378			listp = nextp;
1379		    } while (nextc != '\0');
1380		    if (count) {
1381			result[count] = None;
1382			override = True;
1383			*resultp = result;
1384		    } else {
1385			XtFree((char *) result);
1386		    }
1387		}
1388	    } else {
1389		TRACE(("Couldn't allocate copy of selection types\n"));
1390	    }
1391	}
1392    }
1393    return override;
1394}
1395
1396#if OPT_WIDE_CHARS
1397static Atom *
1398allocUtf8Targets(Widget w, TScreen * screen)
1399{
1400    Atom **resultp = &(screen->selection_targets_utf8);
1401
1402    if (*resultp == 0) {
1403	Atom *result;
1404
1405	if (!overrideTargets(w, screen->utf8_select_types, &result)) {
1406	    result = (Atom *) XtMalloc((Cardinal) (5 * sizeof(Atom)));
1407	    if (result == NULL) {
1408		TRACE(("Couldn't allocate utf-8 selection targets\n"));
1409	    } else {
1410		int n = 0;
1411
1412		result[n++] = XA_UTF8_STRING(XtDisplay(w));
1413#ifdef X_HAVE_UTF8_STRING
1414		if (screen->i18nSelections) {
1415		    result[n++] = XA_TEXT(XtDisplay(w));
1416		    result[n++] = XA_COMPOUND_TEXT(XtDisplay(w));
1417		}
1418#endif
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 = (Atom *) XtMalloc((Cardinal) (5 * sizeof(Atom)));
1441	    if (result == NULL) {
1442		TRACE(("Couldn't allocate 8bit selection targets\n"));
1443	    } else {
1444		int n = 0;
1445
1446#ifdef X_HAVE_UTF8_STRING
1447		result[n++] = XA_UTF8_STRING(XtDisplay(w));
1448#endif
1449		if (screen->i18nSelections) {
1450		    result[n++] = XA_TEXT(XtDisplay(w));
1451		    result[n++] = XA_COMPOUND_TEXT(XtDisplay(w));
1452		}
1453		result[n++] = XA_STRING;
1454		result[n] = None;
1455	    }
1456	}
1457
1458	*resultp = result;
1459    }
1460
1461    return *resultp;
1462}
1463
1464static Atom *
1465_SelectionTargets(Widget w)
1466{
1467    Atom *result;
1468    TScreen *screen;
1469    XtermWidget xw;
1470
1471    if ((xw = getXtermWidget(w)) == 0) {
1472	result = NULL;
1473    } else {
1474	screen = TScreenOf(xw);
1475
1476#if OPT_WIDE_CHARS
1477	if (screen->wide_chars) {
1478	    result = allocUtf8Targets(w, screen);
1479	} else
1480#endif
1481	{
1482	    /* not screen->wide_chars */
1483	    result = alloc8bitTargets(w, screen);
1484	}
1485    }
1486
1487    return result;
1488}
1489
1490#define isSELECT(value) (!strcmp(value, "SELECT"))
1491
1492static void
1493UnmapSelections(XtermWidget xw)
1494{
1495    TScreen *screen = TScreenOf(xw);
1496    Cardinal n;
1497
1498    if (screen->mappedSelect) {
1499	for (n = 0; screen->mappedSelect[n] != 0; ++n)
1500	    free((void *) screen->mappedSelect[n]);
1501	free(screen->mappedSelect);
1502	screen->mappedSelect = 0;
1503    }
1504}
1505
1506/*
1507 * xterm generally uses the primary selection.  Some applications prefer
1508 * (or are limited to) the clipboard.  Since the translations resource is
1509 * complicated, users seldom change the way it affects selection.  But it
1510 * is simple to remap the choice between primary and clipboard before the
1511 * call to XmuInternStrings().
1512 */
1513static String *
1514MapSelections(XtermWidget xw, String * params, Cardinal num_params)
1515{
1516    String *result = params;
1517
1518    if (num_params > 0) {
1519	Cardinal j;
1520	Boolean map = False;
1521
1522	for (j = 0; j < num_params; ++j) {
1523	    TRACE(("param[%d]:%s\n", j, params[j]));
1524	    if (isSELECT(params[j])) {
1525		map = True;
1526		break;
1527	    }
1528	}
1529	if (map) {
1530	    TScreen *screen = TScreenOf(xw);
1531	    const char *mapTo = (screen->selectToClipboard
1532				 ? "CLIPBOARD"
1533				 : "PRIMARY");
1534
1535	    UnmapSelections(xw);
1536	    if ((result = TypeMallocN(String, num_params + 1)) != 0) {
1537		result[num_params] = 0;
1538		for (j = 0; j < num_params; ++j) {
1539		    result[j] = x_strdup((isSELECT(params[j])
1540					  ? mapTo
1541					  : params[j]));
1542		    if (result[j] == 0) {
1543			UnmapSelections(xw);
1544			result = 0;
1545			break;
1546		    }
1547		}
1548		screen->mappedSelect = result;
1549	    }
1550	}
1551    }
1552    return result;
1553}
1554
1555/*
1556 * Lookup the cut-buffer number, which will be in the range 0-7.
1557 * If it is not a cut-buffer, it is the primary selection (-1).
1558 */
1559static int
1560CutBuffer(Atom code)
1561{
1562    int cutbuffer;
1563    switch ((unsigned) code) {
1564    case XA_CUT_BUFFER0:
1565	cutbuffer = 0;
1566	break;
1567    case XA_CUT_BUFFER1:
1568	cutbuffer = 1;
1569	break;
1570    case XA_CUT_BUFFER2:
1571	cutbuffer = 2;
1572	break;
1573    case XA_CUT_BUFFER3:
1574	cutbuffer = 3;
1575	break;
1576    case XA_CUT_BUFFER4:
1577	cutbuffer = 4;
1578	break;
1579    case XA_CUT_BUFFER5:
1580	cutbuffer = 5;
1581	break;
1582    case XA_CUT_BUFFER6:
1583	cutbuffer = 6;
1584	break;
1585    case XA_CUT_BUFFER7:
1586	cutbuffer = 7;
1587	break;
1588    default:
1589	cutbuffer = -1;
1590	break;
1591    }
1592    return cutbuffer;
1593}
1594
1595#if OPT_PASTE64
1596static void
1597FinishPaste64(XtermWidget xw)
1598{
1599    TScreen *screen = TScreenOf(xw);
1600
1601    TRACE(("FinishPaste64(%d)\n", screen->base64_paste));
1602    if (screen->base64_paste) {
1603	screen->base64_paste = 0;
1604	unparseputc1(xw, screen->base64_final);
1605	unparse_end(xw);
1606    }
1607}
1608#endif
1609
1610#if !OPT_PASTE64
1611static
1612#endif
1613void
1614xtermGetSelection(Widget w,
1615		  Time ev_time,
1616		  String * params,	/* selections in precedence order */
1617		  Cardinal num_params,
1618		  Atom * targets)
1619{
1620    Atom selection;
1621    int cutbuffer;
1622    Atom target;
1623
1624    XtermWidget xw;
1625
1626    if (num_params == 0)
1627	return;
1628    if ((xw = getXtermWidget(w)) == 0)
1629	return;
1630
1631    TRACE(("xtermGetSelection num_params %d\n", num_params));
1632    params = MapSelections(xw, params, num_params);
1633
1634    XmuInternStrings(XtDisplay(w), params, (Cardinal) 1, &selection);
1635    cutbuffer = CutBuffer(selection);
1636
1637    TRACE(("Cutbuffer: %d, target: %s\n", cutbuffer,
1638	   (targets
1639	    ? visibleSelectionTarget(XtDisplay(w), targets[0])
1640	    : "None")));
1641
1642    if (cutbuffer >= 0) {
1643	int inbytes;
1644	unsigned long nbytes;
1645	int fmt8 = 8;
1646	Atom type = XA_STRING;
1647	char *line;
1648
1649	/* 'line' is freed in SelectionReceived */
1650	line = XFetchBuffer(XtDisplay(w), &inbytes, cutbuffer);
1651	nbytes = (unsigned long) inbytes;
1652
1653	if (nbytes > 0)
1654	    SelectionReceived(w, NULL, &selection, &type, (XtPointer) line,
1655			      &nbytes, &fmt8);
1656	else if (num_params > 1) {
1657	    xtermGetSelection(w, ev_time, params + 1, num_params - 1, NULL);
1658	}
1659#if OPT_PASTE64
1660	else {
1661	    FinishPaste64(xw);
1662	}
1663#endif
1664	return;
1665    } else {
1666	struct _SelectionList *list;
1667
1668	if (targets == NULL || targets[0] == None) {
1669	    targets = _SelectionTargets(w);
1670	}
1671
1672	if (targets != 0) {
1673	    target = targets[0];
1674
1675	    if (targets[1] == None) {	/* last target in list */
1676		params++;
1677		num_params--;
1678		targets = _SelectionTargets(w);
1679	    } else {
1680		targets = &(targets[1]);
1681	    }
1682
1683	    if (num_params) {
1684		/* 'list' is freed in SelectionReceived */
1685		list = XtNew(struct _SelectionList);
1686		if (list != 0) {
1687		    list->params = params;
1688		    list->count = num_params;
1689		    list->targets = targets;
1690		    list->time = ev_time;
1691		}
1692	    } else {
1693		list = NULL;
1694	    }
1695
1696	    XtGetSelectionValue(w, selection,
1697				target,
1698				SelectionReceived,
1699				(XtPointer) list, ev_time);
1700	}
1701    }
1702}
1703
1704#if OPT_TRACE && OPT_WIDE_CHARS
1705static void
1706GettingSelection(Display * dpy, Atom type, Char * line, unsigned long len)
1707{
1708    Char *cp;
1709    char *name;
1710
1711    name = XGetAtomName(dpy, type);
1712
1713    TRACE(("Getting %s (%ld)\n", name, (long int) type));
1714    for (cp = line; cp < line + len; cp++) {
1715	TRACE(("[%d:%lu]", (int) (cp + 1 - line), len));
1716	if (isprint(*cp)) {
1717	    TRACE(("%c\n", *cp));
1718	} else {
1719	    TRACE(("\\x%02x\n", *cp));
1720	}
1721    }
1722}
1723#else
1724#define GettingSelection(dpy,type,line,len)	/* nothing */
1725#endif
1726
1727#ifdef VMS
1728#  define tty_vwrite(pty,lag,l)		tt_write(lag,l)
1729#else /* !( VMS ) */
1730#  define tty_vwrite(pty,lag,l)		v_write(pty,lag,l)
1731#endif /* defined VMS */
1732
1733#if OPT_PASTE64
1734/* Return base64 code character given 6-bit number */
1735static const char base64_code[] = "\
1736ABCDEFGHIJKLMNOPQRSTUVWXYZ\
1737abcdefghijklmnopqrstuvwxyz\
17380123456789+/";
1739static void
1740base64_flush(TScreen * screen)
1741{
1742    Char x;
1743    switch (screen->base64_count) {
1744    case 0:
1745	break;
1746    case 2:
1747	x = CharOf(base64_code[screen->base64_accu << 4]);
1748	tty_vwrite(screen->respond, &x, 1);
1749	break;
1750    case 4:
1751	x = CharOf(base64_code[screen->base64_accu << 2]);
1752	tty_vwrite(screen->respond, &x, 1);
1753	break;
1754    }
1755    if (screen->base64_pad & 3)
1756	tty_vwrite(screen->respond,
1757		   (const Char *) "===",
1758		   (unsigned) (4 - (screen->base64_pad & 3)));
1759    screen->base64_count = 0;
1760    screen->base64_accu = 0;
1761    screen->base64_pad = 0;
1762}
1763#endif /* OPT_PASTE64 */
1764
1765static void
1766_qWriteSelectionData(TScreen * screen, Char * lag, unsigned length)
1767{
1768#if OPT_PASTE64
1769    if (screen->base64_paste) {
1770	/* Send data as base64 */
1771	Char *p = lag;
1772	Char buf[64];
1773	unsigned x = 0;
1774	while (length--) {
1775	    switch (screen->base64_count) {
1776	    case 0:
1777		buf[x++] = CharOf(base64_code[*p >> 2]);
1778		screen->base64_accu = (unsigned) (*p & 0x3);
1779		screen->base64_count = 2;
1780		++p;
1781		break;
1782	    case 2:
1783		buf[x++] = CharOf(base64_code[(screen->base64_accu << 4) +
1784					      (*p >> 4)]);
1785		screen->base64_accu = (unsigned) (*p & 0xF);
1786		screen->base64_count = 4;
1787		++p;
1788		break;
1789	    case 4:
1790		buf[x++] = CharOf(base64_code[(screen->base64_accu << 2) +
1791					      (*p >> 6)]);
1792		buf[x++] = CharOf(base64_code[*p & 0x3F]);
1793		screen->base64_accu = 0;
1794		screen->base64_count = 0;
1795		++p;
1796		break;
1797	    }
1798	    if (x >= 63) {
1799		/* Write 63 or 64 characters */
1800		screen->base64_pad += x;
1801		tty_vwrite(screen->respond, buf, x);
1802		x = 0;
1803	    }
1804	}
1805	if (x != 0) {
1806	    screen->base64_pad += x;
1807	    tty_vwrite(screen->respond, buf, x);
1808	}
1809    } else
1810#endif /* OPT_PASTE64 */
1811#if OPT_READLINE
1812    if (SCREEN_FLAG(screen, paste_quotes)) {
1813	while (length--) {
1814	    tty_vwrite(screen->respond, (const Char *) "\026", 1);	/* Control-V */
1815	    tty_vwrite(screen->respond, lag++, 1);
1816	}
1817    } else
1818#endif
1819	tty_vwrite(screen->respond, lag, length);
1820}
1821
1822static void
1823_WriteSelectionData(TScreen * screen, Char * line, size_t length)
1824{
1825    /* Write data to pty a line at a time. */
1826    /* Doing this one line at a time may no longer be necessary
1827       because v_write has been re-written. */
1828
1829    Char *lag, *end;
1830
1831    /* in the VMS version, if tt_pasting isn't set to True then qio
1832       reads aren't blocked and an infinite loop is entered, where the
1833       pasted text shows up as new input, goes in again, shows up
1834       again, ad nauseum. */
1835#ifdef VMS
1836    tt_pasting = True;
1837#endif
1838
1839    end = &line[length];
1840    lag = line;
1841
1842#if OPT_PASTE64
1843    if (screen->base64_paste) {
1844	_qWriteSelectionData(screen, lag, (unsigned) (end - lag));
1845	base64_flush(screen);
1846    } else
1847#endif
1848    {
1849	if (!SCREEN_FLAG(screen, paste_literal_nl)) {
1850	    Char *cp;
1851	    for (cp = line; cp != end; cp++) {
1852		if (*cp == '\n') {
1853		    *cp = '\r';
1854		    _qWriteSelectionData(screen, lag, (unsigned) (cp - lag + 1));
1855		    lag = cp + 1;
1856		}
1857	    }
1858	}
1859
1860	if (lag != end) {
1861	    _qWriteSelectionData(screen, lag, (unsigned) (end - lag));
1862	}
1863    }
1864#ifdef VMS
1865    tt_pasting = False;
1866    tt_start_read();		/* reenable reads or a character may be lost */
1867#endif
1868}
1869
1870#if OPT_READLINE
1871static void
1872_WriteKey(TScreen * screen, const Char * in)
1873{
1874    Char line[16];
1875    unsigned count = 0;
1876    size_t length = strlen((const char *) in);
1877
1878    if (screen->control_eight_bits) {
1879	line[count++] = ANSI_CSI;
1880    } else {
1881	line[count++] = ANSI_ESC;
1882	line[count++] = '[';
1883    }
1884    while (length--)
1885	line[count++] = *in++;
1886    line[count++] = '~';
1887    tty_vwrite(screen->respond, line, count);
1888}
1889#endif /* OPT_READLINE */
1890
1891/* SelectionReceived: stuff received selection text into pty */
1892
1893/* ARGSUSED */
1894static void
1895SelectionReceived(Widget w,
1896		  XtPointer client_data,
1897		  Atom * selection GCC_UNUSED,
1898		  Atom * type,
1899		  XtPointer value,
1900		  unsigned long *length,
1901		  int *format)
1902{
1903    char **text_list = NULL;
1904    int text_list_count;
1905    XTextProperty text_prop;
1906    TScreen *screen;
1907    Display *dpy;
1908#if OPT_TRACE && OPT_WIDE_CHARS
1909    Char *line = (Char *) value;
1910#endif
1911
1912    XtermWidget xw;
1913
1914    if ((xw = getXtermWidget(w)) == 0)
1915	return;
1916
1917    screen = TScreenOf(xw);
1918    dpy = XtDisplay(w);
1919
1920    if (*type == 0		/*XT_CONVERT_FAIL */
1921	|| *length == 0
1922	|| value == NULL)
1923	goto fail;
1924
1925    text_prop.value = (unsigned char *) value;
1926    text_prop.encoding = *type;
1927    text_prop.format = *format;
1928    text_prop.nitems = *length;
1929
1930    TRACE(("SelectionReceived %s format %d, nitems %ld\n",
1931	   visibleSelectionTarget(dpy, text_prop.encoding),
1932	   text_prop.format,
1933	   text_prop.nitems));
1934
1935#if OPT_WIDE_CHARS
1936    if (screen->wide_chars) {
1937	if (*type == XA_UTF8_STRING(dpy) ||
1938	    *type == XA_STRING ||
1939	    *type == XA_COMPOUND_TEXT(dpy)) {
1940	    GettingSelection(dpy, *type, line, *length);
1941	    if (Xutf8TextPropertyToTextList(dpy, &text_prop,
1942					    &text_list,
1943					    &text_list_count) < 0) {
1944		TRACE(("Conversion failed\n"));
1945		text_list = NULL;
1946	    }
1947	}
1948    } else
1949#endif /* OPT_WIDE_CHARS */
1950    {
1951	/* Convert the selection to locale's multibyte encoding. */
1952
1953	if (*type == XA_UTF8_STRING(dpy) ||
1954	    *type == XA_STRING ||
1955	    *type == XA_COMPOUND_TEXT(dpy)) {
1956	    Status rc;
1957
1958	    GettingSelection(dpy, *type, line, *length);
1959
1960#if OPT_WIDE_CHARS
1961	    if (*type == XA_UTF8_STRING(dpy) &&
1962		!(screen->wide_chars || screen->c1_printable)) {
1963		rc = xtermUtf8ToTextList(xw, &text_prop,
1964					 &text_list, &text_list_count);
1965	    } else
1966#endif
1967	    if (*type == XA_STRING && screen->brokenSelections) {
1968		rc = XTextPropertyToStringList(&text_prop,
1969					       &text_list, &text_list_count);
1970	    } else {
1971		rc = XmbTextPropertyToTextList(dpy, &text_prop,
1972					       &text_list,
1973					       &text_list_count);
1974	    }
1975	    if (rc < 0) {
1976		TRACE(("Conversion failed\n"));
1977		text_list = NULL;
1978	    }
1979	}
1980    }
1981
1982    if (text_list != NULL && text_list_count != 0) {
1983	int i;
1984
1985#if OPT_PASTE64
1986	if (screen->base64_paste) {
1987	    ;
1988	} else
1989#endif
1990#if OPT_READLINE
1991	if (SCREEN_FLAG(screen, paste_brackets)) {
1992	    _WriteKey(screen, (const Char *) "200");
1993	}
1994#endif
1995	for (i = 0; i < text_list_count; i++) {
1996	    size_t len = strlen(text_list[i]);
1997	    _WriteSelectionData(screen, (Char *) text_list[i], len);
1998	}
1999#if OPT_PASTE64
2000	if (screen->base64_paste) {
2001	    FinishPaste64(xw);
2002	} else
2003#endif
2004#if OPT_READLINE
2005	if (SCREEN_FLAG(screen, paste_brackets)) {
2006	    _WriteKey(screen, (const Char *) "201");
2007	}
2008#endif
2009	XFreeStringList(text_list);
2010    } else
2011	goto fail;
2012
2013    XtFree((char *) client_data);
2014    XtFree((char *) value);
2015
2016    return;
2017
2018  fail:
2019    if (client_data != 0) {
2020	struct _SelectionList *list = (struct _SelectionList *) client_data;
2021
2022	TRACE(("SelectionReceived ->xtermGetSelection\n"));
2023	xtermGetSelection(w, list->time,
2024			  list->params, list->count, list->targets);
2025	XtFree((char *) client_data);
2026#if OPT_PASTE64
2027    } else {
2028	FinishPaste64(xw);
2029#endif
2030    }
2031    return;
2032}
2033
2034void
2035HandleInsertSelection(Widget w,
2036		      XEvent * event,	/* assumed to be XButtonEvent* */
2037		      String * params,	/* selections in precedence order */
2038		      Cardinal *num_params)
2039{
2040    XtermWidget xw;
2041
2042    if ((xw = getXtermWidget(w)) != 0) {
2043	if (!SendMousePosition(xw, event)) {
2044#if OPT_READLINE
2045	    int ldelta;
2046	    TScreen *screen = TScreenOf(xw);
2047	    if (IsBtnEvent(event)
2048	    /* Disable on Shift-mouse, including the application-mouse modes */
2049		&& !(KeyModifiers(event) & ShiftMask)
2050		&& (screen->send_mouse_pos == MOUSE_OFF)
2051		&& SCREEN_FLAG(screen, paste_moves)
2052		&& rowOnCurrentLine(screen, eventRow(screen, event), &ldelta))
2053		ReadLineMovePoint(screen, eventColBetween(screen, event), ldelta);
2054#endif /* OPT_READLINE */
2055
2056	    xtermGetSelection(w, event->xbutton.time, params, *num_params, NULL);
2057	}
2058    }
2059}
2060
2061static SelectUnit
2062EvalSelectUnit(XtermWidget xw,
2063	       Time buttonDownTime,
2064	       SelectUnit defaultUnit,
2065	       unsigned int button)
2066{
2067    TScreen *screen = TScreenOf(xw);
2068    SelectUnit result;
2069    int delta;
2070
2071    if (button != screen->lastButton) {
2072	delta = screen->multiClickTime + 1;
2073    } else if (screen->lastButtonUpTime == (Time) 0) {
2074	/* first time and once in a blue moon */
2075	delta = screen->multiClickTime + 1;
2076    } else if (buttonDownTime > screen->lastButtonUpTime) {
2077	/* most of the time */
2078	delta = (int) (buttonDownTime - screen->lastButtonUpTime);
2079    } else {
2080	/* time has rolled over since lastButtonUpTime */
2081	delta = (int) ((((Time) ~ 0) - screen->lastButtonUpTime) + buttonDownTime);
2082    }
2083
2084    if (delta > screen->multiClickTime) {
2085	screen->numberOfClicks = 1;
2086	result = defaultUnit;
2087    } else {
2088	result = screen->selectMap[screen->numberOfClicks % screen->maxClicks];
2089	screen->numberOfClicks += 1;
2090    }
2091    TRACE(("EvalSelectUnit(%d) = %d\n", screen->numberOfClicks, result));
2092    return result;
2093}
2094
2095static void
2096do_select_start(XtermWidget xw,
2097		XEvent * event,	/* must be XButtonEvent* */
2098		CELL * cell)
2099{
2100    TScreen *screen = TScreenOf(xw);
2101
2102    if (SendMousePosition(xw, event))
2103	return;
2104    screen->selectUnit = EvalSelectUnit(xw,
2105					event->xbutton.time,
2106					Select_CHAR,
2107					event->xbutton.button);
2108    screen->replyToEmacs = False;
2109
2110#if OPT_READLINE
2111    lastButtonDownTime = event->xbutton.time;
2112#endif
2113
2114    StartSelect(xw, cell);
2115}
2116
2117/* ARGSUSED */
2118void
2119HandleSelectStart(Widget w,
2120		  XEvent * event,	/* must be XButtonEvent* */
2121		  String * params GCC_UNUSED,
2122		  Cardinal *num_params GCC_UNUSED)
2123{
2124    XtermWidget xw;
2125
2126    if ((xw = getXtermWidget(w)) != 0) {
2127	TScreen *screen = TScreenOf(xw);
2128	CELL cell;
2129
2130	screen->firstValidRow = 0;
2131	screen->lastValidRow = screen->max_row;
2132	PointToCELL(screen, event->xbutton.y, event->xbutton.x, &cell);
2133
2134#if OPT_READLINE
2135	ExtendingSelection = 0;
2136#endif
2137
2138	do_select_start(xw, event, &cell);
2139    }
2140}
2141
2142/* ARGSUSED */
2143void
2144HandleKeyboardSelectStart(Widget w,
2145			  XEvent * event,	/* must be XButtonEvent* */
2146			  String * params GCC_UNUSED,
2147			  Cardinal *num_params GCC_UNUSED)
2148{
2149    XtermWidget xw;
2150
2151    if ((xw = getXtermWidget(w)) != 0) {
2152	TScreen *screen = TScreenOf(xw);
2153	do_select_start(xw, event, &screen->cursorp);
2154    }
2155}
2156
2157static void
2158TrackDown(XtermWidget xw, XButtonEvent * event)
2159{
2160    TScreen *screen = TScreenOf(xw);
2161    CELL cell;
2162
2163    screen->selectUnit = EvalSelectUnit(xw,
2164					event->time,
2165					Select_CHAR,
2166					event->button);
2167    if (screen->numberOfClicks > 1) {
2168	PointToCELL(screen, event->y, event->x, &cell);
2169	screen->replyToEmacs = True;
2170	StartSelect(xw, &cell);
2171    } else {
2172	screen->waitingForTrackInfo = True;
2173	EditorButton(xw, event);
2174    }
2175}
2176
2177#define boundsCheck(x)	if (x < 0) \
2178			    x = 0; \
2179			else if (x >= screen->max_row) \
2180			    x = screen->max_row
2181
2182void
2183TrackMouse(XtermWidget xw,
2184	   int func,
2185	   CELL * start,
2186	   int firstrow,
2187	   int lastrow)
2188{
2189    TScreen *screen = TScreenOf(xw);
2190
2191    if (screen->waitingForTrackInfo) {	/* if Timed, ignore */
2192	screen->waitingForTrackInfo = False;
2193
2194	if (func != 0) {
2195	    CELL first = *start;
2196
2197	    boundsCheck(first.row);
2198	    boundsCheck(firstrow);
2199	    boundsCheck(lastrow);
2200	    screen->firstValidRow = firstrow;
2201	    screen->lastValidRow = lastrow;
2202	    screen->replyToEmacs = True;
2203	    StartSelect(xw, &first);
2204	}
2205    }
2206}
2207
2208static void
2209StartSelect(XtermWidget xw, const CELL * cell)
2210{
2211    TScreen *screen = TScreenOf(xw);
2212
2213    TRACE(("StartSelect row=%d, col=%d\n", cell->row, cell->col));
2214    if (screen->cursor_state)
2215	HideCursor();
2216    if (screen->numberOfClicks == 1) {
2217	/* set start of selection */
2218	screen->rawPos = *cell;
2219    }
2220    /* else use old values in rawPos */
2221    screen->saveStartR = screen->startExt = screen->rawPos;
2222    screen->saveEndR = screen->endExt = screen->rawPos;
2223    if (Coordinate(screen, cell) < Coordinate(screen, &(screen->rawPos))) {
2224	screen->eventMode = LEFTEXTENSION;
2225	screen->startExt = *cell;
2226    } else {
2227	screen->eventMode = RIGHTEXTENSION;
2228	screen->endExt = *cell;
2229    }
2230    ComputeSelect(xw, &(screen->startExt), &(screen->endExt), False);
2231}
2232
2233static void
2234EndExtend(XtermWidget xw,
2235	  XEvent * event,	/* must be XButtonEvent */
2236	  String * params,	/* selections */
2237	  Cardinal num_params,
2238	  Bool use_cursor_loc)
2239{
2240    CELL cell;
2241    unsigned count;
2242    TScreen *screen = TScreenOf(xw);
2243    Char line[20];
2244
2245    if (use_cursor_loc) {
2246	cell = screen->cursorp;
2247    } else {
2248	PointToCELL(screen, event->xbutton.y, event->xbutton.x, &cell);
2249    }
2250    ExtendExtend(xw, &cell);
2251    screen->lastButtonUpTime = event->xbutton.time;
2252    screen->lastButton = event->xbutton.button;
2253    if (!isSameCELL(&(screen->startSel), &(screen->endSel))) {
2254	if (screen->replyToEmacs) {
2255	    count = 0;
2256	    if (screen->control_eight_bits) {
2257		line[count++] = ANSI_CSI;
2258	    } else {
2259		line[count++] = ANSI_ESC;
2260		line[count++] = '[';
2261	    }
2262	    if (isSameCELL(&(screen->rawPos), &(screen->startSel))
2263		&& isSameCELL(&cell, &(screen->endSel))) {
2264		/* Use short-form emacs select */
2265		line[count++] = 't';
2266		count = EmitMousePosition(screen, line, count, screen->endSel.col);
2267		count = EmitMousePosition(screen, line, count, screen->endSel.row);
2268	    } else {
2269		/* long-form, specify everything */
2270		line[count++] = 'T';
2271		count = EmitMousePosition(screen, line, count, screen->startSel.col);
2272		count = EmitMousePosition(screen, line, count, screen->startSel.row);
2273		count = EmitMousePosition(screen, line, count, screen->endSel.col);
2274		count = EmitMousePosition(screen, line, count, screen->endSel.row);
2275		count = EmitMousePosition(screen, line, count, cell.col);
2276		count = EmitMousePosition(screen, line, count, cell.row);
2277	    }
2278	    v_write(screen->respond, line, count);
2279	    TrackText(xw, &zeroCELL, &zeroCELL);
2280	}
2281    }
2282    SelectSet(xw, event, params, num_params);
2283    screen->eventMode = NORMAL;
2284}
2285
2286void
2287HandleSelectSet(Widget w,
2288		XEvent * event,
2289		String * params,
2290		Cardinal *num_params)
2291{
2292    XtermWidget xw;
2293
2294    if ((xw = getXtermWidget(w)) != 0) {
2295	SelectSet(xw, event, params, *num_params);
2296    }
2297}
2298
2299/* ARGSUSED */
2300static void
2301SelectSet(XtermWidget xw,
2302	  XEvent * event GCC_UNUSED,
2303	  String * params,
2304	  Cardinal num_params)
2305{
2306    TScreen *screen = TScreenOf(xw);
2307
2308    TRACE(("SelectSet\n"));
2309    /* Only do select stuff if non-null select */
2310    if (!isSameCELL(&(screen->startSel), &(screen->endSel))) {
2311	SaltTextAway(xw, &(screen->startSel), &(screen->endSel), params, num_params);
2312    } else {
2313	DisownSelection(xw);
2314    }
2315}
2316
2317#define Abs(x)		((x) < 0 ? -(x) : (x))
2318
2319/* ARGSUSED */
2320static void
2321do_start_extend(XtermWidget xw,
2322		XEvent * event,	/* must be XButtonEvent* */
2323		String * params GCC_UNUSED,
2324		Cardinal *num_params GCC_UNUSED,
2325		Bool use_cursor_loc)
2326{
2327    TScreen *screen = TScreenOf(xw);
2328    int coord;
2329    CELL cell;
2330
2331    if (SendMousePosition(xw, event))
2332	return;
2333
2334    screen->firstValidRow = 0;
2335    screen->lastValidRow = screen->max_row;
2336#if OPT_READLINE
2337    if ((KeyModifiers(event) & ShiftMask)
2338	|| event->xbutton.button != Button3
2339	|| !(SCREEN_FLAG(screen, dclick3_deletes)))
2340#endif
2341	screen->selectUnit = EvalSelectUnit(xw,
2342					    event->xbutton.time,
2343					    screen->selectUnit,
2344					    event->xbutton.button);
2345    screen->replyToEmacs = False;
2346
2347#if OPT_READLINE
2348    CheckSecondPress3(screen, event);
2349#endif
2350
2351    if (screen->numberOfClicks == 1
2352	|| (SCREEN_FLAG(screen, dclick3_deletes)	/* Dclick special */
2353	    &&!(KeyModifiers(event) & ShiftMask))) {
2354	/* Save existing selection so we can reestablish it if the guy
2355	   extends past the other end of the selection */
2356	screen->saveStartR = screen->startExt = screen->startRaw;
2357	screen->saveEndR = screen->endExt = screen->endRaw;
2358    } else {
2359	/* He just needed the selection mode changed, use old values. */
2360	screen->startExt = screen->startRaw = screen->saveStartR;
2361	screen->endExt = screen->endRaw = screen->saveEndR;
2362    }
2363    if (use_cursor_loc) {
2364	cell = screen->cursorp;
2365    } else {
2366	PointToCELL(screen, event->xbutton.y, event->xbutton.x, &cell);
2367    }
2368    coord = Coordinate(screen, &cell);
2369
2370    if (Abs(coord - Coordinate(screen, &(screen->startSel)))
2371	< Abs(coord - Coordinate(screen, &(screen->endSel)))
2372	|| coord < Coordinate(screen, &(screen->startSel))) {
2373	/* point is close to left side of selection */
2374	screen->eventMode = LEFTEXTENSION;
2375	screen->startExt = cell;
2376    } else {
2377	/* point is close to left side of selection */
2378	screen->eventMode = RIGHTEXTENSION;
2379	screen->endExt = cell;
2380    }
2381    ComputeSelect(xw, &(screen->startExt), &(screen->endExt), True);
2382
2383#if OPT_READLINE
2384    if (!isSameCELL(&(screen->startSel), &(screen->endSel)))
2385	ExtendingSelection = 1;
2386#endif
2387}
2388
2389static void
2390ExtendExtend(XtermWidget xw, const CELL * cell)
2391{
2392    TScreen *screen = TScreenOf(xw);
2393    int coord = Coordinate(screen, cell);
2394
2395    TRACE(("ExtendExtend row=%d, col=%d\n", cell->row, cell->col));
2396    if (screen->eventMode == LEFTEXTENSION
2397	&& ((coord + (screen->selectUnit != Select_CHAR))
2398	    > Coordinate(screen, &(screen->endSel)))) {
2399	/* Whoops, he's changed his mind.  Do RIGHTEXTENSION */
2400	screen->eventMode = RIGHTEXTENSION;
2401	screen->startExt = screen->saveStartR;
2402    } else if (screen->eventMode == RIGHTEXTENSION
2403	       && coord < Coordinate(screen, &(screen->startSel))) {
2404	/* Whoops, he's changed his mind.  Do LEFTEXTENSION */
2405	screen->eventMode = LEFTEXTENSION;
2406	screen->endExt = screen->saveEndR;
2407    }
2408    if (screen->eventMode == LEFTEXTENSION) {
2409	screen->startExt = *cell;
2410    } else {
2411	screen->endExt = *cell;
2412    }
2413    ComputeSelect(xw, &(screen->startExt), &(screen->endExt), False);
2414
2415#if OPT_READLINE
2416    if (!isSameCELL(&(screen->startSel), &(screen->endSel)))
2417	ExtendingSelection = 1;
2418#endif
2419}
2420
2421void
2422HandleStartExtend(Widget w,
2423		  XEvent * event,	/* must be XButtonEvent* */
2424		  String * params,	/* unused */
2425		  Cardinal *num_params)		/* unused */
2426{
2427    XtermWidget xw;
2428
2429    if ((xw = getXtermWidget(w)) != 0) {
2430	do_start_extend(xw, event, params, num_params, False);
2431    }
2432}
2433
2434void
2435HandleKeyboardStartExtend(Widget w,
2436			  XEvent * event,	/* must be XButtonEvent* */
2437			  String * params,	/* unused */
2438			  Cardinal *num_params)		/* unused */
2439{
2440    XtermWidget xw;
2441
2442    if ((xw = getXtermWidget(w)) != 0) {
2443	do_start_extend(xw, event, params, num_params, True);
2444    }
2445}
2446
2447void
2448ScrollSelection(TScreen * screen, int amount, Bool always)
2449{
2450    int minrow = INX2ROW(screen, -screen->savedlines);
2451    int maxrow = INX2ROW(screen, screen->max_row);
2452    int maxcol = screen->max_col;
2453
2454#define scroll_update_one(cell) \
2455	(cell)->row += amount; \
2456	if ((cell)->row < minrow) { \
2457	    (cell)->row = minrow; \
2458	    (cell)->col = 0; \
2459	} \
2460	if ((cell)->row > maxrow) { \
2461	    (cell)->row = maxrow; \
2462	    (cell)->col = maxcol; \
2463	}
2464
2465    scroll_update_one(&(screen->startRaw));
2466    scroll_update_one(&(screen->endRaw));
2467    scroll_update_one(&(screen->startSel));
2468    scroll_update_one(&(screen->endSel));
2469
2470    scroll_update_one(&(screen->rawPos));
2471
2472    /*
2473     * If we are told to scroll the selection but it lies outside the scrolling
2474     * margins, then that could cause the selection to move (bad).  It is not
2475     * simple to fix, because this function is called both for the scrollbar
2476     * actions as well as application scrolling.  The 'always' flag is set in
2477     * the former case.  The rest of the logic handles the latter.
2478     */
2479    if (ScrnHaveSelection(screen)) {
2480	int adjust;
2481
2482	adjust = ROW2INX(screen, screen->startH.row);
2483	if (always
2484	    || !ScrnHaveLineMargins(screen)
2485	    || ScrnIsLineInMargins(screen, adjust)) {
2486	    scroll_update_one(&screen->startH);
2487	}
2488	adjust = ROW2INX(screen, screen->endH.row);
2489	if (always
2490	    || !ScrnHaveLineMargins(screen)
2491	    || ScrnIsLineInMargins(screen, adjust)) {
2492	    scroll_update_one(&screen->endH);
2493	}
2494    }
2495
2496    screen->startHCoord = Coordinate(screen, &screen->startH);
2497    screen->endHCoord = Coordinate(screen, &screen->endH);
2498}
2499
2500/*ARGSUSED*/
2501void
2502ResizeSelection(TScreen * screen GCC_UNUSED, int rows, int cols)
2503{
2504    rows--;			/* decr to get 0-max */
2505    cols--;
2506
2507    if (screen->startRaw.row > rows)
2508	screen->startRaw.row = rows;
2509    if (screen->startSel.row > rows)
2510	screen->startSel.row = rows;
2511    if (screen->endRaw.row > rows)
2512	screen->endRaw.row = rows;
2513    if (screen->endSel.row > rows)
2514	screen->endSel.row = rows;
2515    if (screen->rawPos.row > rows)
2516	screen->rawPos.row = rows;
2517
2518    if (screen->startRaw.col > cols)
2519	screen->startRaw.col = cols;
2520    if (screen->startSel.col > cols)
2521	screen->startSel.col = cols;
2522    if (screen->endRaw.col > cols)
2523	screen->endRaw.col = cols;
2524    if (screen->endSel.col > cols)
2525	screen->endSel.col = cols;
2526    if (screen->rawPos.col > cols)
2527	screen->rawPos.col = cols;
2528}
2529
2530#if OPT_WIDE_CHARS
2531Bool
2532iswide(int i)
2533{
2534    return (i == HIDDEN_CHAR) || (WideCells(i) == 2);
2535}
2536
2537#define isWideCell(row, col) iswide((int)XTERM_CELL(row, col))
2538#endif
2539
2540static void
2541PointToCELL(TScreen * screen,
2542	    int y,
2543	    int x,
2544	    CELL * cell)
2545/* Convert pixel coordinates to character coordinates.
2546   Rows are clipped between firstValidRow and lastValidRow.
2547   Columns are clipped between to be 0 or greater, but are not clipped to some
2548       maximum value. */
2549{
2550    cell->row = (y - screen->border) / FontHeight(screen);
2551    if (cell->row < screen->firstValidRow)
2552	cell->row = screen->firstValidRow;
2553    else if (cell->row > screen->lastValidRow)
2554	cell->row = screen->lastValidRow;
2555    cell->col = (x - OriginX(screen)) / FontWidth(screen);
2556    if (cell->col < 0)
2557	cell->col = 0;
2558    else if (cell->col > MaxCols(screen)) {
2559	cell->col = MaxCols(screen);
2560    }
2561#if OPT_WIDE_CHARS
2562    /*
2563     * If we got a click on the right half of a doublewidth character,
2564     * pretend it happened on the left half.
2565     */
2566    if (cell->col > 0
2567	&& isWideCell(cell->row, cell->col - 1)
2568	&& (XTERM_CELL(cell->row, cell->col) == HIDDEN_CHAR)) {
2569	cell->col -= 1;
2570    }
2571#endif
2572}
2573
2574/*
2575 * Find the last column at which text was drawn on the given row.
2576 */
2577static int
2578LastTextCol(TScreen * screen, LineData * ld, int row)
2579{
2580    int i = -1;
2581    Char *ch;
2582
2583    if (ld != 0) {
2584	if (okScrnRow(screen, row)) {
2585	    for (i = screen->max_col,
2586		 ch = ld->attribs + i;
2587		 i >= 0 && !(*ch & CHARDRAWN);
2588		 ch--, i--) {
2589		;
2590	    }
2591#if OPT_DEC_CHRSET
2592	    if (CSET_DOUBLE(GetLineDblCS(ld))) {
2593		i *= 2;
2594	    }
2595#endif
2596	}
2597    }
2598    return (i);
2599}
2600
2601#if !OPT_WIDE_CHARS
2602/*
2603** double click table for cut and paste in 8 bits
2604**
2605** This table is divided in four parts :
2606**
2607**	- control characters	[0,0x1f] U [0x80,0x9f]
2608**	- separators		[0x20,0x3f] U [0xa0,0xb9]
2609**	- binding characters	[0x40,0x7f] U [0xc0,0xff]
2610**	- exceptions
2611*/
2612/* *INDENT-OFF* */
2613static int charClass[256] =
2614{
2615/* NUL  SOH  STX  ETX  EOT  ENQ  ACK  BEL */
2616    32,  1,    1,   1,   1,   1,   1,   1,
2617/*  BS   HT   NL   VT   NP   CR   SO   SI */
2618     1,  32,   1,   1,   1,   1,   1,   1,
2619/* DLE  DC1  DC2  DC3  DC4  NAK  SYN  ETB */
2620     1,   1,   1,   1,   1,   1,   1,   1,
2621/* CAN   EM  SUB  ESC   FS   GS   RS   US */
2622     1,   1,   1,   1,   1,   1,   1,   1,
2623/*  SP    !    "    #    $    %    &    ' */
2624    32,  33,  34,  35,  36,  37,  38,  39,
2625/*   (    )    *    +    ,    -    .    / */
2626    40,  41,  42,  43,  44,  45,  46,  47,
2627/*   0    1    2    3    4    5    6    7 */
2628    48,  48,  48,  48,  48,  48,  48,  48,
2629/*   8    9    :    ;    <    =    >    ? */
2630    48,  48,  58,  59,  60,  61,  62,  63,
2631/*   @    A    B    C    D    E    F    G */
2632    64,  48,  48,  48,  48,  48,  48,  48,
2633/*   H    I    J    K    L    M    N    O */
2634    48,  48,  48,  48,  48,  48,  48,  48,
2635/*   P    Q    R    S    T    U    V    W */
2636    48,  48,  48,  48,  48,  48,  48,  48,
2637/*   X    Y    Z    [    \    ]    ^    _ */
2638    48,  48,  48,  91,  92,  93,  94,  48,
2639/*   `    a    b    c    d    e    f    g */
2640    96,  48,  48,  48,  48,  48,  48,  48,
2641/*   h    i    j    k    l    m    n    o */
2642    48,  48,  48,  48,  48,  48,  48,  48,
2643/*   p    q    r    s    t    u    v    w */
2644    48,  48,  48,  48,  48,  48,  48,  48,
2645/*   x    y    z    {    |    }    ~  DEL */
2646    48,  48,  48, 123, 124, 125, 126,   1,
2647/* x80  x81  x82  x83  IND  NEL  SSA  ESA */
2648    1,    1,   1,   1,   1,   1,   1,   1,
2649/* HTS  HTJ  VTS  PLD  PLU   RI  SS2  SS3 */
2650    1,    1,   1,   1,   1,   1,   1,   1,
2651/* DCS  PU1  PU2  STS  CCH   MW  SPA  EPA */
2652    1,    1,   1,   1,   1,   1,   1,   1,
2653/* x98  x99  x9A  CSI   ST  OSC   PM  APC */
2654    1,    1,   1,   1,   1,   1,   1,   1,
2655/*   -    i   c/    L   ox   Y-    |   So */
2656    160, 161, 162, 163, 164, 165, 166, 167,
2657/*  ..   c0   ip   <<    _        R0    - */
2658    168, 169, 170, 171, 172, 173, 174, 175,
2659/*   o   +-    2    3    '    u   q|    . */
2660    176, 177, 178, 179, 180, 181, 182, 183,
2661/*   ,    1    2   >>  1/4  1/2  3/4    ? */
2662    184, 185, 186, 187, 188, 189, 190, 191,
2663/*  A`   A'   A^   A~   A:   Ao   AE   C, */
2664     48,  48,  48,  48,  48,  48,  48,  48,
2665/*  E`   E'   E^   E:   I`   I'   I^   I: */
2666     48,  48,  48,  48,  48,  48,  48,  48,
2667/*  D-   N~   O`   O'   O^   O~   O:    X */
2668     48,  48,  48,  48,  48,  48,  48, 215,
2669/*  O/   U`   U'   U^   U:   Y'    P    B */
2670     48,  48,  48,  48,  48,  48,  48,  48,
2671/*  a`   a'   a^   a~   a:   ao   ae   c, */
2672     48,  48,  48,  48,  48,  48,  48,  48,
2673/*  e`   e'   e^   e:    i`  i'   i^   i: */
2674     48,  48,  48,  48,  48,  48,  48,  48,
2675/*   d   n~   o`   o'   o^   o~   o:   -: */
2676     48,  48,  48,  48,  48,  48,  48, 247,
2677/*  o/   u`   u'   u^   u:   y'    P   y: */
2678     48,  48,  48,  48,  48,  48,  48,  48};
2679/* *INDENT-ON* */
2680
2681int
2682SetCharacterClassRange(int low,	/* in range of [0..255] */
2683		       int high,
2684		       int value)	/* arbitrary */
2685{
2686
2687    if (low < 0 || high > 255 || high < low)
2688	return (-1);
2689
2690    for (; low <= high; low++)
2691	charClass[low] = value;
2692
2693    return (0);
2694}
2695#endif
2696
2697static int
2698class_of(LineData * ld, CELL * cell)
2699{
2700    CELL temp = *cell;
2701
2702#if OPT_DEC_CHRSET
2703    if (CSET_DOUBLE(GetLineDblCS(ld))) {
2704	temp.col /= 2;
2705    }
2706#endif
2707
2708    assert(temp.col < ld->lineSize);
2709    return CharacterClass((int) (ld->charData[temp.col]));
2710}
2711
2712#if OPT_WIDE_CHARS
2713#define CClassSelects(name, cclass) \
2714	 (CClassOf(name) == cclass \
2715	 || XTERM_CELL(screen->name.row, screen->name.col) == HIDDEN_CHAR)
2716#else
2717#define CClassSelects(name, cclass) \
2718	 (class_of(ld.name, &((screen->name))) == cclass)
2719#endif
2720
2721#define CClassOf(name) class_of(ld.name, &((screen->name)))
2722
2723/*
2724 * If the given column is past the end of text on the given row, bump to the
2725 * beginning of the next line.
2726 */
2727static Boolean
2728okPosition(TScreen * screen,
2729	   LineData ** ld,
2730	   CELL * cell)
2731{
2732    Boolean result = True;
2733
2734    if (cell->row > screen->max_row) {
2735	result = False;
2736    } else if (cell->col > (LastTextCol(screen, *ld, cell->row) + 1)) {
2737	if (cell->row < screen->max_row) {
2738	    cell->col = 0;
2739	    *ld = GET_LINEDATA(screen, ++cell->row);
2740	    result = False;
2741	}
2742    }
2743    return result;
2744}
2745
2746static void
2747trimLastLine(TScreen * screen,
2748	     LineData ** ld,
2749	     CELL * last)
2750{
2751    if (screen->cutNewline && last->row < screen->max_row) {
2752	last->col = 0;
2753	*ld = GET_LINEDATA(screen, ++last->row);
2754    } else {
2755	last->col = LastTextCol(screen, *ld, last->row) + 1;
2756    }
2757}
2758
2759#if OPT_SELECT_REGEX
2760/*
2761 * Returns the first row of a wrapped line.
2762 */
2763static int
2764firstRowOfLine(TScreen * screen, int row, Bool visible)
2765{
2766    LineData *ld = 0;
2767    int limit = visible ? 0 : -screen->savedlines;
2768
2769    while (row > limit &&
2770	   (ld = GET_LINEDATA(screen, row - 1)) != 0 &&
2771	   LineTstWrapped(ld)) {
2772	--row;
2773    }
2774    return row;
2775}
2776
2777/*
2778 * Returns the last row of a wrapped line.
2779 */
2780static int
2781lastRowOfLine(TScreen * screen, int row)
2782{
2783    LineData *ld;
2784
2785    while (row < screen->max_row &&
2786	   (ld = GET_LINEDATA(screen, row)) != 0 &&
2787	   LineTstWrapped(ld)) {
2788	++row;
2789    }
2790    return row;
2791}
2792
2793/*
2794 * Returns the number of cells on the range of rows.
2795 */
2796static unsigned
2797lengthOfLines(TScreen * screen, int firstRow, int lastRow)
2798{
2799    unsigned length = 0;
2800    int n;
2801
2802    for (n = firstRow; n <= lastRow; ++n) {
2803	LineData *ld = GET_LINEDATA(screen, n);
2804	int value = LastTextCol(screen, ld, n);
2805	if (value >= 0)
2806	    length += (unsigned) (value + 1);
2807    }
2808    return length;
2809}
2810
2811/*
2812 * Make a copy of the wrapped-line which corresponds to the given row as a
2813 * string of bytes.  Construct an index for the columns from the beginning of
2814 * the line.
2815 */
2816static char *
2817make_indexed_text(TScreen * screen, int row, unsigned length, int *indexed)
2818{
2819    Char *result = 0;
2820    size_t need = (length + 1);
2821
2822    /*
2823     * Get a quick upper bound to the number of bytes needed, if the whole
2824     * string were UTF-8.
2825     */
2826    if_OPT_WIDE_CHARS(screen, {
2827	need *= ((screen->lineExtra + 1) * 6);
2828    });
2829
2830    if ((result = TypeCallocN(Char, need + 1)) != 0) {
2831	LineData *ld = GET_LINEDATA(screen, row);
2832	unsigned used = 0;
2833	Char *last = result;
2834
2835	do {
2836	    int col = 0;
2837	    int limit = LastTextCol(screen, ld, row);
2838
2839	    while (col <= limit) {
2840		Char *next = last;
2841		unsigned data = ld->charData[col];
2842
2843		assert(col < ld->lineSize);
2844		/* some internal points may not be drawn */
2845		if (data == 0)
2846		    data = ' ';
2847
2848		if_WIDE_OR_NARROW(screen, {
2849		    next = convertToUTF8(last, data);
2850		}
2851		, {
2852		    *next++ = CharOf(data);
2853		});
2854
2855		if_OPT_WIDE_CHARS(screen, {
2856		    size_t off;
2857		    for_each_combData(off, ld) {
2858			data = ld->combData[off][col];
2859			if (data == 0)
2860			    break;
2861			next = convertToUTF8(next, data);
2862		    }
2863		});
2864
2865		indexed[used] = (int) (last - result);
2866		*next = 0;
2867		/* TRACE(("index[%d.%d] %d:%s\n", row, used, indexed[used], last)); */
2868		last = next;
2869		++used;
2870		++col;
2871		indexed[used] = (int) (next - result);
2872	    }
2873	} while (used < length &&
2874		 LineTstWrapped(ld) &&
2875		 (ld = GET_LINEDATA(screen, ++row)) != 0 &&
2876		 row < screen->max_row);
2877    }
2878    /* TRACE(("result:%s\n", result)); */
2879    return (char *) result;
2880}
2881
2882/*
2883 * Find the column given an offset into the character string by using the
2884 * index constructed in make_indexed_text().
2885 */
2886static int
2887indexToCol(int *indexed, int len, int off)
2888{
2889    int col = 0;
2890    while (indexed[col] < len) {
2891	if (indexed[col] >= off)
2892	    break;
2893	++col;
2894    }
2895    return col;
2896}
2897
2898/*
2899 * Given a row number, and a column offset from that (which may be wrapped),
2900 * set the cell to the actual row/column values.
2901 */
2902static void
2903columnToCell(TScreen * screen, int row, int col, CELL * cell)
2904{
2905    while (row < screen->max_row) {
2906	LineData *ld = GET_LINEDATA(screen, row);
2907	int last = LastTextCol(screen, ld, row);
2908
2909	/* TRACE(("last(%d) = %d, have %d\n", row, last, col)); */
2910	if (col <= last) {
2911	    break;
2912	}
2913	/*
2914	 * Stop if the current row does not wrap (does not continue the current
2915	 * line).
2916	 */
2917	if (!LineTstWrapped(ld)) {
2918	    col = last + 1;
2919	    break;
2920	}
2921	col -= (last + 1);
2922	++row;
2923    }
2924    if (col < 0)
2925	col = 0;
2926    cell->row = row;
2927    cell->col = col;
2928}
2929
2930/*
2931 * Given a cell, find the corresponding column offset.
2932 */
2933static int
2934cellToColumn(TScreen * screen, CELL * cell)
2935{
2936    LineData *ld = 0;
2937    int col = cell->col;
2938    int row = firstRowOfLine(screen, cell->row, False);
2939    while (row < cell->row) {
2940	ld = GET_LINEDATA(screen, row);
2941	col += LastTextCol(screen, ld, row++);
2942    }
2943#if OPT_DEC_CHRSET
2944    if (ld == 0)
2945	ld = GET_LINEDATA(screen, row);
2946    if (CSET_DOUBLE(GetLineDblCS(ld)))
2947	col /= 2;
2948#endif
2949    return col;
2950}
2951
2952static void
2953do_select_regex(TScreen * screen, CELL * startc, CELL * endc)
2954{
2955    LineData *ld = GET_LINEDATA(screen, startc->row);
2956    int inx = ((screen->numberOfClicks - 1) % screen->maxClicks);
2957    char *expr = screen->selectExpr[inx];
2958    regex_t preg;
2959    regmatch_t match;
2960    char *search;
2961    int *indexed;
2962
2963    TRACE(("Select_REGEX:%s\n", NonNull(expr)));
2964    if (okPosition(screen, &ld, startc) && expr != 0) {
2965	if (regcomp(&preg, expr, REG_EXTENDED) == 0) {
2966	    int firstRow = firstRowOfLine(screen, startc->row, True);
2967	    int lastRow = lastRowOfLine(screen, firstRow);
2968	    unsigned size = lengthOfLines(screen, firstRow, lastRow);
2969	    int actual = cellToColumn(screen, startc);
2970
2971	    TRACE(("regcomp ok rows %d..%d bytes %d\n",
2972		   firstRow, lastRow, size));
2973
2974	    if ((indexed = TypeCallocN(int, size + 1)) != 0) {
2975		if ((search = make_indexed_text(screen,
2976						firstRow,
2977						size,
2978						indexed)) != 0) {
2979		    int len = (int) strlen(search);
2980		    int col;
2981		    int best_col = -1;
2982		    int best_len = -1;
2983
2984		    for (col = 0; indexed[col] < len; ++col) {
2985			if (regexec(&preg,
2986				    search + indexed[col],
2987				    (size_t) 1, &match, 0) == 0) {
2988			    int start_inx = match.rm_so + indexed[col];
2989			    int finis_inx = match.rm_eo + indexed[col];
2990			    int start_col = indexToCol(indexed, len, start_inx);
2991			    int finis_col = indexToCol(indexed, len, finis_inx);
2992
2993			    if (start_col <= actual &&
2994				actual < finis_col) {
2995				int test = finis_col - start_col;
2996				if (best_len < test) {
2997				    best_len = test;
2998				    best_col = start_col;
2999				    TRACE(("match column %d len %d\n",
3000					   best_col,
3001					   best_len));
3002				}
3003			    }
3004			}
3005		    }
3006		    if (best_col >= 0) {
3007			int best_nxt = best_col + best_len;
3008			columnToCell(screen, firstRow, best_col, startc);
3009			columnToCell(screen, firstRow, best_nxt, endc);
3010			TRACE(("search::%s\n", search));
3011			TRACE(("indexed:%d..%d -> %d..%d\n",
3012			       best_col, best_nxt,
3013			       indexed[best_col],
3014			       indexed[best_nxt]));
3015			TRACE(("matched:%d:%s\n",
3016			       indexed[best_nxt] + 1 -
3017			       indexed[best_col],
3018			       visibleChars((Char *) (search + indexed[best_col]),
3019					    (unsigned) (indexed[best_nxt] +
3020							1 -
3021							indexed[best_col]))));
3022		    }
3023		    free(search);
3024		}
3025		free(indexed);
3026#if OPT_DEC_CHRSET
3027		if ((ld = GET_LINEDATA(screen, startc->row)) != 0) {
3028		    if (CSET_DOUBLE(GetLineDblCS(ld)))
3029			startc->col *= 2;
3030		}
3031		if ((ld = GET_LINEDATA(screen, endc->row)) != 0) {
3032		    if (CSET_DOUBLE(GetLineDblCS(ld)))
3033			endc->col *= 2;
3034		}
3035#endif
3036	    }
3037	    regfree(&preg);
3038	}
3039    }
3040}
3041#endif /* OPT_SELECT_REGEX */
3042
3043#define InitRow(name) \
3044	ld.name = GET_LINEDATA(screen, screen->name.row)
3045
3046#define NextRow(name) \
3047	ld.name = GET_LINEDATA(screen, ++screen->name.row)
3048
3049#define PrevRow(name) \
3050	ld.name = GET_LINEDATA(screen, --screen->name.row)
3051
3052#define MoreRows(name) \
3053	(screen->name.row < screen->max_row)
3054
3055#define isPrevWrapped(name) \
3056	(screen->name.row > 0 \
3057	   && (ltmp = GET_LINEDATA(screen, screen->name.row - 1)) != 0 \
3058	   && LineTstWrapped(ltmp))
3059
3060/*
3061 * sets startSel endSel
3062 * ensuring that they have legal values
3063 */
3064static void
3065ComputeSelect(XtermWidget xw,
3066	      CELL * startc,
3067	      CELL * endc,
3068	      Bool extend)
3069{
3070    TScreen *screen = TScreenOf(xw);
3071
3072    int length;
3073    int cclass;
3074    CELL first = *startc;
3075    CELL last = *endc;
3076    Boolean ignored = False;
3077
3078    struct {
3079	LineData *startSel;
3080	LineData *endSel;
3081    } ld;
3082    LineData *ltmp;
3083
3084    TRACE(("ComputeSelect(startRow=%d, startCol=%d, endRow=%d, endCol=%d, %sextend)\n",
3085	   first.row, first.col,
3086	   last.row, last.col,
3087	   extend ? "" : "no"));
3088
3089#if OPT_WIDE_CHARS
3090    if (first.col > 1
3091	&& isWideCell(first.row, first.col - 1)
3092	&& XTERM_CELL(first.row, first.col - 0) == HIDDEN_CHAR) {
3093	TRACE(("Adjusting start. Changing downwards from %i.\n", first.col));
3094	first.col -= 1;
3095	if (last.col == (first.col + 1))
3096	    last.col--;
3097    }
3098
3099    if (last.col > 1
3100	&& isWideCell(last.row, last.col - 1)
3101	&& XTERM_CELL(last.row, last.col) == HIDDEN_CHAR) {
3102	last.col += 1;
3103    }
3104#endif
3105
3106    if (Coordinate(screen, &first) <= Coordinate(screen, &last)) {
3107	screen->startSel = screen->startRaw = first;
3108	screen->endSel = screen->endRaw = last;
3109    } else {			/* Swap them */
3110	screen->startSel = screen->startRaw = last;
3111	screen->endSel = screen->endRaw = first;
3112    }
3113
3114    InitRow(startSel);
3115    InitRow(endSel);
3116
3117    switch (screen->selectUnit) {
3118    case Select_CHAR:
3119	(void) okPosition(screen, &(ld.startSel), &(screen->startSel));
3120	(void) okPosition(screen, &(ld.endSel), &(screen->endSel));
3121	break;
3122
3123    case Select_WORD:
3124	TRACE(("Select_WORD\n"));
3125	if (okPosition(screen, &(ld.startSel), &(screen->startSel))) {
3126	    cclass = CClassOf(startSel);
3127	    do {
3128		--screen->startSel.col;
3129		if (screen->startSel.col < 0
3130		    && isPrevWrapped(startSel)) {
3131		    PrevRow(startSel);
3132		    screen->startSel.col = LastTextCol(screen, ld.startSel, screen->startSel.row);
3133		}
3134	    } while (screen->startSel.col >= 0
3135		     && CClassSelects(startSel, cclass));
3136	    ++screen->startSel.col;
3137	}
3138#if OPT_WIDE_CHARS
3139	if (screen->startSel.col
3140	    && XTERM_CELL(screen->startSel.row,
3141			  screen->startSel.col) == HIDDEN_CHAR)
3142	    screen->startSel.col++;
3143#endif
3144
3145	if (okPosition(screen, &(ld.endSel), &(screen->endSel))) {
3146	    length = LastTextCol(screen, ld.endSel, screen->endSel.row);
3147	    cclass = CClassOf(endSel);
3148	    do {
3149		++screen->endSel.col;
3150		if (screen->endSel.col > length
3151		    && LineTstWrapped(ld.endSel)) {
3152		    if (!MoreRows(endSel))
3153			break;
3154		    screen->endSel.col = 0;
3155		    NextRow(endSel);
3156		    length = LastTextCol(screen, ld.endSel, screen->endSel.row);
3157		}
3158	    } while (screen->endSel.col <= length
3159		     && CClassSelects(endSel, cclass));
3160	    /* Word-select selects if pointing to any char in "word",
3161	     * especially note that it includes the last character in a word.
3162	     * So we do no --endSel.col and do special eol handling.
3163	     */
3164	    if (screen->endSel.col > length + 1
3165		&& MoreRows(endSel)) {
3166		screen->endSel.col = 0;
3167		NextRow(endSel);
3168	    }
3169	}
3170#if OPT_WIDE_CHARS
3171	if (screen->endSel.col
3172	    && XTERM_CELL(screen->endSel.row,
3173			  screen->endSel.col) == HIDDEN_CHAR)
3174	    screen->endSel.col++;
3175#endif
3176
3177	screen->saveStartW = screen->startSel;
3178	break;
3179
3180    case Select_LINE:
3181	TRACE(("Select_LINE\n"));
3182	while (LineTstWrapped(ld.endSel)
3183	       && MoreRows(endSel)) {
3184	    NextRow(endSel);
3185	}
3186	if (screen->cutToBeginningOfLine
3187	    || screen->startSel.row < screen->saveStartW.row) {
3188	    screen->startSel.col = 0;
3189	    while (isPrevWrapped(startSel)) {
3190		PrevRow(startSel);
3191	    }
3192	} else if (!extend) {
3193	    if ((first.row < screen->saveStartW.row)
3194		|| (isSameRow(&first, &(screen->saveStartW))
3195		    && first.col < screen->saveStartW.col)) {
3196		screen->startSel.col = 0;
3197		while (isPrevWrapped(startSel)) {
3198		    PrevRow(startSel);
3199		}
3200	    } else {
3201		screen->startSel = screen->saveStartW;
3202	    }
3203	}
3204	trimLastLine(screen, &(ld.endSel), &(screen->endSel));
3205	break;
3206
3207    case Select_GROUP:		/* paragraph */
3208	TRACE(("Select_GROUP\n"));
3209	if (okPosition(screen, &(ld.startSel), &(screen->startSel))) {
3210	    /* scan backward for beginning of group */
3211	    while (screen->startSel.row > 0 &&
3212		   (LastTextCol(screen, ld.startSel, screen->startSel.row -
3213				1) > 0 ||
3214		    isPrevWrapped(startSel))) {
3215		PrevRow(startSel);
3216	    }
3217	    screen->startSel.col = 0;
3218	    /* scan forward for end of group */
3219	    while (MoreRows(endSel) &&
3220		   (LastTextCol(screen, ld.endSel, screen->endSel.row + 1) >
3221		    0 ||
3222		    LineTstWrapped(ld.endSel))) {
3223		NextRow(endSel);
3224	    }
3225	    trimLastLine(screen, &(ld.endSel), &(screen->endSel));
3226	}
3227	break;
3228
3229    case Select_PAGE:		/* everything one can see */
3230	TRACE(("Select_PAGE\n"));
3231	screen->startSel.row = 0;
3232	screen->startSel.col = 0;
3233	screen->endSel.row = MaxRows(screen);
3234	screen->endSel.col = 0;
3235	break;
3236
3237    case Select_ALL:		/* counts scrollback if in normal screen */
3238	TRACE(("Select_ALL\n"));
3239	screen->startSel.row = -screen->savedlines;
3240	screen->startSel.col = 0;
3241	screen->endSel.row = MaxRows(screen);
3242	screen->endSel.col = 0;
3243	break;
3244
3245#if OPT_SELECT_REGEX
3246    case Select_REGEX:
3247	do_select_regex(screen, &(screen->startSel), &(screen->endSel));
3248	break;
3249#endif
3250
3251    case NSELECTUNITS:		/* always ignore */
3252	ignored = True;
3253	break;
3254    }
3255
3256    if (!ignored) {
3257	/* check boundaries */
3258	ScrollSelection(screen, 0, False);
3259	TrackText(xw, &(screen->startSel), &(screen->endSel));
3260    }
3261
3262    return;
3263}
3264
3265/* Guaranteed (first.row, first.col) <= (last.row, last.col) */
3266static void
3267TrackText(XtermWidget xw,
3268	  const CELL * firstp,
3269	  const CELL * lastp)
3270{
3271    TScreen *screen = TScreenOf(xw);
3272    int from, to;
3273    CELL old_start, old_end;
3274    CELL first = *firstp;
3275    CELL last = *lastp;
3276
3277    TRACE(("TrackText(first=%d,%d, last=%d,%d)\n",
3278	   first.row, first.col, last.row, last.col));
3279
3280    old_start = screen->startH;
3281    old_end = screen->endH;
3282    if (isSameCELL(&first, &old_start) &&
3283	isSameCELL(&last, &old_end))
3284	return;
3285    screen->startH = first;
3286    screen->endH = last;
3287    from = Coordinate(screen, &screen->startH);
3288    to = Coordinate(screen, &screen->endH);
3289    if (to <= screen->startHCoord || from > screen->endHCoord) {
3290	/* No overlap whatsoever between old and new hilite */
3291	ReHiliteText(xw, &old_start, &old_end);
3292	ReHiliteText(xw, &first, &last);
3293    } else {
3294	if (from < screen->startHCoord) {
3295	    /* Extend left end */
3296	    ReHiliteText(xw, &first, &old_start);
3297	} else if (from > screen->startHCoord) {
3298	    /* Shorten left end */
3299	    ReHiliteText(xw, &old_start, &first);
3300	}
3301	if (to > screen->endHCoord) {
3302	    /* Extend right end */
3303	    ReHiliteText(xw, &old_end, &last);
3304	} else if (to < screen->endHCoord) {
3305	    /* Shorten right end */
3306	    ReHiliteText(xw, &last, &old_end);
3307	}
3308    }
3309    screen->startHCoord = from;
3310    screen->endHCoord = to;
3311}
3312
3313/* Guaranteed that (first->row, first->col) <= (last->row, last->col) */
3314static void
3315ReHiliteText(XtermWidget xw,
3316	     CELL * firstp,
3317	     CELL * lastp)
3318{
3319    TScreen *screen = TScreenOf(xw);
3320    int i;
3321    CELL first = *firstp;
3322    CELL last = *lastp;
3323
3324    TRACE(("ReHiliteText from %d.%d to %d.%d\n",
3325	   first.row, first.col, last.row, last.col));
3326
3327    if (first.row < 0)
3328	first.row = first.col = 0;
3329    else if (first.row > screen->max_row)
3330	return;			/* nothing to do, since last.row >= first.row */
3331
3332    if (last.row < 0)
3333	return;			/* nothing to do, since first.row <= last.row */
3334    else if (last.row > screen->max_row) {
3335	last.row = screen->max_row;
3336	last.col = MaxCols(screen);
3337    }
3338    if (isSameCELL(&first, &last))
3339	return;
3340
3341    if (!isSameRow(&first, &last)) {	/* do multiple rows */
3342	if ((i = screen->max_col - first.col + 1) > 0) {	/* first row */
3343	    ScrnRefresh(xw, first.row, first.col, 1, i, True);
3344	}
3345	if ((i = last.row - first.row - 1) > 0) {	/* middle rows */
3346	    ScrnRefresh(xw, first.row + 1, 0, i, MaxCols(screen), True);
3347	}
3348	if (last.col > 0 && last.row <= screen->max_row) {	/* last row */
3349	    ScrnRefresh(xw, last.row, 0, 1, last.col, True);
3350	}
3351    } else {			/* do single row */
3352	ScrnRefresh(xw, first.row, first.col, 1, last.col - first.col, True);
3353    }
3354}
3355
3356/*
3357 * Guaranteed that (cellc->row, cellc->col) <= (cell->row, cell->col), and that both points are valid
3358 * (may have cell->row = screen->max_row+1, cell->col = 0).
3359 */
3360static void
3361SaltTextAway(XtermWidget xw,
3362	     CELL * cellc,
3363	     CELL * cell,
3364	     String * params,	/* selections */
3365	     Cardinal num_params)
3366{
3367    TScreen *screen = TScreenOf(xw);
3368    int i, j = 0;
3369    int eol;
3370    int tmp;
3371    Char *line;
3372    Char *lp;
3373    CELL first = *cellc;
3374    CELL last = *cell;
3375
3376    if (isSameRow(&first, &last) && first.col > last.col) {
3377	EXCHANGE(first.col, last.col, tmp);
3378    }
3379
3380    --last.col;
3381    /* first we need to know how long the string is before we can save it */
3382
3383    if (isSameRow(&last, &first)) {
3384	j = Length(screen, first.row, first.col, last.col);
3385    } else {			/* two cases, cut is on same line, cut spans multiple lines */
3386	j += Length(screen, first.row, first.col, screen->max_col) + 1;
3387	for (i = first.row + 1; i < last.row; i++)
3388	    j += Length(screen, i, 0, screen->max_col) + 1;
3389	if (last.col >= 0)
3390	    j += Length(screen, last.row, 0, last.col);
3391    }
3392
3393    /* UTF-8 may require more space */
3394    if_OPT_WIDE_CHARS(screen, {
3395	j *= 4;
3396    });
3397
3398    /* now get some memory to save it in */
3399
3400    if (screen->selection_size <= j) {
3401	if ((line = (Char *) malloc((size_t) j + 1)) == 0)
3402	    SysError(ERROR_BMALLOC2);
3403	XtFree((char *) screen->selection_data);
3404	screen->selection_data = line;
3405	screen->selection_size = j + 1;
3406    } else {
3407	line = screen->selection_data;
3408    }
3409
3410    if ((line == 0)
3411	|| (j < 0))
3412	return;
3413
3414    line[j] = '\0';		/* make sure it is null terminated */
3415    lp = line;			/* lp points to where to save the text */
3416    if (isSameRow(&last, &first)) {
3417	lp = SaveText(screen, last.row, first.col, last.col, lp, &eol);
3418    } else {
3419	lp = SaveText(screen, first.row, first.col, screen->max_col, lp, &eol);
3420	if (eol)
3421	    *lp++ = '\n';	/* put in newline at end of line */
3422	for (i = first.row + 1; i < last.row; i++) {
3423	    lp = SaveText(screen, i, 0, screen->max_col, lp, &eol);
3424	    if (eol)
3425		*lp++ = '\n';
3426	}
3427	if (last.col >= 0)
3428	    lp = SaveText(screen, last.row, 0, last.col, lp, &eol);
3429    }
3430    *lp = '\0';			/* make sure we have end marked */
3431
3432    TRACE(("Salted TEXT:%d:%s\n", (int) (lp - line),
3433	   visibleChars(line, (unsigned) (lp - line))));
3434
3435    screen->selection_length = (unsigned long) (lp - line);
3436    _OwnSelection(xw, params, num_params);
3437}
3438
3439#if OPT_PASTE64
3440void
3441ClearSelectionBuffer(TScreen * screen)
3442{
3443    screen->selection_length = 0;
3444    screen->base64_count = 0;
3445}
3446
3447static void
3448AppendStrToSelectionBuffer(TScreen * screen, Char * text, size_t len)
3449{
3450    if (len != 0) {
3451	int j = (int) (screen->selection_length + len);		/* New length */
3452	int k = j + (j >> 2) + 80;	/* New size if we grow buffer: grow by ~50% */
3453	if (j + 1 >= screen->selection_size) {
3454	    if (!screen->selection_length) {
3455		/* New buffer */
3456		Char *line;
3457		if ((line = (Char *) malloc((size_t) k)) == 0)
3458		    SysError(ERROR_BMALLOC2);
3459		XtFree((char *) screen->selection_data);
3460		screen->selection_data = line;
3461	    } else {
3462		/* Realloc buffer */
3463		screen->selection_data = (Char *)
3464		    realloc(screen->selection_data,
3465			    (size_t) k);
3466		if (screen->selection_data == 0)
3467		    SysError(ERROR_BMALLOC2);
3468	    }
3469	    screen->selection_size = k;
3470	}
3471	if (screen->selection_data != 0) {
3472	    memcpy(screen->selection_data + screen->selection_length, text, len);
3473	    screen->selection_length += len;
3474	    screen->selection_data[screen->selection_length] = 0;
3475	}
3476    }
3477}
3478
3479void
3480AppendToSelectionBuffer(TScreen * screen, unsigned c)
3481{
3482    unsigned six;
3483    Char ch;
3484
3485    /* Decode base64 character */
3486    if (c >= 'A' && c <= 'Z')
3487	six = c - 'A';
3488    else if (c >= 'a' && c <= 'z')
3489	six = c - 'a' + 26;
3490    else if (c >= '0' && c <= '9')
3491	six = c - '0' + 52;
3492    else if (c == '+')
3493	six = 62;
3494    else if (c == '/')
3495	six = 63;
3496    else
3497	return;
3498
3499    /* Accumulate bytes */
3500    switch (screen->base64_count) {
3501    case 0:
3502	screen->base64_accu = six;
3503	screen->base64_count = 6;
3504	break;
3505
3506    case 2:
3507	ch = CharOf((screen->base64_accu << 6) + six);
3508	screen->base64_count = 0;
3509	AppendStrToSelectionBuffer(screen, &ch, (size_t) 1);
3510	break;
3511
3512    case 4:
3513	ch = CharOf((screen->base64_accu << 4) + (six >> 2));
3514	screen->base64_accu = (six & 0x3);
3515	screen->base64_count = 2;
3516	AppendStrToSelectionBuffer(screen, &ch, (size_t) 1);
3517	break;
3518
3519    case 6:
3520	ch = CharOf((screen->base64_accu << 2) + (six >> 4));
3521	screen->base64_accu = (six & 0xF);
3522	screen->base64_count = 4;
3523	AppendStrToSelectionBuffer(screen, &ch, (size_t) 1);
3524	break;
3525    }
3526}
3527
3528void
3529CompleteSelection(XtermWidget xw, String * args, Cardinal len)
3530{
3531    TScreen *screen = TScreenOf(xw);
3532
3533    screen->base64_count = 0;
3534    screen->base64_accu = 0;
3535    _OwnSelection(xw, args, len);
3536}
3537#endif /* OPT_PASTE64 */
3538
3539static Bool
3540_ConvertSelectionHelper(Widget w,
3541			Atom * type,
3542			XtPointer *value,
3543			unsigned long *length,
3544			int *format,
3545			int (*conversion_function) (Display *,
3546						    char **, int,
3547						    XICCEncodingStyle,
3548						    XTextProperty *),
3549			XICCEncodingStyle conversion_style)
3550{
3551    XtermWidget xw;
3552
3553    if ((xw = getXtermWidget(w)) != 0) {
3554	TScreen *screen = TScreenOf(xw);
3555	Display *dpy = XtDisplay(w);
3556	XTextProperty textprop;
3557	char *the_data = (char *) screen->selection_data;
3558
3559	if (conversion_function(dpy, &the_data, 1,
3560				conversion_style,
3561				&textprop) >= Success) {
3562	    *value = (XtPointer) textprop.value;
3563	    *length = textprop.nitems;
3564	    *type = textprop.encoding;
3565	    *format = textprop.format;
3566	    return True;
3567	}
3568    }
3569    return False;
3570}
3571
3572static Boolean
3573SaveConvertedLength(XtPointer *target, unsigned long source)
3574{
3575    Boolean result = False;
3576
3577    *target = XtMalloc(4);
3578    if (*target != 0) {
3579	result = True;
3580	if (sizeof(unsigned long) == 4) {
3581	    *(unsigned long *) *target = source;
3582	} else if (sizeof(unsigned) == 4) {
3583	    *(unsigned *) *target = (unsigned) source;
3584	} else if (sizeof(unsigned short) == 4) {
3585	    *(unsigned short *) *target = (unsigned short) source;
3586	} else {
3587	    /* FIXME - does this depend on byte-order? */
3588	    unsigned long temp = source;
3589	    memcpy((char *) *target,
3590		   ((char *) &temp) + sizeof(temp) - 4,
3591		   (size_t) 4);
3592	}
3593    }
3594    return result;
3595}
3596
3597static Boolean
3598ConvertSelection(Widget w,
3599		 Atom * selection,
3600		 Atom * target,
3601		 Atom * type,
3602		 XtPointer *value,
3603		 unsigned long *length,
3604		 int *format)
3605{
3606    Display *dpy = XtDisplay(w);
3607    TScreen *screen;
3608    Bool result = False;
3609
3610    XtermWidget xw;
3611
3612    if ((xw = getXtermWidget(w)) == 0)
3613	return False;
3614
3615    screen = TScreenOf(xw);
3616
3617    if (screen->selection_data == NULL)
3618	return False;		/* can this happen? */
3619
3620    TRACE(("ConvertSelection %s\n",
3621	   visibleSelectionTarget(dpy, *target)));
3622
3623    if (*target == XA_TARGETS(dpy)) {
3624	Atom *allocP;
3625	Atom *targetP;
3626	Atom *std_targets;
3627	XPointer std_return = 0;
3628	unsigned long std_length;
3629
3630	if (XmuConvertStandardSelection(w, screen->selection_time, selection,
3631					target, type, &std_return,
3632					&std_length, format)) {
3633	    Atom *my_targets = _SelectionTargets(w);
3634
3635	    TRACE(("XmuConvertStandardSelection - success\n"));
3636	    std_targets = (Atom *) (std_return);
3637	    *length = std_length + 6;
3638
3639	    targetP = (Atom *) XtMalloc((Cardinal) (sizeof(Atom) * (*length)));
3640	    allocP = targetP;
3641
3642	    *value = (XtPointer) targetP;
3643
3644	    while (*my_targets != None) {
3645		*targetP++ = *my_targets++;
3646	    }
3647	    *targetP++ = XA_LENGTH(dpy);
3648	    *targetP++ = XA_LIST_LENGTH(dpy);
3649
3650	    *length = std_length + (unsigned long) (targetP - allocP);
3651
3652	    memcpy(targetP, std_targets, sizeof(Atom) * std_length);
3653	    XtFree((char *) std_targets);
3654	    *type = XA_ATOM;
3655	    *format = 32;
3656	    result = True;
3657	} else {
3658	    TRACE(("XmuConvertStandardSelection - failed\n"));
3659	}
3660    }
3661#if OPT_WIDE_CHARS
3662    else if (screen->wide_chars && *target == XA_STRING) {
3663	result =
3664	    _ConvertSelectionHelper(w,
3665				    type, value, length, format,
3666				    Xutf8TextListToTextProperty,
3667				    XStringStyle);
3668	TRACE(("...Xutf8TextListToTextProperty:%d\n", result));
3669    } else if (screen->wide_chars && *target == XA_UTF8_STRING(dpy)) {
3670	result =
3671	    _ConvertSelectionHelper(w,
3672				    type, value, length, format,
3673				    Xutf8TextListToTextProperty,
3674				    XUTF8StringStyle);
3675	TRACE(("...Xutf8TextListToTextProperty:%d\n", result));
3676    } else if (screen->wide_chars && *target == XA_TEXT(dpy)) {
3677	result =
3678	    _ConvertSelectionHelper(w,
3679				    type, value, length, format,
3680				    Xutf8TextListToTextProperty,
3681				    XStdICCTextStyle);
3682	TRACE(("...Xutf8TextListToTextProperty:%d\n", result));
3683    } else if (screen->wide_chars && *target == XA_COMPOUND_TEXT(dpy)) {
3684	result =
3685	    _ConvertSelectionHelper(w,
3686				    type, value, length, format,
3687				    Xutf8TextListToTextProperty,
3688				    XCompoundTextStyle);
3689	TRACE(("...Xutf8TextListToTextProperty:%d\n", result));
3690    }
3691#endif
3692
3693    else if (*target == XA_STRING) {	/* not wide_chars */
3694	/* We can only reach this point if the selection requestor
3695	   requested STRING before any of TEXT, COMPOUND_TEXT or
3696	   UTF8_STRING.  We therefore assume that the requestor is not
3697	   properly internationalised, and dump raw eight-bit data
3698	   with no conversion into the selection.  Yes, this breaks
3699	   the ICCCM in non-Latin-1 locales. */
3700	*type = XA_STRING;
3701	*value = (XtPointer) screen->selection_data;
3702	*length = screen->selection_length;
3703	*format = 8;
3704	result = True;
3705	TRACE(("...raw 8-bit data:%d\n", result));
3706    } else if (*target == XA_TEXT(dpy)) {	/* not wide_chars */
3707	result =
3708	    _ConvertSelectionHelper(w,
3709				    type, value, length, format,
3710				    XmbTextListToTextProperty,
3711				    XStdICCTextStyle);
3712	TRACE(("...XmbTextListToTextProperty(StdICC):%d\n", result));
3713    } else if (*target == XA_COMPOUND_TEXT(dpy)) {	/* not wide_chars */
3714	result =
3715	    _ConvertSelectionHelper(w,
3716				    type, value, length, format,
3717				    XmbTextListToTextProperty,
3718				    XCompoundTextStyle);
3719	TRACE(("...XmbTextListToTextProperty(Compound):%d\n", result));
3720    }
3721#ifdef X_HAVE_UTF8_STRING
3722    else if (*target == XA_UTF8_STRING(dpy)) {	/* not wide_chars */
3723	result =
3724	    _ConvertSelectionHelper(w,
3725				    type, value, length, format,
3726				    XmbTextListToTextProperty,
3727				    XUTF8StringStyle);
3728	TRACE(("...XmbTextListToTextProperty(UTF8):%d\n", result));
3729    }
3730#endif
3731    else if (*target == XA_LIST_LENGTH(dpy)) {
3732	result = SaveConvertedLength(value, (unsigned long) 1);
3733	*type = XA_INTEGER;
3734	*length = 1;
3735	*format = 32;
3736	TRACE(("...list of values:%d\n", result));
3737    } else if (*target == XA_LENGTH(dpy)) {
3738	/* This value is wrong if we have UTF-8 text */
3739	result = SaveConvertedLength(value, screen->selection_length);
3740	*type = XA_INTEGER;
3741	*length = 1;
3742	*format = 32;
3743	TRACE(("...list of values:%d\n", result));
3744    } else if (XmuConvertStandardSelection(w,
3745					   screen->selection_time, selection,
3746					   target, type, (XPointer *) value,
3747					   length, format)) {
3748	result = True;
3749	TRACE(("...XmuConvertStandardSelection:%d\n", result));
3750    }
3751
3752    /* else */
3753    return (Boolean) result;
3754}
3755
3756static void
3757LoseSelection(Widget w, Atom * selection)
3758{
3759    TScreen *screen;
3760    Atom *atomP;
3761    Cardinal i;
3762
3763    XtermWidget xw;
3764
3765    if ((xw = getXtermWidget(w)) == 0)
3766	return;
3767
3768    screen = TScreenOf(xw);
3769    for (i = 0, atomP = screen->selection_atoms;
3770	 i < screen->selection_count; i++, atomP++) {
3771	if (*selection == *atomP)
3772	    *atomP = (Atom) 0;
3773	if (CutBuffer(*atomP) >= 0) {
3774	    *atomP = (Atom) 0;
3775	}
3776    }
3777
3778    for (i = screen->selection_count; i; i--) {
3779	if (screen->selection_atoms[i - 1] != 0)
3780	    break;
3781    }
3782    screen->selection_count = i;
3783
3784    for (i = 0, atomP = screen->selection_atoms;
3785	 i < screen->selection_count; i++, atomP++) {
3786	if (*atomP == (Atom) 0) {
3787	    *atomP = screen->selection_atoms[--screen->selection_count];
3788	}
3789    }
3790
3791    if (screen->selection_count == 0)
3792	TrackText(xw, &zeroCELL, &zeroCELL);
3793}
3794
3795/* ARGSUSED */
3796static void
3797SelectionDone(Widget w GCC_UNUSED,
3798	      Atom * selection GCC_UNUSED,
3799	      Atom * target GCC_UNUSED)
3800{
3801    /* empty proc so Intrinsics know we want to keep storage */
3802}
3803
3804static void
3805_OwnSelection(XtermWidget xw,
3806	      String * selections,
3807	      Cardinal count)
3808{
3809    TScreen *screen = TScreenOf(xw);
3810    Atom *atoms = screen->selection_atoms;
3811    Cardinal i;
3812    Bool have_selection = False;
3813
3814    if (count == 0)
3815	return;
3816    if (screen->selection_length == 0)
3817	return;
3818
3819    TRACE(("_OwnSelection count %d\n", count));
3820    selections = MapSelections(xw, selections, count);
3821
3822    if (count > screen->sel_atoms_size) {
3823	XtFree((char *) atoms);
3824	atoms = (Atom *) XtMalloc((Cardinal) (count * sizeof(Atom)));
3825	screen->selection_atoms = atoms;
3826	screen->sel_atoms_size = count;
3827    }
3828    XmuInternStrings(XtDisplay((Widget) xw), selections, count, atoms);
3829    for (i = 0; i < count; i++) {
3830	int cutbuffer = CutBuffer(atoms[i]);
3831	if (cutbuffer >= 0) {
3832	    unsigned long limit =
3833	    (unsigned long) (4 * XMaxRequestSize(XtDisplay((Widget) xw)) - 32);
3834	    if (screen->selection_length > limit) {
3835		TRACE(("selection too big (%lu bytes), not storing in CUT_BUFFER%d\n",
3836		       screen->selection_length, cutbuffer));
3837		fprintf(stderr,
3838			"%s: selection too big (%lu bytes), not storing in CUT_BUFFER%d\n",
3839			xterm_name, screen->selection_length, cutbuffer);
3840	    } else {
3841		/* This used to just use the UTF-8 data, which was totally
3842		 * broken as not even the corresponding paste code in Xterm
3843		 * understood this!  So now it converts to Latin1 first.
3844		 *   Robert Brady, 2000-09-05
3845		 */
3846		unsigned long length = screen->selection_length;
3847		Char *data = screen->selection_data;
3848		if_OPT_WIDE_CHARS((screen), {
3849		    data = UTF8toLatin1(screen, data, length, &length);
3850		});
3851		TRACE(("XStoreBuffer(%d)\n", cutbuffer));
3852		XStoreBuffer(XtDisplay((Widget) xw),
3853			     (char *) data,
3854			     (int) length,
3855			     cutbuffer);
3856	    }
3857	} else if (!screen->replyToEmacs) {
3858	    have_selection |=
3859		XtOwnSelection((Widget) xw, atoms[i],
3860			       screen->selection_time,
3861			       ConvertSelection, LoseSelection, SelectionDone);
3862	}
3863    }
3864    if (!screen->replyToEmacs)
3865	screen->selection_count = count;
3866    if (!have_selection)
3867	TrackText(xw, &zeroCELL, &zeroCELL);
3868}
3869
3870static void
3871ResetSelectionState(TScreen * screen)
3872{
3873    screen->selection_count = 0;
3874    screen->startH = zeroCELL;
3875    screen->endH = zeroCELL;
3876}
3877
3878void
3879DisownSelection(XtermWidget xw)
3880{
3881    TScreen *screen = TScreenOf(xw);
3882    Atom *atoms = screen->selection_atoms;
3883    Cardinal count = screen->selection_count;
3884    Cardinal i;
3885
3886    TRACE(("DisownSelection count %d, start %d.%d, end %d.%d\n",
3887	   count,
3888	   screen->startH.row,
3889	   screen->startH.col,
3890	   screen->endH.row,
3891	   screen->endH.col));
3892
3893    for (i = 0; i < count; i++) {
3894	int cutbuffer = CutBuffer(atoms[i]);
3895	if (cutbuffer < 0) {
3896	    XtDisownSelection((Widget) xw, atoms[i],
3897			      screen->selection_time);
3898	}
3899    }
3900    /*
3901     * If none of the callbacks via XtDisownSelection() reset highlighting
3902     * do it now.
3903     */
3904    if (ScrnHaveSelection(screen)) {
3905	/* save data which will be reset */
3906	CELL first = screen->startH;
3907	CELL last = screen->endH;
3908
3909	ResetSelectionState(screen);
3910	ReHiliteText(xw, &first, &last);
3911    } else {
3912	ResetSelectionState(screen);
3913    }
3914}
3915
3916void
3917UnhiliteSelection(XtermWidget xw)
3918{
3919    TScreen *screen = TScreenOf(xw);
3920
3921    if (ScrnHaveSelection(screen)) {
3922	CELL first = screen->startH;
3923	CELL last = screen->endH;
3924
3925	screen->startH = zeroCELL;
3926	screen->endH = zeroCELL;
3927	ReHiliteText(xw, &first, &last);
3928    }
3929}
3930
3931/* returns number of chars in line from scol to ecol out */
3932/* ARGSUSED */
3933static int
3934Length(TScreen * screen,
3935       int row,
3936       int scol,
3937       int ecol)
3938{
3939    LineData *ld = GET_LINEDATA(screen, row);
3940    int lastcol = LastTextCol(screen, ld, row);
3941
3942    if (ecol > lastcol)
3943	ecol = lastcol;
3944    return (ecol - scol + 1);
3945}
3946
3947/* copies text into line, preallocated */
3948static Char *
3949SaveText(TScreen * screen,
3950	 int row,
3951	 int scol,
3952	 int ecol,
3953	 Char * lp,		/* pointer to where to put the text */
3954	 int *eol)
3955{
3956    LineData *ld;
3957    int i = 0;
3958    unsigned c;
3959    Char *result = lp;
3960#if OPT_WIDE_CHARS
3961    unsigned previous = 0;
3962#endif
3963
3964    ld = GET_LINEDATA(screen, row);
3965    i = Length(screen, row, scol, ecol);
3966    ecol = scol + i;
3967#if OPT_DEC_CHRSET
3968    if (CSET_DOUBLE(GetLineDblCS(ld))) {
3969	scol = (scol + 0) / 2;
3970	ecol = (ecol + 1) / 2;
3971    }
3972#endif
3973    *eol = !LineTstWrapped(ld);
3974    for (i = scol; i < ecol; i++) {
3975	assert(i < ld->lineSize);
3976	c = E2A(ld->charData[i]);
3977#if OPT_WIDE_CHARS
3978	/* We want to strip out every occurrence of HIDDEN_CHAR AFTER a
3979	 * wide character.
3980	 */
3981	if (c == HIDDEN_CHAR && isWide((int) previous)) {
3982	    previous = c;
3983	    /* Combining characters attached to double-width characters
3984	       are in memory attached to the HIDDEN_CHAR */
3985	    if_OPT_WIDE_CHARS(screen, {
3986		if (screen->utf8_mode != uFalse) {
3987		    unsigned ch;
3988		    size_t off;
3989		    for_each_combData(off, ld) {
3990			ch = ld->combData[off][i];
3991			if (ch == 0)
3992			    break;
3993			lp = convertToUTF8(lp, ch);
3994		    }
3995		}
3996	    });
3997	    continue;
3998	}
3999	previous = c;
4000	if (screen->utf8_mode != uFalse) {
4001	    lp = convertToUTF8(lp, (c != 0) ? c : ' ');
4002	    if_OPT_WIDE_CHARS(screen, {
4003		unsigned ch;
4004		size_t off;
4005		for_each_combData(off, ld) {
4006		    ch = ld->combData[off][i];
4007		    if (ch == 0)
4008			break;
4009		    lp = convertToUTF8(lp, ch);
4010		}
4011	    });
4012	} else
4013#endif
4014	{
4015	    if (c == 0) {
4016		c = E2A(' ');
4017	    } else if (c < E2A(' ')) {
4018		c = DECtoASCII(c);
4019	    } else if (c == 0x7f) {
4020		c = 0x5f;
4021	    }
4022	    *lp++ = CharOf(A2E(c));
4023	}
4024	if (c != E2A(' '))
4025	    result = lp;
4026    }
4027
4028    /*
4029     * If requested, trim trailing blanks from selected lines.  Do not do this
4030     * if the line is wrapped.
4031     */
4032    if (!*eol || !screen->trim_selection)
4033	result = lp;
4034
4035    return (result);
4036}
4037
4038/* 32 + following 7-bit word:
4039
4040   1:0  Button no: 0, 1, 2.  3=release.
4041     2  shift
4042     3  meta
4043     4  ctrl
4044     5  set for motion notify
4045     6  set for wheel
4046*/
4047
4048/* Position: 32 - 255. */
4049
4050static Char
4051BtnCode(XButtonEvent * event, int button)
4052{
4053    int result = (int) (32 + (KeyState(event->state) << 2));
4054
4055    if (button < 0 || button > 5) {
4056	result += 3;
4057    } else {
4058	if (button > 3)
4059	    result += (64 - 4);
4060	if (event->type == MotionNotify)
4061	    result += 32;
4062	result += button;
4063    }
4064    return CharOf(result);
4065}
4066
4067static void
4068EditorButton(XtermWidget xw, XButtonEvent * event)
4069{
4070    TScreen *screen = TScreenOf(xw);
4071    int pty = screen->respond;
4072    int mouse_limit = screen->ext_mode_mouse ? EXT_MOUSE_LIMIT : MOUSE_LIMIT;
4073    Char line[10];
4074    int row, col;
4075    int button;
4076    unsigned count = 0;
4077    Boolean changed = True;
4078
4079    /* If button event, get button # adjusted for DEC compatibility */
4080    button = (int) (event->button - 1);
4081    if (button >= 3)
4082	button++;
4083
4084    /* Compute character position of mouse pointer */
4085    row = (event->y - screen->border) / FontHeight(screen);
4086    col = (event->x - OriginX(screen)) / FontWidth(screen);
4087
4088    /* Limit to screen dimensions */
4089    if (row < 0)
4090	row = 0;
4091    else if (row > screen->max_row)
4092	row = screen->max_row;
4093
4094    if (col < 0)
4095	col = 0;
4096    else if (col > screen->max_col)
4097	col = screen->max_col;
4098
4099    /* Limit to representable mouse dimensions */
4100    if (row > mouse_limit)
4101	row = mouse_limit;
4102    if (col > mouse_limit)
4103	col = mouse_limit;
4104
4105    /* Build key sequence starting with \E[M */
4106    if (screen->control_eight_bits) {
4107	line[count++] = ANSI_CSI;
4108    } else {
4109	line[count++] = ANSI_ESC;
4110	line[count++] = '[';
4111    }
4112#if OPT_SCO_FUNC_KEYS
4113    if (xw->keyboard.type == keyboardIsSCO) {
4114	/*
4115	 * SCO function key F1 is \E[M, which would conflict with xterm's
4116	 * normal kmous.
4117	 */
4118	line[count++] = '>';
4119    }
4120#endif
4121    line[count++] = 'M';
4122
4123    /* Add event code to key sequence */
4124    if (screen->send_mouse_pos == X10_MOUSE) {
4125	line[count++] = CharOf(' ' + button);
4126    } else {
4127	/* Button-Motion events */
4128	switch (event->type) {
4129	case ButtonPress:
4130	    line[count++] = BtnCode(event, screen->mouse_button = button);
4131	    break;
4132	case ButtonRelease:
4133	    /*
4134	     * Wheel mouse interface generates release-events for buttons
4135	     * 4 and 5, coded here as 3 and 4 respectively.  We change the
4136	     * release for buttons 1..3 to a -1.
4137	     */
4138	    if (button < 3)
4139		button = -1;
4140	    line[count++] = BtnCode(event, screen->mouse_button = button);
4141	    break;
4142	case MotionNotify:
4143	    /* BTN_EVENT_MOUSE and ANY_EVENT_MOUSE modes send motion
4144	     * events only if character cell has changed.
4145	     */
4146	    if ((row == screen->mouse_row)
4147		&& (col == screen->mouse_col)) {
4148		changed = False;
4149	    } else {
4150		line[count++] = BtnCode(event, screen->mouse_button);
4151	    }
4152	    break;
4153	default:
4154	    changed = False;
4155	    break;
4156	}
4157    }
4158
4159    if (changed) {
4160	screen->mouse_row = row;
4161	screen->mouse_col = col;
4162
4163	TRACE(("mouse at %d,%d button+mask = %#x\n", row, col, line[count - 1]));
4164
4165	/* Add pointer position to key sequence */
4166	count = EmitMousePosition(screen, line, count, col);
4167	count = EmitMousePosition(screen, line, count, row);
4168
4169	/* Transmit key sequence to process running under xterm */
4170	v_write(pty, line, count);
4171    }
4172    return;
4173}
4174
4175#if OPT_FOCUS_EVENT
4176void
4177SendFocusButton(XtermWidget xw, XFocusChangeEvent * event)
4178{
4179    TScreen *screen = TScreenOf(xw);
4180
4181    if (screen->send_focus_pos) {
4182	ANSI reply;
4183
4184	memset(&reply, 0, sizeof(reply));
4185	reply.a_type = ANSI_CSI;
4186
4187#if OPT_SCO_FUNC_KEYS
4188	if (xw->keyboard.type == keyboardIsSCO) {
4189	    reply.a_pintro = '>';
4190	}
4191#endif
4192	reply.a_final = CharOf((event->type == FocusIn) ? 'I' : 'O');
4193	unparseseq(xw, &reply);
4194    }
4195    return;
4196}
4197#endif /* OPT_FOCUS_EVENT */
4198