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