button.c revision 04b94745
104b94745Smrg/* $XTermId: button.c,v 1.663 2024/04/19 07:42:00 tom Exp $ */
2d522f475Smrg
3d522f475Smrg/*
404b94745Smrg * Copyright 1999-2023,2024 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>
8004b94745Smrg#include <xterm_io.h>
81d522f475Smrg
82d522f475Smrg#if OPT_SELECT_REGEX
83a5ae21e4Smrg#if defined(HAVE_PCRE2POSIX_H)
84f2e35a3aSmrg#include <pcre2posix.h>
85a5ae21e4Smrg
86a5ae21e4Smrg/* pcre2 used to provide its "POSIX" entrypoints using the same names as the
87a5ae21e4Smrg * standard ones in the C runtime, but that never worked because the linker
88a5ae21e4Smrg * would use the C runtime.  Debian patched the library to fix this symbol
89a5ae21e4Smrg * conflict, but overlooked the header file, and Debian's patch was made
90a5ae21e4Smrg * obsolete when pcre2 was changed early in 2019 to provide different names.
91a5ae21e4Smrg *
92a5ae21e4Smrg * Here is a workaround to make the older version of Debian's package work.
93a5ae21e4Smrg */
94a5ae21e4Smrg#if !defined(PCRE2regcomp) && defined(HAVE_PCRE2REGCOMP)
95a5ae21e4Smrg
96a5ae21e4Smrg#undef regcomp
97a5ae21e4Smrg#undef regexec
98a5ae21e4Smrg#undef regfree
99a5ae21e4Smrg
100a5ae21e4Smrg#ifdef __cplusplus
101a5ae21e4Smrgextern "C" {
102a5ae21e4Smrg#endif
103a5ae21e4Smrg    PCRE2POSIX_EXP_DECL int PCRE2regcomp(regex_t *, const char *, int);
104a5ae21e4Smrg    PCRE2POSIX_EXP_DECL int PCRE2regexec(const regex_t *, const char *, size_t,
105a5ae21e4Smrg					 regmatch_t *, int);
106a5ae21e4Smrg    PCRE2POSIX_EXP_DECL void PCRE2regfree(regex_t *);
107a5ae21e4Smrg#ifdef __cplusplus
108a5ae21e4Smrg}				/* extern "C" */
109a5ae21e4Smrg#endif
110a5ae21e4Smrg#define regcomp(r,s,n)          PCRE2regcomp(r,s,n)
111a5ae21e4Smrg#define regexec(r,s,n,m,x)      PCRE2regexec(r,s,n,m,x)
112a5ae21e4Smrg#define regfree(r)              PCRE2regfree(r)
113a5ae21e4Smrg#endif
114a5ae21e4Smrg/* end workaround... */
115a5ae21e4Smrg#elif defined(HAVE_PCREPOSIX_H)
116d522f475Smrg#include <pcreposix.h>
117d522f475Smrg#else /* POSIX regex.h */
118d522f475Smrg#include <sys/types.h>
119d522f475Smrg#include <regex.h>
120d522f475Smrg#endif
121a5ae21e4Smrg#endif /* OPT_SELECT_REGEX */
122f2e35a3aSmrg
123f2e35a3aSmrg#ifdef HAVE_X11_TRANSLATEI_H
124f2e35a3aSmrg#include <X11/ConvertI.h>
125f2e35a3aSmrg#include <X11/TranslateI.h>
126f2e35a3aSmrg#else
127f2e35a3aSmrgextern String _XtPrintXlations(Widget w,
128f2e35a3aSmrg			       XtTranslations xlations,
129f2e35a3aSmrg			       Widget accelWidget,
130f2e35a3aSmrg			       _XtBoolean includeRHS);
131f2e35a3aSmrg#endif
132f2e35a3aSmrg
133f2e35a3aSmrg#define PRIMARY_NAME    "PRIMARY"
134f2e35a3aSmrg#define CLIPBOARD_NAME  "CLIPBOARD"
135f2e35a3aSmrg#define SECONDARY_NAME  "SECONDARY"
136f2e35a3aSmrg
137f2e35a3aSmrg#define AtomToSelection(d,n) \
138f2e35a3aSmrg		 (((n) == XA_CLIPBOARD(d)) \
139f2e35a3aSmrg		  ? CLIPBOARD_CODE \
140f2e35a3aSmrg		  : (((n) == XA_SECONDARY) \
141f2e35a3aSmrg		     ? SECONDARY_CODE \
142f2e35a3aSmrg		     : PRIMARY_CODE))
143f2e35a3aSmrg
144f2e35a3aSmrg#define isSelectionCode(n) ((n) >= PRIMARY_CODE)
145f2e35a3aSmrg#define CutBufferToCode(n) ((n) +  MAX_SELECTION_CODES)
146f2e35a3aSmrg#define okSelectionCode(n) (isSelectionCode(n) ? (n) : PRIMARY_CODE)
147d522f475Smrg
148d522f475Smrg#if OPT_WIDE_CHARS
149d522f475Smrg#include <ctype.h>
150d522f475Smrg#include <wcwidth.h>
151d522f475Smrg#else
152d522f475Smrg#define CharacterClass(value) \
153f2e35a3aSmrg	charClass[(value) & (int)((sizeof(charClass)/sizeof(charClass[0]))-1)]
154d522f475Smrg#endif
155d522f475Smrg
156956cc18dSsnj    /*
157956cc18dSsnj     * We'll generally map rows to indices when doing selection.
158956cc18dSsnj     * Simplify that with a macro.
159956cc18dSsnj     *
160956cc18dSsnj     * Note that ROW2INX() is safe to use with auto increment/decrement for
161956cc18dSsnj     * the row expression since that is evaluated once.
162956cc18dSsnj     */
163956cc18dSsnj#define GET_LINEDATA(screen, row) \
164956cc18dSsnj	getLineData(screen, ROW2INX(screen, row))
165956cc18dSsnj
166f2e35a3aSmrg#define MaxMouseBtn  5
167492d43a5Smrg
168492d43a5Smrg#define IsBtnEvent(event) ((event)->type == ButtonPress || (event)->type == ButtonRelease)
1690bd37d32Smrg#define IsKeyEvent(event) ((event)->type == KeyPress    || (event)->type == KeyRelease)
170d522f475Smrg
171d522f475Smrg#define	Coordinate(s,c)	((c)->row * MaxCols(s) + (c)->col)
172d522f475Smrg
173d522f475Smrgstatic const CELL zeroCELL =
174d522f475Smrg{0, 0};
175d522f475Smrg
176d522f475Smrg#if OPT_DEC_LOCATOR
177894e0ac8Smrgstatic Bool SendLocatorPosition(XtermWidget xw, XButtonEvent *event);
178894e0ac8Smrgstatic void CheckLocatorPosition(XtermWidget xw, XButtonEvent *event);
179d522f475Smrg#endif /* OPT_DEC_LOCATOR */
180d522f475Smrg
181d522f475Smrg/* Multi-click handling */
182d522f475Smrg#if OPT_READLINE
183d522f475Smrgstatic Time lastButtonDownTime = 0;
184d522f475Smrgstatic int ExtendingSelection = 0;
185d522f475Smrgstatic Time lastButton3UpTime = 0;
186d522f475Smrgstatic Time lastButton3DoubleDownTime = 0;
187d522f475Smrgstatic CELL lastButton3;	/* At the release time */
188d522f475Smrg#endif /* OPT_READLINE */
189d522f475Smrg
190e0a2b6dfSmrgstatic Char *SaveText(TScreen *screen, int row, int scol, int ecol,
191e0a2b6dfSmrg		      Char *lp, int *eol);
192e0a2b6dfSmrgstatic int Length(TScreen *screen, int row, int scol, int ecol);
193f2e35a3aSmrgstatic void ComputeSelect(XtermWidget xw, CELL *startc, CELL *endc, Bool
194f2e35a3aSmrg			  extend, Bool normal);
195894e0ac8Smrgstatic void EditorButton(XtermWidget xw, XButtonEvent *event);
196894e0ac8Smrgstatic void EndExtend(XtermWidget w, XEvent *event, String *params, Cardinal
197d522f475Smrg		      num_params, Bool use_cursor_loc);
198e0a2b6dfSmrgstatic void ExtendExtend(XtermWidget xw, const CELL *cell);
199e0a2b6dfSmrgstatic void PointToCELL(TScreen *screen, int y, int x, CELL *cell);
200e0a2b6dfSmrgstatic void ReHiliteText(XtermWidget xw, CELL *first, CELL *last);
201f2e35a3aSmrgstatic void SaltTextAway(XtermWidget xw, int which, CELL *cellc, CELL *cell);
202894e0ac8Smrgstatic void SelectSet(XtermWidget xw, XEvent *event, String *params, Cardinal num_params);
203d522f475Smrgstatic void SelectionReceived PROTO_XT_SEL_CB_ARGS;
204e0a2b6dfSmrgstatic void StartSelect(XtermWidget xw, const CELL *cell);
205894e0ac8Smrgstatic void TrackDown(XtermWidget xw, XButtonEvent *event);
206e0a2b6dfSmrgstatic void TrackText(XtermWidget xw, const CELL *first, const CELL *last);
207f2e35a3aSmrgstatic void UnHiliteText(XtermWidget xw);
208e0a2b6dfSmrgstatic void _OwnSelection(XtermWidget xw, String *selections, Cardinal count);
209894e0ac8Smrgstatic void do_select_end(XtermWidget xw, XEvent *event, String *params,
210d522f475Smrg			  Cardinal *num_params, Bool use_cursor_loc);
211d522f475Smrg
212492d43a5Smrg#define MOUSE_LIMIT (255 - 32)
213d522f475Smrg
214492d43a5Smrg/* Send SET_EXT_SIZE_MOUSE to enable offsets up to EXT_MOUSE_LIMIT */
215492d43a5Smrg#define EXT_MOUSE_LIMIT (2047 - 32)
216492d43a5Smrg#define EXT_MOUSE_START (127 - 32)
217d522f475Smrg
2180bd37d32Smrgstatic int
219e0a2b6dfSmrgMouseLimit(TScreen *screen)
2200bd37d32Smrg{
2210bd37d32Smrg    int mouse_limit;
2220bd37d32Smrg
2230bd37d32Smrg    switch (screen->extend_coords) {
2240bd37d32Smrg    default:
2250bd37d32Smrg	mouse_limit = MOUSE_LIMIT;
2260bd37d32Smrg	break;
2270bd37d32Smrg    case SET_EXT_MODE_MOUSE:
2280bd37d32Smrg	mouse_limit = EXT_MOUSE_LIMIT;
2290bd37d32Smrg	break;
2300bd37d32Smrg    case SET_SGR_EXT_MODE_MOUSE:
2310bd37d32Smrg    case SET_URXVT_EXT_MODE_MOUSE:
232f2e35a3aSmrg    case SET_PIXEL_POSITION_MOUSE:
2330bd37d32Smrg	mouse_limit = -1;
2340bd37d32Smrg	break;
2350bd37d32Smrg    }
2360bd37d32Smrg    return mouse_limit;
2370bd37d32Smrg}
2380bd37d32Smrg
239492d43a5Smrgstatic unsigned
240e0a2b6dfSmrgEmitMousePosition(TScreen *screen, Char line[], unsigned count, int value)
241492d43a5Smrg{
2420bd37d32Smrg    int mouse_limit = MouseLimit(screen);
243492d43a5Smrg
244492d43a5Smrg    /*
245492d43a5Smrg     * Add pointer position to key sequence
246492d43a5Smrg     *
247492d43a5Smrg     * In extended mode we encode large positions as two-byte UTF-8.
248492d43a5Smrg     *
249492d43a5Smrg     * NOTE: historically, it was possible to emit 256, which became
250492d43a5Smrg     * zero by truncation to 8 bits. While this was arguably a bug,
251492d43a5Smrg     * it's also somewhat useful as a past-end marker. We preserve
252492d43a5Smrg     * this behavior for both normal and extended mouse modes.
253492d43a5Smrg     */
2540bd37d32Smrg    switch (screen->extend_coords) {
2550bd37d32Smrg    default:
2560bd37d32Smrg	if (value == mouse_limit) {
2570bd37d32Smrg	    line[count++] = CharOf(0);
2580bd37d32Smrg	} else {
2590bd37d32Smrg	    line[count++] = CharOf(' ' + value + 1);
2600bd37d32Smrg	}
2610bd37d32Smrg	break;
2620bd37d32Smrg    case SET_EXT_MODE_MOUSE:
2630bd37d32Smrg	if (value == mouse_limit) {
2640bd37d32Smrg	    line[count++] = CharOf(0);
2650bd37d32Smrg	} else if (value < EXT_MOUSE_START) {
2660bd37d32Smrg	    line[count++] = CharOf(' ' + value + 1);
2670bd37d32Smrg	} else {
2680bd37d32Smrg	    value += ' ' + 1;
2690bd37d32Smrg	    line[count++] = CharOf(0xC0 + (value >> 6));
2700bd37d32Smrg	    line[count++] = CharOf(0x80 + (value & 0x3F));
2710bd37d32Smrg	}
2720bd37d32Smrg	break;
2730bd37d32Smrg    case SET_SGR_EXT_MODE_MOUSE:
2740bd37d32Smrg    case SET_URXVT_EXT_MODE_MOUSE:
275f2e35a3aSmrg    case SET_PIXEL_POSITION_MOUSE:
2760bd37d32Smrg	count += (unsigned) sprintf((char *) line + count, "%d", value + 1);
2770bd37d32Smrg	break;
2780bd37d32Smrg    }
2790bd37d32Smrg    return count;
2800bd37d32Smrg}
2810bd37d32Smrg
2820bd37d32Smrgstatic unsigned
283e0a2b6dfSmrgEmitMousePositionSeparator(TScreen *screen, Char line[], unsigned count)
2840bd37d32Smrg{
2850bd37d32Smrg    switch (screen->extend_coords) {
2860bd37d32Smrg    case SET_SGR_EXT_MODE_MOUSE:
2870bd37d32Smrg    case SET_URXVT_EXT_MODE_MOUSE:
288f2e35a3aSmrg    case SET_PIXEL_POSITION_MOUSE:
2890bd37d32Smrg	line[count++] = ';';
2900bd37d32Smrg	break;
291d522f475Smrg    }
292492d43a5Smrg    return count;
293492d43a5Smrg}
294d522f475Smrg
295f2e35a3aSmrgenum {
296f2e35a3aSmrg    scanMods,
297f2e35a3aSmrg    scanKey,
298f2e35a3aSmrg    scanColon,
299f2e35a3aSmrg    scanFunc,
300f2e35a3aSmrg    scanArgs
301f2e35a3aSmrg};
302f2e35a3aSmrg
303f2e35a3aSmrg#if OPT_TRACE > 1
304f2e35a3aSmrgstatic const char *
305f2e35a3aSmrgvisibleScan(int mode)
306f2e35a3aSmrg{
307f2e35a3aSmrg    const char *result = "?";
308f2e35a3aSmrg#define DATA(name) case name: result = #name; break
309f2e35a3aSmrg    switch (mode) {
310f2e35a3aSmrg	DATA(scanMods);
311f2e35a3aSmrg	DATA(scanKey);
312f2e35a3aSmrg	DATA(scanColon);
313f2e35a3aSmrg	DATA(scanFunc);
314f2e35a3aSmrg	DATA(scanArgs);
315f2e35a3aSmrg    }
316f2e35a3aSmrg#undef DATA
317f2e35a3aSmrg    return result;
318f2e35a3aSmrg}
319f2e35a3aSmrg#endif
320f2e35a3aSmrg
321f2e35a3aSmrg#define L_BRACK '<'
322f2e35a3aSmrg#define R_BRACK '>'
323f2e35a3aSmrg#define L_PAREN '('
324f2e35a3aSmrg#define R_PAREN ')'
325f2e35a3aSmrg
326f2e35a3aSmrgstatic char *
327f2e35a3aSmrgscanTrans(char *source, int *this_is, int *next_is, unsigned *first, unsigned *last)
328f2e35a3aSmrg{
329f2e35a3aSmrg    char *target = source;
330f2e35a3aSmrg
331f2e35a3aSmrg    *first = *last = 0;
332f2e35a3aSmrg    if (IsEmpty(target)) {
333f2e35a3aSmrg	target = 0;
334f2e35a3aSmrg    } else {
335f2e35a3aSmrg	do {
3364419d26bSmrg	    char ch;
337f2e35a3aSmrg	    while (IsSpace(*target))
338f2e35a3aSmrg		target++;
339f2e35a3aSmrg	    *first = (unsigned) (target - source);
340f2e35a3aSmrg	    switch (*this_is = *next_is) {
341f2e35a3aSmrg	    case scanMods:
342f2e35a3aSmrg		while ((ch = *target)) {
343f2e35a3aSmrg		    if (IsSpace(ch)) {
344f2e35a3aSmrg			break;
345f2e35a3aSmrg		    } else if (ch == L_BRACK) {
346f2e35a3aSmrg			*next_is = scanKey;
347f2e35a3aSmrg			break;
348f2e35a3aSmrg		    } else if (ch == ':') {
349f2e35a3aSmrg			*next_is = scanColon;
350f2e35a3aSmrg			break;
351f2e35a3aSmrg		    } else if (ch == '~' && target != source) {
352f2e35a3aSmrg			break;
353f2e35a3aSmrg		    }
354f2e35a3aSmrg		    target++;
355f2e35a3aSmrg		}
356f2e35a3aSmrg		break;
357f2e35a3aSmrg	    case scanKey:
358f2e35a3aSmrg		while ((ch = *target)) {
359f2e35a3aSmrg		    if (IsSpace(ch)) {
360f2e35a3aSmrg			break;
361f2e35a3aSmrg		    } else if (ch == ':') {
362f2e35a3aSmrg			*next_is = scanColon;
363f2e35a3aSmrg			break;
364f2e35a3aSmrg		    }
365f2e35a3aSmrg		    target++;
366f2e35a3aSmrg		    if (ch == R_BRACK)
367f2e35a3aSmrg			break;
368f2e35a3aSmrg		}
369f2e35a3aSmrg		break;
370f2e35a3aSmrg	    case scanColon:
371f2e35a3aSmrg		*next_is = scanFunc;
372f2e35a3aSmrg		target++;
373f2e35a3aSmrg		break;
374f2e35a3aSmrg	    case scanFunc:
375f2e35a3aSmrg		while ((ch = *target)) {
376f2e35a3aSmrg		    if (IsSpace(ch)) {
377f2e35a3aSmrg			break;
378f2e35a3aSmrg		    } else if (ch == L_PAREN) {
379f2e35a3aSmrg			*next_is = scanArgs;
380f2e35a3aSmrg			break;
381f2e35a3aSmrg		    }
382f2e35a3aSmrg		    target++;
383f2e35a3aSmrg		}
384f2e35a3aSmrg		break;
385f2e35a3aSmrg	    case scanArgs:
386f2e35a3aSmrg		while ((ch = *target)) {
387f2e35a3aSmrg		    if (ch == R_PAREN) {
388f2e35a3aSmrg			target++;
389f2e35a3aSmrg			*next_is = scanFunc;
390f2e35a3aSmrg			break;
391f2e35a3aSmrg		    }
392f2e35a3aSmrg		    target++;
393f2e35a3aSmrg		}
394f2e35a3aSmrg		break;
395f2e35a3aSmrg	    }
396f2e35a3aSmrg	    *last = (unsigned) (target - source);
397f2e35a3aSmrg	    if (*target == '\n') {
398f2e35a3aSmrg		*next_is = scanMods;
399f2e35a3aSmrg		target++;
400f2e35a3aSmrg	    }
401f2e35a3aSmrg	} while (*first == *last);
402f2e35a3aSmrg    }
403f2e35a3aSmrg    return target;
404f2e35a3aSmrg}
405f2e35a3aSmrg
406f2e35a3aSmrgvoid
407f2e35a3aSmrgxtermButtonInit(XtermWidget xw)
408f2e35a3aSmrg{
409f2e35a3aSmrg    Widget w = (Widget) xw;
410f2e35a3aSmrg    XErrorHandler save = XSetErrorHandler(ignore_x11_error);
411f2e35a3aSmrg    XtTranslations xlations;
412f2e35a3aSmrg    Widget xcelerat;
413f2e35a3aSmrg    String result;
414f2e35a3aSmrg
415f2e35a3aSmrg    XtVaGetValues(w,
416f2e35a3aSmrg		  XtNtranslations, &xlations,
417f2e35a3aSmrg		  XtNaccelerators, &xcelerat,
418f2e35a3aSmrg		  (XtPointer) 0);
419f2e35a3aSmrg    result = _XtPrintXlations(w, xlations, xcelerat, True);
420f2e35a3aSmrg    if (result) {
421f2e35a3aSmrg	static const char *table[] =
422f2e35a3aSmrg	{
423f2e35a3aSmrg	    "insert-selection",
424f2e35a3aSmrg	    "select-end",
425f2e35a3aSmrg	    "select-extend",
426f2e35a3aSmrg	    "select-start",
427f2e35a3aSmrg	    "start-extend",
428f2e35a3aSmrg	};
429f2e35a3aSmrg	char *data = x_strdup(result);
430f2e35a3aSmrg	char *next;
431f2e35a3aSmrg	int state = scanMods;
432f2e35a3aSmrg	int state2 = scanMods;
433f2e35a3aSmrg	unsigned first;
434f2e35a3aSmrg	unsigned last;
435f2e35a3aSmrg	int have_button = -1;
436f2e35a3aSmrg	Bool want_button = False;
437f2e35a3aSmrg	Bool have_shift = False;
438f2e35a3aSmrg	unsigned allowed = 0;
439f2e35a3aSmrg	unsigned disallow = 0;
440f2e35a3aSmrg
441f2e35a3aSmrg	TRACE(("xtermButtonInit length %ld\n", (long) strlen(result)));
442f2e35a3aSmrg	xw->keyboard.print_translations = data;
443f2e35a3aSmrg	while ((next = scanTrans(data, &state, &state2, &first, &last)) != 0) {
444f2e35a3aSmrg	    unsigned len = (last - first);
445f2e35a3aSmrg	    TRACE2(("parse %s:%d..%d '%.*s'\n",
446f2e35a3aSmrg		    visibleScan(state), first, last,
447f2e35a3aSmrg		    len, data + first));
448f2e35a3aSmrg	    if (state == scanMods) {
449f2e35a3aSmrg		if (len > 1 && data[first] == '~') {
450f2e35a3aSmrg		    len--;
451f2e35a3aSmrg		    first++;
452f2e35a3aSmrg		}
453f2e35a3aSmrg		if (len == 7 && !x_strncasecmp(data + first, "button", len - 1)) {
454f2e35a3aSmrg		    have_button = data[first + 6] - '0';
455f2e35a3aSmrg		} else if (len == 5 && !x_strncasecmp(data + first, "shift", len)) {
456f2e35a3aSmrg		    have_shift = True;
457f2e35a3aSmrg		}
458f2e35a3aSmrg	    } else if (state == scanKey) {
459f2e35a3aSmrg		if (!x_strncasecmp(data + first, "<buttonpress>", len) ||
460f2e35a3aSmrg		    !x_strncasecmp(data + first, "<buttonrelease>", len)) {
461f2e35a3aSmrg		    want_button = True;
462f2e35a3aSmrg		} else if (want_button) {
463f2e35a3aSmrg		    have_button = data[first] - '0';
464f2e35a3aSmrg		    want_button = False;
465f2e35a3aSmrg		}
466f2e35a3aSmrg	    } else if (state == scanFunc && have_button > 0) {
467f2e35a3aSmrg		Cardinal n;
468f2e35a3aSmrg		unsigned bmask = 1U << (have_button - 1);
469f2e35a3aSmrg		for (n = 0; n < XtNumber(table); ++n) {
470f2e35a3aSmrg		    if (!x_strncasecmp(table[n], data + first, len)) {
471f2e35a3aSmrg			TRACE(("...button %d: %s%s\n",
472f2e35a3aSmrg			       have_button, table[n],
473f2e35a3aSmrg			       have_shift ? " (disallow)" : ""));
474f2e35a3aSmrg			if (have_shift)
475f2e35a3aSmrg			    disallow |= bmask;
476f2e35a3aSmrg			else
477f2e35a3aSmrg			    allowed |= bmask;
478f2e35a3aSmrg			break;
479f2e35a3aSmrg		    }
480f2e35a3aSmrg		}
481f2e35a3aSmrg	    }
482f2e35a3aSmrg	    if (state2 == scanMods && state >= scanColon) {
483f2e35a3aSmrg		have_button = -1;
484f2e35a3aSmrg		want_button = False;
485f2e35a3aSmrg		have_shift = False;
486f2e35a3aSmrg	    }
487f2e35a3aSmrg	    state = state2;
488f2e35a3aSmrg	    data = next;
489f2e35a3aSmrg	}
490f2e35a3aSmrg	XFree((char *) result);
491f2e35a3aSmrg	xw->keyboard.shift_buttons = allowed & ~disallow;
492f2e35a3aSmrg#if OPT_TRACE
493f2e35a3aSmrg	if (xw->keyboard.shift_buttons) {
494f2e35a3aSmrg	    int button = 0;
495f2e35a3aSmrg	    unsigned mask = xw->keyboard.shift_buttons;
496f2e35a3aSmrg	    TRACE(("...Buttons used for selection that can be overridden:"));
497f2e35a3aSmrg	    while (mask != 0) {
498f2e35a3aSmrg		++button;
499f2e35a3aSmrg		if ((mask & 1) != 0)
500f2e35a3aSmrg		    TRACE((" %d", button));
501f2e35a3aSmrg		mask >>= 1;
502f2e35a3aSmrg	    }
503f2e35a3aSmrg	    TRACE(("\n"));
504f2e35a3aSmrg	} else {
505f2e35a3aSmrg	    TRACE(("...No buttons used with selection can be overridden\n"));
506f2e35a3aSmrg	}
507f2e35a3aSmrg#endif
508f2e35a3aSmrg    }
509f2e35a3aSmrg    XSetErrorHandler(save);
510f2e35a3aSmrg}
511f2e35a3aSmrg
512f2e35a3aSmrg/*
513f2e35a3aSmrg * Shift and control are regular X11 modifiers, but meta is not:
514f2e35a3aSmrg * + X10 (which had no xmodmap utility) had a meta mask, but X11 did not.
515f2e35a3aSmrg * + X11R1 introduced xmodmap, along with the current set of modifier masks.
516f2e35a3aSmrg *   The meta key has been assumed to be mod1 since X11R1.
517f2e35a3aSmrg *   The initial xterm logic in X11 was different, but gave the same result.
518f2e35a3aSmrg * + X11R2 modified xterm was to eliminate the X10 table which provided part of
519f2e35a3aSmrg *   the meta logic.
520f2e35a3aSmrg * + X11R3 modified Xt, making Meta_L and Meta_R assignable via xmodmap, and
521f2e35a3aSmrg *   equating Alt with Meta.  Neither Alt/Meta are modifiers, but Alt is more
522f2e35a3aSmrg *   likely to be on the keyboard.  This release also added keymap tables for
523f2e35a3aSmrg *   the server; Meta was used frequently in HP keymaps, which were the most
524f2e35a3aSmrg *   extensive set of keymaps.
525f2e35a3aSmrg * + X11R4 mentions Meta in the ICCCM, stating that if Meta_L or Meta_R are
526f2e35a3aSmrg *   found in the keysyms for a given modifier, that the client should use
527f2e35a3aSmrg *   that modifier.
528f2e35a3aSmrg *
529f2e35a3aSmrg * This function follows the ICCCM, picking the modifier which contains the
530f2e35a3aSmrg * Meta_L/Meta_R keysyms (if available), falling back to the Alt_L/Alt_R
531f2e35a3aSmrg * (as per X11R3), and ultimately to mod1 (per X11R1).
532f2e35a3aSmrg */
533f2e35a3aSmrgstatic unsigned
534f2e35a3aSmrgMetaMask(XtermWidget xw)
535f2e35a3aSmrg{
536f2e35a3aSmrg#if OPT_NUM_LOCK
537f2e35a3aSmrg    unsigned meta = xw->work.meta_mods;
538f2e35a3aSmrg    if (meta == 0)
539f2e35a3aSmrg	meta = xw->work.alt_mods;
540f2e35a3aSmrg    if (meta == 0)
541f2e35a3aSmrg	meta = Mod1Mask;
542f2e35a3aSmrg#else
543f2e35a3aSmrg    unsigned meta = Mod1Mask;
544f2e35a3aSmrg    (void) xw;
545f2e35a3aSmrg#endif
546f2e35a3aSmrg    return meta;
547f2e35a3aSmrg}
548f2e35a3aSmrg
549f2e35a3aSmrg/*
550f2e35a3aSmrg * Returns a mask of the modifiers we may use for modifying the mouse protocol
551f2e35a3aSmrg * response strings.
552f2e35a3aSmrg */
553f2e35a3aSmrgstatic unsigned
554f2e35a3aSmrgOurModifiers(XtermWidget xw)
555f2e35a3aSmrg{
556f2e35a3aSmrg    return (ShiftMask
557f2e35a3aSmrg	    | ControlMask
558f2e35a3aSmrg	    | MetaMask(xw));
559f2e35a3aSmrg}
560f2e35a3aSmrg
561f2e35a3aSmrg/*
562f2e35a3aSmrg * The actual check for the shift-mask, to see if it should tell xterm to
563f2e35a3aSmrg * override mouse-protocol in favor of select/paste actions depends upon
564f2e35a3aSmrg * whether the shiftEscape resource is set to true/always vs false/never.
565f2e35a3aSmrg */
566f2e35a3aSmrgstatic Boolean
567f2e35a3aSmrgShiftOverride(XtermWidget xw, unsigned state, int button)
568f2e35a3aSmrg{
569f2e35a3aSmrg    unsigned check = (state & OurModifiers(xw));
570f2e35a3aSmrg    Boolean result = False;
571f2e35a3aSmrg
572f2e35a3aSmrg    if (check & ShiftMask) {
573f2e35a3aSmrg	if (xw->keyboard.shift_escape == ssFalse ||
574f2e35a3aSmrg	    xw->keyboard.shift_escape == ssNever) {
575f2e35a3aSmrg	    result = True;
576f2e35a3aSmrg	} else if (xw->keyboard.shift_escape == ssTrue) {
577f2e35a3aSmrg	    /*
578f2e35a3aSmrg	     * Check if the button is one that we found does not directly use
579f2e35a3aSmrg	     * the shift-modifier in its bindings to select/copy actions.
580f2e35a3aSmrg	     */
581f2e35a3aSmrg	    if (button > 0 && button <= MaxMouseBtn) {
582f2e35a3aSmrg		if (xw->keyboard.shift_buttons & (1U << (button - 1))) {
583f2e35a3aSmrg		    result = True;
584f2e35a3aSmrg		}
585f2e35a3aSmrg	    } else {
586f2e35a3aSmrg		result = True;	/* unlikely, and we don't care */
587f2e35a3aSmrg	    }
588f2e35a3aSmrg	}
589f2e35a3aSmrg    }
590f2e35a3aSmrg    TRACE2(("ShiftOverride ( %#x -> %#x ) %d\n", state, check, result));
591f2e35a3aSmrg    return result;
592f2e35a3aSmrg}
593f2e35a3aSmrg
594f2e35a3aSmrg/*
595f2e35a3aSmrg * Normally xterm treats the shift-modifier specially when the mouse protocol
596f2e35a3aSmrg * is active.  The translations resource binds otherwise unmodified button
597f2e35a3aSmrg * for these mouse-related events:
598f2e35a3aSmrg *
599f2e35a3aSmrg *         ~Meta <Btn1Down>:select-start() \n\
600f2e35a3aSmrg *       ~Meta <Btn1Motion>:select-extend() \n\
601f2e35a3aSmrg *     ~Ctrl ~Meta <Btn2Up>:insert-selection(SELECT, CUT_BUFFER0) \n\
602f2e35a3aSmrg *   ~Ctrl ~Meta <Btn3Down>:start-extend() \n\
603f2e35a3aSmrg *       ~Meta <Btn3Motion>:select-extend() \n\
604f2e35a3aSmrg *                  <BtnUp>:select-end(SELECT, CUT_BUFFER0) \n\
605f2e35a3aSmrg *
606f2e35a3aSmrg * There is no API in the X libraries which would tell us if a given mouse
607f2e35a3aSmrg * button is bound to one of these actions.  These functions make the choice
608f2e35a3aSmrg * configurable.
609f2e35a3aSmrg */
610f2e35a3aSmrgstatic Bool
611f2e35a3aSmrgInterpretButton(XtermWidget xw, XButtonEvent *event)
612f2e35a3aSmrg{
613f2e35a3aSmrg    Bool result = False;
614f2e35a3aSmrg
615f2e35a3aSmrg    if (ShiftOverride(xw, event->state, (int) event->button)) {
616f2e35a3aSmrg	TRACE(("...shift-button #%d overrides mouse-protocol\n", event->button));
617f2e35a3aSmrg	result = True;
618f2e35a3aSmrg    }
619f2e35a3aSmrg    return result;
620f2e35a3aSmrg}
621f2e35a3aSmrg
622f2e35a3aSmrg#define Button1Index 8		/* X.h should have done this */
623f2e35a3aSmrg
624f2e35a3aSmrgstatic int
625f2e35a3aSmrgMotionButton(unsigned state)
626f2e35a3aSmrg{
627f2e35a3aSmrg    unsigned bmask = state >> Button1Index;
628f2e35a3aSmrg    int result = 1;
629f2e35a3aSmrg
630f2e35a3aSmrg    if (bmask != 0) {
631f2e35a3aSmrg	while (!(bmask & 1)) {
632f2e35a3aSmrg	    ++result;
633f2e35a3aSmrg	    bmask >>= 1;
634f2e35a3aSmrg	}
635f2e35a3aSmrg    }
636f2e35a3aSmrg    return result;
637f2e35a3aSmrg}
638f2e35a3aSmrg
639f2e35a3aSmrgstatic Bool
640f2e35a3aSmrgInterpretEvent(XtermWidget xw, XEvent *event)
641f2e35a3aSmrg{
642f2e35a3aSmrg    Bool result = False;	/* if not a button, is motion */
643f2e35a3aSmrg
644f2e35a3aSmrg    if (IsBtnEvent(event)) {
645f2e35a3aSmrg	result = InterpretButton(xw, (XButtonEvent *) event);
646f2e35a3aSmrg    } else if (event->type == MotionNotify) {
647f2e35a3aSmrg	unsigned state = event->xmotion.state;
648f2e35a3aSmrg	int button = MotionButton(state);
649f2e35a3aSmrg
650f2e35a3aSmrg	if (ShiftOverride(xw, state, button)) {
651f2e35a3aSmrg	    TRACE(("...shift-motion #%d (%d,%d) overrides mouse-protocol\n",
652f2e35a3aSmrg		   button,
653f2e35a3aSmrg		   event->xmotion.y,
654f2e35a3aSmrg		   event->xmotion.x));
655f2e35a3aSmrg	    result = True;
656f2e35a3aSmrg	}
657f2e35a3aSmrg    }
658f2e35a3aSmrg    return result;
659f2e35a3aSmrg}
660f2e35a3aSmrg
661f2e35a3aSmrg#define OverrideEvent(event)  InterpretEvent(xw, event)
662f2e35a3aSmrg#define OverrideButton(event) InterpretButton(xw, event)
663f2e35a3aSmrg
664f2e35a3aSmrg/*
665f2e35a3aSmrg * Returns true if we handled the event here, and nothing more is needed.
666f2e35a3aSmrg */
667492d43a5SmrgBool
668894e0ac8SmrgSendMousePosition(XtermWidget xw, XEvent *event)
669492d43a5Smrg{
670492d43a5Smrg    XButtonEvent *my_event = (XButtonEvent *) event;
671492d43a5Smrg    Bool result = False;
672d522f475Smrg
673913cc679Smrg    switch (okSendMousePos(xw)) {
674492d43a5Smrg    case MOUSE_OFF:
675492d43a5Smrg	/* If send_mouse_pos mode isn't on, we shouldn't be here */
676492d43a5Smrg	break;
677d522f475Smrg
678d522f475Smrg    case BTN_EVENT_MOUSE:
679d522f475Smrg    case ANY_EVENT_MOUSE:
680f2e35a3aSmrg	if (!OverrideEvent(event)) {
681492d43a5Smrg	    /* xterm extension for motion reporting. June 1998 */
682492d43a5Smrg	    /* EditorButton() will distinguish between the modes */
683492d43a5Smrg	    switch (event->type) {
684492d43a5Smrg	    case MotionNotify:
685492d43a5Smrg		my_event->button = 0;
686492d43a5Smrg		/* FALLTHRU */
687492d43a5Smrg	    case ButtonPress:
688492d43a5Smrg		/* FALLTHRU */
689492d43a5Smrg	    case ButtonRelease:
690492d43a5Smrg		EditorButton(xw, my_event);
691492d43a5Smrg		result = True;
692492d43a5Smrg		break;
6932eaa94a1Schristos	    }
694d522f475Smrg	}
695492d43a5Smrg	break;
696d522f475Smrg
697913cc679Smrg    case X10_MOUSE:		/* X10 compatibility sequences */
698492d43a5Smrg	if (IsBtnEvent(event)) {
699f2e35a3aSmrg	    if (!OverrideButton(my_event)) {
700913cc679Smrg		if (my_event->type == ButtonPress)
701492d43a5Smrg		    EditorButton(xw, my_event);
702913cc679Smrg		result = True;
703913cc679Smrg	    }
704913cc679Smrg	}
705913cc679Smrg	break;
706492d43a5Smrg
707913cc679Smrg    case VT200_HIGHLIGHT_MOUSE:	/* DEC vt200 hilite tracking */
708913cc679Smrg	if (IsBtnEvent(event)) {
709f2e35a3aSmrg	    if (!OverrideButton(my_event)) {
710f2e35a3aSmrg		if (my_event->type == ButtonPress &&
711f2e35a3aSmrg		    my_event->button == Button1) {
712f2e35a3aSmrg		    TrackDown(xw, my_event);
713f2e35a3aSmrg		} else {
714f2e35a3aSmrg		    EditorButton(xw, my_event);
715f2e35a3aSmrg		}
716913cc679Smrg		result = True;
717913cc679Smrg	    }
718913cc679Smrg	}
719913cc679Smrg	break;
720492d43a5Smrg
721913cc679Smrg    case VT200_MOUSE:		/* DEC vt200 compatible */
722913cc679Smrg	if (IsBtnEvent(event)) {
723f2e35a3aSmrg	    if (!OverrideButton(my_event)) {
724913cc679Smrg		EditorButton(xw, my_event);
725913cc679Smrg		result = True;
726913cc679Smrg	    }
727913cc679Smrg	}
728913cc679Smrg	break;
729913cc679Smrg
730913cc679Smrg    case DEC_LOCATOR:
731492d43a5Smrg#if OPT_DEC_LOCATOR
732f2e35a3aSmrg	if (IsBtnEvent(event) || event->type == MotionNotify) {
733913cc679Smrg	    result = SendLocatorPosition(xw, my_event);
734492d43a5Smrg	}
735f2e35a3aSmrg#endif /* OPT_DEC_LOCATOR */
736913cc679Smrg	break;
737d522f475Smrg    }
738492d43a5Smrg    return result;
739d522f475Smrg}
740d522f475Smrg
741d522f475Smrg#if OPT_DEC_LOCATOR
742d522f475Smrg
743d522f475Smrg#define	LocatorCoords( row, col, x, y, oor )			\
744d522f475Smrg    if( screen->locator_pixels ) {				\
745d522f475Smrg	(oor)=False; (row) = (y)+1; (col) = (x)+1;		\
746d522f475Smrg	/* Limit to screen dimensions */			\
747d522f475Smrg	if ((row) < 1) (row) = 1,(oor)=True;			\
748d522f475Smrg	else if ((row) > screen->border*2+Height(screen))	\
749d522f475Smrg	    (row) = screen->border*2+Height(screen),(oor)=True;	\
750d522f475Smrg	if ((col) < 1) (col) = 1,(oor)=True;			\
751d522f475Smrg	else if ((col) > OriginX(screen)*2+Width(screen))	\
752d522f475Smrg	    (col) = OriginX(screen)*2+Width(screen),(oor)=True;	\
753d522f475Smrg    } else {							\
754d522f475Smrg	(oor)=False;						\
755d522f475Smrg	/* Compute character position of mouse pointer */	\
756d522f475Smrg	(row) = ((y) - screen->border) / FontHeight(screen);	\
757d522f475Smrg	(col) = ((x) - OriginX(screen)) / FontWidth(screen);	\
758d522f475Smrg	/* Limit to screen dimensions */			\
759d522f475Smrg	if ((row) < 0) (row) = 0,(oor)=True;			\
760d522f475Smrg	else if ((row) > screen->max_row)			\
761d522f475Smrg	    (row) = screen->max_row,(oor)=True;			\
762d522f475Smrg	if ((col) < 0) (col) = 0,(oor)=True;			\
763d522f475Smrg	else if ((col) > screen->max_col)			\
764d522f475Smrg	    (col) = screen->max_col,(oor)=True;			\
765d522f475Smrg	(row)++; (col)++;					\
766d522f475Smrg    }
767d522f475Smrg
768d522f475Smrgstatic Bool
769894e0ac8SmrgSendLocatorPosition(XtermWidget xw, XButtonEvent *event)
770d522f475Smrg{
771d522f475Smrg    ANSI reply;
772956cc18dSsnj    TScreen *screen = TScreenOf(xw);
773d522f475Smrg    int row, col;
774d522f475Smrg    Bool oor;
775d522f475Smrg    int button;
7762eaa94a1Schristos    unsigned state;
777d522f475Smrg
778d522f475Smrg    /* Make sure the event is an appropriate type */
779f2e35a3aSmrg    if (IsBtnEvent(event)) {
780f2e35a3aSmrg	if (OverrideButton(event))
781f2e35a3aSmrg	    return (False);
782f2e35a3aSmrg    } else {
783f2e35a3aSmrg	if (!screen->loc_filter)
784f2e35a3aSmrg	    return (False);
785f2e35a3aSmrg    }
786d522f475Smrg
787d522f475Smrg    if ((event->type == ButtonPress &&
788d522f475Smrg	 !(screen->locator_events & LOC_BTNS_DN)) ||
789d522f475Smrg	(event->type == ButtonRelease &&
790d522f475Smrg	 !(screen->locator_events & LOC_BTNS_UP)))
791d522f475Smrg	return (True);
792d522f475Smrg
793d522f475Smrg    if (event->type == MotionNotify) {
794d522f475Smrg	CheckLocatorPosition(xw, event);
795d522f475Smrg	return (True);
796d522f475Smrg    }
797d522f475Smrg
798d522f475Smrg    /* get button # */
799492d43a5Smrg    button = (int) event->button - 1;
800d522f475Smrg
801492d43a5Smrg    LocatorCoords(row, col, event->x, event->y, oor);
802d522f475Smrg
803d522f475Smrg    /*
804d522f475Smrg     * DECterm mouse:
805d522f475Smrg     *
806d522f475Smrg     * ESCAPE '[' event ; mask ; row ; column '&' 'w'
807d522f475Smrg     */
808d522f475Smrg    memset(&reply, 0, sizeof(reply));
809d522f475Smrg    reply.a_type = ANSI_CSI;
810d522f475Smrg
811d522f475Smrg    if (oor) {
812d522f475Smrg	reply.a_nparam = 1;
813d522f475Smrg	reply.a_param[0] = 0;	/* Event - 0 = locator unavailable */
814d522f475Smrg	reply.a_inters = '&';
815d522f475Smrg	reply.a_final = 'w';
816d522f475Smrg	unparseseq(xw, &reply);
817d522f475Smrg
818d522f475Smrg	if (screen->locator_reset) {
819d522f475Smrg	    MotionOff(screen, xw);
820d522f475Smrg	    screen->send_mouse_pos = MOUSE_OFF;
821d522f475Smrg	}
822d522f475Smrg	return (True);
823d522f475Smrg    }
824d522f475Smrg
825d522f475Smrg    /*
826d522f475Smrg     * event:
827d522f475Smrg     *        1       no buttons
828d522f475Smrg     *        2       left button down
829d522f475Smrg     *        3       left button up
830d522f475Smrg     *        4       middle button down
831d522f475Smrg     *        5       middle button up
832d522f475Smrg     *        6       right button down
833d522f475Smrg     *        7       right button up
834d522f475Smrg     *        8       M4 down
835d522f475Smrg     *        9       M4 up
836d522f475Smrg     */
837d522f475Smrg    reply.a_nparam = 4;
838d522f475Smrg    switch (event->type) {
839d522f475Smrg    case ButtonPress:
8402eaa94a1Schristos	reply.a_param[0] = (ParmType) (2 + (button << 1));
841d522f475Smrg	break;
842d522f475Smrg    case ButtonRelease:
8432eaa94a1Schristos	reply.a_param[0] = (ParmType) (3 + (button << 1));
844d522f475Smrg	break;
845d522f475Smrg    default:
846d522f475Smrg	return (True);
847d522f475Smrg    }
848d522f475Smrg    /*
849d522f475Smrg     * mask:
850913cc679Smrg     * bit7   bit6   bit5   bit4   bit3     bit2       bit1         bit0
851913cc679Smrg     *                             M4 down  left down  middle down  right down
852d522f475Smrg     *
853d522f475Smrg     * Notice that Button1 (left) and Button3 (right) are swapped in the mask.
854d522f475Smrg     * Also, mask should be the state after the button press/release,
855d522f475Smrg     * X provides the state not including the button press/release.
856d522f475Smrg     */
857492d43a5Smrg    state = (event->state
858d522f475Smrg	     & (Button1Mask | Button2Mask | Button3Mask | Button4Mask)) >> 8;
8594e40088cSchristos    /* update mask to "after" state */
86020d2c4d2Smrg    state ^= ((unsigned) (1 << button));
8614e40088cSchristos    /* swap Button1 & Button3 */
862956cc18dSsnj    state = ((state & (unsigned) ~(4 | 1))
863956cc18dSsnj	     | ((state & 1) ? 4 : 0)
864956cc18dSsnj	     | ((state & 4) ? 1 : 0));
865d522f475Smrg
8662eaa94a1Schristos    reply.a_param[1] = (ParmType) state;
8672eaa94a1Schristos    reply.a_param[2] = (ParmType) row;
8682eaa94a1Schristos    reply.a_param[3] = (ParmType) col;
869d522f475Smrg    reply.a_inters = '&';
870d522f475Smrg    reply.a_final = 'w';
871d522f475Smrg
872d522f475Smrg    unparseseq(xw, &reply);
873d522f475Smrg
874d522f475Smrg    if (screen->locator_reset) {
875d522f475Smrg	MotionOff(screen, xw);
876d522f475Smrg	screen->send_mouse_pos = MOUSE_OFF;
877d522f475Smrg    }
878d522f475Smrg
879d522f475Smrg    /*
880913cc679Smrg     * DECterm turns the Locator off if a button is pressed while a filter
881913cc679Smrg     * rectangle is active.  This might be a bug, but I don't know, so I'll
882913cc679Smrg     * emulate it anyway.
883d522f475Smrg     */
884d522f475Smrg    if (screen->loc_filter) {
885d522f475Smrg	screen->send_mouse_pos = MOUSE_OFF;
886d522f475Smrg	screen->loc_filter = False;
887d522f475Smrg	screen->locator_events = 0;
888d522f475Smrg	MotionOff(screen, xw);
889d522f475Smrg    }
890d522f475Smrg
891d522f475Smrg    return (True);
892d522f475Smrg}
893d522f475Smrg
894d522f475Smrg/*
895d522f475Smrg * mask:
896d522f475Smrg * bit 7   bit 6   bit 5   bit 4   bit 3   bit 2       bit 1         bit 0
897d522f475Smrg *                                 M4 down left down   middle down   right down
898d522f475Smrg *
899d522f475Smrg * Button1 (left) and Button3 (right) are swapped in the mask relative to X.
900d522f475Smrg */
901d522f475Smrg#define	ButtonState(state, mask)	\
902913cc679Smrg{ int stemp = (int) (((mask) & (Button1Mask | Button2Mask | Button3Mask | Button4Mask)) >> 8);	\
903d522f475Smrg  /* swap Button1 & Button3 */								\
904913cc679Smrg  (state) = (stemp & ~(4|1)) | ((stemp & 1) ? 4 : 0) | ((stemp & 4) ? 1 : 0);			\
905d522f475Smrg}
906d522f475Smrg
907d522f475Smrgvoid
908d522f475SmrgGetLocatorPosition(XtermWidget xw)
909d522f475Smrg{
910d522f475Smrg    ANSI reply;
911956cc18dSsnj    TScreen *screen = TScreenOf(xw);
912d522f475Smrg    Window root, child;
913d522f475Smrg    int rx, ry, x, y;
914f2e35a3aSmrg    unsigned int mask = 0;
915d522f475Smrg    int row = 0, col = 0;
916d522f475Smrg    Bool oor = False;
917d522f475Smrg    Bool ret = False;
918d522f475Smrg    int state;
919d522f475Smrg
920d522f475Smrg    /*
921913cc679Smrg     * DECterm turns the Locator off if the position is requested while a
922913cc679Smrg     * filter rectangle is active.  This might be a bug, but I don't know, so
923913cc679Smrg     * I'll emulate it anyways.
924d522f475Smrg     */
925d522f475Smrg    if (screen->loc_filter) {
926d522f475Smrg	screen->send_mouse_pos = MOUSE_OFF;
927d522f475Smrg	screen->loc_filter = False;
928d522f475Smrg	screen->locator_events = 0;
929d522f475Smrg	MotionOff(screen, xw);
930d522f475Smrg    }
931d522f475Smrg
932d522f475Smrg    memset(&reply, 0, sizeof(reply));
933d522f475Smrg    reply.a_type = ANSI_CSI;
934d522f475Smrg
935913cc679Smrg    if (okSendMousePos(xw) == DEC_LOCATOR) {
936d522f475Smrg	ret = XQueryPointer(screen->display, VWindow(screen), &root,
937d522f475Smrg			    &child, &rx, &ry, &x, &y, &mask);
938d522f475Smrg	if (ret) {
939d522f475Smrg	    LocatorCoords(row, col, x, y, oor);
940d522f475Smrg	}
941d522f475Smrg    }
942d522f475Smrg    if (ret == False || oor) {
943d522f475Smrg	reply.a_nparam = 1;
944d522f475Smrg	reply.a_param[0] = 0;	/* Event - 0 = locator unavailable */
945d522f475Smrg	reply.a_inters = '&';
946d522f475Smrg	reply.a_final = 'w';
947d522f475Smrg	unparseseq(xw, &reply);
948d522f475Smrg
949d522f475Smrg	if (screen->locator_reset) {
950d522f475Smrg	    MotionOff(screen, xw);
951d522f475Smrg	    screen->send_mouse_pos = MOUSE_OFF;
952d522f475Smrg	}
953d522f475Smrg	return;
954d522f475Smrg    }
955d522f475Smrg
956d522f475Smrg    ButtonState(state, mask);
957d522f475Smrg
958d522f475Smrg    reply.a_nparam = 4;
959d522f475Smrg    reply.a_param[0] = 1;	/* Event - 1 = response to locator request */
9602eaa94a1Schristos    reply.a_param[1] = (ParmType) state;
9612eaa94a1Schristos    reply.a_param[2] = (ParmType) row;
9622eaa94a1Schristos    reply.a_param[3] = (ParmType) col;
963d522f475Smrg    reply.a_inters = '&';
964d522f475Smrg    reply.a_final = 'w';
965d522f475Smrg    unparseseq(xw, &reply);
966d522f475Smrg
967d522f475Smrg    if (screen->locator_reset) {
968d522f475Smrg	MotionOff(screen, xw);
969d522f475Smrg	screen->send_mouse_pos = MOUSE_OFF;
970d522f475Smrg    }
971d522f475Smrg}
972d522f475Smrg
973d522f475Smrgvoid
974d522f475SmrgInitLocatorFilter(XtermWidget xw)
975d522f475Smrg{
976d522f475Smrg    ANSI reply;
977956cc18dSsnj    TScreen *screen = TScreenOf(xw);
978d522f475Smrg    Window root, child;
979d522f475Smrg    int rx, ry, x, y;
980d522f475Smrg    unsigned int mask;
981d522f475Smrg    int row = 0, col = 0;
982d522f475Smrg    Bool oor = 0;
983d522f475Smrg    Bool ret;
984d522f475Smrg
985d522f475Smrg    ret = XQueryPointer(screen->display, VWindow(screen),
986d522f475Smrg			&root, &child, &rx, &ry, &x, &y, &mask);
987d522f475Smrg    if (ret) {
988d522f475Smrg	LocatorCoords(row, col, x, y, oor);
989d522f475Smrg    }
990d522f475Smrg    if (ret == False || oor) {
991d522f475Smrg	/* Locator is unavailable */
992d522f475Smrg
993d522f475Smrg	if (screen->loc_filter_top != LOC_FILTER_POS ||
994d522f475Smrg	    screen->loc_filter_left != LOC_FILTER_POS ||
995d522f475Smrg	    screen->loc_filter_bottom != LOC_FILTER_POS ||
996d522f475Smrg	    screen->loc_filter_right != LOC_FILTER_POS) {
997d522f475Smrg	    /*
998d522f475Smrg	     * If any explicit coordinates were received,
999d522f475Smrg	     * report immediately with no coordinates.
1000d522f475Smrg	     */
1001d522f475Smrg	    memset(&reply, 0, sizeof(reply));
1002d522f475Smrg	    reply.a_type = ANSI_CSI;
1003d522f475Smrg	    reply.a_nparam = 1;
1004d522f475Smrg	    reply.a_param[0] = 0;	/* Event - 0 = locator unavailable */
1005d522f475Smrg	    reply.a_inters = '&';
1006d522f475Smrg	    reply.a_final = 'w';
1007d522f475Smrg	    unparseseq(xw, &reply);
1008d522f475Smrg
1009d522f475Smrg	    if (screen->locator_reset) {
1010d522f475Smrg		MotionOff(screen, xw);
1011d522f475Smrg		screen->send_mouse_pos = MOUSE_OFF;
1012d522f475Smrg	    }
1013d522f475Smrg	} else {
1014d522f475Smrg	    /*
1015d522f475Smrg	     * No explicit coordinates were received, and the pointer is
1016d522f475Smrg	     * unavailable.  Report when the pointer re-enters the window.
1017d522f475Smrg	     */
1018d522f475Smrg	    screen->loc_filter = True;
1019d522f475Smrg	    MotionOn(screen, xw);
1020d522f475Smrg	}
1021d522f475Smrg	return;
1022d522f475Smrg    }
1023d522f475Smrg
1024d522f475Smrg    /*
1025d522f475Smrg     * Adjust rectangle coordinates:
1026d522f475Smrg     *  1. Replace "LOC_FILTER_POS" with current coordinates
1027d522f475Smrg     *  2. Limit coordinates to screen size
1028d522f475Smrg     *  3. make sure top and left are less than bottom and right, resp.
1029d522f475Smrg     */
1030d522f475Smrg    if (screen->locator_pixels) {
1031d522f475Smrg	rx = OriginX(screen) * 2 + Width(screen);
1032d522f475Smrg	ry = screen->border * 2 + Height(screen);
1033d522f475Smrg    } else {
1034d522f475Smrg	rx = screen->max_col;
1035d522f475Smrg	ry = screen->max_row;
1036d522f475Smrg    }
1037d522f475Smrg
1038d522f475Smrg#define	Adjust( coord, def, max )				\
1039d522f475Smrg	if( (coord) == LOC_FILTER_POS )	(coord) = (def);	\
1040d522f475Smrg	else if ((coord) < 1)		(coord) = 1;		\
1041d522f475Smrg	else if ((coord) > (max))	(coord) = (max)
1042d522f475Smrg
1043d522f475Smrg    Adjust(screen->loc_filter_top, row, ry);
1044d522f475Smrg    Adjust(screen->loc_filter_left, col, rx);
1045d522f475Smrg    Adjust(screen->loc_filter_bottom, row, ry);
1046d522f475Smrg    Adjust(screen->loc_filter_right, col, rx);
1047d522f475Smrg
1048d522f475Smrg    if (screen->loc_filter_top > screen->loc_filter_bottom) {
1049d522f475Smrg	ry = screen->loc_filter_top;
1050d522f475Smrg	screen->loc_filter_top = screen->loc_filter_bottom;
1051d522f475Smrg	screen->loc_filter_bottom = ry;
1052d522f475Smrg    }
1053d522f475Smrg
1054d522f475Smrg    if (screen->loc_filter_left > screen->loc_filter_right) {
1055d522f475Smrg	rx = screen->loc_filter_left;
1056d522f475Smrg	screen->loc_filter_left = screen->loc_filter_right;
1057d522f475Smrg	screen->loc_filter_right = rx;
1058d522f475Smrg    }
1059d522f475Smrg
1060d522f475Smrg    if ((col < screen->loc_filter_left) ||
1061d522f475Smrg	(col > screen->loc_filter_right) ||
1062d522f475Smrg	(row < screen->loc_filter_top) ||
1063d522f475Smrg	(row > screen->loc_filter_bottom)) {
10642e4f8982Smrg	int state;
10652e4f8982Smrg
1066d522f475Smrg	/* Pointer is already outside the rectangle - report immediately */
1067d522f475Smrg	ButtonState(state, mask);
1068d522f475Smrg
1069d522f475Smrg	memset(&reply, 0, sizeof(reply));
1070d522f475Smrg	reply.a_type = ANSI_CSI;
1071d522f475Smrg	reply.a_nparam = 4;
1072d522f475Smrg	reply.a_param[0] = 10;	/* Event - 10 = locator outside filter */
10732eaa94a1Schristos	reply.a_param[1] = (ParmType) state;
10742eaa94a1Schristos	reply.a_param[2] = (ParmType) row;
10752eaa94a1Schristos	reply.a_param[3] = (ParmType) col;
1076d522f475Smrg	reply.a_inters = '&';
1077d522f475Smrg	reply.a_final = 'w';
1078d522f475Smrg	unparseseq(xw, &reply);
1079d522f475Smrg
1080d522f475Smrg	if (screen->locator_reset) {
1081d522f475Smrg	    MotionOff(screen, xw);
1082d522f475Smrg	    screen->send_mouse_pos = MOUSE_OFF;
1083d522f475Smrg	}
1084d522f475Smrg	return;
1085d522f475Smrg    }
1086d522f475Smrg
1087d522f475Smrg    /*
1088d522f475Smrg     * Rectangle is set up.  Allow pointer tracking
1089d522f475Smrg     * to detect if the mouse leaves the rectangle.
1090d522f475Smrg     */
1091d522f475Smrg    screen->loc_filter = True;
1092d522f475Smrg    MotionOn(screen, xw);
1093d522f475Smrg}
1094d522f475Smrg
1095d522f475Smrgstatic void
1096894e0ac8SmrgCheckLocatorPosition(XtermWidget xw, XButtonEvent *event)
1097d522f475Smrg{
1098d522f475Smrg    ANSI reply;
1099956cc18dSsnj    TScreen *screen = TScreenOf(xw);
1100d522f475Smrg    int row, col;
1101d522f475Smrg    Bool oor;
1102d522f475Smrg
1103492d43a5Smrg    LocatorCoords(row, col, event->x, event->y, oor);
1104d522f475Smrg
1105d522f475Smrg    /*
1106d522f475Smrg     * Send report if the pointer left the filter rectangle, if
1107d522f475Smrg     * the pointer left the window, or if the filter rectangle
1108d522f475Smrg     * had no coordinates and the pointer re-entered the window.
1109d522f475Smrg     */
1110d522f475Smrg    if (oor || (screen->loc_filter_top == LOC_FILTER_POS) ||
1111d522f475Smrg	(col < screen->loc_filter_left) ||
1112d522f475Smrg	(col > screen->loc_filter_right) ||
1113d522f475Smrg	(row < screen->loc_filter_top) ||
1114d522f475Smrg	(row > screen->loc_filter_bottom)) {
1115d522f475Smrg	/* Filter triggered - disable it */
1116d522f475Smrg	screen->loc_filter = False;
1117d522f475Smrg	MotionOff(screen, xw);
1118d522f475Smrg
1119d522f475Smrg	memset(&reply, 0, sizeof(reply));
1120d522f475Smrg	reply.a_type = ANSI_CSI;
1121d522f475Smrg	if (oor) {
1122d522f475Smrg	    reply.a_nparam = 1;
1123d522f475Smrg	    reply.a_param[0] = 0;	/* Event - 0 = locator unavailable */
1124d522f475Smrg	} else {
11252e4f8982Smrg	    int state;
11262e4f8982Smrg
1127492d43a5Smrg	    ButtonState(state, event->state);
1128d522f475Smrg
1129d522f475Smrg	    reply.a_nparam = 4;
1130d522f475Smrg	    reply.a_param[0] = 10;	/* Event - 10 = locator outside filter */
11312eaa94a1Schristos	    reply.a_param[1] = (ParmType) state;
11322eaa94a1Schristos	    reply.a_param[2] = (ParmType) row;
11332eaa94a1Schristos	    reply.a_param[3] = (ParmType) col;
1134d522f475Smrg	}
1135d522f475Smrg
1136d522f475Smrg	reply.a_inters = '&';
1137d522f475Smrg	reply.a_final = 'w';
1138d522f475Smrg	unparseseq(xw, &reply);
1139d522f475Smrg
1140d522f475Smrg	if (screen->locator_reset) {
1141d522f475Smrg	    MotionOff(screen, xw);
1142d522f475Smrg	    screen->send_mouse_pos = MOUSE_OFF;
1143d522f475Smrg	}
1144d522f475Smrg    }
1145d522f475Smrg}
1146d522f475Smrg#endif /* OPT_DEC_LOCATOR */
1147d522f475Smrg
1148d522f475Smrg#if OPT_READLINE
1149d522f475Smrgstatic int
1150913cc679SmrgisClick1_clean(XtermWidget xw, XButtonEvent *event)
1151d522f475Smrg{
1152913cc679Smrg    TScreen *screen = TScreenOf(xw);
1153d522f475Smrg    int delta;
1154d522f475Smrg
1155d522f475Smrg    /* Disable on Shift-Click-1, including the application-mouse modes */
1156f2e35a3aSmrg    if (OverrideButton(event)
1157f2e35a3aSmrg	|| (okSendMousePos(xw) != MOUSE_OFF)
1158f2e35a3aSmrg	|| ExtendingSelection)	/* Was moved */
1159d522f475Smrg	return 0;
1160d522f475Smrg
1161d522f475Smrg    if (event->type != ButtonRelease)
1162d522f475Smrg	return 0;
1163d522f475Smrg
1164d522f475Smrg    if (lastButtonDownTime == (Time) 0) {
1165d522f475Smrg	/* first time or once in a blue moon */
1166d522f475Smrg	delta = screen->multiClickTime + 1;
1167492d43a5Smrg    } else if (event->time > lastButtonDownTime) {
1168d522f475Smrg	/* most of the time */
1169492d43a5Smrg	delta = (int) (event->time - lastButtonDownTime);
1170d522f475Smrg    } else {
1171d522f475Smrg	/* time has rolled over since lastButtonUpTime */
1172492d43a5Smrg	delta = (int) ((((Time) ~ 0) - lastButtonDownTime) + event->time);
1173d522f475Smrg    }
1174d522f475Smrg
1175d522f475Smrg    return delta <= screen->multiClickTime;
1176d522f475Smrg}
1177d522f475Smrg
1178d522f475Smrgstatic int
1179f2e35a3aSmrgisDoubleClick3(XtermWidget xw, TScreen *screen, XButtonEvent *event)
1180d522f475Smrg{
1181d522f475Smrg    int delta;
1182d522f475Smrg
1183d522f475Smrg    if (event->type != ButtonRelease
1184f2e35a3aSmrg	|| OverrideButton(event)
1185492d43a5Smrg	|| event->button != Button3) {
1186d522f475Smrg	lastButton3UpTime = 0;	/* Disable the cached info */
1187d522f475Smrg	return 0;
1188d522f475Smrg    }
1189d522f475Smrg    /* Process Btn3Release. */
1190d522f475Smrg    if (lastButton3DoubleDownTime == (Time) 0) {
1191d522f475Smrg	/* No previous click or once in a blue moon */
1192d522f475Smrg	delta = screen->multiClickTime + 1;
1193492d43a5Smrg    } else if (event->time > lastButton3DoubleDownTime) {
1194d522f475Smrg	/* most of the time */
1195492d43a5Smrg	delta = (int) (event->time - lastButton3DoubleDownTime);
1196d522f475Smrg    } else {
1197d522f475Smrg	/* time has rolled over since lastButton3DoubleDownTime */
1198492d43a5Smrg	delta = (int) ((((Time) ~ 0) - lastButton3DoubleDownTime) + event->time);
1199d522f475Smrg    }
1200d522f475Smrg    if (delta <= screen->multiClickTime) {
1201d522f475Smrg	/* Double click */
1202d522f475Smrg	CELL cell;
1203d522f475Smrg
1204d522f475Smrg	/* Cannot check ExtendingSelection, since mouse-3 always sets it */
1205492d43a5Smrg	PointToCELL(screen, event->y, event->x, &cell);
1206d522f475Smrg	if (isSameCELL(&cell, &lastButton3)) {
1207d522f475Smrg	    lastButton3DoubleDownTime = 0;	/* Disable the third click */
1208d522f475Smrg	    return 1;
1209d522f475Smrg	}
1210d522f475Smrg    }
1211d522f475Smrg    /* Not a double click, memorize for future check. */
1212492d43a5Smrg    lastButton3UpTime = event->time;
1213492d43a5Smrg    PointToCELL(screen, event->y, event->x, &lastButton3);
1214d522f475Smrg    return 0;
1215d522f475Smrg}
1216d522f475Smrg
1217d522f475Smrgstatic int
1218f2e35a3aSmrgCheckSecondPress3(XtermWidget xw, TScreen *screen, XEvent *event)
1219d522f475Smrg{
1220d522f475Smrg    int delta;
1221d522f475Smrg
1222d522f475Smrg    if (event->type != ButtonPress
1223f2e35a3aSmrg	|| OverrideEvent(event)
1224d522f475Smrg	|| event->xbutton.button != Button3) {
1225d522f475Smrg	lastButton3DoubleDownTime = 0;	/* Disable the cached info */
1226d522f475Smrg	return 0;
1227d522f475Smrg    }
1228d522f475Smrg    /* Process Btn3Press. */
1229d522f475Smrg    if (lastButton3UpTime == (Time) 0) {
1230d522f475Smrg	/* No previous click or once in a blue moon */
1231d522f475Smrg	delta = screen->multiClickTime + 1;
1232d522f475Smrg    } else if (event->xbutton.time > lastButton3UpTime) {
1233d522f475Smrg	/* most of the time */
12342eaa94a1Schristos	delta = (int) (event->xbutton.time - lastButton3UpTime);
1235d522f475Smrg    } else {
1236d522f475Smrg	/* time has rolled over since lastButton3UpTime */
12372eaa94a1Schristos	delta = (int) ((((Time) ~ 0) - lastButton3UpTime) + event->xbutton.time);
1238d522f475Smrg    }
1239d522f475Smrg    if (delta <= screen->multiClickTime) {
1240d522f475Smrg	CELL cell;
1241d522f475Smrg
1242d522f475Smrg	PointToCELL(screen, event->xbutton.y, event->xbutton.x, &cell);
1243d522f475Smrg	if (isSameCELL(&cell, &lastButton3)) {
1244d522f475Smrg	    /* A candidate for a double-click */
1245d522f475Smrg	    lastButton3DoubleDownTime = event->xbutton.time;
1246d522f475Smrg	    PointToCELL(screen, event->xbutton.y, event->xbutton.x, &lastButton3);
1247d522f475Smrg	    return 1;
1248d522f475Smrg	}
1249d522f475Smrg	lastButton3UpTime = 0;	/* Disable the info about the previous click */
1250d522f475Smrg    }
1251d522f475Smrg    /* Either too long, or moved, disable. */
1252d522f475Smrg    lastButton3DoubleDownTime = 0;
1253d522f475Smrg    return 0;
1254d522f475Smrg}
1255d522f475Smrg
1256d522f475Smrgstatic int
1257e0a2b6dfSmrgrowOnCurrentLine(TScreen *screen,
1258d522f475Smrg		 int line,
1259d522f475Smrg		 int *deltap)	/* must be XButtonEvent */
1260d522f475Smrg{
1261956cc18dSsnj    int result = 1;
1262d522f475Smrg
1263d522f475Smrg    *deltap = 0;
12642e4f8982Smrg
1265956cc18dSsnj    if (line != screen->cur_row) {
12662e4f8982Smrg	int l1, l2;
12672e4f8982Smrg
1268f2e35a3aSmrg	if (line < screen->cur_row) {
1269f2e35a3aSmrg	    l1 = line;
1270f2e35a3aSmrg	    l2 = screen->cur_row;
1271f2e35a3aSmrg	} else {
1272f2e35a3aSmrg	    l2 = line;
1273f2e35a3aSmrg	    l1 = screen->cur_row;
1274f2e35a3aSmrg	}
1275956cc18dSsnj	l1--;
1276956cc18dSsnj	while (++l1 < l2) {
1277956cc18dSsnj	    LineData *ld = GET_LINEDATA(screen, l1);
1278956cc18dSsnj	    if (!LineTstWrapped(ld)) {
1279956cc18dSsnj		result = 0;
1280956cc18dSsnj		break;
1281956cc18dSsnj	    }
1282956cc18dSsnj	}
1283956cc18dSsnj	if (result) {
1284956cc18dSsnj	    /* Everything is on one "wrapped line" now */
1285956cc18dSsnj	    *deltap = line - screen->cur_row;
1286956cc18dSsnj	}
1287956cc18dSsnj    }
1288956cc18dSsnj    return result;
1289d522f475Smrg}
1290d522f475Smrg
1291d522f475Smrgstatic int
1292894e0ac8SmrgeventRow(TScreen *screen, XEvent *event)	/* must be XButtonEvent */
1293d522f475Smrg{
1294d522f475Smrg    return (event->xbutton.y - screen->border) / FontHeight(screen);
1295d522f475Smrg}
1296d522f475Smrg
1297d522f475Smrgstatic int
1298894e0ac8SmrgeventColBetween(TScreen *screen, XEvent *event)		/* must be XButtonEvent */
1299d522f475Smrg{
1300d522f475Smrg    /* Correct by half a width - we are acting on a boundary, not on a cell. */
1301d522f475Smrg    return ((event->xbutton.x - OriginX(screen) + (FontWidth(screen) - 1) / 2)
1302d522f475Smrg	    / FontWidth(screen));
1303d522f475Smrg}
1304d522f475Smrg
1305d522f475Smrgstatic int
13065307cd1aSmrgReadLineMovePoint(XtermWidget xw, int col, int ldelta)
1307d522f475Smrg{
13085307cd1aSmrg    TScreen *screen = TScreenOf(xw);
1309d522f475Smrg    Char line[6];
1310d522f475Smrg    unsigned count = 0;
1311d522f475Smrg
1312d522f475Smrg    col += ldelta * MaxCols(screen) - screen->cur_col;
1313d522f475Smrg    if (col == 0)
1314d522f475Smrg	return 0;
1315d522f475Smrg    if (screen->control_eight_bits) {
1316d522f475Smrg	line[count++] = ANSI_CSI;
1317d522f475Smrg    } else {
1318d522f475Smrg	line[count++] = ANSI_ESC;
13195307cd1aSmrg	line[count++] = (xw->keyboard.flags & MODE_DECCKM) ? 'O' : '[';
1320d522f475Smrg    }
132120d2c4d2Smrg    line[count] = CharOf(col > 0 ? 'C' : 'D');
1322d522f475Smrg    if (col < 0)
1323d522f475Smrg	col = -col;
1324d522f475Smrg    while (col--)
13255307cd1aSmrg	v_write(screen->respond, line, (size_t) 3);
1326d522f475Smrg    return 1;
1327d522f475Smrg}
1328d522f475Smrg
1329d522f475Smrgstatic int
1330e0a2b6dfSmrgReadLineDelete(TScreen *screen, CELL *cell1, CELL *cell2)
1331d522f475Smrg{
1332d522f475Smrg    int del;
13335307cd1aSmrg    Char erases[2];
13345307cd1aSmrg
13355307cd1aSmrg    erases[0] = (Char) get_tty_erase(screen->respond, XTERM_ERASE, "pty");
13365307cd1aSmrg    erases[1] = 0;
1337d522f475Smrg
1338d522f475Smrg    del = (cell2->col - cell1->col) + ((cell2->row - cell1->row) * MaxCols(screen));
1339d522f475Smrg    if (del <= 0)		/* Just in case... */
1340d522f475Smrg	return 0;
1341d522f475Smrg    while (del--)
13425307cd1aSmrg	v_write(screen->respond, erases, (size_t) 1);
1343d522f475Smrg    return 1;
1344d522f475Smrg}
1345492d43a5Smrg
1346492d43a5Smrgstatic void
1347913cc679SmrgreadlineExtend(XtermWidget xw, XEvent *event)
1348492d43a5Smrg{
1349913cc679Smrg    TScreen *screen = TScreenOf(xw);
1350492d43a5Smrg    int ldelta1, ldelta2;
1351492d43a5Smrg
1352492d43a5Smrg    if (IsBtnEvent(event)) {
1353492d43a5Smrg	XButtonEvent *my_event = (XButtonEvent *) event;
1354913cc679Smrg	if (isClick1_clean(xw, my_event)
1355492d43a5Smrg	    && SCREEN_FLAG(screen, click1_moves)
1356492d43a5Smrg	    && rowOnCurrentLine(screen, eventRow(screen, event), &ldelta1)) {
13575307cd1aSmrg	    ReadLineMovePoint(xw, eventColBetween(screen, event), ldelta1);
1358492d43a5Smrg	}
1359f2e35a3aSmrg	if (isDoubleClick3(xw, screen, my_event)
1360492d43a5Smrg	    && SCREEN_FLAG(screen, dclick3_deletes)
1361492d43a5Smrg	    && rowOnCurrentLine(screen, screen->startSel.row, &ldelta1)
1362492d43a5Smrg	    && rowOnCurrentLine(screen, screen->endSel.row, &ldelta2)) {
13635307cd1aSmrg	    ReadLineMovePoint(xw, screen->endSel.col, ldelta2);
1364492d43a5Smrg	    ReadLineDelete(screen, &screen->startSel, &(screen->endSel));
1365492d43a5Smrg	}
1366492d43a5Smrg    }
1367492d43a5Smrg}
1368d522f475Smrg#endif /* OPT_READLINE */
1369d522f475Smrg
1370d522f475Smrg/* ^XM-G<line+' '><col+' '> */
1371d522f475Smrgvoid
1372d522f475SmrgDiredButton(Widget w,
1373894e0ac8Smrg	    XEvent *event,	/* must be XButtonEvent */
1374e0a2b6dfSmrg	    String *params GCC_UNUSED,	/* selections */
1375d522f475Smrg	    Cardinal *num_params GCC_UNUSED)
1376d522f475Smrg{
1377956cc18dSsnj    XtermWidget xw;
1378956cc18dSsnj
1379956cc18dSsnj    if ((xw = getXtermWidget(w)) != 0) {
1380956cc18dSsnj	TScreen *screen = TScreenOf(xw);
1381d522f475Smrg
1382492d43a5Smrg	if (IsBtnEvent(event)
13832eaa94a1Schristos	    && (event->xbutton.y >= screen->border)
13842eaa94a1Schristos	    && (event->xbutton.x >= OriginX(screen))) {
13852e4f8982Smrg	    Char Line[6];
13862e4f8982Smrg	    unsigned line, col;
13872e4f8982Smrg
138820d2c4d2Smrg	    line = (unsigned) ((event->xbutton.y - screen->border)
138920d2c4d2Smrg			       / FontHeight(screen));
139020d2c4d2Smrg	    col = (unsigned) ((event->xbutton.x - OriginX(screen))
139120d2c4d2Smrg			      / FontWidth(screen));
1392d522f475Smrg	    Line[0] = CONTROL('X');
1393d522f475Smrg	    Line[1] = ANSI_ESC;
1394d522f475Smrg	    Line[2] = 'G';
13952eaa94a1Schristos	    Line[3] = CharOf(' ' + col);
13962eaa94a1Schristos	    Line[4] = CharOf(' ' + line);
13975307cd1aSmrg	    v_write(screen->respond, Line, (size_t) 5);
1398d522f475Smrg	}
1399d522f475Smrg    }
1400d522f475Smrg}
1401d522f475Smrg
1402d522f475Smrg#if OPT_READLINE
1403d522f475Smrgvoid
1404d522f475SmrgReadLineButton(Widget w,
1405894e0ac8Smrg	       XEvent *event,	/* must be XButtonEvent */
1406f2e35a3aSmrg	       String *params,	/* selections */
1407f2e35a3aSmrg	       Cardinal *num_params)
1408d522f475Smrg{
1409956cc18dSsnj    XtermWidget xw;
1410956cc18dSsnj
1411956cc18dSsnj    if ((xw = getXtermWidget(w)) != 0) {
1412956cc18dSsnj	TScreen *screen = TScreenOf(xw);
1413d522f475Smrg	Char Line[6];
1414d522f475Smrg	int line, col, ldelta = 0;
1415d522f475Smrg
1416492d43a5Smrg	if (!IsBtnEvent(event)
1417913cc679Smrg	    || (okSendMousePos(xw) != MOUSE_OFF) || ExtendingSelection)
1418d522f475Smrg	    goto finish;
1419d522f475Smrg	if (event->type == ButtonRelease) {
1420d522f475Smrg	    int delta;
1421d522f475Smrg
1422d522f475Smrg	    if (lastButtonDownTime == (Time) 0) {
1423d522f475Smrg		/* first time and once in a blue moon */
1424d522f475Smrg		delta = screen->multiClickTime + 1;
1425d522f475Smrg	    } else if (event->xbutton.time > lastButtonDownTime) {
1426d522f475Smrg		/* most of the time */
14272eaa94a1Schristos		delta = (int) (event->xbutton.time - lastButtonDownTime);
1428d522f475Smrg	    } else {
1429d522f475Smrg		/* time has rolled over since lastButtonUpTime */
14302eaa94a1Schristos		delta = (int) ((((Time) ~ 0) - lastButtonDownTime) + event->xbutton.time);
1431d522f475Smrg	    }
1432d522f475Smrg	    if (delta > screen->multiClickTime)
1433d522f475Smrg		goto finish;	/* All this work for this... */
1434d522f475Smrg	}
1435d522f475Smrg	line = (event->xbutton.y - screen->border) / FontHeight(screen);
1436956cc18dSsnj	if (!rowOnCurrentLine(screen, line, &ldelta))
1437956cc18dSsnj	    goto finish;
1438d522f475Smrg	/* Correct by half a width - we are acting on a boundary, not on a cell. */
1439d522f475Smrg	col = (event->xbutton.x - OriginX(screen) + (FontWidth(screen) - 1)
1440d522f475Smrg	       / 2)
1441d522f475Smrg	    / FontWidth(screen) - screen->cur_col + ldelta * MaxCols(screen);
1442d522f475Smrg	if (col == 0)
1443d522f475Smrg	    goto finish;
1444d522f475Smrg	Line[0] = ANSI_ESC;
14455307cd1aSmrg	Line[1] = (xw->keyboard.flags & MODE_DECCKM) ? 'O' : '[';
14462eaa94a1Schristos	Line[2] = CharOf(col > 0 ? 'C' : 'D');
1447d522f475Smrg	if (col < 0)
1448d522f475Smrg	    col = -col;
1449d522f475Smrg	while (col--)
14505307cd1aSmrg	    v_write(screen->respond, Line, (size_t) 3);
1451d522f475Smrg      finish:
1452d522f475Smrg	if (event->type == ButtonRelease)
1453d522f475Smrg	    do_select_end(xw, event, params, num_params, False);
1454d522f475Smrg    }
1455d522f475Smrg}
1456d522f475Smrg#endif /* OPT_READLINE */
1457d522f475Smrg
1458d522f475Smrg/* repeats <ESC>n or <ESC>p */
1459d522f475Smrgvoid
1460d522f475SmrgViButton(Widget w,
1461894e0ac8Smrg	 XEvent *event,		/* must be XButtonEvent */
1462e0a2b6dfSmrg	 String *params GCC_UNUSED,	/* selections */
1463d522f475Smrg	 Cardinal *num_params GCC_UNUSED)
1464d522f475Smrg{
1465956cc18dSsnj    XtermWidget xw;
1466956cc18dSsnj
1467956cc18dSsnj    if ((xw = getXtermWidget(w)) != 0) {
1468956cc18dSsnj	TScreen *screen = TScreenOf(xw);
1469d522f475Smrg	int pty = screen->respond;
1470d522f475Smrg
1471492d43a5Smrg	if (IsBtnEvent(event)) {
14722e4f8982Smrg	    int line;
1473d522f475Smrg
1474d522f475Smrg	    line = screen->cur_row -
1475d522f475Smrg		((event->xbutton.y - screen->border) / FontHeight(screen));
14762e4f8982Smrg
1477d522f475Smrg	    if (line != 0) {
14782e4f8982Smrg		Char Line[6];
14792e4f8982Smrg
1480d522f475Smrg		Line[0] = ANSI_ESC;	/* force an exit from insert-mode */
14815307cd1aSmrg		v_write(pty, Line, (size_t) 1);
1482d522f475Smrg
1483d522f475Smrg		if (line < 0) {
1484d522f475Smrg		    line = -line;
1485d522f475Smrg		    Line[0] = CONTROL('n');
1486d522f475Smrg		} else {
1487d522f475Smrg		    Line[0] = CONTROL('p');
1488d522f475Smrg		}
1489d522f475Smrg		while (--line >= 0)
14905307cd1aSmrg		    v_write(pty, Line, (size_t) 1);
1491d522f475Smrg	    }
1492d522f475Smrg	}
1493d522f475Smrg    }
1494d522f475Smrg}
1495d522f475Smrg
1496d522f475Smrg/*
1497d522f475Smrg * This function handles button-motion events
1498d522f475Smrg */
1499d522f475Smrg/*ARGSUSED*/
1500d522f475Smrgvoid
1501d522f475SmrgHandleSelectExtend(Widget w,
1502894e0ac8Smrg		   XEvent *event,	/* must be XMotionEvent */
1503e0a2b6dfSmrg		   String *params GCC_UNUSED,
1504d522f475Smrg		   Cardinal *num_params GCC_UNUSED)
1505d522f475Smrg{
1506956cc18dSsnj    XtermWidget xw;
1507956cc18dSsnj
1508956cc18dSsnj    if ((xw = getXtermWidget(w)) != 0) {
1509956cc18dSsnj	TScreen *screen = TScreenOf(xw);
1510d522f475Smrg	CELL cell;
1511d522f475Smrg
1512f2e35a3aSmrg	TRACE_EVENT("HandleSelectExtend", event, params, num_params);
15130bd37d32Smrg
1514d522f475Smrg	screen->selection_time = event->xmotion.time;
1515d522f475Smrg	switch (screen->eventMode) {
1516d522f475Smrg	    /* If not in one of the DEC mouse-reporting modes */
1517d522f475Smrg	case LEFTEXTENSION:
1518d522f475Smrg	case RIGHTEXTENSION:
1519d522f475Smrg	    PointToCELL(screen, event->xmotion.y, event->xmotion.x, &cell);
1520d522f475Smrg	    ExtendExtend(xw, &cell);
1521d522f475Smrg	    break;
1522d522f475Smrg
1523d522f475Smrg	    /* If in motion reporting mode, send mouse position to
1524d522f475Smrg	       character process as a key sequence \E[M... */
1525d522f475Smrg	case NORMAL:
1526d522f475Smrg	    /* will get here if send_mouse_pos != MOUSE_OFF */
1527913cc679Smrg	    if (okSendMousePos(xw) == BTN_EVENT_MOUSE
1528913cc679Smrg		|| okSendMousePos(xw) == ANY_EVENT_MOUSE) {
1529d522f475Smrg		(void) SendMousePosition(xw, event);
1530d522f475Smrg	    }
1531d522f475Smrg	    break;
1532d522f475Smrg	}
1533d522f475Smrg    }
1534d522f475Smrg}
1535d522f475Smrg
1536d522f475Smrgvoid
1537d522f475SmrgHandleKeyboardSelectExtend(Widget w,
1538894e0ac8Smrg			   XEvent *event GCC_UNUSED,	/* must be XButtonEvent */
1539e0a2b6dfSmrg			   String *params GCC_UNUSED,
1540d522f475Smrg			   Cardinal *num_params GCC_UNUSED)
1541d522f475Smrg{
1542956cc18dSsnj    XtermWidget xw;
1543956cc18dSsnj
1544956cc18dSsnj    if ((xw = getXtermWidget(w)) != 0) {
1545956cc18dSsnj	TScreen *screen = TScreenOf(xw);
15460bd37d32Smrg
1547f2e35a3aSmrg	TRACE_EVENT("HandleKeyboardSelectExtend", event, params, num_params);
1548d522f475Smrg	ExtendExtend(xw, &screen->cursorp);
1549d522f475Smrg    }
1550d522f475Smrg}
1551d522f475Smrg
1552d522f475Smrgstatic void
1553d522f475Smrgdo_select_end(XtermWidget xw,
1554894e0ac8Smrg	      XEvent *event,	/* must be XButtonEvent */
1555e0a2b6dfSmrg	      String *params,	/* selections */
1556d522f475Smrg	      Cardinal *num_params,
1557d522f475Smrg	      Bool use_cursor_loc)
1558d522f475Smrg{
1559956cc18dSsnj    TScreen *screen = TScreenOf(xw);
1560d522f475Smrg
1561d522f475Smrg    screen->selection_time = event->xbutton.time;
1562f2e35a3aSmrg
1563f2e35a3aSmrg    TRACE(("do_select_end %s @%ld\n",
1564f2e35a3aSmrg	   visibleEventMode(screen->eventMode),
1565f2e35a3aSmrg	   screen->selection_time));
1566f2e35a3aSmrg
1567d522f475Smrg    switch (screen->eventMode) {
1568d522f475Smrg    case NORMAL:
1569d522f475Smrg	(void) SendMousePosition(xw, event);
1570d522f475Smrg	break;
1571d522f475Smrg    case LEFTEXTENSION:
1572d522f475Smrg    case RIGHTEXTENSION:
1573d522f475Smrg	EndExtend(xw, event, params, *num_params, use_cursor_loc);
1574d522f475Smrg#if OPT_READLINE
1575913cc679Smrg	readlineExtend(xw, event);
1576d522f475Smrg#endif /* OPT_READLINE */
1577d522f475Smrg	break;
1578d522f475Smrg    }
1579d522f475Smrg}
1580d522f475Smrg
1581d522f475Smrgvoid
1582d522f475SmrgHandleSelectEnd(Widget w,
1583894e0ac8Smrg		XEvent *event,	/* must be XButtonEvent */
1584e0a2b6dfSmrg		String *params,	/* selections */
1585d522f475Smrg		Cardinal *num_params)
1586d522f475Smrg{
1587956cc18dSsnj    XtermWidget xw;
1588956cc18dSsnj
1589956cc18dSsnj    if ((xw = getXtermWidget(w)) != 0) {
15900bd37d32Smrg	TRACE(("HandleSelectEnd\n"));
1591956cc18dSsnj	do_select_end(xw, event, params, num_params, False);
1592956cc18dSsnj    }
1593d522f475Smrg}
1594d522f475Smrg
1595d522f475Smrgvoid
1596d522f475SmrgHandleKeyboardSelectEnd(Widget w,
1597894e0ac8Smrg			XEvent *event,	/* must be XButtonEvent */
1598e0a2b6dfSmrg			String *params,		/* selections */
1599d522f475Smrg			Cardinal *num_params)
1600d522f475Smrg{
1601956cc18dSsnj    XtermWidget xw;
1602956cc18dSsnj
1603956cc18dSsnj    if ((xw = getXtermWidget(w)) != 0) {
16040bd37d32Smrg	TRACE(("HandleKeyboardSelectEnd\n"));
1605956cc18dSsnj	do_select_end(xw, event, params, num_params, True);
1606956cc18dSsnj    }
1607d522f475Smrg}
1608d522f475Smrg
1609f2e35a3aSmrgvoid
1610f2e35a3aSmrgHandlePointerMotion(Widget w,
1611f2e35a3aSmrg		    XEvent *event,
1612f2e35a3aSmrg		    String *params,	/* selections */
1613f2e35a3aSmrg		    Cardinal *num_params)
1614f2e35a3aSmrg{
1615f2e35a3aSmrg    XtermWidget xw;
1616f2e35a3aSmrg
1617f2e35a3aSmrg    (void) params;
1618f2e35a3aSmrg    (void) num_params;
1619f2e35a3aSmrg    if ((xw = getXtermWidget(w)) != 0) {
1620f2e35a3aSmrg	TRACE(("HandlePointerMotion\n"));
1621f2e35a3aSmrg	if (event->type == MotionNotify)
1622f2e35a3aSmrg	    (void) SendMousePosition(xw, event);
1623f2e35a3aSmrg    }
1624f2e35a3aSmrg}
1625f2e35a3aSmrg
1626f2e35a3aSmrgvoid
1627f2e35a3aSmrgHandlePointerButton(Widget w,
1628f2e35a3aSmrg		    XEvent *event,
1629f2e35a3aSmrg		    String *params,	/* selections */
1630f2e35a3aSmrg		    Cardinal *num_params)
1631f2e35a3aSmrg{
1632f2e35a3aSmrg    XtermWidget xw;
1633f2e35a3aSmrg
1634f2e35a3aSmrg    (void) params;
1635f2e35a3aSmrg    (void) num_params;
1636f2e35a3aSmrg    if ((xw = getXtermWidget(w)) != 0) {
1637f2e35a3aSmrg	TRACE(("HandlePointerButton\n"));
1638f2e35a3aSmrg	if (IsBtnEvent(event))
1639f2e35a3aSmrg	    (void) SendMousePosition(xw, event);
1640f2e35a3aSmrg    }
1641f2e35a3aSmrg}
1642f2e35a3aSmrg
1643492d43a5Smrg/*
16446879286fSmrg * Copy the selection data to the given target(s).
1645492d43a5Smrg */
1646492d43a5Smrgvoid
16476879286fSmrgHandleCopySelection(Widget w,
1648894e0ac8Smrg		    XEvent *event,
1649e0a2b6dfSmrg		    String *params,	/* list of targets */
16506879286fSmrg		    Cardinal *num_params)
1651492d43a5Smrg{
1652492d43a5Smrg    XtermWidget xw;
1653492d43a5Smrg
1654492d43a5Smrg    if ((xw = getXtermWidget(w)) != 0) {
1655f2e35a3aSmrg	TRACE_EVENT("HandleCopySelection", event, params, num_params);
16566879286fSmrg	SelectSet(xw, event, params, *num_params);
1657492d43a5Smrg    }
1658492d43a5Smrg}
1659492d43a5Smrg
1660d522f475Smrgstruct _SelectionList {
1661d522f475Smrg    String *params;
1662d522f475Smrg    Cardinal count;
1663d522f475Smrg    Atom *targets;
1664d522f475Smrg    Time time;
1665d522f475Smrg};
1666d522f475Smrg
1667d522f475Smrgstatic unsigned
1668d522f475SmrgDECtoASCII(unsigned ch)
1669d522f475Smrg{
1670d522f475Smrg    if (xtermIsDecGraphic(ch)) {
16712eaa94a1Schristos	ch = CharOf("###########+++++##-##++++|######"[ch]);
16722eaa94a1Schristos	/*           01234567890123456789012345678901 */
167304b94745Smrg    } else {
167404b94745Smrg	ch = '?';		/* DEC Technical has no mapping */
1675d522f475Smrg    }
1676d522f475Smrg    return ch;
1677d522f475Smrg}
167820d2c4d2Smrg
167920d2c4d2Smrg#if OPT_WIDE_CHARS
168020d2c4d2Smrgstatic Cardinal
1681e0a2b6dfSmrgaddXtermChar(Char **buffer, Cardinal *used, Cardinal offset, unsigned value)
168220d2c4d2Smrg{
168320d2c4d2Smrg    if (offset + 1 >= *used) {
168420d2c4d2Smrg	*used = 1 + (2 * (offset + 1));
168520d2c4d2Smrg	allocXtermChars(buffer, *used);
168620d2c4d2Smrg    }
168720d2c4d2Smrg    (*buffer)[offset++] = (Char) value;
168820d2c4d2Smrg    return offset;
168920d2c4d2Smrg}
169020d2c4d2Smrg#define AddChar(buffer, used, offset, value) \
169120d2c4d2Smrg	offset = addXtermChar(buffer, used, offset, (unsigned) value)
169220d2c4d2Smrg
1693d522f475Smrg/*
1694d522f475Smrg * Convert a UTF-8 string to Latin-1, replacing non Latin-1 characters by `#',
1695d522f475Smrg * or ASCII/Latin-1 equivalents for special cases.
1696d522f475Smrg */
1697d522f475Smrgstatic Char *
1698e0a2b6dfSmrgUTF8toLatin1(TScreen *screen, Char *s, unsigned long len, unsigned long *result)
1699d522f475Smrg{
1700d522f475Smrg    static Char *buffer;
1701956cc18dSsnj    static Cardinal used;
1702d522f475Smrg
170320d2c4d2Smrg    Cardinal offset = 0;
1704d522f475Smrg
170520d2c4d2Smrg    if (len != 0) {
1706d522f475Smrg	PtyData data;
170704b94745Smrg	Boolean save_vt100 = screen->vt100_graphics;
1708d522f475Smrg
1709d522f475Smrg	fakePtyData(&data, s, s + len);
171004b94745Smrg	screen->vt100_graphics = False;		/* temporary override */
1711894e0ac8Smrg	while (decodeUtf8(screen, &data)) {
1712956cc18dSsnj	    Bool fails = False;
1713956cc18dSsnj	    Bool extra = False;
1714f2e35a3aSmrg	    IChar value;
1715f2e35a3aSmrg	    skipPtyData(&data, value);
171604b94745Smrg	    if (is_UCS_SPECIAL(value)) {
1717956cc18dSsnj		fails = True;
1718d522f475Smrg	    } else if (value < 256) {
171920d2c4d2Smrg		AddChar(&buffer, &used, offset, CharOf(value));
1720d522f475Smrg	    } else {
1721f2e35a3aSmrg		unsigned eqv = ucs2dec(screen, value);
172204b94745Smrg		if (xtermIsInternalCs(eqv)) {
172320d2c4d2Smrg		    AddChar(&buffer, &used, offset, DECtoASCII(eqv));
1724d522f475Smrg		} else {
1725d522f475Smrg		    eqv = AsciiEquivs(value);
1726956cc18dSsnj		    if (eqv == value) {
1727956cc18dSsnj			fails = True;
1728956cc18dSsnj		    } else {
172920d2c4d2Smrg			AddChar(&buffer, &used, offset, eqv);
1730956cc18dSsnj		    }
1731956cc18dSsnj		    if (isWide((wchar_t) value))
1732956cc18dSsnj			extra = True;
1733956cc18dSsnj		}
1734956cc18dSsnj	    }
1735956cc18dSsnj
1736956cc18dSsnj	    /*
1737956cc18dSsnj	     * If we're not able to plug in a single-byte result, insert the
1738956cc18dSsnj	     * defaultString (which normally is a single "#", but could be
1739956cc18dSsnj	     * whatever the user wants).
1740956cc18dSsnj	     */
1741956cc18dSsnj	    if (fails) {
17422e4f8982Smrg		const Char *p;
17432e4f8982Smrg
1744492d43a5Smrg		for (p = (const Char *) screen->default_string; *p != '\0'; ++p) {
174520d2c4d2Smrg		    AddChar(&buffer, &used, offset, *p);
1746d522f475Smrg		}
1747d522f475Smrg	    }
1748956cc18dSsnj	    if (extra)
174920d2c4d2Smrg		AddChar(&buffer, &used, offset, ' ');
1750d522f475Smrg	}
175120d2c4d2Smrg	AddChar(&buffer, &used, offset, '\0');
175204b94745Smrg	screen->vt100_graphics = save_vt100;
175320d2c4d2Smrg	*result = (unsigned long) (offset - 1);
1754d522f475Smrg    } else {
1755d522f475Smrg	*result = 0;
1756d522f475Smrg    }
1757d522f475Smrg    return buffer;
1758d522f475Smrg}
175920d2c4d2Smrg
176020d2c4d2Smrgint
176120d2c4d2SmrgxtermUtf8ToTextList(XtermWidget xw,
176220d2c4d2Smrg		    XTextProperty * text_prop,
176320d2c4d2Smrg		    char ***text_list,
176420d2c4d2Smrg		    int *text_list_count)
176520d2c4d2Smrg{
176620d2c4d2Smrg    TScreen *screen = TScreenOf(xw);
176720d2c4d2Smrg    Display *dpy = screen->display;
176820d2c4d2Smrg    int rc = -1;
176920d2c4d2Smrg
177020d2c4d2Smrg    if (text_prop->format == 8
177120d2c4d2Smrg	&& (rc = Xutf8TextPropertyToTextList(dpy, text_prop,
177220d2c4d2Smrg					     text_list,
177320d2c4d2Smrg					     text_list_count)) >= 0) {
177420d2c4d2Smrg	if (*text_list != NULL && *text_list_count != 0) {
177520d2c4d2Smrg	    int i;
177620d2c4d2Smrg	    Char *data;
177720d2c4d2Smrg	    char **new_text_list, *tmp;
177820d2c4d2Smrg	    unsigned long size, new_size;
177920d2c4d2Smrg
178020d2c4d2Smrg	    TRACE(("xtermUtf8ToTextList size %d\n", *text_list_count));
178120d2c4d2Smrg
178220d2c4d2Smrg	    /*
178320d2c4d2Smrg	     * XLib StringList actually uses only two pointers, one for the
178420d2c4d2Smrg	     * list itself, and one for the data.  Pointer to the data is the
178520d2c4d2Smrg	     * first element of the list, the rest (if any) list elements point
178620d2c4d2Smrg	     * to the same memory block as the first element
178720d2c4d2Smrg	     */
178820d2c4d2Smrg	    new_size = 0;
178920d2c4d2Smrg	    for (i = 0; i < *text_list_count; ++i) {
179020d2c4d2Smrg		data = (Char *) (*text_list)[i];
179120d2c4d2Smrg		size = strlen((*text_list)[i]) + 1;
179220d2c4d2Smrg		(void) UTF8toLatin1(screen, data, size, &size);
179320d2c4d2Smrg		new_size += size + 1;
179420d2c4d2Smrg	    }
1795a1f3da82Smrg	    new_text_list = TypeXtMallocN(char *, *text_list_count);
179620d2c4d2Smrg	    new_text_list[0] = tmp = XtMalloc((Cardinal) new_size);
179720d2c4d2Smrg	    for (i = 0; i < (*text_list_count); ++i) {
179820d2c4d2Smrg		data = (Char *) (*text_list)[i];
179920d2c4d2Smrg		size = strlen((*text_list)[i]) + 1;
18000bd37d32Smrg		if ((data = UTF8toLatin1(screen, data, size, &size)) != 0) {
18010bd37d32Smrg		    memcpy(tmp, data, size + 1);
18020bd37d32Smrg		    new_text_list[i] = tmp;
18030bd37d32Smrg		    tmp += size + 1;
18040bd37d32Smrg		}
180520d2c4d2Smrg	    }
180620d2c4d2Smrg	    XFreeStringList((*text_list));
180720d2c4d2Smrg	    *text_list = new_text_list;
180820d2c4d2Smrg	} else {
180920d2c4d2Smrg	    rc = -1;
181020d2c4d2Smrg	}
181120d2c4d2Smrg    }
181220d2c4d2Smrg    return rc;
181320d2c4d2Smrg}
1814d522f475Smrg#endif /* OPT_WIDE_CHARS */
1815d522f475Smrg
1816956cc18dSsnjstatic char *
1817956cc18dSsnjparseItem(char *value, char *nextc)
1818d522f475Smrg{
1819956cc18dSsnj    char *nextp = value;
1820956cc18dSsnj    while (*nextp != '\0' && *nextp != ',') {
1821956cc18dSsnj	*nextp = x_toupper(*nextp);
1822956cc18dSsnj	++nextp;
1823956cc18dSsnj    }
1824956cc18dSsnj    *nextc = *nextp;
1825956cc18dSsnj    *nextp = '\0';
1826d522f475Smrg
1827956cc18dSsnj    return nextp;
1828956cc18dSsnj}
1829d522f475Smrg
1830956cc18dSsnj/*
1831956cc18dSsnj * All of the wanted strings are unique in the first character, so we can
1832956cc18dSsnj * use simple abbreviations.
1833956cc18dSsnj */
1834956cc18dSsnjstatic Bool
1835956cc18dSsnjsameItem(const char *actual, const char *wanted)
1836956cc18dSsnj{
1837956cc18dSsnj    Bool result = False;
1838956cc18dSsnj    size_t have = strlen(actual);
1839956cc18dSsnj    size_t need = strlen(wanted);
1840d522f475Smrg
1841956cc18dSsnj    if (have != 0 && have <= need) {
1842956cc18dSsnj	if (!strncmp(actual, wanted, have)) {
1843956cc18dSsnj	    TRACE(("...matched \"%s\"\n", wanted));
1844956cc18dSsnj	    result = True;
1845956cc18dSsnj	}
1846956cc18dSsnj    }
1847956cc18dSsnj
1848956cc18dSsnj    return result;
1849956cc18dSsnj}
1850956cc18dSsnj
1851956cc18dSsnj/*
1852956cc18dSsnj * Handle the eightBitSelectTypes or utf8SelectTypes resource values.
1853956cc18dSsnj */
1854956cc18dSsnjstatic Bool
1855894e0ac8SmrgoverrideTargets(Widget w, String value, Atom **resultp)
1856956cc18dSsnj{
1857956cc18dSsnj    Bool override = False;
1858956cc18dSsnj    XtermWidget xw;
1859956cc18dSsnj
1860956cc18dSsnj    if ((xw = getXtermWidget(w)) != 0) {
1861956cc18dSsnj	TScreen *screen = TScreenOf(xw);
1862956cc18dSsnj
186320d2c4d2Smrg	if (!IsEmpty(value)) {
1864492d43a5Smrg	    char *copied = x_strdup(value);
1865956cc18dSsnj	    if (copied != 0) {
1866956cc18dSsnj		Atom *result = 0;
1867956cc18dSsnj		Cardinal count = 1;
1868956cc18dSsnj		int n;
1869d522f475Smrg
1870956cc18dSsnj		TRACE(("decoding SelectTypes \"%s\"\n", value));
1871956cc18dSsnj		for (n = 0; copied[n] != '\0'; ++n) {
1872956cc18dSsnj		    if (copied[n] == ',')
1873956cc18dSsnj			++count;
1874956cc18dSsnj		}
1875a1f3da82Smrg		result = TypeXtMallocN(Atom, (2 * count) + 1);
1876956cc18dSsnj		if (result == NULL) {
1877956cc18dSsnj		    TRACE(("Couldn't allocate selection types\n"));
1878956cc18dSsnj		} else {
1879956cc18dSsnj		    char nextc = '?';
188020d2c4d2Smrg		    char *listp = (char *) copied;
1881956cc18dSsnj		    count = 0;
1882956cc18dSsnj		    do {
1883956cc18dSsnj			char *nextp = parseItem(listp, &nextc);
18840bd37d32Smrg			char *item = x_strtrim(listp);
18850bd37d32Smrg			size_t len = (item ? strlen(item) : 0);
1886956cc18dSsnj
1887956cc18dSsnj			if (len == 0) {
1888a1f3da82Smrg			    /* EMPTY */ ;
1889956cc18dSsnj			}
1890956cc18dSsnj#if OPT_WIDE_CHARS
18910bd37d32Smrg			else if (sameItem(item, "UTF8")) {
1892956cc18dSsnj			    result[count++] = XA_UTF8_STRING(XtDisplay(w));
1893956cc18dSsnj			}
1894956cc18dSsnj#endif
18950bd37d32Smrg			else if (sameItem(item, "I18N")) {
1896956cc18dSsnj			    if (screen->i18nSelections) {
1897956cc18dSsnj				result[count++] = XA_TEXT(XtDisplay(w));
1898956cc18dSsnj				result[count++] = XA_COMPOUND_TEXT(XtDisplay(w));
1899956cc18dSsnj			    }
19000bd37d32Smrg			} else if (sameItem(item, "TEXT")) {
1901956cc18dSsnj			    result[count++] = XA_TEXT(XtDisplay(w));
19020bd37d32Smrg			} else if (sameItem(item, "COMPOUND_TEXT")) {
1903956cc18dSsnj			    result[count++] = XA_COMPOUND_TEXT(XtDisplay(w));
19040bd37d32Smrg			} else if (sameItem(item, "STRING")) {
1905956cc18dSsnj			    result[count++] = XA_STRING;
1906956cc18dSsnj			}
1907956cc18dSsnj			*nextp++ = nextc;
1908956cc18dSsnj			listp = nextp;
19090bd37d32Smrg			free(item);
1910956cc18dSsnj		    } while (nextc != '\0');
1911956cc18dSsnj		    if (count) {
1912956cc18dSsnj			result[count] = None;
1913956cc18dSsnj			override = True;
1914956cc18dSsnj			*resultp = result;
1915956cc18dSsnj		    } else {
1916956cc18dSsnj			XtFree((char *) result);
1917956cc18dSsnj		    }
1918956cc18dSsnj		}
19190bd37d32Smrg		free(copied);
1920956cc18dSsnj	    } else {
1921956cc18dSsnj		TRACE(("Couldn't allocate copy of selection types\n"));
1922d522f475Smrg	    }
1923956cc18dSsnj	}
1924956cc18dSsnj    }
1925956cc18dSsnj    return override;
1926956cc18dSsnj}
1927956cc18dSsnj
1928956cc18dSsnj#if OPT_WIDE_CHARS
1929956cc18dSsnjstatic Atom *
1930e0a2b6dfSmrgallocUtf8Targets(Widget w, TScreen *screen)
1931956cc18dSsnj{
1932956cc18dSsnj    Atom **resultp = &(screen->selection_targets_utf8);
1933956cc18dSsnj
1934956cc18dSsnj    if (*resultp == 0) {
1935956cc18dSsnj	Atom *result;
1936956cc18dSsnj
1937956cc18dSsnj	if (!overrideTargets(w, screen->utf8_select_types, &result)) {
1938a1f3da82Smrg	    result = TypeXtMallocN(Atom, 5);
1939956cc18dSsnj	    if (result == NULL) {
1940956cc18dSsnj		TRACE(("Couldn't allocate utf-8 selection targets\n"));
1941956cc18dSsnj	    } else {
1942956cc18dSsnj		int n = 0;
1943956cc18dSsnj
1944e39b573cSmrg		if (XSupportsLocale()) {
1945e39b573cSmrg		    result[n++] = XA_UTF8_STRING(XtDisplay(w));
1946d522f475Smrg#ifdef X_HAVE_UTF8_STRING
1947e39b573cSmrg		    if (screen->i18nSelections) {
1948e39b573cSmrg			result[n++] = XA_TEXT(XtDisplay(w));
1949e39b573cSmrg			result[n++] = XA_COMPOUND_TEXT(XtDisplay(w));
1950e39b573cSmrg		    }
1951d522f475Smrg#endif
1952e39b573cSmrg		}
1953956cc18dSsnj		result[n++] = XA_STRING;
1954956cc18dSsnj		result[n] = None;
1955956cc18dSsnj	    }
1956d522f475Smrg	}
1957956cc18dSsnj
1958956cc18dSsnj	*resultp = result;
1959d522f475Smrg    }
1960956cc18dSsnj
1961956cc18dSsnj    return *resultp;
1962956cc18dSsnj}
1963d522f475Smrg#endif
1964d522f475Smrg
1965956cc18dSsnjstatic Atom *
1966e0a2b6dfSmrgalloc8bitTargets(Widget w, TScreen *screen)
1967956cc18dSsnj{
1968956cc18dSsnj    Atom **resultp = &(screen->selection_targets_8bit);
1969956cc18dSsnj
1970956cc18dSsnj    if (*resultp == 0) {
1971956cc18dSsnj	Atom *result = 0;
1972956cc18dSsnj
1973956cc18dSsnj	if (!overrideTargets(w, screen->eightbit_select_types, &result)) {
1974a1f3da82Smrg	    result = TypeXtMallocN(Atom, 5);
1975956cc18dSsnj	    if (result == NULL) {
1976956cc18dSsnj		TRACE(("Couldn't allocate 8bit selection targets\n"));
1977956cc18dSsnj	    } else {
1978956cc18dSsnj		int n = 0;
1979956cc18dSsnj
1980e39b573cSmrg		if (XSupportsLocale()) {
1981d522f475Smrg#ifdef X_HAVE_UTF8_STRING
1982e39b573cSmrg		    result[n++] = XA_UTF8_STRING(XtDisplay(w));
1983956cc18dSsnj#endif
1984e39b573cSmrg		    if (screen->i18nSelections) {
1985e39b573cSmrg			result[n++] = XA_TEXT(XtDisplay(w));
1986e39b573cSmrg			result[n++] = XA_COMPOUND_TEXT(XtDisplay(w));
1987e39b573cSmrg		    }
1988956cc18dSsnj		}
1989956cc18dSsnj		result[n++] = XA_STRING;
1990956cc18dSsnj		result[n] = None;
1991956cc18dSsnj	    }
1992956cc18dSsnj	}
1993956cc18dSsnj
1994956cc18dSsnj	*resultp = result;
1995956cc18dSsnj    }
1996956cc18dSsnj
1997956cc18dSsnj    return *resultp;
1998956cc18dSsnj}
1999956cc18dSsnj
2000956cc18dSsnjstatic Atom *
2001956cc18dSsnj_SelectionTargets(Widget w)
2002956cc18dSsnj{
2003956cc18dSsnj    Atom *result;
2004956cc18dSsnj    XtermWidget xw;
2005956cc18dSsnj
2006956cc18dSsnj    if ((xw = getXtermWidget(w)) == 0) {
2007956cc18dSsnj	result = NULL;
2008956cc18dSsnj    } else {
20092e4f8982Smrg	TScreen *screen = TScreenOf(xw);
2010956cc18dSsnj
2011956cc18dSsnj#if OPT_WIDE_CHARS
2012956cc18dSsnj	if (screen->wide_chars) {
2013956cc18dSsnj	    result = allocUtf8Targets(w, screen);
2014956cc18dSsnj	} else
2015d522f475Smrg#endif
2016956cc18dSsnj	{
2017956cc18dSsnj	    /* not screen->wide_chars */
2018956cc18dSsnj	    result = alloc8bitTargets(w, screen);
2019d522f475Smrg	}
2020d522f475Smrg    }
2021956cc18dSsnj
2022956cc18dSsnj    return result;
2023d522f475Smrg}
2024d522f475Smrg
20255307cd1aSmrg#define isSELECT(value) (!strcmp(NonNull(value), "SELECT"))
2026d522f475Smrg
2027f2e35a3aSmrgstatic int
2028f2e35a3aSmrgDefaultSelection(TScreen *screen)
2029f2e35a3aSmrg{
2030f2e35a3aSmrg    return (screen->selectToClipboard ? 1 : 0);
2031f2e35a3aSmrg}
2032f2e35a3aSmrg
2033f2e35a3aSmrgstatic int
2034f2e35a3aSmrgTargetToSelection(TScreen *screen, String name)
2035f2e35a3aSmrg{
2036f2e35a3aSmrg    int result = -1;
2037f2e35a3aSmrg    int cutb;
2038f2e35a3aSmrg
2039f2e35a3aSmrg    if (isSELECT(name)) {
2040f2e35a3aSmrg	result = DefaultSelection(screen);
2041f2e35a3aSmrg    } else if (!strcmp(name, PRIMARY_NAME)) {
2042f2e35a3aSmrg	result = PRIMARY_CODE;
2043f2e35a3aSmrg    } else if (!strcmp(name, CLIPBOARD_NAME)) {
2044f2e35a3aSmrg	result = CLIPBOARD_CODE;
2045f2e35a3aSmrg    } else if (!strcmp(name, SECONDARY_NAME)) {
2046f2e35a3aSmrg	result = SECONDARY_CODE;
2047f2e35a3aSmrg    } else if (sscanf(name, "CUT_BUFFER%d", &cutb) == 1) {
2048f2e35a3aSmrg	if (cutb >= 0 && cutb < MAX_CUT_BUFFER) {
2049f2e35a3aSmrg	    result = CutBufferToCode(cutb);
2050f2e35a3aSmrg	} else {
2051f2e35a3aSmrg	    xtermWarning("unexpected cut-buffer code: %d\n", cutb);
2052f2e35a3aSmrg	}
2053f2e35a3aSmrg    } else {
2054f2e35a3aSmrg	xtermWarning("unexpected selection target: %s\n", name);
2055f2e35a3aSmrg    }
2056f2e35a3aSmrg    TRACE2(("TargetToSelection(%s) ->%d\n", name, result));
2057f2e35a3aSmrg    return result;
2058f2e35a3aSmrg}
2059f2e35a3aSmrg
2060f2e35a3aSmrgvoid
2061d522f475SmrgUnmapSelections(XtermWidget xw)
2062d522f475Smrg{
2063956cc18dSsnj    TScreen *screen = TScreenOf(xw);
2064d522f475Smrg
20655307cd1aSmrg    FreeAndNull(screen->mappedSelect);
2066d522f475Smrg}
2067d522f475Smrg
2068d522f475Smrg/*
2069d522f475Smrg * xterm generally uses the primary selection.  Some applications prefer
2070d522f475Smrg * (or are limited to) the clipboard.  Since the translations resource is
2071d522f475Smrg * complicated, users seldom change the way it affects selection.  But it
2072d522f475Smrg * is simple to remap the choice between primary and clipboard before the
2073d522f475Smrg * call to XmuInternStrings().
2074d522f475Smrg */
2075d522f475Smrgstatic String *
2076e0a2b6dfSmrgMapSelections(XtermWidget xw, String *params, Cardinal num_params)
2077d522f475Smrg{
2078d522f475Smrg    String *result = params;
2079d522f475Smrg
2080913cc679Smrg    if (params != 0 && num_params > 0) {
2081d522f475Smrg	Cardinal j;
2082d522f475Smrg	Boolean map = False;
2083d522f475Smrg
2084d522f475Smrg	for (j = 0; j < num_params; ++j) {
2085d522f475Smrg	    TRACE(("param[%d]:%s\n", j, params[j]));
2086d522f475Smrg	    if (isSELECT(params[j])) {
2087d522f475Smrg		map = True;
2088d522f475Smrg		break;
2089d522f475Smrg	    }
2090d522f475Smrg	}
2091d522f475Smrg	if (map) {
2092956cc18dSsnj	    TScreen *screen = TScreenOf(xw);
2093956cc18dSsnj	    const char *mapTo = (screen->selectToClipboard
2094f2e35a3aSmrg				 ? CLIPBOARD_NAME
2095f2e35a3aSmrg				 : PRIMARY_NAME);
2096d522f475Smrg
2097d522f475Smrg	    UnmapSelections(xw);
2098d522f475Smrg	    if ((result = TypeMallocN(String, num_params + 1)) != 0) {
2099d522f475Smrg		result[num_params] = 0;
2100d522f475Smrg		for (j = 0; j < num_params; ++j) {
21015307cd1aSmrg		    result[j] = (String) (isSELECT(params[j])
2102d522f475Smrg					  ? mapTo
21035307cd1aSmrg					  : params[j]);
2104d522f475Smrg		    if (result[j] == 0) {
2105d522f475Smrg			UnmapSelections(xw);
2106f2e35a3aSmrg			FreeAndNull(result);
2107d522f475Smrg			break;
2108d522f475Smrg		    }
2109d522f475Smrg		}
2110956cc18dSsnj		screen->mappedSelect = result;
2111d522f475Smrg	    }
2112d522f475Smrg	}
2113d522f475Smrg    }
2114d522f475Smrg    return result;
2115d522f475Smrg}
2116d522f475Smrg
2117d522f475Smrg/*
2118d522f475Smrg * Lookup the cut-buffer number, which will be in the range 0-7.
2119f2e35a3aSmrg * If it is not a cut-buffer, it is a type of selection, e.g., primary.
2120d522f475Smrg */
2121d522f475Smrgstatic int
212220d2c4d2SmrgCutBuffer(Atom code)
2123d522f475Smrg{
2124d522f475Smrg    int cutbuffer;
212520d2c4d2Smrg    switch ((unsigned) code) {
2126d522f475Smrg    case XA_CUT_BUFFER0:
2127d522f475Smrg	cutbuffer = 0;
2128d522f475Smrg	break;
2129d522f475Smrg    case XA_CUT_BUFFER1:
2130d522f475Smrg	cutbuffer = 1;
2131d522f475Smrg	break;
2132d522f475Smrg    case XA_CUT_BUFFER2:
2133d522f475Smrg	cutbuffer = 2;
2134d522f475Smrg	break;
2135d522f475Smrg    case XA_CUT_BUFFER3:
2136d522f475Smrg	cutbuffer = 3;
2137d522f475Smrg	break;
2138d522f475Smrg    case XA_CUT_BUFFER4:
2139d522f475Smrg	cutbuffer = 4;
2140d522f475Smrg	break;
2141d522f475Smrg    case XA_CUT_BUFFER5:
2142d522f475Smrg	cutbuffer = 5;
2143d522f475Smrg	break;
2144d522f475Smrg    case XA_CUT_BUFFER6:
2145d522f475Smrg	cutbuffer = 6;
2146d522f475Smrg	break;
2147d522f475Smrg    case XA_CUT_BUFFER7:
2148d522f475Smrg	cutbuffer = 7;
2149d522f475Smrg	break;
2150d522f475Smrg    default:
2151d522f475Smrg	cutbuffer = -1;
2152d522f475Smrg	break;
2153d522f475Smrg    }
2154f2e35a3aSmrg    TRACE2(("CutBuffer(%d) = %d\n", (int) code, cutbuffer));
2155d522f475Smrg    return cutbuffer;
2156d522f475Smrg}
2157d522f475Smrg
2158d522f475Smrg#if OPT_PASTE64
2159d522f475Smrgstatic void
2160d522f475SmrgFinishPaste64(XtermWidget xw)
2161d522f475Smrg{
2162956cc18dSsnj    TScreen *screen = TScreenOf(xw);
2163956cc18dSsnj
2164956cc18dSsnj    TRACE(("FinishPaste64(%d)\n", screen->base64_paste));
2165956cc18dSsnj    if (screen->base64_paste) {
2166956cc18dSsnj	screen->base64_paste = 0;
2167956cc18dSsnj	unparseputc1(xw, screen->base64_final);
2168d522f475Smrg	unparse_end(xw);
2169d522f475Smrg    }
2170d522f475Smrg}
2171d522f475Smrg#endif
2172d522f475Smrg
2173d522f475Smrg#if !OPT_PASTE64
2174d522f475Smrgstatic
2175d522f475Smrg#endif
2176d522f475Smrgvoid
2177d522f475SmrgxtermGetSelection(Widget w,
2178d522f475Smrg		  Time ev_time,
2179e0a2b6dfSmrg		  String *params,	/* selections in precedence order */
2180d522f475Smrg		  Cardinal num_params,
2181894e0ac8Smrg		  Atom *targets)
2182d522f475Smrg{
2183d522f475Smrg    Atom selection;
2184d522f475Smrg    int cutbuffer;
2185d522f475Smrg    Atom target;
2186d522f475Smrg
2187956cc18dSsnj    XtermWidget xw;
2188956cc18dSsnj
218920d2c4d2Smrg    if (num_params == 0)
219020d2c4d2Smrg	return;
2191956cc18dSsnj    if ((xw = getXtermWidget(w)) == 0)
2192d522f475Smrg	return;
2193d522f475Smrg
2194e0a2b6dfSmrg    TRACE(("xtermGetSelection num_params %d @%ld\n", num_params, ev_time));
2195956cc18dSsnj    params = MapSelections(xw, params, num_params);
2196d522f475Smrg
2197d522f475Smrg    XmuInternStrings(XtDisplay(w), params, (Cardinal) 1, &selection);
2198d522f475Smrg    cutbuffer = CutBuffer(selection);
2199d522f475Smrg
2200956cc18dSsnj    TRACE(("Cutbuffer: %d, target: %s\n", cutbuffer,
2201956cc18dSsnj	   (targets
2202956cc18dSsnj	    ? visibleSelectionTarget(XtDisplay(w), targets[0])
2203956cc18dSsnj	    : "None")));
2204d522f475Smrg
2205d522f475Smrg    if (cutbuffer >= 0) {
2206d522f475Smrg	int inbytes;
2207d522f475Smrg	unsigned long nbytes;
2208d522f475Smrg	int fmt8 = 8;
2209d522f475Smrg	Atom type = XA_STRING;
2210d522f475Smrg	char *line;
2211d522f475Smrg
2212d522f475Smrg	/* 'line' is freed in SelectionReceived */
2213d522f475Smrg	line = XFetchBuffer(XtDisplay(w), &inbytes, cutbuffer);
2214d522f475Smrg	nbytes = (unsigned long) inbytes;
2215d522f475Smrg
22160bd37d32Smrg	if (nbytes > 0) {
2217d522f475Smrg	    SelectionReceived(w, NULL, &selection, &type, (XtPointer) line,
2218d522f475Smrg			      &nbytes, &fmt8);
22190bd37d32Smrg	} else if (num_params > 1) {
2220d522f475Smrg	    xtermGetSelection(w, ev_time, params + 1, num_params - 1, NULL);
2221d522f475Smrg	}
2222d522f475Smrg#if OPT_PASTE64
2223d522f475Smrg	else {
2224956cc18dSsnj	    FinishPaste64(xw);
2225d522f475Smrg	}
2226d522f475Smrg#endif
2227d522f475Smrg    } else {
2228d522f475Smrg
2229d522f475Smrg	if (targets == NULL || targets[0] == None) {
2230d522f475Smrg	    targets = _SelectionTargets(w);
2231d522f475Smrg	}
2232d522f475Smrg
2233d522f475Smrg	if (targets != 0) {
22342e4f8982Smrg	    struct _SelectionList *list;
22352e4f8982Smrg
2236d522f475Smrg	    target = targets[0];
2237d522f475Smrg
2238d522f475Smrg	    if (targets[1] == None) {	/* last target in list */
2239d522f475Smrg		params++;
2240d522f475Smrg		num_params--;
2241d522f475Smrg		targets = _SelectionTargets(w);
2242d522f475Smrg	    } else {
2243d522f475Smrg		targets = &(targets[1]);
2244d522f475Smrg	    }
2245d522f475Smrg
2246d522f475Smrg	    if (num_params) {
2247d522f475Smrg		/* 'list' is freed in SelectionReceived */
2248a1f3da82Smrg		list = TypeXtMalloc(struct _SelectionList);
2249d522f475Smrg		if (list != 0) {
2250d522f475Smrg		    list->params = params;
2251d522f475Smrg		    list->count = num_params;
2252d522f475Smrg		    list->targets = targets;
2253d522f475Smrg		    list->time = ev_time;
2254d522f475Smrg		}
2255d522f475Smrg	    } else {
2256d522f475Smrg		list = NULL;
2257d522f475Smrg	    }
2258d522f475Smrg
2259d522f475Smrg	    XtGetSelectionValue(w, selection,
2260d522f475Smrg				target,
2261d522f475Smrg				SelectionReceived,
2262d522f475Smrg				(XtPointer) list, ev_time);
2263d522f475Smrg	}
2264d522f475Smrg    }
2265d522f475Smrg}
2266d522f475Smrg
2267d522f475Smrg#if OPT_TRACE && OPT_WIDE_CHARS
2268d522f475Smrgstatic void
2269e0a2b6dfSmrgGettingSelection(Display *dpy, Atom type, Char *line, unsigned long len)
2270d522f475Smrg{
2271d522f475Smrg    Char *cp;
2272913cc679Smrg    const char *name = TraceAtomName(dpy, type);
2273d522f475Smrg
227401037d57Smrg    TRACE(("Getting %s (type=%ld, length=%ld)\n", name, (long int) type, len));
2275d522f475Smrg    for (cp = line; cp < line + len; cp++) {
2276956cc18dSsnj	TRACE(("[%d:%lu]", (int) (cp + 1 - line), len));
2277d522f475Smrg	if (isprint(*cp)) {
2278d522f475Smrg	    TRACE(("%c\n", *cp));
2279d522f475Smrg	} else {
2280d522f475Smrg	    TRACE(("\\x%02x\n", *cp));
2281d522f475Smrg	}
2282d522f475Smrg    }
2283d522f475Smrg}
2284d522f475Smrg#else
2285d522f475Smrg#define GettingSelection(dpy,type,line,len)	/* nothing */
2286d522f475Smrg#endif
2287d522f475Smrg
2288d522f475Smrg#ifdef VMS
2289d522f475Smrg#  define tty_vwrite(pty,lag,l)		tt_write(lag,l)
2290d522f475Smrg#else /* !( VMS ) */
22915307cd1aSmrg#  define tty_vwrite(pty,lag,l)		v_write(pty,lag,(size_t) l)
2292d522f475Smrg#endif /* defined VMS */
2293d522f475Smrg
2294d522f475Smrg#if OPT_PASTE64
2295d522f475Smrg/* Return base64 code character given 6-bit number */
2296d522f475Smrgstatic const char base64_code[] = "\
2297d522f475SmrgABCDEFGHIJKLMNOPQRSTUVWXYZ\
2298d522f475Smrgabcdefghijklmnopqrstuvwxyz\
2299d522f475Smrg0123456789+/";
2300d522f475Smrgstatic void
2301e0a2b6dfSmrgbase64_flush(TScreen *screen)
2302d522f475Smrg{
2303d522f475Smrg    Char x;
230401037d57Smrg
230501037d57Smrg    TRACE(("base64_flush count %d, pad %d (%d)\n",
230601037d57Smrg	   screen->base64_count,
230701037d57Smrg	   screen->base64_pad,
230801037d57Smrg	   screen->base64_pad & 3));
230901037d57Smrg
2310d522f475Smrg    switch (screen->base64_count) {
2311d522f475Smrg    case 0:
2312d522f475Smrg	break;
2313d522f475Smrg    case 2:
23142eaa94a1Schristos	x = CharOf(base64_code[screen->base64_accu << 4]);
2315d522f475Smrg	tty_vwrite(screen->respond, &x, 1);
2316d522f475Smrg	break;
2317d522f475Smrg    case 4:
23182eaa94a1Schristos	x = CharOf(base64_code[screen->base64_accu << 2]);
2319d522f475Smrg	tty_vwrite(screen->respond, &x, 1);
2320d522f475Smrg	break;
2321d522f475Smrg    }
232201037d57Smrg    if (screen->base64_pad & 3) {
2323d522f475Smrg	tty_vwrite(screen->respond,
2324492d43a5Smrg		   (const Char *) "===",
232501037d57Smrg		   (unsigned) (3 - (screen->base64_pad & 3)));
232601037d57Smrg    }
2327d522f475Smrg    screen->base64_count = 0;
2328d522f475Smrg    screen->base64_accu = 0;
2329d522f475Smrg    screen->base64_pad = 0;
2330d522f475Smrg}
2331d522f475Smrg#endif /* OPT_PASTE64 */
2332d522f475Smrg
2333e0a2b6dfSmrg/*
2334e0a2b6dfSmrg * Translate ISO-8859-1 or UTF-8 data to NRCS.
2335e0a2b6dfSmrg */
2336d522f475Smrgstatic void
23375307cd1aSmrgToNational(XtermWidget xw, Char *buffer, size_t *length)
2338e0a2b6dfSmrg{
2339f2e35a3aSmrg    TScreen *screen = TScreenOf(xw);
2340f2e35a3aSmrg    DECNRCM_codes gsetL = screen->gsets[screen->curgl];
2341f2e35a3aSmrg    DECNRCM_codes gsetR = screen->gsets[screen->curgr];
2342e0a2b6dfSmrg
2343e0a2b6dfSmrg#if OPT_WIDE_CHARS
2344e0a2b6dfSmrg    if ((screen->utf8_nrc_mode | screen->utf8_mode) != uFalse) {
23452e4f8982Smrg	Char *p;
2346e0a2b6dfSmrg	PtyData *data = TypeXtMallocX(PtyData, *length);
2347e0a2b6dfSmrg
2348e0a2b6dfSmrg	memset(data, 0, sizeof(*data));
2349e0a2b6dfSmrg	data->next = data->buffer;
2350e0a2b6dfSmrg	data->last = data->buffer + *length;
23515307cd1aSmrg	memcpy(data->buffer, buffer, *length);
2352e0a2b6dfSmrg	p = buffer;
2353e0a2b6dfSmrg	while (data->next < data->last) {
23542e4f8982Smrg	    unsigned chr, out, gl, gr;
23552e4f8982Smrg
2356894e0ac8Smrg	    if (!decodeUtf8(screen, data)) {
2357e0a2b6dfSmrg		data->utf_size = 1;
2358e0a2b6dfSmrg		data->utf_data = data->next[0];
2359e0a2b6dfSmrg	    }
2360e0a2b6dfSmrg	    data->next += data->utf_size;
2361e0a2b6dfSmrg	    chr = data->utf_data;
2362e0a2b6dfSmrg	    out = chr;
2363f2e35a3aSmrg	    if ((gl = xtermCharSetIn(xw, chr, gsetL)) != chr) {
2364e0a2b6dfSmrg		out = gl;
2365f2e35a3aSmrg	    } else if ((gr = xtermCharSetIn(xw, chr, gsetR)) != chr) {
2366e0a2b6dfSmrg		out = gr;
2367e0a2b6dfSmrg	    }
2368e0a2b6dfSmrg	    *p++ = (Char) ((out < 256) ? out : ' ');
2369e0a2b6dfSmrg	}
23705307cd1aSmrg	*length = (size_t) (p - buffer);
2371e0a2b6dfSmrg	free(data);
2372e0a2b6dfSmrg    } else
2373e0a2b6dfSmrg#endif
2374e0a2b6dfSmrg    {
23752e4f8982Smrg	Char *p;
23762e4f8982Smrg
23775307cd1aSmrg	for (p = buffer; (size_t) (p - buffer) < *length; ++p) {
23782e4f8982Smrg	    unsigned gl, gr;
23792e4f8982Smrg	    unsigned chr = *p;
23802e4f8982Smrg	    unsigned out = chr;
2381f2e35a3aSmrg	    if ((gl = xtermCharSetIn(xw, chr, gsetL)) != chr) {
2382e0a2b6dfSmrg		out = gl;
2383f2e35a3aSmrg	    } else if ((gr = xtermCharSetIn(xw, chr, gsetR)) != chr) {
2384e0a2b6dfSmrg		out = gr;
2385e0a2b6dfSmrg	    }
2386e0a2b6dfSmrg	    *p = (Char) out;
2387e0a2b6dfSmrg	}
2388e0a2b6dfSmrg    }
2389e0a2b6dfSmrg}
2390e0a2b6dfSmrg
2391e0a2b6dfSmrgstatic void
23925307cd1aSmrg_qWriteSelectionData(XtermWidget xw, Char *lag, size_t length)
2393d522f475Smrg{
23940bd37d32Smrg    TScreen *screen = TScreenOf(xw);
23950bd37d32Smrg
2396e0a2b6dfSmrg    /*
2397e0a2b6dfSmrg     * If we are pasting into a window which is using NRCS, we want to map
2398e0a2b6dfSmrg     * the text from the normal encoding (ISO-8859-1 or UTF-8) into the coding
2399e0a2b6dfSmrg     * that an application would use to write characters with NRCS.
2400e0a2b6dfSmrg     *
2401e0a2b6dfSmrg     * TODO: handle conversion from UTF-8, and adjust length.  This can be done
2402e0a2b6dfSmrg     * in the same buffer because the target is always 8-bit.
2403e0a2b6dfSmrg     */
2404e0a2b6dfSmrg    if ((xw->flags & NATIONAL) && (length != 0)) {
2405f2e35a3aSmrg	ToNational(xw, lag, &length);
2406e0a2b6dfSmrg    }
2407d522f475Smrg#if OPT_PASTE64
2408d522f475Smrg    if (screen->base64_paste) {
2409d522f475Smrg	/* Send data as base64 */
2410d522f475Smrg	Char *p = lag;
2411d522f475Smrg	Char buf[64];
2412d522f475Smrg	unsigned x = 0;
24130bd37d32Smrg
24145307cd1aSmrg	TRACE(("convert to base64 %lu:%s\n",
24155307cd1aSmrg	       (unsigned long) length,
24165307cd1aSmrg	       visibleChars(p, length)));
241701037d57Smrg
24180bd37d32Smrg	/*
24190bd37d32Smrg	 * Handle the case where the selection is from _this_ xterm, which
24200bd37d32Smrg	 * puts part of the reply in the buffer before the selection callback
24210bd37d32Smrg	 * happens.
24220bd37d32Smrg	 */
24230bd37d32Smrg	if (screen->base64_paste && screen->unparse_len) {
24240bd37d32Smrg	    unparse_end(xw);
24250bd37d32Smrg	}
2426d522f475Smrg	while (length--) {
2427d522f475Smrg	    switch (screen->base64_count) {
2428d522f475Smrg	    case 0:
24292eaa94a1Schristos		buf[x++] = CharOf(base64_code[*p >> 2]);
24302eaa94a1Schristos		screen->base64_accu = (unsigned) (*p & 0x3);
2431d522f475Smrg		screen->base64_count = 2;
2432d522f475Smrg		++p;
2433d522f475Smrg		break;
2434d522f475Smrg	    case 2:
24352eaa94a1Schristos		buf[x++] = CharOf(base64_code[(screen->base64_accu << 4) +
24362eaa94a1Schristos					      (*p >> 4)]);
24372eaa94a1Schristos		screen->base64_accu = (unsigned) (*p & 0xF);
2438d522f475Smrg		screen->base64_count = 4;
2439d522f475Smrg		++p;
2440d522f475Smrg		break;
2441d522f475Smrg	    case 4:
24422eaa94a1Schristos		buf[x++] = CharOf(base64_code[(screen->base64_accu << 2) +
24432eaa94a1Schristos					      (*p >> 6)]);
24442eaa94a1Schristos		buf[x++] = CharOf(base64_code[*p & 0x3F]);
2445d522f475Smrg		screen->base64_accu = 0;
2446d522f475Smrg		screen->base64_count = 0;
2447d522f475Smrg		++p;
2448d522f475Smrg		break;
2449d522f475Smrg	    }
2450d522f475Smrg	    if (x >= 63) {
2451d522f475Smrg		/* Write 63 or 64 characters */
2452d522f475Smrg		screen->base64_pad += x;
245301037d57Smrg		TRACE(("writing base64 interim %s\n", visibleChars(buf, x)));
2454d522f475Smrg		tty_vwrite(screen->respond, buf, x);
2455d522f475Smrg		x = 0;
2456d522f475Smrg	    }
2457d522f475Smrg	}
2458d522f475Smrg	if (x != 0) {
2459d522f475Smrg	    screen->base64_pad += x;
246001037d57Smrg	    TRACE(("writing base64 finish %s\n", visibleChars(buf, x)));
2461d522f475Smrg	    tty_vwrite(screen->respond, buf, x);
2462d522f475Smrg	}
2463d522f475Smrg    } else
2464d522f475Smrg#endif /* OPT_PASTE64 */
2465d522f475Smrg#if OPT_READLINE
2466d522f475Smrg    if (SCREEN_FLAG(screen, paste_quotes)) {
24675307cd1aSmrg	Char quote[2];
24685307cd1aSmrg	quote[0] = (Char) get_tty_lnext(screen->respond, XTERM_LNEXT, "pty");
24695307cd1aSmrg	quote[1] = 0;
24705307cd1aSmrg	TRACE(("writing quoted selection data %s\n", visibleChars(lag, length)));
2471d522f475Smrg	while (length--) {
24725307cd1aSmrg	    tty_vwrite(screen->respond, quote, 1);
2473d522f475Smrg	    tty_vwrite(screen->respond, lag++, 1);
2474d522f475Smrg	}
2475d522f475Smrg    } else
2476d522f475Smrg#endif
247701037d57Smrg    {
24785307cd1aSmrg	TRACE(("writing selection data %s\n", visibleChars(lag, length)));
2479d522f475Smrg	tty_vwrite(screen->respond, lag, length);
248001037d57Smrg    }
2481d522f475Smrg}
2482d522f475Smrg
2483d522f475Smrgstatic void
2484e0a2b6dfSmrg_WriteSelectionData(XtermWidget xw, Char *line, size_t length)
2485d522f475Smrg{
24865307cd1aSmrg#if OPT_PASTE64 || OPT_READLINE
24870bd37d32Smrg    TScreen *screen = TScreenOf(xw);
24882e4f8982Smrg#endif
2489d522f475Smrg
2490d522f475Smrg    /* in the VMS version, if tt_pasting isn't set to True then qio
2491d522f475Smrg       reads aren't blocked and an infinite loop is entered, where the
2492d522f475Smrg       pasted text shows up as new input, goes in again, shows up
2493d522f475Smrg       again, ad nauseum. */
2494d522f475Smrg#ifdef VMS
2495d522f475Smrg    tt_pasting = True;
2496d522f475Smrg#endif
2497d522f475Smrg
2498d522f475Smrg#if OPT_PASTE64
2499d522f475Smrg    if (screen->base64_paste) {
25005307cd1aSmrg	_qWriteSelectionData(xw, line, length);
2501d522f475Smrg	base64_flush(screen);
2502d522f475Smrg    } else
2503d522f475Smrg#endif
2504d522f475Smrg    {
2505d522f475Smrg	if (!SCREEN_FLAG(screen, paste_literal_nl)) {
25065307cd1aSmrg	    size_t n;
25075307cd1aSmrg	    for (n = 0; n < length; ++n) {
25085307cd1aSmrg		if (line[n] == '\n') {
25095307cd1aSmrg		    line[n] = '\r';
2510d522f475Smrg		}
2511d522f475Smrg	    }
2512d522f475Smrg	}
2513d522f475Smrg
25145307cd1aSmrg	_qWriteSelectionData(xw, line, length);
2515d522f475Smrg    }
2516d522f475Smrg#ifdef VMS
2517d522f475Smrg    tt_pasting = False;
2518d522f475Smrg    tt_start_read();		/* reenable reads or a character may be lost */
2519d522f475Smrg#endif
2520d522f475Smrg}
2521d522f475Smrg
2522f2e35a3aSmrg#if OPT_PASTE64 || OPT_READLINE
2523d522f475Smrgstatic void
2524e0a2b6dfSmrg_WriteKey(TScreen *screen, const Char *in)
2525d522f475Smrg{
2526d522f475Smrg    Char line[16];
2527d522f475Smrg    unsigned count = 0;
2528492d43a5Smrg    size_t length = strlen((const char *) in);
2529d522f475Smrg
2530d522f475Smrg    if (screen->control_eight_bits) {
2531d522f475Smrg	line[count++] = ANSI_CSI;
2532d522f475Smrg    } else {
2533d522f475Smrg	line[count++] = ANSI_ESC;
2534d522f475Smrg	line[count++] = '[';
2535d522f475Smrg    }
2536d522f475Smrg    while (length--)
2537d522f475Smrg	line[count++] = *in++;
2538d522f475Smrg    line[count++] = '~';
2539d522f475Smrg    tty_vwrite(screen->respond, line, count);
2540d522f475Smrg}
2541d522f475Smrg#endif /* OPT_READLINE */
2542d522f475Smrg
25430bd37d32Smrg/*
25440bd37d32Smrg * Unless enabled by the user, strip control characters other than formatting.
25450bd37d32Smrg */
25460bd37d32Smrgstatic size_t
25470bd37d32SmrgremoveControls(XtermWidget xw, char *value)
25480bd37d32Smrg{
25490bd37d32Smrg    TScreen *screen = TScreenOf(xw);
25500bd37d32Smrg    size_t dst = 0;
25510bd37d32Smrg
25520bd37d32Smrg    if (screen->allowPasteControls) {
25530bd37d32Smrg	dst = strlen(value);
25540bd37d32Smrg    } else {
25552e4f8982Smrg	size_t src = 0;
255604b94745Smrg	Boolean *disallowed = screen->disallow_paste_ops;
255704b94745Smrg	TERMIO_STRUCT data;
255804b94745Smrg	char current_chars[epLAST];
255904b94745Smrg
256004b94745Smrg	if (disallowed[epSTTY] && ttyGetAttr(screen->respond, &data) == 0) {
256104b94745Smrg	    int n;
256204b94745Smrg	    int disabled = xtermDisabledChar();
256304b94745Smrg
256404b94745Smrg	    TRACE(("disallow(STTY):"));
256504b94745Smrg	    memcpy(current_chars, disallowed, sizeof(current_chars));
256604b94745Smrg
256704b94745Smrg	    for (n = 0; n < NCCS; ++n) {
256804b94745Smrg		PasteControls nc = (data.c_cc[n] < 32
256904b94745Smrg				    ? data.c_cc[n]
257004b94745Smrg				    : (data.c_cc[n] == 127
257104b94745Smrg				       ? epDEL
257204b94745Smrg				       : epLAST));
257304b94745Smrg		if (nc == epNUL || nc == epLAST)
257404b94745Smrg		    continue;
257504b94745Smrg		if (CharOf(data.c_cc[n]) == CharOf(disabled))
257604b94745Smrg		    continue;
257704b94745Smrg		if ((n == VMIN || n == VTIME) && !(data.c_lflag & ICANON))
257804b94745Smrg		    continue;
257904b94745Smrg		switch (n) {
258004b94745Smrg		    /* POSIX */
258104b94745Smrg		case VEOF:
258204b94745Smrg		case VEOL:
258304b94745Smrg		case VERASE:
258404b94745Smrg		case VINTR:
258504b94745Smrg		case VKILL:
258604b94745Smrg		case VQUIT:
258704b94745Smrg		case VSTART:
258804b94745Smrg		case VSTOP:
258904b94745Smrg		case VSUSP:
259004b94745Smrg		    /* system-dependent */
259104b94745Smrg#ifdef VDISCARD
259204b94745Smrg		case VDISCARD:
259304b94745Smrg#endif
259404b94745Smrg#ifdef VDSUSP
259504b94745Smrg		case VDSUSP:
259604b94745Smrg#endif
259704b94745Smrg#ifdef VEOL2
259804b94745Smrg		case VEOL2:
259904b94745Smrg#endif
260004b94745Smrg#ifdef VLNEXT
260104b94745Smrg		case VLNEXT:
260204b94745Smrg#endif
260304b94745Smrg#ifdef VREPRINT
260404b94745Smrg		case VREPRINT:
260504b94745Smrg#endif
260604b94745Smrg#ifdef VSTATUS
260704b94745Smrg		case VSTATUS:
260804b94745Smrg#endif
260904b94745Smrg#ifdef VSWTC
261004b94745Smrg		case VSWTC:	/* System V SWTCH */
261104b94745Smrg#endif
261204b94745Smrg#ifdef VWERASE
261304b94745Smrg		case VWERASE:
261404b94745Smrg#endif
261504b94745Smrg		    break;
261604b94745Smrg		default:
261704b94745Smrg		    continue;
261804b94745Smrg		}
261904b94745Smrg		if (nc != epLAST) {
262004b94745Smrg		    TRACE((" \\%03o", data.c_cc[n]));
262104b94745Smrg		    current_chars[nc] = 1;
262204b94745Smrg		}
262304b94745Smrg	    }
262404b94745Smrg	    TRACE(("\n"));
262504b94745Smrg	    disallowed = current_chars;
262604b94745Smrg	}
26270bd37d32Smrg	while ((value[dst] = value[src]) != '\0') {
26280bd37d32Smrg	    int ch = CharOf(value[src++]);
2629f2e35a3aSmrg
2630f2e35a3aSmrg#define ReplacePaste(n) \
263104b94745Smrg	    if (disallowed[n]) \
2632f2e35a3aSmrg		value[dst] = ' '
2633f2e35a3aSmrg
26340bd37d32Smrg	    if (ch < 32) {
2635f2e35a3aSmrg		ReplacePaste(epC0);
2636f2e35a3aSmrg		ReplacePaste(ch);
2637f2e35a3aSmrg		++dst;
2638f2e35a3aSmrg	    } else if (ch == ANSI_DEL) {
2639f2e35a3aSmrg		ReplacePaste(epDEL);
2640f2e35a3aSmrg		++dst;
26410bd37d32Smrg	    }
26420bd37d32Smrg#if OPT_WIDE_CHARS
2643e0a2b6dfSmrg	    else if (screen->utf8_inparse || screen->utf8_nrc_mode)
26440bd37d32Smrg		++dst;
26450bd37d32Smrg#endif
26460bd37d32Smrg#if OPT_C1_PRINT || OPT_WIDE_CHARS
26470bd37d32Smrg	    else if (screen->c1_printable)
26480bd37d32Smrg		++dst;
26490bd37d32Smrg#endif
26500bd37d32Smrg	    else if (ch >= 128 && ch < 160)
26510bd37d32Smrg		continue;
26520bd37d32Smrg	    else
26530bd37d32Smrg		++dst;
26540bd37d32Smrg	}
26550bd37d32Smrg    }
26560bd37d32Smrg    return dst;
26570bd37d32Smrg}
26580bd37d32Smrg
2659f2e35a3aSmrg#if OPT_SELECTION_OPS
2660f2e35a3aSmrgstatic void
2661f2e35a3aSmrgbeginInternalSelect(XtermWidget xw)
2662f2e35a3aSmrg{
2663f2e35a3aSmrg    TScreen *screen = TScreenOf(xw);
2664f2e35a3aSmrg    InternalSelect *mydata = &(screen->internal_select);
2665f2e35a3aSmrg
2666f2e35a3aSmrg    (void) mydata;
2667f2e35a3aSmrg    /* override flags so that SelectionReceived only updates a buffer */
2668f2e35a3aSmrg#if OPT_PASTE64
2669f2e35a3aSmrg    mydata->base64_paste = screen->base64_paste;
2670f2e35a3aSmrg    screen->base64_paste = 0;
2671f2e35a3aSmrg#endif
2672f2e35a3aSmrg#if OPT_PASTE64 || OPT_READLINE
2673f2e35a3aSmrg    mydata->paste_brackets = screen->paste_brackets;
2674f2e35a3aSmrg    SCREEN_FLAG_unset(screen, paste_brackets);
2675f2e35a3aSmrg#endif
2676f2e35a3aSmrg}
2677f2e35a3aSmrg
2678f2e35a3aSmrgstatic void
2679f2e35a3aSmrgfinishInternalSelect(XtermWidget xw)
2680f2e35a3aSmrg{
2681f2e35a3aSmrg    TScreen *screen = TScreenOf(xw);
2682f2e35a3aSmrg    InternalSelect *mydata = &(screen->internal_select);
2683f2e35a3aSmrg
2684f2e35a3aSmrg    (void) mydata;
2685f2e35a3aSmrg#if OPT_PASTE64
2686f2e35a3aSmrg    screen->base64_paste = mydata->base64_paste;
2687f2e35a3aSmrg#endif
2688f2e35a3aSmrg#if OPT_PASTE64 || OPT_READLINE
2689f2e35a3aSmrg    screen->paste_brackets = mydata->paste_brackets;
2690f2e35a3aSmrg#endif
2691f2e35a3aSmrg}
2692f2e35a3aSmrg
2693f2e35a3aSmrg#else
2694f2e35a3aSmrg#define finishInternalSelect(xw)	/* nothing */
2695f2e35a3aSmrg#endif /* OPT_SELECTION_OPS */
2696f2e35a3aSmrg
2697d522f475Smrg/* SelectionReceived: stuff received selection text into pty */
2698d522f475Smrg
2699d522f475Smrg/* ARGSUSED */
2700d522f475Smrgstatic void
2701d522f475SmrgSelectionReceived(Widget w,
2702d522f475Smrg		  XtPointer client_data,
2703894e0ac8Smrg		  Atom *selection GCC_UNUSED,
2704894e0ac8Smrg		  Atom *type,
2705d522f475Smrg		  XtPointer value,
2706d522f475Smrg		  unsigned long *length,
2707d522f475Smrg		  int *format)
2708d522f475Smrg{
2709d522f475Smrg    char **text_list = NULL;
27102e4f8982Smrg    int text_list_count = 0;
2711d522f475Smrg    XTextProperty text_prop;
2712d522f475Smrg    TScreen *screen;
2713d522f475Smrg    Display *dpy;
2714d522f475Smrg#if OPT_TRACE && OPT_WIDE_CHARS
2715d522f475Smrg    Char *line = (Char *) value;
2716d522f475Smrg#endif
2717d522f475Smrg
2718956cc18dSsnj    XtermWidget xw;
2719956cc18dSsnj
2720956cc18dSsnj    if ((xw = getXtermWidget(w)) == 0)
2721d522f475Smrg	return;
2722956cc18dSsnj
2723956cc18dSsnj    screen = TScreenOf(xw);
2724d522f475Smrg    dpy = XtDisplay(w);
2725d522f475Smrg
2726d522f475Smrg    if (*type == 0		/*XT_CONVERT_FAIL */
2727d522f475Smrg	|| *length == 0
272801037d57Smrg	|| value == NULL) {
272901037d57Smrg	TRACE(("...no data to convert\n"));
2730d522f475Smrg	goto fail;
273101037d57Smrg    }
2732d522f475Smrg
2733d522f475Smrg    text_prop.value = (unsigned char *) value;
2734d522f475Smrg    text_prop.encoding = *type;
2735d522f475Smrg    text_prop.format = *format;
2736d522f475Smrg    text_prop.nitems = *length;
2737d522f475Smrg
273801037d57Smrg    TRACE(("SelectionReceived %s %s format %d, nitems %ld\n",
2739913cc679Smrg	   TraceAtomName(screen->display, *selection),
2740956cc18dSsnj	   visibleSelectionTarget(dpy, text_prop.encoding),
2741956cc18dSsnj	   text_prop.format,
2742956cc18dSsnj	   text_prop.nitems));
2743956cc18dSsnj
2744d522f475Smrg#if OPT_WIDE_CHARS
2745e39b573cSmrg    if (XSupportsLocale() && screen->wide_chars) {
2746d522f475Smrg	if (*type == XA_UTF8_STRING(dpy) ||
2747d522f475Smrg	    *type == XA_STRING ||
2748d522f475Smrg	    *type == XA_COMPOUND_TEXT(dpy)) {
2749d522f475Smrg	    GettingSelection(dpy, *type, line, *length);
2750d522f475Smrg	    if (Xutf8TextPropertyToTextList(dpy, &text_prop,
2751d522f475Smrg					    &text_list,
2752d522f475Smrg					    &text_list_count) < 0) {
2753e39b573cSmrg		TRACE(("default Xutf8 Conversion failed\n"));
2754d522f475Smrg		text_list = NULL;
2755d522f475Smrg	    }
2756d522f475Smrg	}
2757d522f475Smrg    } else
2758d522f475Smrg#endif /* OPT_WIDE_CHARS */
2759d522f475Smrg    {
2760d522f475Smrg	/* Convert the selection to locale's multibyte encoding. */
2761d522f475Smrg
2762d522f475Smrg	if (*type == XA_UTF8_STRING(dpy) ||
2763d522f475Smrg	    *type == XA_STRING ||
2764d522f475Smrg	    *type == XA_COMPOUND_TEXT(dpy)) {
2765d522f475Smrg	    Status rc;
2766d522f475Smrg
2767d522f475Smrg	    GettingSelection(dpy, *type, line, *length);
2768d522f475Smrg
2769d522f475Smrg#if OPT_WIDE_CHARS
2770d522f475Smrg	    if (*type == XA_UTF8_STRING(dpy) &&
2771d522f475Smrg		!(screen->wide_chars || screen->c1_printable)) {
277220d2c4d2Smrg		rc = xtermUtf8ToTextList(xw, &text_prop,
277320d2c4d2Smrg					 &text_list, &text_list_count);
2774d522f475Smrg	    } else
2775d522f475Smrg#endif
2776e39b573cSmrg	    if (*type == XA_STRING && (!XSupportsLocale() || screen->brokenSelections)) {
2777d522f475Smrg		rc = XTextPropertyToStringList(&text_prop,
2778d522f475Smrg					       &text_list, &text_list_count);
2779d522f475Smrg	    } else {
2780d522f475Smrg		rc = XmbTextPropertyToTextList(dpy, &text_prop,
2781d522f475Smrg					       &text_list,
2782d522f475Smrg					       &text_list_count);
2783d522f475Smrg	    }
2784d522f475Smrg	    if (rc < 0) {
2785d522f475Smrg		TRACE(("Conversion failed\n"));
2786d522f475Smrg		text_list = NULL;
2787d522f475Smrg	    }
2788d522f475Smrg	}
2789d522f475Smrg    }
2790d522f475Smrg
2791d522f475Smrg    if (text_list != NULL && text_list_count != 0) {
2792d522f475Smrg	int i;
2793d522f475Smrg
2794d522f475Smrg#if OPT_PASTE64
2795d522f475Smrg	if (screen->base64_paste) {
2796a1f3da82Smrg	    /* EMPTY */ ;
2797d522f475Smrg	} else
2798d522f475Smrg#endif
2799f2e35a3aSmrg#if OPT_PASTE64 || OPT_READLINE
2800f2e35a3aSmrg	if (SCREEN_FLAG(screen, paste_brackets) && !screen->selectToBuffer) {
2801492d43a5Smrg	    _WriteKey(screen, (const Char *) "200");
2802d522f475Smrg	}
2803d522f475Smrg#endif
2804d522f475Smrg	for (i = 0; i < text_list_count; i++) {
28050bd37d32Smrg	    size_t len = removeControls(xw, text_list[i]);
280601037d57Smrg
28070bd37d32Smrg	    if (screen->selectToBuffer) {
280801037d57Smrg		InternalSelect *mydata = &(screen->internal_select);
2809f2e35a3aSmrg		if (!mydata->done) {
2810f2e35a3aSmrg		    size_t have = (mydata->buffer
2811f2e35a3aSmrg				   ? strlen(mydata->buffer)
2812f2e35a3aSmrg				   : 0);
2813f2e35a3aSmrg		    size_t need = have + len + 1;
2814f2e35a3aSmrg		    char *buffer = realloc(mydata->buffer, need);
2815f2e35a3aSmrg
2816f2e35a3aSmrg		    if (buffer != 0) {
2817f2e35a3aSmrg			strcpy(buffer + have, text_list[i]);
2818f2e35a3aSmrg			mydata->buffer = buffer;
2819f2e35a3aSmrg		    }
2820f2e35a3aSmrg		    TRACE(("FormatSelect %d.%d .. %d.%d %s\n",
2821f2e35a3aSmrg			   screen->startSel.row,
2822f2e35a3aSmrg			   screen->startSel.col,
2823f2e35a3aSmrg			   screen->endSel.row,
2824f2e35a3aSmrg			   screen->endSel.col,
2825f2e35a3aSmrg			   mydata->buffer));
2826f2e35a3aSmrg		    mydata->format_select(w, mydata->format, mydata->buffer,
2827f2e35a3aSmrg					  &(screen->startSel),
2828f2e35a3aSmrg					  &(screen->endSel));
2829f2e35a3aSmrg		    mydata->done = True;
28300bd37d32Smrg		}
283101037d57Smrg
28320bd37d32Smrg	    } else {
28330bd37d32Smrg		_WriteSelectionData(xw, (Char *) text_list[i], len);
28340bd37d32Smrg	    }
2835d522f475Smrg	}
2836d522f475Smrg#if OPT_PASTE64
2837d522f475Smrg	if (screen->base64_paste) {
2838956cc18dSsnj	    FinishPaste64(xw);
2839d522f475Smrg	} else
2840d522f475Smrg#endif
2841f2e35a3aSmrg#if OPT_PASTE64 || OPT_READLINE
2842f2e35a3aSmrg	if (SCREEN_FLAG(screen, paste_brackets) && !screen->selectToBuffer) {
2843492d43a5Smrg	    _WriteKey(screen, (const Char *) "201");
2844d522f475Smrg	}
2845d522f475Smrg#endif
2846f2e35a3aSmrg	if (screen->selectToBuffer) {
2847f2e35a3aSmrg	    InternalSelect *mydata = &(screen->internal_select);
2848f2e35a3aSmrg	    finishInternalSelect(xw);
2849f2e35a3aSmrg	    if (mydata->done) {
2850f2e35a3aSmrg		free(mydata->format);
2851f2e35a3aSmrg		free(mydata->buffer);
2852f2e35a3aSmrg		memset(mydata, 0, sizeof(*mydata));
2853f2e35a3aSmrg	    }
2854f2e35a3aSmrg	    screen->selectToBuffer = False;
2855f2e35a3aSmrg	}
2856d522f475Smrg	XFreeStringList(text_list);
285701037d57Smrg    } else {
285801037d57Smrg	TRACE(("...empty text-list\n"));
2859d522f475Smrg	goto fail;
286001037d57Smrg    }
2861d522f475Smrg
2862d522f475Smrg    XtFree((char *) client_data);
2863d522f475Smrg    XtFree((char *) value);
2864d522f475Smrg
2865d522f475Smrg    return;
2866d522f475Smrg
2867d522f475Smrg  fail:
2868d522f475Smrg    if (client_data != 0) {
2869d522f475Smrg	struct _SelectionList *list = (struct _SelectionList *) client_data;
2870956cc18dSsnj
2871956cc18dSsnj	TRACE(("SelectionReceived ->xtermGetSelection\n"));
2872d522f475Smrg	xtermGetSelection(w, list->time,
2873d522f475Smrg			  list->params, list->count, list->targets);
2874d522f475Smrg	XtFree((char *) client_data);
2875d522f475Smrg#if OPT_PASTE64
2876d522f475Smrg    } else {
2877956cc18dSsnj	FinishPaste64(xw);
2878d522f475Smrg#endif
2879d522f475Smrg    }
2880d522f475Smrg    return;
2881d522f475Smrg}
2882d522f475Smrg
2883d522f475Smrgvoid
2884d522f475SmrgHandleInsertSelection(Widget w,
2885894e0ac8Smrg		      XEvent *event,	/* assumed to be XButtonEvent* */
2886e0a2b6dfSmrg		      String *params,	/* selections in precedence order */
2887d522f475Smrg		      Cardinal *num_params)
2888d522f475Smrg{
2889956cc18dSsnj    XtermWidget xw;
2890d522f475Smrg
2891956cc18dSsnj    if ((xw = getXtermWidget(w)) != 0) {
2892f2e35a3aSmrg	TRACE_EVENT("HandleInsertSelection", event, params, num_params);
2893d522f475Smrg	if (!SendMousePosition(xw, event)) {
2894d522f475Smrg#if OPT_READLINE
2895d522f475Smrg	    int ldelta;
2896956cc18dSsnj	    TScreen *screen = TScreenOf(xw);
2897492d43a5Smrg	    if (IsBtnEvent(event)
2898f2e35a3aSmrg		&& !OverrideEvent(event)
2899913cc679Smrg		&& (okSendMousePos(xw) == MOUSE_OFF)
2900d522f475Smrg		&& SCREEN_FLAG(screen, paste_moves)
2901d522f475Smrg		&& rowOnCurrentLine(screen, eventRow(screen, event), &ldelta))
29025307cd1aSmrg		ReadLineMovePoint(xw, eventColBetween(screen, event), ldelta);
2903d522f475Smrg#endif /* OPT_READLINE */
2904d522f475Smrg
2905d522f475Smrg	    xtermGetSelection(w, event->xbutton.time, params, *num_params, NULL);
2906d522f475Smrg	}
2907d522f475Smrg    }
2908d522f475Smrg}
2909d522f475Smrg
2910d522f475Smrgstatic SelectUnit
2911956cc18dSsnjEvalSelectUnit(XtermWidget xw,
2912d522f475Smrg	       Time buttonDownTime,
2913d522f475Smrg	       SelectUnit defaultUnit,
2914d522f475Smrg	       unsigned int button)
2915d522f475Smrg{
2916956cc18dSsnj    TScreen *screen = TScreenOf(xw);
2917d522f475Smrg    SelectUnit result;
2918d522f475Smrg    int delta;
2919d522f475Smrg
2920d522f475Smrg    if (button != screen->lastButton) {
292120d2c4d2Smrg	delta = screen->multiClickTime + 1;
2922d522f475Smrg    } else if (screen->lastButtonUpTime == (Time) 0) {
2923d522f475Smrg	/* first time and once in a blue moon */
2924d522f475Smrg	delta = screen->multiClickTime + 1;
2925d522f475Smrg    } else if (buttonDownTime > screen->lastButtonUpTime) {
2926d522f475Smrg	/* most of the time */
29272eaa94a1Schristos	delta = (int) (buttonDownTime - screen->lastButtonUpTime);
2928d522f475Smrg    } else {
2929d522f475Smrg	/* time has rolled over since lastButtonUpTime */
29302eaa94a1Schristos	delta = (int) ((((Time) ~ 0) - screen->lastButtonUpTime) + buttonDownTime);
2931d522f475Smrg    }
2932d522f475Smrg
2933d522f475Smrg    if (delta > screen->multiClickTime) {
2934d522f475Smrg	screen->numberOfClicks = 1;
2935d522f475Smrg	result = defaultUnit;
2936d522f475Smrg    } else {
2937d522f475Smrg	result = screen->selectMap[screen->numberOfClicks % screen->maxClicks];
2938d522f475Smrg	screen->numberOfClicks += 1;
2939d522f475Smrg    }
2940d522f475Smrg    TRACE(("EvalSelectUnit(%d) = %d\n", screen->numberOfClicks, result));
2941d522f475Smrg    return result;
2942d522f475Smrg}
2943d522f475Smrg
2944d522f475Smrgstatic void
2945d522f475Smrgdo_select_start(XtermWidget xw,
2946894e0ac8Smrg		XEvent *event,	/* must be XButtonEvent* */
2947e0a2b6dfSmrg		CELL *cell)
2948d522f475Smrg{
2949956cc18dSsnj    TScreen *screen = TScreenOf(xw);
2950d522f475Smrg
2951d522f475Smrg    if (SendMousePosition(xw, event))
2952d522f475Smrg	return;
2953956cc18dSsnj    screen->selectUnit = EvalSelectUnit(xw,
2954d522f475Smrg					event->xbutton.time,
2955d522f475Smrg					Select_CHAR,
2956d522f475Smrg					event->xbutton.button);
2957d522f475Smrg    screen->replyToEmacs = False;
2958d522f475Smrg
2959d522f475Smrg#if OPT_READLINE
2960d522f475Smrg    lastButtonDownTime = event->xbutton.time;
2961d522f475Smrg#endif
2962d522f475Smrg
2963d522f475Smrg    StartSelect(xw, cell);
2964d522f475Smrg}
2965d522f475Smrg
2966d522f475Smrg/* ARGSUSED */
2967d522f475Smrgvoid
2968d522f475SmrgHandleSelectStart(Widget w,
2969894e0ac8Smrg		  XEvent *event,	/* must be XButtonEvent* */
2970e0a2b6dfSmrg		  String *params GCC_UNUSED,
2971d522f475Smrg		  Cardinal *num_params GCC_UNUSED)
2972d522f475Smrg{
2973956cc18dSsnj    XtermWidget xw;
2974956cc18dSsnj
2975956cc18dSsnj    if ((xw = getXtermWidget(w)) != 0) {
2976956cc18dSsnj	TScreen *screen = TScreenOf(xw);
2977d522f475Smrg	CELL cell;
2978d522f475Smrg
2979f2e35a3aSmrg	TRACE_EVENT("HandleSelectStart", event, params, num_params);
2980d522f475Smrg	screen->firstValidRow = 0;
2981d522f475Smrg	screen->lastValidRow = screen->max_row;
2982d522f475Smrg	PointToCELL(screen, event->xbutton.y, event->xbutton.x, &cell);
2983d522f475Smrg
2984d522f475Smrg#if OPT_READLINE
2985d522f475Smrg	ExtendingSelection = 0;
2986d522f475Smrg#endif
2987d522f475Smrg
2988d522f475Smrg	do_select_start(xw, event, &cell);
2989d522f475Smrg    }
2990d522f475Smrg}
2991d522f475Smrg
2992d522f475Smrg/* ARGSUSED */
2993d522f475Smrgvoid
2994d522f475SmrgHandleKeyboardSelectStart(Widget w,
2995894e0ac8Smrg			  XEvent *event,	/* must be XButtonEvent* */
2996e0a2b6dfSmrg			  String *params GCC_UNUSED,
2997d522f475Smrg			  Cardinal *num_params GCC_UNUSED)
2998d522f475Smrg{
2999956cc18dSsnj    XtermWidget xw;
3000956cc18dSsnj
3001956cc18dSsnj    if ((xw = getXtermWidget(w)) != 0) {
3002956cc18dSsnj	TScreen *screen = TScreenOf(xw);
30030bd37d32Smrg
3004f2e35a3aSmrg	TRACE_EVENT("HandleKeyboardSelectStart", event, params, num_params);
3005d522f475Smrg	do_select_start(xw, event, &screen->cursorp);
3006d522f475Smrg    }
3007d522f475Smrg}
3008d522f475Smrg
3009d522f475Smrgstatic void
3010894e0ac8SmrgTrackDown(XtermWidget xw, XButtonEvent *event)
3011d522f475Smrg{
3012956cc18dSsnj    TScreen *screen = TScreenOf(xw);
3013d522f475Smrg    CELL cell;
3014d522f475Smrg
3015956cc18dSsnj    screen->selectUnit = EvalSelectUnit(xw,
3016d522f475Smrg					event->time,
3017d522f475Smrg					Select_CHAR,
3018d522f475Smrg					event->button);
3019d522f475Smrg    if (screen->numberOfClicks > 1) {
3020d522f475Smrg	PointToCELL(screen, event->y, event->x, &cell);
3021d522f475Smrg	screen->replyToEmacs = True;
3022d522f475Smrg	StartSelect(xw, &cell);
3023d522f475Smrg    } else {
3024d522f475Smrg	screen->waitingForTrackInfo = True;
3025492d43a5Smrg	EditorButton(xw, event);
3026d522f475Smrg    }
3027d522f475Smrg}
3028d522f475Smrg
3029d522f475Smrg#define boundsCheck(x)	if (x < 0) \
3030d522f475Smrg			    x = 0; \
3031d522f475Smrg			else if (x >= screen->max_row) \
3032d522f475Smrg			    x = screen->max_row
3033d522f475Smrg
3034d522f475Smrgvoid
3035d522f475SmrgTrackMouse(XtermWidget xw,
3036d522f475Smrg	   int func,
3037e0a2b6dfSmrg	   CELL *start,
3038d522f475Smrg	   int firstrow,
3039d522f475Smrg	   int lastrow)
3040d522f475Smrg{
3041956cc18dSsnj    TScreen *screen = TScreenOf(xw);
3042d522f475Smrg
3043d522f475Smrg    if (screen->waitingForTrackInfo) {	/* if Timed, ignore */
3044d522f475Smrg	screen->waitingForTrackInfo = False;
3045d522f475Smrg
3046d522f475Smrg	if (func != 0) {
3047d522f475Smrg	    CELL first = *start;
3048d522f475Smrg
3049d522f475Smrg	    boundsCheck(first.row);
3050d522f475Smrg	    boundsCheck(firstrow);
3051d522f475Smrg	    boundsCheck(lastrow);
3052d522f475Smrg	    screen->firstValidRow = firstrow;
3053d522f475Smrg	    screen->lastValidRow = lastrow;
3054d522f475Smrg	    screen->replyToEmacs = True;
3055d522f475Smrg	    StartSelect(xw, &first);
3056d522f475Smrg	}
3057d522f475Smrg    }
3058d522f475Smrg}
3059d522f475Smrg
3060d522f475Smrgstatic void
3061e0a2b6dfSmrgStartSelect(XtermWidget xw, const CELL *cell)
3062d522f475Smrg{
3063956cc18dSsnj    TScreen *screen = TScreenOf(xw);
3064d522f475Smrg
3065d522f475Smrg    TRACE(("StartSelect row=%d, col=%d\n", cell->row, cell->col));
3066d522f475Smrg    if (screen->cursor_state)
3067f2e35a3aSmrg	HideCursor(xw);
3068d522f475Smrg    if (screen->numberOfClicks == 1) {
3069d522f475Smrg	/* set start of selection */
3070d522f475Smrg	screen->rawPos = *cell;
3071d522f475Smrg    }
3072d522f475Smrg    /* else use old values in rawPos */
3073d522f475Smrg    screen->saveStartR = screen->startExt = screen->rawPos;
3074d522f475Smrg    screen->saveEndR = screen->endExt = screen->rawPos;
3075d522f475Smrg    if (Coordinate(screen, cell) < Coordinate(screen, &(screen->rawPos))) {
3076d522f475Smrg	screen->eventMode = LEFTEXTENSION;
3077d522f475Smrg	screen->startExt = *cell;
3078d522f475Smrg    } else {
3079d522f475Smrg	screen->eventMode = RIGHTEXTENSION;
3080d522f475Smrg	screen->endExt = *cell;
3081d522f475Smrg    }
3082f2e35a3aSmrg    ComputeSelect(xw, &(screen->startExt), &(screen->endExt), False, True);
3083d522f475Smrg}
3084d522f475Smrg
3085d522f475Smrgstatic void
3086d522f475SmrgEndExtend(XtermWidget xw,
3087894e0ac8Smrg	  XEvent *event,	/* must be XButtonEvent */
3088e0a2b6dfSmrg	  String *params,	/* selections */
3089d522f475Smrg	  Cardinal num_params,
3090d522f475Smrg	  Bool use_cursor_loc)
3091d522f475Smrg{
3092d522f475Smrg    CELL cell;
3093956cc18dSsnj    TScreen *screen = TScreenOf(xw);
3094d522f475Smrg
3095f2e35a3aSmrg    TRACE_EVENT("EndExtend", event, params, &num_params);
3096d522f475Smrg    if (use_cursor_loc) {
3097d522f475Smrg	cell = screen->cursorp;
3098d522f475Smrg    } else {
3099d522f475Smrg	PointToCELL(screen, event->xbutton.y, event->xbutton.x, &cell);
3100d522f475Smrg    }
3101d522f475Smrg    ExtendExtend(xw, &cell);
31022e4f8982Smrg
3103d522f475Smrg    screen->lastButtonUpTime = event->xbutton.time;
3104d522f475Smrg    screen->lastButton = event->xbutton.button;
31052e4f8982Smrg
3106d522f475Smrg    if (!isSameCELL(&(screen->startSel), &(screen->endSel))) {
3107d522f475Smrg	if (screen->replyToEmacs) {
31082e4f8982Smrg	    Char line[64];
31092e4f8982Smrg	    unsigned count = 0;
31102e4f8982Smrg
3111d522f475Smrg	    if (screen->control_eight_bits) {
3112d522f475Smrg		line[count++] = ANSI_CSI;
3113d522f475Smrg	    } else {
3114d522f475Smrg		line[count++] = ANSI_ESC;
3115d522f475Smrg		line[count++] = '[';
3116d522f475Smrg	    }
3117d522f475Smrg	    if (isSameCELL(&(screen->rawPos), &(screen->startSel))
3118d522f475Smrg		&& isSameCELL(&cell, &(screen->endSel))) {
3119d522f475Smrg		/* Use short-form emacs select */
31200bd37d32Smrg
31210bd37d32Smrg		switch (screen->extend_coords) {
31220bd37d32Smrg		case 0:
31230bd37d32Smrg		case SET_EXT_MODE_MOUSE:
31240bd37d32Smrg		    line[count++] = 't';
31250bd37d32Smrg		    break;
31260bd37d32Smrg		case SET_SGR_EXT_MODE_MOUSE:
3127f2e35a3aSmrg		case SET_PIXEL_POSITION_MOUSE:
31280bd37d32Smrg		    line[count++] = '<';
31290bd37d32Smrg		    break;
31300bd37d32Smrg		}
31310bd37d32Smrg
3132492d43a5Smrg		count = EmitMousePosition(screen, line, count, screen->endSel.col);
31330bd37d32Smrg		count = EmitMousePositionSeparator(screen, line, count);
3134492d43a5Smrg		count = EmitMousePosition(screen, line, count, screen->endSel.row);
31350bd37d32Smrg
31360bd37d32Smrg		switch (screen->extend_coords) {
31370bd37d32Smrg		case SET_SGR_EXT_MODE_MOUSE:
31380bd37d32Smrg		case SET_URXVT_EXT_MODE_MOUSE:
3139f2e35a3aSmrg		case SET_PIXEL_POSITION_MOUSE:
31400bd37d32Smrg		    line[count++] = 't';
31410bd37d32Smrg		    break;
31420bd37d32Smrg		}
3143d522f475Smrg	    } else {
3144d522f475Smrg		/* long-form, specify everything */
31450bd37d32Smrg
31460bd37d32Smrg		switch (screen->extend_coords) {
31470bd37d32Smrg		case 0:
31480bd37d32Smrg		case SET_EXT_MODE_MOUSE:
31490bd37d32Smrg		    line[count++] = 'T';
31500bd37d32Smrg		    break;
31510bd37d32Smrg		case SET_SGR_EXT_MODE_MOUSE:
3152f2e35a3aSmrg		case SET_PIXEL_POSITION_MOUSE:
31530bd37d32Smrg		    line[count++] = '<';
31540bd37d32Smrg		    break;
31550bd37d32Smrg		}
31560bd37d32Smrg
3157492d43a5Smrg		count = EmitMousePosition(screen, line, count, screen->startSel.col);
31580bd37d32Smrg		count = EmitMousePositionSeparator(screen, line, count);
3159492d43a5Smrg		count = EmitMousePosition(screen, line, count, screen->startSel.row);
31600bd37d32Smrg		count = EmitMousePositionSeparator(screen, line, count);
3161492d43a5Smrg		count = EmitMousePosition(screen, line, count, screen->endSel.col);
31620bd37d32Smrg		count = EmitMousePositionSeparator(screen, line, count);
3163492d43a5Smrg		count = EmitMousePosition(screen, line, count, screen->endSel.row);
31640bd37d32Smrg		count = EmitMousePositionSeparator(screen, line, count);
3165492d43a5Smrg		count = EmitMousePosition(screen, line, count, cell.col);
31660bd37d32Smrg		count = EmitMousePositionSeparator(screen, line, count);
3167492d43a5Smrg		count = EmitMousePosition(screen, line, count, cell.row);
31680bd37d32Smrg
31690bd37d32Smrg		switch (screen->extend_coords) {
31700bd37d32Smrg		case SET_SGR_EXT_MODE_MOUSE:
31710bd37d32Smrg		case SET_URXVT_EXT_MODE_MOUSE:
3172f2e35a3aSmrg		case SET_PIXEL_POSITION_MOUSE:
31730bd37d32Smrg		    line[count++] = 'T';
31740bd37d32Smrg		    break;
31750bd37d32Smrg		}
3176d522f475Smrg	    }
31775307cd1aSmrg	    v_write(screen->respond, line, (size_t) count);
3178f2e35a3aSmrg	    UnHiliteText(xw);
3179d522f475Smrg	}
3180d522f475Smrg    }
3181d522f475Smrg    SelectSet(xw, event, params, num_params);
3182d522f475Smrg    screen->eventMode = NORMAL;
3183d522f475Smrg}
3184d522f475Smrg
3185d522f475Smrgvoid
3186d522f475SmrgHandleSelectSet(Widget w,
3187894e0ac8Smrg		XEvent *event,
3188e0a2b6dfSmrg		String *params,
3189d522f475Smrg		Cardinal *num_params)
3190d522f475Smrg{
3191956cc18dSsnj    XtermWidget xw;
3192956cc18dSsnj
3193956cc18dSsnj    if ((xw = getXtermWidget(w)) != 0) {
3194f2e35a3aSmrg	TRACE_EVENT("HandleSelectSet", event, params, num_params);
3195956cc18dSsnj	SelectSet(xw, event, params, *num_params);
3196d522f475Smrg    }
3197d522f475Smrg}
3198d522f475Smrg
3199d522f475Smrg/* ARGSUSED */
3200d522f475Smrgstatic void
3201d522f475SmrgSelectSet(XtermWidget xw,
3202894e0ac8Smrg	  XEvent *event GCC_UNUSED,
3203e0a2b6dfSmrg	  String *params,
3204d522f475Smrg	  Cardinal num_params)
3205d522f475Smrg{
3206956cc18dSsnj    TScreen *screen = TScreenOf(xw);
3207d522f475Smrg
3208d522f475Smrg    TRACE(("SelectSet\n"));
3209d522f475Smrg    /* Only do select stuff if non-null select */
3210d522f475Smrg    if (!isSameCELL(&(screen->startSel), &(screen->endSel))) {
3211f2e35a3aSmrg	Cardinal n;
3212f2e35a3aSmrg	for (n = 0; n < num_params; ++n) {
3213f2e35a3aSmrg	    SaltTextAway(xw,
3214f2e35a3aSmrg			 TargetToSelection(screen, params[n]),
3215f2e35a3aSmrg			 &(screen->startSel), &(screen->endSel));
3216f2e35a3aSmrg	}
32170bd37d32Smrg	_OwnSelection(xw, params, num_params);
3218d522f475Smrg    } else {
32190bd37d32Smrg	ScrnDisownSelection(xw);
3220d522f475Smrg    }
3221d522f475Smrg}
3222d522f475Smrg
3223d522f475Smrg#define Abs(x)		((x) < 0 ? -(x) : (x))
3224d522f475Smrg
3225d522f475Smrg/* ARGSUSED */
3226d522f475Smrgstatic void
3227d522f475Smrgdo_start_extend(XtermWidget xw,
3228894e0ac8Smrg		XEvent *event,	/* must be XButtonEvent* */
3229e0a2b6dfSmrg		String *params GCC_UNUSED,
3230d522f475Smrg		Cardinal *num_params GCC_UNUSED,
3231d522f475Smrg		Bool use_cursor_loc)
3232d522f475Smrg{
3233956cc18dSsnj    TScreen *screen = TScreenOf(xw);
3234d522f475Smrg    int coord;
3235d522f475Smrg    CELL cell;
3236d522f475Smrg
3237d522f475Smrg    if (SendMousePosition(xw, event))
3238d522f475Smrg	return;
3239d522f475Smrg
3240d522f475Smrg    screen->firstValidRow = 0;
3241d522f475Smrg    screen->lastValidRow = screen->max_row;
3242d522f475Smrg#if OPT_READLINE
3243f2e35a3aSmrg    if (OverrideEvent(event)
3244d522f475Smrg	|| event->xbutton.button != Button3
3245d522f475Smrg	|| !(SCREEN_FLAG(screen, dclick3_deletes)))
3246d522f475Smrg#endif
3247956cc18dSsnj	screen->selectUnit = EvalSelectUnit(xw,
3248d522f475Smrg					    event->xbutton.time,
3249d522f475Smrg					    screen->selectUnit,
3250d522f475Smrg					    event->xbutton.button);
3251d522f475Smrg    screen->replyToEmacs = False;
3252d522f475Smrg
3253d522f475Smrg#if OPT_READLINE
3254f2e35a3aSmrg    CheckSecondPress3(xw, screen, event);
3255d522f475Smrg#endif
3256d522f475Smrg
3257d522f475Smrg    if (screen->numberOfClicks == 1
3258f2e35a3aSmrg	|| (SCREEN_FLAG(screen, dclick3_deletes)
3259f2e35a3aSmrg	    && !OverrideEvent(event))) {
3260d522f475Smrg	/* Save existing selection so we can reestablish it if the guy
3261d522f475Smrg	   extends past the other end of the selection */
3262d522f475Smrg	screen->saveStartR = screen->startExt = screen->startRaw;
3263d522f475Smrg	screen->saveEndR = screen->endExt = screen->endRaw;
3264d522f475Smrg    } else {
3265d522f475Smrg	/* He just needed the selection mode changed, use old values. */
3266d522f475Smrg	screen->startExt = screen->startRaw = screen->saveStartR;
3267d522f475Smrg	screen->endExt = screen->endRaw = screen->saveEndR;
3268d522f475Smrg    }
3269d522f475Smrg    if (use_cursor_loc) {
3270d522f475Smrg	cell = screen->cursorp;
3271d522f475Smrg    } else {
3272d522f475Smrg	PointToCELL(screen, event->xbutton.y, event->xbutton.x, &cell);
3273d522f475Smrg    }
3274d522f475Smrg    coord = Coordinate(screen, &cell);
3275d522f475Smrg
3276d522f475Smrg    if (Abs(coord - Coordinate(screen, &(screen->startSel)))
3277d522f475Smrg	< Abs(coord - Coordinate(screen, &(screen->endSel)))
3278d522f475Smrg	|| coord < Coordinate(screen, &(screen->startSel))) {
3279d522f475Smrg	/* point is close to left side of selection */
3280d522f475Smrg	screen->eventMode = LEFTEXTENSION;
3281d522f475Smrg	screen->startExt = cell;
3282d522f475Smrg    } else {
3283d522f475Smrg	/* point is close to left side of selection */
3284d522f475Smrg	screen->eventMode = RIGHTEXTENSION;
3285d522f475Smrg	screen->endExt = cell;
3286d522f475Smrg    }
3287f2e35a3aSmrg    ComputeSelect(xw, &(screen->startExt), &(screen->endExt), True, True);
3288d522f475Smrg
3289d522f475Smrg#if OPT_READLINE
3290d522f475Smrg    if (!isSameCELL(&(screen->startSel), &(screen->endSel)))
3291d522f475Smrg	ExtendingSelection = 1;
3292d522f475Smrg#endif
3293d522f475Smrg}
3294d522f475Smrg
3295d522f475Smrgstatic void
3296e0a2b6dfSmrgExtendExtend(XtermWidget xw, const CELL *cell)
3297d522f475Smrg{
3298956cc18dSsnj    TScreen *screen = TScreenOf(xw);
3299d522f475Smrg    int coord = Coordinate(screen, cell);
3300d522f475Smrg
3301d522f475Smrg    TRACE(("ExtendExtend row=%d, col=%d\n", cell->row, cell->col));
3302d522f475Smrg    if (screen->eventMode == LEFTEXTENSION
3303d522f475Smrg	&& ((coord + (screen->selectUnit != Select_CHAR))
3304d522f475Smrg	    > Coordinate(screen, &(screen->endSel)))) {
3305d522f475Smrg	/* Whoops, he's changed his mind.  Do RIGHTEXTENSION */
3306d522f475Smrg	screen->eventMode = RIGHTEXTENSION;
3307d522f475Smrg	screen->startExt = screen->saveStartR;
3308d522f475Smrg    } else if (screen->eventMode == RIGHTEXTENSION
3309d522f475Smrg	       && coord < Coordinate(screen, &(screen->startSel))) {
3310d522f475Smrg	/* Whoops, he's changed his mind.  Do LEFTEXTENSION */
3311d522f475Smrg	screen->eventMode = LEFTEXTENSION;
3312d522f475Smrg	screen->endExt = screen->saveEndR;
3313d522f475Smrg    }
3314d522f475Smrg    if (screen->eventMode == LEFTEXTENSION) {
3315d522f475Smrg	screen->startExt = *cell;
3316d522f475Smrg    } else {
3317d522f475Smrg	screen->endExt = *cell;
3318d522f475Smrg    }
3319f2e35a3aSmrg    ComputeSelect(xw, &(screen->startExt), &(screen->endExt), False, True);
3320d522f475Smrg
3321d522f475Smrg#if OPT_READLINE
3322d522f475Smrg    if (!isSameCELL(&(screen->startSel), &(screen->endSel)))
3323d522f475Smrg	ExtendingSelection = 1;
3324d522f475Smrg#endif
3325d522f475Smrg}
3326d522f475Smrg
3327d522f475Smrgvoid
3328d522f475SmrgHandleStartExtend(Widget w,
3329894e0ac8Smrg		  XEvent *event,	/* must be XButtonEvent* */
3330e0a2b6dfSmrg		  String *params,	/* unused */
3331d522f475Smrg		  Cardinal *num_params)		/* unused */
3332d522f475Smrg{
3333956cc18dSsnj    XtermWidget xw;
3334956cc18dSsnj
3335956cc18dSsnj    if ((xw = getXtermWidget(w)) != 0) {
3336f2e35a3aSmrg	TRACE_EVENT("HandleStartExtend", event, params, num_params);
3337956cc18dSsnj	do_start_extend(xw, event, params, num_params, False);
3338956cc18dSsnj    }
3339d522f475Smrg}
3340d522f475Smrg
3341d522f475Smrgvoid
3342d522f475SmrgHandleKeyboardStartExtend(Widget w,
3343894e0ac8Smrg			  XEvent *event,	/* must be XButtonEvent* */
3344e0a2b6dfSmrg			  String *params,	/* unused */
3345d522f475Smrg			  Cardinal *num_params)		/* unused */
3346d522f475Smrg{
3347956cc18dSsnj    XtermWidget xw;
3348956cc18dSsnj
3349956cc18dSsnj    if ((xw = getXtermWidget(w)) != 0) {
3350f2e35a3aSmrg	TRACE_EVENT("HandleKeyboardStartExtend", event, params, num_params);
3351956cc18dSsnj	do_start_extend(xw, event, params, num_params, True);
3352956cc18dSsnj    }
3353d522f475Smrg}
3354d522f475Smrg
3355d522f475Smrgvoid
3356e0a2b6dfSmrgScrollSelection(TScreen *screen, int amount, Bool always)
3357d522f475Smrg{
3358d522f475Smrg    int minrow = INX2ROW(screen, -screen->savedlines);
3359d522f475Smrg    int maxrow = INX2ROW(screen, screen->max_row);
3360d522f475Smrg    int maxcol = screen->max_col;
3361d522f475Smrg
3362d522f475Smrg#define scroll_update_one(cell) \
3363d522f475Smrg	(cell)->row += amount; \
3364d522f475Smrg	if ((cell)->row < minrow) { \
3365d522f475Smrg	    (cell)->row = minrow; \
3366d522f475Smrg	    (cell)->col = 0; \
3367d522f475Smrg	} \
3368d522f475Smrg	if ((cell)->row > maxrow) { \
3369d522f475Smrg	    (cell)->row = maxrow; \
3370d522f475Smrg	    (cell)->col = maxcol; \
3371d522f475Smrg	}
3372d522f475Smrg
3373d522f475Smrg    scroll_update_one(&(screen->startRaw));
3374d522f475Smrg    scroll_update_one(&(screen->endRaw));
3375d522f475Smrg    scroll_update_one(&(screen->startSel));
3376d522f475Smrg    scroll_update_one(&(screen->endSel));
3377d522f475Smrg
3378d522f475Smrg    scroll_update_one(&(screen->rawPos));
3379d522f475Smrg
3380d522f475Smrg    /*
3381d522f475Smrg     * If we are told to scroll the selection but it lies outside the scrolling
3382d522f475Smrg     * margins, then that could cause the selection to move (bad).  It is not
3383d522f475Smrg     * simple to fix, because this function is called both for the scrollbar
3384d522f475Smrg     * actions as well as application scrolling.  The 'always' flag is set in
3385d522f475Smrg     * the former case.  The rest of the logic handles the latter.
3386d522f475Smrg     */
3387d522f475Smrg    if (ScrnHaveSelection(screen)) {
3388d522f475Smrg	int adjust;
3389d522f475Smrg
3390d522f475Smrg	adjust = ROW2INX(screen, screen->startH.row);
3391d522f475Smrg	if (always
33920bd37d32Smrg	    || !ScrnHaveRowMargins(screen)
33930bd37d32Smrg	    || ScrnIsRowInMargins(screen, adjust)) {
3394d522f475Smrg	    scroll_update_one(&screen->startH);
3395d522f475Smrg	}
3396d522f475Smrg	adjust = ROW2INX(screen, screen->endH.row);
3397d522f475Smrg	if (always
33980bd37d32Smrg	    || !ScrnHaveRowMargins(screen)
33990bd37d32Smrg	    || ScrnIsRowInMargins(screen, adjust)) {
3400d522f475Smrg	    scroll_update_one(&screen->endH);
3401d522f475Smrg	}
3402d522f475Smrg    }
3403d522f475Smrg
3404d522f475Smrg    screen->startHCoord = Coordinate(screen, &screen->startH);
3405d522f475Smrg    screen->endHCoord = Coordinate(screen, &screen->endH);
3406d522f475Smrg}
3407d522f475Smrg
3408d522f475Smrg/*ARGSUSED*/
3409d522f475Smrgvoid
3410f2e35a3aSmrgResizeSelection(TScreen *screen, int rows, int cols)
3411d522f475Smrg{
3412d522f475Smrg    rows--;			/* decr to get 0-max */
3413d522f475Smrg    cols--;
3414d522f475Smrg
3415d522f475Smrg    if (screen->startRaw.row > rows)
3416d522f475Smrg	screen->startRaw.row = rows;
3417d522f475Smrg    if (screen->startSel.row > rows)
3418d522f475Smrg	screen->startSel.row = rows;
3419d522f475Smrg    if (screen->endRaw.row > rows)
3420d522f475Smrg	screen->endRaw.row = rows;
3421d522f475Smrg    if (screen->endSel.row > rows)
3422d522f475Smrg	screen->endSel.row = rows;
3423d522f475Smrg    if (screen->rawPos.row > rows)
3424d522f475Smrg	screen->rawPos.row = rows;
3425d522f475Smrg
3426d522f475Smrg    if (screen->startRaw.col > cols)
3427d522f475Smrg	screen->startRaw.col = cols;
3428d522f475Smrg    if (screen->startSel.col > cols)
3429d522f475Smrg	screen->startSel.col = cols;
3430d522f475Smrg    if (screen->endRaw.col > cols)
3431d522f475Smrg	screen->endRaw.col = cols;
3432d522f475Smrg    if (screen->endSel.col > cols)
3433d522f475Smrg	screen->endSel.col = cols;
3434d522f475Smrg    if (screen->rawPos.col > cols)
3435d522f475Smrg	screen->rawPos.col = cols;
3436d522f475Smrg}
3437d522f475Smrg
3438d522f475Smrg#if OPT_WIDE_CHARS
3439f2e35a3aSmrg#define isWideCell(row, col) isWideFrg((int)XTERM_CELL(row, col))
3440d522f475Smrg#endif
3441d522f475Smrg
3442d522f475Smrgstatic void
3443e0a2b6dfSmrgPointToCELL(TScreen *screen,
3444d522f475Smrg	    int y,
3445d522f475Smrg	    int x,
3446e0a2b6dfSmrg	    CELL *cell)
3447d522f475Smrg/* Convert pixel coordinates to character coordinates.
3448d522f475Smrg   Rows are clipped between firstValidRow and lastValidRow.
3449d522f475Smrg   Columns are clipped between to be 0 or greater, but are not clipped to some
3450d522f475Smrg       maximum value. */
3451d522f475Smrg{
3452d522f475Smrg    cell->row = (y - screen->border) / FontHeight(screen);
3453d522f475Smrg    if (cell->row < screen->firstValidRow)
3454d522f475Smrg	cell->row = screen->firstValidRow;
3455d522f475Smrg    else if (cell->row > screen->lastValidRow)
3456d522f475Smrg	cell->row = screen->lastValidRow;
3457d522f475Smrg    cell->col = (x - OriginX(screen)) / FontWidth(screen);
3458d522f475Smrg    if (cell->col < 0)
3459d522f475Smrg	cell->col = 0;
3460d522f475Smrg    else if (cell->col > MaxCols(screen)) {
3461d522f475Smrg	cell->col = MaxCols(screen);
3462d522f475Smrg    }
3463d522f475Smrg#if OPT_WIDE_CHARS
3464d522f475Smrg    /*
3465d522f475Smrg     * If we got a click on the right half of a doublewidth character,
3466d522f475Smrg     * pretend it happened on the left half.
3467d522f475Smrg     */
3468d522f475Smrg    if (cell->col > 0
3469d522f475Smrg	&& isWideCell(cell->row, cell->col - 1)
3470d522f475Smrg	&& (XTERM_CELL(cell->row, cell->col) == HIDDEN_CHAR)) {
3471d522f475Smrg	cell->col -= 1;
3472d522f475Smrg    }
3473d522f475Smrg#endif
3474d522f475Smrg}
3475d522f475Smrg
3476d522f475Smrg/*
3477d522f475Smrg * Find the last column at which text was drawn on the given row.
3478d522f475Smrg */
3479d522f475Smrgstatic int
348001037d57SmrgLastTextCol(TScreen *screen, CLineData *ld, int row)
3481d522f475Smrg{
348220d2c4d2Smrg    int i = -1;
3483d522f475Smrg
348420d2c4d2Smrg    if (ld != 0) {
348520d2c4d2Smrg	if (okScrnRow(screen, row)) {
34862e4f8982Smrg	    const IAttr *ch;
348720d2c4d2Smrg	    for (i = screen->max_col,
348820d2c4d2Smrg		 ch = ld->attribs + i;
348920d2c4d2Smrg		 i >= 0 && !(*ch & CHARDRAWN);
349020d2c4d2Smrg		 ch--, i--) {
349120d2c4d2Smrg		;
349220d2c4d2Smrg	    }
3493d522f475Smrg#if OPT_DEC_CHRSET
349420d2c4d2Smrg	    if (CSET_DOUBLE(GetLineDblCS(ld))) {
349520d2c4d2Smrg		i *= 2;
349620d2c4d2Smrg	    }
3497d522f475Smrg#endif
349820d2c4d2Smrg	}
3499d522f475Smrg    }
3500d522f475Smrg    return (i);
3501d522f475Smrg}
3502d522f475Smrg
3503d522f475Smrg#if !OPT_WIDE_CHARS
3504d522f475Smrg/*
3505d522f475Smrg** double click table for cut and paste in 8 bits
3506d522f475Smrg**
3507d522f475Smrg** This table is divided in four parts :
3508d522f475Smrg**
3509d522f475Smrg**	- control characters	[0,0x1f] U [0x80,0x9f]
3510d522f475Smrg**	- separators		[0x20,0x3f] U [0xa0,0xb9]
3511d522f475Smrg**	- binding characters	[0x40,0x7f] U [0xc0,0xff]
3512d522f475Smrg**	- exceptions
3513d522f475Smrg*/
3514d522f475Smrg/* *INDENT-OFF* */
3515d522f475Smrgstatic int charClass[256] =
3516d522f475Smrg{
3517d522f475Smrg/* NUL  SOH  STX  ETX  EOT  ENQ  ACK  BEL */
3518d522f475Smrg    32,  1,    1,   1,   1,   1,   1,   1,
35190bd37d32Smrg/*  BS   HT   NL   VT   FF   CR   SO   SI */
3520d522f475Smrg     1,  32,   1,   1,   1,   1,   1,   1,
3521d522f475Smrg/* DLE  DC1  DC2  DC3  DC4  NAK  SYN  ETB */
3522d522f475Smrg     1,   1,   1,   1,   1,   1,   1,   1,
3523d522f475Smrg/* CAN   EM  SUB  ESC   FS   GS   RS   US */
3524d522f475Smrg     1,   1,   1,   1,   1,   1,   1,   1,
3525d522f475Smrg/*  SP    !    "    #    $    %    &    ' */
3526d522f475Smrg    32,  33,  34,  35,  36,  37,  38,  39,
3527d522f475Smrg/*   (    )    *    +    ,    -    .    / */
3528d522f475Smrg    40,  41,  42,  43,  44,  45,  46,  47,
3529d522f475Smrg/*   0    1    2    3    4    5    6    7 */
3530d522f475Smrg    48,  48,  48,  48,  48,  48,  48,  48,
3531d522f475Smrg/*   8    9    :    ;    <    =    >    ? */
3532d522f475Smrg    48,  48,  58,  59,  60,  61,  62,  63,
3533d522f475Smrg/*   @    A    B    C    D    E    F    G */
3534d522f475Smrg    64,  48,  48,  48,  48,  48,  48,  48,
3535d522f475Smrg/*   H    I    J    K    L    M    N    O */
3536d522f475Smrg    48,  48,  48,  48,  48,  48,  48,  48,
3537d522f475Smrg/*   P    Q    R    S    T    U    V    W */
3538d522f475Smrg    48,  48,  48,  48,  48,  48,  48,  48,
3539d522f475Smrg/*   X    Y    Z    [    \    ]    ^    _ */
3540d522f475Smrg    48,  48,  48,  91,  92,  93,  94,  48,
3541d522f475Smrg/*   `    a    b    c    d    e    f    g */
3542d522f475Smrg    96,  48,  48,  48,  48,  48,  48,  48,
3543d522f475Smrg/*   h    i    j    k    l    m    n    o */
3544d522f475Smrg    48,  48,  48,  48,  48,  48,  48,  48,
3545d522f475Smrg/*   p    q    r    s    t    u    v    w */
3546d522f475Smrg    48,  48,  48,  48,  48,  48,  48,  48,
3547d522f475Smrg/*   x    y    z    {    |    }    ~  DEL */
3548d522f475Smrg    48,  48,  48, 123, 124, 125, 126,   1,
3549d522f475Smrg/* x80  x81  x82  x83  IND  NEL  SSA  ESA */
3550d522f475Smrg    1,    1,   1,   1,   1,   1,   1,   1,
3551d522f475Smrg/* HTS  HTJ  VTS  PLD  PLU   RI  SS2  SS3 */
3552d522f475Smrg    1,    1,   1,   1,   1,   1,   1,   1,
3553d522f475Smrg/* DCS  PU1  PU2  STS  CCH   MW  SPA  EPA */
3554d522f475Smrg    1,    1,   1,   1,   1,   1,   1,   1,
3555d522f475Smrg/* x98  x99  x9A  CSI   ST  OSC   PM  APC */
3556d522f475Smrg    1,    1,   1,   1,   1,   1,   1,   1,
3557d522f475Smrg/*   -    i   c/    L   ox   Y-    |   So */
3558d522f475Smrg    160, 161, 162, 163, 164, 165, 166, 167,
3559d522f475Smrg/*  ..   c0   ip   <<    _        R0    - */
3560d522f475Smrg    168, 169, 170, 171, 172, 173, 174, 175,
3561d522f475Smrg/*   o   +-    2    3    '    u   q|    . */
3562d522f475Smrg    176, 177, 178, 179, 180, 181, 182, 183,
3563d522f475Smrg/*   ,    1    2   >>  1/4  1/2  3/4    ? */
3564d522f475Smrg    184, 185, 186, 187, 188, 189, 190, 191,
3565d522f475Smrg/*  A`   A'   A^   A~   A:   Ao   AE   C, */
3566d522f475Smrg     48,  48,  48,  48,  48,  48,  48,  48,
3567d522f475Smrg/*  E`   E'   E^   E:   I`   I'   I^   I: */
3568d522f475Smrg     48,  48,  48,  48,  48,  48,  48,  48,
3569d522f475Smrg/*  D-   N~   O`   O'   O^   O~   O:    X */
3570d522f475Smrg     48,  48,  48,  48,  48,  48,  48, 215,
3571d522f475Smrg/*  O/   U`   U'   U^   U:   Y'    P    B */
3572d522f475Smrg     48,  48,  48,  48,  48,  48,  48,  48,
3573d522f475Smrg/*  a`   a'   a^   a~   a:   ao   ae   c, */
3574d522f475Smrg     48,  48,  48,  48,  48,  48,  48,  48,
3575d522f475Smrg/*  e`   e'   e^   e:    i`  i'   i^   i: */
3576d522f475Smrg     48,  48,  48,  48,  48,  48,  48,  48,
3577d522f475Smrg/*   d   n~   o`   o'   o^   o~   o:   -: */
3578d522f475Smrg     48,  48,  48,  48,  48,  48,  48, 247,
3579d522f475Smrg/*  o/   u`   u'   u^   u:   y'    P   y: */
3580d522f475Smrg     48,  48,  48,  48,  48,  48,  48,  48};
3581d522f475Smrg/* *INDENT-ON* */
3582d522f475Smrg
3583d522f475Smrgint
3584d522f475SmrgSetCharacterClassRange(int low,	/* in range of [0..255] */
3585d522f475Smrg		       int high,
3586d522f475Smrg		       int value)	/* arbitrary */
3587d522f475Smrg{
3588d522f475Smrg
3589d522f475Smrg    if (low < 0 || high > 255 || high < low)
3590d522f475Smrg	return (-1);
3591d522f475Smrg
3592d522f475Smrg    for (; low <= high; low++)
3593d522f475Smrg	charClass[low] = value;
3594d522f475Smrg
3595d522f475Smrg    return (0);
3596d522f475Smrg}
3597d522f475Smrg#endif
3598d522f475Smrg
3599d522f475Smrgstatic int
3600e0a2b6dfSmrgclass_of(LineData *ld, CELL *cell)
3601d522f475Smrg{
3602d522f475Smrg    CELL temp = *cell;
36030bd37d32Smrg    int result = 0;
3604d522f475Smrg
3605d522f475Smrg#if OPT_DEC_CHRSET
3606956cc18dSsnj    if (CSET_DOUBLE(GetLineDblCS(ld))) {
3607d522f475Smrg	temp.col /= 2;
3608d522f475Smrg    }
3609d522f475Smrg#endif
36100bd37d32Smrg    if (temp.col < (int) ld->lineSize)
36110bd37d32Smrg	result = CharacterClass((int) (ld->charData[temp.col]));
36120bd37d32Smrg    return result;
3613d522f475Smrg}
3614956cc18dSsnj
3615956cc18dSsnj#if OPT_WIDE_CHARS
3616956cc18dSsnj#define CClassSelects(name, cclass) \
3617956cc18dSsnj	 (CClassOf(name) == cclass \
3618956cc18dSsnj	 || XTERM_CELL(screen->name.row, screen->name.col) == HIDDEN_CHAR)
3619d522f475Smrg#else
3620956cc18dSsnj#define CClassSelects(name, cclass) \
3621956cc18dSsnj	 (class_of(ld.name, &((screen->name))) == cclass)
3622d522f475Smrg#endif
3623d522f475Smrg
3624956cc18dSsnj#define CClassOf(name) class_of(ld.name, &((screen->name)))
3625956cc18dSsnj
3626913cc679Smrg#if OPT_REPORT_CCLASS
3627913cc679Smrgstatic int
3628913cc679Smrgshow_cclass_range(int lo, int hi)
3629913cc679Smrg{
3630913cc679Smrg    int cclass = CharacterClass(lo);
3631913cc679Smrg    int ident = (cclass == lo);
3632913cc679Smrg    int more = 0;
3633913cc679Smrg    if (ident) {
3634913cc679Smrg	int ch;
3635913cc679Smrg	for (ch = lo + 1; ch <= hi; ch++) {
3636913cc679Smrg	    if (CharacterClass(ch) != ch) {
3637913cc679Smrg		ident = 0;
3638913cc679Smrg		break;
3639913cc679Smrg	    }
3640913cc679Smrg	}
3641913cc679Smrg	if (ident && (hi < 255)) {
3642913cc679Smrg	    ch = hi + 1;
3643913cc679Smrg	    if (CharacterClass(ch) == ch) {
3644913cc679Smrg		if (ch >= 255 || CharacterClass(ch + 1) != ch) {
3645913cc679Smrg		    more = 1;
3646913cc679Smrg		}
3647913cc679Smrg	    }
3648913cc679Smrg	}
3649913cc679Smrg    }
3650913cc679Smrg    if (!more) {
3651913cc679Smrg	if (lo == hi) {
3652913cc679Smrg	    printf("\t%d", lo);
3653913cc679Smrg	} else {
3654913cc679Smrg	    printf("\t%d-%d", lo, hi);
3655913cc679Smrg	}
3656913cc679Smrg	if (!ident)
3657913cc679Smrg	    printf(":%d", cclass);
3658913cc679Smrg	if (hi < 255)
3659913cc679Smrg	    printf(", \\");
3660913cc679Smrg	printf("\n");
3661913cc679Smrg    }
3662913cc679Smrg    return !more;
3663913cc679Smrg}
3664913cc679Smrg
3665913cc679Smrgvoid
3666913cc679Smrgreport_char_class(XtermWidget xw)
3667913cc679Smrg{
3668913cc679Smrg    /* simple table, to match documentation */
3669913cc679Smrg    static const char charnames[] =
3670913cc679Smrg    "NUL\0" "SOH\0" "STX\0" "ETX\0" "EOT\0" "ENQ\0" "ACK\0" "BEL\0"
3671913cc679Smrg    " BS\0" " HT\0" " NL\0" " VT\0" " NP\0" " CR\0" " SO\0" " SI\0"
3672913cc679Smrg    "DLE\0" "DC1\0" "DC2\0" "DC3\0" "DC4\0" "NAK\0" "SYN\0" "ETB\0"
3673913cc679Smrg    "CAN\0" " EM\0" "SUB\0" "ESC\0" " FS\0" " GS\0" " RS\0" " US\0"
3674913cc679Smrg    " SP\0" "  !\0" "  \"\0" "  #\0" "  $\0" "  %\0" "  &\0" "  '\0"
3675913cc679Smrg    "  (\0" "  )\0" "  *\0" "  +\0" "  ,\0" "  -\0" "  .\0" "  /\0"
3676913cc679Smrg    "  0\0" "  1\0" "  2\0" "  3\0" "  4\0" "  5\0" "  6\0" "  7\0"
3677913cc679Smrg    "  8\0" "  9\0" "  :\0" "  ;\0" "  <\0" "  =\0" "  >\0" "  ?\0"
3678913cc679Smrg    "  @\0" "  A\0" "  B\0" "  C\0" "  D\0" "  E\0" "  F\0" "  G\0"
3679913cc679Smrg    "  H\0" "  I\0" "  J\0" "  K\0" "  L\0" "  M\0" "  N\0" "  O\0"
3680913cc679Smrg    "  P\0" "  Q\0" "  R\0" "  S\0" "  T\0" "  U\0" "  V\0" "  W\0"
3681913cc679Smrg    "  X\0" "  Y\0" "  Z\0" "  [\0" "  \\\0" "  ]\0" "  ^\0" "  _\0"
3682913cc679Smrg    "  `\0" "  a\0" "  b\0" "  c\0" "  d\0" "  e\0" "  f\0" "  g\0"
3683913cc679Smrg    "  h\0" "  i\0" "  j\0" "  k\0" "  l\0" "  m\0" "  n\0" "  o\0"
3684913cc679Smrg    "  p\0" "  q\0" "  r\0" "  s\0" "  t\0" "  u\0" "  v\0" "  w\0"
3685913cc679Smrg    "  x\0" "  y\0" "  z\0" "  {\0" "  |\0" "  }\0" "  ~\0" "DEL\0"
3686913cc679Smrg    "x80\0" "x81\0" "x82\0" "x83\0" "IND\0" "NEL\0" "SSA\0" "ESA\0"
3687913cc679Smrg    "HTS\0" "HTJ\0" "VTS\0" "PLD\0" "PLU\0" " RI\0" "SS2\0" "SS3\0"
3688913cc679Smrg    "DCS\0" "PU1\0" "PU2\0" "STS\0" "CCH\0" " MW\0" "SPA\0" "EPA\0"
3689913cc679Smrg    "x98\0" "x99\0" "x9A\0" "CSI\0" " ST\0" "OSC\0" " PM\0" "APC\0"
3690913cc679Smrg    "  -\0" "  i\0" " c/\0" "  L\0" " ox\0" " Y-\0" "  |\0" " So\0"
3691913cc679Smrg    " ..\0" " c0\0" " ip\0" " <<\0" "  _\0" "   \0" " R0\0" "  -\0"
3692913cc679Smrg    "  o\0" " +-\0" "  2\0" "  3\0" "  '\0" "  u\0" " q|\0" "  .\0"
3693913cc679Smrg    "  ,\0" "  1\0" "  2\0" " >>\0" "1/4\0" "1/2\0" "3/4\0" "  ?\0"
3694913cc679Smrg    " A`\0" " A'\0" " A^\0" " A~\0" " A:\0" " Ao\0" " AE\0" " C,\0"
3695913cc679Smrg    " E`\0" " E'\0" " E^\0" " E:\0" " I`\0" " I'\0" " I^\0" " I:\0"
3696913cc679Smrg    " D-\0" " N~\0" " O`\0" " O'\0" " O^\0" " O~\0" " O:\0" "  X\0"
3697913cc679Smrg    " O/\0" " U`\0" " U'\0" " U^\0" " U:\0" " Y'\0" "  P\0" "  B\0"
3698913cc679Smrg    " a`\0" " a'\0" " a^\0" " a~\0" " a:\0" " ao\0" " ae\0" " c,\0"
3699913cc679Smrg    " e`\0" " e'\0" " e^\0" " e:\0" " i`\0" " i'\0" " i^\0" " i:\0"
3700913cc679Smrg    "  d\0" " n~\0" " o`\0" " o'\0" " o^\0" " o~\0" " o:\0" " -:\0"
3701913cc679Smrg    " o/\0" " u`\0" " u'\0" " u^\0" " u:\0" " y'\0" "  P\0" " y:\0";
3702913cc679Smrg    int ch, dh;
3703913cc679Smrg    int class_p;
3704913cc679Smrg
3705913cc679Smrg    (void) xw;
3706913cc679Smrg
3707913cc679Smrg    printf("static int charClass[256] = {\n");
3708913cc679Smrg    for (ch = 0; ch < 256; ++ch) {
3709913cc679Smrg	const char *s = charnames + (ch * 4);
3710913cc679Smrg	if ((ch & 7) == 0)
3711913cc679Smrg	    printf("/*");
3712913cc679Smrg	printf(" %s ", s);
3713913cc679Smrg	if (((ch + 1) & 7) == 0) {
3714913cc679Smrg	    printf("*/\n  ");
3715913cc679Smrg	    for (dh = ch - 7; dh <= ch; ++dh) {
3716913cc679Smrg		printf(" %3d%s", CharacterClass(dh), dh == 255 ? "};" : ",");
3717913cc679Smrg	    }
3718913cc679Smrg	    printf("\n");
3719913cc679Smrg	}
3720913cc679Smrg    }
3721913cc679Smrg
3722913cc679Smrg    /* print the table as if it were the charClass resource */
3723913cc679Smrg    printf("\n");
3724913cc679Smrg    printf("The table is equivalent to this \"charClass\" resource:\n");
3725913cc679Smrg    class_p = CharacterClass(dh = 0);
3726913cc679Smrg    for (ch = 0; ch < 256; ++ch) {
3727913cc679Smrg	int class_c = CharacterClass(ch);
3728913cc679Smrg	if (class_c != class_p) {
3729913cc679Smrg	    if (show_cclass_range(dh, ch - 1)) {
3730913cc679Smrg		dh = ch;
3731913cc679Smrg		class_p = class_c;
3732913cc679Smrg	    }
3733913cc679Smrg	}
3734913cc679Smrg    }
3735913cc679Smrg    if (dh < 255) {
3736913cc679Smrg	show_cclass_range(dh, 255);
3737913cc679Smrg    }
3738913cc679Smrg
3739913cc679Smrg    if_OPT_WIDE_CHARS(TScreenOf(xw), {
3740913cc679Smrg	/* if this is a wide-character configuration, print all intervals */
3741913cc679Smrg	report_wide_char_class();
3742913cc679Smrg    });
3743913cc679Smrg}
3744913cc679Smrg#endif
3745913cc679Smrg
3746d522f475Smrg/*
3747d522f475Smrg * If the given column is past the end of text on the given row, bump to the
3748d522f475Smrg * beginning of the next line.
3749d522f475Smrg */
3750d522f475Smrgstatic Boolean
3751e0a2b6dfSmrgokPosition(TScreen *screen,
3752e0a2b6dfSmrg	   LineData **ld,
3753e0a2b6dfSmrg	   CELL *cell)
3754d522f475Smrg{
375520d2c4d2Smrg    Boolean result = True;
375620d2c4d2Smrg
375720d2c4d2Smrg    if (cell->row > screen->max_row) {
375820d2c4d2Smrg	result = False;
3759f2e35a3aSmrg	TRACE(("okPosition cell row %d > screen max %d\n", cell->row, screen->max_row));
376020d2c4d2Smrg    } else if (cell->col > (LastTextCol(screen, *ld, cell->row) + 1)) {
3761f2e35a3aSmrg	TRACE(("okPosition cell col %d > screen max %d\n", cell->col,
3762f2e35a3aSmrg	       (LastTextCol(screen, *ld, cell->row) + 1)));
376320d2c4d2Smrg	if (cell->row < screen->max_row) {
3764f2e35a3aSmrg	    TRACE(("okPosition cell row %d < screen max %d\n", cell->row, screen->max_row));
376520d2c4d2Smrg	    cell->col = 0;
376620d2c4d2Smrg	    *ld = GET_LINEDATA(screen, ++cell->row);
376720d2c4d2Smrg	    result = False;
376820d2c4d2Smrg	}
3769d522f475Smrg    }
377020d2c4d2Smrg    return result;
3771d522f475Smrg}
3772d522f475Smrg
3773d522f475Smrgstatic void
3774e0a2b6dfSmrgtrimLastLine(TScreen *screen,
3775e0a2b6dfSmrg	     LineData **ld,
3776e0a2b6dfSmrg	     CELL *last)
3777d522f475Smrg{
377820d2c4d2Smrg    if (screen->cutNewline && last->row < screen->max_row) {
3779d522f475Smrg	last->col = 0;
3780956cc18dSsnj	*ld = GET_LINEDATA(screen, ++last->row);
3781d522f475Smrg    } else {
3782956cc18dSsnj	last->col = LastTextCol(screen, *ld, last->row) + 1;
3783d522f475Smrg    }
3784d522f475Smrg}
3785d522f475Smrg
3786d522f475Smrg#if OPT_SELECT_REGEX
3787d522f475Smrg/*
3788d522f475Smrg * Returns the first row of a wrapped line.
3789d522f475Smrg */
3790d522f475Smrgstatic int
3791e0a2b6dfSmrgfirstRowOfLine(TScreen *screen, int row, Bool visible)
3792d522f475Smrg{
3793956cc18dSsnj    LineData *ld = 0;
3794d522f475Smrg    int limit = visible ? 0 : -screen->savedlines;
3795d522f475Smrg
3796d522f475Smrg    while (row > limit &&
3797956cc18dSsnj	   (ld = GET_LINEDATA(screen, row - 1)) != 0 &&
3798956cc18dSsnj	   LineTstWrapped(ld)) {
3799d522f475Smrg	--row;
3800956cc18dSsnj    }
3801d522f475Smrg    return row;
3802d522f475Smrg}
3803d522f475Smrg
3804d522f475Smrg/*
3805d522f475Smrg * Returns the last row of a wrapped line.
3806d522f475Smrg */
3807d522f475Smrgstatic int
3808e0a2b6dfSmrglastRowOfLine(TScreen *screen, int row)
3809d522f475Smrg{
3810956cc18dSsnj    LineData *ld;
3811956cc18dSsnj
3812d522f475Smrg    while (row < screen->max_row &&
3813956cc18dSsnj	   (ld = GET_LINEDATA(screen, row)) != 0 &&
3814956cc18dSsnj	   LineTstWrapped(ld)) {
3815d522f475Smrg	++row;
3816956cc18dSsnj    }
3817d522f475Smrg    return row;
3818d522f475Smrg}
3819d522f475Smrg
3820d522f475Smrg/*
3821d522f475Smrg * Returns the number of cells on the range of rows.
3822d522f475Smrg */
3823d522f475Smrgstatic unsigned
3824e0a2b6dfSmrglengthOfLines(TScreen *screen, int firstRow, int lastRow)
3825d522f475Smrg{
3826d522f475Smrg    unsigned length = 0;
3827d522f475Smrg    int n;
3828d522f475Smrg
3829d522f475Smrg    for (n = firstRow; n <= lastRow; ++n) {
3830956cc18dSsnj	LineData *ld = GET_LINEDATA(screen, n);
3831956cc18dSsnj	int value = LastTextCol(screen, ld, n);
3832d522f475Smrg	if (value >= 0)
38332eaa94a1Schristos	    length += (unsigned) (value + 1);
3834d522f475Smrg    }
3835d522f475Smrg    return length;
3836d522f475Smrg}
3837d522f475Smrg
3838d522f475Smrg/*
3839d522f475Smrg * Make a copy of the wrapped-line which corresponds to the given row as a
3840d522f475Smrg * string of bytes.  Construct an index for the columns from the beginning of
3841d522f475Smrg * the line.
3842d522f475Smrg */
3843d522f475Smrgstatic char *
3844e0a2b6dfSmrgmake_indexed_text(TScreen *screen, int row, unsigned length, int *indexed)
3845d522f475Smrg{
3846d522f475Smrg    Char *result = 0;
384720d2c4d2Smrg    size_t need = (length + 1);
3848d522f475Smrg
3849d522f475Smrg    /*
3850d522f475Smrg     * Get a quick upper bound to the number of bytes needed, if the whole
3851d522f475Smrg     * string were UTF-8.
3852d522f475Smrg     */
3853d522f475Smrg    if_OPT_WIDE_CHARS(screen, {
3854956cc18dSsnj	need *= ((screen->lineExtra + 1) * 6);
3855d522f475Smrg    });
3856d522f475Smrg
3857d522f475Smrg    if ((result = TypeCallocN(Char, need + 1)) != 0) {
3858956cc18dSsnj	LineData *ld = GET_LINEDATA(screen, row);
3859d522f475Smrg	unsigned used = 0;
3860d522f475Smrg	Char *last = result;
3861d522f475Smrg
3862d522f475Smrg	do {
3863d522f475Smrg	    int col = 0;
3864956cc18dSsnj	    int limit = LastTextCol(screen, ld, row);
3865d522f475Smrg
3866d522f475Smrg	    while (col <= limit) {
3867d522f475Smrg		Char *next = last;
3868956cc18dSsnj		unsigned data = ld->charData[col];
3869d522f475Smrg
38700bd37d32Smrg		assert(col < (int) ld->lineSize);
3871d522f475Smrg		/* some internal points may not be drawn */
3872d522f475Smrg		if (data == 0)
3873d522f475Smrg		    data = ' ';
3874d522f475Smrg
3875d522f475Smrg		if_WIDE_OR_NARROW(screen, {
3876d522f475Smrg		    next = convertToUTF8(last, data);
3877d522f475Smrg		}
3878d522f475Smrg		, {
3879d522f475Smrg		    *next++ = CharOf(data);
3880d522f475Smrg		});
3881d522f475Smrg
3882d522f475Smrg		if_OPT_WIDE_CHARS(screen, {
3883956cc18dSsnj		    size_t off;
3884956cc18dSsnj		    for_each_combData(off, ld) {
3885956cc18dSsnj			data = ld->combData[off][col];
3886956cc18dSsnj			if (data == 0)
3887d522f475Smrg			    break;
3888d522f475Smrg			next = convertToUTF8(next, data);
3889d522f475Smrg		    }
3890d522f475Smrg		});
3891d522f475Smrg
389220d2c4d2Smrg		indexed[used] = (int) (last - result);
3893d522f475Smrg		*next = 0;
3894d522f475Smrg		/* TRACE(("index[%d.%d] %d:%s\n", row, used, indexed[used], last)); */
3895d522f475Smrg		last = next;
3896d522f475Smrg		++used;
3897d522f475Smrg		++col;
389820d2c4d2Smrg		indexed[used] = (int) (next - result);
3899d522f475Smrg	    }
3900d522f475Smrg	} while (used < length &&
3901956cc18dSsnj		 LineTstWrapped(ld) &&
3902956cc18dSsnj		 (ld = GET_LINEDATA(screen, ++row)) != 0 &&
3903956cc18dSsnj		 row < screen->max_row);
3904d522f475Smrg    }
3905d522f475Smrg    /* TRACE(("result:%s\n", result)); */
3906d522f475Smrg    return (char *) result;
3907d522f475Smrg}
3908d522f475Smrg
3909d522f475Smrg/*
3910d522f475Smrg * Find the column given an offset into the character string by using the
3911d522f475Smrg * index constructed in make_indexed_text().
3912d522f475Smrg */
3913d522f475Smrgstatic int
3914d522f475SmrgindexToCol(int *indexed, int len, int off)
3915d522f475Smrg{
3916d522f475Smrg    int col = 0;
3917d522f475Smrg    while (indexed[col] < len) {
3918d522f475Smrg	if (indexed[col] >= off)
3919d522f475Smrg	    break;
3920d522f475Smrg	++col;
3921d522f475Smrg    }
3922d522f475Smrg    return col;
3923d522f475Smrg}
3924d522f475Smrg
3925d522f475Smrg/*
3926d522f475Smrg * Given a row number, and a column offset from that (which may be wrapped),
3927d522f475Smrg * set the cell to the actual row/column values.
3928d522f475Smrg */
3929d522f475Smrgstatic void
3930e0a2b6dfSmrgcolumnToCell(TScreen *screen, int row, int col, CELL *cell)
3931d522f475Smrg{
3932d522f475Smrg    while (row < screen->max_row) {
393301037d57Smrg	CLineData *ld = GET_LINEDATA(screen, row);
3934956cc18dSsnj	int last = LastTextCol(screen, ld, row);
3935d522f475Smrg
3936d522f475Smrg	/* TRACE(("last(%d) = %d, have %d\n", row, last, col)); */
3937d522f475Smrg	if (col <= last) {
3938d522f475Smrg	    break;
3939d522f475Smrg	}
3940d522f475Smrg	/*
3941d522f475Smrg	 * Stop if the current row does not wrap (does not continue the current
3942d522f475Smrg	 * line).
3943d522f475Smrg	 */
3944956cc18dSsnj	if (!LineTstWrapped(ld)) {
3945d522f475Smrg	    col = last + 1;
3946d522f475Smrg	    break;
3947d522f475Smrg	}
3948d522f475Smrg	col -= (last + 1);
3949d522f475Smrg	++row;
3950d522f475Smrg    }
3951d522f475Smrg    if (col < 0)
3952d522f475Smrg	col = 0;
3953d522f475Smrg    cell->row = row;
3954d522f475Smrg    cell->col = col;
3955d522f475Smrg}
3956d522f475Smrg
3957d522f475Smrg/*
3958d522f475Smrg * Given a cell, find the corresponding column offset.
3959d522f475Smrg */
3960d522f475Smrgstatic int
3961e0a2b6dfSmrgcellToColumn(TScreen *screen, CELL *cell)
3962d522f475Smrg{
396301037d57Smrg    CLineData *ld = 0;
3964d522f475Smrg    int col = cell->col;
3965d522f475Smrg    int row = firstRowOfLine(screen, cell->row, False);
3966d522f475Smrg    while (row < cell->row) {
3967956cc18dSsnj	ld = GET_LINEDATA(screen, row);
3968956cc18dSsnj	col += LastTextCol(screen, ld, row++);
3969d522f475Smrg    }
3970956cc18dSsnj#if OPT_DEC_CHRSET
3971956cc18dSsnj    if (ld == 0)
3972956cc18dSsnj	ld = GET_LINEDATA(screen, row);
3973956cc18dSsnj    if (CSET_DOUBLE(GetLineDblCS(ld)))
3974956cc18dSsnj	col /= 2;
3975956cc18dSsnj#endif
3976d522f475Smrg    return col;
3977d522f475Smrg}
3978d522f475Smrg
3979d522f475Smrgstatic void
3980e0a2b6dfSmrgdo_select_regex(TScreen *screen, CELL *startc, CELL *endc)
3981d522f475Smrg{
3982956cc18dSsnj    LineData *ld = GET_LINEDATA(screen, startc->row);
3983d522f475Smrg    int inx = ((screen->numberOfClicks - 1) % screen->maxClicks);
3984d522f475Smrg    char *expr = screen->selectExpr[inx];
3985d522f475Smrg    regex_t preg;
3986d522f475Smrg    regmatch_t match;
3987d522f475Smrg
398801037d57Smrg    TRACE(("Select_REGEX[%d]:%s\n", inx, NonNull(expr)));
3989956cc18dSsnj    if (okPosition(screen, &ld, startc) && expr != 0) {
3990d522f475Smrg	if (regcomp(&preg, expr, REG_EXTENDED) == 0) {
3991d522f475Smrg	    int firstRow = firstRowOfLine(screen, startc->row, True);
3992d522f475Smrg	    int lastRow = lastRowOfLine(screen, firstRow);
3993d522f475Smrg	    unsigned size = lengthOfLines(screen, firstRow, lastRow);
3994d522f475Smrg	    int actual = cellToColumn(screen, startc);
39952e4f8982Smrg	    int *indexed;
3996d522f475Smrg
3997d522f475Smrg	    TRACE(("regcomp ok rows %d..%d bytes %d\n",
3998d522f475Smrg		   firstRow, lastRow, size));
3999d522f475Smrg
4000d522f475Smrg	    if ((indexed = TypeCallocN(int, size + 1)) != 0) {
40012e4f8982Smrg		char *search;
4002d522f475Smrg		if ((search = make_indexed_text(screen,
4003d522f475Smrg						firstRow,
4004d522f475Smrg						size,
4005d522f475Smrg						indexed)) != 0) {
40062eaa94a1Schristos		    int len = (int) strlen(search);
4007d522f475Smrg		    int col;
400804b94745Smrg		    int offset;
4009d522f475Smrg		    int best_col = -1;
4010d522f475Smrg		    int best_len = -1;
4011d522f475Smrg
4012913cc679Smrg		    startc->row = 0;
4013913cc679Smrg		    startc->col = 0;
4014913cc679Smrg		    endc->row = 0;
4015913cc679Smrg		    endc->col = 0;
4016913cc679Smrg
401704b94745Smrg		    for (col = 0; (offset = indexed[col]) < len; ++col) {
4018d522f475Smrg			if (regexec(&preg,
401904b94745Smrg				    search + offset,
402004b94745Smrg				    (size_t) 1, &match,
402104b94745Smrg				    col ? REG_NOTBOL : 0) == 0) {
402204b94745Smrg			    int start_inx = (int) (match.rm_so + offset);
402304b94745Smrg			    int finis_inx = (int) (match.rm_eo + offset);
4024d522f475Smrg			    int start_col = indexToCol(indexed, len, start_inx);
4025d522f475Smrg			    int finis_col = indexToCol(indexed, len, finis_inx);
4026d522f475Smrg
4027d522f475Smrg			    if (start_col <= actual &&
4028913cc679Smrg				actual <= finis_col) {
4029d522f475Smrg				int test = finis_col - start_col;
4030d522f475Smrg				if (best_len < test) {
4031d522f475Smrg				    best_len = test;
4032d522f475Smrg				    best_col = start_col;
4033d522f475Smrg				    TRACE(("match column %d len %d\n",
4034d522f475Smrg					   best_col,
4035d522f475Smrg					   best_len));
4036d522f475Smrg				}
4037d522f475Smrg			    }
4038d522f475Smrg			}
4039d522f475Smrg		    }
4040d522f475Smrg		    if (best_col >= 0) {
4041d522f475Smrg			int best_nxt = best_col + best_len;
4042d522f475Smrg			columnToCell(screen, firstRow, best_col, startc);
4043d522f475Smrg			columnToCell(screen, firstRow, best_nxt, endc);
4044d522f475Smrg			TRACE(("search::%s\n", search));
4045d522f475Smrg			TRACE(("indexed:%d..%d -> %d..%d\n",
4046d522f475Smrg			       best_col, best_nxt,
4047d522f475Smrg			       indexed[best_col],
4048d522f475Smrg			       indexed[best_nxt]));
4049d522f475Smrg			TRACE(("matched:%d:%s\n",
405004b94745Smrg			       indexed[best_nxt] -
4051d522f475Smrg			       indexed[best_col],
4052956cc18dSsnj			       visibleChars((Char *) (search + indexed[best_col]),
405304b94745Smrg					    (unsigned) (indexed[best_nxt] -
4054d522f475Smrg							indexed[best_col]))));
4055d522f475Smrg		    }
4056d522f475Smrg		    free(search);
4057d522f475Smrg		}
4058d522f475Smrg		free(indexed);
4059956cc18dSsnj#if OPT_DEC_CHRSET
4060956cc18dSsnj		if ((ld = GET_LINEDATA(screen, startc->row)) != 0) {
4061956cc18dSsnj		    if (CSET_DOUBLE(GetLineDblCS(ld)))
4062956cc18dSsnj			startc->col *= 2;
4063956cc18dSsnj		}
4064956cc18dSsnj		if ((ld = GET_LINEDATA(screen, endc->row)) != 0) {
4065956cc18dSsnj		    if (CSET_DOUBLE(GetLineDblCS(ld)))
4066956cc18dSsnj			endc->col *= 2;
4067956cc18dSsnj		}
4068956cc18dSsnj#endif
4069d522f475Smrg	    }
4070d522f475Smrg	    regfree(&preg);
4071d522f475Smrg	}
4072d522f475Smrg    }
4073d522f475Smrg}
4074d522f475Smrg#endif /* OPT_SELECT_REGEX */
4075d522f475Smrg
4076956cc18dSsnj#define InitRow(name) \
4077956cc18dSsnj	ld.name = GET_LINEDATA(screen, screen->name.row)
4078956cc18dSsnj
4079956cc18dSsnj#define NextRow(name) \
4080956cc18dSsnj	ld.name = GET_LINEDATA(screen, ++screen->name.row)
4081956cc18dSsnj
4082956cc18dSsnj#define PrevRow(name) \
4083956cc18dSsnj	ld.name = GET_LINEDATA(screen, --screen->name.row)
4084956cc18dSsnj
408520d2c4d2Smrg#define MoreRows(name) \
408620d2c4d2Smrg	(screen->name.row < screen->max_row)
408720d2c4d2Smrg
4088956cc18dSsnj#define isPrevWrapped(name) \
4089956cc18dSsnj	(screen->name.row > 0 \
4090956cc18dSsnj	   && (ltmp = GET_LINEDATA(screen, screen->name.row - 1)) != 0 \
4091956cc18dSsnj	   && LineTstWrapped(ltmp))
4092956cc18dSsnj
4093d522f475Smrg/*
4094d522f475Smrg * sets startSel endSel
4095d522f475Smrg * ensuring that they have legal values
4096d522f475Smrg */
4097d522f475Smrgstatic void
4098d522f475SmrgComputeSelect(XtermWidget xw,
4099e0a2b6dfSmrg	      CELL *startc,
4100e0a2b6dfSmrg	      CELL *endc,
4101f2e35a3aSmrg	      Bool extend,
4102f2e35a3aSmrg	      Bool normal)
4103d522f475Smrg{
4104956cc18dSsnj    TScreen *screen = TScreenOf(xw);
4105956cc18dSsnj
4106d522f475Smrg    int cclass;
4107d522f475Smrg    CELL first = *startc;
4108d522f475Smrg    CELL last = *endc;
4109956cc18dSsnj    Boolean ignored = False;
4110956cc18dSsnj
4111956cc18dSsnj    struct {
4112956cc18dSsnj	LineData *startSel;
4113956cc18dSsnj	LineData *endSel;
4114956cc18dSsnj    } ld;
4115956cc18dSsnj    LineData *ltmp;
4116d522f475Smrg
4117d522f475Smrg    TRACE(("ComputeSelect(startRow=%d, startCol=%d, endRow=%d, endCol=%d, %sextend)\n",
4118d522f475Smrg	   first.row, first.col,
4119d522f475Smrg	   last.row, last.col,
4120d522f475Smrg	   extend ? "" : "no"));
4121d522f475Smrg
4122d522f475Smrg#if OPT_WIDE_CHARS
4123d522f475Smrg    if (first.col > 1
4124d522f475Smrg	&& isWideCell(first.row, first.col - 1)
4125d522f475Smrg	&& XTERM_CELL(first.row, first.col - 0) == HIDDEN_CHAR) {
412620d2c4d2Smrg	TRACE(("Adjusting start. Changing downwards from %i.\n", first.col));
4127d522f475Smrg	first.col -= 1;
4128d522f475Smrg	if (last.col == (first.col + 1))
4129d522f475Smrg	    last.col--;
4130d522f475Smrg    }
4131d522f475Smrg
4132d522f475Smrg    if (last.col > 1
4133d522f475Smrg	&& isWideCell(last.row, last.col - 1)
4134d522f475Smrg	&& XTERM_CELL(last.row, last.col) == HIDDEN_CHAR) {
4135d522f475Smrg	last.col += 1;
4136d522f475Smrg    }
4137d522f475Smrg#endif
4138d522f475Smrg
4139d522f475Smrg    if (Coordinate(screen, &first) <= Coordinate(screen, &last)) {
4140d522f475Smrg	screen->startSel = screen->startRaw = first;
4141d522f475Smrg	screen->endSel = screen->endRaw = last;
4142d522f475Smrg    } else {			/* Swap them */
4143d522f475Smrg	screen->startSel = screen->startRaw = last;
4144d522f475Smrg	screen->endSel = screen->endRaw = first;
4145d522f475Smrg    }
4146d522f475Smrg
4147956cc18dSsnj    InitRow(startSel);
4148956cc18dSsnj    InitRow(endSel);
4149956cc18dSsnj
4150d522f475Smrg    switch (screen->selectUnit) {
4151d522f475Smrg    case Select_CHAR:
4152956cc18dSsnj	(void) okPosition(screen, &(ld.startSel), &(screen->startSel));
4153956cc18dSsnj	(void) okPosition(screen, &(ld.endSel), &(screen->endSel));
4154d522f475Smrg	break;
4155d522f475Smrg
4156d522f475Smrg    case Select_WORD:
4157d522f475Smrg	TRACE(("Select_WORD\n"));
4158956cc18dSsnj	if (okPosition(screen, &(ld.startSel), &(screen->startSel))) {
4159f2e35a3aSmrg	    CELL mark;
4160956cc18dSsnj	    cclass = CClassOf(startSel);
4161f2e35a3aSmrg	    TRACE(("...starting with class %d\n", cclass));
4162d522f475Smrg	    do {
4163f2e35a3aSmrg		mark = screen->startSel;
4164d522f475Smrg		--screen->startSel.col;
4165956cc18dSsnj		if (screen->startSel.col < 0
4166956cc18dSsnj		    && isPrevWrapped(startSel)) {
4167956cc18dSsnj		    PrevRow(startSel);
4168956cc18dSsnj		    screen->startSel.col = LastTextCol(screen, ld.startSel, screen->startSel.row);
4169d522f475Smrg		}
4170d522f475Smrg	    } while (screen->startSel.col >= 0
4171956cc18dSsnj		     && CClassSelects(startSel, cclass));
4172f2e35a3aSmrg	    if (normal)
4173f2e35a3aSmrg		++screen->startSel.col;
4174f2e35a3aSmrg	    else
4175f2e35a3aSmrg		screen->startSel = mark;
4176d522f475Smrg	}
4177d522f475Smrg#if OPT_WIDE_CHARS
4178f2e35a3aSmrg#define SkipHiddenCell(mark) \
4179f2e35a3aSmrg	if (mark.col && XTERM_CELL(mark.row, mark.col) == HIDDEN_CHAR) \
4180f2e35a3aSmrg	    mark.col++
4181f2e35a3aSmrg#else
4182f2e35a3aSmrg#define SkipHiddenCell(mark)	/* nothing */
4183d522f475Smrg#endif
4184f2e35a3aSmrg	SkipHiddenCell(screen->startSel);
4185f2e35a3aSmrg
4186f2e35a3aSmrg	if (!normal) {
4187f2e35a3aSmrg	    screen->endSel = screen->startSel;
4188f2e35a3aSmrg	    ld.endSel = ld.startSel;
4189f2e35a3aSmrg	}
4190d522f475Smrg
4191956cc18dSsnj	if (okPosition(screen, &(ld.endSel), &(screen->endSel))) {
41922e4f8982Smrg	    int length = LastTextCol(screen, ld.endSel, screen->endSel.row);
4193956cc18dSsnj	    cclass = CClassOf(endSel);
4194f2e35a3aSmrg	    TRACE(("...ending with class %d\n", cclass));
4195d522f475Smrg	    do {
4196d522f475Smrg		++screen->endSel.col;
4197d522f475Smrg		if (screen->endSel.col > length
4198956cc18dSsnj		    && LineTstWrapped(ld.endSel)) {
419920d2c4d2Smrg		    if (!MoreRows(endSel))
420020d2c4d2Smrg			break;
4201d522f475Smrg		    screen->endSel.col = 0;
4202956cc18dSsnj		    NextRow(endSel);
4203956cc18dSsnj		    length = LastTextCol(screen, ld.endSel, screen->endSel.row);
4204d522f475Smrg		}
4205d522f475Smrg	    } while (screen->endSel.col <= length
4206956cc18dSsnj		     && CClassSelects(endSel, cclass));
4207f2e35a3aSmrg	    if (normal
4208f2e35a3aSmrg		&& screen->endSel.col > length + 1
420920d2c4d2Smrg		&& MoreRows(endSel)) {
4210d522f475Smrg		screen->endSel.col = 0;
4211956cc18dSsnj		NextRow(endSel);
4212d522f475Smrg	    }
4213d522f475Smrg	}
4214f2e35a3aSmrg	SkipHiddenCell(screen->endSel);
4215d522f475Smrg
4216d522f475Smrg	screen->saveStartW = screen->startSel;
4217d522f475Smrg	break;
4218d522f475Smrg
4219d522f475Smrg    case Select_LINE:
4220d522f475Smrg	TRACE(("Select_LINE\n"));
422120d2c4d2Smrg	while (LineTstWrapped(ld.endSel)
422220d2c4d2Smrg	       && MoreRows(endSel)) {
4223956cc18dSsnj	    NextRow(endSel);
4224d522f475Smrg	}
4225d522f475Smrg	if (screen->cutToBeginningOfLine
4226d522f475Smrg	    || screen->startSel.row < screen->saveStartW.row) {
4227d522f475Smrg	    screen->startSel.col = 0;
4228956cc18dSsnj	    while (isPrevWrapped(startSel)) {
4229956cc18dSsnj		PrevRow(startSel);
4230d522f475Smrg	    }
4231d522f475Smrg	} else if (!extend) {
4232d522f475Smrg	    if ((first.row < screen->saveStartW.row)
4233d522f475Smrg		|| (isSameRow(&first, &(screen->saveStartW))
4234d522f475Smrg		    && first.col < screen->saveStartW.col)) {
4235d522f475Smrg		screen->startSel.col = 0;
4236956cc18dSsnj		while (isPrevWrapped(startSel)) {
4237956cc18dSsnj		    PrevRow(startSel);
4238d522f475Smrg		}
4239d522f475Smrg	    } else {
4240d522f475Smrg		screen->startSel = screen->saveStartW;
4241d522f475Smrg	    }
4242d522f475Smrg	}
4243956cc18dSsnj	trimLastLine(screen, &(ld.endSel), &(screen->endSel));
4244d522f475Smrg	break;
4245d522f475Smrg
4246d522f475Smrg    case Select_GROUP:		/* paragraph */
4247d522f475Smrg	TRACE(("Select_GROUP\n"));
4248956cc18dSsnj	if (okPosition(screen, &(ld.startSel), &(screen->startSel))) {
4249d522f475Smrg	    /* scan backward for beginning of group */
4250d522f475Smrg	    while (screen->startSel.row > 0 &&
4251956cc18dSsnj		   (LastTextCol(screen, ld.startSel, screen->startSel.row -
4252956cc18dSsnj				1) > 0 ||
4253956cc18dSsnj		    isPrevWrapped(startSel))) {
4254956cc18dSsnj		PrevRow(startSel);
4255d522f475Smrg	    }
4256d522f475Smrg	    screen->startSel.col = 0;
4257d522f475Smrg	    /* scan forward for end of group */
425820d2c4d2Smrg	    while (MoreRows(endSel) &&
4259956cc18dSsnj		   (LastTextCol(screen, ld.endSel, screen->endSel.row + 1) >
4260956cc18dSsnj		    0 ||
4261956cc18dSsnj		    LineTstWrapped(ld.endSel))) {
4262956cc18dSsnj		NextRow(endSel);
4263d522f475Smrg	    }
4264956cc18dSsnj	    trimLastLine(screen, &(ld.endSel), &(screen->endSel));
4265d522f475Smrg	}
4266d522f475Smrg	break;
4267d522f475Smrg
4268d522f475Smrg    case Select_PAGE:		/* everything one can see */
4269d522f475Smrg	TRACE(("Select_PAGE\n"));
4270d522f475Smrg	screen->startSel.row = 0;
4271d522f475Smrg	screen->startSel.col = 0;
427220d2c4d2Smrg	screen->endSel.row = MaxRows(screen);
4273d522f475Smrg	screen->endSel.col = 0;
4274d522f475Smrg	break;
4275d522f475Smrg
4276d522f475Smrg    case Select_ALL:		/* counts scrollback if in normal screen */
4277d522f475Smrg	TRACE(("Select_ALL\n"));
4278d522f475Smrg	screen->startSel.row = -screen->savedlines;
4279d522f475Smrg	screen->startSel.col = 0;
428020d2c4d2Smrg	screen->endSel.row = MaxRows(screen);
4281d522f475Smrg	screen->endSel.col = 0;
4282d522f475Smrg	break;
4283d522f475Smrg
4284d522f475Smrg#if OPT_SELECT_REGEX
4285d522f475Smrg    case Select_REGEX:
4286d522f475Smrg	do_select_regex(screen, &(screen->startSel), &(screen->endSel));
4287d522f475Smrg	break;
4288d522f475Smrg#endif
4289d522f475Smrg
4290d522f475Smrg    case NSELECTUNITS:		/* always ignore */
4291956cc18dSsnj	ignored = True;
4292956cc18dSsnj	break;
4293d522f475Smrg    }
4294d522f475Smrg
4295956cc18dSsnj    if (!ignored) {
4296956cc18dSsnj	/* check boundaries */
4297956cc18dSsnj	ScrollSelection(screen, 0, False);
4298956cc18dSsnj	TrackText(xw, &(screen->startSel), &(screen->endSel));
4299956cc18dSsnj    }
4300d522f475Smrg
4301d522f475Smrg    return;
4302d522f475Smrg}
4303d522f475Smrg
4304d522f475Smrg/* Guaranteed (first.row, first.col) <= (last.row, last.col) */
4305d522f475Smrgstatic void
4306d522f475SmrgTrackText(XtermWidget xw,
4307e0a2b6dfSmrg	  const CELL *firstp,
4308e0a2b6dfSmrg	  const CELL *lastp)
4309d522f475Smrg{
4310956cc18dSsnj    TScreen *screen = TScreenOf(xw);
4311d522f475Smrg    int from, to;
4312d522f475Smrg    CELL old_start, old_end;
4313d522f475Smrg    CELL first = *firstp;
4314d522f475Smrg    CELL last = *lastp;
4315d522f475Smrg
4316d522f475Smrg    TRACE(("TrackText(first=%d,%d, last=%d,%d)\n",
4317d522f475Smrg	   first.row, first.col, last.row, last.col));
4318d522f475Smrg
4319d522f475Smrg    old_start = screen->startH;
4320d522f475Smrg    old_end = screen->endH;
43210bd37d32Smrg    TRACE(("...previous(first=%d,%d, last=%d,%d)\n",
43220bd37d32Smrg	   old_start.row, old_start.col,
43230bd37d32Smrg	   old_end.row, old_end.col));
4324d522f475Smrg    if (isSameCELL(&first, &old_start) &&
43250bd37d32Smrg	isSameCELL(&last, &old_end)) {
4326d522f475Smrg	return;
43270bd37d32Smrg    }
43280bd37d32Smrg
4329d522f475Smrg    screen->startH = first;
4330d522f475Smrg    screen->endH = last;
4331d522f475Smrg    from = Coordinate(screen, &screen->startH);
4332d522f475Smrg    to = Coordinate(screen, &screen->endH);
4333d522f475Smrg    if (to <= screen->startHCoord || from > screen->endHCoord) {
4334d522f475Smrg	/* No overlap whatsoever between old and new hilite */
4335d522f475Smrg	ReHiliteText(xw, &old_start, &old_end);
4336d522f475Smrg	ReHiliteText(xw, &first, &last);
4337d522f475Smrg    } else {
4338d522f475Smrg	if (from < screen->startHCoord) {
4339d522f475Smrg	    /* Extend left end */
4340d522f475Smrg	    ReHiliteText(xw, &first, &old_start);
4341d522f475Smrg	} else if (from > screen->startHCoord) {
4342d522f475Smrg	    /* Shorten left end */
4343d522f475Smrg	    ReHiliteText(xw, &old_start, &first);
4344d522f475Smrg	}
4345d522f475Smrg	if (to > screen->endHCoord) {
4346d522f475Smrg	    /* Extend right end */
4347d522f475Smrg	    ReHiliteText(xw, &old_end, &last);
4348d522f475Smrg	} else if (to < screen->endHCoord) {
4349d522f475Smrg	    /* Shorten right end */
4350d522f475Smrg	    ReHiliteText(xw, &last, &old_end);
4351d522f475Smrg	}
4352d522f475Smrg    }
4353d522f475Smrg    screen->startHCoord = from;
4354d522f475Smrg    screen->endHCoord = to;
4355d522f475Smrg}
4356d522f475Smrg
4357f2e35a3aSmrgstatic void
4358f2e35a3aSmrgUnHiliteText(XtermWidget xw)
4359f2e35a3aSmrg{
4360f2e35a3aSmrg    TrackText(xw, &zeroCELL, &zeroCELL);
4361f2e35a3aSmrg}
4362f2e35a3aSmrg
4363d522f475Smrg/* Guaranteed that (first->row, first->col) <= (last->row, last->col) */
4364d522f475Smrgstatic void
4365d522f475SmrgReHiliteText(XtermWidget xw,
4366e0a2b6dfSmrg	     CELL *firstp,
4367e0a2b6dfSmrg	     CELL *lastp)
4368d522f475Smrg{
4369956cc18dSsnj    TScreen *screen = TScreenOf(xw);
4370d522f475Smrg    CELL first = *firstp;
4371d522f475Smrg    CELL last = *lastp;
4372d522f475Smrg
4373d522f475Smrg    TRACE(("ReHiliteText from %d.%d to %d.%d\n",
4374d522f475Smrg	   first.row, first.col, last.row, last.col));
4375d522f475Smrg
4376d522f475Smrg    if (first.row < 0)
4377d522f475Smrg	first.row = first.col = 0;
4378d522f475Smrg    else if (first.row > screen->max_row)
4379d522f475Smrg	return;			/* nothing to do, since last.row >= first.row */
4380d522f475Smrg
4381d522f475Smrg    if (last.row < 0)
4382d522f475Smrg	return;			/* nothing to do, since first.row <= last.row */
4383d522f475Smrg    else if (last.row > screen->max_row) {
4384d522f475Smrg	last.row = screen->max_row;
4385d522f475Smrg	last.col = MaxCols(screen);
4386d522f475Smrg    }
4387d522f475Smrg    if (isSameCELL(&first, &last))
4388d522f475Smrg	return;
4389d522f475Smrg
4390d522f475Smrg    if (!isSameRow(&first, &last)) {	/* do multiple rows */
43912e4f8982Smrg	int i;
4392d522f475Smrg	if ((i = screen->max_col - first.col + 1) > 0) {	/* first row */
4393d522f475Smrg	    ScrnRefresh(xw, first.row, first.col, 1, i, True);
4394d522f475Smrg	}
4395d522f475Smrg	if ((i = last.row - first.row - 1) > 0) {	/* middle rows */
4396d522f475Smrg	    ScrnRefresh(xw, first.row + 1, 0, i, MaxCols(screen), True);
4397d522f475Smrg	}
4398d522f475Smrg	if (last.col > 0 && last.row <= screen->max_row) {	/* last row */
4399d522f475Smrg	    ScrnRefresh(xw, last.row, 0, 1, last.col, True);
4400d522f475Smrg	}
4401d522f475Smrg    } else {			/* do single row */
4402d522f475Smrg	ScrnRefresh(xw, first.row, first.col, 1, last.col - first.col, True);
4403d522f475Smrg    }
4404d522f475Smrg}
4405d522f475Smrg
4406d522f475Smrg/*
4407913cc679Smrg * Guaranteed that (cellc->row, cellc->col) <= (cell->row, cell->col),
4408913cc679Smrg * and that both points are valid
4409d522f475Smrg * (may have cell->row = screen->max_row+1, cell->col = 0).
4410d522f475Smrg */
4411d522f475Smrgstatic void
4412d522f475SmrgSaltTextAway(XtermWidget xw,
4413f2e35a3aSmrg	     int which,
4414e0a2b6dfSmrg	     CELL *cellc,
4415e0a2b6dfSmrg	     CELL *cell)
4416d522f475Smrg{
4417956cc18dSsnj    TScreen *screen = TScreenOf(xw);
4418f2e35a3aSmrg    SelectedCells *scp;
4419f2e35a3aSmrg    int i;
4420d522f475Smrg    int eol;
4421f2e35a3aSmrg    int need = 0;
4422f2e35a3aSmrg    size_t have = 0;
4423d522f475Smrg    Char *line;
4424d522f475Smrg    Char *lp;
4425d522f475Smrg    CELL first = *cellc;
4426d522f475Smrg    CELL last = *cell;
4427d522f475Smrg
4428f2e35a3aSmrg    if (which < 0 || which >= MAX_SELECTIONS) {
4429f2e35a3aSmrg	TRACE(("SaltTextAway - which selection?\n"));
4430f2e35a3aSmrg	return;
4431f2e35a3aSmrg    }
4432f2e35a3aSmrg    scp = &(screen->selected_cells[which]);
4433f2e35a3aSmrg
4434f2e35a3aSmrg    TRACE(("SaltTextAway which=%d, first=%d,%d, last=%d,%d\n",
4435f2e35a3aSmrg	   which, first.row, first.col, last.row, last.col));
4436f2e35a3aSmrg
4437d522f475Smrg    if (isSameRow(&first, &last) && first.col > last.col) {
44382e4f8982Smrg	int tmp;
4439956cc18dSsnj	EXCHANGE(first.col, last.col, tmp);
4440d522f475Smrg    }
4441d522f475Smrg
4442d522f475Smrg    --last.col;
4443d522f475Smrg    /* first we need to know how long the string is before we can save it */
4444d522f475Smrg
4445d522f475Smrg    if (isSameRow(&last, &first)) {
4446f2e35a3aSmrg	need = Length(screen, first.row, first.col, last.col);
4447d522f475Smrg    } else {			/* two cases, cut is on same line, cut spans multiple lines */
4448f2e35a3aSmrg	need += Length(screen, first.row, first.col, screen->max_col) + 1;
4449d522f475Smrg	for (i = first.row + 1; i < last.row; i++)
4450f2e35a3aSmrg	    need += Length(screen, i, 0, screen->max_col) + 1;
4451d522f475Smrg	if (last.col >= 0)
4452f2e35a3aSmrg	    need += Length(screen, last.row, 0, last.col);
4453d522f475Smrg    }
4454d522f475Smrg
4455d522f475Smrg    /* UTF-8 may require more space */
4456d522f475Smrg    if_OPT_WIDE_CHARS(screen, {
4457f2e35a3aSmrg	if (need > 0) {
4458f2e35a3aSmrg	    if (screen->max_combining > 0)
4459f2e35a3aSmrg		need += screen->max_combining;
4460f2e35a3aSmrg	    need *= 6;
4461f2e35a3aSmrg	}
4462d522f475Smrg    });
4463d522f475Smrg
4464d522f475Smrg    /* now get some memory to save it in */
4465f2e35a3aSmrg    if (need < 0)
4466f2e35a3aSmrg	return;
4467d522f475Smrg
4468f2e35a3aSmrg    if (scp->data_limit <= (unsigned) need) {
4469f2e35a3aSmrg	if ((line = (Char *) malloc((size_t) need + 1)) == 0)
4470d522f475Smrg	    SysError(ERROR_BMALLOC2);
4471f2e35a3aSmrg	free(scp->data_buffer);
4472f2e35a3aSmrg	scp->data_buffer = line;
4473f2e35a3aSmrg	scp->data_limit = (size_t) (need + 1);
4474d522f475Smrg    } else {
4475f2e35a3aSmrg	line = scp->data_buffer;
4476d522f475Smrg    }
4477d522f475Smrg
4478f2e35a3aSmrg    if (line == 0)
4479d522f475Smrg	return;
4480d522f475Smrg
4481f2e35a3aSmrg    line[need] = '\0';		/* make sure it is null terminated */
4482d522f475Smrg    lp = line;			/* lp points to where to save the text */
4483d522f475Smrg    if (isSameRow(&last, &first)) {
4484d522f475Smrg	lp = SaveText(screen, last.row, first.col, last.col, lp, &eol);
4485d522f475Smrg    } else {
4486d522f475Smrg	lp = SaveText(screen, first.row, first.col, screen->max_col, lp, &eol);
4487d522f475Smrg	if (eol)
4488d522f475Smrg	    *lp++ = '\n';	/* put in newline at end of line */
4489d522f475Smrg	for (i = first.row + 1; i < last.row; i++) {
4490d522f475Smrg	    lp = SaveText(screen, i, 0, screen->max_col, lp, &eol);
4491d522f475Smrg	    if (eol)
4492d522f475Smrg		*lp++ = '\n';
4493d522f475Smrg	}
4494d522f475Smrg	if (last.col >= 0)
4495d522f475Smrg	    lp = SaveText(screen, last.row, 0, last.col, lp, &eol);
4496d522f475Smrg    }
4497d522f475Smrg    *lp = '\0';			/* make sure we have end marked */
4498d522f475Smrg
4499f2e35a3aSmrg    have = (size_t) (lp - line);
4500f2e35a3aSmrg    /*
4501f2e35a3aSmrg     * Scanning the buffer twice is unnecessary.  Discard unwanted memory if
4502f2e35a3aSmrg     * the estimate is too-far off.
4503f2e35a3aSmrg     */
4504f2e35a3aSmrg    if ((have * 2) < (size_t) need) {
4505f2e35a3aSmrg	Char *next;
4506f2e35a3aSmrg	scp->data_limit = have + 1;
4507f2e35a3aSmrg	next = realloc(line, scp->data_limit);
4508f2e35a3aSmrg	if (next == NULL) {
4509f2e35a3aSmrg	    free(line);
4510f2e35a3aSmrg	    scp->data_length = 0;
4511f2e35a3aSmrg	    scp->data_limit = 0;
4512f2e35a3aSmrg	}
4513f2e35a3aSmrg	scp->data_buffer = next;
4514f2e35a3aSmrg    }
4515f2e35a3aSmrg    scp->data_length = have;
4516d522f475Smrg
4517f2e35a3aSmrg    TRACE(("Salted TEXT:%u:%s\n", (unsigned) have,
4518f2e35a3aSmrg	   visibleChars(scp->data_buffer, (unsigned) have)));
4519d522f475Smrg}
4520d522f475Smrg
4521d522f475Smrg#if OPT_PASTE64
4522d522f475Smrgvoid
4523f2e35a3aSmrgClearSelectionBuffer(TScreen *screen, String selection)
4524d522f475Smrg{
4525f2e35a3aSmrg    int which = TargetToSelection(screen, selection);
4526f2e35a3aSmrg    SelectedCells *scp = &(screen->selected_cells[okSelectionCode(which)]);
4527f2e35a3aSmrg    FreeAndNull(scp->data_buffer);
4528f2e35a3aSmrg    scp->data_limit = 0;
4529f2e35a3aSmrg    scp->data_length = 0;
4530d522f475Smrg    screen->base64_count = 0;
4531d522f475Smrg}
4532d522f475Smrg
4533d522f475Smrgstatic void
4534f2e35a3aSmrgAppendStrToSelectionBuffer(SelectedCells * scp, Char *text, size_t len)
4535d522f475Smrg{
4536d522f475Smrg    if (len != 0) {
4537f2e35a3aSmrg	size_t j = (scp->data_length + len);
4538f2e35a3aSmrg	size_t k = j + (j >> 2) + 80;
4539f2e35a3aSmrg	if (j + 1 >= scp->data_limit) {
4540f2e35a3aSmrg	    Char *line;
4541f2e35a3aSmrg	    if (!scp->data_length) {
4542f2e35a3aSmrg		line = (Char *) malloc(k);
4543d522f475Smrg	    } else {
4544f2e35a3aSmrg		line = (Char *) realloc(scp->data_buffer, k);
4545d522f475Smrg	    }
4546f2e35a3aSmrg	    if (line == 0)
4547f2e35a3aSmrg		SysError(ERROR_BMALLOC2);
4548f2e35a3aSmrg	    scp->data_buffer = line;
4549f2e35a3aSmrg	    scp->data_limit = k;
4550d522f475Smrg	}
4551f2e35a3aSmrg	if (scp->data_buffer != 0) {
4552f2e35a3aSmrg	    memcpy(scp->data_buffer + scp->data_length, text, len);
4553f2e35a3aSmrg	    scp->data_length += len;
4554f2e35a3aSmrg	    scp->data_buffer[scp->data_length] = 0;
455520d2c4d2Smrg	}
4556d522f475Smrg    }
4557d522f475Smrg}
4558d522f475Smrg
4559d522f475Smrgvoid
4560f2e35a3aSmrgAppendToSelectionBuffer(TScreen *screen, unsigned c, String selection)
4561d522f475Smrg{
4562f2e35a3aSmrg    int which = TargetToSelection(screen, selection);
4563f2e35a3aSmrg    SelectedCells *scp = &(screen->selected_cells[okSelectionCode(which)]);
45642eaa94a1Schristos    unsigned six;
4565d522f475Smrg    Char ch;
4566d522f475Smrg
4567d522f475Smrg    /* Decode base64 character */
4568d522f475Smrg    if (c >= 'A' && c <= 'Z')
4569d522f475Smrg	six = c - 'A';
4570d522f475Smrg    else if (c >= 'a' && c <= 'z')
4571d522f475Smrg	six = c - 'a' + 26;
4572d522f475Smrg    else if (c >= '0' && c <= '9')
4573d522f475Smrg	six = c - '0' + 52;
4574d522f475Smrg    else if (c == '+')
4575d522f475Smrg	six = 62;
4576d522f475Smrg    else if (c == '/')
4577d522f475Smrg	six = 63;
4578d522f475Smrg    else
4579d522f475Smrg	return;
4580d522f475Smrg
4581d522f475Smrg    /* Accumulate bytes */
4582d522f475Smrg    switch (screen->base64_count) {
4583d522f475Smrg    case 0:
4584d522f475Smrg	screen->base64_accu = six;
4585d522f475Smrg	screen->base64_count = 6;
4586d522f475Smrg	break;
4587d522f475Smrg
4588d522f475Smrg    case 2:
45892eaa94a1Schristos	ch = CharOf((screen->base64_accu << 6) + six);
4590d522f475Smrg	screen->base64_count = 0;
4591f2e35a3aSmrg	AppendStrToSelectionBuffer(scp, &ch, (size_t) 1);
4592d522f475Smrg	break;
4593d522f475Smrg
4594d522f475Smrg    case 4:
45952eaa94a1Schristos	ch = CharOf((screen->base64_accu << 4) + (six >> 2));
4596d522f475Smrg	screen->base64_accu = (six & 0x3);
4597d522f475Smrg	screen->base64_count = 2;
4598f2e35a3aSmrg	AppendStrToSelectionBuffer(scp, &ch, (size_t) 1);
4599d522f475Smrg	break;
4600d522f475Smrg
4601d522f475Smrg    case 6:
46022eaa94a1Schristos	ch = CharOf((screen->base64_accu << 2) + (six >> 4));
4603d522f475Smrg	screen->base64_accu = (six & 0xF);
4604d522f475Smrg	screen->base64_count = 4;
4605f2e35a3aSmrg	AppendStrToSelectionBuffer(scp, &ch, (size_t) 1);
4606d522f475Smrg	break;
4607d522f475Smrg    }
4608d522f475Smrg}
4609d522f475Smrg
4610d522f475Smrgvoid
4611e0a2b6dfSmrgCompleteSelection(XtermWidget xw, String *args, Cardinal len)
4612d522f475Smrg{
4613956cc18dSsnj    TScreen *screen = TScreenOf(xw);
4614956cc18dSsnj
4615956cc18dSsnj    screen->base64_count = 0;
4616956cc18dSsnj    screen->base64_accu = 0;
4617d522f475Smrg    _OwnSelection(xw, args, len);
4618d522f475Smrg}
4619d522f475Smrg#endif /* OPT_PASTE64 */
4620d522f475Smrg
4621d522f475Smrgstatic Bool
4622d522f475Smrg_ConvertSelectionHelper(Widget w,
4623f2e35a3aSmrg			SelectedCells * scp,
4624894e0ac8Smrg			Atom *type,
4625d522f475Smrg			XtPointer *value,
4626d522f475Smrg			unsigned long *length,
4627d522f475Smrg			int *format,
4628d522f475Smrg			int (*conversion_function) (Display *,
4629d522f475Smrg						    char **, int,
4630d522f475Smrg						    XICCEncodingStyle,
4631d522f475Smrg						    XTextProperty *),
4632d522f475Smrg			XICCEncodingStyle conversion_style)
4633d522f475Smrg{
463401037d57Smrg    *value = 0;
463501037d57Smrg    *length = 0;
463601037d57Smrg    *type = 0;
463701037d57Smrg    *format = 0;
463801037d57Smrg
4639f2e35a3aSmrg    if (getXtermWidget(w) != 0) {
4640d522f475Smrg	Display *dpy = XtDisplay(w);
4641d522f475Smrg	XTextProperty textprop;
464201037d57Smrg	int out_n = 0;
464301037d57Smrg	char *result = 0;
4644f2e35a3aSmrg	char *the_data = (char *) scp->data_buffer;
464501037d57Smrg	char *the_next;
4646f2e35a3aSmrg	unsigned long remaining = scp->data_length;
464701037d57Smrg
464801037d57Smrg	TRACE(("converting %ld:'%s'\n",
4649f2e35a3aSmrg	       (long) scp->data_length,
4650f2e35a3aSmrg	       visibleChars(scp->data_buffer, (unsigned) scp->data_length)));
465101037d57Smrg	/*
465201037d57Smrg	 * For most selections, we can convert in one pass.  It is possible
465301037d57Smrg	 * that some applications contain embedded nulls, e.g., using xterm's
465401037d57Smrg	 * paste64 feature.  For those cases, we will build up the result in
465501037d57Smrg	 * parts.
465601037d57Smrg	 */
4657f2e35a3aSmrg	if (memchr(the_data, 0, scp->data_length) != 0) {
465801037d57Smrg	    TRACE(("selection contains embedded nulls\n"));
4659f2e35a3aSmrg	    result = calloc(scp->data_length + 1, sizeof(char));
466001037d57Smrg	}
4661d522f475Smrg
466201037d57Smrg      next_try:
466301037d57Smrg	memset(&textprop, 0, sizeof(textprop));
4664d522f475Smrg	if (conversion_function(dpy, &the_data, 1,
4665d522f475Smrg				conversion_style,
4666d522f475Smrg				&textprop) >= Success) {
466701037d57Smrg	    if ((result != 0)
466801037d57Smrg		&& (textprop.value != 0)
466901037d57Smrg		&& (textprop.format == 8)) {
467001037d57Smrg		char *text_values = (char *) textprop.value;
467101037d57Smrg		unsigned long in_n;
467201037d57Smrg
467301037d57Smrg		if (out_n == 0) {
467401037d57Smrg		    *value = result;
467501037d57Smrg		    *type = textprop.encoding;
467601037d57Smrg		    *format = textprop.format;
467701037d57Smrg		}
467801037d57Smrg		for (in_n = 0; in_n < textprop.nitems; ++in_n) {
467901037d57Smrg		    result[out_n++] = text_values[in_n];
468001037d57Smrg		}
468101037d57Smrg		*length += textprop.nitems;
468201037d57Smrg		if ((the_next = memchr(the_data, 0, remaining)) != 0) {
468301037d57Smrg		    unsigned long this_was = (unsigned long) (the_next - the_data);
468401037d57Smrg		    this_was++;
468501037d57Smrg		    the_data += this_was;
468601037d57Smrg		    remaining -= this_was;
468701037d57Smrg		    result[out_n++] = 0;
468801037d57Smrg		    *length += 1;
468901037d57Smrg		    if (remaining)
469001037d57Smrg			goto next_try;
469101037d57Smrg		}
469201037d57Smrg		return True;
469301037d57Smrg	    } else {
469401037d57Smrg		free(result);
469501037d57Smrg		*value = (XtPointer) textprop.value;
469601037d57Smrg		*length = textprop.nitems;
469701037d57Smrg		*type = textprop.encoding;
469801037d57Smrg		*format = textprop.format;
469901037d57Smrg		return True;
470001037d57Smrg	    }
4701d522f475Smrg	}
470201037d57Smrg	free(result);
4703d522f475Smrg    }
4704d522f475Smrg    return False;
4705d522f475Smrg}
4706d522f475Smrg
47072eaa94a1Schristosstatic Boolean
47082eaa94a1SchristosSaveConvertedLength(XtPointer *target, unsigned long source)
47092eaa94a1Schristos{
47102eaa94a1Schristos    Boolean result = False;
47112eaa94a1Schristos
47122eaa94a1Schristos    *target = XtMalloc(4);
47132eaa94a1Schristos    if (*target != 0) {
47142eaa94a1Schristos	result = True;
47152eaa94a1Schristos	if (sizeof(unsigned long) == 4) {
47162eaa94a1Schristos	    *(unsigned long *) *target = source;
47172eaa94a1Schristos	} else if (sizeof(unsigned) == 4) {
471820d2c4d2Smrg	    *(unsigned *) *target = (unsigned) source;
47192eaa94a1Schristos	} else if (sizeof(unsigned short) == 4) {
47202eaa94a1Schristos	    *(unsigned short *) *target = (unsigned short) source;
47212eaa94a1Schristos	} else {
47222eaa94a1Schristos	    /* FIXME - does this depend on byte-order? */
47232eaa94a1Schristos	    unsigned long temp = source;
472420d2c4d2Smrg	    memcpy((char *) *target,
472520d2c4d2Smrg		   ((char *) &temp) + sizeof(temp) - 4,
472620d2c4d2Smrg		   (size_t) 4);
47272eaa94a1Schristos	}
47282eaa94a1Schristos    }
47292eaa94a1Schristos    return result;
47302eaa94a1Schristos}
47312eaa94a1Schristos
4732f2e35a3aSmrg#define keepClipboard(d,atom) ((screen->keepClipboard) && \
4733f2e35a3aSmrg	 (atom == XA_CLIPBOARD(d)))
47342e4f8982Smrg
4735d522f475Smrgstatic Boolean
4736d522f475SmrgConvertSelection(Widget w,
4737894e0ac8Smrg		 Atom *selection,
4738894e0ac8Smrg		 Atom *target,
4739894e0ac8Smrg		 Atom *type,
4740d522f475Smrg		 XtPointer *value,
4741d522f475Smrg		 unsigned long *length,
4742d522f475Smrg		 int *format)
4743d522f475Smrg{
4744d522f475Smrg    Display *dpy = XtDisplay(w);
4745d522f475Smrg    TScreen *screen;
4746f2e35a3aSmrg    SelectedCells *scp;
4747d522f475Smrg    Bool result = False;
4748d522f475Smrg
47492e4f8982Smrg    Char *data;
47502e4f8982Smrg    unsigned long data_length;
47512e4f8982Smrg
4752956cc18dSsnj    XtermWidget xw;
4753956cc18dSsnj
4754956cc18dSsnj    if ((xw = getXtermWidget(w)) == 0)
4755d522f475Smrg	return False;
4756d522f475Smrg
4757956cc18dSsnj    screen = TScreenOf(xw);
4758d522f475Smrg
4759f2e35a3aSmrg    TRACE(("ConvertSelection %s -> %s\n",
4760f2e35a3aSmrg	   TraceAtomName(screen->display, *selection),
4761956cc18dSsnj	   visibleSelectionTarget(dpy, *target)));
4762956cc18dSsnj
4763f2e35a3aSmrg    if (keepClipboard(dpy, *selection)) {
47642e4f8982Smrg	TRACE(("asked for clipboard\n"));
4765f2e35a3aSmrg	scp = &(screen->clipboard_data);
47662e4f8982Smrg    } else {
47672e4f8982Smrg	TRACE(("asked for selection\n"));
4768f2e35a3aSmrg	scp = &(screen->selected_cells[AtomToSelection(dpy, *selection)]);
47692e4f8982Smrg    }
47702e4f8982Smrg
4771f2e35a3aSmrg    data = scp->data_buffer;
4772f2e35a3aSmrg    data_length = scp->data_length;
47732e4f8982Smrg    if (data == NULL) {
4774f2e35a3aSmrg	TRACE(("...no selection-data\n"));
4775f2e35a3aSmrg	return False;
477601037d57Smrg    }
477701037d57Smrg
4778d522f475Smrg    if (*target == XA_TARGETS(dpy)) {
4779d522f475Smrg	Atom *targetP;
4780d522f475Smrg	XPointer std_return = 0;
4781d522f475Smrg	unsigned long std_length;
4782d522f475Smrg
4783d522f475Smrg	if (XmuConvertStandardSelection(w, screen->selection_time, selection,
4784d522f475Smrg					target, type, &std_return,
4785d522f475Smrg					&std_length, format)) {
4786956cc18dSsnj	    Atom *my_targets = _SelectionTargets(w);
47872e4f8982Smrg	    Atom *allocP;
47882e4f8982Smrg	    Atom *std_targets;
4789956cc18dSsnj
4790956cc18dSsnj	    TRACE(("XmuConvertStandardSelection - success\n"));
4791a1f3da82Smrg	    std_targets = (Atom *) (void *) (std_return);
4792d522f475Smrg	    *length = std_length + 6;
4793d522f475Smrg
4794a1f3da82Smrg	    targetP = TypeXtMallocN(Atom, *length);
4795d522f475Smrg	    allocP = targetP;
4796d522f475Smrg
4797d522f475Smrg	    *value = (XtPointer) targetP;
4798d522f475Smrg
47990bd37d32Smrg	    if (my_targets != 0) {
48000bd37d32Smrg		while (*my_targets != None) {
48010bd37d32Smrg		    *targetP++ = *my_targets++;
48020bd37d32Smrg		}
4803956cc18dSsnj	    }
4804d522f475Smrg	    *targetP++ = XA_LENGTH(dpy);
4805d522f475Smrg	    *targetP++ = XA_LIST_LENGTH(dpy);
4806d522f475Smrg
48072eaa94a1Schristos	    *length = std_length + (unsigned long) (targetP - allocP);
4808d522f475Smrg
4809d522f475Smrg	    memcpy(targetP, std_targets, sizeof(Atom) * std_length);
4810d522f475Smrg	    XtFree((char *) std_targets);
4811d522f475Smrg	    *type = XA_ATOM;
4812d522f475Smrg	    *format = 32;
4813d522f475Smrg	    result = True;
4814956cc18dSsnj	} else {
4815956cc18dSsnj	    TRACE(("XmuConvertStandardSelection - failed\n"));
4816d522f475Smrg	}
4817d522f475Smrg    }
4818d522f475Smrg#if OPT_WIDE_CHARS
4819d522f475Smrg    else if (screen->wide_chars && *target == XA_STRING) {
4820d522f475Smrg	result =
4821f2e35a3aSmrg	    _ConvertSelectionHelper(w, scp,
4822f2e35a3aSmrg				    type, value, length, format,
4823d522f475Smrg				    Xutf8TextListToTextProperty,
4824d522f475Smrg				    XStringStyle);
4825956cc18dSsnj	TRACE(("...Xutf8TextListToTextProperty:%d\n", result));
4826d522f475Smrg    } else if (screen->wide_chars && *target == XA_UTF8_STRING(dpy)) {
4827d522f475Smrg	result =
4828f2e35a3aSmrg	    _ConvertSelectionHelper(w, scp,
4829f2e35a3aSmrg				    type, value, length, format,
4830d522f475Smrg				    Xutf8TextListToTextProperty,
4831d522f475Smrg				    XUTF8StringStyle);
4832956cc18dSsnj	TRACE(("...Xutf8TextListToTextProperty:%d\n", result));
4833d522f475Smrg    } else if (screen->wide_chars && *target == XA_TEXT(dpy)) {
4834d522f475Smrg	result =
4835f2e35a3aSmrg	    _ConvertSelectionHelper(w, scp,
4836f2e35a3aSmrg				    type, value, length, format,
4837d522f475Smrg				    Xutf8TextListToTextProperty,
4838d522f475Smrg				    XStdICCTextStyle);
4839956cc18dSsnj	TRACE(("...Xutf8TextListToTextProperty:%d\n", result));
4840d522f475Smrg    } else if (screen->wide_chars && *target == XA_COMPOUND_TEXT(dpy)) {
4841d522f475Smrg	result =
4842f2e35a3aSmrg	    _ConvertSelectionHelper(w, scp,
4843f2e35a3aSmrg				    type, value, length, format,
4844d522f475Smrg				    Xutf8TextListToTextProperty,
4845d522f475Smrg				    XCompoundTextStyle);
4846956cc18dSsnj	TRACE(("...Xutf8TextListToTextProperty:%d\n", result));
4847d522f475Smrg    }
4848d522f475Smrg#endif
4849d522f475Smrg
4850d522f475Smrg    else if (*target == XA_STRING) {	/* not wide_chars */
4851d522f475Smrg	/* We can only reach this point if the selection requestor
4852d522f475Smrg	   requested STRING before any of TEXT, COMPOUND_TEXT or
4853d522f475Smrg	   UTF8_STRING.  We therefore assume that the requestor is not
4854d522f475Smrg	   properly internationalised, and dump raw eight-bit data
4855d522f475Smrg	   with no conversion into the selection.  Yes, this breaks
4856d522f475Smrg	   the ICCCM in non-Latin-1 locales. */
4857d522f475Smrg	*type = XA_STRING;
4858f2e35a3aSmrg	*value = (XtPointer) data;
4859f2e35a3aSmrg	*length = data_length;
4860d522f475Smrg	*format = 8;
4861d522f475Smrg	result = True;
4862956cc18dSsnj	TRACE(("...raw 8-bit data:%d\n", result));
4863d522f475Smrg    } else if (*target == XA_TEXT(dpy)) {	/* not wide_chars */
4864d522f475Smrg	result =
4865f2e35a3aSmrg	    _ConvertSelectionHelper(w, scp,
4866f2e35a3aSmrg				    type, value, length, format,
4867d522f475Smrg				    XmbTextListToTextProperty,
4868d522f475Smrg				    XStdICCTextStyle);
4869956cc18dSsnj	TRACE(("...XmbTextListToTextProperty(StdICC):%d\n", result));
4870d522f475Smrg    } else if (*target == XA_COMPOUND_TEXT(dpy)) {	/* not wide_chars */
4871d522f475Smrg	result =
4872f2e35a3aSmrg	    _ConvertSelectionHelper(w, scp,
4873f2e35a3aSmrg				    type, value, length, format,
4874d522f475Smrg				    XmbTextListToTextProperty,
4875d522f475Smrg				    XCompoundTextStyle);
4876956cc18dSsnj	TRACE(("...XmbTextListToTextProperty(Compound):%d\n", result));
4877d522f475Smrg    }
4878d522f475Smrg#ifdef X_HAVE_UTF8_STRING
4879d522f475Smrg    else if (*target == XA_UTF8_STRING(dpy)) {	/* not wide_chars */
4880d522f475Smrg	result =
4881f2e35a3aSmrg	    _ConvertSelectionHelper(w, scp,
4882f2e35a3aSmrg				    type, value, length, format,
4883d522f475Smrg				    XmbTextListToTextProperty,
4884d522f475Smrg				    XUTF8StringStyle);
4885956cc18dSsnj	TRACE(("...XmbTextListToTextProperty(UTF8):%d\n", result));
4886d522f475Smrg    }
4887d522f475Smrg#endif
4888d522f475Smrg    else if (*target == XA_LIST_LENGTH(dpy)) {
488920d2c4d2Smrg	result = SaveConvertedLength(value, (unsigned long) 1);
4890d522f475Smrg	*type = XA_INTEGER;
4891d522f475Smrg	*length = 1;
4892d522f475Smrg	*format = 32;
4893956cc18dSsnj	TRACE(("...list of values:%d\n", result));
4894d522f475Smrg    } else if (*target == XA_LENGTH(dpy)) {
4895d522f475Smrg	/* This value is wrong if we have UTF-8 text */
4896f2e35a3aSmrg	result = SaveConvertedLength(value, scp->data_length);
4897d522f475Smrg	*type = XA_INTEGER;
4898d522f475Smrg	*length = 1;
4899d522f475Smrg	*format = 32;
4900956cc18dSsnj	TRACE(("...list of values:%d\n", result));
4901d522f475Smrg    } else if (XmuConvertStandardSelection(w,
4902d522f475Smrg					   screen->selection_time, selection,
4903d522f475Smrg					   target, type, (XPointer *) value,
4904d522f475Smrg					   length, format)) {
4905d522f475Smrg	result = True;
4906956cc18dSsnj	TRACE(("...XmuConvertStandardSelection:%d\n", result));
4907d522f475Smrg    }
4908d522f475Smrg
4909d522f475Smrg    /* else */
49102eaa94a1Schristos    return (Boolean) result;
4911d522f475Smrg}
4912d522f475Smrg
4913d522f475Smrgstatic void
4914894e0ac8SmrgLoseSelection(Widget w, Atom *selection)
4915d522f475Smrg{
4916d522f475Smrg    TScreen *screen;
4917d522f475Smrg    Atom *atomP;
4918d522f475Smrg    Cardinal i;
4919d522f475Smrg
4920956cc18dSsnj    XtermWidget xw;
4921956cc18dSsnj
4922956cc18dSsnj    if ((xw = getXtermWidget(w)) == 0)
4923d522f475Smrg	return;
4924d522f475Smrg
4925956cc18dSsnj    screen = TScreenOf(xw);
4926913cc679Smrg    TRACE(("LoseSelection %s\n", TraceAtomName(screen->display, *selection)));
492701037d57Smrg
4928d522f475Smrg    for (i = 0, atomP = screen->selection_atoms;
4929d522f475Smrg	 i < screen->selection_count; i++, atomP++) {
4930d522f475Smrg	if (*selection == *atomP)
4931d522f475Smrg	    *atomP = (Atom) 0;
4932d522f475Smrg	if (CutBuffer(*atomP) >= 0) {
4933d522f475Smrg	    *atomP = (Atom) 0;
4934d522f475Smrg	}
4935d522f475Smrg    }
4936d522f475Smrg
4937d522f475Smrg    for (i = screen->selection_count; i; i--) {
4938d522f475Smrg	if (screen->selection_atoms[i - 1] != 0)
4939d522f475Smrg	    break;
4940d522f475Smrg    }
4941d522f475Smrg    screen->selection_count = i;
4942d522f475Smrg
4943d522f475Smrg    for (i = 0, atomP = screen->selection_atoms;
4944d522f475Smrg	 i < screen->selection_count; i++, atomP++) {
4945d522f475Smrg	if (*atomP == (Atom) 0) {
4946d522f475Smrg	    *atomP = screen->selection_atoms[--screen->selection_count];
4947d522f475Smrg	}
4948d522f475Smrg    }
4949d522f475Smrg
4950d522f475Smrg    if (screen->selection_count == 0)
4951f2e35a3aSmrg	UnHiliteText(xw);
4952d522f475Smrg}
4953d522f475Smrg
4954d522f475Smrg/* ARGSUSED */
4955d522f475Smrgstatic void
4956d522f475SmrgSelectionDone(Widget w GCC_UNUSED,
4957894e0ac8Smrg	      Atom *selection GCC_UNUSED,
4958894e0ac8Smrg	      Atom *target GCC_UNUSED)
4959d522f475Smrg{
4960d522f475Smrg    /* empty proc so Intrinsics know we want to keep storage */
496101037d57Smrg    TRACE(("SelectionDone\n"));
4962d522f475Smrg}
4963d522f475Smrg
4964d522f475Smrgstatic void
4965d522f475Smrg_OwnSelection(XtermWidget xw,
4966e0a2b6dfSmrg	      String *selections,
4967d522f475Smrg	      Cardinal count)
4968d522f475Smrg{
4969956cc18dSsnj    TScreen *screen = TScreenOf(xw);
4970f2e35a3aSmrg    Display *dpy = screen->display;
4971d522f475Smrg    Atom *atoms = screen->selection_atoms;
4972d522f475Smrg    Cardinal i;
4973d522f475Smrg    Bool have_selection = False;
4974f2e35a3aSmrg    SelectedCells *scp;
4975d522f475Smrg
497620d2c4d2Smrg    if (count == 0)
497720d2c4d2Smrg	return;
4978d522f475Smrg
4979f2e35a3aSmrg    TRACE(("_OwnSelection count %d\n", count));
4980d522f475Smrg    selections = MapSelections(xw, selections, count);
4981d522f475Smrg
4982d522f475Smrg    if (count > screen->sel_atoms_size) {
4983d522f475Smrg	XtFree((char *) atoms);
4984a1f3da82Smrg	atoms = TypeXtMallocN(Atom, count);
4985d522f475Smrg	screen->selection_atoms = atoms;
4986d522f475Smrg	screen->sel_atoms_size = count;
4987d522f475Smrg    }
4988f2e35a3aSmrg    XmuInternStrings(dpy, selections, count, atoms);
4989d522f475Smrg    for (i = 0; i < count; i++) {
4990d522f475Smrg	int cutbuffer = CutBuffer(atoms[i]);
4991d522f475Smrg	if (cutbuffer >= 0) {
49922eaa94a1Schristos	    unsigned long limit =
4993f2e35a3aSmrg	    (unsigned long) (4 * XMaxRequestSize(dpy) - 32);
4994f2e35a3aSmrg	    scp = &(screen->selected_cells[CutBufferToCode(cutbuffer)]);
4995f2e35a3aSmrg	    if (scp->data_length > limit) {
499620d2c4d2Smrg		TRACE(("selection too big (%lu bytes), not storing in CUT_BUFFER%d\n",
4997f2e35a3aSmrg		       (unsigned long) scp->data_length, cutbuffer));
49980bd37d32Smrg		xtermWarning("selection too big (%lu bytes), not storing in CUT_BUFFER%d\n",
4999f2e35a3aSmrg			     (unsigned long) scp->data_length, cutbuffer);
5000d522f475Smrg	    } else {
5001d522f475Smrg		/* This used to just use the UTF-8 data, which was totally
5002894e0ac8Smrg		 * broken as not even the corresponding paste code in xterm
5003d522f475Smrg		 * understood this!  So now it converts to Latin1 first.
5004d522f475Smrg		 *   Robert Brady, 2000-09-05
5005d522f475Smrg		 */
5006f2e35a3aSmrg		unsigned long length = scp->data_length;
5007f2e35a3aSmrg		Char *data = scp->data_buffer;
5008d522f475Smrg		if_OPT_WIDE_CHARS((screen), {
5009956cc18dSsnj		    data = UTF8toLatin1(screen, data, length, &length);
5010d522f475Smrg		});
5011d522f475Smrg		TRACE(("XStoreBuffer(%d)\n", cutbuffer));
5012f2e35a3aSmrg		XStoreBuffer(dpy,
5013d522f475Smrg			     (char *) data,
5014d522f475Smrg			     (int) length,
5015d522f475Smrg			     cutbuffer);
5016d522f475Smrg	    }
5017f2e35a3aSmrg	} else {
5018f2e35a3aSmrg	    int which = AtomToSelection(dpy, atoms[i]);
5019f2e35a3aSmrg	    if (keepClipboard(dpy, atoms[i])) {
5020f2e35a3aSmrg		Char *buf;
5021f2e35a3aSmrg		SelectedCells *tcp = &(screen->clipboard_data);
5022f2e35a3aSmrg		TRACE(("saving selection to clipboard buffer\n"));
5023f2e35a3aSmrg		scp = &(screen->selected_cells[CLIPBOARD_CODE]);
50245307cd1aSmrg		if ((buf = (Char *) malloc((size_t) scp->data_length)) == 0) {
5025f2e35a3aSmrg		    SysError(ERROR_BMALLOC2);
50265307cd1aSmrg		} else {
50275307cd1aSmrg		    free(tcp->data_buffer);
50285307cd1aSmrg		    memcpy(buf, scp->data_buffer, scp->data_length);
50295307cd1aSmrg		    tcp->data_buffer = buf;
50305307cd1aSmrg		    tcp->data_limit = scp->data_length;
50315307cd1aSmrg		    tcp->data_length = scp->data_length;
50325307cd1aSmrg		}
5033f2e35a3aSmrg	    }
5034f2e35a3aSmrg	    scp = &(screen->selected_cells[which]);
5035f2e35a3aSmrg	    if (scp->data_length == 0) {
5036f2e35a3aSmrg		TRACE(("XtDisownSelection(%s, @%ld)\n",
5037f2e35a3aSmrg		       TraceAtomName(screen->display, atoms[i]),
5038f2e35a3aSmrg		       (long) screen->selection_time));
5039f2e35a3aSmrg		XtDisownSelection((Widget) xw,
5040f2e35a3aSmrg				  atoms[i],
5041f2e35a3aSmrg				  screen->selection_time);
5042f2e35a3aSmrg	    } else if (!screen->replyToEmacs && atoms[i] != 0) {
5043f2e35a3aSmrg		TRACE(("XtOwnSelection(%s, @%ld)\n",
5044f2e35a3aSmrg		       TraceAtomName(screen->display, atoms[i]),
5045f2e35a3aSmrg		       (long) screen->selection_time));
5046f2e35a3aSmrg		have_selection |=
5047f2e35a3aSmrg		    XtOwnSelection((Widget) xw, atoms[i],
5048f2e35a3aSmrg				   screen->selection_time,
5049f2e35a3aSmrg				   ConvertSelection,
5050f2e35a3aSmrg				   LoseSelection,
5051f2e35a3aSmrg				   SelectionDone);
5052f2e35a3aSmrg	    }
5053d522f475Smrg	}
5054f2e35a3aSmrg	TRACE(("... _OwnSelection used length %lu value %s\n",
5055f2e35a3aSmrg	       (unsigned long) scp->data_length,
5056f2e35a3aSmrg	       visibleChars(scp->data_buffer,
5057f2e35a3aSmrg			    (unsigned) scp->data_length)));
5058d522f475Smrg    }
5059d522f475Smrg    if (!screen->replyToEmacs)
5060d522f475Smrg	screen->selection_count = count;
5061d522f475Smrg    if (!have_selection)
5062f2e35a3aSmrg	UnHiliteText(xw);
5063d522f475Smrg}
5064d522f475Smrg
5065d522f475Smrgstatic void
5066e0a2b6dfSmrgResetSelectionState(TScreen *screen)
5067d522f475Smrg{
5068d522f475Smrg    screen->selection_count = 0;
5069d522f475Smrg    screen->startH = zeroCELL;
5070d522f475Smrg    screen->endH = zeroCELL;
5071d522f475Smrg}
5072d522f475Smrg
5073d522f475Smrgvoid
5074d522f475SmrgDisownSelection(XtermWidget xw)
5075d522f475Smrg{
5076956cc18dSsnj    TScreen *screen = TScreenOf(xw);
5077d522f475Smrg    Atom *atoms = screen->selection_atoms;
5078d522f475Smrg    Cardinal count = screen->selection_count;
5079d522f475Smrg    Cardinal i;
5080d522f475Smrg
5081d522f475Smrg    TRACE(("DisownSelection count %d, start %d.%d, end %d.%d\n",
5082d522f475Smrg	   count,
5083d522f475Smrg	   screen->startH.row,
5084d522f475Smrg	   screen->startH.col,
5085d522f475Smrg	   screen->endH.row,
5086d522f475Smrg	   screen->endH.col));
5087d522f475Smrg
5088d522f475Smrg    for (i = 0; i < count; i++) {
5089d522f475Smrg	int cutbuffer = CutBuffer(atoms[i]);
5090d522f475Smrg	if (cutbuffer < 0) {
5091d522f475Smrg	    XtDisownSelection((Widget) xw, atoms[i],
5092d522f475Smrg			      screen->selection_time);
5093d522f475Smrg	}
5094d522f475Smrg    }
5095d522f475Smrg    /*
5096d522f475Smrg     * If none of the callbacks via XtDisownSelection() reset highlighting
5097d522f475Smrg     * do it now.
5098d522f475Smrg     */
5099d522f475Smrg    if (ScrnHaveSelection(screen)) {
5100d522f475Smrg	/* save data which will be reset */
5101d522f475Smrg	CELL first = screen->startH;
5102d522f475Smrg	CELL last = screen->endH;
5103d522f475Smrg
5104d522f475Smrg	ResetSelectionState(screen);
5105d522f475Smrg	ReHiliteText(xw, &first, &last);
5106d522f475Smrg    } else {
5107d522f475Smrg	ResetSelectionState(screen);
5108d522f475Smrg    }
5109d522f475Smrg}
5110d522f475Smrg
5111d522f475Smrgvoid
5112d522f475SmrgUnhiliteSelection(XtermWidget xw)
5113d522f475Smrg{
5114956cc18dSsnj    TScreen *screen = TScreenOf(xw);
5115d522f475Smrg
5116d522f475Smrg    if (ScrnHaveSelection(screen)) {
5117d522f475Smrg	CELL first = screen->startH;
5118d522f475Smrg	CELL last = screen->endH;
5119d522f475Smrg
5120d522f475Smrg	screen->startH = zeroCELL;
5121d522f475Smrg	screen->endH = zeroCELL;
5122d522f475Smrg	ReHiliteText(xw, &first, &last);
5123d522f475Smrg    }
5124d522f475Smrg}
5125d522f475Smrg
5126d522f475Smrg/* returns number of chars in line from scol to ecol out */
5127d522f475Smrg/* ARGSUSED */
5128d522f475Smrgstatic int
5129e0a2b6dfSmrgLength(TScreen *screen,
5130d522f475Smrg       int row,
5131d522f475Smrg       int scol,
5132d522f475Smrg       int ecol)
5133d522f475Smrg{
513401037d57Smrg    CLineData *ld = GET_LINEDATA(screen, row);
513501037d57Smrg    const int lastcol = LastTextCol(screen, ld, row);
5136d522f475Smrg
5137d522f475Smrg    if (ecol > lastcol)
5138d522f475Smrg	ecol = lastcol;
5139d522f475Smrg    return (ecol - scol + 1);
5140d522f475Smrg}
5141d522f475Smrg
5142d522f475Smrg/* copies text into line, preallocated */
5143d522f475Smrgstatic Char *
5144e0a2b6dfSmrgSaveText(TScreen *screen,
5145d522f475Smrg	 int row,
5146d522f475Smrg	 int scol,
5147d522f475Smrg	 int ecol,
5148e0a2b6dfSmrg	 Char *lp,		/* pointer to where to put the text */
5149d522f475Smrg	 int *eol)
5150d522f475Smrg{
5151956cc18dSsnj    LineData *ld;
5152d522f475Smrg    int i = 0;
5153d522f475Smrg    Char *result = lp;
5154d522f475Smrg#if OPT_WIDE_CHARS
51552eaa94a1Schristos    unsigned previous = 0;
5156d522f475Smrg#endif
5157d522f475Smrg
5158956cc18dSsnj    ld = GET_LINEDATA(screen, row);
5159d522f475Smrg    i = Length(screen, row, scol, ecol);
5160d522f475Smrg    ecol = scol + i;
5161d522f475Smrg#if OPT_DEC_CHRSET
5162956cc18dSsnj    if (CSET_DOUBLE(GetLineDblCS(ld))) {
5163d522f475Smrg	scol = (scol + 0) / 2;
5164d522f475Smrg	ecol = (ecol + 1) / 2;
5165d522f475Smrg    }
5166d522f475Smrg#endif
5167956cc18dSsnj    *eol = !LineTstWrapped(ld);
5168d522f475Smrg    for (i = scol; i < ecol; i++) {
51692e4f8982Smrg	unsigned c;
51700bd37d32Smrg	assert(i < (int) ld->lineSize);
5171956cc18dSsnj	c = E2A(ld->charData[i]);
517204b94745Smrg	if (ld->attribs[i] & INVISIBLE)
517304b94745Smrg	    continue;
5174d522f475Smrg#if OPT_WIDE_CHARS
5175d522f475Smrg	/* We want to strip out every occurrence of HIDDEN_CHAR AFTER a
5176d522f475Smrg	 * wide character.
5177d522f475Smrg	 */
5178894e0ac8Smrg	if (c == HIDDEN_CHAR) {
5179894e0ac8Smrg	    if (isWide((int) previous)) {
5180894e0ac8Smrg		previous = c;
5181894e0ac8Smrg		/* Combining characters attached to double-width characters
5182894e0ac8Smrg		   are in memory attached to the HIDDEN_CHAR */
5183894e0ac8Smrg		if_OPT_WIDE_CHARS(screen, {
5184894e0ac8Smrg		    if ((screen->utf8_nrc_mode | screen->utf8_mode) != uFalse) {
5185894e0ac8Smrg			size_t off;
5186894e0ac8Smrg			for_each_combData(off, ld) {
51872e4f8982Smrg			    unsigned ch = ld->combData[off][i];
5188894e0ac8Smrg			    if (ch == 0)
5189894e0ac8Smrg				break;
5190894e0ac8Smrg			    lp = convertToUTF8(lp, ch);
5191894e0ac8Smrg			}
5192d522f475Smrg		    }
5193894e0ac8Smrg		});
5194894e0ac8Smrg		continue;
5195894e0ac8Smrg	    } else {
5196894e0ac8Smrg		c = ' ';	/* should not happen, but just in case... */
5197894e0ac8Smrg	    }
5198d522f475Smrg	}
5199d522f475Smrg	previous = c;
5200e0a2b6dfSmrg	if ((screen->utf8_nrc_mode | screen->utf8_mode) != uFalse) {
5201d522f475Smrg	    lp = convertToUTF8(lp, (c != 0) ? c : ' ');
5202d522f475Smrg	    if_OPT_WIDE_CHARS(screen, {
5203956cc18dSsnj		size_t off;
5204956cc18dSsnj		for_each_combData(off, ld) {
52052e4f8982Smrg		    unsigned ch = ld->combData[off][i];
5206956cc18dSsnj		    if (ch == 0)
5207d522f475Smrg			break;
5208d522f475Smrg		    lp = convertToUTF8(lp, ch);
5209d522f475Smrg		}
5210d522f475Smrg	    });
5211d522f475Smrg	} else
5212d522f475Smrg#endif
5213d522f475Smrg	{
5214d522f475Smrg	    if (c == 0) {
5215d522f475Smrg		c = E2A(' ');
5216d522f475Smrg	    } else if (c < E2A(' ')) {
5217d522f475Smrg		c = DECtoASCII(c);
5218d522f475Smrg	    } else if (c == 0x7f) {
5219d522f475Smrg		c = 0x5f;
5220d522f475Smrg	    }
52212eaa94a1Schristos	    *lp++ = CharOf(A2E(c));
5222d522f475Smrg	}
5223d522f475Smrg	if (c != E2A(' '))
5224d522f475Smrg	    result = lp;
5225d522f475Smrg    }
5226d522f475Smrg
5227d522f475Smrg    /*
5228d522f475Smrg     * If requested, trim trailing blanks from selected lines.  Do not do this
5229d522f475Smrg     * if the line is wrapped.
5230d522f475Smrg     */
5231d522f475Smrg    if (!*eol || !screen->trim_selection)
5232d522f475Smrg	result = lp;
5233d522f475Smrg
5234d522f475Smrg    return (result);
5235d522f475Smrg}
5236d522f475Smrg
5237f2e35a3aSmrg/*
5238f2e35a3aSmrg * This adds together the bits:
5239f2e35a3aSmrg *   shift key   -> 1
5240f2e35a3aSmrg *   meta key    -> 2
5241f2e35a3aSmrg *   control key -> 4
5242f2e35a3aSmrg */
5243f2e35a3aSmrgstatic unsigned
5244f2e35a3aSmrgKeyState(XtermWidget xw, unsigned x)
5245f2e35a3aSmrg{
5246f2e35a3aSmrg    return ((((x) & (ShiftMask | ControlMask)))
5247f2e35a3aSmrg	    + (((x) & MetaMask(xw)) ? 2 : 0));
5248f2e35a3aSmrg}
5249f2e35a3aSmrg
5250f2e35a3aSmrg/* 32 + following 8-bit word:
5251d522f475Smrg
5252d522f475Smrg   1:0  Button no: 0, 1, 2.  3=release.
5253d522f475Smrg     2  shift
5254d522f475Smrg     3  meta
5255d522f475Smrg     4  ctrl
5256d522f475Smrg     5  set for motion notify
5257f2e35a3aSmrg     6  set for wheel (and button 6 and 7)
5258f2e35a3aSmrg     7  set for buttons 8 to 11
5259d522f475Smrg*/
5260d522f475Smrg
5261d522f475Smrg/* Position: 32 - 255. */
5262a1f3da82Smrgstatic int
5263f2e35a3aSmrgBtnCode(XtermWidget xw, XButtonEvent *event, int button)
5264d522f475Smrg{
5265f2e35a3aSmrg    int result = (int) (32 + (KeyState(xw, event->state) << 2));
5266d522f475Smrg
52670bd37d32Smrg    if (event->type == MotionNotify)
52680bd37d32Smrg	result += 32;
52690bd37d32Smrg
5270f2e35a3aSmrg    if (button < 0) {
5271d522f475Smrg	result += 3;
5272d522f475Smrg    } else {
5273f2e35a3aSmrg	result += button & 3;
5274f2e35a3aSmrg	if (button & 4)
5275f2e35a3aSmrg	    result += 64;
5276f2e35a3aSmrg	if (button & 8)
5277f2e35a3aSmrg	    result += 128;
5278d522f475Smrg    }
52790bd37d32Smrg    TRACE(("BtnCode button %d, %s state " FMT_MODIFIER_NAMES " ->%#x\n",
52800bd37d32Smrg	   button,
52810bd37d32Smrg	   visibleEventType(event->type),
52820bd37d32Smrg	   ARG_MODIFIER_NAMES(event->state),
52830bd37d32Smrg	   result));
5284a1f3da82Smrg    return result;
5285a1f3da82Smrg}
5286a1f3da82Smrg
5287a1f3da82Smrgstatic unsigned
5288913cc679SmrgEmitButtonCode(XtermWidget xw,
5289e0a2b6dfSmrg	       Char *line,
52900bd37d32Smrg	       unsigned count,
5291894e0ac8Smrg	       XButtonEvent *event,
52920bd37d32Smrg	       int button)
5293a1f3da82Smrg{
5294913cc679Smrg    TScreen *screen = TScreenOf(xw);
52950bd37d32Smrg    int value;
5296a1f3da82Smrg
5297913cc679Smrg    if (okSendMousePos(xw) == X10_MOUSE) {
52980bd37d32Smrg	value = CharOf(' ' + button);
5299a1f3da82Smrg    } else {
5300f2e35a3aSmrg	value = BtnCode(xw, event, button);
53010bd37d32Smrg    }
53020bd37d32Smrg
53030bd37d32Smrg    switch (screen->extend_coords) {
53040bd37d32Smrg    default:
53050bd37d32Smrg	line[count++] = CharOf(value);
53060bd37d32Smrg	break;
53070bd37d32Smrg    case SET_SGR_EXT_MODE_MOUSE:
5308f2e35a3aSmrg    case SET_PIXEL_POSITION_MOUSE:
53090bd37d32Smrg	value -= 32;		/* encoding starts at zero */
53100bd37d32Smrg	/* FALLTHRU */
53110bd37d32Smrg    case SET_URXVT_EXT_MODE_MOUSE:
53120bd37d32Smrg	count += (unsigned) sprintf((char *) line + count, "%d", value);
53130bd37d32Smrg	break;
53140bd37d32Smrg    case SET_EXT_MODE_MOUSE:
53150bd37d32Smrg	if (value < 128) {
53160bd37d32Smrg	    line[count++] = CharOf(value);
53170bd37d32Smrg	} else {
53180bd37d32Smrg	    line[count++] = CharOf(0xC0 + (value >> 6));
53190bd37d32Smrg	    line[count++] = CharOf(0x80 + (value & 0x3F));
53200bd37d32Smrg	}
53210bd37d32Smrg	break;
5322a1f3da82Smrg    }
5323a1f3da82Smrg    return count;
5324d522f475Smrg}
5325d522f475Smrg
53260bd37d32Smrgstatic int
53270bd37d32SmrgFirstBitN(int bits)
53280bd37d32Smrg{
53290bd37d32Smrg    int result = -1;
53300bd37d32Smrg    if (bits > 0) {
53310bd37d32Smrg	result = 0;
53320bd37d32Smrg	while (!(bits & 1)) {
53330bd37d32Smrg	    bits /= 2;
53340bd37d32Smrg	    ++result;
53350bd37d32Smrg	}
53360bd37d32Smrg    }
53370bd37d32Smrg    return result;
53380bd37d32Smrg}
53390bd37d32Smrg
53400bd37d32Smrg#define ButtonBit(button) ((button >= 0) ? (1 << (button)) : 0)
53410bd37d32Smrg
5342913cc679Smrg#define EMIT_BUTTON(button) EmitButtonCode(xw, line, count, event, button)
53430bd37d32Smrg
5344d522f475Smrgstatic void
5345894e0ac8SmrgEditorButton(XtermWidget xw, XButtonEvent *event)
5346d522f475Smrg{
5347956cc18dSsnj    TScreen *screen = TScreenOf(xw);
5348d522f475Smrg    int pty = screen->respond;
53490bd37d32Smrg    int mouse_limit = MouseLimit(screen);
53500bd37d32Smrg    Char line[32];
53510bd37d32Smrg    Char final = 'M';
5352d522f475Smrg    int row, col;
5353d522f475Smrg    int button;
5354d522f475Smrg    unsigned count = 0;
5355d522f475Smrg    Boolean changed = True;
5356d522f475Smrg
5357d522f475Smrg    /* If button event, get button # adjusted for DEC compatibility */
53582eaa94a1Schristos    button = (int) (event->button - 1);
5359d522f475Smrg    if (button >= 3)
5360d522f475Smrg	button++;
5361d522f475Smrg
5362f2e35a3aSmrg    /* Ignore buttons that cannot be encoded */
5363f2e35a3aSmrg    if (screen->send_mouse_pos == X10_MOUSE) {
5364f2e35a3aSmrg	if (button > 3)
5365f2e35a3aSmrg	    return;
5366f2e35a3aSmrg    } else if (screen->extend_coords == SET_SGR_EXT_MODE_MOUSE
5367f2e35a3aSmrg	       || screen->extend_coords == SET_URXVT_EXT_MODE_MOUSE
5368f2e35a3aSmrg	       || screen->extend_coords == SET_PIXEL_POSITION_MOUSE) {
5369f2e35a3aSmrg	if (button > 15) {
5370f2e35a3aSmrg	    return;
5371f2e35a3aSmrg	}
5372f2e35a3aSmrg    } else {
5373f2e35a3aSmrg	if (button > 11) {
5374f2e35a3aSmrg	    return;
5375f2e35a3aSmrg	}
5376f2e35a3aSmrg    }
5377d522f475Smrg
5378f2e35a3aSmrg    if (screen->extend_coords == SET_PIXEL_POSITION_MOUSE) {
5379f2e35a3aSmrg	row = event->y - OriginY(screen);
5380f2e35a3aSmrg	col = event->x - OriginX(screen);
5381f2e35a3aSmrg    } else {
5382f2e35a3aSmrg	/* Compute character position of mouse pointer */
5383f2e35a3aSmrg	row = (event->y - screen->border) / FontHeight(screen);
5384f2e35a3aSmrg	col = (event->x - OriginX(screen)) / FontWidth(screen);
5385d522f475Smrg
5386f2e35a3aSmrg	/* Limit to screen dimensions */
5387f2e35a3aSmrg	if (row < 0)
5388f2e35a3aSmrg	    row = 0;
5389f2e35a3aSmrg	else if (row > screen->max_row)
5390f2e35a3aSmrg	    row = screen->max_row;
5391492d43a5Smrg
5392f2e35a3aSmrg	if (col < 0)
5393f2e35a3aSmrg	    col = 0;
5394f2e35a3aSmrg	else if (col > screen->max_col)
5395f2e35a3aSmrg	    col = screen->max_col;
5396f2e35a3aSmrg
5397f2e35a3aSmrg	if (mouse_limit > 0) {
5398f2e35a3aSmrg	    /* Limit to representable mouse dimensions */
5399f2e35a3aSmrg	    if (row > mouse_limit)
5400f2e35a3aSmrg		row = mouse_limit;
5401f2e35a3aSmrg	    if (col > mouse_limit)
5402f2e35a3aSmrg		col = mouse_limit;
5403f2e35a3aSmrg	}
54040bd37d32Smrg    }
5405d522f475Smrg
5406d522f475Smrg    /* Build key sequence starting with \E[M */
5407d522f475Smrg    if (screen->control_eight_bits) {
5408d522f475Smrg	line[count++] = ANSI_CSI;
5409d522f475Smrg    } else {
5410d522f475Smrg	line[count++] = ANSI_ESC;
5411d522f475Smrg	line[count++] = '[';
5412d522f475Smrg    }
54130bd37d32Smrg    switch (screen->extend_coords) {
54140bd37d32Smrg    case 0:
54150bd37d32Smrg    case SET_EXT_MODE_MOUSE:
5416d522f475Smrg#if OPT_SCO_FUNC_KEYS
54170bd37d32Smrg	if (xw->keyboard.type == keyboardIsSCO) {
54180bd37d32Smrg	    /*
54190bd37d32Smrg	     * SCO function key F1 is \E[M, which would conflict with xterm's
54200bd37d32Smrg	     * normal kmous.
54210bd37d32Smrg	     */
54220bd37d32Smrg	    line[count++] = '>';
54230bd37d32Smrg	}
5424d522f475Smrg#endif
54250bd37d32Smrg	line[count++] = final;
54260bd37d32Smrg	break;
54270bd37d32Smrg    case SET_SGR_EXT_MODE_MOUSE:
5428f2e35a3aSmrg    case SET_PIXEL_POSITION_MOUSE:
54290bd37d32Smrg	line[count++] = '<';
54300bd37d32Smrg	break;
54310bd37d32Smrg    }
5432d522f475Smrg
5433d522f475Smrg    /* Add event code to key sequence */
5434913cc679Smrg    if (okSendMousePos(xw) == X10_MOUSE) {
54350bd37d32Smrg	count = EMIT_BUTTON(button);
5436d522f475Smrg    } else {
5437d522f475Smrg	/* Button-Motion events */
5438d522f475Smrg	switch (event->type) {
5439d522f475Smrg	case ButtonPress:
54400bd37d32Smrg	    screen->mouse_button |= ButtonBit(button);
54410bd37d32Smrg	    count = EMIT_BUTTON(button);
5442d522f475Smrg	    break;
5443d522f475Smrg	case ButtonRelease:
5444d522f475Smrg	    /*
5445f2e35a3aSmrg	     * The (vertical) wheel mouse interface generates release-events
5446f2e35a3aSmrg	     * for buttons 4 and 5.
5447f2e35a3aSmrg	     *
5448f2e35a3aSmrg	     * The X10/X11 xterm protocol maps the release for buttons 1..3 to
5449f2e35a3aSmrg	     * a -1, which will be later mapped into a "0" (some button was
5450f2e35a3aSmrg	     * released),  At this point, buttons 1..3 are encoded 0..2 (the
5451f2e35a3aSmrg	     * code 3 is unused).
5452f2e35a3aSmrg	     *
5453f2e35a3aSmrg	     * The SGR (extended) xterm mouse protocol keeps the button number
5454f2e35a3aSmrg	     * and uses a "m" to indicate button release.
5455f2e35a3aSmrg	     *
5456f2e35a3aSmrg	     * The behavior for mice with more buttons is unclear, and may be
5457f2e35a3aSmrg	     * revised -TD
5458d522f475Smrg	     */
54590bd37d32Smrg	    screen->mouse_button &= ~ButtonBit(button);
5460f2e35a3aSmrg	    if (button < 3 || button > 5) {
54610bd37d32Smrg		switch (screen->extend_coords) {
54620bd37d32Smrg		case SET_SGR_EXT_MODE_MOUSE:
5463f2e35a3aSmrg		case SET_PIXEL_POSITION_MOUSE:
54640bd37d32Smrg		    final = 'm';
54650bd37d32Smrg		    break;
54660bd37d32Smrg		default:
54670bd37d32Smrg		    button = -1;
54680bd37d32Smrg		    break;
54690bd37d32Smrg		}
54700bd37d32Smrg	    }
54710bd37d32Smrg	    count = EMIT_BUTTON(button);
5472d522f475Smrg	    break;
5473d522f475Smrg	case MotionNotify:
5474d522f475Smrg	    /* BTN_EVENT_MOUSE and ANY_EVENT_MOUSE modes send motion
5475d522f475Smrg	     * events only if character cell has changed.
5476d522f475Smrg	     */
5477d522f475Smrg	    if ((row == screen->mouse_row)
5478d522f475Smrg		&& (col == screen->mouse_col)) {
5479d522f475Smrg		changed = False;
5480d522f475Smrg	    } else {
54810bd37d32Smrg		count = EMIT_BUTTON(FirstBitN(screen->mouse_button));
5482d522f475Smrg	    }
5483d522f475Smrg	    break;
5484d522f475Smrg	default:
5485d522f475Smrg	    changed = False;
5486d522f475Smrg	    break;
5487d522f475Smrg	}
5488d522f475Smrg    }
5489d522f475Smrg
5490d522f475Smrg    if (changed) {
5491d522f475Smrg	screen->mouse_row = row;
5492d522f475Smrg	screen->mouse_col = col;
5493d522f475Smrg
5494492d43a5Smrg	TRACE(("mouse at %d,%d button+mask = %#x\n", row, col, line[count - 1]));
5495d522f475Smrg
5496492d43a5Smrg	/* Add pointer position to key sequence */
54970bd37d32Smrg	count = EmitMousePositionSeparator(screen, line, count);
5498492d43a5Smrg	count = EmitMousePosition(screen, line, count, col);
54990bd37d32Smrg	count = EmitMousePositionSeparator(screen, line, count);
5500492d43a5Smrg	count = EmitMousePosition(screen, line, count, row);
5501d522f475Smrg
55020bd37d32Smrg	switch (screen->extend_coords) {
55030bd37d32Smrg	case SET_SGR_EXT_MODE_MOUSE:
55040bd37d32Smrg	case SET_URXVT_EXT_MODE_MOUSE:
5505f2e35a3aSmrg	case SET_PIXEL_POSITION_MOUSE:
55060bd37d32Smrg	    line[count++] = final;
55070bd37d32Smrg	    break;
55080bd37d32Smrg	}
55090bd37d32Smrg
5510d522f475Smrg	/* Transmit key sequence to process running under xterm */
5511f2e35a3aSmrg	TRACE(("EditorButton -> %s\n", visibleChars(line, count)));
55125307cd1aSmrg	v_write(pty, line, (size_t) count);
5513d522f475Smrg    }
5514d522f475Smrg    return;
5515d522f475Smrg}
5516d522f475Smrg
5517913cc679Smrg/*
5518913cc679Smrg * Check the current send_mouse_pos against allowed mouse-operations, returning
5519913cc679Smrg * none if it is disallowed.
5520913cc679Smrg */
5521913cc679SmrgXtermMouseModes
5522913cc679SmrgokSendMousePos(XtermWidget xw)
5523913cc679Smrg{
5524913cc679Smrg    TScreen *screen = TScreenOf(xw);
5525f2e35a3aSmrg    XtermMouseModes result = (XtermMouseModes) screen->send_mouse_pos;
5526913cc679Smrg
5527f2e35a3aSmrg    switch ((int) result) {
5528913cc679Smrg    case MOUSE_OFF:
5529913cc679Smrg	break;
5530913cc679Smrg    case X10_MOUSE:
5531913cc679Smrg	if (!AllowMouseOps(xw, emX10))
5532913cc679Smrg	    result = MOUSE_OFF;
5533913cc679Smrg	break;
5534913cc679Smrg    case VT200_MOUSE:
5535913cc679Smrg	if (!AllowMouseOps(xw, emVT200Click))
5536913cc679Smrg	    result = MOUSE_OFF;
5537913cc679Smrg	break;
5538913cc679Smrg    case VT200_HIGHLIGHT_MOUSE:
5539913cc679Smrg	if (!AllowMouseOps(xw, emVT200Hilite))
5540913cc679Smrg	    result = MOUSE_OFF;
5541913cc679Smrg	break;
5542913cc679Smrg    case BTN_EVENT_MOUSE:
5543913cc679Smrg	if (!AllowMouseOps(xw, emAnyButton))
5544913cc679Smrg	    result = MOUSE_OFF;
5545913cc679Smrg	break;
5546913cc679Smrg    case ANY_EVENT_MOUSE:
5547913cc679Smrg	if (!AllowMouseOps(xw, emAnyEvent))
5548913cc679Smrg	    result = MOUSE_OFF;
5549913cc679Smrg	break;
5550913cc679Smrg    case DEC_LOCATOR:
5551913cc679Smrg	if (!AllowMouseOps(xw, emLocator))
5552913cc679Smrg	    result = MOUSE_OFF;
5553913cc679Smrg	break;
5554913cc679Smrg    }
5555913cc679Smrg    return result;
5556913cc679Smrg}
5557913cc679Smrg
5558d522f475Smrg#if OPT_FOCUS_EVENT
5559913cc679Smrg/*
5560913cc679Smrg * Check the current send_focus_pos against allowed mouse-operations, returning
5561913cc679Smrg * none if it is disallowed.
5562913cc679Smrg */
5563913cc679Smrgstatic int
5564913cc679SmrgokSendFocusPos(XtermWidget xw)
5565d522f475Smrg{
5566956cc18dSsnj    TScreen *screen = TScreenOf(xw);
5567913cc679Smrg    int result = screen->send_focus_pos;
5568913cc679Smrg
5569913cc679Smrg    if (!AllowMouseOps(xw, emFocusEvent)) {
5570913cc679Smrg	result = False;
5571913cc679Smrg    }
5572913cc679Smrg    return result;
5573913cc679Smrg}
5574d522f475Smrg
5575913cc679Smrgvoid
5576913cc679SmrgSendFocusButton(XtermWidget xw, XFocusChangeEvent *event)
5577913cc679Smrg{
5578913cc679Smrg    if (okSendFocusPos(xw)) {
5579d522f475Smrg	ANSI reply;
5580d522f475Smrg
5581d522f475Smrg	memset(&reply, 0, sizeof(reply));
5582d522f475Smrg	reply.a_type = ANSI_CSI;
5583d522f475Smrg
5584d522f475Smrg#if OPT_SCO_FUNC_KEYS
5585d522f475Smrg	if (xw->keyboard.type == keyboardIsSCO) {
5586d522f475Smrg	    reply.a_pintro = '>';
5587d522f475Smrg	}
5588d522f475Smrg#endif
55892eaa94a1Schristos	reply.a_final = CharOf((event->type == FocusIn) ? 'I' : 'O');
5590d522f475Smrg	unparseseq(xw, &reply);
5591d522f475Smrg    }
5592d522f475Smrg    return;
5593d522f475Smrg}
5594d522f475Smrg#endif /* OPT_FOCUS_EVENT */
55950bd37d32Smrg
55960bd37d32Smrg#if OPT_SELECTION_OPS
55970bd37d32Smrg/*
55980bd37d32Smrg * Get the event-time, needed to process selections.
55990bd37d32Smrg */
56000bd37d32Smrgstatic Time
5601894e0ac8SmrggetEventTime(XEvent *event)
56020bd37d32Smrg{
56030bd37d32Smrg    Time result;
56040bd37d32Smrg
56050bd37d32Smrg    if (IsBtnEvent(event)) {
56060bd37d32Smrg	result = ((XButtonEvent *) event)->time;
56070bd37d32Smrg    } else if (IsKeyEvent(event)) {
56080bd37d32Smrg	result = ((XKeyEvent *) event)->time;
56090bd37d32Smrg    } else {
56100bd37d32Smrg	result = 0;
56110bd37d32Smrg    }
56120bd37d32Smrg
56130bd37d32Smrg    return result;
56140bd37d32Smrg}
56150bd37d32Smrg
56160bd37d32Smrg/* obtain the selection string, passing the endpoints to caller's parameters */
561701037d57Smrgstatic void
561801037d57SmrgdoSelectionFormat(XtermWidget xw,
561901037d57Smrg		  Widget w,
562001037d57Smrg		  XEvent *event,
562101037d57Smrg		  String *params,
562201037d57Smrg		  Cardinal *num_params,
562301037d57Smrg		  FormatSelect format_select)
56240bd37d32Smrg{
56250bd37d32Smrg    TScreen *screen = TScreenOf(xw);
562601037d57Smrg    InternalSelect *mydata = &(screen->internal_select);
562701037d57Smrg
562801037d57Smrg    memset(mydata, 0, sizeof(*mydata));
562901037d57Smrg    mydata->format = x_strdup(params[0]);
563001037d57Smrg    mydata->format_select = format_select;
56310bd37d32Smrg
56320bd37d32Smrg    screen->selectToBuffer = True;
5633f2e35a3aSmrg    beginInternalSelect(xw);
5634f2e35a3aSmrg
56350bd37d32Smrg    xtermGetSelection(w, getEventTime(event), params + 1, *num_params - 1, NULL);
5636f2e35a3aSmrg
5637f2e35a3aSmrg    if (screen->selectToBuffer)
5638f2e35a3aSmrg	finishInternalSelect(xw);
56390bd37d32Smrg}
56400bd37d32Smrg
56410bd37d32Smrg/* obtain data from the screen, passing the endpoints to caller's parameters */
56420bd37d32Smrgstatic char *
5643913cc679SmrggetDataFromScreen(XtermWidget xw, XEvent *event, String method, CELL *start, CELL *finish)
56440bd37d32Smrg{
56450bd37d32Smrg    TScreen *screen = TScreenOf(xw);
56460bd37d32Smrg
56470bd37d32Smrg    CELL save_old_start = screen->startH;
56480bd37d32Smrg    CELL save_old_end = screen->endH;
56490bd37d32Smrg
56500bd37d32Smrg    CELL save_startSel = screen->startSel;
56510bd37d32Smrg    CELL save_startRaw = screen->startRaw;
56520bd37d32Smrg    CELL save_finishSel = screen->endSel;
56530bd37d32Smrg    CELL save_finishRaw = screen->endRaw;
56540bd37d32Smrg
56550bd37d32Smrg    int save_firstValidRow = screen->firstValidRow;
56560bd37d32Smrg    int save_lastValidRow = screen->lastValidRow;
56570bd37d32Smrg
565801037d57Smrg    const Cardinal noClick = 0;
565901037d57Smrg    int save_numberOfClicks = screen->numberOfClicks;
566001037d57Smrg
56610bd37d32Smrg    SelectUnit saveUnits = screen->selectUnit;
566201037d57Smrg    SelectUnit saveMap = screen->selectMap[noClick];
56630bd37d32Smrg#if OPT_SELECT_REGEX
566401037d57Smrg    char *saveExpr = screen->selectExpr[noClick];
56650bd37d32Smrg#endif
5666f2e35a3aSmrg    SelectedCells *scp = &(screen->selected_cells[PRIMARY_CODE]);
5667f2e35a3aSmrg    SelectedCells save_selection = *scp;
56680bd37d32Smrg
56690bd37d32Smrg    char *result = 0;
56700bd37d32Smrg
56710bd37d32Smrg    TRACE(("getDataFromScreen %s\n", method));
56720bd37d32Smrg
5673f2e35a3aSmrg    memset(scp, 0, sizeof(*scp));
56740bd37d32Smrg
567501037d57Smrg    screen->numberOfClicks = 1;
567601037d57Smrg    lookupSelectUnit(xw, noClick, method);
567701037d57Smrg    screen->selectUnit = screen->selectMap[noClick];
56780bd37d32Smrg
56790bd37d32Smrg    memset(start, 0, sizeof(*start));
5680913cc679Smrg    if (IsBtnEvent(event)) {
5681913cc679Smrg	XButtonEvent *btn_event = (XButtonEvent *) event;
5682913cc679Smrg	CELL cell;
5683913cc679Smrg	screen->firstValidRow = 0;
5684913cc679Smrg	screen->lastValidRow = screen->max_row;
5685913cc679Smrg	PointToCELL(screen, btn_event->y, btn_event->x, &cell);
5686913cc679Smrg	start->row = cell.row;
5687913cc679Smrg	start->col = cell.col;
5688913cc679Smrg	finish->row = cell.row;
5689913cc679Smrg	finish->col = screen->max_col;
5690913cc679Smrg    } else {
5691913cc679Smrg	start->row = screen->cur_row;
5692913cc679Smrg	start->col = screen->cur_col;
5693913cc679Smrg	finish->row = screen->cur_row;
5694913cc679Smrg	finish->col = screen->max_col;
5695913cc679Smrg    }
56960bd37d32Smrg
5697f2e35a3aSmrg    ComputeSelect(xw, start, finish, False, False);
5698f2e35a3aSmrg    SaltTextAway(xw,
5699f2e35a3aSmrg		 TargetToSelection(screen, PRIMARY_NAME),
5700f2e35a3aSmrg		 &(screen->startSel), &(screen->endSel));
57010bd37d32Smrg
5702f2e35a3aSmrg    if (scp->data_limit && scp->data_buffer) {
5703f2e35a3aSmrg	TRACE(("...getDataFromScreen selection-data %.*s\n",
5704f2e35a3aSmrg	       (int) scp->data_limit,
5705f2e35a3aSmrg	       scp->data_buffer));
5706f2e35a3aSmrg	result = malloc(scp->data_limit + 1);
57070bd37d32Smrg	if (result) {
5708f2e35a3aSmrg	    memcpy(result, scp->data_buffer, scp->data_limit);
5709f2e35a3aSmrg	    result[scp->data_limit] = 0;
57100bd37d32Smrg	}
5711f2e35a3aSmrg	free(scp->data_buffer);
5712f2e35a3aSmrg	scp->data_limit = 0;
57130bd37d32Smrg    }
57140bd37d32Smrg
57150bd37d32Smrg    TRACE(("...getDataFromScreen restoring previous selection\n"));
57160bd37d32Smrg
57170bd37d32Smrg    screen->startSel = save_startSel;
57180bd37d32Smrg    screen->startRaw = save_startRaw;
57190bd37d32Smrg    screen->endSel = save_finishSel;
57200bd37d32Smrg    screen->endRaw = save_finishRaw;
57210bd37d32Smrg
57220bd37d32Smrg    screen->firstValidRow = save_firstValidRow;
57230bd37d32Smrg    screen->lastValidRow = save_lastValidRow;
57240bd37d32Smrg
572501037d57Smrg    screen->numberOfClicks = save_numberOfClicks;
57260bd37d32Smrg    screen->selectUnit = saveUnits;
572701037d57Smrg    screen->selectMap[noClick] = saveMap;
57280bd37d32Smrg#if OPT_SELECT_REGEX
572901037d57Smrg    screen->selectExpr[noClick] = saveExpr;
57300bd37d32Smrg#endif
57310bd37d32Smrg
5732f2e35a3aSmrg    screen->selected_cells[0] = save_selection;
57330bd37d32Smrg
57340bd37d32Smrg    TrackText(xw, &save_old_start, &save_old_end);
57350bd37d32Smrg
57360bd37d32Smrg    TRACE(("...getDataFromScreen done\n"));
57370bd37d32Smrg    return result;
57380bd37d32Smrg}
57390bd37d32Smrg
57405307cd1aSmrg#if OPT_EXEC_SELECTION
57410bd37d32Smrg/*
57420bd37d32Smrg * Split-up the format before substituting data, to avoid quoting issues.
57430bd37d32Smrg * The resource mechanism has a limited ability to handle escapes.  We take
57440bd37d32Smrg * the result as if it were an sh-type string and parse it into a regular
57450bd37d32Smrg * argv array.
57460bd37d32Smrg */
57470bd37d32Smrgstatic char **
57480bd37d32SmrgtokenizeFormat(String format)
57490bd37d32Smrg{
57500bd37d32Smrg    char **result = 0;
57510bd37d32Smrg
57520bd37d32Smrg    format = x_skip_blanks(format);
57530bd37d32Smrg    if (*format != '\0') {
57540bd37d32Smrg	char *blob = x_strdup(format);
57552e4f8982Smrg	int pass;
57560bd37d32Smrg
57570bd37d32Smrg	for (pass = 0; pass < 2; ++pass) {
57580bd37d32Smrg	    int used = 0;
57590bd37d32Smrg	    int first = 1;
57600bd37d32Smrg	    int escaped = 0;
57610bd37d32Smrg	    int squoted = 0;
57620bd37d32Smrg	    int dquoted = 0;
57632e4f8982Smrg	    int n;
5764f2e35a3aSmrg	    int argc = 0;
57650bd37d32Smrg
57660bd37d32Smrg	    for (n = 0; format[n] != '\0'; ++n) {
57670bd37d32Smrg		if (escaped) {
57680bd37d32Smrg		    blob[used++] = format[n];
57690bd37d32Smrg		    escaped = 0;
57700bd37d32Smrg		} else if (format[n] == '"') {
57710bd37d32Smrg		    if (!squoted) {
57720bd37d32Smrg			if (!dquoted)
57730bd37d32Smrg			    blob[used++] = format[n];
57740bd37d32Smrg			dquoted = !dquoted;
57750bd37d32Smrg		    }
57760bd37d32Smrg		} else if (format[n] == '\'') {
57770bd37d32Smrg		    if (!dquoted) {
57780bd37d32Smrg			if (!squoted)
57790bd37d32Smrg			    blob[used++] = format[n];
57800bd37d32Smrg			squoted = !squoted;
57810bd37d32Smrg		    }
57820bd37d32Smrg		} else if (format[n] == '\\') {
57830bd37d32Smrg		    blob[used++] = format[n];
57840bd37d32Smrg		    escaped = 1;
57850bd37d32Smrg		} else {
57860bd37d32Smrg		    if (first) {
57870bd37d32Smrg			first = 0;
57880bd37d32Smrg			if (pass) {
57890bd37d32Smrg			    result[argc] = &blob[n];
57900bd37d32Smrg			}
57910bd37d32Smrg			++argc;
57920bd37d32Smrg		    }
57930bd37d32Smrg		    if (isspace((Char) format[n])) {
57940bd37d32Smrg			first = !isspace((Char) format[n + 1]);
57950bd37d32Smrg			if (squoted || dquoted) {
57960bd37d32Smrg			    blob[used++] = format[n];
57970bd37d32Smrg			} else if (first) {
57980bd37d32Smrg			    blob[used++] = '\0';
57990bd37d32Smrg			}
58000bd37d32Smrg		    } else {
58010bd37d32Smrg			blob[used++] = format[n];
58020bd37d32Smrg		    }
58030bd37d32Smrg		}
58040bd37d32Smrg	    }
58050bd37d32Smrg	    blob[used] = '\0';
58060bd37d32Smrg	    assert(strlen(blob) <= strlen(format));
58070bd37d32Smrg	    if (!pass) {
58080bd37d32Smrg		result = TypeCallocN(char *, argc + 1);
58090bd37d32Smrg		if (result == 0) {
58100bd37d32Smrg		    free(blob);
58110bd37d32Smrg		    break;
58120bd37d32Smrg		}
58130bd37d32Smrg	    }
58140bd37d32Smrg	}
58150bd37d32Smrg    }
58160bd37d32Smrg#if OPT_TRACE
58170bd37d32Smrg    if (result) {
5818f2e35a3aSmrg	int n;
58190bd37d32Smrg	TRACE(("tokenizeFormat %s\n", format));
5820f2e35a3aSmrg	for (n = 0; result[n]; ++n) {
5821f2e35a3aSmrg	    TRACE(("argv[%d] = %s\n", n, result[n]));
58220bd37d32Smrg	}
58230bd37d32Smrg    }
58240bd37d32Smrg#endif
58250bd37d32Smrg
58260bd37d32Smrg    return result;
58270bd37d32Smrg}
58285307cd1aSmrg#endif /* OPT_EXEC_SELECTION */
58290bd37d32Smrg
58300bd37d32Smrgstatic void
5831e0a2b6dfSmrgformatVideoAttrs(XtermWidget xw, char *buffer, CELL *cell)
58320bd37d32Smrg{
58330bd37d32Smrg    TScreen *screen = TScreenOf(xw);
58340bd37d32Smrg    LineData *ld = GET_LINEDATA(screen, cell->row);
58350bd37d32Smrg
58360bd37d32Smrg    *buffer = '\0';
58370bd37d32Smrg    if (ld != 0 && cell->col < (int) ld->lineSize) {
5838894e0ac8Smrg	IAttr attribs = ld->attribs[cell->col];
58390bd37d32Smrg	const char *delim = "";
58400bd37d32Smrg
58410bd37d32Smrg	if (attribs & INVERSE) {
58420bd37d32Smrg	    buffer += sprintf(buffer, "7");
58430bd37d32Smrg	    delim = ";";
58440bd37d32Smrg	}
58450bd37d32Smrg	if (attribs & UNDERLINE) {
58460bd37d32Smrg	    buffer += sprintf(buffer, "%s4", delim);
58470bd37d32Smrg	    delim = ";";
58480bd37d32Smrg	}
58490bd37d32Smrg	if (attribs & BOLD) {
58500bd37d32Smrg	    buffer += sprintf(buffer, "%s1", delim);
58510bd37d32Smrg	    delim = ";";
58520bd37d32Smrg	}
58530bd37d32Smrg	if (attribs & BLINK) {
58540bd37d32Smrg	    buffer += sprintf(buffer, "%s5", delim);
58550bd37d32Smrg	    delim = ";";
58560bd37d32Smrg	}
58570bd37d32Smrg#if OPT_ISO_COLORS
58580bd37d32Smrg	if (attribs & FG_COLOR) {
5859f2e35a3aSmrg	    Pixel fg = extract_fg(xw, ld->color[cell->col], attribs);
58600bd37d32Smrg	    if (fg < 8) {
58610bd37d32Smrg		fg += 30;
58620bd37d32Smrg	    } else if (fg < 16) {
58630bd37d32Smrg		fg += 90;
58640bd37d32Smrg	    } else {
58650bd37d32Smrg		buffer += sprintf(buffer, "%s38;5", delim);
58660bd37d32Smrg		delim = ";";
58670bd37d32Smrg	    }
5868f2e35a3aSmrg	    buffer += sprintf(buffer, "%s%lu", delim, fg);
58690bd37d32Smrg	    delim = ";";
58700bd37d32Smrg	}
58710bd37d32Smrg	if (attribs & BG_COLOR) {
5872f2e35a3aSmrg	    Pixel bg = extract_bg(xw, ld->color[cell->col], attribs);
58730bd37d32Smrg	    if (bg < 8) {
58740bd37d32Smrg		bg += 40;
58750bd37d32Smrg	    } else if (bg < 16) {
58760bd37d32Smrg		bg += 100;
58770bd37d32Smrg	    } else {
58780bd37d32Smrg		buffer += sprintf(buffer, "%s48;5", delim);
58790bd37d32Smrg		delim = ";";
58800bd37d32Smrg	    }
5881f2e35a3aSmrg	    (void) sprintf(buffer, "%s%lu", delim, bg);
58820bd37d32Smrg	}
58830bd37d32Smrg#endif
58840bd37d32Smrg    }
58850bd37d32Smrg}
58860bd37d32Smrg
58872e4f8982Smrgstatic char *
58882e4f8982SmrgformatStrlen(char *target, char *source, int freeit)
58892e4f8982Smrg{
58902e4f8982Smrg    if (source != 0) {
58912e4f8982Smrg	sprintf(target, "%u", (unsigned) strlen(source));
58922e4f8982Smrg	if (freeit) {
58932e4f8982Smrg	    free(source);
58942e4f8982Smrg	}
58952e4f8982Smrg    } else {
58962e4f8982Smrg	strcpy(target, "0");
58972e4f8982Smrg    }
58982e4f8982Smrg    return target;
58992e4f8982Smrg}
59002e4f8982Smrg
59010bd37d32Smrg/* substitute data into format, reallocating the result */
59020bd37d32Smrgstatic char *
59030bd37d32SmrgexpandFormat(XtermWidget xw,
59040bd37d32Smrg	     const char *format,
59050bd37d32Smrg	     char *data,
5906e0a2b6dfSmrg	     CELL *start,
5907e0a2b6dfSmrg	     CELL *finish)
59080bd37d32Smrg{
59090bd37d32Smrg    char *result = 0;
59100bd37d32Smrg    if (!IsEmpty(format)) {
59110bd37d32Smrg	static char empty[1];
59120bd37d32Smrg	int pass;
59130bd37d32Smrg	int n;
59140bd37d32Smrg	char numbers[80];
59150bd37d32Smrg
59160bd37d32Smrg	if (data == 0)
59170bd37d32Smrg	    data = empty;
59180bd37d32Smrg
59190bd37d32Smrg	for (pass = 0; pass < 2; ++pass) {
59200bd37d32Smrg	    size_t need = 0;
59210bd37d32Smrg
59220bd37d32Smrg	    for (n = 0; format[n] != '\0'; ++n) {
59230bd37d32Smrg
59240bd37d32Smrg		if (format[n] == '%') {
59252e4f8982Smrg		    char *value = 0;
59262e4f8982Smrg
59270bd37d32Smrg		    switch (format[++n]) {
59280bd37d32Smrg		    case '%':
59290bd37d32Smrg			if (pass) {
59300bd37d32Smrg			    result[need] = format[n];
59310bd37d32Smrg			}
59320bd37d32Smrg			++need;
59330bd37d32Smrg			break;
59340bd37d32Smrg		    case 'P':
59350bd37d32Smrg			sprintf(numbers, "%d;%d",
59360bd37d32Smrg				TScreenOf(xw)->topline + start->row + 1,
59370bd37d32Smrg				start->col + 1);
59380bd37d32Smrg			value = numbers;
59390bd37d32Smrg			break;
59400bd37d32Smrg		    case 'p':
59410bd37d32Smrg			sprintf(numbers, "%d;%d",
59420bd37d32Smrg				TScreenOf(xw)->topline + finish->row + 1,
59430bd37d32Smrg				finish->col + 1);
59440bd37d32Smrg			value = numbers;
59450bd37d32Smrg			break;
59462e4f8982Smrg		    case 'R':
59472e4f8982Smrg			value = formatStrlen(numbers, x_strrtrim(data), 1);
59482e4f8982Smrg			break;
59492e4f8982Smrg		    case 'r':
59502e4f8982Smrg			value = x_strrtrim(data);
59512e4f8982Smrg			break;
59520bd37d32Smrg		    case 'S':
59532e4f8982Smrg			value = formatStrlen(numbers, data, 0);
59540bd37d32Smrg			break;
59550bd37d32Smrg		    case 's':
59560bd37d32Smrg			value = data;
59570bd37d32Smrg			break;
59580bd37d32Smrg		    case 'T':
59592e4f8982Smrg			value = formatStrlen(numbers, x_strtrim(data), 1);
59600bd37d32Smrg			break;
59610bd37d32Smrg		    case 't':
59620bd37d32Smrg			value = x_strtrim(data);
59630bd37d32Smrg			break;
59640bd37d32Smrg		    case 'V':
59650bd37d32Smrg			formatVideoAttrs(xw, numbers, start);
59660bd37d32Smrg			value = numbers;
59670bd37d32Smrg			break;
59680bd37d32Smrg		    case 'v':
59690bd37d32Smrg			formatVideoAttrs(xw, numbers, finish);
59700bd37d32Smrg			value = numbers;
59710bd37d32Smrg			break;
59720bd37d32Smrg		    default:
59730bd37d32Smrg			if (pass) {
59740bd37d32Smrg			    result[need] = format[n];
59750bd37d32Smrg			}
59760bd37d32Smrg			--n;
59770bd37d32Smrg			++need;
59780bd37d32Smrg			break;
59790bd37d32Smrg		    }
59800bd37d32Smrg		    if (value != 0) {
59810bd37d32Smrg			if (pass) {
59820bd37d32Smrg			    strcpy(result + need, value);
59830bd37d32Smrg			}
59840bd37d32Smrg			need += strlen(value);
59850bd37d32Smrg			if (value != numbers && value != data) {
59860bd37d32Smrg			    free(value);
59870bd37d32Smrg			}
59880bd37d32Smrg		    }
59890bd37d32Smrg		} else {
59900bd37d32Smrg		    if (pass) {
59910bd37d32Smrg			result[need] = format[n];
59920bd37d32Smrg		    }
59930bd37d32Smrg		    ++need;
59940bd37d32Smrg		}
59950bd37d32Smrg	    }
59960bd37d32Smrg	    if (pass) {
59970bd37d32Smrg		result[need] = '\0';
59980bd37d32Smrg	    } else {
59990bd37d32Smrg		++need;
60000bd37d32Smrg		result = malloc(need);
60010bd37d32Smrg		if (result == 0) {
60020bd37d32Smrg		    break;
60030bd37d32Smrg		}
60040bd37d32Smrg	    }
60050bd37d32Smrg	}
60060bd37d32Smrg    }
60070bd37d32Smrg    TRACE(("expandFormat(%s) = %s\n", NonNull(format), NonNull(result)));
60080bd37d32Smrg    return result;
60090bd37d32Smrg}
60100bd37d32Smrg
60115307cd1aSmrg#if OPT_EXEC_SELECTION
60120bd37d32Smrg/* execute the command after forking.  The main process frees its data */
60130bd37d32Smrgstatic void
60142e4f8982SmrgexecuteCommand(pid_t pid, char **argv)
60150bd37d32Smrg{
60162e4f8982Smrg    (void) pid;
60170bd37d32Smrg    if (argv != 0 && argv[0] != 0) {
60182e4f8982Smrg	char *child_cwd = ProcGetCWD(pid);
60192e4f8982Smrg
60200bd37d32Smrg	if (fork() == 0) {
60212e4f8982Smrg	    if (child_cwd) {
60222e4f8982Smrg		IGNORE_RC(chdir(child_cwd));	/* We don't care if this fails */
60232e4f8982Smrg	    }
60240bd37d32Smrg	    execvp(argv[0], argv);
60250bd37d32Smrg	    exit(EXIT_FAILURE);
60260bd37d32Smrg	}
6027913cc679Smrg	free(child_cwd);
60280bd37d32Smrg    }
60290bd37d32Smrg}
60300bd37d32Smrg
60310bd37d32Smrgstatic void
60320bd37d32SmrgfreeArgv(char *blob, char **argv)
60330bd37d32Smrg{
60340bd37d32Smrg    if (blob) {
60350bd37d32Smrg	free(blob);
60360bd37d32Smrg	if (argv) {
60372e4f8982Smrg	    int n;
60380bd37d32Smrg	    for (n = 0; argv[n]; ++n)
60390bd37d32Smrg		free(argv[n]);
60400bd37d32Smrg	    free(argv);
60410bd37d32Smrg	}
60420bd37d32Smrg    }
60430bd37d32Smrg}
60440bd37d32Smrg
604501037d57Smrgstatic void
604601037d57SmrgreallyExecFormatted(Widget w, char *format, char *data, CELL *start, CELL *finish)
604701037d57Smrg{
604801037d57Smrg    XtermWidget xw;
604901037d57Smrg
605001037d57Smrg    if ((xw = getXtermWidget(w)) != 0) {
605101037d57Smrg	char **argv;
605201037d57Smrg
605301037d57Smrg	if ((argv = tokenizeFormat(format)) != 0) {
60542e4f8982Smrg	    char *blob = argv[0];
60552e4f8982Smrg	    int argc;
60562e4f8982Smrg
605701037d57Smrg	    for (argc = 0; argv[argc] != 0; ++argc) {
605801037d57Smrg		argv[argc] = expandFormat(xw, argv[argc], data, start, finish);
605901037d57Smrg	    }
60602e4f8982Smrg	    executeCommand(TScreenOf(xw)->pid, argv);
606101037d57Smrg	    freeArgv(blob, argv);
606201037d57Smrg	}
606301037d57Smrg    }
606401037d57Smrg}
606501037d57Smrg
60660bd37d32Smrgvoid
60670bd37d32SmrgHandleExecFormatted(Widget w,
606801037d57Smrg		    XEvent *event,
6069e0a2b6dfSmrg		    String *params,	/* selections */
60700bd37d32Smrg		    Cardinal *num_params)
60710bd37d32Smrg{
60720bd37d32Smrg    XtermWidget xw;
60730bd37d32Smrg
6074f2e35a3aSmrg    TRACE_EVENT("HandleExecFormatted", event, params, num_params);
607501037d57Smrg    if ((xw = getXtermWidget(w)) != 0 &&
607601037d57Smrg	(*num_params > 1)) {
607701037d57Smrg	doSelectionFormat(xw, w, event, params, num_params, reallyExecFormatted);
60780bd37d32Smrg    }
60790bd37d32Smrg}
60800bd37d32Smrg
60810bd37d32Smrgvoid
60820bd37d32SmrgHandleExecSelectable(Widget w,
6083913cc679Smrg		     XEvent *event,
6084e0a2b6dfSmrg		     String *params,	/* selections */
60850bd37d32Smrg		     Cardinal *num_params)
60860bd37d32Smrg{
60870bd37d32Smrg    XtermWidget xw;
60880bd37d32Smrg
60890bd37d32Smrg    if ((xw = getXtermWidget(w)) != 0) {
6090f2e35a3aSmrg	TRACE_EVENT("HandleExecSelectable", event, params, num_params);
60910bd37d32Smrg
60920bd37d32Smrg	if (*num_params == 2) {
60930bd37d32Smrg	    CELL start, finish;
60940bd37d32Smrg	    char *data;
60950bd37d32Smrg	    char **argv;
60960bd37d32Smrg
6097913cc679Smrg	    data = getDataFromScreen(xw, event, params[1], &start, &finish);
60980bd37d32Smrg	    if (data != 0) {
60990bd37d32Smrg		if ((argv = tokenizeFormat(params[0])) != 0) {
61002e4f8982Smrg		    char *blob = argv[0];
61012e4f8982Smrg		    int argc;
61022e4f8982Smrg
61030bd37d32Smrg		    for (argc = 0; argv[argc] != 0; ++argc) {
61040bd37d32Smrg			argv[argc] = expandFormat(xw, argv[argc], data,
61050bd37d32Smrg						  &start, &finish);
61060bd37d32Smrg		    }
61072e4f8982Smrg		    executeCommand(TScreenOf(xw)->pid, argv);
61080bd37d32Smrg		    freeArgv(blob, argv);
61090bd37d32Smrg		}
6110894e0ac8Smrg		free(data);
61110bd37d32Smrg	    }
61120bd37d32Smrg	}
61130bd37d32Smrg    }
61140bd37d32Smrg}
61155307cd1aSmrg#endif /* OPT_EXEC_SELECTION */
61160bd37d32Smrg
611701037d57Smrgstatic void
611801037d57SmrgreallyInsertFormatted(Widget w, char *format, char *data, CELL *start, CELL *finish)
611901037d57Smrg{
612001037d57Smrg    XtermWidget xw;
612101037d57Smrg
612201037d57Smrg    if ((xw = getXtermWidget(w)) != 0) {
612301037d57Smrg	char *exps;
612401037d57Smrg
612501037d57Smrg	if ((exps = expandFormat(xw, format, data, start, finish)) != 0) {
612601037d57Smrg	    unparseputs(xw, exps);
612701037d57Smrg	    unparse_end(xw);
612801037d57Smrg	    free(exps);
612901037d57Smrg	}
613001037d57Smrg    }
613101037d57Smrg}
613201037d57Smrg
61330bd37d32Smrgvoid
61340bd37d32SmrgHandleInsertFormatted(Widget w,
613501037d57Smrg		      XEvent *event,
6136e0a2b6dfSmrg		      String *params,	/* selections */
61370bd37d32Smrg		      Cardinal *num_params)
61380bd37d32Smrg{
61390bd37d32Smrg    XtermWidget xw;
61400bd37d32Smrg
6141f2e35a3aSmrg    TRACE_EVENT("HandleInsertFormatted", event, params, num_params);
614201037d57Smrg    if ((xw = getXtermWidget(w)) != 0 &&
614301037d57Smrg	(*num_params > 1)) {
614401037d57Smrg	doSelectionFormat(xw, w, event, params, num_params, reallyInsertFormatted);
61450bd37d32Smrg    }
61460bd37d32Smrg}
61470bd37d32Smrg
61480bd37d32Smrgvoid
61490bd37d32SmrgHandleInsertSelectable(Widget w,
6150913cc679Smrg		       XEvent *event,
6151e0a2b6dfSmrg		       String *params,	/* selections */
61520bd37d32Smrg		       Cardinal *num_params)
61530bd37d32Smrg{
61540bd37d32Smrg    XtermWidget xw;
61550bd37d32Smrg
61560bd37d32Smrg    if ((xw = getXtermWidget(w)) != 0) {
6157f2e35a3aSmrg	TRACE_EVENT("HandleInsertSelectable", event, params, num_params);
61580bd37d32Smrg
61590bd37d32Smrg	if (*num_params == 2) {
61600bd37d32Smrg	    CELL start, finish;
61610bd37d32Smrg	    char *data;
61620bd37d32Smrg	    char *temp = x_strdup(params[0]);
61630bd37d32Smrg
6164913cc679Smrg	    data = getDataFromScreen(xw, event, params[1], &start, &finish);
61650bd37d32Smrg	    if (data != 0) {
61662e4f8982Smrg		char *exps = expandFormat(xw, temp, data, &start, &finish);
61670bd37d32Smrg		if (exps != 0) {
61680bd37d32Smrg		    unparseputs(xw, exps);
616901037d57Smrg		    unparse_end(xw);
61700bd37d32Smrg		    free(exps);
61710bd37d32Smrg		}
61720bd37d32Smrg		free(data);
61730bd37d32Smrg	    }
61740bd37d32Smrg	    free(temp);
61750bd37d32Smrg	}
61760bd37d32Smrg    }
61770bd37d32Smrg}
61780bd37d32Smrg#endif /* OPT_SELECTION_OPS */
6179