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