button.c revision a5ae21e4
1a5ae21e4Smrg/* $XTermId: button.c,v 1.637 2021/08/12 00:33:06 tom Exp $ */
2d522f475Smrg
3d522f475Smrg/*
4f2e35a3aSmrg * Copyright 1999-2020,2021 by Thomas E. Dickey
5d522f475Smrg *
6d522f475Smrg *                         All Rights Reserved
7d522f475Smrg *
8d522f475Smrg * Permission is hereby granted, free of charge, to any person obtaining a
9d522f475Smrg * copy of this software and associated documentation files (the
10d522f475Smrg * "Software"), to deal in the Software without restriction, including
11d522f475Smrg * without limitation the rights to use, copy, modify, merge, publish,
12d522f475Smrg * distribute, sublicense, and/or sell copies of the Software, and to
13d522f475Smrg * permit persons to whom the Software is furnished to do so, subject to
14d522f475Smrg * the following conditions:
15d522f475Smrg *
16d522f475Smrg * The above copyright notice and this permission notice shall be included
17d522f475Smrg * in all copies or substantial portions of the Software.
18d522f475Smrg *
19d522f475Smrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
20d522f475Smrg * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
21d522f475Smrg * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
22d522f475Smrg * IN NO EVENT SHALL THE ABOVE LISTED COPYRIGHT HOLDER(S) BE LIABLE FOR ANY
23d522f475Smrg * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
24d522f475Smrg * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
25d522f475Smrg * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
26d522f475Smrg *
27d522f475Smrg * Except as contained in this notice, the name(s) of the above copyright
28d522f475Smrg * holders shall not be used in advertising or otherwise to promote the
29d522f475Smrg * sale, use or other dealings in this Software without prior written
30d522f475Smrg * authorization.
31d522f475Smrg *
32d522f475Smrg *
33d522f475Smrg * Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts.
34d522f475Smrg *
35d522f475Smrg *                         All Rights Reserved
36d522f475Smrg *
37d522f475Smrg * Permission to use, copy, modify, and distribute this software and its
38d522f475Smrg * documentation for any purpose and without fee is hereby granted,
39d522f475Smrg * provided that the above copyright notice appear in all copies and that
40d522f475Smrg * both that copyright notice and this permission notice appear in
41d522f475Smrg * supporting documentation, and that the name of Digital Equipment
42d522f475Smrg * Corporation not be used in advertising or publicity pertaining to
43d522f475Smrg * distribution of the software without specific, written prior permission.
44d522f475Smrg *
45d522f475Smrg *
46d522f475Smrg * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
47d522f475Smrg * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
48d522f475Smrg * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
49d522f475Smrg * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
50d522f475Smrg * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
51d522f475Smrg * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
52d522f475Smrg * SOFTWARE.
53d522f475Smrg */
54d522f475Smrg
55d522f475Smrg/*
56d522f475Smrgbutton.c	Handles button events in the terminal emulator.
57d522f475Smrg		does cut/paste operations, change modes via menu,
58d522f475Smrg		passes button events through to some applications.
59d522f475Smrg				J. Gettys.
60d522f475Smrg*/
61d522f475Smrg
62d522f475Smrg#include <xterm.h>
63d522f475Smrg
64d522f475Smrg#include <stdio.h>
650bd37d32Smrg#include <ctype.h>
6620d2c4d2Smrg#include <assert.h>
67d522f475Smrg
68d522f475Smrg#include <X11/Xatom.h>
69d522f475Smrg#include <X11/Xmu/Atoms.h>
70d522f475Smrg#include <X11/Xmu/StdSel.h>
71d522f475Smrg
72d522f475Smrg#include <xutf8.h>
73d522f475Smrg#include <fontutils.h>
74d522f475Smrg
75d522f475Smrg#include <data.h>
76d522f475Smrg#include <error.h>
77d522f475Smrg#include <menu.h>
78d522f475Smrg#include <charclass.h>
79d522f475Smrg#include <xstrings.h>
80d522f475Smrg
81d522f475Smrg#if OPT_SELECT_REGEX
82a5ae21e4Smrg#if defined(HAVE_PCRE2POSIX_H)
83f2e35a3aSmrg#include <pcre2posix.h>
84a5ae21e4Smrg
85a5ae21e4Smrg/* pcre2 used to provide its "POSIX" entrypoints using the same names as the
86a5ae21e4Smrg * standard ones in the C runtime, but that never worked because the linker
87a5ae21e4Smrg * would use the C runtime.  Debian patched the library to fix this symbol
88a5ae21e4Smrg * conflict, but overlooked the header file, and Debian's patch was made
89a5ae21e4Smrg * obsolete when pcre2 was changed early in 2019 to provide different names.
90a5ae21e4Smrg *
91a5ae21e4Smrg * Here is a workaround to make the older version of Debian's package work.
92a5ae21e4Smrg */
93a5ae21e4Smrg#if !defined(PCRE2regcomp) && defined(HAVE_PCRE2REGCOMP)
94a5ae21e4Smrg
95a5ae21e4Smrg#undef regcomp
96a5ae21e4Smrg#undef regexec
97a5ae21e4Smrg#undef regfree
98a5ae21e4Smrg
99a5ae21e4Smrg#ifdef __cplusplus
100a5ae21e4Smrgextern "C" {
101a5ae21e4Smrg#endif
102a5ae21e4Smrg    PCRE2POSIX_EXP_DECL int PCRE2regcomp(regex_t *, const char *, int);
103a5ae21e4Smrg    PCRE2POSIX_EXP_DECL int PCRE2regexec(const regex_t *, const char *, size_t,
104a5ae21e4Smrg					 regmatch_t *, int);
105a5ae21e4Smrg    PCRE2POSIX_EXP_DECL void PCRE2regfree(regex_t *);
106a5ae21e4Smrg#ifdef __cplusplus
107a5ae21e4Smrg}				/* extern "C" */
108a5ae21e4Smrg#endif
109a5ae21e4Smrg#define regcomp(r,s,n)          PCRE2regcomp(r,s,n)
110a5ae21e4Smrg#define regexec(r,s,n,m,x)      PCRE2regexec(r,s,n,m,x)
111a5ae21e4Smrg#define regfree(r)              PCRE2regfree(r)
112a5ae21e4Smrg#endif
113a5ae21e4Smrg/* end workaround... */
114a5ae21e4Smrg#elif defined(HAVE_PCREPOSIX_H)
115d522f475Smrg#include <pcreposix.h>
116d522f475Smrg#else /* POSIX regex.h */
117d522f475Smrg#include <sys/types.h>
118d522f475Smrg#include <regex.h>
119d522f475Smrg#endif
120a5ae21e4Smrg#endif /* OPT_SELECT_REGEX */
121f2e35a3aSmrg
122f2e35a3aSmrg#ifdef HAVE_X11_TRANSLATEI_H
123f2e35a3aSmrg#include <X11/ConvertI.h>
124f2e35a3aSmrg#include <X11/TranslateI.h>
125f2e35a3aSmrg#else
126f2e35a3aSmrgextern String _XtPrintXlations(Widget w,
127f2e35a3aSmrg			       XtTranslations xlations,
128f2e35a3aSmrg			       Widget accelWidget,
129f2e35a3aSmrg			       _XtBoolean includeRHS);
130f2e35a3aSmrg#endif
131f2e35a3aSmrg
132f2e35a3aSmrg#define PRIMARY_NAME    "PRIMARY"
133f2e35a3aSmrg#define CLIPBOARD_NAME  "CLIPBOARD"
134f2e35a3aSmrg#define SECONDARY_NAME  "SECONDARY"
135f2e35a3aSmrg
136f2e35a3aSmrg#define AtomToSelection(d,n) \
137f2e35a3aSmrg		 (((n) == XA_CLIPBOARD(d)) \
138f2e35a3aSmrg		  ? CLIPBOARD_CODE \
139f2e35a3aSmrg		  : (((n) == XA_SECONDARY) \
140f2e35a3aSmrg		     ? SECONDARY_CODE \
141f2e35a3aSmrg		     : PRIMARY_CODE))
142f2e35a3aSmrg
143f2e35a3aSmrg#define isSelectionCode(n) ((n) >= PRIMARY_CODE)
144f2e35a3aSmrg#define CutBufferToCode(n) ((n) +  MAX_SELECTION_CODES)
145f2e35a3aSmrg#define okSelectionCode(n) (isSelectionCode(n) ? (n) : PRIMARY_CODE)
146d522f475Smrg
147d522f475Smrg#if OPT_WIDE_CHARS
148d522f475Smrg#include <ctype.h>
149d522f475Smrg#include <wcwidth.h>
150d522f475Smrg#else
151d522f475Smrg#define CharacterClass(value) \
152f2e35a3aSmrg	charClass[(value) & (int)((sizeof(charClass)/sizeof(charClass[0]))-1)]
153d522f475Smrg#endif
154d522f475Smrg
155956cc18dSsnj    /*
156956cc18dSsnj     * We'll generally map rows to indices when doing selection.
157956cc18dSsnj     * Simplify that with a macro.
158956cc18dSsnj     *
159956cc18dSsnj     * Note that ROW2INX() is safe to use with auto increment/decrement for
160956cc18dSsnj     * the row expression since that is evaluated once.
161956cc18dSsnj     */
162956cc18dSsnj#define GET_LINEDATA(screen, row) \
163956cc18dSsnj	getLineData(screen, ROW2INX(screen, row))
164956cc18dSsnj
165f2e35a3aSmrg#define MaxMouseBtn  5
166492d43a5Smrg
167492d43a5Smrg#define IsBtnEvent(event) ((event)->type == ButtonPress || (event)->type == ButtonRelease)
1680bd37d32Smrg#define IsKeyEvent(event) ((event)->type == KeyPress    || (event)->type == KeyRelease)
169d522f475Smrg
170d522f475Smrg#define	Coordinate(s,c)	((c)->row * MaxCols(s) + (c)->col)
171d522f475Smrg
172d522f475Smrgstatic const CELL zeroCELL =
173d522f475Smrg{0, 0};
174d522f475Smrg
175d522f475Smrg#if OPT_DEC_LOCATOR
176894e0ac8Smrgstatic Bool SendLocatorPosition(XtermWidget xw, XButtonEvent *event);
177894e0ac8Smrgstatic void CheckLocatorPosition(XtermWidget xw, XButtonEvent *event);
178d522f475Smrg#endif /* OPT_DEC_LOCATOR */
179d522f475Smrg
180d522f475Smrg/* Multi-click handling */
181d522f475Smrg#if OPT_READLINE
182d522f475Smrgstatic Time lastButtonDownTime = 0;
183d522f475Smrgstatic int ExtendingSelection = 0;
184d522f475Smrgstatic Time lastButton3UpTime = 0;
185d522f475Smrgstatic Time lastButton3DoubleDownTime = 0;
186d522f475Smrgstatic CELL lastButton3;	/* At the release time */
187d522f475Smrg#endif /* OPT_READLINE */
188d522f475Smrg
189e0a2b6dfSmrgstatic Char *SaveText(TScreen *screen, int row, int scol, int ecol,
190e0a2b6dfSmrg		      Char *lp, int *eol);
191e0a2b6dfSmrgstatic int Length(TScreen *screen, int row, int scol, int ecol);
192f2e35a3aSmrgstatic void ComputeSelect(XtermWidget xw, CELL *startc, CELL *endc, Bool
193f2e35a3aSmrg			  extend, Bool normal);
194894e0ac8Smrgstatic void EditorButton(XtermWidget xw, XButtonEvent *event);
195894e0ac8Smrgstatic void EndExtend(XtermWidget w, XEvent *event, String *params, Cardinal
196d522f475Smrg		      num_params, Bool use_cursor_loc);
197e0a2b6dfSmrgstatic void ExtendExtend(XtermWidget xw, const CELL *cell);
198e0a2b6dfSmrgstatic void PointToCELL(TScreen *screen, int y, int x, CELL *cell);
199e0a2b6dfSmrgstatic void ReHiliteText(XtermWidget xw, CELL *first, CELL *last);
200f2e35a3aSmrgstatic void SaltTextAway(XtermWidget xw, int which, CELL *cellc, CELL *cell);
201894e0ac8Smrgstatic void SelectSet(XtermWidget xw, XEvent *event, String *params, Cardinal num_params);
202d522f475Smrgstatic void SelectionReceived PROTO_XT_SEL_CB_ARGS;
203e0a2b6dfSmrgstatic void StartSelect(XtermWidget xw, const CELL *cell);
204894e0ac8Smrgstatic void TrackDown(XtermWidget xw, XButtonEvent *event);
205e0a2b6dfSmrgstatic void TrackText(XtermWidget xw, const CELL *first, const CELL *last);
206f2e35a3aSmrgstatic void UnHiliteText(XtermWidget xw);
207e0a2b6dfSmrgstatic void _OwnSelection(XtermWidget xw, String *selections, Cardinal count);
208894e0ac8Smrgstatic void do_select_end(XtermWidget xw, XEvent *event, String *params,
209d522f475Smrg			  Cardinal *num_params, Bool use_cursor_loc);
210d522f475Smrg
211492d43a5Smrg#define MOUSE_LIMIT (255 - 32)
212d522f475Smrg
213492d43a5Smrg/* Send SET_EXT_SIZE_MOUSE to enable offsets up to EXT_MOUSE_LIMIT */
214492d43a5Smrg#define EXT_MOUSE_LIMIT (2047 - 32)
215492d43a5Smrg#define EXT_MOUSE_START (127 - 32)
216d522f475Smrg
2170bd37d32Smrgstatic int
218e0a2b6dfSmrgMouseLimit(TScreen *screen)
2190bd37d32Smrg{
2200bd37d32Smrg    int mouse_limit;
2210bd37d32Smrg
2220bd37d32Smrg    switch (screen->extend_coords) {
2230bd37d32Smrg    default:
2240bd37d32Smrg	mouse_limit = MOUSE_LIMIT;
2250bd37d32Smrg	break;
2260bd37d32Smrg    case SET_EXT_MODE_MOUSE:
2270bd37d32Smrg	mouse_limit = EXT_MOUSE_LIMIT;
2280bd37d32Smrg	break;
2290bd37d32Smrg    case SET_SGR_EXT_MODE_MOUSE:
2300bd37d32Smrg    case SET_URXVT_EXT_MODE_MOUSE:
231f2e35a3aSmrg    case SET_PIXEL_POSITION_MOUSE:
2320bd37d32Smrg	mouse_limit = -1;
2330bd37d32Smrg	break;
2340bd37d32Smrg    }
2350bd37d32Smrg    return mouse_limit;
2360bd37d32Smrg}
2370bd37d32Smrg
238492d43a5Smrgstatic unsigned
239e0a2b6dfSmrgEmitMousePosition(TScreen *screen, Char line[], unsigned count, int value)
240492d43a5Smrg{
2410bd37d32Smrg    int mouse_limit = MouseLimit(screen);
242492d43a5Smrg
243492d43a5Smrg    /*
244492d43a5Smrg     * Add pointer position to key sequence
245492d43a5Smrg     *
246492d43a5Smrg     * In extended mode we encode large positions as two-byte UTF-8.
247492d43a5Smrg     *
248492d43a5Smrg     * NOTE: historically, it was possible to emit 256, which became
249492d43a5Smrg     * zero by truncation to 8 bits. While this was arguably a bug,
250492d43a5Smrg     * it's also somewhat useful as a past-end marker. We preserve
251492d43a5Smrg     * this behavior for both normal and extended mouse modes.
252492d43a5Smrg     */
2530bd37d32Smrg    switch (screen->extend_coords) {
2540bd37d32Smrg    default:
2550bd37d32Smrg	if (value == mouse_limit) {
2560bd37d32Smrg	    line[count++] = CharOf(0);
2570bd37d32Smrg	} else {
2580bd37d32Smrg	    line[count++] = CharOf(' ' + value + 1);
2590bd37d32Smrg	}
2600bd37d32Smrg	break;
2610bd37d32Smrg    case SET_EXT_MODE_MOUSE:
2620bd37d32Smrg	if (value == mouse_limit) {
2630bd37d32Smrg	    line[count++] = CharOf(0);
2640bd37d32Smrg	} else if (value < EXT_MOUSE_START) {
2650bd37d32Smrg	    line[count++] = CharOf(' ' + value + 1);
2660bd37d32Smrg	} else {
2670bd37d32Smrg	    value += ' ' + 1;
2680bd37d32Smrg	    line[count++] = CharOf(0xC0 + (value >> 6));
2690bd37d32Smrg	    line[count++] = CharOf(0x80 + (value & 0x3F));
2700bd37d32Smrg	}
2710bd37d32Smrg	break;
2720bd37d32Smrg    case SET_SGR_EXT_MODE_MOUSE:
2730bd37d32Smrg    case SET_URXVT_EXT_MODE_MOUSE:
274f2e35a3aSmrg    case SET_PIXEL_POSITION_MOUSE:
2750bd37d32Smrg	count += (unsigned) sprintf((char *) line + count, "%d", value + 1);
2760bd37d32Smrg	break;
2770bd37d32Smrg    }
2780bd37d32Smrg    return count;
2790bd37d32Smrg}
2800bd37d32Smrg
2810bd37d32Smrgstatic unsigned
282e0a2b6dfSmrgEmitMousePositionSeparator(TScreen *screen, Char line[], unsigned count)
2830bd37d32Smrg{
2840bd37d32Smrg    switch (screen->extend_coords) {
2850bd37d32Smrg    case SET_SGR_EXT_MODE_MOUSE:
2860bd37d32Smrg    case SET_URXVT_EXT_MODE_MOUSE:
287f2e35a3aSmrg    case SET_PIXEL_POSITION_MOUSE:
2880bd37d32Smrg	line[count++] = ';';
2890bd37d32Smrg	break;
290d522f475Smrg    }
291492d43a5Smrg    return count;
292492d43a5Smrg}
293d522f475Smrg
294f2e35a3aSmrgenum {
295f2e35a3aSmrg    scanMods,
296f2e35a3aSmrg    scanKey,
297f2e35a3aSmrg    scanColon,
298f2e35a3aSmrg    scanFunc,
299f2e35a3aSmrg    scanArgs
300f2e35a3aSmrg};
301f2e35a3aSmrg
302f2e35a3aSmrg#if OPT_TRACE > 1
303f2e35a3aSmrgstatic const char *
304f2e35a3aSmrgvisibleScan(int mode)
305f2e35a3aSmrg{
306f2e35a3aSmrg    const char *result = "?";
307f2e35a3aSmrg#define DATA(name) case name: result = #name; break
308f2e35a3aSmrg    switch (mode) {
309f2e35a3aSmrg	DATA(scanMods);
310f2e35a3aSmrg	DATA(scanKey);
311f2e35a3aSmrg	DATA(scanColon);
312f2e35a3aSmrg	DATA(scanFunc);
313f2e35a3aSmrg	DATA(scanArgs);
314f2e35a3aSmrg    }
315f2e35a3aSmrg#undef DATA
316f2e35a3aSmrg    return result;
317f2e35a3aSmrg}
318f2e35a3aSmrg#endif
319f2e35a3aSmrg
320f2e35a3aSmrg#define L_BRACK '<'
321f2e35a3aSmrg#define R_BRACK '>'
322f2e35a3aSmrg#define L_PAREN '('
323f2e35a3aSmrg#define R_PAREN ')'
324f2e35a3aSmrg
325f2e35a3aSmrgstatic char *
326f2e35a3aSmrgscanTrans(char *source, int *this_is, int *next_is, unsigned *first, unsigned *last)
327f2e35a3aSmrg{
328f2e35a3aSmrg    char ch;
329f2e35a3aSmrg    char *target = source;
330f2e35a3aSmrg
331f2e35a3aSmrg    *first = *last = 0;
332f2e35a3aSmrg    if (IsEmpty(target)) {
333f2e35a3aSmrg	target = 0;
334f2e35a3aSmrg    } else {
335f2e35a3aSmrg	do {
336f2e35a3aSmrg	    while (IsSpace(*target))
337f2e35a3aSmrg		target++;
338f2e35a3aSmrg	    *first = (unsigned) (target - source);
339f2e35a3aSmrg	    switch (*this_is = *next_is) {
340f2e35a3aSmrg	    case scanMods:
341f2e35a3aSmrg		while ((ch = *target)) {
342f2e35a3aSmrg		    if (IsSpace(ch)) {
343f2e35a3aSmrg			break;
344f2e35a3aSmrg		    } else if (ch == L_BRACK) {
345f2e35a3aSmrg			*next_is = scanKey;
346f2e35a3aSmrg			break;
347f2e35a3aSmrg		    } else if (ch == ':') {
348f2e35a3aSmrg			*next_is = scanColon;
349f2e35a3aSmrg			break;
350f2e35a3aSmrg		    } else if (ch == '~' && target != source) {
351f2e35a3aSmrg			break;
352f2e35a3aSmrg		    }
353f2e35a3aSmrg		    target++;
354f2e35a3aSmrg		}
355f2e35a3aSmrg		break;
356f2e35a3aSmrg	    case scanKey:
357f2e35a3aSmrg		while ((ch = *target)) {
358f2e35a3aSmrg		    if (IsSpace(ch)) {
359f2e35a3aSmrg			break;
360f2e35a3aSmrg		    } else if (ch == ':') {
361f2e35a3aSmrg			*next_is = scanColon;
362f2e35a3aSmrg			break;
363f2e35a3aSmrg		    }
364f2e35a3aSmrg		    target++;
365f2e35a3aSmrg		    if (ch == R_BRACK)
366f2e35a3aSmrg			break;
367f2e35a3aSmrg		}
368f2e35a3aSmrg		break;
369f2e35a3aSmrg	    case scanColon:
370f2e35a3aSmrg		*next_is = scanFunc;
371f2e35a3aSmrg		target++;
372f2e35a3aSmrg		break;
373f2e35a3aSmrg	    case scanFunc:
374f2e35a3aSmrg		while ((ch = *target)) {
375f2e35a3aSmrg		    if (IsSpace(ch)) {
376f2e35a3aSmrg			break;
377f2e35a3aSmrg		    } else if (ch == L_PAREN) {
378f2e35a3aSmrg			*next_is = scanArgs;
379f2e35a3aSmrg			break;
380f2e35a3aSmrg		    }
381f2e35a3aSmrg		    target++;
382f2e35a3aSmrg		}
383f2e35a3aSmrg		break;
384f2e35a3aSmrg	    case scanArgs:
385f2e35a3aSmrg		while ((ch = *target)) {
386f2e35a3aSmrg		    if (ch == R_PAREN) {
387f2e35a3aSmrg			target++;
388f2e35a3aSmrg			*next_is = scanFunc;
389f2e35a3aSmrg			break;
390f2e35a3aSmrg		    }
391f2e35a3aSmrg		    target++;
392f2e35a3aSmrg		}
393f2e35a3aSmrg		break;
394f2e35a3aSmrg	    }
395f2e35a3aSmrg	    *last = (unsigned) (target - source);
396f2e35a3aSmrg	    if (*target == '\n') {
397f2e35a3aSmrg		*next_is = scanMods;
398f2e35a3aSmrg		target++;
399f2e35a3aSmrg	    }
400f2e35a3aSmrg	} while (*first == *last);
401f2e35a3aSmrg    }
402f2e35a3aSmrg    return target;
403f2e35a3aSmrg}
404f2e35a3aSmrg
405f2e35a3aSmrgvoid
406f2e35a3aSmrgxtermButtonInit(XtermWidget xw)
407f2e35a3aSmrg{
408f2e35a3aSmrg    Widget w = (Widget) xw;
409f2e35a3aSmrg    XErrorHandler save = XSetErrorHandler(ignore_x11_error);
410f2e35a3aSmrg    XtTranslations xlations;
411f2e35a3aSmrg    Widget xcelerat;
412f2e35a3aSmrg    String result;
413f2e35a3aSmrg
414f2e35a3aSmrg    XtVaGetValues(w,
415f2e35a3aSmrg		  XtNtranslations, &xlations,
416f2e35a3aSmrg		  XtNaccelerators, &xcelerat,
417f2e35a3aSmrg		  (XtPointer) 0);
418f2e35a3aSmrg    result = _XtPrintXlations(w, xlations, xcelerat, True);
419f2e35a3aSmrg    if (result) {
420f2e35a3aSmrg	static const char *table[] =
421f2e35a3aSmrg	{
422f2e35a3aSmrg	    "insert-selection",
423f2e35a3aSmrg	    "select-end",
424f2e35a3aSmrg	    "select-extend",
425f2e35a3aSmrg	    "select-start",
426f2e35a3aSmrg	    "start-extend",
427f2e35a3aSmrg	};
428f2e35a3aSmrg	char *data = x_strdup(result);
429f2e35a3aSmrg	char *next;
430f2e35a3aSmrg	int state = scanMods;
431f2e35a3aSmrg	int state2 = scanMods;
432f2e35a3aSmrg	unsigned first;
433f2e35a3aSmrg	unsigned last;
434f2e35a3aSmrg	int have_button = -1;
435f2e35a3aSmrg	Bool want_button = False;
436f2e35a3aSmrg	Bool have_shift = False;
437f2e35a3aSmrg	unsigned allowed = 0;
438f2e35a3aSmrg	unsigned disallow = 0;
439f2e35a3aSmrg
440f2e35a3aSmrg	TRACE(("xtermButtonInit length %ld\n", (long) strlen(result)));
441f2e35a3aSmrg	xw->keyboard.print_translations = data;
442f2e35a3aSmrg	while ((next = scanTrans(data, &state, &state2, &first, &last)) != 0) {
443f2e35a3aSmrg	    unsigned len = (last - first);
444f2e35a3aSmrg	    TRACE2(("parse %s:%d..%d '%.*s'\n",
445f2e35a3aSmrg		    visibleScan(state), first, last,
446f2e35a3aSmrg		    len, data + first));
447f2e35a3aSmrg	    if (state == scanMods) {
448f2e35a3aSmrg		if (len > 1 && data[first] == '~') {
449f2e35a3aSmrg		    len--;
450f2e35a3aSmrg		    first++;
451f2e35a3aSmrg		}
452f2e35a3aSmrg		if (len == 7 && !x_strncasecmp(data + first, "button", len - 1)) {
453f2e35a3aSmrg		    have_button = data[first + 6] - '0';
454f2e35a3aSmrg		} else if (len == 5 && !x_strncasecmp(data + first, "shift", len)) {
455f2e35a3aSmrg		    have_shift = True;
456f2e35a3aSmrg		}
457f2e35a3aSmrg	    } else if (state == scanKey) {
458f2e35a3aSmrg		if (!x_strncasecmp(data + first, "<buttonpress>", len) ||
459f2e35a3aSmrg		    !x_strncasecmp(data + first, "<buttonrelease>", len)) {
460f2e35a3aSmrg		    want_button = True;
461f2e35a3aSmrg		} else if (want_button) {
462f2e35a3aSmrg		    have_button = data[first] - '0';
463f2e35a3aSmrg		    want_button = False;
464f2e35a3aSmrg		}
465f2e35a3aSmrg	    } else if (state == scanFunc && have_button > 0) {
466f2e35a3aSmrg		Cardinal n;
467f2e35a3aSmrg		unsigned bmask = 1U << (have_button - 1);
468f2e35a3aSmrg		for (n = 0; n < XtNumber(table); ++n) {
469f2e35a3aSmrg		    if (!x_strncasecmp(table[n], data + first, len)) {
470f2e35a3aSmrg			TRACE(("...button %d: %s%s\n",
471f2e35a3aSmrg			       have_button, table[n],
472f2e35a3aSmrg			       have_shift ? " (disallow)" : ""));
473f2e35a3aSmrg			if (have_shift)
474f2e35a3aSmrg			    disallow |= bmask;
475f2e35a3aSmrg			else
476f2e35a3aSmrg			    allowed |= bmask;
477f2e35a3aSmrg			break;
478f2e35a3aSmrg		    }
479f2e35a3aSmrg		}
480f2e35a3aSmrg	    }
481f2e35a3aSmrg	    if (state2 == scanMods && state >= scanColon) {
482f2e35a3aSmrg		have_button = -1;
483f2e35a3aSmrg		want_button = False;
484f2e35a3aSmrg		have_shift = False;
485f2e35a3aSmrg	    }
486f2e35a3aSmrg	    state = state2;
487f2e35a3aSmrg	    data = next;
488f2e35a3aSmrg	}
489f2e35a3aSmrg	XFree((char *) result);
490f2e35a3aSmrg	xw->keyboard.shift_buttons = allowed & ~disallow;
491f2e35a3aSmrg#if OPT_TRACE
492f2e35a3aSmrg	if (xw->keyboard.shift_buttons) {
493f2e35a3aSmrg	    int button = 0;
494f2e35a3aSmrg	    unsigned mask = xw->keyboard.shift_buttons;
495f2e35a3aSmrg	    TRACE(("...Buttons used for selection that can be overridden:"));
496f2e35a3aSmrg	    while (mask != 0) {
497f2e35a3aSmrg		++button;
498f2e35a3aSmrg		if ((mask & 1) != 0)
499f2e35a3aSmrg		    TRACE((" %d", button));
500f2e35a3aSmrg		mask >>= 1;
501f2e35a3aSmrg	    }
502f2e35a3aSmrg	    TRACE(("\n"));
503f2e35a3aSmrg	} else {
504f2e35a3aSmrg	    TRACE(("...No buttons used with selection can be overridden\n"));
505f2e35a3aSmrg	}
506f2e35a3aSmrg#endif
507f2e35a3aSmrg    }
508f2e35a3aSmrg    XSetErrorHandler(save);
509f2e35a3aSmrg}
510f2e35a3aSmrg
511f2e35a3aSmrg/*
512f2e35a3aSmrg * Shift and control are regular X11 modifiers, but meta is not:
513f2e35a3aSmrg * + X10 (which had no xmodmap utility) had a meta mask, but X11 did not.
514f2e35a3aSmrg * + X11R1 introduced xmodmap, along with the current set of modifier masks.
515f2e35a3aSmrg *   The meta key has been assumed to be mod1 since X11R1.
516f2e35a3aSmrg *   The initial xterm logic in X11 was different, but gave the same result.
517f2e35a3aSmrg * + X11R2 modified xterm was to eliminate the X10 table which provided part of
518f2e35a3aSmrg *   the meta logic.
519f2e35a3aSmrg * + X11R3 modified Xt, making Meta_L and Meta_R assignable via xmodmap, and
520f2e35a3aSmrg *   equating Alt with Meta.  Neither Alt/Meta are modifiers, but Alt is more
521f2e35a3aSmrg *   likely to be on the keyboard.  This release also added keymap tables for
522f2e35a3aSmrg *   the server; Meta was used frequently in HP keymaps, which were the most
523f2e35a3aSmrg *   extensive set of keymaps.
524f2e35a3aSmrg * + X11R4 mentions Meta in the ICCCM, stating that if Meta_L or Meta_R are
525f2e35a3aSmrg *   found in the keysyms for a given modifier, that the client should use
526f2e35a3aSmrg *   that modifier.
527f2e35a3aSmrg *
528f2e35a3aSmrg * This function follows the ICCCM, picking the modifier which contains the
529f2e35a3aSmrg * Meta_L/Meta_R keysyms (if available), falling back to the Alt_L/Alt_R
530f2e35a3aSmrg * (as per X11R3), and ultimately to mod1 (per X11R1).
531f2e35a3aSmrg */
532f2e35a3aSmrgstatic unsigned
533f2e35a3aSmrgMetaMask(XtermWidget xw)
534f2e35a3aSmrg{
535f2e35a3aSmrg#if OPT_NUM_LOCK
536f2e35a3aSmrg    unsigned meta = xw->work.meta_mods;
537f2e35a3aSmrg    if (meta == 0)
538f2e35a3aSmrg	meta = xw->work.alt_mods;
539f2e35a3aSmrg    if (meta == 0)
540f2e35a3aSmrg	meta = Mod1Mask;
541f2e35a3aSmrg#else
542f2e35a3aSmrg    unsigned meta = Mod1Mask;
543f2e35a3aSmrg    (void) xw;
544f2e35a3aSmrg#endif
545f2e35a3aSmrg    return meta;
546f2e35a3aSmrg}
547f2e35a3aSmrg
548f2e35a3aSmrg/*
549f2e35a3aSmrg * Returns a mask of the modifiers we may use for modifying the mouse protocol
550f2e35a3aSmrg * response strings.
551f2e35a3aSmrg */
552f2e35a3aSmrgstatic unsigned
553f2e35a3aSmrgOurModifiers(XtermWidget xw)
554f2e35a3aSmrg{
555f2e35a3aSmrg    return (ShiftMask
556f2e35a3aSmrg	    | ControlMask
557f2e35a3aSmrg	    | MetaMask(xw));
558f2e35a3aSmrg}
559f2e35a3aSmrg
560f2e35a3aSmrg/*
561f2e35a3aSmrg * The actual check for the shift-mask, to see if it should tell xterm to
562f2e35a3aSmrg * override mouse-protocol in favor of select/paste actions depends upon
563f2e35a3aSmrg * whether the shiftEscape resource is set to true/always vs false/never.
564f2e35a3aSmrg */
565f2e35a3aSmrgstatic Boolean
566f2e35a3aSmrgShiftOverride(XtermWidget xw, unsigned state, int button)
567f2e35a3aSmrg{
568f2e35a3aSmrg    unsigned check = (state & OurModifiers(xw));
569f2e35a3aSmrg    Boolean result = False;
570f2e35a3aSmrg
571f2e35a3aSmrg    if (check & ShiftMask) {
572f2e35a3aSmrg	if (xw->keyboard.shift_escape == ssFalse ||
573f2e35a3aSmrg	    xw->keyboard.shift_escape == ssNever) {
574f2e35a3aSmrg	    result = True;
575f2e35a3aSmrg	} else if (xw->keyboard.shift_escape == ssTrue) {
576f2e35a3aSmrg	    /*
577f2e35a3aSmrg	     * Check if the button is one that we found does not directly use
578f2e35a3aSmrg	     * the shift-modifier in its bindings to select/copy actions.
579f2e35a3aSmrg	     */
580f2e35a3aSmrg	    if (button > 0 && button <= MaxMouseBtn) {
581f2e35a3aSmrg		if (xw->keyboard.shift_buttons & (1U << (button - 1))) {
582f2e35a3aSmrg		    result = True;
583f2e35a3aSmrg		}
584f2e35a3aSmrg	    } else {
585f2e35a3aSmrg		result = True;	/* unlikely, and we don't care */
586f2e35a3aSmrg	    }
587f2e35a3aSmrg	}
588f2e35a3aSmrg    }
589f2e35a3aSmrg    TRACE2(("ShiftOverride ( %#x -> %#x ) %d\n", state, check, result));
590f2e35a3aSmrg    return result;
591f2e35a3aSmrg}
592f2e35a3aSmrg
593f2e35a3aSmrg/*
594f2e35a3aSmrg * Normally xterm treats the shift-modifier specially when the mouse protocol
595f2e35a3aSmrg * is active.  The translations resource binds otherwise unmodified button
596f2e35a3aSmrg * for these mouse-related events:
597f2e35a3aSmrg *
598f2e35a3aSmrg *         ~Meta <Btn1Down>:select-start() \n\
599f2e35a3aSmrg *       ~Meta <Btn1Motion>:select-extend() \n\
600f2e35a3aSmrg *     ~Ctrl ~Meta <Btn2Up>:insert-selection(SELECT, CUT_BUFFER0) \n\
601f2e35a3aSmrg *   ~Ctrl ~Meta <Btn3Down>:start-extend() \n\
602f2e35a3aSmrg *       ~Meta <Btn3Motion>:select-extend() \n\
603f2e35a3aSmrg *                  <BtnUp>:select-end(SELECT, CUT_BUFFER0) \n\
604f2e35a3aSmrg *
605f2e35a3aSmrg * There is no API in the X libraries which would tell us if a given mouse
606f2e35a3aSmrg * button is bound to one of these actions.  These functions make the choice
607f2e35a3aSmrg * configurable.
608f2e35a3aSmrg */
609f2e35a3aSmrgstatic Bool
610f2e35a3aSmrgInterpretButton(XtermWidget xw, XButtonEvent *event)
611f2e35a3aSmrg{
612f2e35a3aSmrg    Bool result = False;
613f2e35a3aSmrg
614f2e35a3aSmrg    if (ShiftOverride(xw, event->state, (int) event->button)) {
615f2e35a3aSmrg	TRACE(("...shift-button #%d overrides mouse-protocol\n", event->button));
616f2e35a3aSmrg	result = True;
617f2e35a3aSmrg    }
618f2e35a3aSmrg    return result;
619f2e35a3aSmrg}
620f2e35a3aSmrg
621f2e35a3aSmrg#define Button1Index 8		/* X.h should have done this */
622f2e35a3aSmrg
623f2e35a3aSmrgstatic int
624f2e35a3aSmrgMotionButton(unsigned state)
625f2e35a3aSmrg{
626f2e35a3aSmrg    unsigned bmask = state >> Button1Index;
627f2e35a3aSmrg    int result = 1;
628f2e35a3aSmrg
629f2e35a3aSmrg    if (bmask != 0) {
630f2e35a3aSmrg	while (!(bmask & 1)) {
631f2e35a3aSmrg	    ++result;
632f2e35a3aSmrg	    bmask >>= 1;
633f2e35a3aSmrg	}
634f2e35a3aSmrg    }
635f2e35a3aSmrg    return result;
636f2e35a3aSmrg}
637f2e35a3aSmrg
638f2e35a3aSmrgstatic Bool
639f2e35a3aSmrgInterpretEvent(XtermWidget xw, XEvent *event)
640f2e35a3aSmrg{
641f2e35a3aSmrg    Bool result = False;	/* if not a button, is motion */
642f2e35a3aSmrg
643f2e35a3aSmrg    if (IsBtnEvent(event)) {
644f2e35a3aSmrg	result = InterpretButton(xw, (XButtonEvent *) event);
645f2e35a3aSmrg    } else if (event->type == MotionNotify) {
646f2e35a3aSmrg	unsigned state = event->xmotion.state;
647f2e35a3aSmrg	int button = MotionButton(state);
648f2e35a3aSmrg
649f2e35a3aSmrg	if (ShiftOverride(xw, state, button)) {
650f2e35a3aSmrg	    TRACE(("...shift-motion #%d (%d,%d) overrides mouse-protocol\n",
651f2e35a3aSmrg		   button,
652f2e35a3aSmrg		   event->xmotion.y,
653f2e35a3aSmrg		   event->xmotion.x));
654f2e35a3aSmrg	    result = True;
655f2e35a3aSmrg	}
656f2e35a3aSmrg    }
657f2e35a3aSmrg    return result;
658f2e35a3aSmrg}
659f2e35a3aSmrg
660f2e35a3aSmrg#define OverrideEvent(event)  InterpretEvent(xw, event)
661f2e35a3aSmrg#define OverrideButton(event) InterpretButton(xw, event)
662f2e35a3aSmrg
663f2e35a3aSmrg/*
664f2e35a3aSmrg * Returns true if we handled the event here, and nothing more is needed.
665f2e35a3aSmrg */
666492d43a5SmrgBool
667894e0ac8SmrgSendMousePosition(XtermWidget xw, XEvent *event)
668492d43a5Smrg{
669492d43a5Smrg    XButtonEvent *my_event = (XButtonEvent *) event;
670492d43a5Smrg    Bool result = False;
671d522f475Smrg
672913cc679Smrg    switch (okSendMousePos(xw)) {
673492d43a5Smrg    case MOUSE_OFF:
674492d43a5Smrg	/* If send_mouse_pos mode isn't on, we shouldn't be here */
675492d43a5Smrg	break;
676d522f475Smrg
677d522f475Smrg    case BTN_EVENT_MOUSE:
678d522f475Smrg    case ANY_EVENT_MOUSE:
679f2e35a3aSmrg	if (!OverrideEvent(event)) {
680492d43a5Smrg	    /* xterm extension for motion reporting. June 1998 */
681492d43a5Smrg	    /* EditorButton() will distinguish between the modes */
682492d43a5Smrg	    switch (event->type) {
683492d43a5Smrg	    case MotionNotify:
684492d43a5Smrg		my_event->button = 0;
685492d43a5Smrg		/* FALLTHRU */
686492d43a5Smrg	    case ButtonPress:
687492d43a5Smrg		/* FALLTHRU */
688492d43a5Smrg	    case ButtonRelease:
689492d43a5Smrg		EditorButton(xw, my_event);
690492d43a5Smrg		result = True;
691492d43a5Smrg		break;
6922eaa94a1Schristos	    }
693d522f475Smrg	}
694492d43a5Smrg	break;
695d522f475Smrg
696913cc679Smrg    case X10_MOUSE:		/* X10 compatibility sequences */
697492d43a5Smrg	if (IsBtnEvent(event)) {
698f2e35a3aSmrg	    if (!OverrideButton(my_event)) {
699913cc679Smrg		if (my_event->type == ButtonPress)
700492d43a5Smrg		    EditorButton(xw, my_event);
701913cc679Smrg		result = True;
702913cc679Smrg	    }
703913cc679Smrg	}
704913cc679Smrg	break;
705492d43a5Smrg
706913cc679Smrg    case VT200_HIGHLIGHT_MOUSE:	/* DEC vt200 hilite tracking */
707913cc679Smrg	if (IsBtnEvent(event)) {
708f2e35a3aSmrg	    if (!OverrideButton(my_event)) {
709f2e35a3aSmrg		if (my_event->type == ButtonPress &&
710f2e35a3aSmrg		    my_event->button == Button1) {
711f2e35a3aSmrg		    TrackDown(xw, my_event);
712f2e35a3aSmrg		} else {
713f2e35a3aSmrg		    EditorButton(xw, my_event);
714f2e35a3aSmrg		}
715913cc679Smrg		result = True;
716913cc679Smrg	    }
717913cc679Smrg	}
718913cc679Smrg	break;
719492d43a5Smrg
720913cc679Smrg    case VT200_MOUSE:		/* DEC vt200 compatible */
721913cc679Smrg	if (IsBtnEvent(event)) {
722f2e35a3aSmrg	    if (!OverrideButton(my_event)) {
723913cc679Smrg		EditorButton(xw, my_event);
724913cc679Smrg		result = True;
725913cc679Smrg	    }
726913cc679Smrg	}
727913cc679Smrg	break;
728913cc679Smrg
729913cc679Smrg    case DEC_LOCATOR:
730492d43a5Smrg#if OPT_DEC_LOCATOR
731f2e35a3aSmrg	if (IsBtnEvent(event) || event->type == MotionNotify) {
732913cc679Smrg	    result = SendLocatorPosition(xw, my_event);
733492d43a5Smrg	}
734f2e35a3aSmrg#endif /* OPT_DEC_LOCATOR */
735913cc679Smrg	break;
736d522f475Smrg    }
737492d43a5Smrg    return result;
738d522f475Smrg}
739d522f475Smrg
740d522f475Smrg#if OPT_DEC_LOCATOR
741d522f475Smrg
742d522f475Smrg#define	LocatorCoords( row, col, x, y, oor )			\
743d522f475Smrg    if( screen->locator_pixels ) {				\
744d522f475Smrg	(oor)=False; (row) = (y)+1; (col) = (x)+1;		\
745d522f475Smrg	/* Limit to screen dimensions */			\
746d522f475Smrg	if ((row) < 1) (row) = 1,(oor)=True;			\
747d522f475Smrg	else if ((row) > screen->border*2+Height(screen))	\
748d522f475Smrg	    (row) = screen->border*2+Height(screen),(oor)=True;	\
749d522f475Smrg	if ((col) < 1) (col) = 1,(oor)=True;			\
750d522f475Smrg	else if ((col) > OriginX(screen)*2+Width(screen))	\
751d522f475Smrg	    (col) = OriginX(screen)*2+Width(screen),(oor)=True;	\
752d522f475Smrg    } else {							\
753d522f475Smrg	(oor)=False;						\
754d522f475Smrg	/* Compute character position of mouse pointer */	\
755d522f475Smrg	(row) = ((y) - screen->border) / FontHeight(screen);	\
756d522f475Smrg	(col) = ((x) - OriginX(screen)) / FontWidth(screen);	\
757d522f475Smrg	/* Limit to screen dimensions */			\
758d522f475Smrg	if ((row) < 0) (row) = 0,(oor)=True;			\
759d522f475Smrg	else if ((row) > screen->max_row)			\
760d522f475Smrg	    (row) = screen->max_row,(oor)=True;			\
761d522f475Smrg	if ((col) < 0) (col) = 0,(oor)=True;			\
762d522f475Smrg	else if ((col) > screen->max_col)			\
763d522f475Smrg	    (col) = screen->max_col,(oor)=True;			\
764d522f475Smrg	(row)++; (col)++;					\
765d522f475Smrg    }
766d522f475Smrg
767d522f475Smrgstatic Bool
768894e0ac8SmrgSendLocatorPosition(XtermWidget xw, XButtonEvent *event)
769d522f475Smrg{
770d522f475Smrg    ANSI reply;
771956cc18dSsnj    TScreen *screen = TScreenOf(xw);
772d522f475Smrg    int row, col;
773d522f475Smrg    Bool oor;
774d522f475Smrg    int button;
7752eaa94a1Schristos    unsigned state;
776d522f475Smrg
777d522f475Smrg    /* Make sure the event is an appropriate type */
778f2e35a3aSmrg    if (IsBtnEvent(event)) {
779f2e35a3aSmrg	if (OverrideButton(event))
780f2e35a3aSmrg	    return (False);
781f2e35a3aSmrg    } else {
782f2e35a3aSmrg	if (!screen->loc_filter)
783f2e35a3aSmrg	    return (False);
784f2e35a3aSmrg    }
785d522f475Smrg
786d522f475Smrg    if ((event->type == ButtonPress &&
787d522f475Smrg	 !(screen->locator_events & LOC_BTNS_DN)) ||
788d522f475Smrg	(event->type == ButtonRelease &&
789d522f475Smrg	 !(screen->locator_events & LOC_BTNS_UP)))
790d522f475Smrg	return (True);
791d522f475Smrg
792d522f475Smrg    if (event->type == MotionNotify) {
793d522f475Smrg	CheckLocatorPosition(xw, event);
794d522f475Smrg	return (True);
795d522f475Smrg    }
796d522f475Smrg
797d522f475Smrg    /* get button # */
798492d43a5Smrg    button = (int) event->button - 1;
799d522f475Smrg
800492d43a5Smrg    LocatorCoords(row, col, event->x, event->y, oor);
801d522f475Smrg
802d522f475Smrg    /*
803d522f475Smrg     * DECterm mouse:
804d522f475Smrg     *
805d522f475Smrg     * ESCAPE '[' event ; mask ; row ; column '&' 'w'
806d522f475Smrg     */
807d522f475Smrg    memset(&reply, 0, sizeof(reply));
808d522f475Smrg    reply.a_type = ANSI_CSI;
809d522f475Smrg
810d522f475Smrg    if (oor) {
811d522f475Smrg	reply.a_nparam = 1;
812d522f475Smrg	reply.a_param[0] = 0;	/* Event - 0 = locator unavailable */
813d522f475Smrg	reply.a_inters = '&';
814d522f475Smrg	reply.a_final = 'w';
815d522f475Smrg	unparseseq(xw, &reply);
816d522f475Smrg
817d522f475Smrg	if (screen->locator_reset) {
818d522f475Smrg	    MotionOff(screen, xw);
819d522f475Smrg	    screen->send_mouse_pos = MOUSE_OFF;
820d522f475Smrg	}
821d522f475Smrg	return (True);
822d522f475Smrg    }
823d522f475Smrg
824d522f475Smrg    /*
825d522f475Smrg     * event:
826d522f475Smrg     *        1       no buttons
827d522f475Smrg     *        2       left button down
828d522f475Smrg     *        3       left button up
829d522f475Smrg     *        4       middle button down
830d522f475Smrg     *        5       middle button up
831d522f475Smrg     *        6       right button down
832d522f475Smrg     *        7       right button up
833d522f475Smrg     *        8       M4 down
834d522f475Smrg     *        9       M4 up
835d522f475Smrg     */
836d522f475Smrg    reply.a_nparam = 4;
837d522f475Smrg    switch (event->type) {
838d522f475Smrg    case ButtonPress:
8392eaa94a1Schristos	reply.a_param[0] = (ParmType) (2 + (button << 1));
840d522f475Smrg	break;
841d522f475Smrg    case ButtonRelease:
8422eaa94a1Schristos	reply.a_param[0] = (ParmType) (3 + (button << 1));
843d522f475Smrg	break;
844d522f475Smrg    default:
845d522f475Smrg	return (True);
846d522f475Smrg    }
847d522f475Smrg    /*
848d522f475Smrg     * mask:
849913cc679Smrg     * bit7   bit6   bit5   bit4   bit3     bit2       bit1         bit0
850913cc679Smrg     *                             M4 down  left down  middle down  right down
851d522f475Smrg     *
852d522f475Smrg     * Notice that Button1 (left) and Button3 (right) are swapped in the mask.
853d522f475Smrg     * Also, mask should be the state after the button press/release,
854d522f475Smrg     * X provides the state not including the button press/release.
855d522f475Smrg     */
856492d43a5Smrg    state = (event->state
857d522f475Smrg	     & (Button1Mask | Button2Mask | Button3Mask | Button4Mask)) >> 8;
8584e40088cSchristos    /* update mask to "after" state */
85920d2c4d2Smrg    state ^= ((unsigned) (1 << button));
8604e40088cSchristos    /* swap Button1 & Button3 */
861956cc18dSsnj    state = ((state & (unsigned) ~(4 | 1))
862956cc18dSsnj	     | ((state & 1) ? 4 : 0)
863956cc18dSsnj	     | ((state & 4) ? 1 : 0));
864d522f475Smrg
8652eaa94a1Schristos    reply.a_param[1] = (ParmType) state;
8662eaa94a1Schristos    reply.a_param[2] = (ParmType) row;
8672eaa94a1Schristos    reply.a_param[3] = (ParmType) col;
868d522f475Smrg    reply.a_inters = '&';
869d522f475Smrg    reply.a_final = 'w';
870d522f475Smrg
871d522f475Smrg    unparseseq(xw, &reply);
872d522f475Smrg
873d522f475Smrg    if (screen->locator_reset) {
874d522f475Smrg	MotionOff(screen, xw);
875d522f475Smrg	screen->send_mouse_pos = MOUSE_OFF;
876d522f475Smrg    }
877d522f475Smrg
878d522f475Smrg    /*
879913cc679Smrg     * DECterm turns the Locator off if a button is pressed while a filter
880913cc679Smrg     * rectangle is active.  This might be a bug, but I don't know, so I'll
881913cc679Smrg     * emulate it anyway.
882d522f475Smrg     */
883d522f475Smrg    if (screen->loc_filter) {
884d522f475Smrg	screen->send_mouse_pos = MOUSE_OFF;
885d522f475Smrg	screen->loc_filter = False;
886d522f475Smrg	screen->locator_events = 0;
887d522f475Smrg	MotionOff(screen, xw);
888d522f475Smrg    }
889d522f475Smrg
890d522f475Smrg    return (True);
891d522f475Smrg}
892d522f475Smrg
893d522f475Smrg/*
894d522f475Smrg * mask:
895d522f475Smrg * bit 7   bit 6   bit 5   bit 4   bit 3   bit 2       bit 1         bit 0
896d522f475Smrg *                                 M4 down left down   middle down   right down
897d522f475Smrg *
898d522f475Smrg * Button1 (left) and Button3 (right) are swapped in the mask relative to X.
899d522f475Smrg */
900d522f475Smrg#define	ButtonState(state, mask)	\
901913cc679Smrg{ int stemp = (int) (((mask) & (Button1Mask | Button2Mask | Button3Mask | Button4Mask)) >> 8);	\
902d522f475Smrg  /* swap Button1 & Button3 */								\
903913cc679Smrg  (state) = (stemp & ~(4|1)) | ((stemp & 1) ? 4 : 0) | ((stemp & 4) ? 1 : 0);			\
904d522f475Smrg}
905d522f475Smrg
906d522f475Smrgvoid
907d522f475SmrgGetLocatorPosition(XtermWidget xw)
908d522f475Smrg{
909d522f475Smrg    ANSI reply;
910956cc18dSsnj    TScreen *screen = TScreenOf(xw);
911d522f475Smrg    Window root, child;
912d522f475Smrg    int rx, ry, x, y;
913f2e35a3aSmrg    unsigned int mask = 0;
914d522f475Smrg    int row = 0, col = 0;
915d522f475Smrg    Bool oor = False;
916d522f475Smrg    Bool ret = False;
917d522f475Smrg    int state;
918d522f475Smrg
919d522f475Smrg    /*
920913cc679Smrg     * DECterm turns the Locator off if the position is requested while a
921913cc679Smrg     * filter rectangle is active.  This might be a bug, but I don't know, so
922913cc679Smrg     * I'll emulate it anyways.
923d522f475Smrg     */
924d522f475Smrg    if (screen->loc_filter) {
925d522f475Smrg	screen->send_mouse_pos = MOUSE_OFF;
926d522f475Smrg	screen->loc_filter = False;
927d522f475Smrg	screen->locator_events = 0;
928d522f475Smrg	MotionOff(screen, xw);
929d522f475Smrg    }
930d522f475Smrg
931d522f475Smrg    memset(&reply, 0, sizeof(reply));
932d522f475Smrg    reply.a_type = ANSI_CSI;
933d522f475Smrg
934913cc679Smrg    if (okSendMousePos(xw) == DEC_LOCATOR) {
935d522f475Smrg	ret = XQueryPointer(screen->display, VWindow(screen), &root,
936d522f475Smrg			    &child, &rx, &ry, &x, &y, &mask);
937d522f475Smrg	if (ret) {
938d522f475Smrg	    LocatorCoords(row, col, x, y, oor);
939d522f475Smrg	}
940d522f475Smrg    }
941d522f475Smrg    if (ret == False || oor) {
942d522f475Smrg	reply.a_nparam = 1;
943d522f475Smrg	reply.a_param[0] = 0;	/* Event - 0 = locator unavailable */
944d522f475Smrg	reply.a_inters = '&';
945d522f475Smrg	reply.a_final = 'w';
946d522f475Smrg	unparseseq(xw, &reply);
947d522f475Smrg
948d522f475Smrg	if (screen->locator_reset) {
949d522f475Smrg	    MotionOff(screen, xw);
950d522f475Smrg	    screen->send_mouse_pos = MOUSE_OFF;
951d522f475Smrg	}
952d522f475Smrg	return;
953d522f475Smrg    }
954d522f475Smrg
955d522f475Smrg    ButtonState(state, mask);
956d522f475Smrg
957d522f475Smrg    reply.a_nparam = 4;
958d522f475Smrg    reply.a_param[0] = 1;	/* Event - 1 = response to locator request */
9592eaa94a1Schristos    reply.a_param[1] = (ParmType) state;
9602eaa94a1Schristos    reply.a_param[2] = (ParmType) row;
9612eaa94a1Schristos    reply.a_param[3] = (ParmType) col;
962d522f475Smrg    reply.a_inters = '&';
963d522f475Smrg    reply.a_final = 'w';
964d522f475Smrg    unparseseq(xw, &reply);
965d522f475Smrg
966d522f475Smrg    if (screen->locator_reset) {
967d522f475Smrg	MotionOff(screen, xw);
968d522f475Smrg	screen->send_mouse_pos = MOUSE_OFF;
969d522f475Smrg    }
970d522f475Smrg}
971d522f475Smrg
972d522f475Smrgvoid
973d522f475SmrgInitLocatorFilter(XtermWidget xw)
974d522f475Smrg{
975d522f475Smrg    ANSI reply;
976956cc18dSsnj    TScreen *screen = TScreenOf(xw);
977d522f475Smrg    Window root, child;
978d522f475Smrg    int rx, ry, x, y;
979d522f475Smrg    unsigned int mask;
980d522f475Smrg    int row = 0, col = 0;
981d522f475Smrg    Bool oor = 0;
982d522f475Smrg    Bool ret;
983d522f475Smrg
984d522f475Smrg    ret = XQueryPointer(screen->display, VWindow(screen),
985d522f475Smrg			&root, &child, &rx, &ry, &x, &y, &mask);
986d522f475Smrg    if (ret) {
987d522f475Smrg	LocatorCoords(row, col, x, y, oor);
988d522f475Smrg    }
989d522f475Smrg    if (ret == False || oor) {
990d522f475Smrg	/* Locator is unavailable */
991d522f475Smrg
992d522f475Smrg	if (screen->loc_filter_top != LOC_FILTER_POS ||
993d522f475Smrg	    screen->loc_filter_left != LOC_FILTER_POS ||
994d522f475Smrg	    screen->loc_filter_bottom != LOC_FILTER_POS ||
995d522f475Smrg	    screen->loc_filter_right != LOC_FILTER_POS) {
996d522f475Smrg	    /*
997d522f475Smrg	     * If any explicit coordinates were received,
998d522f475Smrg	     * report immediately with no coordinates.
999d522f475Smrg	     */
1000d522f475Smrg	    memset(&reply, 0, sizeof(reply));
1001d522f475Smrg	    reply.a_type = ANSI_CSI;
1002d522f475Smrg	    reply.a_nparam = 1;
1003d522f475Smrg	    reply.a_param[0] = 0;	/* Event - 0 = locator unavailable */
1004d522f475Smrg	    reply.a_inters = '&';
1005d522f475Smrg	    reply.a_final = 'w';
1006d522f475Smrg	    unparseseq(xw, &reply);
1007d522f475Smrg
1008d522f475Smrg	    if (screen->locator_reset) {
1009d522f475Smrg		MotionOff(screen, xw);
1010d522f475Smrg		screen->send_mouse_pos = MOUSE_OFF;
1011d522f475Smrg	    }
1012d522f475Smrg	} else {
1013d522f475Smrg	    /*
1014d522f475Smrg	     * No explicit coordinates were received, and the pointer is
1015d522f475Smrg	     * unavailable.  Report when the pointer re-enters the window.
1016d522f475Smrg	     */
1017d522f475Smrg	    screen->loc_filter = True;
1018d522f475Smrg	    MotionOn(screen, xw);
1019d522f475Smrg	}
1020d522f475Smrg	return;
1021d522f475Smrg    }
1022d522f475Smrg
1023d522f475Smrg    /*
1024d522f475Smrg     * Adjust rectangle coordinates:
1025d522f475Smrg     *  1. Replace "LOC_FILTER_POS" with current coordinates
1026d522f475Smrg     *  2. Limit coordinates to screen size
1027d522f475Smrg     *  3. make sure top and left are less than bottom and right, resp.
1028d522f475Smrg     */
1029d522f475Smrg    if (screen->locator_pixels) {
1030d522f475Smrg	rx = OriginX(screen) * 2 + Width(screen);
1031d522f475Smrg	ry = screen->border * 2 + Height(screen);
1032d522f475Smrg    } else {
1033d522f475Smrg	rx = screen->max_col;
1034d522f475Smrg	ry = screen->max_row;
1035d522f475Smrg    }
1036d522f475Smrg
1037d522f475Smrg#define	Adjust( coord, def, max )				\
1038d522f475Smrg	if( (coord) == LOC_FILTER_POS )	(coord) = (def);	\
1039d522f475Smrg	else if ((coord) < 1)		(coord) = 1;		\
1040d522f475Smrg	else if ((coord) > (max))	(coord) = (max)
1041d522f475Smrg
1042d522f475Smrg    Adjust(screen->loc_filter_top, row, ry);
1043d522f475Smrg    Adjust(screen->loc_filter_left, col, rx);
1044d522f475Smrg    Adjust(screen->loc_filter_bottom, row, ry);
1045d522f475Smrg    Adjust(screen->loc_filter_right, col, rx);
1046d522f475Smrg
1047d522f475Smrg    if (screen->loc_filter_top > screen->loc_filter_bottom) {
1048d522f475Smrg	ry = screen->loc_filter_top;
1049d522f475Smrg	screen->loc_filter_top = screen->loc_filter_bottom;
1050d522f475Smrg	screen->loc_filter_bottom = ry;
1051d522f475Smrg    }
1052d522f475Smrg
1053d522f475Smrg    if (screen->loc_filter_left > screen->loc_filter_right) {
1054d522f475Smrg	rx = screen->loc_filter_left;
1055d522f475Smrg	screen->loc_filter_left = screen->loc_filter_right;
1056d522f475Smrg	screen->loc_filter_right = rx;
1057d522f475Smrg    }
1058d522f475Smrg
1059d522f475Smrg    if ((col < screen->loc_filter_left) ||
1060d522f475Smrg	(col > screen->loc_filter_right) ||
1061d522f475Smrg	(row < screen->loc_filter_top) ||
1062d522f475Smrg	(row > screen->loc_filter_bottom)) {
10632e4f8982Smrg	int state;
10642e4f8982Smrg
1065d522f475Smrg	/* Pointer is already outside the rectangle - report immediately */
1066d522f475Smrg	ButtonState(state, mask);
1067d522f475Smrg
1068d522f475Smrg	memset(&reply, 0, sizeof(reply));
1069d522f475Smrg	reply.a_type = ANSI_CSI;
1070d522f475Smrg	reply.a_nparam = 4;
1071d522f475Smrg	reply.a_param[0] = 10;	/* Event - 10 = locator outside filter */
10722eaa94a1Schristos	reply.a_param[1] = (ParmType) state;
10732eaa94a1Schristos	reply.a_param[2] = (ParmType) row;
10742eaa94a1Schristos	reply.a_param[3] = (ParmType) col;
1075d522f475Smrg	reply.a_inters = '&';
1076d522f475Smrg	reply.a_final = 'w';
1077d522f475Smrg	unparseseq(xw, &reply);
1078d522f475Smrg
1079d522f475Smrg	if (screen->locator_reset) {
1080d522f475Smrg	    MotionOff(screen, xw);
1081d522f475Smrg	    screen->send_mouse_pos = MOUSE_OFF;
1082d522f475Smrg	}
1083d522f475Smrg	return;
1084d522f475Smrg    }
1085d522f475Smrg
1086d522f475Smrg    /*
1087d522f475Smrg     * Rectangle is set up.  Allow pointer tracking
1088d522f475Smrg     * to detect if the mouse leaves the rectangle.
1089d522f475Smrg     */
1090d522f475Smrg    screen->loc_filter = True;
1091d522f475Smrg    MotionOn(screen, xw);
1092d522f475Smrg}
1093d522f475Smrg
1094d522f475Smrgstatic void
1095894e0ac8SmrgCheckLocatorPosition(XtermWidget xw, XButtonEvent *event)
1096d522f475Smrg{
1097d522f475Smrg    ANSI reply;
1098956cc18dSsnj    TScreen *screen = TScreenOf(xw);
1099d522f475Smrg    int row, col;
1100d522f475Smrg    Bool oor;
1101d522f475Smrg
1102492d43a5Smrg    LocatorCoords(row, col, event->x, event->y, oor);
1103d522f475Smrg
1104d522f475Smrg    /*
1105d522f475Smrg     * Send report if the pointer left the filter rectangle, if
1106d522f475Smrg     * the pointer left the window, or if the filter rectangle
1107d522f475Smrg     * had no coordinates and the pointer re-entered the window.
1108d522f475Smrg     */
1109d522f475Smrg    if (oor || (screen->loc_filter_top == LOC_FILTER_POS) ||
1110d522f475Smrg	(col < screen->loc_filter_left) ||
1111d522f475Smrg	(col > screen->loc_filter_right) ||
1112d522f475Smrg	(row < screen->loc_filter_top) ||
1113d522f475Smrg	(row > screen->loc_filter_bottom)) {
1114d522f475Smrg	/* Filter triggered - disable it */
1115d522f475Smrg	screen->loc_filter = False;
1116d522f475Smrg	MotionOff(screen, xw);
1117d522f475Smrg
1118d522f475Smrg	memset(&reply, 0, sizeof(reply));
1119d522f475Smrg	reply.a_type = ANSI_CSI;
1120d522f475Smrg	if (oor) {
1121d522f475Smrg	    reply.a_nparam = 1;
1122d522f475Smrg	    reply.a_param[0] = 0;	/* Event - 0 = locator unavailable */
1123d522f475Smrg	} else {
11242e4f8982Smrg	    int state;
11252e4f8982Smrg
1126492d43a5Smrg	    ButtonState(state, event->state);
1127d522f475Smrg
1128d522f475Smrg	    reply.a_nparam = 4;
1129d522f475Smrg	    reply.a_param[0] = 10;	/* Event - 10 = locator outside filter */
11302eaa94a1Schristos	    reply.a_param[1] = (ParmType) state;
11312eaa94a1Schristos	    reply.a_param[2] = (ParmType) row;
11322eaa94a1Schristos	    reply.a_param[3] = (ParmType) col;
1133d522f475Smrg	}
1134d522f475Smrg
1135d522f475Smrg	reply.a_inters = '&';
1136d522f475Smrg	reply.a_final = 'w';
1137d522f475Smrg	unparseseq(xw, &reply);
1138d522f475Smrg
1139d522f475Smrg	if (screen->locator_reset) {
1140d522f475Smrg	    MotionOff(screen, xw);
1141d522f475Smrg	    screen->send_mouse_pos = MOUSE_OFF;
1142d522f475Smrg	}
1143d522f475Smrg    }
1144d522f475Smrg}
1145d522f475Smrg#endif /* OPT_DEC_LOCATOR */
1146d522f475Smrg
1147d522f475Smrg#if OPT_READLINE
1148d522f475Smrgstatic int
1149913cc679SmrgisClick1_clean(XtermWidget xw, XButtonEvent *event)
1150d522f475Smrg{
1151913cc679Smrg    TScreen *screen = TScreenOf(xw);
1152d522f475Smrg    int delta;
1153d522f475Smrg
1154d522f475Smrg    /* Disable on Shift-Click-1, including the application-mouse modes */
1155f2e35a3aSmrg    if (OverrideButton(event)
1156f2e35a3aSmrg	|| (okSendMousePos(xw) != MOUSE_OFF)
1157f2e35a3aSmrg	|| ExtendingSelection)	/* Was moved */
1158d522f475Smrg	return 0;
1159d522f475Smrg
1160d522f475Smrg    if (event->type != ButtonRelease)
1161d522f475Smrg	return 0;
1162d522f475Smrg
1163d522f475Smrg    if (lastButtonDownTime == (Time) 0) {
1164d522f475Smrg	/* first time or once in a blue moon */
1165d522f475Smrg	delta = screen->multiClickTime + 1;
1166492d43a5Smrg    } else if (event->time > lastButtonDownTime) {
1167d522f475Smrg	/* most of the time */
1168492d43a5Smrg	delta = (int) (event->time - lastButtonDownTime);
1169d522f475Smrg    } else {
1170d522f475Smrg	/* time has rolled over since lastButtonUpTime */
1171492d43a5Smrg	delta = (int) ((((Time) ~ 0) - lastButtonDownTime) + event->time);
1172d522f475Smrg    }
1173d522f475Smrg
1174d522f475Smrg    return delta <= screen->multiClickTime;
1175d522f475Smrg}
1176d522f475Smrg
1177d522f475Smrgstatic int
1178f2e35a3aSmrgisDoubleClick3(XtermWidget xw, TScreen *screen, XButtonEvent *event)
1179d522f475Smrg{
1180d522f475Smrg    int delta;
1181d522f475Smrg
1182d522f475Smrg    if (event->type != ButtonRelease
1183f2e35a3aSmrg	|| OverrideButton(event)
1184492d43a5Smrg	|| event->button != Button3) {
1185d522f475Smrg	lastButton3UpTime = 0;	/* Disable the cached info */
1186d522f475Smrg	return 0;
1187d522f475Smrg    }
1188d522f475Smrg    /* Process Btn3Release. */
1189d522f475Smrg    if (lastButton3DoubleDownTime == (Time) 0) {
1190d522f475Smrg	/* No previous click or once in a blue moon */
1191d522f475Smrg	delta = screen->multiClickTime + 1;
1192492d43a5Smrg    } else if (event->time > lastButton3DoubleDownTime) {
1193d522f475Smrg	/* most of the time */
1194492d43a5Smrg	delta = (int) (event->time - lastButton3DoubleDownTime);
1195d522f475Smrg    } else {
1196d522f475Smrg	/* time has rolled over since lastButton3DoubleDownTime */
1197492d43a5Smrg	delta = (int) ((((Time) ~ 0) - lastButton3DoubleDownTime) + event->time);
1198d522f475Smrg    }
1199d522f475Smrg    if (delta <= screen->multiClickTime) {
1200d522f475Smrg	/* Double click */
1201d522f475Smrg	CELL cell;
1202d522f475Smrg
1203d522f475Smrg	/* Cannot check ExtendingSelection, since mouse-3 always sets it */
1204492d43a5Smrg	PointToCELL(screen, event->y, event->x, &cell);
1205d522f475Smrg	if (isSameCELL(&cell, &lastButton3)) {
1206d522f475Smrg	    lastButton3DoubleDownTime = 0;	/* Disable the third click */
1207d522f475Smrg	    return 1;
1208d522f475Smrg	}
1209d522f475Smrg    }
1210d522f475Smrg    /* Not a double click, memorize for future check. */
1211492d43a5Smrg    lastButton3UpTime = event->time;
1212492d43a5Smrg    PointToCELL(screen, event->y, event->x, &lastButton3);
1213d522f475Smrg    return 0;
1214d522f475Smrg}
1215d522f475Smrg
1216d522f475Smrgstatic int
1217f2e35a3aSmrgCheckSecondPress3(XtermWidget xw, TScreen *screen, XEvent *event)
1218d522f475Smrg{
1219d522f475Smrg    int delta;
1220d522f475Smrg
1221d522f475Smrg    if (event->type != ButtonPress
1222f2e35a3aSmrg	|| OverrideEvent(event)
1223d522f475Smrg	|| event->xbutton.button != Button3) {
1224d522f475Smrg	lastButton3DoubleDownTime = 0;	/* Disable the cached info */
1225d522f475Smrg	return 0;
1226d522f475Smrg    }
1227d522f475Smrg    /* Process Btn3Press. */
1228d522f475Smrg    if (lastButton3UpTime == (Time) 0) {
1229d522f475Smrg	/* No previous click or once in a blue moon */
1230d522f475Smrg	delta = screen->multiClickTime + 1;
1231d522f475Smrg    } else if (event->xbutton.time > lastButton3UpTime) {
1232d522f475Smrg	/* most of the time */
12332eaa94a1Schristos	delta = (int) (event->xbutton.time - lastButton3UpTime);
1234d522f475Smrg    } else {
1235d522f475Smrg	/* time has rolled over since lastButton3UpTime */
12362eaa94a1Schristos	delta = (int) ((((Time) ~ 0) - lastButton3UpTime) + event->xbutton.time);
1237d522f475Smrg    }
1238d522f475Smrg    if (delta <= screen->multiClickTime) {
1239d522f475Smrg	CELL cell;
1240d522f475Smrg
1241d522f475Smrg	PointToCELL(screen, event->xbutton.y, event->xbutton.x, &cell);
1242d522f475Smrg	if (isSameCELL(&cell, &lastButton3)) {
1243d522f475Smrg	    /* A candidate for a double-click */
1244d522f475Smrg	    lastButton3DoubleDownTime = event->xbutton.time;
1245d522f475Smrg	    PointToCELL(screen, event->xbutton.y, event->xbutton.x, &lastButton3);
1246d522f475Smrg	    return 1;
1247d522f475Smrg	}
1248d522f475Smrg	lastButton3UpTime = 0;	/* Disable the info about the previous click */
1249d522f475Smrg    }
1250d522f475Smrg    /* Either too long, or moved, disable. */
1251d522f475Smrg    lastButton3DoubleDownTime = 0;
1252d522f475Smrg    return 0;
1253d522f475Smrg}
1254d522f475Smrg
1255d522f475Smrgstatic int
1256e0a2b6dfSmrgrowOnCurrentLine(TScreen *screen,
1257d522f475Smrg		 int line,
1258d522f475Smrg		 int *deltap)	/* must be XButtonEvent */
1259d522f475Smrg{
1260956cc18dSsnj    int result = 1;
1261d522f475Smrg
1262d522f475Smrg    *deltap = 0;
12632e4f8982Smrg
1264956cc18dSsnj    if (line != screen->cur_row) {
12652e4f8982Smrg	int l1, l2;
12662e4f8982Smrg
1267f2e35a3aSmrg	if (line < screen->cur_row) {
1268f2e35a3aSmrg	    l1 = line;
1269f2e35a3aSmrg	    l2 = screen->cur_row;
1270f2e35a3aSmrg	} else {
1271f2e35a3aSmrg	    l2 = line;
1272f2e35a3aSmrg	    l1 = screen->cur_row;
1273f2e35a3aSmrg	}
1274956cc18dSsnj	l1--;
1275956cc18dSsnj	while (++l1 < l2) {
1276956cc18dSsnj	    LineData *ld = GET_LINEDATA(screen, l1);
1277956cc18dSsnj	    if (!LineTstWrapped(ld)) {
1278956cc18dSsnj		result = 0;
1279956cc18dSsnj		break;
1280956cc18dSsnj	    }
1281956cc18dSsnj	}
1282956cc18dSsnj	if (result) {
1283956cc18dSsnj	    /* Everything is on one "wrapped line" now */
1284956cc18dSsnj	    *deltap = line - screen->cur_row;
1285956cc18dSsnj	}
1286956cc18dSsnj    }
1287956cc18dSsnj    return result;
1288d522f475Smrg}
1289d522f475Smrg
1290d522f475Smrgstatic int
1291894e0ac8SmrgeventRow(TScreen *screen, XEvent *event)	/* must be XButtonEvent */
1292d522f475Smrg{
1293d522f475Smrg    return (event->xbutton.y - screen->border) / FontHeight(screen);
1294d522f475Smrg}
1295d522f475Smrg
1296d522f475Smrgstatic int
1297894e0ac8SmrgeventColBetween(TScreen *screen, XEvent *event)		/* must be XButtonEvent */
1298d522f475Smrg{
1299d522f475Smrg    /* Correct by half a width - we are acting on a boundary, not on a cell. */
1300d522f475Smrg    return ((event->xbutton.x - OriginX(screen) + (FontWidth(screen) - 1) / 2)
1301d522f475Smrg	    / FontWidth(screen));
1302d522f475Smrg}
1303d522f475Smrg
1304d522f475Smrgstatic int
1305e0a2b6dfSmrgReadLineMovePoint(TScreen *screen, int col, int ldelta)
1306d522f475Smrg{
1307d522f475Smrg    Char line[6];
1308d522f475Smrg    unsigned count = 0;
1309d522f475Smrg
1310d522f475Smrg    col += ldelta * MaxCols(screen) - screen->cur_col;
1311d522f475Smrg    if (col == 0)
1312d522f475Smrg	return 0;
1313d522f475Smrg    if (screen->control_eight_bits) {
1314d522f475Smrg	line[count++] = ANSI_CSI;
1315d522f475Smrg    } else {
1316d522f475Smrg	line[count++] = ANSI_ESC;
1317d522f475Smrg	line[count++] = '[';	/* XXX maybe sometimes O is better? */
1318d522f475Smrg    }
131920d2c4d2Smrg    line[count] = CharOf(col > 0 ? 'C' : 'D');
1320d522f475Smrg    if (col < 0)
1321d522f475Smrg	col = -col;
1322d522f475Smrg    while (col--)
1323d522f475Smrg	v_write(screen->respond, line, 3);
1324d522f475Smrg    return 1;
1325d522f475Smrg}
1326d522f475Smrg
1327d522f475Smrgstatic int
1328e0a2b6dfSmrgReadLineDelete(TScreen *screen, CELL *cell1, CELL *cell2)
1329d522f475Smrg{
1330d522f475Smrg    int del;
1331d522f475Smrg
1332d522f475Smrg    del = (cell2->col - cell1->col) + ((cell2->row - cell1->row) * MaxCols(screen));
1333d522f475Smrg    if (del <= 0)		/* Just in case... */
1334d522f475Smrg	return 0;
1335d522f475Smrg    while (del--)
1336492d43a5Smrg	v_write(screen->respond, (const Char *) "\177", 1);
1337d522f475Smrg    return 1;
1338d522f475Smrg}
1339492d43a5Smrg
1340492d43a5Smrgstatic void
1341913cc679SmrgreadlineExtend(XtermWidget xw, XEvent *event)
1342492d43a5Smrg{
1343913cc679Smrg    TScreen *screen = TScreenOf(xw);
1344492d43a5Smrg    int ldelta1, ldelta2;
1345492d43a5Smrg
1346492d43a5Smrg    if (IsBtnEvent(event)) {
1347492d43a5Smrg	XButtonEvent *my_event = (XButtonEvent *) event;
1348913cc679Smrg	if (isClick1_clean(xw, my_event)
1349492d43a5Smrg	    && SCREEN_FLAG(screen, click1_moves)
1350492d43a5Smrg	    && rowOnCurrentLine(screen, eventRow(screen, event), &ldelta1)) {
1351492d43a5Smrg	    ReadLineMovePoint(screen, eventColBetween(screen, event), ldelta1);
1352492d43a5Smrg	}
1353f2e35a3aSmrg	if (isDoubleClick3(xw, screen, my_event)
1354492d43a5Smrg	    && SCREEN_FLAG(screen, dclick3_deletes)
1355492d43a5Smrg	    && rowOnCurrentLine(screen, screen->startSel.row, &ldelta1)
1356492d43a5Smrg	    && rowOnCurrentLine(screen, screen->endSel.row, &ldelta2)) {
1357492d43a5Smrg	    ReadLineMovePoint(screen, screen->endSel.col, ldelta2);
1358492d43a5Smrg	    ReadLineDelete(screen, &screen->startSel, &(screen->endSel));
1359492d43a5Smrg	}
1360492d43a5Smrg    }
1361492d43a5Smrg}
1362d522f475Smrg#endif /* OPT_READLINE */
1363d522f475Smrg
1364d522f475Smrg/* ^XM-G<line+' '><col+' '> */
1365d522f475Smrgvoid
1366d522f475SmrgDiredButton(Widget w,
1367894e0ac8Smrg	    XEvent *event,	/* must be XButtonEvent */
1368e0a2b6dfSmrg	    String *params GCC_UNUSED,	/* selections */
1369d522f475Smrg	    Cardinal *num_params GCC_UNUSED)
1370d522f475Smrg{
1371956cc18dSsnj    XtermWidget xw;
1372956cc18dSsnj
1373956cc18dSsnj    if ((xw = getXtermWidget(w)) != 0) {
1374956cc18dSsnj	TScreen *screen = TScreenOf(xw);
1375d522f475Smrg
1376492d43a5Smrg	if (IsBtnEvent(event)
13772eaa94a1Schristos	    && (event->xbutton.y >= screen->border)
13782eaa94a1Schristos	    && (event->xbutton.x >= OriginX(screen))) {
13792e4f8982Smrg	    Char Line[6];
13802e4f8982Smrg	    unsigned line, col;
13812e4f8982Smrg
138220d2c4d2Smrg	    line = (unsigned) ((event->xbutton.y - screen->border)
138320d2c4d2Smrg			       / FontHeight(screen));
138420d2c4d2Smrg	    col = (unsigned) ((event->xbutton.x - OriginX(screen))
138520d2c4d2Smrg			      / FontWidth(screen));
1386d522f475Smrg	    Line[0] = CONTROL('X');
1387d522f475Smrg	    Line[1] = ANSI_ESC;
1388d522f475Smrg	    Line[2] = 'G';
13892eaa94a1Schristos	    Line[3] = CharOf(' ' + col);
13902eaa94a1Schristos	    Line[4] = CharOf(' ' + line);
1391d522f475Smrg	    v_write(screen->respond, Line, 5);
1392d522f475Smrg	}
1393d522f475Smrg    }
1394d522f475Smrg}
1395d522f475Smrg
1396d522f475Smrg#if OPT_READLINE
1397d522f475Smrgvoid
1398d522f475SmrgReadLineButton(Widget w,
1399894e0ac8Smrg	       XEvent *event,	/* must be XButtonEvent */
1400f2e35a3aSmrg	       String *params,	/* selections */
1401f2e35a3aSmrg	       Cardinal *num_params)
1402d522f475Smrg{
1403956cc18dSsnj    XtermWidget xw;
1404956cc18dSsnj
1405956cc18dSsnj    if ((xw = getXtermWidget(w)) != 0) {
1406956cc18dSsnj	TScreen *screen = TScreenOf(xw);
1407d522f475Smrg	Char Line[6];
1408d522f475Smrg	int line, col, ldelta = 0;
1409d522f475Smrg
1410492d43a5Smrg	if (!IsBtnEvent(event)
1411913cc679Smrg	    || (okSendMousePos(xw) != MOUSE_OFF) || ExtendingSelection)
1412d522f475Smrg	    goto finish;
1413d522f475Smrg	if (event->type == ButtonRelease) {
1414d522f475Smrg	    int delta;
1415d522f475Smrg
1416d522f475Smrg	    if (lastButtonDownTime == (Time) 0) {
1417d522f475Smrg		/* first time and once in a blue moon */
1418d522f475Smrg		delta = screen->multiClickTime + 1;
1419d522f475Smrg	    } else if (event->xbutton.time > lastButtonDownTime) {
1420d522f475Smrg		/* most of the time */
14212eaa94a1Schristos		delta = (int) (event->xbutton.time - lastButtonDownTime);
1422d522f475Smrg	    } else {
1423d522f475Smrg		/* time has rolled over since lastButtonUpTime */
14242eaa94a1Schristos		delta = (int) ((((Time) ~ 0) - lastButtonDownTime) + event->xbutton.time);
1425d522f475Smrg	    }
1426d522f475Smrg	    if (delta > screen->multiClickTime)
1427d522f475Smrg		goto finish;	/* All this work for this... */
1428d522f475Smrg	}
1429d522f475Smrg	line = (event->xbutton.y - screen->border) / FontHeight(screen);
1430956cc18dSsnj	if (!rowOnCurrentLine(screen, line, &ldelta))
1431956cc18dSsnj	    goto finish;
1432d522f475Smrg	/* Correct by half a width - we are acting on a boundary, not on a cell. */
1433d522f475Smrg	col = (event->xbutton.x - OriginX(screen) + (FontWidth(screen) - 1)
1434d522f475Smrg	       / 2)
1435d522f475Smrg	    / FontWidth(screen) - screen->cur_col + ldelta * MaxCols(screen);
1436d522f475Smrg	if (col == 0)
1437d522f475Smrg	    goto finish;
1438d522f475Smrg	Line[0] = ANSI_ESC;
1439d522f475Smrg	/* XXX: sometimes it is better to send '['? */
1440d522f475Smrg	Line[1] = 'O';
14412eaa94a1Schristos	Line[2] = CharOf(col > 0 ? 'C' : 'D');
1442d522f475Smrg	if (col < 0)
1443d522f475Smrg	    col = -col;
1444d522f475Smrg	while (col--)
1445d522f475Smrg	    v_write(screen->respond, Line, 3);
1446d522f475Smrg      finish:
1447d522f475Smrg	if (event->type == ButtonRelease)
1448d522f475Smrg	    do_select_end(xw, event, params, num_params, False);
1449d522f475Smrg    }
1450d522f475Smrg}
1451d522f475Smrg#endif /* OPT_READLINE */
1452d522f475Smrg
1453d522f475Smrg/* repeats <ESC>n or <ESC>p */
1454d522f475Smrgvoid
1455d522f475SmrgViButton(Widget w,
1456894e0ac8Smrg	 XEvent *event,		/* must be XButtonEvent */
1457e0a2b6dfSmrg	 String *params GCC_UNUSED,	/* selections */
1458d522f475Smrg	 Cardinal *num_params GCC_UNUSED)
1459d522f475Smrg{
1460956cc18dSsnj    XtermWidget xw;
1461956cc18dSsnj
1462956cc18dSsnj    if ((xw = getXtermWidget(w)) != 0) {
1463956cc18dSsnj	TScreen *screen = TScreenOf(xw);
1464d522f475Smrg	int pty = screen->respond;
1465d522f475Smrg
1466492d43a5Smrg	if (IsBtnEvent(event)) {
14672e4f8982Smrg	    int line;
1468d522f475Smrg
1469d522f475Smrg	    line = screen->cur_row -
1470d522f475Smrg		((event->xbutton.y - screen->border) / FontHeight(screen));
14712e4f8982Smrg
1472d522f475Smrg	    if (line != 0) {
14732e4f8982Smrg		Char Line[6];
14742e4f8982Smrg
1475d522f475Smrg		Line[0] = ANSI_ESC;	/* force an exit from insert-mode */
1476d522f475Smrg		v_write(pty, Line, 1);
1477d522f475Smrg
1478d522f475Smrg		if (line < 0) {
1479d522f475Smrg		    line = -line;
1480d522f475Smrg		    Line[0] = CONTROL('n');
1481d522f475Smrg		} else {
1482d522f475Smrg		    Line[0] = CONTROL('p');
1483d522f475Smrg		}
1484d522f475Smrg		while (--line >= 0)
1485d522f475Smrg		    v_write(pty, Line, 1);
1486d522f475Smrg	    }
1487d522f475Smrg	}
1488d522f475Smrg    }
1489d522f475Smrg}
1490d522f475Smrg
1491d522f475Smrg/*
1492d522f475Smrg * This function handles button-motion events
1493d522f475Smrg */
1494d522f475Smrg/*ARGSUSED*/
1495d522f475Smrgvoid
1496d522f475SmrgHandleSelectExtend(Widget w,
1497894e0ac8Smrg		   XEvent *event,	/* must be XMotionEvent */
1498e0a2b6dfSmrg		   String *params GCC_UNUSED,
1499d522f475Smrg		   Cardinal *num_params GCC_UNUSED)
1500d522f475Smrg{
1501956cc18dSsnj    XtermWidget xw;
1502956cc18dSsnj
1503956cc18dSsnj    if ((xw = getXtermWidget(w)) != 0) {
1504956cc18dSsnj	TScreen *screen = TScreenOf(xw);
1505d522f475Smrg	CELL cell;
1506d522f475Smrg
1507f2e35a3aSmrg	TRACE_EVENT("HandleSelectExtend", event, params, num_params);
15080bd37d32Smrg
1509d522f475Smrg	screen->selection_time = event->xmotion.time;
1510d522f475Smrg	switch (screen->eventMode) {
1511d522f475Smrg	    /* If not in one of the DEC mouse-reporting modes */
1512d522f475Smrg	case LEFTEXTENSION:
1513d522f475Smrg	case RIGHTEXTENSION:
1514d522f475Smrg	    PointToCELL(screen, event->xmotion.y, event->xmotion.x, &cell);
1515d522f475Smrg	    ExtendExtend(xw, &cell);
1516d522f475Smrg	    break;
1517d522f475Smrg
1518d522f475Smrg	    /* If in motion reporting mode, send mouse position to
1519d522f475Smrg	       character process as a key sequence \E[M... */
1520d522f475Smrg	case NORMAL:
1521d522f475Smrg	    /* will get here if send_mouse_pos != MOUSE_OFF */
1522913cc679Smrg	    if (okSendMousePos(xw) == BTN_EVENT_MOUSE
1523913cc679Smrg		|| okSendMousePos(xw) == ANY_EVENT_MOUSE) {
1524d522f475Smrg		(void) SendMousePosition(xw, event);
1525d522f475Smrg	    }
1526d522f475Smrg	    break;
1527d522f475Smrg	}
1528d522f475Smrg    }
1529d522f475Smrg}
1530d522f475Smrg
1531d522f475Smrgvoid
1532d522f475SmrgHandleKeyboardSelectExtend(Widget w,
1533894e0ac8Smrg			   XEvent *event GCC_UNUSED,	/* must be XButtonEvent */
1534e0a2b6dfSmrg			   String *params GCC_UNUSED,
1535d522f475Smrg			   Cardinal *num_params GCC_UNUSED)
1536d522f475Smrg{
1537956cc18dSsnj    XtermWidget xw;
1538956cc18dSsnj
1539956cc18dSsnj    if ((xw = getXtermWidget(w)) != 0) {
1540956cc18dSsnj	TScreen *screen = TScreenOf(xw);
15410bd37d32Smrg
1542f2e35a3aSmrg	TRACE_EVENT("HandleKeyboardSelectExtend", event, params, num_params);
1543d522f475Smrg	ExtendExtend(xw, &screen->cursorp);
1544d522f475Smrg    }
1545d522f475Smrg}
1546d522f475Smrg
1547d522f475Smrgstatic void
1548d522f475Smrgdo_select_end(XtermWidget xw,
1549894e0ac8Smrg	      XEvent *event,	/* must be XButtonEvent */
1550e0a2b6dfSmrg	      String *params,	/* selections */
1551d522f475Smrg	      Cardinal *num_params,
1552d522f475Smrg	      Bool use_cursor_loc)
1553d522f475Smrg{
1554956cc18dSsnj    TScreen *screen = TScreenOf(xw);
1555d522f475Smrg
1556d522f475Smrg    screen->selection_time = event->xbutton.time;
1557f2e35a3aSmrg
1558f2e35a3aSmrg    TRACE(("do_select_end %s @%ld\n",
1559f2e35a3aSmrg	   visibleEventMode(screen->eventMode),
1560f2e35a3aSmrg	   screen->selection_time));
1561f2e35a3aSmrg
1562d522f475Smrg    switch (screen->eventMode) {
1563d522f475Smrg    case NORMAL:
1564d522f475Smrg	(void) SendMousePosition(xw, event);
1565d522f475Smrg	break;
1566d522f475Smrg    case LEFTEXTENSION:
1567d522f475Smrg    case RIGHTEXTENSION:
1568d522f475Smrg	EndExtend(xw, event, params, *num_params, use_cursor_loc);
1569d522f475Smrg#if OPT_READLINE
1570913cc679Smrg	readlineExtend(xw, event);
1571d522f475Smrg#endif /* OPT_READLINE */
1572d522f475Smrg	break;
1573d522f475Smrg    }
1574d522f475Smrg}
1575d522f475Smrg
1576d522f475Smrgvoid
1577d522f475SmrgHandleSelectEnd(Widget w,
1578894e0ac8Smrg		XEvent *event,	/* must be XButtonEvent */
1579e0a2b6dfSmrg		String *params,	/* selections */
1580d522f475Smrg		Cardinal *num_params)
1581d522f475Smrg{
1582956cc18dSsnj    XtermWidget xw;
1583956cc18dSsnj
1584956cc18dSsnj    if ((xw = getXtermWidget(w)) != 0) {
15850bd37d32Smrg	TRACE(("HandleSelectEnd\n"));
1586956cc18dSsnj	do_select_end(xw, event, params, num_params, False);
1587956cc18dSsnj    }
1588d522f475Smrg}
1589d522f475Smrg
1590d522f475Smrgvoid
1591d522f475SmrgHandleKeyboardSelectEnd(Widget w,
1592894e0ac8Smrg			XEvent *event,	/* must be XButtonEvent */
1593e0a2b6dfSmrg			String *params,		/* selections */
1594d522f475Smrg			Cardinal *num_params)
1595d522f475Smrg{
1596956cc18dSsnj    XtermWidget xw;
1597956cc18dSsnj
1598956cc18dSsnj    if ((xw = getXtermWidget(w)) != 0) {
15990bd37d32Smrg	TRACE(("HandleKeyboardSelectEnd\n"));
1600956cc18dSsnj	do_select_end(xw, event, params, num_params, True);
1601956cc18dSsnj    }
1602d522f475Smrg}
1603d522f475Smrg
1604f2e35a3aSmrgvoid
1605f2e35a3aSmrgHandlePointerMotion(Widget w,
1606f2e35a3aSmrg		    XEvent *event,
1607f2e35a3aSmrg		    String *params,	/* selections */
1608f2e35a3aSmrg		    Cardinal *num_params)
1609f2e35a3aSmrg{
1610f2e35a3aSmrg    XtermWidget xw;
1611f2e35a3aSmrg
1612f2e35a3aSmrg    (void) params;
1613f2e35a3aSmrg    (void) num_params;
1614f2e35a3aSmrg    if ((xw = getXtermWidget(w)) != 0) {
1615f2e35a3aSmrg	TRACE(("HandlePointerMotion\n"));
1616f2e35a3aSmrg	if (event->type == MotionNotify)
1617f2e35a3aSmrg	    (void) SendMousePosition(xw, event);
1618f2e35a3aSmrg    }
1619f2e35a3aSmrg}
1620f2e35a3aSmrg
1621f2e35a3aSmrgvoid
1622f2e35a3aSmrgHandlePointerButton(Widget w,
1623f2e35a3aSmrg		    XEvent *event,
1624f2e35a3aSmrg		    String *params,	/* selections */
1625f2e35a3aSmrg		    Cardinal *num_params)
1626f2e35a3aSmrg{
1627f2e35a3aSmrg    XtermWidget xw;
1628f2e35a3aSmrg
1629f2e35a3aSmrg    (void) params;
1630f2e35a3aSmrg    (void) num_params;
1631f2e35a3aSmrg    if ((xw = getXtermWidget(w)) != 0) {
1632f2e35a3aSmrg	TRACE(("HandlePointerButton\n"));
1633f2e35a3aSmrg	if (IsBtnEvent(event))
1634f2e35a3aSmrg	    (void) SendMousePosition(xw, event);
1635f2e35a3aSmrg    }
1636f2e35a3aSmrg}
1637f2e35a3aSmrg
1638492d43a5Smrg/*
16396879286fSmrg * Copy the selection data to the given target(s).
1640492d43a5Smrg */
1641492d43a5Smrgvoid
16426879286fSmrgHandleCopySelection(Widget w,
1643894e0ac8Smrg		    XEvent *event,
1644e0a2b6dfSmrg		    String *params,	/* list of targets */
16456879286fSmrg		    Cardinal *num_params)
1646492d43a5Smrg{
1647492d43a5Smrg    XtermWidget xw;
1648492d43a5Smrg
1649492d43a5Smrg    if ((xw = getXtermWidget(w)) != 0) {
1650f2e35a3aSmrg	TRACE_EVENT("HandleCopySelection", event, params, num_params);
16516879286fSmrg	SelectSet(xw, event, params, *num_params);
1652492d43a5Smrg    }
1653492d43a5Smrg}
1654492d43a5Smrg
1655d522f475Smrgstruct _SelectionList {
1656d522f475Smrg    String *params;
1657d522f475Smrg    Cardinal count;
1658d522f475Smrg    Atom *targets;
1659d522f475Smrg    Time time;
1660d522f475Smrg};
1661d522f475Smrg
1662d522f475Smrgstatic unsigned
1663d522f475SmrgDECtoASCII(unsigned ch)
1664d522f475Smrg{
1665d522f475Smrg    if (xtermIsDecGraphic(ch)) {
16662eaa94a1Schristos	ch = CharOf("###########+++++##-##++++|######"[ch]);
16672eaa94a1Schristos	/*           01234567890123456789012345678901 */
1668d522f475Smrg    }
1669d522f475Smrg    return ch;
1670d522f475Smrg}
167120d2c4d2Smrg
167220d2c4d2Smrg#if OPT_WIDE_CHARS
167320d2c4d2Smrgstatic Cardinal
1674e0a2b6dfSmrgaddXtermChar(Char **buffer, Cardinal *used, Cardinal offset, unsigned value)
167520d2c4d2Smrg{
167620d2c4d2Smrg    if (offset + 1 >= *used) {
167720d2c4d2Smrg	*used = 1 + (2 * (offset + 1));
167820d2c4d2Smrg	allocXtermChars(buffer, *used);
167920d2c4d2Smrg    }
168020d2c4d2Smrg    (*buffer)[offset++] = (Char) value;
168120d2c4d2Smrg    return offset;
168220d2c4d2Smrg}
168320d2c4d2Smrg#define AddChar(buffer, used, offset, value) \
168420d2c4d2Smrg	offset = addXtermChar(buffer, used, offset, (unsigned) value)
168520d2c4d2Smrg
1686d522f475Smrg/*
1687d522f475Smrg * Convert a UTF-8 string to Latin-1, replacing non Latin-1 characters by `#',
1688d522f475Smrg * or ASCII/Latin-1 equivalents for special cases.
1689d522f475Smrg */
1690d522f475Smrgstatic Char *
1691e0a2b6dfSmrgUTF8toLatin1(TScreen *screen, Char *s, unsigned long len, unsigned long *result)
1692d522f475Smrg{
1693d522f475Smrg    static Char *buffer;
1694956cc18dSsnj    static Cardinal used;
1695d522f475Smrg
169620d2c4d2Smrg    Cardinal offset = 0;
1697d522f475Smrg
169820d2c4d2Smrg    if (len != 0) {
1699d522f475Smrg	PtyData data;
1700d522f475Smrg
1701d522f475Smrg	fakePtyData(&data, s, s + len);
1702894e0ac8Smrg	while (decodeUtf8(screen, &data)) {
1703956cc18dSsnj	    Bool fails = False;
1704956cc18dSsnj	    Bool extra = False;
1705f2e35a3aSmrg	    IChar value;
1706f2e35a3aSmrg	    skipPtyData(&data, value);
1707d522f475Smrg	    if (value == UCS_REPL) {
1708956cc18dSsnj		fails = True;
1709d522f475Smrg	    } else if (value < 256) {
171020d2c4d2Smrg		AddChar(&buffer, &used, offset, CharOf(value));
1711d522f475Smrg	    } else {
1712f2e35a3aSmrg		unsigned eqv = ucs2dec(screen, value);
1713d522f475Smrg		if (xtermIsDecGraphic(eqv)) {
171420d2c4d2Smrg		    AddChar(&buffer, &used, offset, DECtoASCII(eqv));
1715d522f475Smrg		} else {
1716d522f475Smrg		    eqv = AsciiEquivs(value);
1717956cc18dSsnj		    if (eqv == value) {
1718956cc18dSsnj			fails = True;
1719956cc18dSsnj		    } else {
172020d2c4d2Smrg			AddChar(&buffer, &used, offset, eqv);
1721956cc18dSsnj		    }
1722956cc18dSsnj		    if (isWide((wchar_t) value))
1723956cc18dSsnj			extra = True;
1724956cc18dSsnj		}
1725956cc18dSsnj	    }
1726956cc18dSsnj
1727956cc18dSsnj	    /*
1728956cc18dSsnj	     * If we're not able to plug in a single-byte result, insert the
1729956cc18dSsnj	     * defaultString (which normally is a single "#", but could be
1730956cc18dSsnj	     * whatever the user wants).
1731956cc18dSsnj	     */
1732956cc18dSsnj	    if (fails) {
17332e4f8982Smrg		const Char *p;
17342e4f8982Smrg
1735492d43a5Smrg		for (p = (const Char *) screen->default_string; *p != '\0'; ++p) {
173620d2c4d2Smrg		    AddChar(&buffer, &used, offset, *p);
1737d522f475Smrg		}
1738d522f475Smrg	    }
1739956cc18dSsnj	    if (extra)
174020d2c4d2Smrg		AddChar(&buffer, &used, offset, ' ');
1741d522f475Smrg	}
174220d2c4d2Smrg	AddChar(&buffer, &used, offset, '\0');
174320d2c4d2Smrg	*result = (unsigned long) (offset - 1);
1744d522f475Smrg    } else {
1745d522f475Smrg	*result = 0;
1746d522f475Smrg    }
1747d522f475Smrg    return buffer;
1748d522f475Smrg}
174920d2c4d2Smrg
175020d2c4d2Smrgint
175120d2c4d2SmrgxtermUtf8ToTextList(XtermWidget xw,
175220d2c4d2Smrg		    XTextProperty * text_prop,
175320d2c4d2Smrg		    char ***text_list,
175420d2c4d2Smrg		    int *text_list_count)
175520d2c4d2Smrg{
175620d2c4d2Smrg    TScreen *screen = TScreenOf(xw);
175720d2c4d2Smrg    Display *dpy = screen->display;
175820d2c4d2Smrg    int rc = -1;
175920d2c4d2Smrg
176020d2c4d2Smrg    if (text_prop->format == 8
176120d2c4d2Smrg	&& (rc = Xutf8TextPropertyToTextList(dpy, text_prop,
176220d2c4d2Smrg					     text_list,
176320d2c4d2Smrg					     text_list_count)) >= 0) {
176420d2c4d2Smrg	if (*text_list != NULL && *text_list_count != 0) {
176520d2c4d2Smrg	    int i;
176620d2c4d2Smrg	    Char *data;
176720d2c4d2Smrg	    char **new_text_list, *tmp;
176820d2c4d2Smrg	    unsigned long size, new_size;
176920d2c4d2Smrg
177020d2c4d2Smrg	    TRACE(("xtermUtf8ToTextList size %d\n", *text_list_count));
177120d2c4d2Smrg
177220d2c4d2Smrg	    /*
177320d2c4d2Smrg	     * XLib StringList actually uses only two pointers, one for the
177420d2c4d2Smrg	     * list itself, and one for the data.  Pointer to the data is the
177520d2c4d2Smrg	     * first element of the list, the rest (if any) list elements point
177620d2c4d2Smrg	     * to the same memory block as the first element
177720d2c4d2Smrg	     */
177820d2c4d2Smrg	    new_size = 0;
177920d2c4d2Smrg	    for (i = 0; i < *text_list_count; ++i) {
178020d2c4d2Smrg		data = (Char *) (*text_list)[i];
178120d2c4d2Smrg		size = strlen((*text_list)[i]) + 1;
178220d2c4d2Smrg		(void) UTF8toLatin1(screen, data, size, &size);
178320d2c4d2Smrg		new_size += size + 1;
178420d2c4d2Smrg	    }
1785a1f3da82Smrg	    new_text_list = TypeXtMallocN(char *, *text_list_count);
178620d2c4d2Smrg	    new_text_list[0] = tmp = XtMalloc((Cardinal) new_size);
178720d2c4d2Smrg	    for (i = 0; i < (*text_list_count); ++i) {
178820d2c4d2Smrg		data = (Char *) (*text_list)[i];
178920d2c4d2Smrg		size = strlen((*text_list)[i]) + 1;
17900bd37d32Smrg		if ((data = UTF8toLatin1(screen, data, size, &size)) != 0) {
17910bd37d32Smrg		    memcpy(tmp, data, size + 1);
17920bd37d32Smrg		    new_text_list[i] = tmp;
17930bd37d32Smrg		    tmp += size + 1;
17940bd37d32Smrg		}
179520d2c4d2Smrg	    }
179620d2c4d2Smrg	    XFreeStringList((*text_list));
179720d2c4d2Smrg	    *text_list = new_text_list;
179820d2c4d2Smrg	} else {
179920d2c4d2Smrg	    rc = -1;
180020d2c4d2Smrg	}
180120d2c4d2Smrg    }
180220d2c4d2Smrg    return rc;
180320d2c4d2Smrg}
1804d522f475Smrg#endif /* OPT_WIDE_CHARS */
1805d522f475Smrg
1806956cc18dSsnjstatic char *
1807956cc18dSsnjparseItem(char *value, char *nextc)
1808d522f475Smrg{
1809956cc18dSsnj    char *nextp = value;
1810956cc18dSsnj    while (*nextp != '\0' && *nextp != ',') {
1811956cc18dSsnj	*nextp = x_toupper(*nextp);
1812956cc18dSsnj	++nextp;
1813956cc18dSsnj    }
1814956cc18dSsnj    *nextc = *nextp;
1815956cc18dSsnj    *nextp = '\0';
1816d522f475Smrg
1817956cc18dSsnj    return nextp;
1818956cc18dSsnj}
1819d522f475Smrg
1820956cc18dSsnj/*
1821956cc18dSsnj * All of the wanted strings are unique in the first character, so we can
1822956cc18dSsnj * use simple abbreviations.
1823956cc18dSsnj */
1824956cc18dSsnjstatic Bool
1825956cc18dSsnjsameItem(const char *actual, const char *wanted)
1826956cc18dSsnj{
1827956cc18dSsnj    Bool result = False;
1828956cc18dSsnj    size_t have = strlen(actual);
1829956cc18dSsnj    size_t need = strlen(wanted);
1830d522f475Smrg
1831956cc18dSsnj    if (have != 0 && have <= need) {
1832956cc18dSsnj	if (!strncmp(actual, wanted, have)) {
1833956cc18dSsnj	    TRACE(("...matched \"%s\"\n", wanted));
1834956cc18dSsnj	    result = True;
1835956cc18dSsnj	}
1836956cc18dSsnj    }
1837956cc18dSsnj
1838956cc18dSsnj    return result;
1839956cc18dSsnj}
1840956cc18dSsnj
1841956cc18dSsnj/*
1842956cc18dSsnj * Handle the eightBitSelectTypes or utf8SelectTypes resource values.
1843956cc18dSsnj */
1844956cc18dSsnjstatic Bool
1845894e0ac8SmrgoverrideTargets(Widget w, String value, Atom **resultp)
1846956cc18dSsnj{
1847956cc18dSsnj    Bool override = False;
1848956cc18dSsnj    XtermWidget xw;
1849956cc18dSsnj
1850956cc18dSsnj    if ((xw = getXtermWidget(w)) != 0) {
1851956cc18dSsnj	TScreen *screen = TScreenOf(xw);
1852956cc18dSsnj
185320d2c4d2Smrg	if (!IsEmpty(value)) {
1854492d43a5Smrg	    char *copied = x_strdup(value);
1855956cc18dSsnj	    if (copied != 0) {
1856956cc18dSsnj		Atom *result = 0;
1857956cc18dSsnj		Cardinal count = 1;
1858956cc18dSsnj		int n;
1859d522f475Smrg
1860956cc18dSsnj		TRACE(("decoding SelectTypes \"%s\"\n", value));
1861956cc18dSsnj		for (n = 0; copied[n] != '\0'; ++n) {
1862956cc18dSsnj		    if (copied[n] == ',')
1863956cc18dSsnj			++count;
1864956cc18dSsnj		}
1865a1f3da82Smrg		result = TypeXtMallocN(Atom, (2 * count) + 1);
1866956cc18dSsnj		if (result == NULL) {
1867956cc18dSsnj		    TRACE(("Couldn't allocate selection types\n"));
1868956cc18dSsnj		} else {
1869956cc18dSsnj		    char nextc = '?';
187020d2c4d2Smrg		    char *listp = (char *) copied;
1871956cc18dSsnj		    count = 0;
1872956cc18dSsnj		    do {
1873956cc18dSsnj			char *nextp = parseItem(listp, &nextc);
18740bd37d32Smrg			char *item = x_strtrim(listp);
18750bd37d32Smrg			size_t len = (item ? strlen(item) : 0);
1876956cc18dSsnj
1877956cc18dSsnj			if (len == 0) {
1878a1f3da82Smrg			    /* EMPTY */ ;
1879956cc18dSsnj			}
1880956cc18dSsnj#if OPT_WIDE_CHARS
18810bd37d32Smrg			else if (sameItem(item, "UTF8")) {
1882956cc18dSsnj			    result[count++] = XA_UTF8_STRING(XtDisplay(w));
1883956cc18dSsnj			}
1884956cc18dSsnj#endif
18850bd37d32Smrg			else if (sameItem(item, "I18N")) {
1886956cc18dSsnj			    if (screen->i18nSelections) {
1887956cc18dSsnj				result[count++] = XA_TEXT(XtDisplay(w));
1888956cc18dSsnj				result[count++] = XA_COMPOUND_TEXT(XtDisplay(w));
1889956cc18dSsnj			    }
18900bd37d32Smrg			} else if (sameItem(item, "TEXT")) {
1891956cc18dSsnj			    result[count++] = XA_TEXT(XtDisplay(w));
18920bd37d32Smrg			} else if (sameItem(item, "COMPOUND_TEXT")) {
1893956cc18dSsnj			    result[count++] = XA_COMPOUND_TEXT(XtDisplay(w));
18940bd37d32Smrg			} else if (sameItem(item, "STRING")) {
1895956cc18dSsnj			    result[count++] = XA_STRING;
1896956cc18dSsnj			}
1897956cc18dSsnj			*nextp++ = nextc;
1898956cc18dSsnj			listp = nextp;
18990bd37d32Smrg			free(item);
1900956cc18dSsnj		    } while (nextc != '\0');
1901956cc18dSsnj		    if (count) {
1902956cc18dSsnj			result[count] = None;
1903956cc18dSsnj			override = True;
1904956cc18dSsnj			*resultp = result;
1905956cc18dSsnj		    } else {
1906956cc18dSsnj			XtFree((char *) result);
1907956cc18dSsnj		    }
1908956cc18dSsnj		}
19090bd37d32Smrg		free(copied);
1910956cc18dSsnj	    } else {
1911956cc18dSsnj		TRACE(("Couldn't allocate copy of selection types\n"));
1912d522f475Smrg	    }
1913956cc18dSsnj	}
1914956cc18dSsnj    }
1915956cc18dSsnj    return override;
1916956cc18dSsnj}
1917956cc18dSsnj
1918956cc18dSsnj#if OPT_WIDE_CHARS
1919956cc18dSsnjstatic Atom *
1920e0a2b6dfSmrgallocUtf8Targets(Widget w, TScreen *screen)
1921956cc18dSsnj{
1922956cc18dSsnj    Atom **resultp = &(screen->selection_targets_utf8);
1923956cc18dSsnj
1924956cc18dSsnj    if (*resultp == 0) {
1925956cc18dSsnj	Atom *result;
1926956cc18dSsnj
1927956cc18dSsnj	if (!overrideTargets(w, screen->utf8_select_types, &result)) {
1928a1f3da82Smrg	    result = TypeXtMallocN(Atom, 5);
1929956cc18dSsnj	    if (result == NULL) {
1930956cc18dSsnj		TRACE(("Couldn't allocate utf-8 selection targets\n"));
1931956cc18dSsnj	    } else {
1932956cc18dSsnj		int n = 0;
1933956cc18dSsnj
1934e39b573cSmrg		if (XSupportsLocale()) {
1935e39b573cSmrg		    result[n++] = XA_UTF8_STRING(XtDisplay(w));
1936d522f475Smrg#ifdef X_HAVE_UTF8_STRING
1937e39b573cSmrg		    if (screen->i18nSelections) {
1938e39b573cSmrg			result[n++] = XA_TEXT(XtDisplay(w));
1939e39b573cSmrg			result[n++] = XA_COMPOUND_TEXT(XtDisplay(w));
1940e39b573cSmrg		    }
1941d522f475Smrg#endif
1942e39b573cSmrg		}
1943956cc18dSsnj		result[n++] = XA_STRING;
1944956cc18dSsnj		result[n] = None;
1945956cc18dSsnj	    }
1946d522f475Smrg	}
1947956cc18dSsnj
1948956cc18dSsnj	*resultp = result;
1949d522f475Smrg    }
1950956cc18dSsnj
1951956cc18dSsnj    return *resultp;
1952956cc18dSsnj}
1953d522f475Smrg#endif
1954d522f475Smrg
1955956cc18dSsnjstatic Atom *
1956e0a2b6dfSmrgalloc8bitTargets(Widget w, TScreen *screen)
1957956cc18dSsnj{
1958956cc18dSsnj    Atom **resultp = &(screen->selection_targets_8bit);
1959956cc18dSsnj
1960956cc18dSsnj    if (*resultp == 0) {
1961956cc18dSsnj	Atom *result = 0;
1962956cc18dSsnj
1963956cc18dSsnj	if (!overrideTargets(w, screen->eightbit_select_types, &result)) {
1964a1f3da82Smrg	    result = TypeXtMallocN(Atom, 5);
1965956cc18dSsnj	    if (result == NULL) {
1966956cc18dSsnj		TRACE(("Couldn't allocate 8bit selection targets\n"));
1967956cc18dSsnj	    } else {
1968956cc18dSsnj		int n = 0;
1969956cc18dSsnj
1970e39b573cSmrg		if (XSupportsLocale()) {
1971d522f475Smrg#ifdef X_HAVE_UTF8_STRING
1972e39b573cSmrg		    result[n++] = XA_UTF8_STRING(XtDisplay(w));
1973956cc18dSsnj#endif
1974e39b573cSmrg		    if (screen->i18nSelections) {
1975e39b573cSmrg			result[n++] = XA_TEXT(XtDisplay(w));
1976e39b573cSmrg			result[n++] = XA_COMPOUND_TEXT(XtDisplay(w));
1977e39b573cSmrg		    }
1978956cc18dSsnj		}
1979956cc18dSsnj		result[n++] = XA_STRING;
1980956cc18dSsnj		result[n] = None;
1981956cc18dSsnj	    }
1982956cc18dSsnj	}
1983956cc18dSsnj
1984956cc18dSsnj	*resultp = result;
1985956cc18dSsnj    }
1986956cc18dSsnj
1987956cc18dSsnj    return *resultp;
1988956cc18dSsnj}
1989956cc18dSsnj
1990956cc18dSsnjstatic Atom *
1991956cc18dSsnj_SelectionTargets(Widget w)
1992956cc18dSsnj{
1993956cc18dSsnj    Atom *result;
1994956cc18dSsnj    XtermWidget xw;
1995956cc18dSsnj
1996956cc18dSsnj    if ((xw = getXtermWidget(w)) == 0) {
1997956cc18dSsnj	result = NULL;
1998956cc18dSsnj    } else {
19992e4f8982Smrg	TScreen *screen = TScreenOf(xw);
2000956cc18dSsnj
2001956cc18dSsnj#if OPT_WIDE_CHARS
2002956cc18dSsnj	if (screen->wide_chars) {
2003956cc18dSsnj	    result = allocUtf8Targets(w, screen);
2004956cc18dSsnj	} else
2005d522f475Smrg#endif
2006956cc18dSsnj	{
2007956cc18dSsnj	    /* not screen->wide_chars */
2008956cc18dSsnj	    result = alloc8bitTargets(w, screen);
2009d522f475Smrg	}
2010d522f475Smrg    }
2011956cc18dSsnj
2012956cc18dSsnj    return result;
2013d522f475Smrg}
2014d522f475Smrg
2015d522f475Smrg#define isSELECT(value) (!strcmp(value, "SELECT"))
2016d522f475Smrg
2017f2e35a3aSmrgstatic int
2018f2e35a3aSmrgDefaultSelection(TScreen *screen)
2019f2e35a3aSmrg{
2020f2e35a3aSmrg    return (screen->selectToClipboard ? 1 : 0);
2021f2e35a3aSmrg}
2022f2e35a3aSmrg
2023f2e35a3aSmrgstatic int
2024f2e35a3aSmrgTargetToSelection(TScreen *screen, String name)
2025f2e35a3aSmrg{
2026f2e35a3aSmrg    int result = -1;
2027f2e35a3aSmrg    int cutb;
2028f2e35a3aSmrg
2029f2e35a3aSmrg    if (isSELECT(name)) {
2030f2e35a3aSmrg	result = DefaultSelection(screen);
2031f2e35a3aSmrg    } else if (!strcmp(name, PRIMARY_NAME)) {
2032f2e35a3aSmrg	result = PRIMARY_CODE;
2033f2e35a3aSmrg    } else if (!strcmp(name, CLIPBOARD_NAME)) {
2034f2e35a3aSmrg	result = CLIPBOARD_CODE;
2035f2e35a3aSmrg    } else if (!strcmp(name, SECONDARY_NAME)) {
2036f2e35a3aSmrg	result = SECONDARY_CODE;
2037f2e35a3aSmrg    } else if (sscanf(name, "CUT_BUFFER%d", &cutb) == 1) {
2038f2e35a3aSmrg	if (cutb >= 0 && cutb < MAX_CUT_BUFFER) {
2039f2e35a3aSmrg	    result = CutBufferToCode(cutb);
2040f2e35a3aSmrg	} else {
2041f2e35a3aSmrg	    xtermWarning("unexpected cut-buffer code: %d\n", cutb);
2042f2e35a3aSmrg	}
2043f2e35a3aSmrg    } else {
2044f2e35a3aSmrg	xtermWarning("unexpected selection target: %s\n", name);
2045f2e35a3aSmrg    }
2046f2e35a3aSmrg    TRACE2(("TargetToSelection(%s) ->%d\n", name, result));
2047f2e35a3aSmrg    return result;
2048f2e35a3aSmrg}
2049f2e35a3aSmrg
2050f2e35a3aSmrgvoid
2051d522f475SmrgUnmapSelections(XtermWidget xw)
2052d522f475Smrg{
2053956cc18dSsnj    TScreen *screen = TScreenOf(xw);
2054d522f475Smrg    Cardinal n;
2055d522f475Smrg
2056d522f475Smrg    if (screen->mappedSelect) {
2057d522f475Smrg	for (n = 0; screen->mappedSelect[n] != 0; ++n)
205820d2c4d2Smrg	    free((void *) screen->mappedSelect[n]);
2059f2e35a3aSmrg	FreeAndNull(screen->mappedSelect);
2060d522f475Smrg    }
2061d522f475Smrg}
2062d522f475Smrg
2063d522f475Smrg/*
2064d522f475Smrg * xterm generally uses the primary selection.  Some applications prefer
2065d522f475Smrg * (or are limited to) the clipboard.  Since the translations resource is
2066d522f475Smrg * complicated, users seldom change the way it affects selection.  But it
2067d522f475Smrg * is simple to remap the choice between primary and clipboard before the
2068d522f475Smrg * call to XmuInternStrings().
2069d522f475Smrg */
2070d522f475Smrgstatic String *
2071e0a2b6dfSmrgMapSelections(XtermWidget xw, String *params, Cardinal num_params)
2072d522f475Smrg{
2073d522f475Smrg    String *result = params;
2074d522f475Smrg
2075913cc679Smrg    if (params != 0 && num_params > 0) {
2076d522f475Smrg	Cardinal j;
2077d522f475Smrg	Boolean map = False;
2078d522f475Smrg
2079d522f475Smrg	for (j = 0; j < num_params; ++j) {
2080d522f475Smrg	    TRACE(("param[%d]:%s\n", j, params[j]));
2081d522f475Smrg	    if (isSELECT(params[j])) {
2082d522f475Smrg		map = True;
2083d522f475Smrg		break;
2084d522f475Smrg	    }
2085d522f475Smrg	}
2086d522f475Smrg	if (map) {
2087956cc18dSsnj	    TScreen *screen = TScreenOf(xw);
2088956cc18dSsnj	    const char *mapTo = (screen->selectToClipboard
2089f2e35a3aSmrg				 ? CLIPBOARD_NAME
2090f2e35a3aSmrg				 : PRIMARY_NAME);
2091d522f475Smrg
2092d522f475Smrg	    UnmapSelections(xw);
2093d522f475Smrg	    if ((result = TypeMallocN(String, num_params + 1)) != 0) {
2094d522f475Smrg		result[num_params] = 0;
2095d522f475Smrg		for (j = 0; j < num_params; ++j) {
2096d522f475Smrg		    result[j] = x_strdup((isSELECT(params[j])
2097d522f475Smrg					  ? mapTo
2098d522f475Smrg					  : params[j]));
2099d522f475Smrg		    if (result[j] == 0) {
2100d522f475Smrg			UnmapSelections(xw);
21010bd37d32Smrg			while (j != 0) {
21020bd37d32Smrg			    free((void *) result[--j]);
21030bd37d32Smrg			}
2104f2e35a3aSmrg			FreeAndNull(result);
2105d522f475Smrg			break;
2106d522f475Smrg		    }
2107d522f475Smrg		}
2108956cc18dSsnj		screen->mappedSelect = result;
2109d522f475Smrg	    }
2110d522f475Smrg	}
2111d522f475Smrg    }
2112d522f475Smrg    return result;
2113d522f475Smrg}
2114d522f475Smrg
2115d522f475Smrg/*
2116d522f475Smrg * Lookup the cut-buffer number, which will be in the range 0-7.
2117f2e35a3aSmrg * If it is not a cut-buffer, it is a type of selection, e.g., primary.
2118d522f475Smrg */
2119d522f475Smrgstatic int
212020d2c4d2SmrgCutBuffer(Atom code)
2121d522f475Smrg{
2122d522f475Smrg    int cutbuffer;
212320d2c4d2Smrg    switch ((unsigned) code) {
2124d522f475Smrg    case XA_CUT_BUFFER0:
2125d522f475Smrg	cutbuffer = 0;
2126d522f475Smrg	break;
2127d522f475Smrg    case XA_CUT_BUFFER1:
2128d522f475Smrg	cutbuffer = 1;
2129d522f475Smrg	break;
2130d522f475Smrg    case XA_CUT_BUFFER2:
2131d522f475Smrg	cutbuffer = 2;
2132d522f475Smrg	break;
2133d522f475Smrg    case XA_CUT_BUFFER3:
2134d522f475Smrg	cutbuffer = 3;
2135d522f475Smrg	break;
2136d522f475Smrg    case XA_CUT_BUFFER4:
2137d522f475Smrg	cutbuffer = 4;
2138d522f475Smrg	break;
2139d522f475Smrg    case XA_CUT_BUFFER5:
2140d522f475Smrg	cutbuffer = 5;
2141d522f475Smrg	break;
2142d522f475Smrg    case XA_CUT_BUFFER6:
2143d522f475Smrg	cutbuffer = 6;
2144d522f475Smrg	break;
2145d522f475Smrg    case XA_CUT_BUFFER7:
2146d522f475Smrg	cutbuffer = 7;
2147d522f475Smrg	break;
2148d522f475Smrg    default:
2149d522f475Smrg	cutbuffer = -1;
2150d522f475Smrg	break;
2151d522f475Smrg    }
2152f2e35a3aSmrg    TRACE2(("CutBuffer(%d) = %d\n", (int) code, cutbuffer));
2153d522f475Smrg    return cutbuffer;
2154d522f475Smrg}
2155d522f475Smrg
2156d522f475Smrg#if OPT_PASTE64
2157d522f475Smrgstatic void
2158d522f475SmrgFinishPaste64(XtermWidget xw)
2159d522f475Smrg{
2160956cc18dSsnj    TScreen *screen = TScreenOf(xw);
2161956cc18dSsnj
2162956cc18dSsnj    TRACE(("FinishPaste64(%d)\n", screen->base64_paste));
2163956cc18dSsnj    if (screen->base64_paste) {
2164956cc18dSsnj	screen->base64_paste = 0;
2165956cc18dSsnj	unparseputc1(xw, screen->base64_final);
2166d522f475Smrg	unparse_end(xw);
2167d522f475Smrg    }
2168d522f475Smrg}
2169d522f475Smrg#endif
2170d522f475Smrg
2171d522f475Smrg#if !OPT_PASTE64
2172d522f475Smrgstatic
2173d522f475Smrg#endif
2174d522f475Smrgvoid
2175d522f475SmrgxtermGetSelection(Widget w,
2176d522f475Smrg		  Time ev_time,
2177e0a2b6dfSmrg		  String *params,	/* selections in precedence order */
2178d522f475Smrg		  Cardinal num_params,
2179894e0ac8Smrg		  Atom *targets)
2180d522f475Smrg{
2181d522f475Smrg    Atom selection;
2182d522f475Smrg    int cutbuffer;
2183d522f475Smrg    Atom target;
2184d522f475Smrg
2185956cc18dSsnj    XtermWidget xw;
2186956cc18dSsnj
218720d2c4d2Smrg    if (num_params == 0)
218820d2c4d2Smrg	return;
2189956cc18dSsnj    if ((xw = getXtermWidget(w)) == 0)
2190d522f475Smrg	return;
2191d522f475Smrg
2192e0a2b6dfSmrg    TRACE(("xtermGetSelection num_params %d @%ld\n", num_params, ev_time));
2193956cc18dSsnj    params = MapSelections(xw, params, num_params);
2194d522f475Smrg
2195d522f475Smrg    XmuInternStrings(XtDisplay(w), params, (Cardinal) 1, &selection);
2196d522f475Smrg    cutbuffer = CutBuffer(selection);
2197d522f475Smrg
2198956cc18dSsnj    TRACE(("Cutbuffer: %d, target: %s\n", cutbuffer,
2199956cc18dSsnj	   (targets
2200956cc18dSsnj	    ? visibleSelectionTarget(XtDisplay(w), targets[0])
2201956cc18dSsnj	    : "None")));
2202d522f475Smrg
2203d522f475Smrg    if (cutbuffer >= 0) {
2204d522f475Smrg	int inbytes;
2205d522f475Smrg	unsigned long nbytes;
2206d522f475Smrg	int fmt8 = 8;
2207d522f475Smrg	Atom type = XA_STRING;
2208d522f475Smrg	char *line;
2209d522f475Smrg
2210d522f475Smrg	/* 'line' is freed in SelectionReceived */
2211d522f475Smrg	line = XFetchBuffer(XtDisplay(w), &inbytes, cutbuffer);
2212d522f475Smrg	nbytes = (unsigned long) inbytes;
2213d522f475Smrg
22140bd37d32Smrg	if (nbytes > 0) {
2215d522f475Smrg	    SelectionReceived(w, NULL, &selection, &type, (XtPointer) line,
2216d522f475Smrg			      &nbytes, &fmt8);
22170bd37d32Smrg	} else if (num_params > 1) {
2218d522f475Smrg	    xtermGetSelection(w, ev_time, params + 1, num_params - 1, NULL);
2219d522f475Smrg	}
2220d522f475Smrg#if OPT_PASTE64
2221d522f475Smrg	else {
2222956cc18dSsnj	    FinishPaste64(xw);
2223d522f475Smrg	}
2224d522f475Smrg#endif
2225d522f475Smrg    } else {
2226d522f475Smrg
2227d522f475Smrg	if (targets == NULL || targets[0] == None) {
2228d522f475Smrg	    targets = _SelectionTargets(w);
2229d522f475Smrg	}
2230d522f475Smrg
2231d522f475Smrg	if (targets != 0) {
22322e4f8982Smrg	    struct _SelectionList *list;
22332e4f8982Smrg
2234d522f475Smrg	    target = targets[0];
2235d522f475Smrg
2236d522f475Smrg	    if (targets[1] == None) {	/* last target in list */
2237d522f475Smrg		params++;
2238d522f475Smrg		num_params--;
2239d522f475Smrg		targets = _SelectionTargets(w);
2240d522f475Smrg	    } else {
2241d522f475Smrg		targets = &(targets[1]);
2242d522f475Smrg	    }
2243d522f475Smrg
2244d522f475Smrg	    if (num_params) {
2245d522f475Smrg		/* 'list' is freed in SelectionReceived */
2246a1f3da82Smrg		list = TypeXtMalloc(struct _SelectionList);
2247d522f475Smrg		if (list != 0) {
2248d522f475Smrg		    list->params = params;
2249d522f475Smrg		    list->count = num_params;
2250d522f475Smrg		    list->targets = targets;
2251d522f475Smrg		    list->time = ev_time;
2252d522f475Smrg		}
2253d522f475Smrg	    } else {
2254d522f475Smrg		list = NULL;
2255d522f475Smrg	    }
2256d522f475Smrg
2257d522f475Smrg	    XtGetSelectionValue(w, selection,
2258d522f475Smrg				target,
2259d522f475Smrg				SelectionReceived,
2260d522f475Smrg				(XtPointer) list, ev_time);
2261d522f475Smrg	}
2262d522f475Smrg    }
2263d522f475Smrg}
2264d522f475Smrg
2265d522f475Smrg#if OPT_TRACE && OPT_WIDE_CHARS
2266d522f475Smrgstatic void
2267e0a2b6dfSmrgGettingSelection(Display *dpy, Atom type, Char *line, unsigned long len)
2268d522f475Smrg{
2269d522f475Smrg    Char *cp;
2270913cc679Smrg    const char *name = TraceAtomName(dpy, type);
2271d522f475Smrg
227201037d57Smrg    TRACE(("Getting %s (type=%ld, length=%ld)\n", name, (long int) type, len));
2273d522f475Smrg    for (cp = line; cp < line + len; cp++) {
2274956cc18dSsnj	TRACE(("[%d:%lu]", (int) (cp + 1 - line), len));
2275d522f475Smrg	if (isprint(*cp)) {
2276d522f475Smrg	    TRACE(("%c\n", *cp));
2277d522f475Smrg	} else {
2278d522f475Smrg	    TRACE(("\\x%02x\n", *cp));
2279d522f475Smrg	}
2280d522f475Smrg    }
2281d522f475Smrg}
2282d522f475Smrg#else
2283d522f475Smrg#define GettingSelection(dpy,type,line,len)	/* nothing */
2284d522f475Smrg#endif
2285d522f475Smrg
2286d522f475Smrg#ifdef VMS
2287d522f475Smrg#  define tty_vwrite(pty,lag,l)		tt_write(lag,l)
2288d522f475Smrg#else /* !( VMS ) */
2289d522f475Smrg#  define tty_vwrite(pty,lag,l)		v_write(pty,lag,l)
2290d522f475Smrg#endif /* defined VMS */
2291d522f475Smrg
2292d522f475Smrg#if OPT_PASTE64
2293d522f475Smrg/* Return base64 code character given 6-bit number */
2294d522f475Smrgstatic const char base64_code[] = "\
2295d522f475SmrgABCDEFGHIJKLMNOPQRSTUVWXYZ\
2296d522f475Smrgabcdefghijklmnopqrstuvwxyz\
2297d522f475Smrg0123456789+/";
2298d522f475Smrgstatic void
2299e0a2b6dfSmrgbase64_flush(TScreen *screen)
2300d522f475Smrg{
2301d522f475Smrg    Char x;
230201037d57Smrg
230301037d57Smrg    TRACE(("base64_flush count %d, pad %d (%d)\n",
230401037d57Smrg	   screen->base64_count,
230501037d57Smrg	   screen->base64_pad,
230601037d57Smrg	   screen->base64_pad & 3));
230701037d57Smrg
2308d522f475Smrg    switch (screen->base64_count) {
2309d522f475Smrg    case 0:
2310d522f475Smrg	break;
2311d522f475Smrg    case 2:
23122eaa94a1Schristos	x = CharOf(base64_code[screen->base64_accu << 4]);
2313d522f475Smrg	tty_vwrite(screen->respond, &x, 1);
2314d522f475Smrg	break;
2315d522f475Smrg    case 4:
23162eaa94a1Schristos	x = CharOf(base64_code[screen->base64_accu << 2]);
2317d522f475Smrg	tty_vwrite(screen->respond, &x, 1);
2318d522f475Smrg	break;
2319d522f475Smrg    }
232001037d57Smrg    if (screen->base64_pad & 3) {
2321d522f475Smrg	tty_vwrite(screen->respond,
2322492d43a5Smrg		   (const Char *) "===",
232301037d57Smrg		   (unsigned) (3 - (screen->base64_pad & 3)));
232401037d57Smrg    }
2325d522f475Smrg    screen->base64_count = 0;
2326d522f475Smrg    screen->base64_accu = 0;
2327d522f475Smrg    screen->base64_pad = 0;
2328d522f475Smrg}
2329d522f475Smrg#endif /* OPT_PASTE64 */
2330d522f475Smrg
2331e0a2b6dfSmrg/*
2332e0a2b6dfSmrg * Translate ISO-8859-1 or UTF-8 data to NRCS.
2333e0a2b6dfSmrg */
2334d522f475Smrgstatic void
2335f2e35a3aSmrgToNational(XtermWidget xw, Char *buffer, unsigned *length)
2336e0a2b6dfSmrg{
2337f2e35a3aSmrg    TScreen *screen = TScreenOf(xw);
2338f2e35a3aSmrg    DECNRCM_codes gsetL = screen->gsets[screen->curgl];
2339f2e35a3aSmrg    DECNRCM_codes gsetR = screen->gsets[screen->curgr];
2340e0a2b6dfSmrg
2341e0a2b6dfSmrg#if OPT_WIDE_CHARS
2342e0a2b6dfSmrg    if ((screen->utf8_nrc_mode | screen->utf8_mode) != uFalse) {
23432e4f8982Smrg	Char *p;
2344e0a2b6dfSmrg	PtyData *data = TypeXtMallocX(PtyData, *length);
2345e0a2b6dfSmrg
2346e0a2b6dfSmrg	memset(data, 0, sizeof(*data));
2347e0a2b6dfSmrg	data->next = data->buffer;
2348e0a2b6dfSmrg	data->last = data->buffer + *length;
2349e0a2b6dfSmrg	memcpy(data->buffer, buffer, (size_t) *length);
2350e0a2b6dfSmrg	p = buffer;
2351e0a2b6dfSmrg	while (data->next < data->last) {
23522e4f8982Smrg	    unsigned chr, out, gl, gr;
23532e4f8982Smrg
2354894e0ac8Smrg	    if (!decodeUtf8(screen, data)) {
2355e0a2b6dfSmrg		data->utf_size = 1;
2356e0a2b6dfSmrg		data->utf_data = data->next[0];
2357e0a2b6dfSmrg	    }
2358e0a2b6dfSmrg	    data->next += data->utf_size;
2359e0a2b6dfSmrg	    chr = data->utf_data;
2360e0a2b6dfSmrg	    out = chr;
2361f2e35a3aSmrg	    if ((gl = xtermCharSetIn(xw, chr, gsetL)) != chr) {
2362e0a2b6dfSmrg		out = gl;
2363f2e35a3aSmrg	    } else if ((gr = xtermCharSetIn(xw, chr, gsetR)) != chr) {
2364e0a2b6dfSmrg		out = gr;
2365e0a2b6dfSmrg	    }
2366e0a2b6dfSmrg	    *p++ = (Char) ((out < 256) ? out : ' ');
2367e0a2b6dfSmrg	}
2368e0a2b6dfSmrg	*length = (unsigned) (p - buffer);
2369e0a2b6dfSmrg	free(data);
2370e0a2b6dfSmrg    } else
2371e0a2b6dfSmrg#endif
2372e0a2b6dfSmrg    {
23732e4f8982Smrg	Char *p;
23742e4f8982Smrg
2375e0a2b6dfSmrg	for (p = buffer; (int) (p - buffer) < (int) *length; ++p) {
23762e4f8982Smrg	    unsigned gl, gr;
23772e4f8982Smrg	    unsigned chr = *p;
23782e4f8982Smrg	    unsigned out = chr;
2379f2e35a3aSmrg	    if ((gl = xtermCharSetIn(xw, chr, gsetL)) != chr) {
2380e0a2b6dfSmrg		out = gl;
2381f2e35a3aSmrg	    } else if ((gr = xtermCharSetIn(xw, chr, gsetR)) != chr) {
2382e0a2b6dfSmrg		out = gr;
2383e0a2b6dfSmrg	    }
2384e0a2b6dfSmrg	    *p = (Char) out;
2385e0a2b6dfSmrg	}
2386e0a2b6dfSmrg    }
2387e0a2b6dfSmrg}
2388e0a2b6dfSmrg
2389e0a2b6dfSmrgstatic void
2390e0a2b6dfSmrg_qWriteSelectionData(XtermWidget xw, Char *lag, unsigned length)
2391d522f475Smrg{
23920bd37d32Smrg    TScreen *screen = TScreenOf(xw);
23930bd37d32Smrg
2394e0a2b6dfSmrg    /*
2395e0a2b6dfSmrg     * If we are pasting into a window which is using NRCS, we want to map
2396e0a2b6dfSmrg     * the text from the normal encoding (ISO-8859-1 or UTF-8) into the coding
2397e0a2b6dfSmrg     * that an application would use to write characters with NRCS.
2398e0a2b6dfSmrg     *
2399e0a2b6dfSmrg     * TODO: handle conversion from UTF-8, and adjust length.  This can be done
2400e0a2b6dfSmrg     * in the same buffer because the target is always 8-bit.
2401e0a2b6dfSmrg     */
2402e0a2b6dfSmrg    if ((xw->flags & NATIONAL) && (length != 0)) {
2403f2e35a3aSmrg	ToNational(xw, lag, &length);
2404e0a2b6dfSmrg    }
2405d522f475Smrg#if OPT_PASTE64
2406d522f475Smrg    if (screen->base64_paste) {
2407d522f475Smrg	/* Send data as base64 */
2408d522f475Smrg	Char *p = lag;
2409d522f475Smrg	Char buf[64];
2410d522f475Smrg	unsigned x = 0;
24110bd37d32Smrg
241201037d57Smrg	TRACE(("convert to base64 %d:%s\n", length, visibleChars(p, length)));
241301037d57Smrg
24140bd37d32Smrg	/*
24150bd37d32Smrg	 * Handle the case where the selection is from _this_ xterm, which
24160bd37d32Smrg	 * puts part of the reply in the buffer before the selection callback
24170bd37d32Smrg	 * happens.
24180bd37d32Smrg	 */
24190bd37d32Smrg	if (screen->base64_paste && screen->unparse_len) {
24200bd37d32Smrg	    unparse_end(xw);
24210bd37d32Smrg	}
2422d522f475Smrg	while (length--) {
2423d522f475Smrg	    switch (screen->base64_count) {
2424d522f475Smrg	    case 0:
24252eaa94a1Schristos		buf[x++] = CharOf(base64_code[*p >> 2]);
24262eaa94a1Schristos		screen->base64_accu = (unsigned) (*p & 0x3);
2427d522f475Smrg		screen->base64_count = 2;
2428d522f475Smrg		++p;
2429d522f475Smrg		break;
2430d522f475Smrg	    case 2:
24312eaa94a1Schristos		buf[x++] = CharOf(base64_code[(screen->base64_accu << 4) +
24322eaa94a1Schristos					      (*p >> 4)]);
24332eaa94a1Schristos		screen->base64_accu = (unsigned) (*p & 0xF);
2434d522f475Smrg		screen->base64_count = 4;
2435d522f475Smrg		++p;
2436d522f475Smrg		break;
2437d522f475Smrg	    case 4:
24382eaa94a1Schristos		buf[x++] = CharOf(base64_code[(screen->base64_accu << 2) +
24392eaa94a1Schristos					      (*p >> 6)]);
24402eaa94a1Schristos		buf[x++] = CharOf(base64_code[*p & 0x3F]);
2441d522f475Smrg		screen->base64_accu = 0;
2442d522f475Smrg		screen->base64_count = 0;
2443d522f475Smrg		++p;
2444d522f475Smrg		break;
2445d522f475Smrg	    }
2446d522f475Smrg	    if (x >= 63) {
2447d522f475Smrg		/* Write 63 or 64 characters */
2448d522f475Smrg		screen->base64_pad += x;
244901037d57Smrg		TRACE(("writing base64 interim %s\n", visibleChars(buf, x)));
2450d522f475Smrg		tty_vwrite(screen->respond, buf, x);
2451d522f475Smrg		x = 0;
2452d522f475Smrg	    }
2453d522f475Smrg	}
2454d522f475Smrg	if (x != 0) {
2455d522f475Smrg	    screen->base64_pad += x;
245601037d57Smrg	    TRACE(("writing base64 finish %s\n", visibleChars(buf, x)));
2457d522f475Smrg	    tty_vwrite(screen->respond, buf, x);
2458d522f475Smrg	}
2459d522f475Smrg    } else
2460d522f475Smrg#endif /* OPT_PASTE64 */
2461d522f475Smrg#if OPT_READLINE
2462d522f475Smrg    if (SCREEN_FLAG(screen, paste_quotes)) {
2463d522f475Smrg	while (length--) {
2464492d43a5Smrg	    tty_vwrite(screen->respond, (const Char *) "\026", 1);	/* Control-V */
2465d522f475Smrg	    tty_vwrite(screen->respond, lag++, 1);
2466d522f475Smrg	}
2467d522f475Smrg    } else
2468d522f475Smrg#endif
246901037d57Smrg    {
247001037d57Smrg	TRACE(("writing base64 padding %s\n", visibleChars(lag, length)));
2471d522f475Smrg	tty_vwrite(screen->respond, lag, length);
247201037d57Smrg    }
2473d522f475Smrg}
2474d522f475Smrg
2475d522f475Smrgstatic void
2476e0a2b6dfSmrg_WriteSelectionData(XtermWidget xw, Char *line, size_t length)
2477d522f475Smrg{
2478d522f475Smrg    /* Write data to pty a line at a time. */
2479d522f475Smrg    /* Doing this one line at a time may no longer be necessary
2480d522f475Smrg       because v_write has been re-written. */
2481d522f475Smrg
24822e4f8982Smrg#if OPT_PASTE64
24830bd37d32Smrg    TScreen *screen = TScreenOf(xw);
24842e4f8982Smrg#endif
2485d522f475Smrg    Char *lag, *end;
2486d522f475Smrg
2487d522f475Smrg    /* in the VMS version, if tt_pasting isn't set to True then qio
2488d522f475Smrg       reads aren't blocked and an infinite loop is entered, where the
2489d522f475Smrg       pasted text shows up as new input, goes in again, shows up
2490d522f475Smrg       again, ad nauseum. */
2491d522f475Smrg#ifdef VMS
2492d522f475Smrg    tt_pasting = True;
2493d522f475Smrg#endif
2494d522f475Smrg
2495d522f475Smrg    end = &line[length];
2496d522f475Smrg    lag = line;
2497d522f475Smrg
2498d522f475Smrg#if OPT_PASTE64
2499d522f475Smrg    if (screen->base64_paste) {
25000bd37d32Smrg	_qWriteSelectionData(xw, lag, (unsigned) (end - lag));
2501d522f475Smrg	base64_flush(screen);
2502d522f475Smrg    } else
2503d522f475Smrg#endif
2504d522f475Smrg    {
2505d522f475Smrg	if (!SCREEN_FLAG(screen, paste_literal_nl)) {
2506d522f475Smrg	    Char *cp;
2507d522f475Smrg	    for (cp = line; cp != end; cp++) {
2508d522f475Smrg		if (*cp == '\n') {
2509d522f475Smrg		    *cp = '\r';
25100bd37d32Smrg		    _qWriteSelectionData(xw, lag, (unsigned) (cp - lag + 1));
2511d522f475Smrg		    lag = cp + 1;
2512d522f475Smrg		}
2513d522f475Smrg	    }
2514d522f475Smrg	}
2515d522f475Smrg
2516d522f475Smrg	if (lag != end) {
25170bd37d32Smrg	    _qWriteSelectionData(xw, lag, (unsigned) (end - lag));
2518d522f475Smrg	}
2519d522f475Smrg    }
2520d522f475Smrg#ifdef VMS
2521d522f475Smrg    tt_pasting = False;
2522d522f475Smrg    tt_start_read();		/* reenable reads or a character may be lost */
2523d522f475Smrg#endif
2524d522f475Smrg}
2525d522f475Smrg
2526f2e35a3aSmrg#if OPT_PASTE64 || OPT_READLINE
2527d522f475Smrgstatic void
2528e0a2b6dfSmrg_WriteKey(TScreen *screen, const Char *in)
2529d522f475Smrg{
2530d522f475Smrg    Char line[16];
2531d522f475Smrg    unsigned count = 0;
2532492d43a5Smrg    size_t length = strlen((const char *) in);
2533d522f475Smrg
2534d522f475Smrg    if (screen->control_eight_bits) {
2535d522f475Smrg	line[count++] = ANSI_CSI;
2536d522f475Smrg    } else {
2537d522f475Smrg	line[count++] = ANSI_ESC;
2538d522f475Smrg	line[count++] = '[';
2539d522f475Smrg    }
2540d522f475Smrg    while (length--)
2541d522f475Smrg	line[count++] = *in++;
2542d522f475Smrg    line[count++] = '~';
2543d522f475Smrg    tty_vwrite(screen->respond, line, count);
2544d522f475Smrg}
2545d522f475Smrg#endif /* OPT_READLINE */
2546d522f475Smrg
25470bd37d32Smrg/*
25480bd37d32Smrg * Unless enabled by the user, strip control characters other than formatting.
25490bd37d32Smrg */
25500bd37d32Smrgstatic size_t
25510bd37d32SmrgremoveControls(XtermWidget xw, char *value)
25520bd37d32Smrg{
25530bd37d32Smrg    TScreen *screen = TScreenOf(xw);
25540bd37d32Smrg    size_t dst = 0;
25550bd37d32Smrg
25560bd37d32Smrg    if (screen->allowPasteControls) {
25570bd37d32Smrg	dst = strlen(value);
25580bd37d32Smrg    } else {
25592e4f8982Smrg	size_t src = 0;
25600bd37d32Smrg	while ((value[dst] = value[src]) != '\0') {
25610bd37d32Smrg	    int ch = CharOf(value[src++]);
2562f2e35a3aSmrg
2563f2e35a3aSmrg#define ReplacePaste(n) \
2564f2e35a3aSmrg	    if (screen->disallow_paste_controls[n]) \
2565f2e35a3aSmrg		value[dst] = ' '
2566f2e35a3aSmrg
25670bd37d32Smrg	    if (ch < 32) {
2568f2e35a3aSmrg		ReplacePaste(epC0);
2569f2e35a3aSmrg		ReplacePaste(ch);
2570f2e35a3aSmrg		++dst;
2571f2e35a3aSmrg	    } else if (ch == ANSI_DEL) {
2572f2e35a3aSmrg		ReplacePaste(epDEL);
2573f2e35a3aSmrg		++dst;
25740bd37d32Smrg	    }
25750bd37d32Smrg#if OPT_WIDE_CHARS
2576e0a2b6dfSmrg	    else if (screen->utf8_inparse || screen->utf8_nrc_mode)
25770bd37d32Smrg		++dst;
25780bd37d32Smrg#endif
25790bd37d32Smrg#if OPT_C1_PRINT || OPT_WIDE_CHARS
25800bd37d32Smrg	    else if (screen->c1_printable)
25810bd37d32Smrg		++dst;
25820bd37d32Smrg#endif
25830bd37d32Smrg	    else if (ch >= 128 && ch < 160)
25840bd37d32Smrg		continue;
25850bd37d32Smrg	    else
25860bd37d32Smrg		++dst;
25870bd37d32Smrg	}
25880bd37d32Smrg    }
25890bd37d32Smrg    return dst;
25900bd37d32Smrg}
25910bd37d32Smrg
2592f2e35a3aSmrg#if OPT_SELECTION_OPS
2593f2e35a3aSmrgstatic void
2594f2e35a3aSmrgbeginInternalSelect(XtermWidget xw)
2595f2e35a3aSmrg{
2596f2e35a3aSmrg    TScreen *screen = TScreenOf(xw);
2597f2e35a3aSmrg    InternalSelect *mydata = &(screen->internal_select);
2598f2e35a3aSmrg
2599f2e35a3aSmrg    (void) mydata;
2600f2e35a3aSmrg    /* override flags so that SelectionReceived only updates a buffer */
2601f2e35a3aSmrg#if OPT_PASTE64
2602f2e35a3aSmrg    mydata->base64_paste = screen->base64_paste;
2603f2e35a3aSmrg    screen->base64_paste = 0;
2604f2e35a3aSmrg#endif
2605f2e35a3aSmrg#if OPT_PASTE64 || OPT_READLINE
2606f2e35a3aSmrg    mydata->paste_brackets = screen->paste_brackets;
2607f2e35a3aSmrg    SCREEN_FLAG_unset(screen, paste_brackets);
2608f2e35a3aSmrg#endif
2609f2e35a3aSmrg}
2610f2e35a3aSmrg
2611f2e35a3aSmrgstatic void
2612f2e35a3aSmrgfinishInternalSelect(XtermWidget xw)
2613f2e35a3aSmrg{
2614f2e35a3aSmrg    TScreen *screen = TScreenOf(xw);
2615f2e35a3aSmrg    InternalSelect *mydata = &(screen->internal_select);
2616f2e35a3aSmrg
2617f2e35a3aSmrg    (void) mydata;
2618f2e35a3aSmrg#if OPT_PASTE64
2619f2e35a3aSmrg    screen->base64_paste = mydata->base64_paste;
2620f2e35a3aSmrg#endif
2621f2e35a3aSmrg#if OPT_PASTE64 || OPT_READLINE
2622f2e35a3aSmrg    screen->paste_brackets = mydata->paste_brackets;
2623f2e35a3aSmrg#endif
2624f2e35a3aSmrg}
2625f2e35a3aSmrg
2626f2e35a3aSmrg#else
2627f2e35a3aSmrg#define finishInternalSelect(xw)	/* nothing */
2628f2e35a3aSmrg#endif /* OPT_SELECTION_OPS */
2629f2e35a3aSmrg
2630d522f475Smrg/* SelectionReceived: stuff received selection text into pty */
2631d522f475Smrg
2632d522f475Smrg/* ARGSUSED */
2633d522f475Smrgstatic void
2634d522f475SmrgSelectionReceived(Widget w,
2635d522f475Smrg		  XtPointer client_data,
2636894e0ac8Smrg		  Atom *selection GCC_UNUSED,
2637894e0ac8Smrg		  Atom *type,
2638d522f475Smrg		  XtPointer value,
2639d522f475Smrg		  unsigned long *length,
2640d522f475Smrg		  int *format)
2641d522f475Smrg{
2642d522f475Smrg    char **text_list = NULL;
26432e4f8982Smrg    int text_list_count = 0;
2644d522f475Smrg    XTextProperty text_prop;
2645d522f475Smrg    TScreen *screen;
2646d522f475Smrg    Display *dpy;
2647d522f475Smrg#if OPT_TRACE && OPT_WIDE_CHARS
2648d522f475Smrg    Char *line = (Char *) value;
2649d522f475Smrg#endif
2650d522f475Smrg
2651956cc18dSsnj    XtermWidget xw;
2652956cc18dSsnj
2653956cc18dSsnj    if ((xw = getXtermWidget(w)) == 0)
2654d522f475Smrg	return;
2655956cc18dSsnj
2656956cc18dSsnj    screen = TScreenOf(xw);
2657d522f475Smrg    dpy = XtDisplay(w);
2658d522f475Smrg
2659d522f475Smrg    if (*type == 0		/*XT_CONVERT_FAIL */
2660d522f475Smrg	|| *length == 0
266101037d57Smrg	|| value == NULL) {
266201037d57Smrg	TRACE(("...no data to convert\n"));
2663d522f475Smrg	goto fail;
266401037d57Smrg    }
2665d522f475Smrg
2666d522f475Smrg    text_prop.value = (unsigned char *) value;
2667d522f475Smrg    text_prop.encoding = *type;
2668d522f475Smrg    text_prop.format = *format;
2669d522f475Smrg    text_prop.nitems = *length;
2670d522f475Smrg
267101037d57Smrg    TRACE(("SelectionReceived %s %s format %d, nitems %ld\n",
2672913cc679Smrg	   TraceAtomName(screen->display, *selection),
2673956cc18dSsnj	   visibleSelectionTarget(dpy, text_prop.encoding),
2674956cc18dSsnj	   text_prop.format,
2675956cc18dSsnj	   text_prop.nitems));
2676956cc18dSsnj
2677d522f475Smrg#if OPT_WIDE_CHARS
2678e39b573cSmrg    if (XSupportsLocale() && screen->wide_chars) {
2679d522f475Smrg	if (*type == XA_UTF8_STRING(dpy) ||
2680d522f475Smrg	    *type == XA_STRING ||
2681d522f475Smrg	    *type == XA_COMPOUND_TEXT(dpy)) {
2682d522f475Smrg	    GettingSelection(dpy, *type, line, *length);
2683d522f475Smrg	    if (Xutf8TextPropertyToTextList(dpy, &text_prop,
2684d522f475Smrg					    &text_list,
2685d522f475Smrg					    &text_list_count) < 0) {
2686e39b573cSmrg		TRACE(("default Xutf8 Conversion failed\n"));
2687d522f475Smrg		text_list = NULL;
2688d522f475Smrg	    }
2689d522f475Smrg	}
2690d522f475Smrg    } else
2691d522f475Smrg#endif /* OPT_WIDE_CHARS */
2692d522f475Smrg    {
2693d522f475Smrg	/* Convert the selection to locale's multibyte encoding. */
2694d522f475Smrg
2695d522f475Smrg	if (*type == XA_UTF8_STRING(dpy) ||
2696d522f475Smrg	    *type == XA_STRING ||
2697d522f475Smrg	    *type == XA_COMPOUND_TEXT(dpy)) {
2698d522f475Smrg	    Status rc;
2699d522f475Smrg
2700d522f475Smrg	    GettingSelection(dpy, *type, line, *length);
2701d522f475Smrg
2702d522f475Smrg#if OPT_WIDE_CHARS
2703d522f475Smrg	    if (*type == XA_UTF8_STRING(dpy) &&
2704d522f475Smrg		!(screen->wide_chars || screen->c1_printable)) {
270520d2c4d2Smrg		rc = xtermUtf8ToTextList(xw, &text_prop,
270620d2c4d2Smrg					 &text_list, &text_list_count);
2707d522f475Smrg	    } else
2708d522f475Smrg#endif
2709e39b573cSmrg	    if (*type == XA_STRING && (!XSupportsLocale() || screen->brokenSelections)) {
2710d522f475Smrg		rc = XTextPropertyToStringList(&text_prop,
2711d522f475Smrg					       &text_list, &text_list_count);
2712d522f475Smrg	    } else {
2713d522f475Smrg		rc = XmbTextPropertyToTextList(dpy, &text_prop,
2714d522f475Smrg					       &text_list,
2715d522f475Smrg					       &text_list_count);
2716d522f475Smrg	    }
2717d522f475Smrg	    if (rc < 0) {
2718d522f475Smrg		TRACE(("Conversion failed\n"));
2719d522f475Smrg		text_list = NULL;
2720d522f475Smrg	    }
2721d522f475Smrg	}
2722d522f475Smrg    }
2723d522f475Smrg
2724d522f475Smrg    if (text_list != NULL && text_list_count != 0) {
2725d522f475Smrg	int i;
2726d522f475Smrg
2727d522f475Smrg#if OPT_PASTE64
2728d522f475Smrg	if (screen->base64_paste) {
2729a1f3da82Smrg	    /* EMPTY */ ;
2730d522f475Smrg	} else
2731d522f475Smrg#endif
2732f2e35a3aSmrg#if OPT_PASTE64 || OPT_READLINE
2733f2e35a3aSmrg	if (SCREEN_FLAG(screen, paste_brackets) && !screen->selectToBuffer) {
2734492d43a5Smrg	    _WriteKey(screen, (const Char *) "200");
2735d522f475Smrg	}
2736d522f475Smrg#endif
2737d522f475Smrg	for (i = 0; i < text_list_count; i++) {
27380bd37d32Smrg	    size_t len = removeControls(xw, text_list[i]);
273901037d57Smrg
27400bd37d32Smrg	    if (screen->selectToBuffer) {
274101037d57Smrg		InternalSelect *mydata = &(screen->internal_select);
2742f2e35a3aSmrg		if (!mydata->done) {
2743f2e35a3aSmrg		    size_t have = (mydata->buffer
2744f2e35a3aSmrg				   ? strlen(mydata->buffer)
2745f2e35a3aSmrg				   : 0);
2746f2e35a3aSmrg		    size_t need = have + len + 1;
2747f2e35a3aSmrg		    char *buffer = realloc(mydata->buffer, need);
2748f2e35a3aSmrg
2749f2e35a3aSmrg		    if (buffer != 0) {
2750f2e35a3aSmrg			strcpy(buffer + have, text_list[i]);
2751f2e35a3aSmrg			mydata->buffer = buffer;
2752f2e35a3aSmrg		    }
2753f2e35a3aSmrg		    TRACE(("FormatSelect %d.%d .. %d.%d %s\n",
2754f2e35a3aSmrg			   screen->startSel.row,
2755f2e35a3aSmrg			   screen->startSel.col,
2756f2e35a3aSmrg			   screen->endSel.row,
2757f2e35a3aSmrg			   screen->endSel.col,
2758f2e35a3aSmrg			   mydata->buffer));
2759f2e35a3aSmrg		    mydata->format_select(w, mydata->format, mydata->buffer,
2760f2e35a3aSmrg					  &(screen->startSel),
2761f2e35a3aSmrg					  &(screen->endSel));
2762f2e35a3aSmrg		    mydata->done = True;
27630bd37d32Smrg		}
276401037d57Smrg
27650bd37d32Smrg	    } else {
27660bd37d32Smrg		_WriteSelectionData(xw, (Char *) text_list[i], len);
27670bd37d32Smrg	    }
2768d522f475Smrg	}
2769d522f475Smrg#if OPT_PASTE64
2770d522f475Smrg	if (screen->base64_paste) {
2771956cc18dSsnj	    FinishPaste64(xw);
2772d522f475Smrg	} else
2773d522f475Smrg#endif
2774f2e35a3aSmrg#if OPT_PASTE64 || OPT_READLINE
2775f2e35a3aSmrg	if (SCREEN_FLAG(screen, paste_brackets) && !screen->selectToBuffer) {
2776492d43a5Smrg	    _WriteKey(screen, (const Char *) "201");
2777d522f475Smrg	}
2778d522f475Smrg#endif
2779f2e35a3aSmrg	if (screen->selectToBuffer) {
2780f2e35a3aSmrg	    InternalSelect *mydata = &(screen->internal_select);
2781f2e35a3aSmrg	    finishInternalSelect(xw);
2782f2e35a3aSmrg	    if (mydata->done) {
2783f2e35a3aSmrg		free(mydata->format);
2784f2e35a3aSmrg		free(mydata->buffer);
2785f2e35a3aSmrg		memset(mydata, 0, sizeof(*mydata));
2786f2e35a3aSmrg	    }
2787f2e35a3aSmrg	    screen->selectToBuffer = False;
2788f2e35a3aSmrg	}
2789d522f475Smrg	XFreeStringList(text_list);
279001037d57Smrg    } else {
279101037d57Smrg	TRACE(("...empty text-list\n"));
2792d522f475Smrg	goto fail;
279301037d57Smrg    }
2794d522f475Smrg
2795d522f475Smrg    XtFree((char *) client_data);
2796d522f475Smrg    XtFree((char *) value);
2797d522f475Smrg
2798d522f475Smrg    return;
2799d522f475Smrg
2800d522f475Smrg  fail:
2801d522f475Smrg    if (client_data != 0) {
2802d522f475Smrg	struct _SelectionList *list = (struct _SelectionList *) client_data;
2803956cc18dSsnj
2804956cc18dSsnj	TRACE(("SelectionReceived ->xtermGetSelection\n"));
2805d522f475Smrg	xtermGetSelection(w, list->time,
2806d522f475Smrg			  list->params, list->count, list->targets);
2807d522f475Smrg	XtFree((char *) client_data);
2808d522f475Smrg#if OPT_PASTE64
2809d522f475Smrg    } else {
2810956cc18dSsnj	FinishPaste64(xw);
2811d522f475Smrg#endif
2812d522f475Smrg    }
2813d522f475Smrg    return;
2814d522f475Smrg}
2815d522f475Smrg
2816d522f475Smrgvoid
2817d522f475SmrgHandleInsertSelection(Widget w,
2818894e0ac8Smrg		      XEvent *event,	/* assumed to be XButtonEvent* */
2819e0a2b6dfSmrg		      String *params,	/* selections in precedence order */
2820d522f475Smrg		      Cardinal *num_params)
2821d522f475Smrg{
2822956cc18dSsnj    XtermWidget xw;
2823d522f475Smrg
2824956cc18dSsnj    if ((xw = getXtermWidget(w)) != 0) {
2825f2e35a3aSmrg	TRACE_EVENT("HandleInsertSelection", event, params, num_params);
2826d522f475Smrg	if (!SendMousePosition(xw, event)) {
2827d522f475Smrg#if OPT_READLINE
2828d522f475Smrg	    int ldelta;
2829956cc18dSsnj	    TScreen *screen = TScreenOf(xw);
2830492d43a5Smrg	    if (IsBtnEvent(event)
2831f2e35a3aSmrg		&& !OverrideEvent(event)
2832913cc679Smrg		&& (okSendMousePos(xw) == MOUSE_OFF)
2833d522f475Smrg		&& SCREEN_FLAG(screen, paste_moves)
2834d522f475Smrg		&& rowOnCurrentLine(screen, eventRow(screen, event), &ldelta))
2835d522f475Smrg		ReadLineMovePoint(screen, eventColBetween(screen, event), ldelta);
2836d522f475Smrg#endif /* OPT_READLINE */
2837d522f475Smrg
2838d522f475Smrg	    xtermGetSelection(w, event->xbutton.time, params, *num_params, NULL);
2839d522f475Smrg	}
2840d522f475Smrg    }
2841d522f475Smrg}
2842d522f475Smrg
2843d522f475Smrgstatic SelectUnit
2844956cc18dSsnjEvalSelectUnit(XtermWidget xw,
2845d522f475Smrg	       Time buttonDownTime,
2846d522f475Smrg	       SelectUnit defaultUnit,
2847d522f475Smrg	       unsigned int button)
2848d522f475Smrg{
2849956cc18dSsnj    TScreen *screen = TScreenOf(xw);
2850d522f475Smrg    SelectUnit result;
2851d522f475Smrg    int delta;
2852d522f475Smrg
2853d522f475Smrg    if (button != screen->lastButton) {
285420d2c4d2Smrg	delta = screen->multiClickTime + 1;
2855d522f475Smrg    } else if (screen->lastButtonUpTime == (Time) 0) {
2856d522f475Smrg	/* first time and once in a blue moon */
2857d522f475Smrg	delta = screen->multiClickTime + 1;
2858d522f475Smrg    } else if (buttonDownTime > screen->lastButtonUpTime) {
2859d522f475Smrg	/* most of the time */
28602eaa94a1Schristos	delta = (int) (buttonDownTime - screen->lastButtonUpTime);
2861d522f475Smrg    } else {
2862d522f475Smrg	/* time has rolled over since lastButtonUpTime */
28632eaa94a1Schristos	delta = (int) ((((Time) ~ 0) - screen->lastButtonUpTime) + buttonDownTime);
2864d522f475Smrg    }
2865d522f475Smrg
2866d522f475Smrg    if (delta > screen->multiClickTime) {
2867d522f475Smrg	screen->numberOfClicks = 1;
2868d522f475Smrg	result = defaultUnit;
2869d522f475Smrg    } else {
2870d522f475Smrg	result = screen->selectMap[screen->numberOfClicks % screen->maxClicks];
2871d522f475Smrg	screen->numberOfClicks += 1;
2872d522f475Smrg    }
2873d522f475Smrg    TRACE(("EvalSelectUnit(%d) = %d\n", screen->numberOfClicks, result));
2874d522f475Smrg    return result;
2875d522f475Smrg}
2876d522f475Smrg
2877d522f475Smrgstatic void
2878d522f475Smrgdo_select_start(XtermWidget xw,
2879894e0ac8Smrg		XEvent *event,	/* must be XButtonEvent* */
2880e0a2b6dfSmrg		CELL *cell)
2881d522f475Smrg{
2882956cc18dSsnj    TScreen *screen = TScreenOf(xw);
2883d522f475Smrg
2884d522f475Smrg    if (SendMousePosition(xw, event))
2885d522f475Smrg	return;
2886956cc18dSsnj    screen->selectUnit = EvalSelectUnit(xw,
2887d522f475Smrg					event->xbutton.time,
2888d522f475Smrg					Select_CHAR,
2889d522f475Smrg					event->xbutton.button);
2890d522f475Smrg    screen->replyToEmacs = False;
2891d522f475Smrg
2892d522f475Smrg#if OPT_READLINE
2893d522f475Smrg    lastButtonDownTime = event->xbutton.time;
2894d522f475Smrg#endif
2895d522f475Smrg
2896d522f475Smrg    StartSelect(xw, cell);
2897d522f475Smrg}
2898d522f475Smrg
2899d522f475Smrg/* ARGSUSED */
2900d522f475Smrgvoid
2901d522f475SmrgHandleSelectStart(Widget w,
2902894e0ac8Smrg		  XEvent *event,	/* must be XButtonEvent* */
2903e0a2b6dfSmrg		  String *params GCC_UNUSED,
2904d522f475Smrg		  Cardinal *num_params GCC_UNUSED)
2905d522f475Smrg{
2906956cc18dSsnj    XtermWidget xw;
2907956cc18dSsnj
2908956cc18dSsnj    if ((xw = getXtermWidget(w)) != 0) {
2909956cc18dSsnj	TScreen *screen = TScreenOf(xw);
2910d522f475Smrg	CELL cell;
2911d522f475Smrg
2912f2e35a3aSmrg	TRACE_EVENT("HandleSelectStart", event, params, num_params);
2913d522f475Smrg	screen->firstValidRow = 0;
2914d522f475Smrg	screen->lastValidRow = screen->max_row;
2915d522f475Smrg	PointToCELL(screen, event->xbutton.y, event->xbutton.x, &cell);
2916d522f475Smrg
2917d522f475Smrg#if OPT_READLINE
2918d522f475Smrg	ExtendingSelection = 0;
2919d522f475Smrg#endif
2920d522f475Smrg
2921d522f475Smrg	do_select_start(xw, event, &cell);
2922d522f475Smrg    }
2923d522f475Smrg}
2924d522f475Smrg
2925d522f475Smrg/* ARGSUSED */
2926d522f475Smrgvoid
2927d522f475SmrgHandleKeyboardSelectStart(Widget w,
2928894e0ac8Smrg			  XEvent *event,	/* must be XButtonEvent* */
2929e0a2b6dfSmrg			  String *params GCC_UNUSED,
2930d522f475Smrg			  Cardinal *num_params GCC_UNUSED)
2931d522f475Smrg{
2932956cc18dSsnj    XtermWidget xw;
2933956cc18dSsnj
2934956cc18dSsnj    if ((xw = getXtermWidget(w)) != 0) {
2935956cc18dSsnj	TScreen *screen = TScreenOf(xw);
29360bd37d32Smrg
2937f2e35a3aSmrg	TRACE_EVENT("HandleKeyboardSelectStart", event, params, num_params);
2938d522f475Smrg	do_select_start(xw, event, &screen->cursorp);
2939d522f475Smrg    }
2940d522f475Smrg}
2941d522f475Smrg
2942d522f475Smrgstatic void
2943894e0ac8SmrgTrackDown(XtermWidget xw, XButtonEvent *event)
2944d522f475Smrg{
2945956cc18dSsnj    TScreen *screen = TScreenOf(xw);
2946d522f475Smrg    CELL cell;
2947d522f475Smrg
2948956cc18dSsnj    screen->selectUnit = EvalSelectUnit(xw,
2949d522f475Smrg					event->time,
2950d522f475Smrg					Select_CHAR,
2951d522f475Smrg					event->button);
2952d522f475Smrg    if (screen->numberOfClicks > 1) {
2953d522f475Smrg	PointToCELL(screen, event->y, event->x, &cell);
2954d522f475Smrg	screen->replyToEmacs = True;
2955d522f475Smrg	StartSelect(xw, &cell);
2956d522f475Smrg    } else {
2957d522f475Smrg	screen->waitingForTrackInfo = True;
2958492d43a5Smrg	EditorButton(xw, event);
2959d522f475Smrg    }
2960d522f475Smrg}
2961d522f475Smrg
2962d522f475Smrg#define boundsCheck(x)	if (x < 0) \
2963d522f475Smrg			    x = 0; \
2964d522f475Smrg			else if (x >= screen->max_row) \
2965d522f475Smrg			    x = screen->max_row
2966d522f475Smrg
2967d522f475Smrgvoid
2968d522f475SmrgTrackMouse(XtermWidget xw,
2969d522f475Smrg	   int func,
2970e0a2b6dfSmrg	   CELL *start,
2971d522f475Smrg	   int firstrow,
2972d522f475Smrg	   int lastrow)
2973d522f475Smrg{
2974956cc18dSsnj    TScreen *screen = TScreenOf(xw);
2975d522f475Smrg
2976d522f475Smrg    if (screen->waitingForTrackInfo) {	/* if Timed, ignore */
2977d522f475Smrg	screen->waitingForTrackInfo = False;
2978d522f475Smrg
2979d522f475Smrg	if (func != 0) {
2980d522f475Smrg	    CELL first = *start;
2981d522f475Smrg
2982d522f475Smrg	    boundsCheck(first.row);
2983d522f475Smrg	    boundsCheck(firstrow);
2984d522f475Smrg	    boundsCheck(lastrow);
2985d522f475Smrg	    screen->firstValidRow = firstrow;
2986d522f475Smrg	    screen->lastValidRow = lastrow;
2987d522f475Smrg	    screen->replyToEmacs = True;
2988d522f475Smrg	    StartSelect(xw, &first);
2989d522f475Smrg	}
2990d522f475Smrg    }
2991d522f475Smrg}
2992d522f475Smrg
2993d522f475Smrgstatic void
2994e0a2b6dfSmrgStartSelect(XtermWidget xw, const CELL *cell)
2995d522f475Smrg{
2996956cc18dSsnj    TScreen *screen = TScreenOf(xw);
2997d522f475Smrg
2998d522f475Smrg    TRACE(("StartSelect row=%d, col=%d\n", cell->row, cell->col));
2999d522f475Smrg    if (screen->cursor_state)
3000f2e35a3aSmrg	HideCursor(xw);
3001d522f475Smrg    if (screen->numberOfClicks == 1) {
3002d522f475Smrg	/* set start of selection */
3003d522f475Smrg	screen->rawPos = *cell;
3004d522f475Smrg    }
3005d522f475Smrg    /* else use old values in rawPos */
3006d522f475Smrg    screen->saveStartR = screen->startExt = screen->rawPos;
3007d522f475Smrg    screen->saveEndR = screen->endExt = screen->rawPos;
3008d522f475Smrg    if (Coordinate(screen, cell) < Coordinate(screen, &(screen->rawPos))) {
3009d522f475Smrg	screen->eventMode = LEFTEXTENSION;
3010d522f475Smrg	screen->startExt = *cell;
3011d522f475Smrg    } else {
3012d522f475Smrg	screen->eventMode = RIGHTEXTENSION;
3013d522f475Smrg	screen->endExt = *cell;
3014d522f475Smrg    }
3015f2e35a3aSmrg    ComputeSelect(xw, &(screen->startExt), &(screen->endExt), False, True);
3016d522f475Smrg}
3017d522f475Smrg
3018d522f475Smrgstatic void
3019d522f475SmrgEndExtend(XtermWidget xw,
3020894e0ac8Smrg	  XEvent *event,	/* must be XButtonEvent */
3021e0a2b6dfSmrg	  String *params,	/* selections */
3022d522f475Smrg	  Cardinal num_params,
3023d522f475Smrg	  Bool use_cursor_loc)
3024d522f475Smrg{
3025d522f475Smrg    CELL cell;
3026956cc18dSsnj    TScreen *screen = TScreenOf(xw);
3027d522f475Smrg
3028f2e35a3aSmrg    TRACE_EVENT("EndExtend", event, params, &num_params);
3029d522f475Smrg    if (use_cursor_loc) {
3030d522f475Smrg	cell = screen->cursorp;
3031d522f475Smrg    } else {
3032d522f475Smrg	PointToCELL(screen, event->xbutton.y, event->xbutton.x, &cell);
3033d522f475Smrg    }
3034d522f475Smrg    ExtendExtend(xw, &cell);
30352e4f8982Smrg
3036d522f475Smrg    screen->lastButtonUpTime = event->xbutton.time;
3037d522f475Smrg    screen->lastButton = event->xbutton.button;
30382e4f8982Smrg
3039d522f475Smrg    if (!isSameCELL(&(screen->startSel), &(screen->endSel))) {
3040d522f475Smrg	if (screen->replyToEmacs) {
30412e4f8982Smrg	    Char line[64];
30422e4f8982Smrg	    unsigned count = 0;
30432e4f8982Smrg
3044d522f475Smrg	    if (screen->control_eight_bits) {
3045d522f475Smrg		line[count++] = ANSI_CSI;
3046d522f475Smrg	    } else {
3047d522f475Smrg		line[count++] = ANSI_ESC;
3048d522f475Smrg		line[count++] = '[';
3049d522f475Smrg	    }
3050d522f475Smrg	    if (isSameCELL(&(screen->rawPos), &(screen->startSel))
3051d522f475Smrg		&& isSameCELL(&cell, &(screen->endSel))) {
3052d522f475Smrg		/* Use short-form emacs select */
30530bd37d32Smrg
30540bd37d32Smrg		switch (screen->extend_coords) {
30550bd37d32Smrg		case 0:
30560bd37d32Smrg		case SET_EXT_MODE_MOUSE:
30570bd37d32Smrg		    line[count++] = 't';
30580bd37d32Smrg		    break;
30590bd37d32Smrg		case SET_SGR_EXT_MODE_MOUSE:
3060f2e35a3aSmrg		case SET_PIXEL_POSITION_MOUSE:
30610bd37d32Smrg		    line[count++] = '<';
30620bd37d32Smrg		    break;
30630bd37d32Smrg		}
30640bd37d32Smrg
3065492d43a5Smrg		count = EmitMousePosition(screen, line, count, screen->endSel.col);
30660bd37d32Smrg		count = EmitMousePositionSeparator(screen, line, count);
3067492d43a5Smrg		count = EmitMousePosition(screen, line, count, screen->endSel.row);
30680bd37d32Smrg
30690bd37d32Smrg		switch (screen->extend_coords) {
30700bd37d32Smrg		case SET_SGR_EXT_MODE_MOUSE:
30710bd37d32Smrg		case SET_URXVT_EXT_MODE_MOUSE:
3072f2e35a3aSmrg		case SET_PIXEL_POSITION_MOUSE:
30730bd37d32Smrg		    line[count++] = 't';
30740bd37d32Smrg		    break;
30750bd37d32Smrg		}
3076d522f475Smrg	    } else {
3077d522f475Smrg		/* long-form, specify everything */
30780bd37d32Smrg
30790bd37d32Smrg		switch (screen->extend_coords) {
30800bd37d32Smrg		case 0:
30810bd37d32Smrg		case SET_EXT_MODE_MOUSE:
30820bd37d32Smrg		    line[count++] = 'T';
30830bd37d32Smrg		    break;
30840bd37d32Smrg		case SET_SGR_EXT_MODE_MOUSE:
3085f2e35a3aSmrg		case SET_PIXEL_POSITION_MOUSE:
30860bd37d32Smrg		    line[count++] = '<';
30870bd37d32Smrg		    break;
30880bd37d32Smrg		}
30890bd37d32Smrg
3090492d43a5Smrg		count = EmitMousePosition(screen, line, count, screen->startSel.col);
30910bd37d32Smrg		count = EmitMousePositionSeparator(screen, line, count);
3092492d43a5Smrg		count = EmitMousePosition(screen, line, count, screen->startSel.row);
30930bd37d32Smrg		count = EmitMousePositionSeparator(screen, line, count);
3094492d43a5Smrg		count = EmitMousePosition(screen, line, count, screen->endSel.col);
30950bd37d32Smrg		count = EmitMousePositionSeparator(screen, line, count);
3096492d43a5Smrg		count = EmitMousePosition(screen, line, count, screen->endSel.row);
30970bd37d32Smrg		count = EmitMousePositionSeparator(screen, line, count);
3098492d43a5Smrg		count = EmitMousePosition(screen, line, count, cell.col);
30990bd37d32Smrg		count = EmitMousePositionSeparator(screen, line, count);
3100492d43a5Smrg		count = EmitMousePosition(screen, line, count, cell.row);
31010bd37d32Smrg
31020bd37d32Smrg		switch (screen->extend_coords) {
31030bd37d32Smrg		case SET_SGR_EXT_MODE_MOUSE:
31040bd37d32Smrg		case SET_URXVT_EXT_MODE_MOUSE:
3105f2e35a3aSmrg		case SET_PIXEL_POSITION_MOUSE:
31060bd37d32Smrg		    line[count++] = 'T';
31070bd37d32Smrg		    break;
31080bd37d32Smrg		}
3109d522f475Smrg	    }
3110d522f475Smrg	    v_write(screen->respond, line, count);
3111f2e35a3aSmrg	    UnHiliteText(xw);
3112d522f475Smrg	}
3113d522f475Smrg    }
3114d522f475Smrg    SelectSet(xw, event, params, num_params);
3115d522f475Smrg    screen->eventMode = NORMAL;
3116d522f475Smrg}
3117d522f475Smrg
3118d522f475Smrgvoid
3119d522f475SmrgHandleSelectSet(Widget w,
3120894e0ac8Smrg		XEvent *event,
3121e0a2b6dfSmrg		String *params,
3122d522f475Smrg		Cardinal *num_params)
3123d522f475Smrg{
3124956cc18dSsnj    XtermWidget xw;
3125956cc18dSsnj
3126956cc18dSsnj    if ((xw = getXtermWidget(w)) != 0) {
3127f2e35a3aSmrg	TRACE_EVENT("HandleSelectSet", event, params, num_params);
3128956cc18dSsnj	SelectSet(xw, event, params, *num_params);
3129d522f475Smrg    }
3130d522f475Smrg}
3131d522f475Smrg
3132d522f475Smrg/* ARGSUSED */
3133d522f475Smrgstatic void
3134d522f475SmrgSelectSet(XtermWidget xw,
3135894e0ac8Smrg	  XEvent *event GCC_UNUSED,
3136e0a2b6dfSmrg	  String *params,
3137d522f475Smrg	  Cardinal num_params)
3138d522f475Smrg{
3139956cc18dSsnj    TScreen *screen = TScreenOf(xw);
3140d522f475Smrg
3141d522f475Smrg    TRACE(("SelectSet\n"));
3142d522f475Smrg    /* Only do select stuff if non-null select */
3143d522f475Smrg    if (!isSameCELL(&(screen->startSel), &(screen->endSel))) {
3144f2e35a3aSmrg	Cardinal n;
3145f2e35a3aSmrg	for (n = 0; n < num_params; ++n) {
3146f2e35a3aSmrg	    SaltTextAway(xw,
3147f2e35a3aSmrg			 TargetToSelection(screen, params[n]),
3148f2e35a3aSmrg			 &(screen->startSel), &(screen->endSel));
3149f2e35a3aSmrg	}
31500bd37d32Smrg	_OwnSelection(xw, params, num_params);
3151d522f475Smrg    } else {
31520bd37d32Smrg	ScrnDisownSelection(xw);
3153d522f475Smrg    }
3154d522f475Smrg}
3155d522f475Smrg
3156d522f475Smrg#define Abs(x)		((x) < 0 ? -(x) : (x))
3157d522f475Smrg
3158d522f475Smrg/* ARGSUSED */
3159d522f475Smrgstatic void
3160d522f475Smrgdo_start_extend(XtermWidget xw,
3161894e0ac8Smrg		XEvent *event,	/* must be XButtonEvent* */
3162e0a2b6dfSmrg		String *params GCC_UNUSED,
3163d522f475Smrg		Cardinal *num_params GCC_UNUSED,
3164d522f475Smrg		Bool use_cursor_loc)
3165d522f475Smrg{
3166956cc18dSsnj    TScreen *screen = TScreenOf(xw);
3167d522f475Smrg    int coord;
3168d522f475Smrg    CELL cell;
3169d522f475Smrg
3170d522f475Smrg    if (SendMousePosition(xw, event))
3171d522f475Smrg	return;
3172d522f475Smrg
3173d522f475Smrg    screen->firstValidRow = 0;
3174d522f475Smrg    screen->lastValidRow = screen->max_row;
3175d522f475Smrg#if OPT_READLINE
3176f2e35a3aSmrg    if (OverrideEvent(event)
3177d522f475Smrg	|| event->xbutton.button != Button3
3178d522f475Smrg	|| !(SCREEN_FLAG(screen, dclick3_deletes)))
3179d522f475Smrg#endif
3180956cc18dSsnj	screen->selectUnit = EvalSelectUnit(xw,
3181d522f475Smrg					    event->xbutton.time,
3182d522f475Smrg					    screen->selectUnit,
3183d522f475Smrg					    event->xbutton.button);
3184d522f475Smrg    screen->replyToEmacs = False;
3185d522f475Smrg
3186d522f475Smrg#if OPT_READLINE
3187f2e35a3aSmrg    CheckSecondPress3(xw, screen, event);
3188d522f475Smrg#endif
3189d522f475Smrg
3190d522f475Smrg    if (screen->numberOfClicks == 1
3191f2e35a3aSmrg	|| (SCREEN_FLAG(screen, dclick3_deletes)
3192f2e35a3aSmrg	    && !OverrideEvent(event))) {
3193d522f475Smrg	/* Save existing selection so we can reestablish it if the guy
3194d522f475Smrg	   extends past the other end of the selection */
3195d522f475Smrg	screen->saveStartR = screen->startExt = screen->startRaw;
3196d522f475Smrg	screen->saveEndR = screen->endExt = screen->endRaw;
3197d522f475Smrg    } else {
3198d522f475Smrg	/* He just needed the selection mode changed, use old values. */
3199d522f475Smrg	screen->startExt = screen->startRaw = screen->saveStartR;
3200d522f475Smrg	screen->endExt = screen->endRaw = screen->saveEndR;
3201d522f475Smrg    }
3202d522f475Smrg    if (use_cursor_loc) {
3203d522f475Smrg	cell = screen->cursorp;
3204d522f475Smrg    } else {
3205d522f475Smrg	PointToCELL(screen, event->xbutton.y, event->xbutton.x, &cell);
3206d522f475Smrg    }
3207d522f475Smrg    coord = Coordinate(screen, &cell);
3208d522f475Smrg
3209d522f475Smrg    if (Abs(coord - Coordinate(screen, &(screen->startSel)))
3210d522f475Smrg	< Abs(coord - Coordinate(screen, &(screen->endSel)))
3211d522f475Smrg	|| coord < Coordinate(screen, &(screen->startSel))) {
3212d522f475Smrg	/* point is close to left side of selection */
3213d522f475Smrg	screen->eventMode = LEFTEXTENSION;
3214d522f475Smrg	screen->startExt = cell;
3215d522f475Smrg    } else {
3216d522f475Smrg	/* point is close to left side of selection */
3217d522f475Smrg	screen->eventMode = RIGHTEXTENSION;
3218d522f475Smrg	screen->endExt = cell;
3219d522f475Smrg    }
3220f2e35a3aSmrg    ComputeSelect(xw, &(screen->startExt), &(screen->endExt), True, True);
3221d522f475Smrg
3222d522f475Smrg#if OPT_READLINE
3223d522f475Smrg    if (!isSameCELL(&(screen->startSel), &(screen->endSel)))
3224d522f475Smrg	ExtendingSelection = 1;
3225d522f475Smrg#endif
3226d522f475Smrg}
3227d522f475Smrg
3228d522f475Smrgstatic void
3229e0a2b6dfSmrgExtendExtend(XtermWidget xw, const CELL *cell)
3230d522f475Smrg{
3231956cc18dSsnj    TScreen *screen = TScreenOf(xw);
3232d522f475Smrg    int coord = Coordinate(screen, cell);
3233d522f475Smrg
3234d522f475Smrg    TRACE(("ExtendExtend row=%d, col=%d\n", cell->row, cell->col));
3235d522f475Smrg    if (screen->eventMode == LEFTEXTENSION
3236d522f475Smrg	&& ((coord + (screen->selectUnit != Select_CHAR))
3237d522f475Smrg	    > Coordinate(screen, &(screen->endSel)))) {
3238d522f475Smrg	/* Whoops, he's changed his mind.  Do RIGHTEXTENSION */
3239d522f475Smrg	screen->eventMode = RIGHTEXTENSION;
3240d522f475Smrg	screen->startExt = screen->saveStartR;
3241d522f475Smrg    } else if (screen->eventMode == RIGHTEXTENSION
3242d522f475Smrg	       && coord < Coordinate(screen, &(screen->startSel))) {
3243d522f475Smrg	/* Whoops, he's changed his mind.  Do LEFTEXTENSION */
3244d522f475Smrg	screen->eventMode = LEFTEXTENSION;
3245d522f475Smrg	screen->endExt = screen->saveEndR;
3246d522f475Smrg    }
3247d522f475Smrg    if (screen->eventMode == LEFTEXTENSION) {
3248d522f475Smrg	screen->startExt = *cell;
3249d522f475Smrg    } else {
3250d522f475Smrg	screen->endExt = *cell;
3251d522f475Smrg    }
3252f2e35a3aSmrg    ComputeSelect(xw, &(screen->startExt), &(screen->endExt), False, True);
3253d522f475Smrg
3254d522f475Smrg#if OPT_READLINE
3255d522f475Smrg    if (!isSameCELL(&(screen->startSel), &(screen->endSel)))
3256d522f475Smrg	ExtendingSelection = 1;
3257d522f475Smrg#endif
3258d522f475Smrg}
3259d522f475Smrg
3260d522f475Smrgvoid
3261d522f475SmrgHandleStartExtend(Widget w,
3262894e0ac8Smrg		  XEvent *event,	/* must be XButtonEvent* */
3263e0a2b6dfSmrg		  String *params,	/* unused */
3264d522f475Smrg		  Cardinal *num_params)		/* unused */
3265d522f475Smrg{
3266956cc18dSsnj    XtermWidget xw;
3267956cc18dSsnj
3268956cc18dSsnj    if ((xw = getXtermWidget(w)) != 0) {
3269f2e35a3aSmrg	TRACE_EVENT("HandleStartExtend", event, params, num_params);
3270956cc18dSsnj	do_start_extend(xw, event, params, num_params, False);
3271956cc18dSsnj    }
3272d522f475Smrg}
3273d522f475Smrg
3274d522f475Smrgvoid
3275d522f475SmrgHandleKeyboardStartExtend(Widget w,
3276894e0ac8Smrg			  XEvent *event,	/* must be XButtonEvent* */
3277e0a2b6dfSmrg			  String *params,	/* unused */
3278d522f475Smrg			  Cardinal *num_params)		/* unused */
3279d522f475Smrg{
3280956cc18dSsnj    XtermWidget xw;
3281956cc18dSsnj
3282956cc18dSsnj    if ((xw = getXtermWidget(w)) != 0) {
3283f2e35a3aSmrg	TRACE_EVENT("HandleKeyboardStartExtend", event, params, num_params);
3284956cc18dSsnj	do_start_extend(xw, event, params, num_params, True);
3285956cc18dSsnj    }
3286d522f475Smrg}
3287d522f475Smrg
3288d522f475Smrgvoid
3289e0a2b6dfSmrgScrollSelection(TScreen *screen, int amount, Bool always)
3290d522f475Smrg{
3291d522f475Smrg    int minrow = INX2ROW(screen, -screen->savedlines);
3292d522f475Smrg    int maxrow = INX2ROW(screen, screen->max_row);
3293d522f475Smrg    int maxcol = screen->max_col;
3294d522f475Smrg
3295d522f475Smrg#define scroll_update_one(cell) \
3296d522f475Smrg	(cell)->row += amount; \
3297d522f475Smrg	if ((cell)->row < minrow) { \
3298d522f475Smrg	    (cell)->row = minrow; \
3299d522f475Smrg	    (cell)->col = 0; \
3300d522f475Smrg	} \
3301d522f475Smrg	if ((cell)->row > maxrow) { \
3302d522f475Smrg	    (cell)->row = maxrow; \
3303d522f475Smrg	    (cell)->col = maxcol; \
3304d522f475Smrg	}
3305d522f475Smrg
3306d522f475Smrg    scroll_update_one(&(screen->startRaw));
3307d522f475Smrg    scroll_update_one(&(screen->endRaw));
3308d522f475Smrg    scroll_update_one(&(screen->startSel));
3309d522f475Smrg    scroll_update_one(&(screen->endSel));
3310d522f475Smrg
3311d522f475Smrg    scroll_update_one(&(screen->rawPos));
3312d522f475Smrg
3313d522f475Smrg    /*
3314d522f475Smrg     * If we are told to scroll the selection but it lies outside the scrolling
3315d522f475Smrg     * margins, then that could cause the selection to move (bad).  It is not
3316d522f475Smrg     * simple to fix, because this function is called both for the scrollbar
3317d522f475Smrg     * actions as well as application scrolling.  The 'always' flag is set in
3318d522f475Smrg     * the former case.  The rest of the logic handles the latter.
3319d522f475Smrg     */
3320d522f475Smrg    if (ScrnHaveSelection(screen)) {
3321d522f475Smrg	int adjust;
3322d522f475Smrg
3323d522f475Smrg	adjust = ROW2INX(screen, screen->startH.row);
3324d522f475Smrg	if (always
33250bd37d32Smrg	    || !ScrnHaveRowMargins(screen)
33260bd37d32Smrg	    || ScrnIsRowInMargins(screen, adjust)) {
3327d522f475Smrg	    scroll_update_one(&screen->startH);
3328d522f475Smrg	}
3329d522f475Smrg	adjust = ROW2INX(screen, screen->endH.row);
3330d522f475Smrg	if (always
33310bd37d32Smrg	    || !ScrnHaveRowMargins(screen)
33320bd37d32Smrg	    || ScrnIsRowInMargins(screen, adjust)) {
3333d522f475Smrg	    scroll_update_one(&screen->endH);
3334d522f475Smrg	}
3335d522f475Smrg    }
3336d522f475Smrg
3337d522f475Smrg    screen->startHCoord = Coordinate(screen, &screen->startH);
3338d522f475Smrg    screen->endHCoord = Coordinate(screen, &screen->endH);
3339d522f475Smrg}
3340d522f475Smrg
3341d522f475Smrg/*ARGSUSED*/
3342d522f475Smrgvoid
3343f2e35a3aSmrgResizeSelection(TScreen *screen, int rows, int cols)
3344d522f475Smrg{
3345d522f475Smrg    rows--;			/* decr to get 0-max */
3346d522f475Smrg    cols--;
3347d522f475Smrg
3348d522f475Smrg    if (screen->startRaw.row > rows)
3349d522f475Smrg	screen->startRaw.row = rows;
3350d522f475Smrg    if (screen->startSel.row > rows)
3351d522f475Smrg	screen->startSel.row = rows;
3352d522f475Smrg    if (screen->endRaw.row > rows)
3353d522f475Smrg	screen->endRaw.row = rows;
3354d522f475Smrg    if (screen->endSel.row > rows)
3355d522f475Smrg	screen->endSel.row = rows;
3356d522f475Smrg    if (screen->rawPos.row > rows)
3357d522f475Smrg	screen->rawPos.row = rows;
3358d522f475Smrg
3359d522f475Smrg    if (screen->startRaw.col > cols)
3360d522f475Smrg	screen->startRaw.col = cols;
3361d522f475Smrg    if (screen->startSel.col > cols)
3362d522f475Smrg	screen->startSel.col = cols;
3363d522f475Smrg    if (screen->endRaw.col > cols)
3364d522f475Smrg	screen->endRaw.col = cols;
3365d522f475Smrg    if (screen->endSel.col > cols)
3366d522f475Smrg	screen->endSel.col = cols;
3367d522f475Smrg    if (screen->rawPos.col > cols)
3368d522f475Smrg	screen->rawPos.col = cols;
3369d522f475Smrg}
3370d522f475Smrg
3371d522f475Smrg#if OPT_WIDE_CHARS
3372f2e35a3aSmrg#define isWideCell(row, col) isWideFrg((int)XTERM_CELL(row, col))
3373d522f475Smrg#endif
3374d522f475Smrg
3375d522f475Smrgstatic void
3376e0a2b6dfSmrgPointToCELL(TScreen *screen,
3377d522f475Smrg	    int y,
3378d522f475Smrg	    int x,
3379e0a2b6dfSmrg	    CELL *cell)
3380d522f475Smrg/* Convert pixel coordinates to character coordinates.
3381d522f475Smrg   Rows are clipped between firstValidRow and lastValidRow.
3382d522f475Smrg   Columns are clipped between to be 0 or greater, but are not clipped to some
3383d522f475Smrg       maximum value. */
3384d522f475Smrg{
3385d522f475Smrg    cell->row = (y - screen->border) / FontHeight(screen);
3386d522f475Smrg    if (cell->row < screen->firstValidRow)
3387d522f475Smrg	cell->row = screen->firstValidRow;
3388d522f475Smrg    else if (cell->row > screen->lastValidRow)
3389d522f475Smrg	cell->row = screen->lastValidRow;
3390d522f475Smrg    cell->col = (x - OriginX(screen)) / FontWidth(screen);
3391d522f475Smrg    if (cell->col < 0)
3392d522f475Smrg	cell->col = 0;
3393d522f475Smrg    else if (cell->col > MaxCols(screen)) {
3394d522f475Smrg	cell->col = MaxCols(screen);
3395d522f475Smrg    }
3396d522f475Smrg#if OPT_WIDE_CHARS
3397d522f475Smrg    /*
3398d522f475Smrg     * If we got a click on the right half of a doublewidth character,
3399d522f475Smrg     * pretend it happened on the left half.
3400d522f475Smrg     */
3401d522f475Smrg    if (cell->col > 0
3402d522f475Smrg	&& isWideCell(cell->row, cell->col - 1)
3403d522f475Smrg	&& (XTERM_CELL(cell->row, cell->col) == HIDDEN_CHAR)) {
3404d522f475Smrg	cell->col -= 1;
3405d522f475Smrg    }
3406d522f475Smrg#endif
3407d522f475Smrg}
3408d522f475Smrg
3409d522f475Smrg/*
3410d522f475Smrg * Find the last column at which text was drawn on the given row.
3411d522f475Smrg */
3412d522f475Smrgstatic int
341301037d57SmrgLastTextCol(TScreen *screen, CLineData *ld, int row)
3414d522f475Smrg{
341520d2c4d2Smrg    int i = -1;
3416d522f475Smrg
341720d2c4d2Smrg    if (ld != 0) {
341820d2c4d2Smrg	if (okScrnRow(screen, row)) {
34192e4f8982Smrg	    const IAttr *ch;
342020d2c4d2Smrg	    for (i = screen->max_col,
342120d2c4d2Smrg		 ch = ld->attribs + i;
342220d2c4d2Smrg		 i >= 0 && !(*ch & CHARDRAWN);
342320d2c4d2Smrg		 ch--, i--) {
342420d2c4d2Smrg		;
342520d2c4d2Smrg	    }
3426d522f475Smrg#if OPT_DEC_CHRSET
342720d2c4d2Smrg	    if (CSET_DOUBLE(GetLineDblCS(ld))) {
342820d2c4d2Smrg		i *= 2;
342920d2c4d2Smrg	    }
3430d522f475Smrg#endif
343120d2c4d2Smrg	}
3432d522f475Smrg    }
3433d522f475Smrg    return (i);
3434d522f475Smrg}
3435d522f475Smrg
3436d522f475Smrg#if !OPT_WIDE_CHARS
3437d522f475Smrg/*
3438d522f475Smrg** double click table for cut and paste in 8 bits
3439d522f475Smrg**
3440d522f475Smrg** This table is divided in four parts :
3441d522f475Smrg**
3442d522f475Smrg**	- control characters	[0,0x1f] U [0x80,0x9f]
3443d522f475Smrg**	- separators		[0x20,0x3f] U [0xa0,0xb9]
3444d522f475Smrg**	- binding characters	[0x40,0x7f] U [0xc0,0xff]
3445d522f475Smrg**	- exceptions
3446d522f475Smrg*/
3447d522f475Smrg/* *INDENT-OFF* */
3448d522f475Smrgstatic int charClass[256] =
3449d522f475Smrg{
3450d522f475Smrg/* NUL  SOH  STX  ETX  EOT  ENQ  ACK  BEL */
3451d522f475Smrg    32,  1,    1,   1,   1,   1,   1,   1,
34520bd37d32Smrg/*  BS   HT   NL   VT   FF   CR   SO   SI */
3453d522f475Smrg     1,  32,   1,   1,   1,   1,   1,   1,
3454d522f475Smrg/* DLE  DC1  DC2  DC3  DC4  NAK  SYN  ETB */
3455d522f475Smrg     1,   1,   1,   1,   1,   1,   1,   1,
3456d522f475Smrg/* CAN   EM  SUB  ESC   FS   GS   RS   US */
3457d522f475Smrg     1,   1,   1,   1,   1,   1,   1,   1,
3458d522f475Smrg/*  SP    !    "    #    $    %    &    ' */
3459d522f475Smrg    32,  33,  34,  35,  36,  37,  38,  39,
3460d522f475Smrg/*   (    )    *    +    ,    -    .    / */
3461d522f475Smrg    40,  41,  42,  43,  44,  45,  46,  47,
3462d522f475Smrg/*   0    1    2    3    4    5    6    7 */
3463d522f475Smrg    48,  48,  48,  48,  48,  48,  48,  48,
3464d522f475Smrg/*   8    9    :    ;    <    =    >    ? */
3465d522f475Smrg    48,  48,  58,  59,  60,  61,  62,  63,
3466d522f475Smrg/*   @    A    B    C    D    E    F    G */
3467d522f475Smrg    64,  48,  48,  48,  48,  48,  48,  48,
3468d522f475Smrg/*   H    I    J    K    L    M    N    O */
3469d522f475Smrg    48,  48,  48,  48,  48,  48,  48,  48,
3470d522f475Smrg/*   P    Q    R    S    T    U    V    W */
3471d522f475Smrg    48,  48,  48,  48,  48,  48,  48,  48,
3472d522f475Smrg/*   X    Y    Z    [    \    ]    ^    _ */
3473d522f475Smrg    48,  48,  48,  91,  92,  93,  94,  48,
3474d522f475Smrg/*   `    a    b    c    d    e    f    g */
3475d522f475Smrg    96,  48,  48,  48,  48,  48,  48,  48,
3476d522f475Smrg/*   h    i    j    k    l    m    n    o */
3477d522f475Smrg    48,  48,  48,  48,  48,  48,  48,  48,
3478d522f475Smrg/*   p    q    r    s    t    u    v    w */
3479d522f475Smrg    48,  48,  48,  48,  48,  48,  48,  48,
3480d522f475Smrg/*   x    y    z    {    |    }    ~  DEL */
3481d522f475Smrg    48,  48,  48, 123, 124, 125, 126,   1,
3482d522f475Smrg/* x80  x81  x82  x83  IND  NEL  SSA  ESA */
3483d522f475Smrg    1,    1,   1,   1,   1,   1,   1,   1,
3484d522f475Smrg/* HTS  HTJ  VTS  PLD  PLU   RI  SS2  SS3 */
3485d522f475Smrg    1,    1,   1,   1,   1,   1,   1,   1,
3486d522f475Smrg/* DCS  PU1  PU2  STS  CCH   MW  SPA  EPA */
3487d522f475Smrg    1,    1,   1,   1,   1,   1,   1,   1,
3488d522f475Smrg/* x98  x99  x9A  CSI   ST  OSC   PM  APC */
3489d522f475Smrg    1,    1,   1,   1,   1,   1,   1,   1,
3490d522f475Smrg/*   -    i   c/    L   ox   Y-    |   So */
3491d522f475Smrg    160, 161, 162, 163, 164, 165, 166, 167,
3492d522f475Smrg/*  ..   c0   ip   <<    _        R0    - */
3493d522f475Smrg    168, 169, 170, 171, 172, 173, 174, 175,
3494d522f475Smrg/*   o   +-    2    3    '    u   q|    . */
3495d522f475Smrg    176, 177, 178, 179, 180, 181, 182, 183,
3496d522f475Smrg/*   ,    1    2   >>  1/4  1/2  3/4    ? */
3497d522f475Smrg    184, 185, 186, 187, 188, 189, 190, 191,
3498d522f475Smrg/*  A`   A'   A^   A~   A:   Ao   AE   C, */
3499d522f475Smrg     48,  48,  48,  48,  48,  48,  48,  48,
3500d522f475Smrg/*  E`   E'   E^   E:   I`   I'   I^   I: */
3501d522f475Smrg     48,  48,  48,  48,  48,  48,  48,  48,
3502d522f475Smrg/*  D-   N~   O`   O'   O^   O~   O:    X */
3503d522f475Smrg     48,  48,  48,  48,  48,  48,  48, 215,
3504d522f475Smrg/*  O/   U`   U'   U^   U:   Y'    P    B */
3505d522f475Smrg     48,  48,  48,  48,  48,  48,  48,  48,
3506d522f475Smrg/*  a`   a'   a^   a~   a:   ao   ae   c, */
3507d522f475Smrg     48,  48,  48,  48,  48,  48,  48,  48,
3508d522f475Smrg/*  e`   e'   e^   e:    i`  i'   i^   i: */
3509d522f475Smrg     48,  48,  48,  48,  48,  48,  48,  48,
3510d522f475Smrg/*   d   n~   o`   o'   o^   o~   o:   -: */
3511d522f475Smrg     48,  48,  48,  48,  48,  48,  48, 247,
3512d522f475Smrg/*  o/   u`   u'   u^   u:   y'    P   y: */
3513d522f475Smrg     48,  48,  48,  48,  48,  48,  48,  48};
3514d522f475Smrg/* *INDENT-ON* */
3515d522f475Smrg
3516d522f475Smrgint
3517d522f475SmrgSetCharacterClassRange(int low,	/* in range of [0..255] */
3518d522f475Smrg		       int high,
3519d522f475Smrg		       int value)	/* arbitrary */
3520d522f475Smrg{
3521d522f475Smrg
3522d522f475Smrg    if (low < 0 || high > 255 || high < low)
3523d522f475Smrg	return (-1);
3524d522f475Smrg
3525d522f475Smrg    for (; low <= high; low++)
3526d522f475Smrg	charClass[low] = value;
3527d522f475Smrg
3528d522f475Smrg    return (0);
3529d522f475Smrg}
3530d522f475Smrg#endif
3531d522f475Smrg
3532d522f475Smrgstatic int
3533e0a2b6dfSmrgclass_of(LineData *ld, CELL *cell)
3534d522f475Smrg{
3535d522f475Smrg    CELL temp = *cell;
35360bd37d32Smrg    int result = 0;
3537d522f475Smrg
3538d522f475Smrg#if OPT_DEC_CHRSET
3539956cc18dSsnj    if (CSET_DOUBLE(GetLineDblCS(ld))) {
3540d522f475Smrg	temp.col /= 2;
3541d522f475Smrg    }
3542d522f475Smrg#endif
35430bd37d32Smrg    if (temp.col < (int) ld->lineSize)
35440bd37d32Smrg	result = CharacterClass((int) (ld->charData[temp.col]));
35450bd37d32Smrg    return result;
3546d522f475Smrg}
3547956cc18dSsnj
3548956cc18dSsnj#if OPT_WIDE_CHARS
3549956cc18dSsnj#define CClassSelects(name, cclass) \
3550956cc18dSsnj	 (CClassOf(name) == cclass \
3551956cc18dSsnj	 || XTERM_CELL(screen->name.row, screen->name.col) == HIDDEN_CHAR)
3552d522f475Smrg#else
3553956cc18dSsnj#define CClassSelects(name, cclass) \
3554956cc18dSsnj	 (class_of(ld.name, &((screen->name))) == cclass)
3555d522f475Smrg#endif
3556d522f475Smrg
3557956cc18dSsnj#define CClassOf(name) class_of(ld.name, &((screen->name)))
3558956cc18dSsnj
3559913cc679Smrg#if OPT_REPORT_CCLASS
3560913cc679Smrgstatic int
3561913cc679Smrgshow_cclass_range(int lo, int hi)
3562913cc679Smrg{
3563913cc679Smrg    int cclass = CharacterClass(lo);
3564913cc679Smrg    int ident = (cclass == lo);
3565913cc679Smrg    int more = 0;
3566913cc679Smrg    if (ident) {
3567913cc679Smrg	int ch;
3568913cc679Smrg	for (ch = lo + 1; ch <= hi; ch++) {
3569913cc679Smrg	    if (CharacterClass(ch) != ch) {
3570913cc679Smrg		ident = 0;
3571913cc679Smrg		break;
3572913cc679Smrg	    }
3573913cc679Smrg	}
3574913cc679Smrg	if (ident && (hi < 255)) {
3575913cc679Smrg	    ch = hi + 1;
3576913cc679Smrg	    if (CharacterClass(ch) == ch) {
3577913cc679Smrg		if (ch >= 255 || CharacterClass(ch + 1) != ch) {
3578913cc679Smrg		    more = 1;
3579913cc679Smrg		}
3580913cc679Smrg	    }
3581913cc679Smrg	}
3582913cc679Smrg    }
3583913cc679Smrg    if (!more) {
3584913cc679Smrg	if (lo == hi) {
3585913cc679Smrg	    printf("\t%d", lo);
3586913cc679Smrg	} else {
3587913cc679Smrg	    printf("\t%d-%d", lo, hi);
3588913cc679Smrg	}
3589913cc679Smrg	if (!ident)
3590913cc679Smrg	    printf(":%d", cclass);
3591913cc679Smrg	if (hi < 255)
3592913cc679Smrg	    printf(", \\");
3593913cc679Smrg	printf("\n");
3594913cc679Smrg    }
3595913cc679Smrg    return !more;
3596913cc679Smrg}
3597913cc679Smrg
3598913cc679Smrgvoid
3599913cc679Smrgreport_char_class(XtermWidget xw)
3600913cc679Smrg{
3601913cc679Smrg    /* simple table, to match documentation */
3602913cc679Smrg    static const char charnames[] =
3603913cc679Smrg    "NUL\0" "SOH\0" "STX\0" "ETX\0" "EOT\0" "ENQ\0" "ACK\0" "BEL\0"
3604913cc679Smrg    " BS\0" " HT\0" " NL\0" " VT\0" " NP\0" " CR\0" " SO\0" " SI\0"
3605913cc679Smrg    "DLE\0" "DC1\0" "DC2\0" "DC3\0" "DC4\0" "NAK\0" "SYN\0" "ETB\0"
3606913cc679Smrg    "CAN\0" " EM\0" "SUB\0" "ESC\0" " FS\0" " GS\0" " RS\0" " US\0"
3607913cc679Smrg    " SP\0" "  !\0" "  \"\0" "  #\0" "  $\0" "  %\0" "  &\0" "  '\0"
3608913cc679Smrg    "  (\0" "  )\0" "  *\0" "  +\0" "  ,\0" "  -\0" "  .\0" "  /\0"
3609913cc679Smrg    "  0\0" "  1\0" "  2\0" "  3\0" "  4\0" "  5\0" "  6\0" "  7\0"
3610913cc679Smrg    "  8\0" "  9\0" "  :\0" "  ;\0" "  <\0" "  =\0" "  >\0" "  ?\0"
3611913cc679Smrg    "  @\0" "  A\0" "  B\0" "  C\0" "  D\0" "  E\0" "  F\0" "  G\0"
3612913cc679Smrg    "  H\0" "  I\0" "  J\0" "  K\0" "  L\0" "  M\0" "  N\0" "  O\0"
3613913cc679Smrg    "  P\0" "  Q\0" "  R\0" "  S\0" "  T\0" "  U\0" "  V\0" "  W\0"
3614913cc679Smrg    "  X\0" "  Y\0" "  Z\0" "  [\0" "  \\\0" "  ]\0" "  ^\0" "  _\0"
3615913cc679Smrg    "  `\0" "  a\0" "  b\0" "  c\0" "  d\0" "  e\0" "  f\0" "  g\0"
3616913cc679Smrg    "  h\0" "  i\0" "  j\0" "  k\0" "  l\0" "  m\0" "  n\0" "  o\0"
3617913cc679Smrg    "  p\0" "  q\0" "  r\0" "  s\0" "  t\0" "  u\0" "  v\0" "  w\0"
3618913cc679Smrg    "  x\0" "  y\0" "  z\0" "  {\0" "  |\0" "  }\0" "  ~\0" "DEL\0"
3619913cc679Smrg    "x80\0" "x81\0" "x82\0" "x83\0" "IND\0" "NEL\0" "SSA\0" "ESA\0"
3620913cc679Smrg    "HTS\0" "HTJ\0" "VTS\0" "PLD\0" "PLU\0" " RI\0" "SS2\0" "SS3\0"
3621913cc679Smrg    "DCS\0" "PU1\0" "PU2\0" "STS\0" "CCH\0" " MW\0" "SPA\0" "EPA\0"
3622913cc679Smrg    "x98\0" "x99\0" "x9A\0" "CSI\0" " ST\0" "OSC\0" " PM\0" "APC\0"
3623913cc679Smrg    "  -\0" "  i\0" " c/\0" "  L\0" " ox\0" " Y-\0" "  |\0" " So\0"
3624913cc679Smrg    " ..\0" " c0\0" " ip\0" " <<\0" "  _\0" "   \0" " R0\0" "  -\0"
3625913cc679Smrg    "  o\0" " +-\0" "  2\0" "  3\0" "  '\0" "  u\0" " q|\0" "  .\0"
3626913cc679Smrg    "  ,\0" "  1\0" "  2\0" " >>\0" "1/4\0" "1/2\0" "3/4\0" "  ?\0"
3627913cc679Smrg    " A`\0" " A'\0" " A^\0" " A~\0" " A:\0" " Ao\0" " AE\0" " C,\0"
3628913cc679Smrg    " E`\0" " E'\0" " E^\0" " E:\0" " I`\0" " I'\0" " I^\0" " I:\0"
3629913cc679Smrg    " D-\0" " N~\0" " O`\0" " O'\0" " O^\0" " O~\0" " O:\0" "  X\0"
3630913cc679Smrg    " O/\0" " U`\0" " U'\0" " U^\0" " U:\0" " Y'\0" "  P\0" "  B\0"
3631913cc679Smrg    " a`\0" " a'\0" " a^\0" " a~\0" " a:\0" " ao\0" " ae\0" " c,\0"
3632913cc679Smrg    " e`\0" " e'\0" " e^\0" " e:\0" " i`\0" " i'\0" " i^\0" " i:\0"
3633913cc679Smrg    "  d\0" " n~\0" " o`\0" " o'\0" " o^\0" " o~\0" " o:\0" " -:\0"
3634913cc679Smrg    " o/\0" " u`\0" " u'\0" " u^\0" " u:\0" " y'\0" "  P\0" " y:\0";
3635913cc679Smrg    int ch, dh;
3636913cc679Smrg    int class_p;
3637913cc679Smrg
3638913cc679Smrg    (void) xw;
3639913cc679Smrg
3640913cc679Smrg    printf("static int charClass[256] = {\n");
3641913cc679Smrg    for (ch = 0; ch < 256; ++ch) {
3642913cc679Smrg	const char *s = charnames + (ch * 4);
3643913cc679Smrg	if ((ch & 7) == 0)
3644913cc679Smrg	    printf("/*");
3645913cc679Smrg	printf(" %s ", s);
3646913cc679Smrg	if (((ch + 1) & 7) == 0) {
3647913cc679Smrg	    printf("*/\n  ");
3648913cc679Smrg	    for (dh = ch - 7; dh <= ch; ++dh) {
3649913cc679Smrg		printf(" %3d%s", CharacterClass(dh), dh == 255 ? "};" : ",");
3650913cc679Smrg	    }
3651913cc679Smrg	    printf("\n");
3652913cc679Smrg	}
3653913cc679Smrg    }
3654913cc679Smrg
3655913cc679Smrg    /* print the table as if it were the charClass resource */
3656913cc679Smrg    printf("\n");
3657913cc679Smrg    printf("The table is equivalent to this \"charClass\" resource:\n");
3658913cc679Smrg    class_p = CharacterClass(dh = 0);
3659913cc679Smrg    for (ch = 0; ch < 256; ++ch) {
3660913cc679Smrg	int class_c = CharacterClass(ch);
3661913cc679Smrg	if (class_c != class_p) {
3662913cc679Smrg	    if (show_cclass_range(dh, ch - 1)) {
3663913cc679Smrg		dh = ch;
3664913cc679Smrg		class_p = class_c;
3665913cc679Smrg	    }
3666913cc679Smrg	}
3667913cc679Smrg    }
3668913cc679Smrg    if (dh < 255) {
3669913cc679Smrg	show_cclass_range(dh, 255);
3670913cc679Smrg    }
3671913cc679Smrg
3672913cc679Smrg    if_OPT_WIDE_CHARS(TScreenOf(xw), {
3673913cc679Smrg	/* if this is a wide-character configuration, print all intervals */
3674913cc679Smrg	report_wide_char_class();
3675913cc679Smrg    });
3676913cc679Smrg}
3677913cc679Smrg#endif
3678913cc679Smrg
3679d522f475Smrg/*
3680d522f475Smrg * If the given column is past the end of text on the given row, bump to the
3681d522f475Smrg * beginning of the next line.
3682d522f475Smrg */
3683d522f475Smrgstatic Boolean
3684e0a2b6dfSmrgokPosition(TScreen *screen,
3685e0a2b6dfSmrg	   LineData **ld,
3686e0a2b6dfSmrg	   CELL *cell)
3687d522f475Smrg{
368820d2c4d2Smrg    Boolean result = True;
368920d2c4d2Smrg
369020d2c4d2Smrg    if (cell->row > screen->max_row) {
369120d2c4d2Smrg	result = False;
3692f2e35a3aSmrg	TRACE(("okPosition cell row %d > screen max %d\n", cell->row, screen->max_row));
369320d2c4d2Smrg    } else if (cell->col > (LastTextCol(screen, *ld, cell->row) + 1)) {
3694f2e35a3aSmrg	TRACE(("okPosition cell col %d > screen max %d\n", cell->col,
3695f2e35a3aSmrg	       (LastTextCol(screen, *ld, cell->row) + 1)));
369620d2c4d2Smrg	if (cell->row < screen->max_row) {
3697f2e35a3aSmrg	    TRACE(("okPosition cell row %d < screen max %d\n", cell->row, screen->max_row));
369820d2c4d2Smrg	    cell->col = 0;
369920d2c4d2Smrg	    *ld = GET_LINEDATA(screen, ++cell->row);
370020d2c4d2Smrg	    result = False;
370120d2c4d2Smrg	}
3702d522f475Smrg    }
370320d2c4d2Smrg    return result;
3704d522f475Smrg}
3705d522f475Smrg
3706d522f475Smrgstatic void
3707e0a2b6dfSmrgtrimLastLine(TScreen *screen,
3708e0a2b6dfSmrg	     LineData **ld,
3709e0a2b6dfSmrg	     CELL *last)
3710d522f475Smrg{
371120d2c4d2Smrg    if (screen->cutNewline && last->row < screen->max_row) {
3712d522f475Smrg	last->col = 0;
3713956cc18dSsnj	*ld = GET_LINEDATA(screen, ++last->row);
3714d522f475Smrg    } else {
3715956cc18dSsnj	last->col = LastTextCol(screen, *ld, last->row) + 1;
3716d522f475Smrg    }
3717d522f475Smrg}
3718d522f475Smrg
3719d522f475Smrg#if OPT_SELECT_REGEX
3720d522f475Smrg/*
3721d522f475Smrg * Returns the first row of a wrapped line.
3722d522f475Smrg */
3723d522f475Smrgstatic int
3724e0a2b6dfSmrgfirstRowOfLine(TScreen *screen, int row, Bool visible)
3725d522f475Smrg{
3726956cc18dSsnj    LineData *ld = 0;
3727d522f475Smrg    int limit = visible ? 0 : -screen->savedlines;
3728d522f475Smrg
3729d522f475Smrg    while (row > limit &&
3730956cc18dSsnj	   (ld = GET_LINEDATA(screen, row - 1)) != 0 &&
3731956cc18dSsnj	   LineTstWrapped(ld)) {
3732d522f475Smrg	--row;
3733956cc18dSsnj    }
3734d522f475Smrg    return row;
3735d522f475Smrg}
3736d522f475Smrg
3737d522f475Smrg/*
3738d522f475Smrg * Returns the last row of a wrapped line.
3739d522f475Smrg */
3740d522f475Smrgstatic int
3741e0a2b6dfSmrglastRowOfLine(TScreen *screen, int row)
3742d522f475Smrg{
3743956cc18dSsnj    LineData *ld;
3744956cc18dSsnj
3745d522f475Smrg    while (row < screen->max_row &&
3746956cc18dSsnj	   (ld = GET_LINEDATA(screen, row)) != 0 &&
3747956cc18dSsnj	   LineTstWrapped(ld)) {
3748d522f475Smrg	++row;
3749956cc18dSsnj    }
3750d522f475Smrg    return row;
3751d522f475Smrg}
3752d522f475Smrg
3753d522f475Smrg/*
3754d522f475Smrg * Returns the number of cells on the range of rows.
3755d522f475Smrg */
3756d522f475Smrgstatic unsigned
3757e0a2b6dfSmrglengthOfLines(TScreen *screen, int firstRow, int lastRow)
3758d522f475Smrg{
3759d522f475Smrg    unsigned length = 0;
3760d522f475Smrg    int n;
3761d522f475Smrg
3762d522f475Smrg    for (n = firstRow; n <= lastRow; ++n) {
3763956cc18dSsnj	LineData *ld = GET_LINEDATA(screen, n);
3764956cc18dSsnj	int value = LastTextCol(screen, ld, n);
3765d522f475Smrg	if (value >= 0)
37662eaa94a1Schristos	    length += (unsigned) (value + 1);
3767d522f475Smrg    }
3768d522f475Smrg    return length;
3769d522f475Smrg}
3770d522f475Smrg
3771d522f475Smrg/*
3772d522f475Smrg * Make a copy of the wrapped-line which corresponds to the given row as a
3773d522f475Smrg * string of bytes.  Construct an index for the columns from the beginning of
3774d522f475Smrg * the line.
3775d522f475Smrg */
3776d522f475Smrgstatic char *
3777e0a2b6dfSmrgmake_indexed_text(TScreen *screen, int row, unsigned length, int *indexed)
3778d522f475Smrg{
3779d522f475Smrg    Char *result = 0;
378020d2c4d2Smrg    size_t need = (length + 1);
3781d522f475Smrg
3782d522f475Smrg    /*
3783d522f475Smrg     * Get a quick upper bound to the number of bytes needed, if the whole
3784d522f475Smrg     * string were UTF-8.
3785d522f475Smrg     */
3786d522f475Smrg    if_OPT_WIDE_CHARS(screen, {
3787956cc18dSsnj	need *= ((screen->lineExtra + 1) * 6);
3788d522f475Smrg    });
3789d522f475Smrg
3790d522f475Smrg    if ((result = TypeCallocN(Char, need + 1)) != 0) {
3791956cc18dSsnj	LineData *ld = GET_LINEDATA(screen, row);
3792d522f475Smrg	unsigned used = 0;
3793d522f475Smrg	Char *last = result;
3794d522f475Smrg
3795d522f475Smrg	do {
3796d522f475Smrg	    int col = 0;
3797956cc18dSsnj	    int limit = LastTextCol(screen, ld, row);
3798d522f475Smrg
3799d522f475Smrg	    while (col <= limit) {
3800d522f475Smrg		Char *next = last;
3801956cc18dSsnj		unsigned data = ld->charData[col];
3802d522f475Smrg
38030bd37d32Smrg		assert(col < (int) ld->lineSize);
3804d522f475Smrg		/* some internal points may not be drawn */
3805d522f475Smrg		if (data == 0)
3806d522f475Smrg		    data = ' ';
3807d522f475Smrg
3808d522f475Smrg		if_WIDE_OR_NARROW(screen, {
3809d522f475Smrg		    next = convertToUTF8(last, data);
3810d522f475Smrg		}
3811d522f475Smrg		, {
3812d522f475Smrg		    *next++ = CharOf(data);
3813d522f475Smrg		});
3814d522f475Smrg
3815d522f475Smrg		if_OPT_WIDE_CHARS(screen, {
3816956cc18dSsnj		    size_t off;
3817956cc18dSsnj		    for_each_combData(off, ld) {
3818956cc18dSsnj			data = ld->combData[off][col];
3819956cc18dSsnj			if (data == 0)
3820d522f475Smrg			    break;
3821d522f475Smrg			next = convertToUTF8(next, data);
3822d522f475Smrg		    }
3823d522f475Smrg		});
3824d522f475Smrg
382520d2c4d2Smrg		indexed[used] = (int) (last - result);
3826d522f475Smrg		*next = 0;
3827d522f475Smrg		/* TRACE(("index[%d.%d] %d:%s\n", row, used, indexed[used], last)); */
3828d522f475Smrg		last = next;
3829d522f475Smrg		++used;
3830d522f475Smrg		++col;
383120d2c4d2Smrg		indexed[used] = (int) (next - result);
3832d522f475Smrg	    }
3833d522f475Smrg	} while (used < length &&
3834956cc18dSsnj		 LineTstWrapped(ld) &&
3835956cc18dSsnj		 (ld = GET_LINEDATA(screen, ++row)) != 0 &&
3836956cc18dSsnj		 row < screen->max_row);
3837d522f475Smrg    }
3838d522f475Smrg    /* TRACE(("result:%s\n", result)); */
3839d522f475Smrg    return (char *) result;
3840d522f475Smrg}
3841d522f475Smrg
3842d522f475Smrg/*
3843d522f475Smrg * Find the column given an offset into the character string by using the
3844d522f475Smrg * index constructed in make_indexed_text().
3845d522f475Smrg */
3846d522f475Smrgstatic int
3847d522f475SmrgindexToCol(int *indexed, int len, int off)
3848d522f475Smrg{
3849d522f475Smrg    int col = 0;
3850d522f475Smrg    while (indexed[col] < len) {
3851d522f475Smrg	if (indexed[col] >= off)
3852d522f475Smrg	    break;
3853d522f475Smrg	++col;
3854d522f475Smrg    }
3855d522f475Smrg    return col;
3856d522f475Smrg}
3857d522f475Smrg
3858d522f475Smrg/*
3859d522f475Smrg * Given a row number, and a column offset from that (which may be wrapped),
3860d522f475Smrg * set the cell to the actual row/column values.
3861d522f475Smrg */
3862d522f475Smrgstatic void
3863e0a2b6dfSmrgcolumnToCell(TScreen *screen, int row, int col, CELL *cell)
3864d522f475Smrg{
3865d522f475Smrg    while (row < screen->max_row) {
386601037d57Smrg	CLineData *ld = GET_LINEDATA(screen, row);
3867956cc18dSsnj	int last = LastTextCol(screen, ld, row);
3868d522f475Smrg
3869d522f475Smrg	/* TRACE(("last(%d) = %d, have %d\n", row, last, col)); */
3870d522f475Smrg	if (col <= last) {
3871d522f475Smrg	    break;
3872d522f475Smrg	}
3873d522f475Smrg	/*
3874d522f475Smrg	 * Stop if the current row does not wrap (does not continue the current
3875d522f475Smrg	 * line).
3876d522f475Smrg	 */
3877956cc18dSsnj	if (!LineTstWrapped(ld)) {
3878d522f475Smrg	    col = last + 1;
3879d522f475Smrg	    break;
3880d522f475Smrg	}
3881d522f475Smrg	col -= (last + 1);
3882d522f475Smrg	++row;
3883d522f475Smrg    }
3884d522f475Smrg    if (col < 0)
3885d522f475Smrg	col = 0;
3886d522f475Smrg    cell->row = row;
3887d522f475Smrg    cell->col = col;
3888d522f475Smrg}
3889d522f475Smrg
3890d522f475Smrg/*
3891d522f475Smrg * Given a cell, find the corresponding column offset.
3892d522f475Smrg */
3893d522f475Smrgstatic int
3894e0a2b6dfSmrgcellToColumn(TScreen *screen, CELL *cell)
3895d522f475Smrg{
389601037d57Smrg    CLineData *ld = 0;
3897d522f475Smrg    int col = cell->col;
3898d522f475Smrg    int row = firstRowOfLine(screen, cell->row, False);
3899d522f475Smrg    while (row < cell->row) {
3900956cc18dSsnj	ld = GET_LINEDATA(screen, row);
3901956cc18dSsnj	col += LastTextCol(screen, ld, row++);
3902d522f475Smrg    }
3903956cc18dSsnj#if OPT_DEC_CHRSET
3904956cc18dSsnj    if (ld == 0)
3905956cc18dSsnj	ld = GET_LINEDATA(screen, row);
3906956cc18dSsnj    if (CSET_DOUBLE(GetLineDblCS(ld)))
3907956cc18dSsnj	col /= 2;
3908956cc18dSsnj#endif
3909d522f475Smrg    return col;
3910d522f475Smrg}
3911d522f475Smrg
3912d522f475Smrgstatic void
3913e0a2b6dfSmrgdo_select_regex(TScreen *screen, CELL *startc, CELL *endc)
3914d522f475Smrg{
3915956cc18dSsnj    LineData *ld = GET_LINEDATA(screen, startc->row);
3916d522f475Smrg    int inx = ((screen->numberOfClicks - 1) % screen->maxClicks);
3917d522f475Smrg    char *expr = screen->selectExpr[inx];
3918d522f475Smrg    regex_t preg;
3919d522f475Smrg    regmatch_t match;
3920d522f475Smrg
392101037d57Smrg    TRACE(("Select_REGEX[%d]:%s\n", inx, NonNull(expr)));
3922956cc18dSsnj    if (okPosition(screen, &ld, startc) && expr != 0) {
3923d522f475Smrg	if (regcomp(&preg, expr, REG_EXTENDED) == 0) {
3924d522f475Smrg	    int firstRow = firstRowOfLine(screen, startc->row, True);
3925d522f475Smrg	    int lastRow = lastRowOfLine(screen, firstRow);
3926d522f475Smrg	    unsigned size = lengthOfLines(screen, firstRow, lastRow);
3927d522f475Smrg	    int actual = cellToColumn(screen, startc);
39282e4f8982Smrg	    int *indexed;
3929d522f475Smrg
3930d522f475Smrg	    TRACE(("regcomp ok rows %d..%d bytes %d\n",
3931d522f475Smrg		   firstRow, lastRow, size));
3932d522f475Smrg
3933d522f475Smrg	    if ((indexed = TypeCallocN(int, size + 1)) != 0) {
39342e4f8982Smrg		char *search;
3935d522f475Smrg		if ((search = make_indexed_text(screen,
3936d522f475Smrg						firstRow,
3937d522f475Smrg						size,
3938d522f475Smrg						indexed)) != 0) {
39392eaa94a1Schristos		    int len = (int) strlen(search);
3940d522f475Smrg		    int col;
3941d522f475Smrg		    int best_col = -1;
3942d522f475Smrg		    int best_len = -1;
3943d522f475Smrg
3944913cc679Smrg		    startc->row = 0;
3945913cc679Smrg		    startc->col = 0;
3946913cc679Smrg		    endc->row = 0;
3947913cc679Smrg		    endc->col = 0;
3948913cc679Smrg
3949d522f475Smrg		    for (col = 0; indexed[col] < len; ++col) {
3950d522f475Smrg			if (regexec(&preg,
3951d522f475Smrg				    search + indexed[col],
395220d2c4d2Smrg				    (size_t) 1, &match, 0) == 0) {
3953894e0ac8Smrg			    int start_inx = (int) (match.rm_so + indexed[col]);
3954894e0ac8Smrg			    int finis_inx = (int) (match.rm_eo + indexed[col]);
3955d522f475Smrg			    int start_col = indexToCol(indexed, len, start_inx);
3956d522f475Smrg			    int finis_col = indexToCol(indexed, len, finis_inx);
3957d522f475Smrg
3958d522f475Smrg			    if (start_col <= actual &&
3959913cc679Smrg				actual <= finis_col) {
3960d522f475Smrg				int test = finis_col - start_col;
3961d522f475Smrg				if (best_len < test) {
3962d522f475Smrg				    best_len = test;
3963d522f475Smrg				    best_col = start_col;
3964d522f475Smrg				    TRACE(("match column %d len %d\n",
3965d522f475Smrg					   best_col,
3966d522f475Smrg					   best_len));
3967d522f475Smrg				}
3968d522f475Smrg			    }
3969d522f475Smrg			}
3970d522f475Smrg		    }
3971d522f475Smrg		    if (best_col >= 0) {
3972d522f475Smrg			int best_nxt = best_col + best_len;
3973d522f475Smrg			columnToCell(screen, firstRow, best_col, startc);
3974d522f475Smrg			columnToCell(screen, firstRow, best_nxt, endc);
3975d522f475Smrg			TRACE(("search::%s\n", search));
3976d522f475Smrg			TRACE(("indexed:%d..%d -> %d..%d\n",
3977d522f475Smrg			       best_col, best_nxt,
3978d522f475Smrg			       indexed[best_col],
3979d522f475Smrg			       indexed[best_nxt]));
3980d522f475Smrg			TRACE(("matched:%d:%s\n",
3981d522f475Smrg			       indexed[best_nxt] + 1 -
3982d522f475Smrg			       indexed[best_col],
3983956cc18dSsnj			       visibleChars((Char *) (search + indexed[best_col]),
3984d522f475Smrg					    (unsigned) (indexed[best_nxt] +
3985d522f475Smrg							1 -
3986d522f475Smrg							indexed[best_col]))));
3987d522f475Smrg		    }
3988d522f475Smrg		    free(search);
3989d522f475Smrg		}
3990d522f475Smrg		free(indexed);
3991956cc18dSsnj#if OPT_DEC_CHRSET
3992956cc18dSsnj		if ((ld = GET_LINEDATA(screen, startc->row)) != 0) {
3993956cc18dSsnj		    if (CSET_DOUBLE(GetLineDblCS(ld)))
3994956cc18dSsnj			startc->col *= 2;
3995956cc18dSsnj		}
3996956cc18dSsnj		if ((ld = GET_LINEDATA(screen, endc->row)) != 0) {
3997956cc18dSsnj		    if (CSET_DOUBLE(GetLineDblCS(ld)))
3998956cc18dSsnj			endc->col *= 2;
3999956cc18dSsnj		}
4000956cc18dSsnj#endif
4001d522f475Smrg	    }
4002d522f475Smrg	    regfree(&preg);
4003d522f475Smrg	}
4004d522f475Smrg    }
4005d522f475Smrg}
4006d522f475Smrg#endif /* OPT_SELECT_REGEX */
4007d522f475Smrg
4008956cc18dSsnj#define InitRow(name) \
4009956cc18dSsnj	ld.name = GET_LINEDATA(screen, screen->name.row)
4010956cc18dSsnj
4011956cc18dSsnj#define NextRow(name) \
4012956cc18dSsnj	ld.name = GET_LINEDATA(screen, ++screen->name.row)
4013956cc18dSsnj
4014956cc18dSsnj#define PrevRow(name) \
4015956cc18dSsnj	ld.name = GET_LINEDATA(screen, --screen->name.row)
4016956cc18dSsnj
401720d2c4d2Smrg#define MoreRows(name) \
401820d2c4d2Smrg	(screen->name.row < screen->max_row)
401920d2c4d2Smrg
4020956cc18dSsnj#define isPrevWrapped(name) \
4021956cc18dSsnj	(screen->name.row > 0 \
4022956cc18dSsnj	   && (ltmp = GET_LINEDATA(screen, screen->name.row - 1)) != 0 \
4023956cc18dSsnj	   && LineTstWrapped(ltmp))
4024956cc18dSsnj
4025d522f475Smrg/*
4026d522f475Smrg * sets startSel endSel
4027d522f475Smrg * ensuring that they have legal values
4028d522f475Smrg */
4029d522f475Smrgstatic void
4030d522f475SmrgComputeSelect(XtermWidget xw,
4031e0a2b6dfSmrg	      CELL *startc,
4032e0a2b6dfSmrg	      CELL *endc,
4033f2e35a3aSmrg	      Bool extend,
4034f2e35a3aSmrg	      Bool normal)
4035d522f475Smrg{
4036956cc18dSsnj    TScreen *screen = TScreenOf(xw);
4037956cc18dSsnj
4038d522f475Smrg    int cclass;
4039d522f475Smrg    CELL first = *startc;
4040d522f475Smrg    CELL last = *endc;
4041956cc18dSsnj    Boolean ignored = False;
4042956cc18dSsnj
4043956cc18dSsnj    struct {
4044956cc18dSsnj	LineData *startSel;
4045956cc18dSsnj	LineData *endSel;
4046956cc18dSsnj    } ld;
4047956cc18dSsnj    LineData *ltmp;
4048d522f475Smrg
4049d522f475Smrg    TRACE(("ComputeSelect(startRow=%d, startCol=%d, endRow=%d, endCol=%d, %sextend)\n",
4050d522f475Smrg	   first.row, first.col,
4051d522f475Smrg	   last.row, last.col,
4052d522f475Smrg	   extend ? "" : "no"));
4053d522f475Smrg
4054d522f475Smrg#if OPT_WIDE_CHARS
4055d522f475Smrg    if (first.col > 1
4056d522f475Smrg	&& isWideCell(first.row, first.col - 1)
4057d522f475Smrg	&& XTERM_CELL(first.row, first.col - 0) == HIDDEN_CHAR) {
405820d2c4d2Smrg	TRACE(("Adjusting start. Changing downwards from %i.\n", first.col));
4059d522f475Smrg	first.col -= 1;
4060d522f475Smrg	if (last.col == (first.col + 1))
4061d522f475Smrg	    last.col--;
4062d522f475Smrg    }
4063d522f475Smrg
4064d522f475Smrg    if (last.col > 1
4065d522f475Smrg	&& isWideCell(last.row, last.col - 1)
4066d522f475Smrg	&& XTERM_CELL(last.row, last.col) == HIDDEN_CHAR) {
4067d522f475Smrg	last.col += 1;
4068d522f475Smrg    }
4069d522f475Smrg#endif
4070d522f475Smrg
4071d522f475Smrg    if (Coordinate(screen, &first) <= Coordinate(screen, &last)) {
4072d522f475Smrg	screen->startSel = screen->startRaw = first;
4073d522f475Smrg	screen->endSel = screen->endRaw = last;
4074d522f475Smrg    } else {			/* Swap them */
4075d522f475Smrg	screen->startSel = screen->startRaw = last;
4076d522f475Smrg	screen->endSel = screen->endRaw = first;
4077d522f475Smrg    }
4078d522f475Smrg
4079956cc18dSsnj    InitRow(startSel);
4080956cc18dSsnj    InitRow(endSel);
4081956cc18dSsnj
4082d522f475Smrg    switch (screen->selectUnit) {
4083d522f475Smrg    case Select_CHAR:
4084956cc18dSsnj	(void) okPosition(screen, &(ld.startSel), &(screen->startSel));
4085956cc18dSsnj	(void) okPosition(screen, &(ld.endSel), &(screen->endSel));
4086d522f475Smrg	break;
4087d522f475Smrg
4088d522f475Smrg    case Select_WORD:
4089d522f475Smrg	TRACE(("Select_WORD\n"));
4090956cc18dSsnj	if (okPosition(screen, &(ld.startSel), &(screen->startSel))) {
4091f2e35a3aSmrg	    CELL mark;
4092956cc18dSsnj	    cclass = CClassOf(startSel);
4093f2e35a3aSmrg	    TRACE(("...starting with class %d\n", cclass));
4094d522f475Smrg	    do {
4095f2e35a3aSmrg		mark = screen->startSel;
4096d522f475Smrg		--screen->startSel.col;
4097956cc18dSsnj		if (screen->startSel.col < 0
4098956cc18dSsnj		    && isPrevWrapped(startSel)) {
4099956cc18dSsnj		    PrevRow(startSel);
4100956cc18dSsnj		    screen->startSel.col = LastTextCol(screen, ld.startSel, screen->startSel.row);
4101d522f475Smrg		}
4102d522f475Smrg	    } while (screen->startSel.col >= 0
4103956cc18dSsnj		     && CClassSelects(startSel, cclass));
4104f2e35a3aSmrg	    if (normal)
4105f2e35a3aSmrg		++screen->startSel.col;
4106f2e35a3aSmrg	    else
4107f2e35a3aSmrg		screen->startSel = mark;
4108d522f475Smrg	}
4109d522f475Smrg#if OPT_WIDE_CHARS
4110f2e35a3aSmrg#define SkipHiddenCell(mark) \
4111f2e35a3aSmrg	if (mark.col && XTERM_CELL(mark.row, mark.col) == HIDDEN_CHAR) \
4112f2e35a3aSmrg	    mark.col++
4113f2e35a3aSmrg#else
4114f2e35a3aSmrg#define SkipHiddenCell(mark)	/* nothing */
4115d522f475Smrg#endif
4116f2e35a3aSmrg	SkipHiddenCell(screen->startSel);
4117f2e35a3aSmrg
4118f2e35a3aSmrg	if (!normal) {
4119f2e35a3aSmrg	    screen->endSel = screen->startSel;
4120f2e35a3aSmrg	    ld.endSel = ld.startSel;
4121f2e35a3aSmrg	}
4122d522f475Smrg
4123956cc18dSsnj	if (okPosition(screen, &(ld.endSel), &(screen->endSel))) {
41242e4f8982Smrg	    int length = LastTextCol(screen, ld.endSel, screen->endSel.row);
4125956cc18dSsnj	    cclass = CClassOf(endSel);
4126f2e35a3aSmrg	    TRACE(("...ending with class %d\n", cclass));
4127d522f475Smrg	    do {
4128d522f475Smrg		++screen->endSel.col;
4129d522f475Smrg		if (screen->endSel.col > length
4130956cc18dSsnj		    && LineTstWrapped(ld.endSel)) {
413120d2c4d2Smrg		    if (!MoreRows(endSel))
413220d2c4d2Smrg			break;
4133d522f475Smrg		    screen->endSel.col = 0;
4134956cc18dSsnj		    NextRow(endSel);
4135956cc18dSsnj		    length = LastTextCol(screen, ld.endSel, screen->endSel.row);
4136d522f475Smrg		}
4137d522f475Smrg	    } while (screen->endSel.col <= length
4138956cc18dSsnj		     && CClassSelects(endSel, cclass));
4139f2e35a3aSmrg	    if (normal
4140f2e35a3aSmrg		&& screen->endSel.col > length + 1
414120d2c4d2Smrg		&& MoreRows(endSel)) {
4142d522f475Smrg		screen->endSel.col = 0;
4143956cc18dSsnj		NextRow(endSel);
4144d522f475Smrg	    }
4145d522f475Smrg	}
4146f2e35a3aSmrg	SkipHiddenCell(screen->endSel);
4147d522f475Smrg
4148d522f475Smrg	screen->saveStartW = screen->startSel;
4149d522f475Smrg	break;
4150d522f475Smrg
4151d522f475Smrg    case Select_LINE:
4152d522f475Smrg	TRACE(("Select_LINE\n"));
415320d2c4d2Smrg	while (LineTstWrapped(ld.endSel)
415420d2c4d2Smrg	       && MoreRows(endSel)) {
4155956cc18dSsnj	    NextRow(endSel);
4156d522f475Smrg	}
4157d522f475Smrg	if (screen->cutToBeginningOfLine
4158d522f475Smrg	    || screen->startSel.row < screen->saveStartW.row) {
4159d522f475Smrg	    screen->startSel.col = 0;
4160956cc18dSsnj	    while (isPrevWrapped(startSel)) {
4161956cc18dSsnj		PrevRow(startSel);
4162d522f475Smrg	    }
4163d522f475Smrg	} else if (!extend) {
4164d522f475Smrg	    if ((first.row < screen->saveStartW.row)
4165d522f475Smrg		|| (isSameRow(&first, &(screen->saveStartW))
4166d522f475Smrg		    && first.col < screen->saveStartW.col)) {
4167d522f475Smrg		screen->startSel.col = 0;
4168956cc18dSsnj		while (isPrevWrapped(startSel)) {
4169956cc18dSsnj		    PrevRow(startSel);
4170d522f475Smrg		}
4171d522f475Smrg	    } else {
4172d522f475Smrg		screen->startSel = screen->saveStartW;
4173d522f475Smrg	    }
4174d522f475Smrg	}
4175956cc18dSsnj	trimLastLine(screen, &(ld.endSel), &(screen->endSel));
4176d522f475Smrg	break;
4177d522f475Smrg
4178d522f475Smrg    case Select_GROUP:		/* paragraph */
4179d522f475Smrg	TRACE(("Select_GROUP\n"));
4180956cc18dSsnj	if (okPosition(screen, &(ld.startSel), &(screen->startSel))) {
4181d522f475Smrg	    /* scan backward for beginning of group */
4182d522f475Smrg	    while (screen->startSel.row > 0 &&
4183956cc18dSsnj		   (LastTextCol(screen, ld.startSel, screen->startSel.row -
4184956cc18dSsnj				1) > 0 ||
4185956cc18dSsnj		    isPrevWrapped(startSel))) {
4186956cc18dSsnj		PrevRow(startSel);
4187d522f475Smrg	    }
4188d522f475Smrg	    screen->startSel.col = 0;
4189d522f475Smrg	    /* scan forward for end of group */
419020d2c4d2Smrg	    while (MoreRows(endSel) &&
4191956cc18dSsnj		   (LastTextCol(screen, ld.endSel, screen->endSel.row + 1) >
4192956cc18dSsnj		    0 ||
4193956cc18dSsnj		    LineTstWrapped(ld.endSel))) {
4194956cc18dSsnj		NextRow(endSel);
4195d522f475Smrg	    }
4196956cc18dSsnj	    trimLastLine(screen, &(ld.endSel), &(screen->endSel));
4197d522f475Smrg	}
4198d522f475Smrg	break;
4199d522f475Smrg
4200d522f475Smrg    case Select_PAGE:		/* everything one can see */
4201d522f475Smrg	TRACE(("Select_PAGE\n"));
4202d522f475Smrg	screen->startSel.row = 0;
4203d522f475Smrg	screen->startSel.col = 0;
420420d2c4d2Smrg	screen->endSel.row = MaxRows(screen);
4205d522f475Smrg	screen->endSel.col = 0;
4206d522f475Smrg	break;
4207d522f475Smrg
4208d522f475Smrg    case Select_ALL:		/* counts scrollback if in normal screen */
4209d522f475Smrg	TRACE(("Select_ALL\n"));
4210d522f475Smrg	screen->startSel.row = -screen->savedlines;
4211d522f475Smrg	screen->startSel.col = 0;
421220d2c4d2Smrg	screen->endSel.row = MaxRows(screen);
4213d522f475Smrg	screen->endSel.col = 0;
4214d522f475Smrg	break;
4215d522f475Smrg
4216d522f475Smrg#if OPT_SELECT_REGEX
4217d522f475Smrg    case Select_REGEX:
4218d522f475Smrg	do_select_regex(screen, &(screen->startSel), &(screen->endSel));
4219d522f475Smrg	break;
4220d522f475Smrg#endif
4221d522f475Smrg
4222d522f475Smrg    case NSELECTUNITS:		/* always ignore */
4223956cc18dSsnj	ignored = True;
4224956cc18dSsnj	break;
4225d522f475Smrg    }
4226d522f475Smrg
4227956cc18dSsnj    if (!ignored) {
4228956cc18dSsnj	/* check boundaries */
4229956cc18dSsnj	ScrollSelection(screen, 0, False);
4230956cc18dSsnj	TrackText(xw, &(screen->startSel), &(screen->endSel));
4231956cc18dSsnj    }
4232d522f475Smrg
4233d522f475Smrg    return;
4234d522f475Smrg}
4235d522f475Smrg
4236d522f475Smrg/* Guaranteed (first.row, first.col) <= (last.row, last.col) */
4237d522f475Smrgstatic void
4238d522f475SmrgTrackText(XtermWidget xw,
4239e0a2b6dfSmrg	  const CELL *firstp,
4240e0a2b6dfSmrg	  const CELL *lastp)
4241d522f475Smrg{
4242956cc18dSsnj    TScreen *screen = TScreenOf(xw);
4243d522f475Smrg    int from, to;
4244d522f475Smrg    CELL old_start, old_end;
4245d522f475Smrg    CELL first = *firstp;
4246d522f475Smrg    CELL last = *lastp;
4247d522f475Smrg
4248d522f475Smrg    TRACE(("TrackText(first=%d,%d, last=%d,%d)\n",
4249d522f475Smrg	   first.row, first.col, last.row, last.col));
4250d522f475Smrg
4251d522f475Smrg    old_start = screen->startH;
4252d522f475Smrg    old_end = screen->endH;
42530bd37d32Smrg    TRACE(("...previous(first=%d,%d, last=%d,%d)\n",
42540bd37d32Smrg	   old_start.row, old_start.col,
42550bd37d32Smrg	   old_end.row, old_end.col));
4256d522f475Smrg    if (isSameCELL(&first, &old_start) &&
42570bd37d32Smrg	isSameCELL(&last, &old_end)) {
4258d522f475Smrg	return;
42590bd37d32Smrg    }
42600bd37d32Smrg
4261d522f475Smrg    screen->startH = first;
4262d522f475Smrg    screen->endH = last;
4263d522f475Smrg    from = Coordinate(screen, &screen->startH);
4264d522f475Smrg    to = Coordinate(screen, &screen->endH);
4265d522f475Smrg    if (to <= screen->startHCoord || from > screen->endHCoord) {
4266d522f475Smrg	/* No overlap whatsoever between old and new hilite */
4267d522f475Smrg	ReHiliteText(xw, &old_start, &old_end);
4268d522f475Smrg	ReHiliteText(xw, &first, &last);
4269d522f475Smrg    } else {
4270d522f475Smrg	if (from < screen->startHCoord) {
4271d522f475Smrg	    /* Extend left end */
4272d522f475Smrg	    ReHiliteText(xw, &first, &old_start);
4273d522f475Smrg	} else if (from > screen->startHCoord) {
4274d522f475Smrg	    /* Shorten left end */
4275d522f475Smrg	    ReHiliteText(xw, &old_start, &first);
4276d522f475Smrg	}
4277d522f475Smrg	if (to > screen->endHCoord) {
4278d522f475Smrg	    /* Extend right end */
4279d522f475Smrg	    ReHiliteText(xw, &old_end, &last);
4280d522f475Smrg	} else if (to < screen->endHCoord) {
4281d522f475Smrg	    /* Shorten right end */
4282d522f475Smrg	    ReHiliteText(xw, &last, &old_end);
4283d522f475Smrg	}
4284d522f475Smrg    }
4285d522f475Smrg    screen->startHCoord = from;
4286d522f475Smrg    screen->endHCoord = to;
4287d522f475Smrg}
4288d522f475Smrg
4289f2e35a3aSmrgstatic void
4290f2e35a3aSmrgUnHiliteText(XtermWidget xw)
4291f2e35a3aSmrg{
4292f2e35a3aSmrg    TrackText(xw, &zeroCELL, &zeroCELL);
4293f2e35a3aSmrg}
4294f2e35a3aSmrg
4295d522f475Smrg/* Guaranteed that (first->row, first->col) <= (last->row, last->col) */
4296d522f475Smrgstatic void
4297d522f475SmrgReHiliteText(XtermWidget xw,
4298e0a2b6dfSmrg	     CELL *firstp,
4299e0a2b6dfSmrg	     CELL *lastp)
4300d522f475Smrg{
4301956cc18dSsnj    TScreen *screen = TScreenOf(xw);
4302d522f475Smrg    CELL first = *firstp;
4303d522f475Smrg    CELL last = *lastp;
4304d522f475Smrg
4305d522f475Smrg    TRACE(("ReHiliteText from %d.%d to %d.%d\n",
4306d522f475Smrg	   first.row, first.col, last.row, last.col));
4307d522f475Smrg
4308d522f475Smrg    if (first.row < 0)
4309d522f475Smrg	first.row = first.col = 0;
4310d522f475Smrg    else if (first.row > screen->max_row)
4311d522f475Smrg	return;			/* nothing to do, since last.row >= first.row */
4312d522f475Smrg
4313d522f475Smrg    if (last.row < 0)
4314d522f475Smrg	return;			/* nothing to do, since first.row <= last.row */
4315d522f475Smrg    else if (last.row > screen->max_row) {
4316d522f475Smrg	last.row = screen->max_row;
4317d522f475Smrg	last.col = MaxCols(screen);
4318d522f475Smrg    }
4319d522f475Smrg    if (isSameCELL(&first, &last))
4320d522f475Smrg	return;
4321d522f475Smrg
4322d522f475Smrg    if (!isSameRow(&first, &last)) {	/* do multiple rows */
43232e4f8982Smrg	int i;
4324d522f475Smrg	if ((i = screen->max_col - first.col + 1) > 0) {	/* first row */
4325d522f475Smrg	    ScrnRefresh(xw, first.row, first.col, 1, i, True);
4326d522f475Smrg	}
4327d522f475Smrg	if ((i = last.row - first.row - 1) > 0) {	/* middle rows */
4328d522f475Smrg	    ScrnRefresh(xw, first.row + 1, 0, i, MaxCols(screen), True);
4329d522f475Smrg	}
4330d522f475Smrg	if (last.col > 0 && last.row <= screen->max_row) {	/* last row */
4331d522f475Smrg	    ScrnRefresh(xw, last.row, 0, 1, last.col, True);
4332d522f475Smrg	}
4333d522f475Smrg    } else {			/* do single row */
4334d522f475Smrg	ScrnRefresh(xw, first.row, first.col, 1, last.col - first.col, True);
4335d522f475Smrg    }
4336d522f475Smrg}
4337d522f475Smrg
4338d522f475Smrg/*
4339913cc679Smrg * Guaranteed that (cellc->row, cellc->col) <= (cell->row, cell->col),
4340913cc679Smrg * and that both points are valid
4341d522f475Smrg * (may have cell->row = screen->max_row+1, cell->col = 0).
4342d522f475Smrg */
4343d522f475Smrgstatic void
4344d522f475SmrgSaltTextAway(XtermWidget xw,
4345f2e35a3aSmrg	     int which,
4346e0a2b6dfSmrg	     CELL *cellc,
4347e0a2b6dfSmrg	     CELL *cell)
4348d522f475Smrg{
4349956cc18dSsnj    TScreen *screen = TScreenOf(xw);
4350f2e35a3aSmrg    SelectedCells *scp;
4351f2e35a3aSmrg    int i;
4352d522f475Smrg    int eol;
4353f2e35a3aSmrg    int need = 0;
4354f2e35a3aSmrg    size_t have = 0;
4355d522f475Smrg    Char *line;
4356d522f475Smrg    Char *lp;
4357d522f475Smrg    CELL first = *cellc;
4358d522f475Smrg    CELL last = *cell;
4359d522f475Smrg
4360f2e35a3aSmrg    if (which < 0 || which >= MAX_SELECTIONS) {
4361f2e35a3aSmrg	TRACE(("SaltTextAway - which selection?\n"));
4362f2e35a3aSmrg	return;
4363f2e35a3aSmrg    }
4364f2e35a3aSmrg    scp = &(screen->selected_cells[which]);
4365f2e35a3aSmrg
4366f2e35a3aSmrg    TRACE(("SaltTextAway which=%d, first=%d,%d, last=%d,%d\n",
4367f2e35a3aSmrg	   which, first.row, first.col, last.row, last.col));
4368f2e35a3aSmrg
4369d522f475Smrg    if (isSameRow(&first, &last) && first.col > last.col) {
43702e4f8982Smrg	int tmp;
4371956cc18dSsnj	EXCHANGE(first.col, last.col, tmp);
4372d522f475Smrg    }
4373d522f475Smrg
4374d522f475Smrg    --last.col;
4375d522f475Smrg    /* first we need to know how long the string is before we can save it */
4376d522f475Smrg
4377d522f475Smrg    if (isSameRow(&last, &first)) {
4378f2e35a3aSmrg	need = Length(screen, first.row, first.col, last.col);
4379d522f475Smrg    } else {			/* two cases, cut is on same line, cut spans multiple lines */
4380f2e35a3aSmrg	need += Length(screen, first.row, first.col, screen->max_col) + 1;
4381d522f475Smrg	for (i = first.row + 1; i < last.row; i++)
4382f2e35a3aSmrg	    need += Length(screen, i, 0, screen->max_col) + 1;
4383d522f475Smrg	if (last.col >= 0)
4384f2e35a3aSmrg	    need += Length(screen, last.row, 0, last.col);
4385d522f475Smrg    }
4386d522f475Smrg
4387d522f475Smrg    /* UTF-8 may require more space */
4388d522f475Smrg    if_OPT_WIDE_CHARS(screen, {
4389f2e35a3aSmrg	if (need > 0) {
4390f2e35a3aSmrg	    if (screen->max_combining > 0)
4391f2e35a3aSmrg		need += screen->max_combining;
4392f2e35a3aSmrg	    need *= 6;
4393f2e35a3aSmrg	}
4394d522f475Smrg    });
4395d522f475Smrg
4396d522f475Smrg    /* now get some memory to save it in */
4397f2e35a3aSmrg    if (need < 0)
4398f2e35a3aSmrg	return;
4399d522f475Smrg
4400f2e35a3aSmrg    if (scp->data_limit <= (unsigned) need) {
4401f2e35a3aSmrg	if ((line = (Char *) malloc((size_t) need + 1)) == 0)
4402d522f475Smrg	    SysError(ERROR_BMALLOC2);
4403f2e35a3aSmrg	free(scp->data_buffer);
4404f2e35a3aSmrg	scp->data_buffer = line;
4405f2e35a3aSmrg	scp->data_limit = (size_t) (need + 1);
4406d522f475Smrg    } else {
4407f2e35a3aSmrg	line = scp->data_buffer;
4408d522f475Smrg    }
4409d522f475Smrg
4410f2e35a3aSmrg    if (line == 0)
4411d522f475Smrg	return;
4412d522f475Smrg
4413f2e35a3aSmrg    line[need] = '\0';		/* make sure it is null terminated */
4414d522f475Smrg    lp = line;			/* lp points to where to save the text */
4415d522f475Smrg    if (isSameRow(&last, &first)) {
4416d522f475Smrg	lp = SaveText(screen, last.row, first.col, last.col, lp, &eol);
4417d522f475Smrg    } else {
4418d522f475Smrg	lp = SaveText(screen, first.row, first.col, screen->max_col, lp, &eol);
4419d522f475Smrg	if (eol)
4420d522f475Smrg	    *lp++ = '\n';	/* put in newline at end of line */
4421d522f475Smrg	for (i = first.row + 1; i < last.row; i++) {
4422d522f475Smrg	    lp = SaveText(screen, i, 0, screen->max_col, lp, &eol);
4423d522f475Smrg	    if (eol)
4424d522f475Smrg		*lp++ = '\n';
4425d522f475Smrg	}
4426d522f475Smrg	if (last.col >= 0)
4427d522f475Smrg	    lp = SaveText(screen, last.row, 0, last.col, lp, &eol);
4428d522f475Smrg    }
4429d522f475Smrg    *lp = '\0';			/* make sure we have end marked */
4430d522f475Smrg
4431f2e35a3aSmrg    have = (size_t) (lp - line);
4432f2e35a3aSmrg    /*
4433f2e35a3aSmrg     * Scanning the buffer twice is unnecessary.  Discard unwanted memory if
4434f2e35a3aSmrg     * the estimate is too-far off.
4435f2e35a3aSmrg     */
4436f2e35a3aSmrg    if ((have * 2) < (size_t) need) {
4437f2e35a3aSmrg	Char *next;
4438f2e35a3aSmrg	scp->data_limit = have + 1;
4439f2e35a3aSmrg	next = realloc(line, scp->data_limit);
4440f2e35a3aSmrg	if (next == NULL) {
4441f2e35a3aSmrg	    free(line);
4442f2e35a3aSmrg	    scp->data_length = 0;
4443f2e35a3aSmrg	    scp->data_limit = 0;
4444f2e35a3aSmrg	}
4445f2e35a3aSmrg	scp->data_buffer = next;
4446f2e35a3aSmrg    }
4447f2e35a3aSmrg    scp->data_length = have;
4448d522f475Smrg
4449f2e35a3aSmrg    TRACE(("Salted TEXT:%u:%s\n", (unsigned) have,
4450f2e35a3aSmrg	   visibleChars(scp->data_buffer, (unsigned) have)));
4451d522f475Smrg}
4452d522f475Smrg
4453d522f475Smrg#if OPT_PASTE64
4454d522f475Smrgvoid
4455f2e35a3aSmrgClearSelectionBuffer(TScreen *screen, String selection)
4456d522f475Smrg{
4457f2e35a3aSmrg    int which = TargetToSelection(screen, selection);
4458f2e35a3aSmrg    SelectedCells *scp = &(screen->selected_cells[okSelectionCode(which)]);
4459f2e35a3aSmrg    FreeAndNull(scp->data_buffer);
4460f2e35a3aSmrg    scp->data_limit = 0;
4461f2e35a3aSmrg    scp->data_length = 0;
4462d522f475Smrg    screen->base64_count = 0;
4463d522f475Smrg}
4464d522f475Smrg
4465d522f475Smrgstatic void
4466f2e35a3aSmrgAppendStrToSelectionBuffer(SelectedCells * scp, Char *text, size_t len)
4467d522f475Smrg{
4468d522f475Smrg    if (len != 0) {
4469f2e35a3aSmrg	size_t j = (scp->data_length + len);
4470f2e35a3aSmrg	size_t k = j + (j >> 2) + 80;
4471f2e35a3aSmrg	if (j + 1 >= scp->data_limit) {
4472f2e35a3aSmrg	    Char *line;
4473f2e35a3aSmrg	    if (!scp->data_length) {
4474f2e35a3aSmrg		line = (Char *) malloc(k);
4475d522f475Smrg	    } else {
4476f2e35a3aSmrg		line = (Char *) realloc(scp->data_buffer, k);
4477d522f475Smrg	    }
4478f2e35a3aSmrg	    if (line == 0)
4479f2e35a3aSmrg		SysError(ERROR_BMALLOC2);
4480f2e35a3aSmrg	    scp->data_buffer = line;
4481f2e35a3aSmrg	    scp->data_limit = k;
4482d522f475Smrg	}
4483f2e35a3aSmrg	if (scp->data_buffer != 0) {
4484f2e35a3aSmrg	    memcpy(scp->data_buffer + scp->data_length, text, len);
4485f2e35a3aSmrg	    scp->data_length += len;
4486f2e35a3aSmrg	    scp->data_buffer[scp->data_length] = 0;
448720d2c4d2Smrg	}
4488d522f475Smrg    }
4489d522f475Smrg}
4490d522f475Smrg
4491d522f475Smrgvoid
4492f2e35a3aSmrgAppendToSelectionBuffer(TScreen *screen, unsigned c, String selection)
4493d522f475Smrg{
4494f2e35a3aSmrg    int which = TargetToSelection(screen, selection);
4495f2e35a3aSmrg    SelectedCells *scp = &(screen->selected_cells[okSelectionCode(which)]);
44962eaa94a1Schristos    unsigned six;
4497d522f475Smrg    Char ch;
4498d522f475Smrg
4499d522f475Smrg    /* Decode base64 character */
4500d522f475Smrg    if (c >= 'A' && c <= 'Z')
4501d522f475Smrg	six = c - 'A';
4502d522f475Smrg    else if (c >= 'a' && c <= 'z')
4503d522f475Smrg	six = c - 'a' + 26;
4504d522f475Smrg    else if (c >= '0' && c <= '9')
4505d522f475Smrg	six = c - '0' + 52;
4506d522f475Smrg    else if (c == '+')
4507d522f475Smrg	six = 62;
4508d522f475Smrg    else if (c == '/')
4509d522f475Smrg	six = 63;
4510d522f475Smrg    else
4511d522f475Smrg	return;
4512d522f475Smrg
4513d522f475Smrg    /* Accumulate bytes */
4514d522f475Smrg    switch (screen->base64_count) {
4515d522f475Smrg    case 0:
4516d522f475Smrg	screen->base64_accu = six;
4517d522f475Smrg	screen->base64_count = 6;
4518d522f475Smrg	break;
4519d522f475Smrg
4520d522f475Smrg    case 2:
45212eaa94a1Schristos	ch = CharOf((screen->base64_accu << 6) + six);
4522d522f475Smrg	screen->base64_count = 0;
4523f2e35a3aSmrg	AppendStrToSelectionBuffer(scp, &ch, (size_t) 1);
4524d522f475Smrg	break;
4525d522f475Smrg
4526d522f475Smrg    case 4:
45272eaa94a1Schristos	ch = CharOf((screen->base64_accu << 4) + (six >> 2));
4528d522f475Smrg	screen->base64_accu = (six & 0x3);
4529d522f475Smrg	screen->base64_count = 2;
4530f2e35a3aSmrg	AppendStrToSelectionBuffer(scp, &ch, (size_t) 1);
4531d522f475Smrg	break;
4532d522f475Smrg
4533d522f475Smrg    case 6:
45342eaa94a1Schristos	ch = CharOf((screen->base64_accu << 2) + (six >> 4));
4535d522f475Smrg	screen->base64_accu = (six & 0xF);
4536d522f475Smrg	screen->base64_count = 4;
4537f2e35a3aSmrg	AppendStrToSelectionBuffer(scp, &ch, (size_t) 1);
4538d522f475Smrg	break;
4539d522f475Smrg    }
4540d522f475Smrg}
4541d522f475Smrg
4542d522f475Smrgvoid
4543e0a2b6dfSmrgCompleteSelection(XtermWidget xw, String *args, Cardinal len)
4544d522f475Smrg{
4545956cc18dSsnj    TScreen *screen = TScreenOf(xw);
4546956cc18dSsnj
4547956cc18dSsnj    screen->base64_count = 0;
4548956cc18dSsnj    screen->base64_accu = 0;
4549d522f475Smrg    _OwnSelection(xw, args, len);
4550d522f475Smrg}
4551d522f475Smrg#endif /* OPT_PASTE64 */
4552d522f475Smrg
4553d522f475Smrgstatic Bool
4554d522f475Smrg_ConvertSelectionHelper(Widget w,
4555f2e35a3aSmrg			SelectedCells * scp,
4556894e0ac8Smrg			Atom *type,
4557d522f475Smrg			XtPointer *value,
4558d522f475Smrg			unsigned long *length,
4559d522f475Smrg			int *format,
4560d522f475Smrg			int (*conversion_function) (Display *,
4561d522f475Smrg						    char **, int,
4562d522f475Smrg						    XICCEncodingStyle,
4563d522f475Smrg						    XTextProperty *),
4564d522f475Smrg			XICCEncodingStyle conversion_style)
4565d522f475Smrg{
456601037d57Smrg    *value = 0;
456701037d57Smrg    *length = 0;
456801037d57Smrg    *type = 0;
456901037d57Smrg    *format = 0;
457001037d57Smrg
4571f2e35a3aSmrg    if (getXtermWidget(w) != 0) {
4572d522f475Smrg	Display *dpy = XtDisplay(w);
4573d522f475Smrg	XTextProperty textprop;
457401037d57Smrg	int out_n = 0;
457501037d57Smrg	char *result = 0;
4576f2e35a3aSmrg	char *the_data = (char *) scp->data_buffer;
457701037d57Smrg	char *the_next;
4578f2e35a3aSmrg	unsigned long remaining = scp->data_length;
457901037d57Smrg
458001037d57Smrg	TRACE(("converting %ld:'%s'\n",
4581f2e35a3aSmrg	       (long) scp->data_length,
4582f2e35a3aSmrg	       visibleChars(scp->data_buffer, (unsigned) scp->data_length)));
458301037d57Smrg	/*
458401037d57Smrg	 * For most selections, we can convert in one pass.  It is possible
458501037d57Smrg	 * that some applications contain embedded nulls, e.g., using xterm's
458601037d57Smrg	 * paste64 feature.  For those cases, we will build up the result in
458701037d57Smrg	 * parts.
458801037d57Smrg	 */
4589f2e35a3aSmrg	if (memchr(the_data, 0, scp->data_length) != 0) {
459001037d57Smrg	    TRACE(("selection contains embedded nulls\n"));
4591f2e35a3aSmrg	    result = calloc(scp->data_length + 1, sizeof(char));
459201037d57Smrg	}
4593d522f475Smrg
459401037d57Smrg      next_try:
459501037d57Smrg	memset(&textprop, 0, sizeof(textprop));
4596d522f475Smrg	if (conversion_function(dpy, &the_data, 1,
4597d522f475Smrg				conversion_style,
4598d522f475Smrg				&textprop) >= Success) {
459901037d57Smrg	    if ((result != 0)
460001037d57Smrg		&& (textprop.value != 0)
460101037d57Smrg		&& (textprop.format == 8)) {
460201037d57Smrg		char *text_values = (char *) textprop.value;
460301037d57Smrg		unsigned long in_n;
460401037d57Smrg
460501037d57Smrg		if (out_n == 0) {
460601037d57Smrg		    *value = result;
460701037d57Smrg		    *type = textprop.encoding;
460801037d57Smrg		    *format = textprop.format;
460901037d57Smrg		}
461001037d57Smrg		for (in_n = 0; in_n < textprop.nitems; ++in_n) {
461101037d57Smrg		    result[out_n++] = text_values[in_n];
461201037d57Smrg		}
461301037d57Smrg		*length += textprop.nitems;
461401037d57Smrg		if ((the_next = memchr(the_data, 0, remaining)) != 0) {
461501037d57Smrg		    unsigned long this_was = (unsigned long) (the_next - the_data);
461601037d57Smrg		    this_was++;
461701037d57Smrg		    the_data += this_was;
461801037d57Smrg		    remaining -= this_was;
461901037d57Smrg		    result[out_n++] = 0;
462001037d57Smrg		    *length += 1;
462101037d57Smrg		    if (remaining)
462201037d57Smrg			goto next_try;
462301037d57Smrg		}
462401037d57Smrg		return True;
462501037d57Smrg	    } else {
462601037d57Smrg		free(result);
462701037d57Smrg		*value = (XtPointer) textprop.value;
462801037d57Smrg		*length = textprop.nitems;
462901037d57Smrg		*type = textprop.encoding;
463001037d57Smrg		*format = textprop.format;
463101037d57Smrg		return True;
463201037d57Smrg	    }
4633d522f475Smrg	}
463401037d57Smrg	free(result);
4635d522f475Smrg    }
4636d522f475Smrg    return False;
4637d522f475Smrg}
4638d522f475Smrg
46392eaa94a1Schristosstatic Boolean
46402eaa94a1SchristosSaveConvertedLength(XtPointer *target, unsigned long source)
46412eaa94a1Schristos{
46422eaa94a1Schristos    Boolean result = False;
46432eaa94a1Schristos
46442eaa94a1Schristos    *target = XtMalloc(4);
46452eaa94a1Schristos    if (*target != 0) {
46462eaa94a1Schristos	result = True;
46472eaa94a1Schristos	if (sizeof(unsigned long) == 4) {
46482eaa94a1Schristos	    *(unsigned long *) *target = source;
46492eaa94a1Schristos	} else if (sizeof(unsigned) == 4) {
465020d2c4d2Smrg	    *(unsigned *) *target = (unsigned) source;
46512eaa94a1Schristos	} else if (sizeof(unsigned short) == 4) {
46522eaa94a1Schristos	    *(unsigned short *) *target = (unsigned short) source;
46532eaa94a1Schristos	} else {
46542eaa94a1Schristos	    /* FIXME - does this depend on byte-order? */
46552eaa94a1Schristos	    unsigned long temp = source;
465620d2c4d2Smrg	    memcpy((char *) *target,
465720d2c4d2Smrg		   ((char *) &temp) + sizeof(temp) - 4,
465820d2c4d2Smrg		   (size_t) 4);
46592eaa94a1Schristos	}
46602eaa94a1Schristos    }
46612eaa94a1Schristos    return result;
46622eaa94a1Schristos}
46632eaa94a1Schristos
4664f2e35a3aSmrg#define keepClipboard(d,atom) ((screen->keepClipboard) && \
4665f2e35a3aSmrg	 (atom == XA_CLIPBOARD(d)))
46662e4f8982Smrg
4667d522f475Smrgstatic Boolean
4668d522f475SmrgConvertSelection(Widget w,
4669894e0ac8Smrg		 Atom *selection,
4670894e0ac8Smrg		 Atom *target,
4671894e0ac8Smrg		 Atom *type,
4672d522f475Smrg		 XtPointer *value,
4673d522f475Smrg		 unsigned long *length,
4674d522f475Smrg		 int *format)
4675d522f475Smrg{
4676d522f475Smrg    Display *dpy = XtDisplay(w);
4677d522f475Smrg    TScreen *screen;
4678f2e35a3aSmrg    SelectedCells *scp;
4679d522f475Smrg    Bool result = False;
4680d522f475Smrg
46812e4f8982Smrg    Char *data;
46822e4f8982Smrg    unsigned long data_length;
46832e4f8982Smrg
4684956cc18dSsnj    XtermWidget xw;
4685956cc18dSsnj
4686956cc18dSsnj    if ((xw = getXtermWidget(w)) == 0)
4687d522f475Smrg	return False;
4688d522f475Smrg
4689956cc18dSsnj    screen = TScreenOf(xw);
4690d522f475Smrg
4691f2e35a3aSmrg    TRACE(("ConvertSelection %s -> %s\n",
4692f2e35a3aSmrg	   TraceAtomName(screen->display, *selection),
4693956cc18dSsnj	   visibleSelectionTarget(dpy, *target)));
4694956cc18dSsnj
4695f2e35a3aSmrg    if (keepClipboard(dpy, *selection)) {
46962e4f8982Smrg	TRACE(("asked for clipboard\n"));
4697f2e35a3aSmrg	scp = &(screen->clipboard_data);
46982e4f8982Smrg    } else {
46992e4f8982Smrg	TRACE(("asked for selection\n"));
4700f2e35a3aSmrg	scp = &(screen->selected_cells[AtomToSelection(dpy, *selection)]);
47012e4f8982Smrg    }
47022e4f8982Smrg
4703f2e35a3aSmrg    data = scp->data_buffer;
4704f2e35a3aSmrg    data_length = scp->data_length;
47052e4f8982Smrg    if (data == NULL) {
4706f2e35a3aSmrg	TRACE(("...no selection-data\n"));
4707f2e35a3aSmrg	return False;
470801037d57Smrg    }
470901037d57Smrg
4710d522f475Smrg    if (*target == XA_TARGETS(dpy)) {
4711d522f475Smrg	Atom *targetP;
4712d522f475Smrg	XPointer std_return = 0;
4713d522f475Smrg	unsigned long std_length;
4714d522f475Smrg
4715d522f475Smrg	if (XmuConvertStandardSelection(w, screen->selection_time, selection,
4716d522f475Smrg					target, type, &std_return,
4717d522f475Smrg					&std_length, format)) {
4718956cc18dSsnj	    Atom *my_targets = _SelectionTargets(w);
47192e4f8982Smrg	    Atom *allocP;
47202e4f8982Smrg	    Atom *std_targets;
4721956cc18dSsnj
4722956cc18dSsnj	    TRACE(("XmuConvertStandardSelection - success\n"));
4723a1f3da82Smrg	    std_targets = (Atom *) (void *) (std_return);
4724d522f475Smrg	    *length = std_length + 6;
4725d522f475Smrg
4726a1f3da82Smrg	    targetP = TypeXtMallocN(Atom, *length);
4727d522f475Smrg	    allocP = targetP;
4728d522f475Smrg
4729d522f475Smrg	    *value = (XtPointer) targetP;
4730d522f475Smrg
47310bd37d32Smrg	    if (my_targets != 0) {
47320bd37d32Smrg		while (*my_targets != None) {
47330bd37d32Smrg		    *targetP++ = *my_targets++;
47340bd37d32Smrg		}
4735956cc18dSsnj	    }
4736d522f475Smrg	    *targetP++ = XA_LENGTH(dpy);
4737d522f475Smrg	    *targetP++ = XA_LIST_LENGTH(dpy);
4738d522f475Smrg
47392eaa94a1Schristos	    *length = std_length + (unsigned long) (targetP - allocP);
4740d522f475Smrg
4741d522f475Smrg	    memcpy(targetP, std_targets, sizeof(Atom) * std_length);
4742d522f475Smrg	    XtFree((char *) std_targets);
4743d522f475Smrg	    *type = XA_ATOM;
4744d522f475Smrg	    *format = 32;
4745d522f475Smrg	    result = True;
4746956cc18dSsnj	} else {
4747956cc18dSsnj	    TRACE(("XmuConvertStandardSelection - failed\n"));
4748d522f475Smrg	}
4749d522f475Smrg    }
4750d522f475Smrg#if OPT_WIDE_CHARS
4751d522f475Smrg    else if (screen->wide_chars && *target == XA_STRING) {
4752d522f475Smrg	result =
4753f2e35a3aSmrg	    _ConvertSelectionHelper(w, scp,
4754f2e35a3aSmrg				    type, value, length, format,
4755d522f475Smrg				    Xutf8TextListToTextProperty,
4756d522f475Smrg				    XStringStyle);
4757956cc18dSsnj	TRACE(("...Xutf8TextListToTextProperty:%d\n", result));
4758d522f475Smrg    } else if (screen->wide_chars && *target == XA_UTF8_STRING(dpy)) {
4759d522f475Smrg	result =
4760f2e35a3aSmrg	    _ConvertSelectionHelper(w, scp,
4761f2e35a3aSmrg				    type, value, length, format,
4762d522f475Smrg				    Xutf8TextListToTextProperty,
4763d522f475Smrg				    XUTF8StringStyle);
4764956cc18dSsnj	TRACE(("...Xutf8TextListToTextProperty:%d\n", result));
4765d522f475Smrg    } else if (screen->wide_chars && *target == XA_TEXT(dpy)) {
4766d522f475Smrg	result =
4767f2e35a3aSmrg	    _ConvertSelectionHelper(w, scp,
4768f2e35a3aSmrg				    type, value, length, format,
4769d522f475Smrg				    Xutf8TextListToTextProperty,
4770d522f475Smrg				    XStdICCTextStyle);
4771956cc18dSsnj	TRACE(("...Xutf8TextListToTextProperty:%d\n", result));
4772d522f475Smrg    } else if (screen->wide_chars && *target == XA_COMPOUND_TEXT(dpy)) {
4773d522f475Smrg	result =
4774f2e35a3aSmrg	    _ConvertSelectionHelper(w, scp,
4775f2e35a3aSmrg				    type, value, length, format,
4776d522f475Smrg				    Xutf8TextListToTextProperty,
4777d522f475Smrg				    XCompoundTextStyle);
4778956cc18dSsnj	TRACE(("...Xutf8TextListToTextProperty:%d\n", result));
4779d522f475Smrg    }
4780d522f475Smrg#endif
4781d522f475Smrg
4782d522f475Smrg    else if (*target == XA_STRING) {	/* not wide_chars */
4783d522f475Smrg	/* We can only reach this point if the selection requestor
4784d522f475Smrg	   requested STRING before any of TEXT, COMPOUND_TEXT or
4785d522f475Smrg	   UTF8_STRING.  We therefore assume that the requestor is not
4786d522f475Smrg	   properly internationalised, and dump raw eight-bit data
4787d522f475Smrg	   with no conversion into the selection.  Yes, this breaks
4788d522f475Smrg	   the ICCCM in non-Latin-1 locales. */
4789d522f475Smrg	*type = XA_STRING;
4790f2e35a3aSmrg	*value = (XtPointer) data;
4791f2e35a3aSmrg	*length = data_length;
4792d522f475Smrg	*format = 8;
4793d522f475Smrg	result = True;
4794956cc18dSsnj	TRACE(("...raw 8-bit data:%d\n", result));
4795d522f475Smrg    } else if (*target == XA_TEXT(dpy)) {	/* not wide_chars */
4796d522f475Smrg	result =
4797f2e35a3aSmrg	    _ConvertSelectionHelper(w, scp,
4798f2e35a3aSmrg				    type, value, length, format,
4799d522f475Smrg				    XmbTextListToTextProperty,
4800d522f475Smrg				    XStdICCTextStyle);
4801956cc18dSsnj	TRACE(("...XmbTextListToTextProperty(StdICC):%d\n", result));
4802d522f475Smrg    } else if (*target == XA_COMPOUND_TEXT(dpy)) {	/* not wide_chars */
4803d522f475Smrg	result =
4804f2e35a3aSmrg	    _ConvertSelectionHelper(w, scp,
4805f2e35a3aSmrg				    type, value, length, format,
4806d522f475Smrg				    XmbTextListToTextProperty,
4807d522f475Smrg				    XCompoundTextStyle);
4808956cc18dSsnj	TRACE(("...XmbTextListToTextProperty(Compound):%d\n", result));
4809d522f475Smrg    }
4810d522f475Smrg#ifdef X_HAVE_UTF8_STRING
4811d522f475Smrg    else if (*target == XA_UTF8_STRING(dpy)) {	/* not wide_chars */
4812d522f475Smrg	result =
4813f2e35a3aSmrg	    _ConvertSelectionHelper(w, scp,
4814f2e35a3aSmrg				    type, value, length, format,
4815d522f475Smrg				    XmbTextListToTextProperty,
4816d522f475Smrg				    XUTF8StringStyle);
4817956cc18dSsnj	TRACE(("...XmbTextListToTextProperty(UTF8):%d\n", result));
4818d522f475Smrg    }
4819d522f475Smrg#endif
4820d522f475Smrg    else if (*target == XA_LIST_LENGTH(dpy)) {
482120d2c4d2Smrg	result = SaveConvertedLength(value, (unsigned long) 1);
4822d522f475Smrg	*type = XA_INTEGER;
4823d522f475Smrg	*length = 1;
4824d522f475Smrg	*format = 32;
4825956cc18dSsnj	TRACE(("...list of values:%d\n", result));
4826d522f475Smrg    } else if (*target == XA_LENGTH(dpy)) {
4827d522f475Smrg	/* This value is wrong if we have UTF-8 text */
4828f2e35a3aSmrg	result = SaveConvertedLength(value, scp->data_length);
4829d522f475Smrg	*type = XA_INTEGER;
4830d522f475Smrg	*length = 1;
4831d522f475Smrg	*format = 32;
4832956cc18dSsnj	TRACE(("...list of values:%d\n", result));
4833d522f475Smrg    } else if (XmuConvertStandardSelection(w,
4834d522f475Smrg					   screen->selection_time, selection,
4835d522f475Smrg					   target, type, (XPointer *) value,
4836d522f475Smrg					   length, format)) {
4837d522f475Smrg	result = True;
4838956cc18dSsnj	TRACE(("...XmuConvertStandardSelection:%d\n", result));
4839d522f475Smrg    }
4840d522f475Smrg
4841d522f475Smrg    /* else */
48422eaa94a1Schristos    return (Boolean) result;
4843d522f475Smrg}
4844d522f475Smrg
4845d522f475Smrgstatic void
4846894e0ac8SmrgLoseSelection(Widget w, Atom *selection)
4847d522f475Smrg{
4848d522f475Smrg    TScreen *screen;
4849d522f475Smrg    Atom *atomP;
4850d522f475Smrg    Cardinal i;
4851d522f475Smrg
4852956cc18dSsnj    XtermWidget xw;
4853956cc18dSsnj
4854956cc18dSsnj    if ((xw = getXtermWidget(w)) == 0)
4855d522f475Smrg	return;
4856d522f475Smrg
4857956cc18dSsnj    screen = TScreenOf(xw);
4858913cc679Smrg    TRACE(("LoseSelection %s\n", TraceAtomName(screen->display, *selection)));
485901037d57Smrg
4860d522f475Smrg    for (i = 0, atomP = screen->selection_atoms;
4861d522f475Smrg	 i < screen->selection_count; i++, atomP++) {
4862d522f475Smrg	if (*selection == *atomP)
4863d522f475Smrg	    *atomP = (Atom) 0;
4864d522f475Smrg	if (CutBuffer(*atomP) >= 0) {
4865d522f475Smrg	    *atomP = (Atom) 0;
4866d522f475Smrg	}
4867d522f475Smrg    }
4868d522f475Smrg
4869d522f475Smrg    for (i = screen->selection_count; i; i--) {
4870d522f475Smrg	if (screen->selection_atoms[i - 1] != 0)
4871d522f475Smrg	    break;
4872d522f475Smrg    }
4873d522f475Smrg    screen->selection_count = i;
4874d522f475Smrg
4875d522f475Smrg    for (i = 0, atomP = screen->selection_atoms;
4876d522f475Smrg	 i < screen->selection_count; i++, atomP++) {
4877d522f475Smrg	if (*atomP == (Atom) 0) {
4878d522f475Smrg	    *atomP = screen->selection_atoms[--screen->selection_count];
4879d522f475Smrg	}
4880d522f475Smrg    }
4881d522f475Smrg
4882d522f475Smrg    if (screen->selection_count == 0)
4883f2e35a3aSmrg	UnHiliteText(xw);
4884d522f475Smrg}
4885d522f475Smrg
4886d522f475Smrg/* ARGSUSED */
4887d522f475Smrgstatic void
4888d522f475SmrgSelectionDone(Widget w GCC_UNUSED,
4889894e0ac8Smrg	      Atom *selection GCC_UNUSED,
4890894e0ac8Smrg	      Atom *target GCC_UNUSED)
4891d522f475Smrg{
4892d522f475Smrg    /* empty proc so Intrinsics know we want to keep storage */
489301037d57Smrg    TRACE(("SelectionDone\n"));
4894d522f475Smrg}
4895d522f475Smrg
4896d522f475Smrgstatic void
4897d522f475Smrg_OwnSelection(XtermWidget xw,
4898e0a2b6dfSmrg	      String *selections,
4899d522f475Smrg	      Cardinal count)
4900d522f475Smrg{
4901956cc18dSsnj    TScreen *screen = TScreenOf(xw);
4902f2e35a3aSmrg    Display *dpy = screen->display;
4903d522f475Smrg    Atom *atoms = screen->selection_atoms;
4904d522f475Smrg    Cardinal i;
4905d522f475Smrg    Bool have_selection = False;
4906f2e35a3aSmrg    SelectedCells *scp;
4907d522f475Smrg
490820d2c4d2Smrg    if (count == 0)
490920d2c4d2Smrg	return;
4910d522f475Smrg
4911f2e35a3aSmrg    TRACE(("_OwnSelection count %d\n", count));
4912d522f475Smrg    selections = MapSelections(xw, selections, count);
4913d522f475Smrg
4914d522f475Smrg    if (count > screen->sel_atoms_size) {
4915d522f475Smrg	XtFree((char *) atoms);
4916a1f3da82Smrg	atoms = TypeXtMallocN(Atom, count);
4917d522f475Smrg	screen->selection_atoms = atoms;
4918d522f475Smrg	screen->sel_atoms_size = count;
4919d522f475Smrg    }
4920f2e35a3aSmrg    XmuInternStrings(dpy, selections, count, atoms);
4921d522f475Smrg    for (i = 0; i < count; i++) {
4922d522f475Smrg	int cutbuffer = CutBuffer(atoms[i]);
4923d522f475Smrg	if (cutbuffer >= 0) {
49242eaa94a1Schristos	    unsigned long limit =
4925f2e35a3aSmrg	    (unsigned long) (4 * XMaxRequestSize(dpy) - 32);
4926f2e35a3aSmrg	    scp = &(screen->selected_cells[CutBufferToCode(cutbuffer)]);
4927f2e35a3aSmrg	    if (scp->data_length > limit) {
492820d2c4d2Smrg		TRACE(("selection too big (%lu bytes), not storing in CUT_BUFFER%d\n",
4929f2e35a3aSmrg		       (unsigned long) scp->data_length, cutbuffer));
49300bd37d32Smrg		xtermWarning("selection too big (%lu bytes), not storing in CUT_BUFFER%d\n",
4931f2e35a3aSmrg			     (unsigned long) scp->data_length, cutbuffer);
4932d522f475Smrg	    } else {
4933d522f475Smrg		/* This used to just use the UTF-8 data, which was totally
4934894e0ac8Smrg		 * broken as not even the corresponding paste code in xterm
4935d522f475Smrg		 * understood this!  So now it converts to Latin1 first.
4936d522f475Smrg		 *   Robert Brady, 2000-09-05
4937d522f475Smrg		 */
4938f2e35a3aSmrg		unsigned long length = scp->data_length;
4939f2e35a3aSmrg		Char *data = scp->data_buffer;
4940d522f475Smrg		if_OPT_WIDE_CHARS((screen), {
4941956cc18dSsnj		    data = UTF8toLatin1(screen, data, length, &length);
4942d522f475Smrg		});
4943d522f475Smrg		TRACE(("XStoreBuffer(%d)\n", cutbuffer));
4944f2e35a3aSmrg		XStoreBuffer(dpy,
4945d522f475Smrg			     (char *) data,
4946d522f475Smrg			     (int) length,
4947d522f475Smrg			     cutbuffer);
4948d522f475Smrg	    }
4949f2e35a3aSmrg	} else {
4950f2e35a3aSmrg	    int which = AtomToSelection(dpy, atoms[i]);
4951f2e35a3aSmrg	    if (keepClipboard(dpy, atoms[i])) {
4952f2e35a3aSmrg		Char *buf;
4953f2e35a3aSmrg		SelectedCells *tcp = &(screen->clipboard_data);
4954f2e35a3aSmrg		TRACE(("saving selection to clipboard buffer\n"));
4955f2e35a3aSmrg		scp = &(screen->selected_cells[CLIPBOARD_CODE]);
4956f2e35a3aSmrg		if ((buf = (Char *) malloc((size_t) scp->data_length)) == 0)
4957f2e35a3aSmrg		    SysError(ERROR_BMALLOC2);
49582e4f8982Smrg
4959f2e35a3aSmrg		free(tcp->data_buffer);
4960f2e35a3aSmrg		memcpy(buf, scp->data_buffer, scp->data_length);
4961f2e35a3aSmrg		tcp->data_buffer = buf;
4962f2e35a3aSmrg		tcp->data_limit = scp->data_length;
4963f2e35a3aSmrg		tcp->data_length = scp->data_length;
4964f2e35a3aSmrg	    }
4965f2e35a3aSmrg	    scp = &(screen->selected_cells[which]);
4966f2e35a3aSmrg	    if (scp->data_length == 0) {
4967f2e35a3aSmrg		TRACE(("XtDisownSelection(%s, @%ld)\n",
4968f2e35a3aSmrg		       TraceAtomName(screen->display, atoms[i]),
4969f2e35a3aSmrg		       (long) screen->selection_time));
4970f2e35a3aSmrg		XtDisownSelection((Widget) xw,
4971f2e35a3aSmrg				  atoms[i],
4972f2e35a3aSmrg				  screen->selection_time);
4973f2e35a3aSmrg	    } else if (!screen->replyToEmacs && atoms[i] != 0) {
4974f2e35a3aSmrg		TRACE(("XtOwnSelection(%s, @%ld)\n",
4975f2e35a3aSmrg		       TraceAtomName(screen->display, atoms[i]),
4976f2e35a3aSmrg		       (long) screen->selection_time));
4977f2e35a3aSmrg		have_selection |=
4978f2e35a3aSmrg		    XtOwnSelection((Widget) xw, atoms[i],
4979f2e35a3aSmrg				   screen->selection_time,
4980f2e35a3aSmrg				   ConvertSelection,
4981f2e35a3aSmrg				   LoseSelection,
4982f2e35a3aSmrg				   SelectionDone);
4983f2e35a3aSmrg	    }
4984d522f475Smrg	}
4985f2e35a3aSmrg	TRACE(("... _OwnSelection used length %lu value %s\n",
4986f2e35a3aSmrg	       (unsigned long) scp->data_length,
4987f2e35a3aSmrg	       visibleChars(scp->data_buffer,
4988f2e35a3aSmrg			    (unsigned) scp->data_length)));
4989d522f475Smrg    }
4990d522f475Smrg    if (!screen->replyToEmacs)
4991d522f475Smrg	screen->selection_count = count;
4992d522f475Smrg    if (!have_selection)
4993f2e35a3aSmrg	UnHiliteText(xw);
4994d522f475Smrg}
4995d522f475Smrg
4996d522f475Smrgstatic void
4997e0a2b6dfSmrgResetSelectionState(TScreen *screen)
4998d522f475Smrg{
4999d522f475Smrg    screen->selection_count = 0;
5000d522f475Smrg    screen->startH = zeroCELL;
5001d522f475Smrg    screen->endH = zeroCELL;
5002d522f475Smrg}
5003d522f475Smrg
5004d522f475Smrgvoid
5005d522f475SmrgDisownSelection(XtermWidget xw)
5006d522f475Smrg{
5007956cc18dSsnj    TScreen *screen = TScreenOf(xw);
5008d522f475Smrg    Atom *atoms = screen->selection_atoms;
5009d522f475Smrg    Cardinal count = screen->selection_count;
5010d522f475Smrg    Cardinal i;
5011d522f475Smrg
5012d522f475Smrg    TRACE(("DisownSelection count %d, start %d.%d, end %d.%d\n",
5013d522f475Smrg	   count,
5014d522f475Smrg	   screen->startH.row,
5015d522f475Smrg	   screen->startH.col,
5016d522f475Smrg	   screen->endH.row,
5017d522f475Smrg	   screen->endH.col));
5018d522f475Smrg
5019d522f475Smrg    for (i = 0; i < count; i++) {
5020d522f475Smrg	int cutbuffer = CutBuffer(atoms[i]);
5021d522f475Smrg	if (cutbuffer < 0) {
5022d522f475Smrg	    XtDisownSelection((Widget) xw, atoms[i],
5023d522f475Smrg			      screen->selection_time);
5024d522f475Smrg	}
5025d522f475Smrg    }
5026d522f475Smrg    /*
5027d522f475Smrg     * If none of the callbacks via XtDisownSelection() reset highlighting
5028d522f475Smrg     * do it now.
5029d522f475Smrg     */
5030d522f475Smrg    if (ScrnHaveSelection(screen)) {
5031d522f475Smrg	/* save data which will be reset */
5032d522f475Smrg	CELL first = screen->startH;
5033d522f475Smrg	CELL last = screen->endH;
5034d522f475Smrg
5035d522f475Smrg	ResetSelectionState(screen);
5036d522f475Smrg	ReHiliteText(xw, &first, &last);
5037d522f475Smrg    } else {
5038d522f475Smrg	ResetSelectionState(screen);
5039d522f475Smrg    }
5040d522f475Smrg}
5041d522f475Smrg
5042d522f475Smrgvoid
5043d522f475SmrgUnhiliteSelection(XtermWidget xw)
5044d522f475Smrg{
5045956cc18dSsnj    TScreen *screen = TScreenOf(xw);
5046d522f475Smrg
5047d522f475Smrg    if (ScrnHaveSelection(screen)) {
5048d522f475Smrg	CELL first = screen->startH;
5049d522f475Smrg	CELL last = screen->endH;
5050d522f475Smrg
5051d522f475Smrg	screen->startH = zeroCELL;
5052d522f475Smrg	screen->endH = zeroCELL;
5053d522f475Smrg	ReHiliteText(xw, &first, &last);
5054d522f475Smrg    }
5055d522f475Smrg}
5056d522f475Smrg
5057d522f475Smrg/* returns number of chars in line from scol to ecol out */
5058d522f475Smrg/* ARGSUSED */
5059d522f475Smrgstatic int
5060e0a2b6dfSmrgLength(TScreen *screen,
5061d522f475Smrg       int row,
5062d522f475Smrg       int scol,
5063d522f475Smrg       int ecol)
5064d522f475Smrg{
506501037d57Smrg    CLineData *ld = GET_LINEDATA(screen, row);
506601037d57Smrg    const int lastcol = LastTextCol(screen, ld, row);
5067d522f475Smrg
5068d522f475Smrg    if (ecol > lastcol)
5069d522f475Smrg	ecol = lastcol;
5070d522f475Smrg    return (ecol - scol + 1);
5071d522f475Smrg}
5072d522f475Smrg
5073d522f475Smrg/* copies text into line, preallocated */
5074d522f475Smrgstatic Char *
5075e0a2b6dfSmrgSaveText(TScreen *screen,
5076d522f475Smrg	 int row,
5077d522f475Smrg	 int scol,
5078d522f475Smrg	 int ecol,
5079e0a2b6dfSmrg	 Char *lp,		/* pointer to where to put the text */
5080d522f475Smrg	 int *eol)
5081d522f475Smrg{
5082956cc18dSsnj    LineData *ld;
5083d522f475Smrg    int i = 0;
5084d522f475Smrg    Char *result = lp;
5085d522f475Smrg#if OPT_WIDE_CHARS
50862eaa94a1Schristos    unsigned previous = 0;
5087d522f475Smrg#endif
5088d522f475Smrg
5089956cc18dSsnj    ld = GET_LINEDATA(screen, row);
5090d522f475Smrg    i = Length(screen, row, scol, ecol);
5091d522f475Smrg    ecol = scol + i;
5092d522f475Smrg#if OPT_DEC_CHRSET
5093956cc18dSsnj    if (CSET_DOUBLE(GetLineDblCS(ld))) {
5094d522f475Smrg	scol = (scol + 0) / 2;
5095d522f475Smrg	ecol = (ecol + 1) / 2;
5096d522f475Smrg    }
5097d522f475Smrg#endif
5098956cc18dSsnj    *eol = !LineTstWrapped(ld);
5099d522f475Smrg    for (i = scol; i < ecol; i++) {
51002e4f8982Smrg	unsigned c;
51010bd37d32Smrg	assert(i < (int) ld->lineSize);
5102956cc18dSsnj	c = E2A(ld->charData[i]);
5103d522f475Smrg#if OPT_WIDE_CHARS
5104d522f475Smrg	/* We want to strip out every occurrence of HIDDEN_CHAR AFTER a
5105d522f475Smrg	 * wide character.
5106d522f475Smrg	 */
5107894e0ac8Smrg	if (c == HIDDEN_CHAR) {
5108894e0ac8Smrg	    if (isWide((int) previous)) {
5109894e0ac8Smrg		previous = c;
5110894e0ac8Smrg		/* Combining characters attached to double-width characters
5111894e0ac8Smrg		   are in memory attached to the HIDDEN_CHAR */
5112894e0ac8Smrg		if_OPT_WIDE_CHARS(screen, {
5113894e0ac8Smrg		    if ((screen->utf8_nrc_mode | screen->utf8_mode) != uFalse) {
5114894e0ac8Smrg			size_t off;
5115894e0ac8Smrg			for_each_combData(off, ld) {
51162e4f8982Smrg			    unsigned ch = ld->combData[off][i];
5117894e0ac8Smrg			    if (ch == 0)
5118894e0ac8Smrg				break;
5119894e0ac8Smrg			    lp = convertToUTF8(lp, ch);
5120894e0ac8Smrg			}
5121d522f475Smrg		    }
5122894e0ac8Smrg		});
5123894e0ac8Smrg		continue;
5124894e0ac8Smrg	    } else {
5125894e0ac8Smrg		c = ' ';	/* should not happen, but just in case... */
5126894e0ac8Smrg	    }
5127d522f475Smrg	}
5128d522f475Smrg	previous = c;
5129e0a2b6dfSmrg	if ((screen->utf8_nrc_mode | screen->utf8_mode) != uFalse) {
5130d522f475Smrg	    lp = convertToUTF8(lp, (c != 0) ? c : ' ');
5131d522f475Smrg	    if_OPT_WIDE_CHARS(screen, {
5132956cc18dSsnj		size_t off;
5133956cc18dSsnj		for_each_combData(off, ld) {
51342e4f8982Smrg		    unsigned ch = ld->combData[off][i];
5135956cc18dSsnj		    if (ch == 0)
5136d522f475Smrg			break;
5137d522f475Smrg		    lp = convertToUTF8(lp, ch);
5138d522f475Smrg		}
5139d522f475Smrg	    });
5140d522f475Smrg	} else
5141d522f475Smrg#endif
5142d522f475Smrg	{
5143d522f475Smrg	    if (c == 0) {
5144d522f475Smrg		c = E2A(' ');
5145d522f475Smrg	    } else if (c < E2A(' ')) {
5146d522f475Smrg		c = DECtoASCII(c);
5147d522f475Smrg	    } else if (c == 0x7f) {
5148d522f475Smrg		c = 0x5f;
5149d522f475Smrg	    }
51502eaa94a1Schristos	    *lp++ = CharOf(A2E(c));
5151d522f475Smrg	}
5152d522f475Smrg	if (c != E2A(' '))
5153d522f475Smrg	    result = lp;
5154d522f475Smrg    }
5155d522f475Smrg
5156d522f475Smrg    /*
5157d522f475Smrg     * If requested, trim trailing blanks from selected lines.  Do not do this
5158d522f475Smrg     * if the line is wrapped.
5159d522f475Smrg     */
5160d522f475Smrg    if (!*eol || !screen->trim_selection)
5161d522f475Smrg	result = lp;
5162d522f475Smrg
5163d522f475Smrg    return (result);
5164d522f475Smrg}
5165d522f475Smrg
5166f2e35a3aSmrg/*
5167f2e35a3aSmrg * This adds together the bits:
5168f2e35a3aSmrg *   shift key   -> 1
5169f2e35a3aSmrg *   meta key    -> 2
5170f2e35a3aSmrg *   control key -> 4
5171f2e35a3aSmrg */
5172f2e35a3aSmrgstatic unsigned
5173f2e35a3aSmrgKeyState(XtermWidget xw, unsigned x)
5174f2e35a3aSmrg{
5175f2e35a3aSmrg    return ((((x) & (ShiftMask | ControlMask)))
5176f2e35a3aSmrg	    + (((x) & MetaMask(xw)) ? 2 : 0));
5177f2e35a3aSmrg}
5178f2e35a3aSmrg
5179f2e35a3aSmrg/* 32 + following 8-bit word:
5180d522f475Smrg
5181d522f475Smrg   1:0  Button no: 0, 1, 2.  3=release.
5182d522f475Smrg     2  shift
5183d522f475Smrg     3  meta
5184d522f475Smrg     4  ctrl
5185d522f475Smrg     5  set for motion notify
5186f2e35a3aSmrg     6  set for wheel (and button 6 and 7)
5187f2e35a3aSmrg     7  set for buttons 8 to 11
5188d522f475Smrg*/
5189d522f475Smrg
5190d522f475Smrg/* Position: 32 - 255. */
5191a1f3da82Smrgstatic int
5192f2e35a3aSmrgBtnCode(XtermWidget xw, XButtonEvent *event, int button)
5193d522f475Smrg{
5194f2e35a3aSmrg    int result = (int) (32 + (KeyState(xw, event->state) << 2));
5195d522f475Smrg
51960bd37d32Smrg    if (event->type == MotionNotify)
51970bd37d32Smrg	result += 32;
51980bd37d32Smrg
5199f2e35a3aSmrg    if (button < 0) {
5200d522f475Smrg	result += 3;
5201d522f475Smrg    } else {
5202f2e35a3aSmrg	result += button & 3;
5203f2e35a3aSmrg	if (button & 4)
5204f2e35a3aSmrg	    result += 64;
5205f2e35a3aSmrg	if (button & 8)
5206f2e35a3aSmrg	    result += 128;
5207d522f475Smrg    }
52080bd37d32Smrg    TRACE(("BtnCode button %d, %s state " FMT_MODIFIER_NAMES " ->%#x\n",
52090bd37d32Smrg	   button,
52100bd37d32Smrg	   visibleEventType(event->type),
52110bd37d32Smrg	   ARG_MODIFIER_NAMES(event->state),
52120bd37d32Smrg	   result));
5213a1f3da82Smrg    return result;
5214a1f3da82Smrg}
5215a1f3da82Smrg
5216a1f3da82Smrgstatic unsigned
5217913cc679SmrgEmitButtonCode(XtermWidget xw,
5218e0a2b6dfSmrg	       Char *line,
52190bd37d32Smrg	       unsigned count,
5220894e0ac8Smrg	       XButtonEvent *event,
52210bd37d32Smrg	       int button)
5222a1f3da82Smrg{
5223913cc679Smrg    TScreen *screen = TScreenOf(xw);
52240bd37d32Smrg    int value;
5225a1f3da82Smrg
5226913cc679Smrg    if (okSendMousePos(xw) == X10_MOUSE) {
52270bd37d32Smrg	value = CharOf(' ' + button);
5228a1f3da82Smrg    } else {
5229f2e35a3aSmrg	value = BtnCode(xw, event, button);
52300bd37d32Smrg    }
52310bd37d32Smrg
52320bd37d32Smrg    switch (screen->extend_coords) {
52330bd37d32Smrg    default:
52340bd37d32Smrg	line[count++] = CharOf(value);
52350bd37d32Smrg	break;
52360bd37d32Smrg    case SET_SGR_EXT_MODE_MOUSE:
5237f2e35a3aSmrg    case SET_PIXEL_POSITION_MOUSE:
52380bd37d32Smrg	value -= 32;		/* encoding starts at zero */
52390bd37d32Smrg	/* FALLTHRU */
52400bd37d32Smrg    case SET_URXVT_EXT_MODE_MOUSE:
52410bd37d32Smrg	count += (unsigned) sprintf((char *) line + count, "%d", value);
52420bd37d32Smrg	break;
52430bd37d32Smrg    case SET_EXT_MODE_MOUSE:
52440bd37d32Smrg	if (value < 128) {
52450bd37d32Smrg	    line[count++] = CharOf(value);
52460bd37d32Smrg	} else {
52470bd37d32Smrg	    line[count++] = CharOf(0xC0 + (value >> 6));
52480bd37d32Smrg	    line[count++] = CharOf(0x80 + (value & 0x3F));
52490bd37d32Smrg	}
52500bd37d32Smrg	break;
5251a1f3da82Smrg    }
5252a1f3da82Smrg    return count;
5253d522f475Smrg}
5254d522f475Smrg
52550bd37d32Smrgstatic int
52560bd37d32SmrgFirstBitN(int bits)
52570bd37d32Smrg{
52580bd37d32Smrg    int result = -1;
52590bd37d32Smrg    if (bits > 0) {
52600bd37d32Smrg	result = 0;
52610bd37d32Smrg	while (!(bits & 1)) {
52620bd37d32Smrg	    bits /= 2;
52630bd37d32Smrg	    ++result;
52640bd37d32Smrg	}
52650bd37d32Smrg    }
52660bd37d32Smrg    return result;
52670bd37d32Smrg}
52680bd37d32Smrg
52690bd37d32Smrg#define ButtonBit(button) ((button >= 0) ? (1 << (button)) : 0)
52700bd37d32Smrg
5271913cc679Smrg#define EMIT_BUTTON(button) EmitButtonCode(xw, line, count, event, button)
52720bd37d32Smrg
5273d522f475Smrgstatic void
5274894e0ac8SmrgEditorButton(XtermWidget xw, XButtonEvent *event)
5275d522f475Smrg{
5276956cc18dSsnj    TScreen *screen = TScreenOf(xw);
5277d522f475Smrg    int pty = screen->respond;
52780bd37d32Smrg    int mouse_limit = MouseLimit(screen);
52790bd37d32Smrg    Char line[32];
52800bd37d32Smrg    Char final = 'M';
5281d522f475Smrg    int row, col;
5282d522f475Smrg    int button;
5283d522f475Smrg    unsigned count = 0;
5284d522f475Smrg    Boolean changed = True;
5285d522f475Smrg
5286d522f475Smrg    /* If button event, get button # adjusted for DEC compatibility */
52872eaa94a1Schristos    button = (int) (event->button - 1);
5288d522f475Smrg    if (button >= 3)
5289d522f475Smrg	button++;
5290d522f475Smrg
5291f2e35a3aSmrg    /* Ignore buttons that cannot be encoded */
5292f2e35a3aSmrg    if (screen->send_mouse_pos == X10_MOUSE) {
5293f2e35a3aSmrg	if (button > 3)
5294f2e35a3aSmrg	    return;
5295f2e35a3aSmrg    } else if (screen->extend_coords == SET_SGR_EXT_MODE_MOUSE
5296f2e35a3aSmrg	       || screen->extend_coords == SET_URXVT_EXT_MODE_MOUSE
5297f2e35a3aSmrg	       || screen->extend_coords == SET_PIXEL_POSITION_MOUSE) {
5298f2e35a3aSmrg	if (button > 15) {
5299f2e35a3aSmrg	    return;
5300f2e35a3aSmrg	}
5301f2e35a3aSmrg    } else {
5302f2e35a3aSmrg	if (button > 11) {
5303f2e35a3aSmrg	    return;
5304f2e35a3aSmrg	}
5305f2e35a3aSmrg    }
5306d522f475Smrg
5307f2e35a3aSmrg    if (screen->extend_coords == SET_PIXEL_POSITION_MOUSE) {
5308f2e35a3aSmrg	row = event->y - OriginY(screen);
5309f2e35a3aSmrg	col = event->x - OriginX(screen);
5310f2e35a3aSmrg    } else {
5311f2e35a3aSmrg	/* Compute character position of mouse pointer */
5312f2e35a3aSmrg	row = (event->y - screen->border) / FontHeight(screen);
5313f2e35a3aSmrg	col = (event->x - OriginX(screen)) / FontWidth(screen);
5314d522f475Smrg
5315f2e35a3aSmrg	/* Limit to screen dimensions */
5316f2e35a3aSmrg	if (row < 0)
5317f2e35a3aSmrg	    row = 0;
5318f2e35a3aSmrg	else if (row > screen->max_row)
5319f2e35a3aSmrg	    row = screen->max_row;
5320492d43a5Smrg
5321f2e35a3aSmrg	if (col < 0)
5322f2e35a3aSmrg	    col = 0;
5323f2e35a3aSmrg	else if (col > screen->max_col)
5324f2e35a3aSmrg	    col = screen->max_col;
5325f2e35a3aSmrg
5326f2e35a3aSmrg	if (mouse_limit > 0) {
5327f2e35a3aSmrg	    /* Limit to representable mouse dimensions */
5328f2e35a3aSmrg	    if (row > mouse_limit)
5329f2e35a3aSmrg		row = mouse_limit;
5330f2e35a3aSmrg	    if (col > mouse_limit)
5331f2e35a3aSmrg		col = mouse_limit;
5332f2e35a3aSmrg	}
53330bd37d32Smrg    }
5334d522f475Smrg
5335d522f475Smrg    /* Build key sequence starting with \E[M */
5336d522f475Smrg    if (screen->control_eight_bits) {
5337d522f475Smrg	line[count++] = ANSI_CSI;
5338d522f475Smrg    } else {
5339d522f475Smrg	line[count++] = ANSI_ESC;
5340d522f475Smrg	line[count++] = '[';
5341d522f475Smrg    }
53420bd37d32Smrg    switch (screen->extend_coords) {
53430bd37d32Smrg    case 0:
53440bd37d32Smrg    case SET_EXT_MODE_MOUSE:
5345d522f475Smrg#if OPT_SCO_FUNC_KEYS
53460bd37d32Smrg	if (xw->keyboard.type == keyboardIsSCO) {
53470bd37d32Smrg	    /*
53480bd37d32Smrg	     * SCO function key F1 is \E[M, which would conflict with xterm's
53490bd37d32Smrg	     * normal kmous.
53500bd37d32Smrg	     */
53510bd37d32Smrg	    line[count++] = '>';
53520bd37d32Smrg	}
5353d522f475Smrg#endif
53540bd37d32Smrg	line[count++] = final;
53550bd37d32Smrg	break;
53560bd37d32Smrg    case SET_SGR_EXT_MODE_MOUSE:
5357f2e35a3aSmrg    case SET_PIXEL_POSITION_MOUSE:
53580bd37d32Smrg	line[count++] = '<';
53590bd37d32Smrg	break;
53600bd37d32Smrg    }
5361d522f475Smrg
5362d522f475Smrg    /* Add event code to key sequence */
5363913cc679Smrg    if (okSendMousePos(xw) == X10_MOUSE) {
53640bd37d32Smrg	count = EMIT_BUTTON(button);
5365d522f475Smrg    } else {
5366d522f475Smrg	/* Button-Motion events */
5367d522f475Smrg	switch (event->type) {
5368d522f475Smrg	case ButtonPress:
53690bd37d32Smrg	    screen->mouse_button |= ButtonBit(button);
53700bd37d32Smrg	    count = EMIT_BUTTON(button);
5371d522f475Smrg	    break;
5372d522f475Smrg	case ButtonRelease:
5373d522f475Smrg	    /*
5374f2e35a3aSmrg	     * The (vertical) wheel mouse interface generates release-events
5375f2e35a3aSmrg	     * for buttons 4 and 5.
5376f2e35a3aSmrg	     *
5377f2e35a3aSmrg	     * The X10/X11 xterm protocol maps the release for buttons 1..3 to
5378f2e35a3aSmrg	     * a -1, which will be later mapped into a "0" (some button was
5379f2e35a3aSmrg	     * released),  At this point, buttons 1..3 are encoded 0..2 (the
5380f2e35a3aSmrg	     * code 3 is unused).
5381f2e35a3aSmrg	     *
5382f2e35a3aSmrg	     * The SGR (extended) xterm mouse protocol keeps the button number
5383f2e35a3aSmrg	     * and uses a "m" to indicate button release.
5384f2e35a3aSmrg	     *
5385f2e35a3aSmrg	     * The behavior for mice with more buttons is unclear, and may be
5386f2e35a3aSmrg	     * revised -TD
5387d522f475Smrg	     */
53880bd37d32Smrg	    screen->mouse_button &= ~ButtonBit(button);
5389f2e35a3aSmrg	    if (button < 3 || button > 5) {
53900bd37d32Smrg		switch (screen->extend_coords) {
53910bd37d32Smrg		case SET_SGR_EXT_MODE_MOUSE:
5392f2e35a3aSmrg		case SET_PIXEL_POSITION_MOUSE:
53930bd37d32Smrg		    final = 'm';
53940bd37d32Smrg		    break;
53950bd37d32Smrg		default:
53960bd37d32Smrg		    button = -1;
53970bd37d32Smrg		    break;
53980bd37d32Smrg		}
53990bd37d32Smrg	    }
54000bd37d32Smrg	    count = EMIT_BUTTON(button);
5401d522f475Smrg	    break;
5402d522f475Smrg	case MotionNotify:
5403d522f475Smrg	    /* BTN_EVENT_MOUSE and ANY_EVENT_MOUSE modes send motion
5404d522f475Smrg	     * events only if character cell has changed.
5405d522f475Smrg	     */
5406d522f475Smrg	    if ((row == screen->mouse_row)
5407d522f475Smrg		&& (col == screen->mouse_col)) {
5408d522f475Smrg		changed = False;
5409d522f475Smrg	    } else {
54100bd37d32Smrg		count = EMIT_BUTTON(FirstBitN(screen->mouse_button));
5411d522f475Smrg	    }
5412d522f475Smrg	    break;
5413d522f475Smrg	default:
5414d522f475Smrg	    changed = False;
5415d522f475Smrg	    break;
5416d522f475Smrg	}
5417d522f475Smrg    }
5418d522f475Smrg
5419d522f475Smrg    if (changed) {
5420d522f475Smrg	screen->mouse_row = row;
5421d522f475Smrg	screen->mouse_col = col;
5422d522f475Smrg
5423492d43a5Smrg	TRACE(("mouse at %d,%d button+mask = %#x\n", row, col, line[count - 1]));
5424d522f475Smrg
5425492d43a5Smrg	/* Add pointer position to key sequence */
54260bd37d32Smrg	count = EmitMousePositionSeparator(screen, line, count);
5427492d43a5Smrg	count = EmitMousePosition(screen, line, count, col);
54280bd37d32Smrg	count = EmitMousePositionSeparator(screen, line, count);
5429492d43a5Smrg	count = EmitMousePosition(screen, line, count, row);
5430d522f475Smrg
54310bd37d32Smrg	switch (screen->extend_coords) {
54320bd37d32Smrg	case SET_SGR_EXT_MODE_MOUSE:
54330bd37d32Smrg	case SET_URXVT_EXT_MODE_MOUSE:
5434f2e35a3aSmrg	case SET_PIXEL_POSITION_MOUSE:
54350bd37d32Smrg	    line[count++] = final;
54360bd37d32Smrg	    break;
54370bd37d32Smrg	}
54380bd37d32Smrg
5439d522f475Smrg	/* Transmit key sequence to process running under xterm */
5440f2e35a3aSmrg	TRACE(("EditorButton -> %s\n", visibleChars(line, count)));
5441d522f475Smrg	v_write(pty, line, count);
5442d522f475Smrg    }
5443d522f475Smrg    return;
5444d522f475Smrg}
5445d522f475Smrg
5446913cc679Smrg/*
5447913cc679Smrg * Check the current send_mouse_pos against allowed mouse-operations, returning
5448913cc679Smrg * none if it is disallowed.
5449913cc679Smrg */
5450913cc679SmrgXtermMouseModes
5451913cc679SmrgokSendMousePos(XtermWidget xw)
5452913cc679Smrg{
5453913cc679Smrg    TScreen *screen = TScreenOf(xw);
5454f2e35a3aSmrg    XtermMouseModes result = (XtermMouseModes) screen->send_mouse_pos;
5455913cc679Smrg
5456f2e35a3aSmrg    switch ((int) result) {
5457913cc679Smrg    case MOUSE_OFF:
5458913cc679Smrg	break;
5459913cc679Smrg    case X10_MOUSE:
5460913cc679Smrg	if (!AllowMouseOps(xw, emX10))
5461913cc679Smrg	    result = MOUSE_OFF;
5462913cc679Smrg	break;
5463913cc679Smrg    case VT200_MOUSE:
5464913cc679Smrg	if (!AllowMouseOps(xw, emVT200Click))
5465913cc679Smrg	    result = MOUSE_OFF;
5466913cc679Smrg	break;
5467913cc679Smrg    case VT200_HIGHLIGHT_MOUSE:
5468913cc679Smrg	if (!AllowMouseOps(xw, emVT200Hilite))
5469913cc679Smrg	    result = MOUSE_OFF;
5470913cc679Smrg	break;
5471913cc679Smrg    case BTN_EVENT_MOUSE:
5472913cc679Smrg	if (!AllowMouseOps(xw, emAnyButton))
5473913cc679Smrg	    result = MOUSE_OFF;
5474913cc679Smrg	break;
5475913cc679Smrg    case ANY_EVENT_MOUSE:
5476913cc679Smrg	if (!AllowMouseOps(xw, emAnyEvent))
5477913cc679Smrg	    result = MOUSE_OFF;
5478913cc679Smrg	break;
5479913cc679Smrg    case DEC_LOCATOR:
5480913cc679Smrg	if (!AllowMouseOps(xw, emLocator))
5481913cc679Smrg	    result = MOUSE_OFF;
5482913cc679Smrg	break;
5483913cc679Smrg    }
5484913cc679Smrg    return result;
5485913cc679Smrg}
5486913cc679Smrg
5487d522f475Smrg#if OPT_FOCUS_EVENT
5488913cc679Smrg/*
5489913cc679Smrg * Check the current send_focus_pos against allowed mouse-operations, returning
5490913cc679Smrg * none if it is disallowed.
5491913cc679Smrg */
5492913cc679Smrgstatic int
5493913cc679SmrgokSendFocusPos(XtermWidget xw)
5494d522f475Smrg{
5495956cc18dSsnj    TScreen *screen = TScreenOf(xw);
5496913cc679Smrg    int result = screen->send_focus_pos;
5497913cc679Smrg
5498913cc679Smrg    if (!AllowMouseOps(xw, emFocusEvent)) {
5499913cc679Smrg	result = False;
5500913cc679Smrg    }
5501913cc679Smrg    return result;
5502913cc679Smrg}
5503d522f475Smrg
5504913cc679Smrgvoid
5505913cc679SmrgSendFocusButton(XtermWidget xw, XFocusChangeEvent *event)
5506913cc679Smrg{
5507913cc679Smrg    if (okSendFocusPos(xw)) {
5508d522f475Smrg	ANSI reply;
5509d522f475Smrg
5510d522f475Smrg	memset(&reply, 0, sizeof(reply));
5511d522f475Smrg	reply.a_type = ANSI_CSI;
5512d522f475Smrg
5513d522f475Smrg#if OPT_SCO_FUNC_KEYS
5514d522f475Smrg	if (xw->keyboard.type == keyboardIsSCO) {
5515d522f475Smrg	    reply.a_pintro = '>';
5516d522f475Smrg	}
5517d522f475Smrg#endif
55182eaa94a1Schristos	reply.a_final = CharOf((event->type == FocusIn) ? 'I' : 'O');
5519d522f475Smrg	unparseseq(xw, &reply);
5520d522f475Smrg    }
5521d522f475Smrg    return;
5522d522f475Smrg}
5523d522f475Smrg#endif /* OPT_FOCUS_EVENT */
55240bd37d32Smrg
55250bd37d32Smrg#if OPT_SELECTION_OPS
55260bd37d32Smrg/*
55270bd37d32Smrg * Get the event-time, needed to process selections.
55280bd37d32Smrg */
55290bd37d32Smrgstatic Time
5530894e0ac8SmrggetEventTime(XEvent *event)
55310bd37d32Smrg{
55320bd37d32Smrg    Time result;
55330bd37d32Smrg
55340bd37d32Smrg    if (IsBtnEvent(event)) {
55350bd37d32Smrg	result = ((XButtonEvent *) event)->time;
55360bd37d32Smrg    } else if (IsKeyEvent(event)) {
55370bd37d32Smrg	result = ((XKeyEvent *) event)->time;
55380bd37d32Smrg    } else {
55390bd37d32Smrg	result = 0;
55400bd37d32Smrg    }
55410bd37d32Smrg
55420bd37d32Smrg    return result;
55430bd37d32Smrg}
55440bd37d32Smrg
55450bd37d32Smrg/* obtain the selection string, passing the endpoints to caller's parameters */
554601037d57Smrgstatic void
554701037d57SmrgdoSelectionFormat(XtermWidget xw,
554801037d57Smrg		  Widget w,
554901037d57Smrg		  XEvent *event,
555001037d57Smrg		  String *params,
555101037d57Smrg		  Cardinal *num_params,
555201037d57Smrg		  FormatSelect format_select)
55530bd37d32Smrg{
55540bd37d32Smrg    TScreen *screen = TScreenOf(xw);
555501037d57Smrg    InternalSelect *mydata = &(screen->internal_select);
555601037d57Smrg
555701037d57Smrg    memset(mydata, 0, sizeof(*mydata));
555801037d57Smrg    mydata->format = x_strdup(params[0]);
555901037d57Smrg    mydata->format_select = format_select;
55600bd37d32Smrg
55610bd37d32Smrg    screen->selectToBuffer = True;
5562f2e35a3aSmrg    beginInternalSelect(xw);
5563f2e35a3aSmrg
55640bd37d32Smrg    xtermGetSelection(w, getEventTime(event), params + 1, *num_params - 1, NULL);
5565f2e35a3aSmrg
5566f2e35a3aSmrg    if (screen->selectToBuffer)
5567f2e35a3aSmrg	finishInternalSelect(xw);
55680bd37d32Smrg}
55690bd37d32Smrg
55700bd37d32Smrg/* obtain data from the screen, passing the endpoints to caller's parameters */
55710bd37d32Smrgstatic char *
5572913cc679SmrggetDataFromScreen(XtermWidget xw, XEvent *event, String method, CELL *start, CELL *finish)
55730bd37d32Smrg{
55740bd37d32Smrg    TScreen *screen = TScreenOf(xw);
55750bd37d32Smrg
55760bd37d32Smrg    CELL save_old_start = screen->startH;
55770bd37d32Smrg    CELL save_old_end = screen->endH;
55780bd37d32Smrg
55790bd37d32Smrg    CELL save_startSel = screen->startSel;
55800bd37d32Smrg    CELL save_startRaw = screen->startRaw;
55810bd37d32Smrg    CELL save_finishSel = screen->endSel;
55820bd37d32Smrg    CELL save_finishRaw = screen->endRaw;
55830bd37d32Smrg
55840bd37d32Smrg    int save_firstValidRow = screen->firstValidRow;
55850bd37d32Smrg    int save_lastValidRow = screen->lastValidRow;
55860bd37d32Smrg
558701037d57Smrg    const Cardinal noClick = 0;
558801037d57Smrg    int save_numberOfClicks = screen->numberOfClicks;
558901037d57Smrg
55900bd37d32Smrg    SelectUnit saveUnits = screen->selectUnit;
559101037d57Smrg    SelectUnit saveMap = screen->selectMap[noClick];
55920bd37d32Smrg#if OPT_SELECT_REGEX
559301037d57Smrg    char *saveExpr = screen->selectExpr[noClick];
55940bd37d32Smrg#endif
5595f2e35a3aSmrg    SelectedCells *scp = &(screen->selected_cells[PRIMARY_CODE]);
5596f2e35a3aSmrg    SelectedCells save_selection = *scp;
55970bd37d32Smrg
55980bd37d32Smrg    char *result = 0;
55990bd37d32Smrg
56000bd37d32Smrg    TRACE(("getDataFromScreen %s\n", method));
56010bd37d32Smrg
5602f2e35a3aSmrg    memset(scp, 0, sizeof(*scp));
56030bd37d32Smrg
560401037d57Smrg    screen->numberOfClicks = 1;
560501037d57Smrg    lookupSelectUnit(xw, noClick, method);
560601037d57Smrg    screen->selectUnit = screen->selectMap[noClick];
56070bd37d32Smrg
56080bd37d32Smrg    memset(start, 0, sizeof(*start));
5609913cc679Smrg    if (IsBtnEvent(event)) {
5610913cc679Smrg	XButtonEvent *btn_event = (XButtonEvent *) event;
5611913cc679Smrg	CELL cell;
5612913cc679Smrg	screen->firstValidRow = 0;
5613913cc679Smrg	screen->lastValidRow = screen->max_row;
5614913cc679Smrg	PointToCELL(screen, btn_event->y, btn_event->x, &cell);
5615913cc679Smrg	start->row = cell.row;
5616913cc679Smrg	start->col = cell.col;
5617913cc679Smrg	finish->row = cell.row;
5618913cc679Smrg	finish->col = screen->max_col;
5619913cc679Smrg    } else {
5620913cc679Smrg	start->row = screen->cur_row;
5621913cc679Smrg	start->col = screen->cur_col;
5622913cc679Smrg	finish->row = screen->cur_row;
5623913cc679Smrg	finish->col = screen->max_col;
5624913cc679Smrg    }
56250bd37d32Smrg
5626f2e35a3aSmrg    ComputeSelect(xw, start, finish, False, False);
5627f2e35a3aSmrg    SaltTextAway(xw,
5628f2e35a3aSmrg		 TargetToSelection(screen, PRIMARY_NAME),
5629f2e35a3aSmrg		 &(screen->startSel), &(screen->endSel));
56300bd37d32Smrg
5631f2e35a3aSmrg    if (scp->data_limit && scp->data_buffer) {
5632f2e35a3aSmrg	TRACE(("...getDataFromScreen selection-data %.*s\n",
5633f2e35a3aSmrg	       (int) scp->data_limit,
5634f2e35a3aSmrg	       scp->data_buffer));
5635f2e35a3aSmrg	result = malloc(scp->data_limit + 1);
56360bd37d32Smrg	if (result) {
5637f2e35a3aSmrg	    memcpy(result, scp->data_buffer, scp->data_limit);
5638f2e35a3aSmrg	    result[scp->data_limit] = 0;
56390bd37d32Smrg	}
5640f2e35a3aSmrg	free(scp->data_buffer);
5641f2e35a3aSmrg	scp->data_limit = 0;
56420bd37d32Smrg    }
56430bd37d32Smrg
56440bd37d32Smrg    TRACE(("...getDataFromScreen restoring previous selection\n"));
56450bd37d32Smrg
56460bd37d32Smrg    screen->startSel = save_startSel;
56470bd37d32Smrg    screen->startRaw = save_startRaw;
56480bd37d32Smrg    screen->endSel = save_finishSel;
56490bd37d32Smrg    screen->endRaw = save_finishRaw;
56500bd37d32Smrg
56510bd37d32Smrg    screen->firstValidRow = save_firstValidRow;
56520bd37d32Smrg    screen->lastValidRow = save_lastValidRow;
56530bd37d32Smrg
565401037d57Smrg    screen->numberOfClicks = save_numberOfClicks;
56550bd37d32Smrg    screen->selectUnit = saveUnits;
565601037d57Smrg    screen->selectMap[noClick] = saveMap;
56570bd37d32Smrg#if OPT_SELECT_REGEX
565801037d57Smrg    screen->selectExpr[noClick] = saveExpr;
56590bd37d32Smrg#endif
56600bd37d32Smrg
5661f2e35a3aSmrg    screen->selected_cells[0] = save_selection;
56620bd37d32Smrg
56630bd37d32Smrg    TrackText(xw, &save_old_start, &save_old_end);
56640bd37d32Smrg
56650bd37d32Smrg    TRACE(("...getDataFromScreen done\n"));
56660bd37d32Smrg    return result;
56670bd37d32Smrg}
56680bd37d32Smrg
56690bd37d32Smrg/*
56700bd37d32Smrg * Split-up the format before substituting data, to avoid quoting issues.
56710bd37d32Smrg * The resource mechanism has a limited ability to handle escapes.  We take
56720bd37d32Smrg * the result as if it were an sh-type string and parse it into a regular
56730bd37d32Smrg * argv array.
56740bd37d32Smrg */
56750bd37d32Smrgstatic char **
56760bd37d32SmrgtokenizeFormat(String format)
56770bd37d32Smrg{
56780bd37d32Smrg    char **result = 0;
56790bd37d32Smrg
56800bd37d32Smrg    format = x_skip_blanks(format);
56810bd37d32Smrg    if (*format != '\0') {
56820bd37d32Smrg	char *blob = x_strdup(format);
56832e4f8982Smrg	int pass;
56840bd37d32Smrg
56850bd37d32Smrg	for (pass = 0; pass < 2; ++pass) {
56860bd37d32Smrg	    int used = 0;
56870bd37d32Smrg	    int first = 1;
56880bd37d32Smrg	    int escaped = 0;
56890bd37d32Smrg	    int squoted = 0;
56900bd37d32Smrg	    int dquoted = 0;
56912e4f8982Smrg	    int n;
5692f2e35a3aSmrg	    int argc = 0;
56930bd37d32Smrg
56940bd37d32Smrg	    for (n = 0; format[n] != '\0'; ++n) {
56950bd37d32Smrg		if (escaped) {
56960bd37d32Smrg		    blob[used++] = format[n];
56970bd37d32Smrg		    escaped = 0;
56980bd37d32Smrg		} else if (format[n] == '"') {
56990bd37d32Smrg		    if (!squoted) {
57000bd37d32Smrg			if (!dquoted)
57010bd37d32Smrg			    blob[used++] = format[n];
57020bd37d32Smrg			dquoted = !dquoted;
57030bd37d32Smrg		    }
57040bd37d32Smrg		} else if (format[n] == '\'') {
57050bd37d32Smrg		    if (!dquoted) {
57060bd37d32Smrg			if (!squoted)
57070bd37d32Smrg			    blob[used++] = format[n];
57080bd37d32Smrg			squoted = !squoted;
57090bd37d32Smrg		    }
57100bd37d32Smrg		} else if (format[n] == '\\') {
57110bd37d32Smrg		    blob[used++] = format[n];
57120bd37d32Smrg		    escaped = 1;
57130bd37d32Smrg		} else {
57140bd37d32Smrg		    if (first) {
57150bd37d32Smrg			first = 0;
57160bd37d32Smrg			if (pass) {
57170bd37d32Smrg			    result[argc] = &blob[n];
57180bd37d32Smrg			}
57190bd37d32Smrg			++argc;
57200bd37d32Smrg		    }
57210bd37d32Smrg		    if (isspace((Char) format[n])) {
57220bd37d32Smrg			first = !isspace((Char) format[n + 1]);
57230bd37d32Smrg			if (squoted || dquoted) {
57240bd37d32Smrg			    blob[used++] = format[n];
57250bd37d32Smrg			} else if (first) {
57260bd37d32Smrg			    blob[used++] = '\0';
57270bd37d32Smrg			}
57280bd37d32Smrg		    } else {
57290bd37d32Smrg			blob[used++] = format[n];
57300bd37d32Smrg		    }
57310bd37d32Smrg		}
57320bd37d32Smrg	    }
57330bd37d32Smrg	    blob[used] = '\0';
57340bd37d32Smrg	    assert(strlen(blob) <= strlen(format));
57350bd37d32Smrg	    if (!pass) {
57360bd37d32Smrg		result = TypeCallocN(char *, argc + 1);
57370bd37d32Smrg		if (result == 0) {
57380bd37d32Smrg		    free(blob);
57390bd37d32Smrg		    break;
57400bd37d32Smrg		}
57410bd37d32Smrg	    }
57420bd37d32Smrg	}
57430bd37d32Smrg    }
57440bd37d32Smrg#if OPT_TRACE
57450bd37d32Smrg    if (result) {
5746f2e35a3aSmrg	int n;
57470bd37d32Smrg	TRACE(("tokenizeFormat %s\n", format));
5748f2e35a3aSmrg	for (n = 0; result[n]; ++n) {
5749f2e35a3aSmrg	    TRACE(("argv[%d] = %s\n", n, result[n]));
57500bd37d32Smrg	}
57510bd37d32Smrg    }
57520bd37d32Smrg#endif
57530bd37d32Smrg
57540bd37d32Smrg    return result;
57550bd37d32Smrg}
57560bd37d32Smrg
57570bd37d32Smrgstatic void
5758e0a2b6dfSmrgformatVideoAttrs(XtermWidget xw, char *buffer, CELL *cell)
57590bd37d32Smrg{
57600bd37d32Smrg    TScreen *screen = TScreenOf(xw);
57610bd37d32Smrg    LineData *ld = GET_LINEDATA(screen, cell->row);
57620bd37d32Smrg
57630bd37d32Smrg    *buffer = '\0';
57640bd37d32Smrg    if (ld != 0 && cell->col < (int) ld->lineSize) {
5765894e0ac8Smrg	IAttr attribs = ld->attribs[cell->col];
57660bd37d32Smrg	const char *delim = "";
57670bd37d32Smrg
57680bd37d32Smrg	if (attribs & INVERSE) {
57690bd37d32Smrg	    buffer += sprintf(buffer, "7");
57700bd37d32Smrg	    delim = ";";
57710bd37d32Smrg	}
57720bd37d32Smrg	if (attribs & UNDERLINE) {
57730bd37d32Smrg	    buffer += sprintf(buffer, "%s4", delim);
57740bd37d32Smrg	    delim = ";";
57750bd37d32Smrg	}
57760bd37d32Smrg	if (attribs & BOLD) {
57770bd37d32Smrg	    buffer += sprintf(buffer, "%s1", delim);
57780bd37d32Smrg	    delim = ";";
57790bd37d32Smrg	}
57800bd37d32Smrg	if (attribs & BLINK) {
57810bd37d32Smrg	    buffer += sprintf(buffer, "%s5", delim);
57820bd37d32Smrg	    delim = ";";
57830bd37d32Smrg	}
57840bd37d32Smrg#if OPT_ISO_COLORS
57850bd37d32Smrg	if (attribs & FG_COLOR) {
5786f2e35a3aSmrg	    Pixel fg = extract_fg(xw, ld->color[cell->col], attribs);
57870bd37d32Smrg	    if (fg < 8) {
57880bd37d32Smrg		fg += 30;
57890bd37d32Smrg	    } else if (fg < 16) {
57900bd37d32Smrg		fg += 90;
57910bd37d32Smrg	    } else {
57920bd37d32Smrg		buffer += sprintf(buffer, "%s38;5", delim);
57930bd37d32Smrg		delim = ";";
57940bd37d32Smrg	    }
5795f2e35a3aSmrg	    buffer += sprintf(buffer, "%s%lu", delim, fg);
57960bd37d32Smrg	    delim = ";";
57970bd37d32Smrg	}
57980bd37d32Smrg	if (attribs & BG_COLOR) {
5799f2e35a3aSmrg	    Pixel bg = extract_bg(xw, ld->color[cell->col], attribs);
58000bd37d32Smrg	    if (bg < 8) {
58010bd37d32Smrg		bg += 40;
58020bd37d32Smrg	    } else if (bg < 16) {
58030bd37d32Smrg		bg += 100;
58040bd37d32Smrg	    } else {
58050bd37d32Smrg		buffer += sprintf(buffer, "%s48;5", delim);
58060bd37d32Smrg		delim = ";";
58070bd37d32Smrg	    }
5808f2e35a3aSmrg	    (void) sprintf(buffer, "%s%lu", delim, bg);
58090bd37d32Smrg	}
58100bd37d32Smrg#endif
58110bd37d32Smrg    }
58120bd37d32Smrg}
58130bd37d32Smrg
58142e4f8982Smrgstatic char *
58152e4f8982SmrgformatStrlen(char *target, char *source, int freeit)
58162e4f8982Smrg{
58172e4f8982Smrg    if (source != 0) {
58182e4f8982Smrg	sprintf(target, "%u", (unsigned) strlen(source));
58192e4f8982Smrg	if (freeit) {
58202e4f8982Smrg	    free(source);
58212e4f8982Smrg	}
58222e4f8982Smrg    } else {
58232e4f8982Smrg	strcpy(target, "0");
58242e4f8982Smrg    }
58252e4f8982Smrg    return target;
58262e4f8982Smrg}
58272e4f8982Smrg
58280bd37d32Smrg/* substitute data into format, reallocating the result */
58290bd37d32Smrgstatic char *
58300bd37d32SmrgexpandFormat(XtermWidget xw,
58310bd37d32Smrg	     const char *format,
58320bd37d32Smrg	     char *data,
5833e0a2b6dfSmrg	     CELL *start,
5834e0a2b6dfSmrg	     CELL *finish)
58350bd37d32Smrg{
58360bd37d32Smrg    char *result = 0;
58370bd37d32Smrg    if (!IsEmpty(format)) {
58380bd37d32Smrg	static char empty[1];
58390bd37d32Smrg	int pass;
58400bd37d32Smrg	int n;
58410bd37d32Smrg	char numbers[80];
58420bd37d32Smrg
58430bd37d32Smrg	if (data == 0)
58440bd37d32Smrg	    data = empty;
58450bd37d32Smrg
58460bd37d32Smrg	for (pass = 0; pass < 2; ++pass) {
58470bd37d32Smrg	    size_t need = 0;
58480bd37d32Smrg
58490bd37d32Smrg	    for (n = 0; format[n] != '\0'; ++n) {
58500bd37d32Smrg
58510bd37d32Smrg		if (format[n] == '%') {
58522e4f8982Smrg		    char *value = 0;
58532e4f8982Smrg
58540bd37d32Smrg		    switch (format[++n]) {
58550bd37d32Smrg		    case '%':
58560bd37d32Smrg			if (pass) {
58570bd37d32Smrg			    result[need] = format[n];
58580bd37d32Smrg			}
58590bd37d32Smrg			++need;
58600bd37d32Smrg			break;
58610bd37d32Smrg		    case 'P':
58620bd37d32Smrg			sprintf(numbers, "%d;%d",
58630bd37d32Smrg				TScreenOf(xw)->topline + start->row + 1,
58640bd37d32Smrg				start->col + 1);
58650bd37d32Smrg			value = numbers;
58660bd37d32Smrg			break;
58670bd37d32Smrg		    case 'p':
58680bd37d32Smrg			sprintf(numbers, "%d;%d",
58690bd37d32Smrg				TScreenOf(xw)->topline + finish->row + 1,
58700bd37d32Smrg				finish->col + 1);
58710bd37d32Smrg			value = numbers;
58720bd37d32Smrg			break;
58732e4f8982Smrg		    case 'R':
58742e4f8982Smrg			value = formatStrlen(numbers, x_strrtrim(data), 1);
58752e4f8982Smrg			break;
58762e4f8982Smrg		    case 'r':
58772e4f8982Smrg			value = x_strrtrim(data);
58782e4f8982Smrg			break;
58790bd37d32Smrg		    case 'S':
58802e4f8982Smrg			value = formatStrlen(numbers, data, 0);
58810bd37d32Smrg			break;
58820bd37d32Smrg		    case 's':
58830bd37d32Smrg			value = data;
58840bd37d32Smrg			break;
58850bd37d32Smrg		    case 'T':
58862e4f8982Smrg			value = formatStrlen(numbers, x_strtrim(data), 1);
58870bd37d32Smrg			break;
58880bd37d32Smrg		    case 't':
58890bd37d32Smrg			value = x_strtrim(data);
58900bd37d32Smrg			break;
58910bd37d32Smrg		    case 'V':
58920bd37d32Smrg			formatVideoAttrs(xw, numbers, start);
58930bd37d32Smrg			value = numbers;
58940bd37d32Smrg			break;
58950bd37d32Smrg		    case 'v':
58960bd37d32Smrg			formatVideoAttrs(xw, numbers, finish);
58970bd37d32Smrg			value = numbers;
58980bd37d32Smrg			break;
58990bd37d32Smrg		    default:
59000bd37d32Smrg			if (pass) {
59010bd37d32Smrg			    result[need] = format[n];
59020bd37d32Smrg			}
59030bd37d32Smrg			--n;
59040bd37d32Smrg			++need;
59050bd37d32Smrg			break;
59060bd37d32Smrg		    }
59070bd37d32Smrg		    if (value != 0) {
59080bd37d32Smrg			if (pass) {
59090bd37d32Smrg			    strcpy(result + need, value);
59100bd37d32Smrg			}
59110bd37d32Smrg			need += strlen(value);
59120bd37d32Smrg			if (value != numbers && value != data) {
59130bd37d32Smrg			    free(value);
59140bd37d32Smrg			}
59150bd37d32Smrg		    }
59160bd37d32Smrg		} else {
59170bd37d32Smrg		    if (pass) {
59180bd37d32Smrg			result[need] = format[n];
59190bd37d32Smrg		    }
59200bd37d32Smrg		    ++need;
59210bd37d32Smrg		}
59220bd37d32Smrg	    }
59230bd37d32Smrg	    if (pass) {
59240bd37d32Smrg		result[need] = '\0';
59250bd37d32Smrg	    } else {
59260bd37d32Smrg		++need;
59270bd37d32Smrg		result = malloc(need);
59280bd37d32Smrg		if (result == 0) {
59290bd37d32Smrg		    break;
59300bd37d32Smrg		}
59310bd37d32Smrg	    }
59320bd37d32Smrg	}
59330bd37d32Smrg    }
59340bd37d32Smrg    TRACE(("expandFormat(%s) = %s\n", NonNull(format), NonNull(result)));
59350bd37d32Smrg    return result;
59360bd37d32Smrg}
59370bd37d32Smrg
59380bd37d32Smrg/* execute the command after forking.  The main process frees its data */
59390bd37d32Smrgstatic void
59402e4f8982SmrgexecuteCommand(pid_t pid, char **argv)
59410bd37d32Smrg{
59422e4f8982Smrg    (void) pid;
59430bd37d32Smrg    if (argv != 0 && argv[0] != 0) {
59442e4f8982Smrg	char *child_cwd = ProcGetCWD(pid);
59452e4f8982Smrg
59460bd37d32Smrg	if (fork() == 0) {
59472e4f8982Smrg	    if (child_cwd) {
59482e4f8982Smrg		IGNORE_RC(chdir(child_cwd));	/* We don't care if this fails */
59492e4f8982Smrg	    }
59500bd37d32Smrg	    execvp(argv[0], argv);
59510bd37d32Smrg	    exit(EXIT_FAILURE);
59520bd37d32Smrg	}
5953913cc679Smrg	free(child_cwd);
59540bd37d32Smrg    }
59550bd37d32Smrg}
59560bd37d32Smrg
59570bd37d32Smrgstatic void
59580bd37d32SmrgfreeArgv(char *blob, char **argv)
59590bd37d32Smrg{
59600bd37d32Smrg    if (blob) {
59610bd37d32Smrg	free(blob);
59620bd37d32Smrg	if (argv) {
59632e4f8982Smrg	    int n;
59640bd37d32Smrg	    for (n = 0; argv[n]; ++n)
59650bd37d32Smrg		free(argv[n]);
59660bd37d32Smrg	    free(argv);
59670bd37d32Smrg	}
59680bd37d32Smrg    }
59690bd37d32Smrg}
59700bd37d32Smrg
597101037d57Smrgstatic void
597201037d57SmrgreallyExecFormatted(Widget w, char *format, char *data, CELL *start, CELL *finish)
597301037d57Smrg{
597401037d57Smrg    XtermWidget xw;
597501037d57Smrg
597601037d57Smrg    if ((xw = getXtermWidget(w)) != 0) {
597701037d57Smrg	char **argv;
597801037d57Smrg
597901037d57Smrg	if ((argv = tokenizeFormat(format)) != 0) {
59802e4f8982Smrg	    char *blob = argv[0];
59812e4f8982Smrg	    int argc;
59822e4f8982Smrg
598301037d57Smrg	    for (argc = 0; argv[argc] != 0; ++argc) {
598401037d57Smrg		argv[argc] = expandFormat(xw, argv[argc], data, start, finish);
598501037d57Smrg	    }
59862e4f8982Smrg	    executeCommand(TScreenOf(xw)->pid, argv);
598701037d57Smrg	    freeArgv(blob, argv);
598801037d57Smrg	}
598901037d57Smrg    }
599001037d57Smrg}
599101037d57Smrg
59920bd37d32Smrgvoid
59930bd37d32SmrgHandleExecFormatted(Widget w,
599401037d57Smrg		    XEvent *event,
5995e0a2b6dfSmrg		    String *params,	/* selections */
59960bd37d32Smrg		    Cardinal *num_params)
59970bd37d32Smrg{
59980bd37d32Smrg    XtermWidget xw;
59990bd37d32Smrg
6000f2e35a3aSmrg    TRACE_EVENT("HandleExecFormatted", event, params, num_params);
600101037d57Smrg    if ((xw = getXtermWidget(w)) != 0 &&
600201037d57Smrg	(*num_params > 1)) {
600301037d57Smrg	doSelectionFormat(xw, w, event, params, num_params, reallyExecFormatted);
60040bd37d32Smrg    }
60050bd37d32Smrg}
60060bd37d32Smrg
60070bd37d32Smrgvoid
60080bd37d32SmrgHandleExecSelectable(Widget w,
6009913cc679Smrg		     XEvent *event,
6010e0a2b6dfSmrg		     String *params,	/* selections */
60110bd37d32Smrg		     Cardinal *num_params)
60120bd37d32Smrg{
60130bd37d32Smrg    XtermWidget xw;
60140bd37d32Smrg
60150bd37d32Smrg    if ((xw = getXtermWidget(w)) != 0) {
6016f2e35a3aSmrg	TRACE_EVENT("HandleExecSelectable", event, params, num_params);
60170bd37d32Smrg
60180bd37d32Smrg	if (*num_params == 2) {
60190bd37d32Smrg	    CELL start, finish;
60200bd37d32Smrg	    char *data;
60210bd37d32Smrg	    char **argv;
60220bd37d32Smrg
6023913cc679Smrg	    data = getDataFromScreen(xw, event, params[1], &start, &finish);
60240bd37d32Smrg	    if (data != 0) {
60250bd37d32Smrg		if ((argv = tokenizeFormat(params[0])) != 0) {
60262e4f8982Smrg		    char *blob = argv[0];
60272e4f8982Smrg		    int argc;
60282e4f8982Smrg
60290bd37d32Smrg		    for (argc = 0; argv[argc] != 0; ++argc) {
60300bd37d32Smrg			argv[argc] = expandFormat(xw, argv[argc], data,
60310bd37d32Smrg						  &start, &finish);
60320bd37d32Smrg		    }
60332e4f8982Smrg		    executeCommand(TScreenOf(xw)->pid, argv);
60340bd37d32Smrg		    freeArgv(blob, argv);
60350bd37d32Smrg		}
6036894e0ac8Smrg		free(data);
60370bd37d32Smrg	    }
60380bd37d32Smrg	}
60390bd37d32Smrg    }
60400bd37d32Smrg}
60410bd37d32Smrg
604201037d57Smrgstatic void
604301037d57SmrgreallyInsertFormatted(Widget w, char *format, char *data, CELL *start, CELL *finish)
604401037d57Smrg{
604501037d57Smrg    XtermWidget xw;
604601037d57Smrg
604701037d57Smrg    if ((xw = getXtermWidget(w)) != 0) {
604801037d57Smrg	char *exps;
604901037d57Smrg
605001037d57Smrg	if ((exps = expandFormat(xw, format, data, start, finish)) != 0) {
605101037d57Smrg	    unparseputs(xw, exps);
605201037d57Smrg	    unparse_end(xw);
605301037d57Smrg	    free(exps);
605401037d57Smrg	}
605501037d57Smrg    }
605601037d57Smrg}
605701037d57Smrg
60580bd37d32Smrgvoid
60590bd37d32SmrgHandleInsertFormatted(Widget w,
606001037d57Smrg		      XEvent *event,
6061e0a2b6dfSmrg		      String *params,	/* selections */
60620bd37d32Smrg		      Cardinal *num_params)
60630bd37d32Smrg{
60640bd37d32Smrg    XtermWidget xw;
60650bd37d32Smrg
6066f2e35a3aSmrg    TRACE_EVENT("HandleInsertFormatted", event, params, num_params);
606701037d57Smrg    if ((xw = getXtermWidget(w)) != 0 &&
606801037d57Smrg	(*num_params > 1)) {
606901037d57Smrg	doSelectionFormat(xw, w, event, params, num_params, reallyInsertFormatted);
60700bd37d32Smrg    }
60710bd37d32Smrg}
60720bd37d32Smrg
60730bd37d32Smrgvoid
60740bd37d32SmrgHandleInsertSelectable(Widget w,
6075913cc679Smrg		       XEvent *event,
6076e0a2b6dfSmrg		       String *params,	/* selections */
60770bd37d32Smrg		       Cardinal *num_params)
60780bd37d32Smrg{
60790bd37d32Smrg    XtermWidget xw;
60800bd37d32Smrg
60810bd37d32Smrg    if ((xw = getXtermWidget(w)) != 0) {
6082f2e35a3aSmrg	TRACE_EVENT("HandleInsertSelectable", event, params, num_params);
60830bd37d32Smrg
60840bd37d32Smrg	if (*num_params == 2) {
60850bd37d32Smrg	    CELL start, finish;
60860bd37d32Smrg	    char *data;
60870bd37d32Smrg	    char *temp = x_strdup(params[0]);
60880bd37d32Smrg
6089913cc679Smrg	    data = getDataFromScreen(xw, event, params[1], &start, &finish);
60900bd37d32Smrg	    if (data != 0) {
60912e4f8982Smrg		char *exps = expandFormat(xw, temp, data, &start, &finish);
60920bd37d32Smrg		if (exps != 0) {
60930bd37d32Smrg		    unparseputs(xw, exps);
609401037d57Smrg		    unparse_end(xw);
60950bd37d32Smrg		    free(exps);
60960bd37d32Smrg		}
60970bd37d32Smrg		free(data);
60980bd37d32Smrg	    }
60990bd37d32Smrg	    free(temp);
61000bd37d32Smrg	}
61010bd37d32Smrg    }
61020bd37d32Smrg}
61030bd37d32Smrg#endif /* OPT_SELECTION_OPS */
6104