root/foundation-apps/mxterm-maxx/button.c

Revision 8, 97.0 KB (checked in by emasson, 3 years ago)

initial import for the community edition

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