button.c revision 5104ee6e
15104ee6eSmrg/* $XTermId: button.c,v 1.669 2025/01/03 00:20:00 tom Exp $ */
2d522f475Smrg
3d522f475Smrg/*
45104ee6eSmrg * Copyright 1999-2024,2025 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);
1935104ee6eSmrgstatic void ComputeSelect(XtermWidget xw, const CELL *startc, const CELL *endc,
1945104ee6eSmrg			  Bool 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);
2005104ee6eSmrgstatic void ReHiliteText(XtermWidget xw, const CELL *first, const CELL *last);
2015104ee6eSmrgstatic void SaltTextAway(XtermWidget xw, int which, const CELL *cellc, const 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)) {
3335104ee6eSmrg	target = NULL;
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;
4435104ee6eSmrg	while ((next = scanTrans(data, &state, &state2, &first, &last)) != NULL) {
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
13795104ee6eSmrg    if ((xw = getXtermWidget(w)) != NULL) {
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
14115104ee6eSmrg    if ((xw = getXtermWidget(w)) != NULL) {
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
14675104ee6eSmrg    if ((xw = getXtermWidget(w)) != NULL) {
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
15085104ee6eSmrg    if ((xw = getXtermWidget(w)) != NULL) {
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
15445104ee6eSmrg    if ((xw = getXtermWidget(w)) != NULL) {
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
15895104ee6eSmrg    if ((xw = getXtermWidget(w)) != NULL) {
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
16035104ee6eSmrg    if ((xw = getXtermWidget(w)) != NULL) {
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;
16195104ee6eSmrg    if ((xw = getXtermWidget(w)) != NULL) {
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;
16365104ee6eSmrg    if ((xw = getXtermWidget(w)) != NULL) {
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
16545104ee6eSmrg    if ((xw = getXtermWidget(w)) != NULL) {
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;
18005104ee6eSmrg		if ((data = UTF8toLatin1(screen, data, size, &size)) != NULL) {
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
18605104ee6eSmrg    if ((xw = getXtermWidget(w)) != NULL) {
1861956cc18dSsnj	TScreen *screen = TScreenOf(xw);
1862956cc18dSsnj
186320d2c4d2Smrg	if (!IsEmpty(value)) {
1864492d43a5Smrg	    char *copied = x_strdup(value);
18655104ee6eSmrg	    if (copied != NULL) {
18665104ee6eSmrg		Atom *result = NULL;
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
19345104ee6eSmrg    if (*resultp == NULL) {
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
19705104ee6eSmrg    if (*resultp == NULL) {
19715104ee6eSmrg	Atom *result = NULL;
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
20065104ee6eSmrg    if ((xw = getXtermWidget(w)) == NULL) {
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
20805104ee6eSmrg    if (params != NULL && 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);
20985104ee6eSmrg	    if ((result = TypeMallocN(String, num_params + 1)) != NULL) {
20995104ee6eSmrg		result[num_params] = NULL;
2100d522f475Smrg		for (j = 0; j < num_params; ++j) {
21015307cd1aSmrg		    result[j] = (String) (isSELECT(params[j])
2102d522f475Smrg					  ? mapTo
21035307cd1aSmrg					  : params[j]);
21045104ee6eSmrg		    if (result[j] == NULL) {
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;
21915104ee6eSmrg    if ((xw = getXtermWidget(w)) == NULL)
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
22335104ee6eSmrg	if (targets != NULL) {
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);
22495104ee6eSmrg		if (list != NULL) {
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
22885104ee6eSmrg#define tty_vwrite(pty,lag,l)		v_write(pty,lag,(size_t) l)
2289d522f475Smrg
2290d522f475Smrg#if OPT_PASTE64
2291d522f475Smrg/* Return base64 code character given 6-bit number */
2292d522f475Smrgstatic const char base64_code[] = "\
2293d522f475SmrgABCDEFGHIJKLMNOPQRSTUVWXYZ\
2294d522f475Smrgabcdefghijklmnopqrstuvwxyz\
2295d522f475Smrg0123456789+/";
2296d522f475Smrgstatic void
2297e0a2b6dfSmrgbase64_flush(TScreen *screen)
2298d522f475Smrg{
2299d522f475Smrg    Char x;
230001037d57Smrg
230101037d57Smrg    TRACE(("base64_flush count %d, pad %d (%d)\n",
230201037d57Smrg	   screen->base64_count,
230301037d57Smrg	   screen->base64_pad,
230401037d57Smrg	   screen->base64_pad & 3));
230501037d57Smrg
2306d522f475Smrg    switch (screen->base64_count) {
2307d522f475Smrg    case 0:
2308d522f475Smrg	break;
2309d522f475Smrg    case 2:
23102eaa94a1Schristos	x = CharOf(base64_code[screen->base64_accu << 4]);
2311d522f475Smrg	tty_vwrite(screen->respond, &x, 1);
2312d522f475Smrg	break;
2313d522f475Smrg    case 4:
23142eaa94a1Schristos	x = CharOf(base64_code[screen->base64_accu << 2]);
2315d522f475Smrg	tty_vwrite(screen->respond, &x, 1);
2316d522f475Smrg	break;
2317d522f475Smrg    }
231801037d57Smrg    if (screen->base64_pad & 3) {
2319d522f475Smrg	tty_vwrite(screen->respond,
2320492d43a5Smrg		   (const Char *) "===",
232101037d57Smrg		   (unsigned) (3 - (screen->base64_pad & 3)));
232201037d57Smrg    }
2323d522f475Smrg    screen->base64_count = 0;
2324d522f475Smrg    screen->base64_accu = 0;
2325d522f475Smrg    screen->base64_pad = 0;
2326d522f475Smrg}
2327d522f475Smrg#endif /* OPT_PASTE64 */
2328d522f475Smrg
2329e0a2b6dfSmrg/*
2330e0a2b6dfSmrg * Translate ISO-8859-1 or UTF-8 data to NRCS.
2331e0a2b6dfSmrg */
2332d522f475Smrgstatic void
23335307cd1aSmrgToNational(XtermWidget xw, Char *buffer, size_t *length)
2334e0a2b6dfSmrg{
2335f2e35a3aSmrg    TScreen *screen = TScreenOf(xw);
2336f2e35a3aSmrg    DECNRCM_codes gsetL = screen->gsets[screen->curgl];
2337f2e35a3aSmrg    DECNRCM_codes gsetR = screen->gsets[screen->curgr];
2338e0a2b6dfSmrg
2339e0a2b6dfSmrg#if OPT_WIDE_CHARS
2340e0a2b6dfSmrg    if ((screen->utf8_nrc_mode | screen->utf8_mode) != uFalse) {
23412e4f8982Smrg	Char *p;
2342e0a2b6dfSmrg	PtyData *data = TypeXtMallocX(PtyData, *length);
2343e0a2b6dfSmrg
2344e0a2b6dfSmrg	memset(data, 0, sizeof(*data));
2345e0a2b6dfSmrg	data->next = data->buffer;
2346e0a2b6dfSmrg	data->last = data->buffer + *length;
23475307cd1aSmrg	memcpy(data->buffer, buffer, *length);
2348e0a2b6dfSmrg	p = buffer;
2349e0a2b6dfSmrg	while (data->next < data->last) {
23502e4f8982Smrg	    unsigned chr, out, gl, gr;
23512e4f8982Smrg
2352894e0ac8Smrg	    if (!decodeUtf8(screen, data)) {
2353e0a2b6dfSmrg		data->utf_size = 1;
2354e0a2b6dfSmrg		data->utf_data = data->next[0];
2355e0a2b6dfSmrg	    }
2356e0a2b6dfSmrg	    data->next += data->utf_size;
2357e0a2b6dfSmrg	    chr = data->utf_data;
2358e0a2b6dfSmrg	    out = chr;
2359f2e35a3aSmrg	    if ((gl = xtermCharSetIn(xw, chr, gsetL)) != chr) {
2360e0a2b6dfSmrg		out = gl;
2361f2e35a3aSmrg	    } else if ((gr = xtermCharSetIn(xw, chr, gsetR)) != chr) {
2362e0a2b6dfSmrg		out = gr;
2363e0a2b6dfSmrg	    }
2364e0a2b6dfSmrg	    *p++ = (Char) ((out < 256) ? out : ' ');
2365e0a2b6dfSmrg	}
23665307cd1aSmrg	*length = (size_t) (p - buffer);
2367e0a2b6dfSmrg	free(data);
2368e0a2b6dfSmrg    } else
2369e0a2b6dfSmrg#endif
2370e0a2b6dfSmrg    {
23712e4f8982Smrg	Char *p;
23722e4f8982Smrg
23735307cd1aSmrg	for (p = buffer; (size_t) (p - buffer) < *length; ++p) {
23742e4f8982Smrg	    unsigned gl, gr;
23752e4f8982Smrg	    unsigned chr = *p;
23762e4f8982Smrg	    unsigned out = chr;
2377f2e35a3aSmrg	    if ((gl = xtermCharSetIn(xw, chr, gsetL)) != chr) {
2378e0a2b6dfSmrg		out = gl;
2379f2e35a3aSmrg	    } else if ((gr = xtermCharSetIn(xw, chr, gsetR)) != chr) {
2380e0a2b6dfSmrg		out = gr;
2381e0a2b6dfSmrg	    }
2382e0a2b6dfSmrg	    *p = (Char) out;
2383e0a2b6dfSmrg	}
2384e0a2b6dfSmrg    }
2385e0a2b6dfSmrg}
2386e0a2b6dfSmrg
2387e0a2b6dfSmrgstatic void
23885307cd1aSmrg_qWriteSelectionData(XtermWidget xw, Char *lag, size_t length)
2389d522f475Smrg{
23900bd37d32Smrg    TScreen *screen = TScreenOf(xw);
23910bd37d32Smrg
2392e0a2b6dfSmrg    /*
2393e0a2b6dfSmrg     * If we are pasting into a window which is using NRCS, we want to map
2394e0a2b6dfSmrg     * the text from the normal encoding (ISO-8859-1 or UTF-8) into the coding
2395e0a2b6dfSmrg     * that an application would use to write characters with NRCS.
2396e0a2b6dfSmrg     *
2397e0a2b6dfSmrg     * TODO: handle conversion from UTF-8, and adjust length.  This can be done
2398e0a2b6dfSmrg     * in the same buffer because the target is always 8-bit.
2399e0a2b6dfSmrg     */
2400e0a2b6dfSmrg    if ((xw->flags & NATIONAL) && (length != 0)) {
2401f2e35a3aSmrg	ToNational(xw, lag, &length);
2402e0a2b6dfSmrg    }
2403d522f475Smrg#if OPT_PASTE64
2404d522f475Smrg    if (screen->base64_paste) {
2405d522f475Smrg	/* Send data as base64 */
2406d522f475Smrg	Char *p = lag;
2407d522f475Smrg	Char buf[64];
2408d522f475Smrg	unsigned x = 0;
24090bd37d32Smrg
24105307cd1aSmrg	TRACE(("convert to base64 %lu:%s\n",
24115307cd1aSmrg	       (unsigned long) length,
24125307cd1aSmrg	       visibleChars(p, length)));
241301037d57Smrg
24140bd37d32Smrg	/*
24150bd37d32Smrg	 * Handle the case where the selection is from _this_ xterm, which
24160bd37d32Smrg	 * puts part of the reply in the buffer before the selection callback
24170bd37d32Smrg	 * happens.
24180bd37d32Smrg	 */
24190bd37d32Smrg	if (screen->base64_paste && screen->unparse_len) {
24200bd37d32Smrg	    unparse_end(xw);
24210bd37d32Smrg	}
2422d522f475Smrg	while (length--) {
2423d522f475Smrg	    switch (screen->base64_count) {
2424d522f475Smrg	    case 0:
24252eaa94a1Schristos		buf[x++] = CharOf(base64_code[*p >> 2]);
24262eaa94a1Schristos		screen->base64_accu = (unsigned) (*p & 0x3);
2427d522f475Smrg		screen->base64_count = 2;
2428d522f475Smrg		++p;
2429d522f475Smrg		break;
2430d522f475Smrg	    case 2:
24312eaa94a1Schristos		buf[x++] = CharOf(base64_code[(screen->base64_accu << 4) +
24322eaa94a1Schristos					      (*p >> 4)]);
24332eaa94a1Schristos		screen->base64_accu = (unsigned) (*p & 0xF);
2434d522f475Smrg		screen->base64_count = 4;
2435d522f475Smrg		++p;
2436d522f475Smrg		break;
2437d522f475Smrg	    case 4:
24382eaa94a1Schristos		buf[x++] = CharOf(base64_code[(screen->base64_accu << 2) +
24392eaa94a1Schristos					      (*p >> 6)]);
24402eaa94a1Schristos		buf[x++] = CharOf(base64_code[*p & 0x3F]);
2441d522f475Smrg		screen->base64_accu = 0;
2442d522f475Smrg		screen->base64_count = 0;
2443d522f475Smrg		++p;
2444d522f475Smrg		break;
2445d522f475Smrg	    }
2446d522f475Smrg	    if (x >= 63) {
2447d522f475Smrg		/* Write 63 or 64 characters */
2448d522f475Smrg		screen->base64_pad += x;
244901037d57Smrg		TRACE(("writing base64 interim %s\n", visibleChars(buf, x)));
2450d522f475Smrg		tty_vwrite(screen->respond, buf, x);
2451d522f475Smrg		x = 0;
2452d522f475Smrg	    }
2453d522f475Smrg	}
2454d522f475Smrg	if (x != 0) {
2455d522f475Smrg	    screen->base64_pad += x;
245601037d57Smrg	    TRACE(("writing base64 finish %s\n", visibleChars(buf, x)));
2457d522f475Smrg	    tty_vwrite(screen->respond, buf, x);
2458d522f475Smrg	}
2459d522f475Smrg    } else
2460d522f475Smrg#endif /* OPT_PASTE64 */
2461d522f475Smrg#if OPT_READLINE
2462d522f475Smrg    if (SCREEN_FLAG(screen, paste_quotes)) {
24635307cd1aSmrg	Char quote[2];
24645307cd1aSmrg	quote[0] = (Char) get_tty_lnext(screen->respond, XTERM_LNEXT, "pty");
24655307cd1aSmrg	quote[1] = 0;
24665307cd1aSmrg	TRACE(("writing quoted selection data %s\n", visibleChars(lag, length)));
2467d522f475Smrg	while (length--) {
24685307cd1aSmrg	    tty_vwrite(screen->respond, quote, 1);
2469d522f475Smrg	    tty_vwrite(screen->respond, lag++, 1);
2470d522f475Smrg	}
2471d522f475Smrg    } else
2472d522f475Smrg#endif
247301037d57Smrg    {
24745307cd1aSmrg	TRACE(("writing selection data %s\n", visibleChars(lag, length)));
2475d522f475Smrg	tty_vwrite(screen->respond, lag, length);
247601037d57Smrg    }
2477d522f475Smrg}
2478d522f475Smrg
2479d522f475Smrgstatic void
2480e0a2b6dfSmrg_WriteSelectionData(XtermWidget xw, Char *line, size_t length)
2481d522f475Smrg{
24825307cd1aSmrg#if OPT_PASTE64 || OPT_READLINE
24830bd37d32Smrg    TScreen *screen = TScreenOf(xw);
24842e4f8982Smrg#endif
2485d522f475Smrg
2486d522f475Smrg#if OPT_PASTE64
2487d522f475Smrg    if (screen->base64_paste) {
24885307cd1aSmrg	_qWriteSelectionData(xw, line, length);
2489d522f475Smrg	base64_flush(screen);
2490d522f475Smrg    } else
2491d522f475Smrg#endif
2492d522f475Smrg    {
2493d522f475Smrg	if (!SCREEN_FLAG(screen, paste_literal_nl)) {
24945307cd1aSmrg	    size_t n;
24955307cd1aSmrg	    for (n = 0; n < length; ++n) {
24965307cd1aSmrg		if (line[n] == '\n') {
24975307cd1aSmrg		    line[n] = '\r';
2498d522f475Smrg		}
2499d522f475Smrg	    }
2500d522f475Smrg	}
2501d522f475Smrg
25025307cd1aSmrg	_qWriteSelectionData(xw, line, length);
2503d522f475Smrg    }
2504d522f475Smrg}
2505d522f475Smrg
2506f2e35a3aSmrg#if OPT_PASTE64 || OPT_READLINE
2507d522f475Smrgstatic void
2508e0a2b6dfSmrg_WriteKey(TScreen *screen, const Char *in)
2509d522f475Smrg{
2510d522f475Smrg    Char line[16];
2511d522f475Smrg    unsigned count = 0;
2512492d43a5Smrg    size_t length = strlen((const char *) in);
2513d522f475Smrg
2514d522f475Smrg    if (screen->control_eight_bits) {
2515d522f475Smrg	line[count++] = ANSI_CSI;
2516d522f475Smrg    } else {
2517d522f475Smrg	line[count++] = ANSI_ESC;
2518d522f475Smrg	line[count++] = '[';
2519d522f475Smrg    }
2520d522f475Smrg    while (length--)
2521d522f475Smrg	line[count++] = *in++;
2522d522f475Smrg    line[count++] = '~';
2523d522f475Smrg    tty_vwrite(screen->respond, line, count);
2524d522f475Smrg}
2525d522f475Smrg#endif /* OPT_READLINE */
2526d522f475Smrg
25270bd37d32Smrg/*
25280bd37d32Smrg * Unless enabled by the user, strip control characters other than formatting.
25290bd37d32Smrg */
25300bd37d32Smrgstatic size_t
25310bd37d32SmrgremoveControls(XtermWidget xw, char *value)
25320bd37d32Smrg{
25330bd37d32Smrg    TScreen *screen = TScreenOf(xw);
25340bd37d32Smrg    size_t dst = 0;
25350bd37d32Smrg
25360bd37d32Smrg    if (screen->allowPasteControls) {
25370bd37d32Smrg	dst = strlen(value);
25380bd37d32Smrg    } else {
25392e4f8982Smrg	size_t src = 0;
254004b94745Smrg	Boolean *disallowed = screen->disallow_paste_ops;
254104b94745Smrg	TERMIO_STRUCT data;
254204b94745Smrg	char current_chars[epLAST];
254304b94745Smrg
254404b94745Smrg	if (disallowed[epSTTY] && ttyGetAttr(screen->respond, &data) == 0) {
254504b94745Smrg	    int n;
254604b94745Smrg	    int disabled = xtermDisabledChar();
254704b94745Smrg
254804b94745Smrg	    TRACE(("disallow(STTY):"));
254904b94745Smrg	    memcpy(current_chars, disallowed, sizeof(current_chars));
255004b94745Smrg
255104b94745Smrg	    for (n = 0; n < NCCS; ++n) {
255204b94745Smrg		PasteControls nc = (data.c_cc[n] < 32
255304b94745Smrg				    ? data.c_cc[n]
255404b94745Smrg				    : (data.c_cc[n] == 127
255504b94745Smrg				       ? epDEL
255604b94745Smrg				       : epLAST));
255704b94745Smrg		if (nc == epNUL || nc == epLAST)
255804b94745Smrg		    continue;
255904b94745Smrg		if (CharOf(data.c_cc[n]) == CharOf(disabled))
256004b94745Smrg		    continue;
256104b94745Smrg		if ((n == VMIN || n == VTIME) && !(data.c_lflag & ICANON))
256204b94745Smrg		    continue;
256304b94745Smrg		switch (n) {
256404b94745Smrg		    /* POSIX */
256504b94745Smrg		case VEOF:
256604b94745Smrg		case VEOL:
256704b94745Smrg		case VERASE:
256804b94745Smrg		case VINTR:
256904b94745Smrg		case VKILL:
257004b94745Smrg		case VQUIT:
257104b94745Smrg		case VSTART:
257204b94745Smrg		case VSTOP:
257304b94745Smrg		case VSUSP:
257404b94745Smrg		    /* system-dependent */
257504b94745Smrg#ifdef VDISCARD
257604b94745Smrg		case VDISCARD:
257704b94745Smrg#endif
257804b94745Smrg#ifdef VDSUSP
257904b94745Smrg		case VDSUSP:
258004b94745Smrg#endif
258104b94745Smrg#ifdef VEOL2
258204b94745Smrg		case VEOL2:
258304b94745Smrg#endif
258404b94745Smrg#ifdef VLNEXT
258504b94745Smrg		case VLNEXT:
258604b94745Smrg#endif
258704b94745Smrg#ifdef VREPRINT
258804b94745Smrg		case VREPRINT:
258904b94745Smrg#endif
259004b94745Smrg#ifdef VSTATUS
259104b94745Smrg		case VSTATUS:
259204b94745Smrg#endif
259304b94745Smrg#ifdef VSWTC
259404b94745Smrg		case VSWTC:	/* System V SWTCH */
259504b94745Smrg#endif
259604b94745Smrg#ifdef VWERASE
259704b94745Smrg		case VWERASE:
259804b94745Smrg#endif
259904b94745Smrg		    break;
260004b94745Smrg		default:
260104b94745Smrg		    continue;
260204b94745Smrg		}
260304b94745Smrg		if (nc != epLAST) {
260404b94745Smrg		    TRACE((" \\%03o", data.c_cc[n]));
260504b94745Smrg		    current_chars[nc] = 1;
260604b94745Smrg		}
260704b94745Smrg	    }
260804b94745Smrg	    TRACE(("\n"));
260904b94745Smrg	    disallowed = current_chars;
261004b94745Smrg	}
26110bd37d32Smrg	while ((value[dst] = value[src]) != '\0') {
26120bd37d32Smrg	    int ch = CharOf(value[src++]);
2613f2e35a3aSmrg
2614f2e35a3aSmrg#define ReplacePaste(n) \
261504b94745Smrg	    if (disallowed[n]) \
2616f2e35a3aSmrg		value[dst] = ' '
2617f2e35a3aSmrg
26180bd37d32Smrg	    if (ch < 32) {
2619f2e35a3aSmrg		ReplacePaste(epC0);
2620f2e35a3aSmrg		ReplacePaste(ch);
2621f2e35a3aSmrg		++dst;
2622f2e35a3aSmrg	    } else if (ch == ANSI_DEL) {
2623f2e35a3aSmrg		ReplacePaste(epDEL);
2624f2e35a3aSmrg		++dst;
26250bd37d32Smrg	    }
26260bd37d32Smrg#if OPT_WIDE_CHARS
2627e0a2b6dfSmrg	    else if (screen->utf8_inparse || screen->utf8_nrc_mode)
26280bd37d32Smrg		++dst;
26290bd37d32Smrg#endif
26300bd37d32Smrg#if OPT_C1_PRINT || OPT_WIDE_CHARS
26310bd37d32Smrg	    else if (screen->c1_printable)
26320bd37d32Smrg		++dst;
26330bd37d32Smrg#endif
26340bd37d32Smrg	    else if (ch >= 128 && ch < 160)
26350bd37d32Smrg		continue;
26360bd37d32Smrg	    else
26370bd37d32Smrg		++dst;
26380bd37d32Smrg	}
26390bd37d32Smrg    }
26400bd37d32Smrg    return dst;
26410bd37d32Smrg}
26420bd37d32Smrg
2643f2e35a3aSmrg#if OPT_SELECTION_OPS
2644f2e35a3aSmrgstatic void
2645f2e35a3aSmrgbeginInternalSelect(XtermWidget xw)
2646f2e35a3aSmrg{
2647f2e35a3aSmrg    TScreen *screen = TScreenOf(xw);
2648f2e35a3aSmrg    InternalSelect *mydata = &(screen->internal_select);
2649f2e35a3aSmrg
2650f2e35a3aSmrg    (void) mydata;
2651f2e35a3aSmrg    /* override flags so that SelectionReceived only updates a buffer */
2652f2e35a3aSmrg#if OPT_PASTE64
2653f2e35a3aSmrg    mydata->base64_paste = screen->base64_paste;
2654f2e35a3aSmrg    screen->base64_paste = 0;
2655f2e35a3aSmrg#endif
2656f2e35a3aSmrg#if OPT_PASTE64 || OPT_READLINE
2657f2e35a3aSmrg    mydata->paste_brackets = screen->paste_brackets;
2658f2e35a3aSmrg    SCREEN_FLAG_unset(screen, paste_brackets);
2659f2e35a3aSmrg#endif
2660f2e35a3aSmrg}
2661f2e35a3aSmrg
2662f2e35a3aSmrgstatic void
2663f2e35a3aSmrgfinishInternalSelect(XtermWidget xw)
2664f2e35a3aSmrg{
2665f2e35a3aSmrg    TScreen *screen = TScreenOf(xw);
2666f2e35a3aSmrg    InternalSelect *mydata = &(screen->internal_select);
2667f2e35a3aSmrg
2668f2e35a3aSmrg    (void) mydata;
2669f2e35a3aSmrg#if OPT_PASTE64
2670f2e35a3aSmrg    screen->base64_paste = mydata->base64_paste;
2671f2e35a3aSmrg#endif
2672f2e35a3aSmrg#if OPT_PASTE64 || OPT_READLINE
2673f2e35a3aSmrg    screen->paste_brackets = mydata->paste_brackets;
2674f2e35a3aSmrg#endif
2675f2e35a3aSmrg}
2676f2e35a3aSmrg
2677f2e35a3aSmrg#else
2678f2e35a3aSmrg#define finishInternalSelect(xw)	/* nothing */
2679f2e35a3aSmrg#endif /* OPT_SELECTION_OPS */
2680f2e35a3aSmrg
2681d522f475Smrg/* SelectionReceived: stuff received selection text into pty */
2682d522f475Smrg
2683d522f475Smrg/* ARGSUSED */
2684d522f475Smrgstatic void
2685d522f475SmrgSelectionReceived(Widget w,
2686d522f475Smrg		  XtPointer client_data,
2687894e0ac8Smrg		  Atom *selection GCC_UNUSED,
2688894e0ac8Smrg		  Atom *type,
2689d522f475Smrg		  XtPointer value,
2690d522f475Smrg		  unsigned long *length,
2691d522f475Smrg		  int *format)
2692d522f475Smrg{
2693d522f475Smrg    char **text_list = NULL;
26942e4f8982Smrg    int text_list_count = 0;
2695d522f475Smrg    XTextProperty text_prop;
2696d522f475Smrg    TScreen *screen;
2697d522f475Smrg    Display *dpy;
2698d522f475Smrg#if OPT_TRACE && OPT_WIDE_CHARS
2699d522f475Smrg    Char *line = (Char *) value;
2700d522f475Smrg#endif
2701d522f475Smrg
2702956cc18dSsnj    XtermWidget xw;
2703956cc18dSsnj
27045104ee6eSmrg    if ((xw = getXtermWidget(w)) == NULL)
2705d522f475Smrg	return;
2706956cc18dSsnj
2707956cc18dSsnj    screen = TScreenOf(xw);
2708d522f475Smrg    dpy = XtDisplay(w);
2709d522f475Smrg
2710d522f475Smrg    if (*type == 0		/*XT_CONVERT_FAIL */
2711d522f475Smrg	|| *length == 0
271201037d57Smrg	|| value == NULL) {
271301037d57Smrg	TRACE(("...no data to convert\n"));
2714d522f475Smrg	goto fail;
271501037d57Smrg    }
2716d522f475Smrg
2717d522f475Smrg    text_prop.value = (unsigned char *) value;
2718d522f475Smrg    text_prop.encoding = *type;
2719d522f475Smrg    text_prop.format = *format;
2720d522f475Smrg    text_prop.nitems = *length;
2721d522f475Smrg
272201037d57Smrg    TRACE(("SelectionReceived %s %s format %d, nitems %ld\n",
2723913cc679Smrg	   TraceAtomName(screen->display, *selection),
2724956cc18dSsnj	   visibleSelectionTarget(dpy, text_prop.encoding),
2725956cc18dSsnj	   text_prop.format,
2726956cc18dSsnj	   text_prop.nitems));
2727956cc18dSsnj
2728d522f475Smrg#if OPT_WIDE_CHARS
2729e39b573cSmrg    if (XSupportsLocale() && screen->wide_chars) {
2730d522f475Smrg	if (*type == XA_UTF8_STRING(dpy) ||
2731d522f475Smrg	    *type == XA_STRING ||
2732d522f475Smrg	    *type == XA_COMPOUND_TEXT(dpy)) {
2733d522f475Smrg	    GettingSelection(dpy, *type, line, *length);
2734d522f475Smrg	    if (Xutf8TextPropertyToTextList(dpy, &text_prop,
2735d522f475Smrg					    &text_list,
2736d522f475Smrg					    &text_list_count) < 0) {
2737e39b573cSmrg		TRACE(("default Xutf8 Conversion failed\n"));
2738d522f475Smrg		text_list = NULL;
2739d522f475Smrg	    }
2740d522f475Smrg	}
2741d522f475Smrg    } else
2742d522f475Smrg#endif /* OPT_WIDE_CHARS */
2743d522f475Smrg    {
2744d522f475Smrg	/* Convert the selection to locale's multibyte encoding. */
2745d522f475Smrg
2746d522f475Smrg	if (*type == XA_UTF8_STRING(dpy) ||
2747d522f475Smrg	    *type == XA_STRING ||
2748d522f475Smrg	    *type == XA_COMPOUND_TEXT(dpy)) {
2749d522f475Smrg	    Status rc;
2750d522f475Smrg
2751d522f475Smrg	    GettingSelection(dpy, *type, line, *length);
2752d522f475Smrg
2753d522f475Smrg#if OPT_WIDE_CHARS
2754d522f475Smrg	    if (*type == XA_UTF8_STRING(dpy) &&
2755d522f475Smrg		!(screen->wide_chars || screen->c1_printable)) {
275620d2c4d2Smrg		rc = xtermUtf8ToTextList(xw, &text_prop,
275720d2c4d2Smrg					 &text_list, &text_list_count);
2758d522f475Smrg	    } else
2759d522f475Smrg#endif
2760e39b573cSmrg	    if (*type == XA_STRING && (!XSupportsLocale() || screen->brokenSelections)) {
2761d522f475Smrg		rc = XTextPropertyToStringList(&text_prop,
2762d522f475Smrg					       &text_list, &text_list_count);
2763d522f475Smrg	    } else {
2764d522f475Smrg		rc = XmbTextPropertyToTextList(dpy, &text_prop,
2765d522f475Smrg					       &text_list,
2766d522f475Smrg					       &text_list_count);
2767d522f475Smrg	    }
2768d522f475Smrg	    if (rc < 0) {
2769d522f475Smrg		TRACE(("Conversion failed\n"));
2770d522f475Smrg		text_list = NULL;
2771d522f475Smrg	    }
2772d522f475Smrg	}
2773d522f475Smrg    }
2774d522f475Smrg
2775d522f475Smrg    if (text_list != NULL && text_list_count != 0) {
2776d522f475Smrg	int i;
2777d522f475Smrg
2778d522f475Smrg#if OPT_PASTE64
2779d522f475Smrg	if (screen->base64_paste) {
2780a1f3da82Smrg	    /* EMPTY */ ;
2781d522f475Smrg	} else
2782d522f475Smrg#endif
2783f2e35a3aSmrg#if OPT_PASTE64 || OPT_READLINE
2784f2e35a3aSmrg	if (SCREEN_FLAG(screen, paste_brackets) && !screen->selectToBuffer) {
2785492d43a5Smrg	    _WriteKey(screen, (const Char *) "200");
2786d522f475Smrg	}
2787d522f475Smrg#endif
2788d522f475Smrg	for (i = 0; i < text_list_count; i++) {
27890bd37d32Smrg	    size_t len = removeControls(xw, text_list[i]);
279001037d57Smrg
27910bd37d32Smrg	    if (screen->selectToBuffer) {
279201037d57Smrg		InternalSelect *mydata = &(screen->internal_select);
2793f2e35a3aSmrg		if (!mydata->done) {
2794f2e35a3aSmrg		    size_t have = (mydata->buffer
2795f2e35a3aSmrg				   ? strlen(mydata->buffer)
2796f2e35a3aSmrg				   : 0);
2797f2e35a3aSmrg		    size_t need = have + len + 1;
2798f2e35a3aSmrg		    char *buffer = realloc(mydata->buffer, need);
2799f2e35a3aSmrg
28005104ee6eSmrg		    if (buffer != NULL) {
2801f2e35a3aSmrg			strcpy(buffer + have, text_list[i]);
2802f2e35a3aSmrg			mydata->buffer = buffer;
2803f2e35a3aSmrg		    }
2804f2e35a3aSmrg		    TRACE(("FormatSelect %d.%d .. %d.%d %s\n",
2805f2e35a3aSmrg			   screen->startSel.row,
2806f2e35a3aSmrg			   screen->startSel.col,
2807f2e35a3aSmrg			   screen->endSel.row,
2808f2e35a3aSmrg			   screen->endSel.col,
2809f2e35a3aSmrg			   mydata->buffer));
2810f2e35a3aSmrg		    mydata->format_select(w, mydata->format, mydata->buffer,
2811f2e35a3aSmrg					  &(screen->startSel),
2812f2e35a3aSmrg					  &(screen->endSel));
2813f2e35a3aSmrg		    mydata->done = True;
28140bd37d32Smrg		}
281501037d57Smrg
28160bd37d32Smrg	    } else {
28170bd37d32Smrg		_WriteSelectionData(xw, (Char *) text_list[i], len);
28180bd37d32Smrg	    }
2819d522f475Smrg	}
2820d522f475Smrg#if OPT_PASTE64
2821d522f475Smrg	if (screen->base64_paste) {
2822956cc18dSsnj	    FinishPaste64(xw);
2823d522f475Smrg	} else
2824d522f475Smrg#endif
2825f2e35a3aSmrg#if OPT_PASTE64 || OPT_READLINE
2826f2e35a3aSmrg	if (SCREEN_FLAG(screen, paste_brackets) && !screen->selectToBuffer) {
2827492d43a5Smrg	    _WriteKey(screen, (const Char *) "201");
2828d522f475Smrg	}
2829d522f475Smrg#endif
2830f2e35a3aSmrg	if (screen->selectToBuffer) {
2831f2e35a3aSmrg	    InternalSelect *mydata = &(screen->internal_select);
2832f2e35a3aSmrg	    finishInternalSelect(xw);
2833f2e35a3aSmrg	    if (mydata->done) {
2834f2e35a3aSmrg		free(mydata->format);
2835f2e35a3aSmrg		free(mydata->buffer);
2836f2e35a3aSmrg		memset(mydata, 0, sizeof(*mydata));
2837f2e35a3aSmrg	    }
2838f2e35a3aSmrg	    screen->selectToBuffer = False;
2839f2e35a3aSmrg	}
2840d522f475Smrg	XFreeStringList(text_list);
284101037d57Smrg    } else {
284201037d57Smrg	TRACE(("...empty text-list\n"));
2843d522f475Smrg	goto fail;
284401037d57Smrg    }
2845d522f475Smrg
2846d522f475Smrg    XtFree((char *) client_data);
2847d522f475Smrg    XtFree((char *) value);
2848d522f475Smrg
2849d522f475Smrg    return;
2850d522f475Smrg
2851d522f475Smrg  fail:
28525104ee6eSmrg    if (client_data != NULL) {
2853d522f475Smrg	struct _SelectionList *list = (struct _SelectionList *) client_data;
2854956cc18dSsnj
2855956cc18dSsnj	TRACE(("SelectionReceived ->xtermGetSelection\n"));
2856d522f475Smrg	xtermGetSelection(w, list->time,
2857d522f475Smrg			  list->params, list->count, list->targets);
2858d522f475Smrg	XtFree((char *) client_data);
2859d522f475Smrg#if OPT_PASTE64
2860d522f475Smrg    } else {
2861956cc18dSsnj	FinishPaste64(xw);
2862d522f475Smrg#endif
2863d522f475Smrg    }
2864d522f475Smrg    return;
2865d522f475Smrg}
2866d522f475Smrg
2867d522f475Smrgvoid
2868d522f475SmrgHandleInsertSelection(Widget w,
2869894e0ac8Smrg		      XEvent *event,	/* assumed to be XButtonEvent* */
2870e0a2b6dfSmrg		      String *params,	/* selections in precedence order */
2871d522f475Smrg		      Cardinal *num_params)
2872d522f475Smrg{
2873956cc18dSsnj    XtermWidget xw;
2874d522f475Smrg
28755104ee6eSmrg    if ((xw = getXtermWidget(w)) != NULL) {
2876f2e35a3aSmrg	TRACE_EVENT("HandleInsertSelection", event, params, num_params);
2877d522f475Smrg	if (!SendMousePosition(xw, event)) {
2878d522f475Smrg#if OPT_READLINE
2879d522f475Smrg	    int ldelta;
2880956cc18dSsnj	    TScreen *screen = TScreenOf(xw);
2881492d43a5Smrg	    if (IsBtnEvent(event)
2882f2e35a3aSmrg		&& !OverrideEvent(event)
2883913cc679Smrg		&& (okSendMousePos(xw) == MOUSE_OFF)
2884d522f475Smrg		&& SCREEN_FLAG(screen, paste_moves)
2885d522f475Smrg		&& rowOnCurrentLine(screen, eventRow(screen, event), &ldelta))
28865307cd1aSmrg		ReadLineMovePoint(xw, eventColBetween(screen, event), ldelta);
2887d522f475Smrg#endif /* OPT_READLINE */
2888d522f475Smrg
2889d522f475Smrg	    xtermGetSelection(w, event->xbutton.time, params, *num_params, NULL);
2890d522f475Smrg	}
2891d522f475Smrg    }
2892d522f475Smrg}
2893d522f475Smrg
2894d522f475Smrgstatic SelectUnit
2895956cc18dSsnjEvalSelectUnit(XtermWidget xw,
2896d522f475Smrg	       Time buttonDownTime,
2897d522f475Smrg	       SelectUnit defaultUnit,
2898d522f475Smrg	       unsigned int button)
2899d522f475Smrg{
2900956cc18dSsnj    TScreen *screen = TScreenOf(xw);
2901d522f475Smrg    SelectUnit result;
2902d522f475Smrg    int delta;
2903d522f475Smrg
2904d522f475Smrg    if (button != screen->lastButton) {
290520d2c4d2Smrg	delta = screen->multiClickTime + 1;
2906d522f475Smrg    } else if (screen->lastButtonUpTime == (Time) 0) {
2907d522f475Smrg	/* first time and once in a blue moon */
2908d522f475Smrg	delta = screen->multiClickTime + 1;
2909d522f475Smrg    } else if (buttonDownTime > screen->lastButtonUpTime) {
2910d522f475Smrg	/* most of the time */
29112eaa94a1Schristos	delta = (int) (buttonDownTime - screen->lastButtonUpTime);
2912d522f475Smrg    } else {
2913d522f475Smrg	/* time has rolled over since lastButtonUpTime */
29142eaa94a1Schristos	delta = (int) ((((Time) ~ 0) - screen->lastButtonUpTime) + buttonDownTime);
2915d522f475Smrg    }
2916d522f475Smrg
29175104ee6eSmrg#if OPT_BLOCK_SELECT
29185104ee6eSmrg    if (screen->blockSelecting
29195104ee6eSmrg	|| screen->blockSelecting != screen->lastSelectWasBlock) {
29205104ee6eSmrg	/* No word, line, paragraph selecting when block selecting
29215104ee6eSmrg	   or when our last click was a block select */
29225104ee6eSmrg	screen->numberOfClicks = 1;
29235104ee6eSmrg	result = defaultUnit;
29245104ee6eSmrg    } else
29255104ee6eSmrg#endif
2926d522f475Smrg    if (delta > screen->multiClickTime) {
2927d522f475Smrg	screen->numberOfClicks = 1;
2928d522f475Smrg	result = defaultUnit;
2929d522f475Smrg    } else {
2930d522f475Smrg	result = screen->selectMap[screen->numberOfClicks % screen->maxClicks];
2931d522f475Smrg	screen->numberOfClicks += 1;
2932d522f475Smrg    }
2933d522f475Smrg    TRACE(("EvalSelectUnit(%d) = %d\n", screen->numberOfClicks, result));
2934d522f475Smrg    return result;
2935d522f475Smrg}
2936d522f475Smrg
2937d522f475Smrgstatic void
2938d522f475Smrgdo_select_start(XtermWidget xw,
2939894e0ac8Smrg		XEvent *event,	/* must be XButtonEvent* */
2940e0a2b6dfSmrg		CELL *cell)
2941d522f475Smrg{
2942956cc18dSsnj    TScreen *screen = TScreenOf(xw);
2943d522f475Smrg
2944d522f475Smrg    if (SendMousePosition(xw, event))
2945d522f475Smrg	return;
2946956cc18dSsnj    screen->selectUnit = EvalSelectUnit(xw,
2947d522f475Smrg					event->xbutton.time,
2948d522f475Smrg					Select_CHAR,
2949d522f475Smrg					event->xbutton.button);
2950d522f475Smrg    screen->replyToEmacs = False;
2951d522f475Smrg
2952d522f475Smrg#if OPT_READLINE
2953d522f475Smrg    lastButtonDownTime = event->xbutton.time;
2954d522f475Smrg#endif
2955d522f475Smrg
2956d522f475Smrg    StartSelect(xw, cell);
2957d522f475Smrg}
2958d522f475Smrg
2959d522f475Smrg/* ARGSUSED */
2960d522f475Smrgvoid
2961d522f475SmrgHandleSelectStart(Widget w,
2962894e0ac8Smrg		  XEvent *event,	/* must be XButtonEvent* */
2963e0a2b6dfSmrg		  String *params GCC_UNUSED,
2964d522f475Smrg		  Cardinal *num_params GCC_UNUSED)
2965d522f475Smrg{
2966956cc18dSsnj    XtermWidget xw;
2967956cc18dSsnj
29685104ee6eSmrg    if ((xw = getXtermWidget(w)) != NULL) {
2969956cc18dSsnj	TScreen *screen = TScreenOf(xw);
2970d522f475Smrg	CELL cell;
2971d522f475Smrg
2972f2e35a3aSmrg	TRACE_EVENT("HandleSelectStart", event, params, num_params);
2973d522f475Smrg	screen->firstValidRow = 0;
2974d522f475Smrg	screen->lastValidRow = screen->max_row;
2975d522f475Smrg	PointToCELL(screen, event->xbutton.y, event->xbutton.x, &cell);
2976d522f475Smrg
2977d522f475Smrg#if OPT_READLINE
2978d522f475Smrg	ExtendingSelection = 0;
2979d522f475Smrg#endif
2980d522f475Smrg
29815104ee6eSmrg#if OPT_BLOCK_SELECT
29825104ee6eSmrg	screen->blockSelecting =
29835104ee6eSmrg	    (*num_params >= 1 && !strcmp(params[0], "block")) ? 1 : 0;
29845104ee6eSmrg#endif
29855104ee6eSmrg
2986d522f475Smrg	do_select_start(xw, event, &cell);
2987d522f475Smrg    }
2988d522f475Smrg}
2989d522f475Smrg
2990d522f475Smrg/* ARGSUSED */
2991d522f475Smrgvoid
2992d522f475SmrgHandleKeyboardSelectStart(Widget w,
2993894e0ac8Smrg			  XEvent *event,	/* must be XButtonEvent* */
2994e0a2b6dfSmrg			  String *params GCC_UNUSED,
2995d522f475Smrg			  Cardinal *num_params GCC_UNUSED)
2996d522f475Smrg{
2997956cc18dSsnj    XtermWidget xw;
2998956cc18dSsnj
29995104ee6eSmrg    if ((xw = getXtermWidget(w)) != NULL) {
3000956cc18dSsnj	TScreen *screen = TScreenOf(xw);
30010bd37d32Smrg
3002f2e35a3aSmrg	TRACE_EVENT("HandleKeyboardSelectStart", event, params, num_params);
3003d522f475Smrg	do_select_start(xw, event, &screen->cursorp);
3004d522f475Smrg    }
3005d522f475Smrg}
3006d522f475Smrg
3007d522f475Smrgstatic void
3008894e0ac8SmrgTrackDown(XtermWidget xw, XButtonEvent *event)
3009d522f475Smrg{
3010956cc18dSsnj    TScreen *screen = TScreenOf(xw);
3011d522f475Smrg    CELL cell;
3012d522f475Smrg
3013956cc18dSsnj    screen->selectUnit = EvalSelectUnit(xw,
3014d522f475Smrg					event->time,
3015d522f475Smrg					Select_CHAR,
3016d522f475Smrg					event->button);
3017d522f475Smrg    if (screen->numberOfClicks > 1) {
3018d522f475Smrg	PointToCELL(screen, event->y, event->x, &cell);
3019d522f475Smrg	screen->replyToEmacs = True;
3020d522f475Smrg	StartSelect(xw, &cell);
3021d522f475Smrg    } else {
3022d522f475Smrg	screen->waitingForTrackInfo = True;
3023492d43a5Smrg	EditorButton(xw, event);
3024d522f475Smrg    }
3025d522f475Smrg}
3026d522f475Smrg
3027d522f475Smrg#define boundsCheck(x)	if (x < 0) \
3028d522f475Smrg			    x = 0; \
3029d522f475Smrg			else if (x >= screen->max_row) \
3030d522f475Smrg			    x = screen->max_row
3031d522f475Smrg
3032d522f475Smrgvoid
3033d522f475SmrgTrackMouse(XtermWidget xw,
3034d522f475Smrg	   int func,
30355104ee6eSmrg	   const CELL *start,
3036d522f475Smrg	   int firstrow,
3037d522f475Smrg	   int lastrow)
3038d522f475Smrg{
3039956cc18dSsnj    TScreen *screen = TScreenOf(xw);
3040d522f475Smrg
3041d522f475Smrg    if (screen->waitingForTrackInfo) {	/* if Timed, ignore */
3042d522f475Smrg	screen->waitingForTrackInfo = False;
3043d522f475Smrg
3044d522f475Smrg	if (func != 0) {
3045d522f475Smrg	    CELL first = *start;
3046d522f475Smrg
3047d522f475Smrg	    boundsCheck(first.row);
3048d522f475Smrg	    boundsCheck(firstrow);
3049d522f475Smrg	    boundsCheck(lastrow);
3050d522f475Smrg	    screen->firstValidRow = firstrow;
3051d522f475Smrg	    screen->lastValidRow = lastrow;
3052d522f475Smrg	    screen->replyToEmacs = True;
3053d522f475Smrg	    StartSelect(xw, &first);
3054d522f475Smrg	}
3055d522f475Smrg    }
3056d522f475Smrg}
3057d522f475Smrg
3058d522f475Smrgstatic void
3059e0a2b6dfSmrgStartSelect(XtermWidget xw, const CELL *cell)
3060d522f475Smrg{
3061956cc18dSsnj    TScreen *screen = TScreenOf(xw);
3062d522f475Smrg
3063d522f475Smrg    TRACE(("StartSelect row=%d, col=%d\n", cell->row, cell->col));
3064d522f475Smrg    if (screen->cursor_state)
3065f2e35a3aSmrg	HideCursor(xw);
3066d522f475Smrg    if (screen->numberOfClicks == 1) {
3067d522f475Smrg	/* set start of selection */
3068d522f475Smrg	screen->rawPos = *cell;
3069d522f475Smrg    }
3070d522f475Smrg    /* else use old values in rawPos */
3071d522f475Smrg    screen->saveStartR = screen->startExt = screen->rawPos;
3072d522f475Smrg    screen->saveEndR = screen->endExt = screen->rawPos;
3073d522f475Smrg    if (Coordinate(screen, cell) < Coordinate(screen, &(screen->rawPos))) {
3074d522f475Smrg	screen->eventMode = LEFTEXTENSION;
3075d522f475Smrg	screen->startExt = *cell;
3076d522f475Smrg    } else {
3077d522f475Smrg	screen->eventMode = RIGHTEXTENSION;
3078d522f475Smrg	screen->endExt = *cell;
3079d522f475Smrg    }
3080f2e35a3aSmrg    ComputeSelect(xw, &(screen->startExt), &(screen->endExt), False, True);
3081d522f475Smrg}
3082d522f475Smrg
3083d522f475Smrgstatic void
3084d522f475SmrgEndExtend(XtermWidget xw,
3085894e0ac8Smrg	  XEvent *event,	/* must be XButtonEvent */
3086e0a2b6dfSmrg	  String *params,	/* selections */
3087d522f475Smrg	  Cardinal num_params,
3088d522f475Smrg	  Bool use_cursor_loc)
3089d522f475Smrg{
3090d522f475Smrg    CELL cell;
3091956cc18dSsnj    TScreen *screen = TScreenOf(xw);
3092d522f475Smrg
3093f2e35a3aSmrg    TRACE_EVENT("EndExtend", event, params, &num_params);
3094d522f475Smrg    if (use_cursor_loc) {
3095d522f475Smrg	cell = screen->cursorp;
3096d522f475Smrg    } else {
3097d522f475Smrg	PointToCELL(screen, event->xbutton.y, event->xbutton.x, &cell);
3098d522f475Smrg    }
3099d522f475Smrg    ExtendExtend(xw, &cell);
31002e4f8982Smrg
3101d522f475Smrg    screen->lastButtonUpTime = event->xbutton.time;
3102d522f475Smrg    screen->lastButton = event->xbutton.button;
31035104ee6eSmrg#if OPT_BLOCK_SELECT
31045104ee6eSmrg    screen->lastSelectWasBlock = screen->blockSelecting;
31055104ee6eSmrg#endif
31062e4f8982Smrg
3107d522f475Smrg    if (!isSameCELL(&(screen->startSel), &(screen->endSel))) {
3108d522f475Smrg	if (screen->replyToEmacs) {
31092e4f8982Smrg	    Char line[64];
31102e4f8982Smrg	    unsigned count = 0;
31112e4f8982Smrg
3112d522f475Smrg	    if (screen->control_eight_bits) {
3113d522f475Smrg		line[count++] = ANSI_CSI;
3114d522f475Smrg	    } else {
3115d522f475Smrg		line[count++] = ANSI_ESC;
3116d522f475Smrg		line[count++] = '[';
3117d522f475Smrg	    }
3118d522f475Smrg	    if (isSameCELL(&(screen->rawPos), &(screen->startSel))
3119d522f475Smrg		&& isSameCELL(&cell, &(screen->endSel))) {
3120d522f475Smrg		/* Use short-form emacs select */
31210bd37d32Smrg
31220bd37d32Smrg		switch (screen->extend_coords) {
31230bd37d32Smrg		case 0:
31240bd37d32Smrg		case SET_EXT_MODE_MOUSE:
31250bd37d32Smrg		    line[count++] = 't';
31260bd37d32Smrg		    break;
31270bd37d32Smrg		case SET_SGR_EXT_MODE_MOUSE:
3128f2e35a3aSmrg		case SET_PIXEL_POSITION_MOUSE:
31290bd37d32Smrg		    line[count++] = '<';
31300bd37d32Smrg		    break;
31310bd37d32Smrg		}
31320bd37d32Smrg
3133492d43a5Smrg		count = EmitMousePosition(screen, line, count, screen->endSel.col);
31340bd37d32Smrg		count = EmitMousePositionSeparator(screen, line, count);
3135492d43a5Smrg		count = EmitMousePosition(screen, line, count, screen->endSel.row);
31360bd37d32Smrg
31370bd37d32Smrg		switch (screen->extend_coords) {
31380bd37d32Smrg		case SET_SGR_EXT_MODE_MOUSE:
31390bd37d32Smrg		case SET_URXVT_EXT_MODE_MOUSE:
3140f2e35a3aSmrg		case SET_PIXEL_POSITION_MOUSE:
31410bd37d32Smrg		    line[count++] = 't';
31420bd37d32Smrg		    break;
31430bd37d32Smrg		}
3144d522f475Smrg	    } else {
3145d522f475Smrg		/* long-form, specify everything */
31460bd37d32Smrg
31470bd37d32Smrg		switch (screen->extend_coords) {
31480bd37d32Smrg		case 0:
31490bd37d32Smrg		case SET_EXT_MODE_MOUSE:
31500bd37d32Smrg		    line[count++] = 'T';
31510bd37d32Smrg		    break;
31520bd37d32Smrg		case SET_SGR_EXT_MODE_MOUSE:
3153f2e35a3aSmrg		case SET_PIXEL_POSITION_MOUSE:
31540bd37d32Smrg		    line[count++] = '<';
31550bd37d32Smrg		    break;
31560bd37d32Smrg		}
31570bd37d32Smrg
3158492d43a5Smrg		count = EmitMousePosition(screen, line, count, screen->startSel.col);
31590bd37d32Smrg		count = EmitMousePositionSeparator(screen, line, count);
3160492d43a5Smrg		count = EmitMousePosition(screen, line, count, screen->startSel.row);
31610bd37d32Smrg		count = EmitMousePositionSeparator(screen, line, count);
3162492d43a5Smrg		count = EmitMousePosition(screen, line, count, screen->endSel.col);
31630bd37d32Smrg		count = EmitMousePositionSeparator(screen, line, count);
3164492d43a5Smrg		count = EmitMousePosition(screen, line, count, screen->endSel.row);
31650bd37d32Smrg		count = EmitMousePositionSeparator(screen, line, count);
3166492d43a5Smrg		count = EmitMousePosition(screen, line, count, cell.col);
31670bd37d32Smrg		count = EmitMousePositionSeparator(screen, line, count);
3168492d43a5Smrg		count = EmitMousePosition(screen, line, count, cell.row);
31690bd37d32Smrg
31700bd37d32Smrg		switch (screen->extend_coords) {
31710bd37d32Smrg		case SET_SGR_EXT_MODE_MOUSE:
31720bd37d32Smrg		case SET_URXVT_EXT_MODE_MOUSE:
3173f2e35a3aSmrg		case SET_PIXEL_POSITION_MOUSE:
31740bd37d32Smrg		    line[count++] = 'T';
31750bd37d32Smrg		    break;
31760bd37d32Smrg		}
3177d522f475Smrg	    }
31785307cd1aSmrg	    v_write(screen->respond, line, (size_t) count);
3179f2e35a3aSmrg	    UnHiliteText(xw);
3180d522f475Smrg	}
3181d522f475Smrg    }
3182d522f475Smrg    SelectSet(xw, event, params, num_params);
3183d522f475Smrg    screen->eventMode = NORMAL;
3184d522f475Smrg}
3185d522f475Smrg
3186d522f475Smrgvoid
3187d522f475SmrgHandleSelectSet(Widget w,
3188894e0ac8Smrg		XEvent *event,
3189e0a2b6dfSmrg		String *params,
3190d522f475Smrg		Cardinal *num_params)
3191d522f475Smrg{
3192956cc18dSsnj    XtermWidget xw;
3193956cc18dSsnj
31945104ee6eSmrg    if ((xw = getXtermWidget(w)) != NULL) {
3195f2e35a3aSmrg	TRACE_EVENT("HandleSelectSet", event, params, num_params);
3196956cc18dSsnj	SelectSet(xw, event, params, *num_params);
3197d522f475Smrg    }
3198d522f475Smrg}
3199d522f475Smrg
3200d522f475Smrg/* ARGSUSED */
3201d522f475Smrgstatic void
3202d522f475SmrgSelectSet(XtermWidget xw,
3203894e0ac8Smrg	  XEvent *event GCC_UNUSED,
3204e0a2b6dfSmrg	  String *params,
3205d522f475Smrg	  Cardinal num_params)
3206d522f475Smrg{
3207956cc18dSsnj    TScreen *screen = TScreenOf(xw);
3208d522f475Smrg
3209d522f475Smrg    TRACE(("SelectSet\n"));
3210d522f475Smrg    /* Only do select stuff if non-null select */
3211d522f475Smrg    if (!isSameCELL(&(screen->startSel), &(screen->endSel))) {
3212f2e35a3aSmrg	Cardinal n;
3213f2e35a3aSmrg	for (n = 0; n < num_params; ++n) {
3214f2e35a3aSmrg	    SaltTextAway(xw,
3215f2e35a3aSmrg			 TargetToSelection(screen, params[n]),
3216f2e35a3aSmrg			 &(screen->startSel), &(screen->endSel));
3217f2e35a3aSmrg	}
32180bd37d32Smrg	_OwnSelection(xw, params, num_params);
3219d522f475Smrg    } else {
32200bd37d32Smrg	ScrnDisownSelection(xw);
3221d522f475Smrg    }
3222d522f475Smrg}
3223d522f475Smrg
3224d522f475Smrg#define Abs(x)		((x) < 0 ? -(x) : (x))
3225d522f475Smrg
3226d522f475Smrg/* ARGSUSED */
3227d522f475Smrgstatic void
3228d522f475Smrgdo_start_extend(XtermWidget xw,
3229894e0ac8Smrg		XEvent *event,	/* must be XButtonEvent* */
3230e0a2b6dfSmrg		String *params GCC_UNUSED,
3231d522f475Smrg		Cardinal *num_params GCC_UNUSED,
3232d522f475Smrg		Bool use_cursor_loc)
3233d522f475Smrg{
3234956cc18dSsnj    TScreen *screen = TScreenOf(xw);
3235d522f475Smrg    int coord;
3236d522f475Smrg    CELL cell;
3237d522f475Smrg
3238d522f475Smrg    if (SendMousePosition(xw, event))
3239d522f475Smrg	return;
3240d522f475Smrg
3241d522f475Smrg    screen->firstValidRow = 0;
3242d522f475Smrg    screen->lastValidRow = screen->max_row;
3243d522f475Smrg#if OPT_READLINE
3244f2e35a3aSmrg    if (OverrideEvent(event)
3245d522f475Smrg	|| event->xbutton.button != Button3
3246d522f475Smrg	|| !(SCREEN_FLAG(screen, dclick3_deletes)))
3247d522f475Smrg#endif
3248956cc18dSsnj	screen->selectUnit = EvalSelectUnit(xw,
3249d522f475Smrg					    event->xbutton.time,
3250d522f475Smrg					    screen->selectUnit,
3251d522f475Smrg					    event->xbutton.button);
3252d522f475Smrg    screen->replyToEmacs = False;
3253d522f475Smrg
3254d522f475Smrg#if OPT_READLINE
3255f2e35a3aSmrg    CheckSecondPress3(xw, screen, event);
3256d522f475Smrg#endif
3257d522f475Smrg
3258d522f475Smrg    if (screen->numberOfClicks == 1
3259f2e35a3aSmrg	|| (SCREEN_FLAG(screen, dclick3_deletes)
3260f2e35a3aSmrg	    && !OverrideEvent(event))) {
3261d522f475Smrg	/* Save existing selection so we can reestablish it if the guy
3262d522f475Smrg	   extends past the other end of the selection */
3263d522f475Smrg	screen->saveStartR = screen->startExt = screen->startRaw;
3264d522f475Smrg	screen->saveEndR = screen->endExt = screen->endRaw;
3265d522f475Smrg    } else {
3266d522f475Smrg	/* He just needed the selection mode changed, use old values. */
3267d522f475Smrg	screen->startExt = screen->startRaw = screen->saveStartR;
3268d522f475Smrg	screen->endExt = screen->endRaw = screen->saveEndR;
3269d522f475Smrg    }
3270d522f475Smrg    if (use_cursor_loc) {
3271d522f475Smrg	cell = screen->cursorp;
3272d522f475Smrg    } else {
3273d522f475Smrg	PointToCELL(screen, event->xbutton.y, event->xbutton.x, &cell);
3274d522f475Smrg    }
3275d522f475Smrg    coord = Coordinate(screen, &cell);
3276d522f475Smrg
3277d522f475Smrg    if (Abs(coord - Coordinate(screen, &(screen->startSel)))
3278d522f475Smrg	< Abs(coord - Coordinate(screen, &(screen->endSel)))
3279d522f475Smrg	|| coord < Coordinate(screen, &(screen->startSel))) {
3280d522f475Smrg	/* point is close to left side of selection */
3281d522f475Smrg	screen->eventMode = LEFTEXTENSION;
3282d522f475Smrg	screen->startExt = cell;
3283d522f475Smrg    } else {
3284d522f475Smrg	/* point is close to left side of selection */
3285d522f475Smrg	screen->eventMode = RIGHTEXTENSION;
3286d522f475Smrg	screen->endExt = cell;
3287d522f475Smrg    }
3288f2e35a3aSmrg    ComputeSelect(xw, &(screen->startExt), &(screen->endExt), True, True);
3289d522f475Smrg
3290d522f475Smrg#if OPT_READLINE
3291d522f475Smrg    if (!isSameCELL(&(screen->startSel), &(screen->endSel)))
3292d522f475Smrg	ExtendingSelection = 1;
3293d522f475Smrg#endif
3294d522f475Smrg}
3295d522f475Smrg
3296d522f475Smrgstatic void
3297e0a2b6dfSmrgExtendExtend(XtermWidget xw, const CELL *cell)
3298d522f475Smrg{
3299956cc18dSsnj    TScreen *screen = TScreenOf(xw);
3300d522f475Smrg    int coord = Coordinate(screen, cell);
3301d522f475Smrg
3302d522f475Smrg    TRACE(("ExtendExtend row=%d, col=%d\n", cell->row, cell->col));
3303d522f475Smrg    if (screen->eventMode == LEFTEXTENSION
3304d522f475Smrg	&& ((coord + (screen->selectUnit != Select_CHAR))
3305d522f475Smrg	    > Coordinate(screen, &(screen->endSel)))) {
3306d522f475Smrg	/* Whoops, he's changed his mind.  Do RIGHTEXTENSION */
3307d522f475Smrg	screen->eventMode = RIGHTEXTENSION;
3308d522f475Smrg	screen->startExt = screen->saveStartR;
3309d522f475Smrg    } else if (screen->eventMode == RIGHTEXTENSION
3310d522f475Smrg	       && coord < Coordinate(screen, &(screen->startSel))) {
3311d522f475Smrg	/* Whoops, he's changed his mind.  Do LEFTEXTENSION */
3312d522f475Smrg	screen->eventMode = LEFTEXTENSION;
3313d522f475Smrg	screen->endExt = screen->saveEndR;
3314d522f475Smrg    }
3315d522f475Smrg    if (screen->eventMode == LEFTEXTENSION) {
3316d522f475Smrg	screen->startExt = *cell;
3317d522f475Smrg    } else {
3318d522f475Smrg	screen->endExt = *cell;
3319d522f475Smrg    }
3320f2e35a3aSmrg    ComputeSelect(xw, &(screen->startExt), &(screen->endExt), False, True);
3321d522f475Smrg
3322d522f475Smrg#if OPT_READLINE
3323d522f475Smrg    if (!isSameCELL(&(screen->startSel), &(screen->endSel)))
3324d522f475Smrg	ExtendingSelection = 1;
3325d522f475Smrg#endif
3326d522f475Smrg}
3327d522f475Smrg
3328d522f475Smrgvoid
3329d522f475SmrgHandleStartExtend(Widget w,
3330894e0ac8Smrg		  XEvent *event,	/* must be XButtonEvent* */
3331e0a2b6dfSmrg		  String *params,	/* unused */
3332d522f475Smrg		  Cardinal *num_params)		/* unused */
3333d522f475Smrg{
3334956cc18dSsnj    XtermWidget xw;
3335956cc18dSsnj
33365104ee6eSmrg    if ((xw = getXtermWidget(w)) != NULL) {
3337f2e35a3aSmrg	TRACE_EVENT("HandleStartExtend", event, params, num_params);
3338956cc18dSsnj	do_start_extend(xw, event, params, num_params, False);
3339956cc18dSsnj    }
3340d522f475Smrg}
3341d522f475Smrg
3342d522f475Smrgvoid
3343d522f475SmrgHandleKeyboardStartExtend(Widget w,
3344894e0ac8Smrg			  XEvent *event,	/* must be XButtonEvent* */
3345e0a2b6dfSmrg			  String *params,	/* unused */
3346d522f475Smrg			  Cardinal *num_params)		/* unused */
3347d522f475Smrg{
3348956cc18dSsnj    XtermWidget xw;
3349956cc18dSsnj
33505104ee6eSmrg    if ((xw = getXtermWidget(w)) != NULL) {
3351f2e35a3aSmrg	TRACE_EVENT("HandleKeyboardStartExtend", event, params, num_params);
3352956cc18dSsnj	do_start_extend(xw, event, params, num_params, True);
3353956cc18dSsnj    }
3354d522f475Smrg}
3355d522f475Smrg
3356d522f475Smrgvoid
3357e0a2b6dfSmrgScrollSelection(TScreen *screen, int amount, Bool always)
3358d522f475Smrg{
3359d522f475Smrg    int minrow = INX2ROW(screen, -screen->savedlines);
3360d522f475Smrg    int maxrow = INX2ROW(screen, screen->max_row);
3361d522f475Smrg    int maxcol = screen->max_col;
3362d522f475Smrg
3363d522f475Smrg#define scroll_update_one(cell) \
3364d522f475Smrg	(cell)->row += amount; \
3365d522f475Smrg	if ((cell)->row < minrow) { \
3366d522f475Smrg	    (cell)->row = minrow; \
3367d522f475Smrg	    (cell)->col = 0; \
3368d522f475Smrg	} \
3369d522f475Smrg	if ((cell)->row > maxrow) { \
3370d522f475Smrg	    (cell)->row = maxrow; \
3371d522f475Smrg	    (cell)->col = maxcol; \
3372d522f475Smrg	}
3373d522f475Smrg
3374d522f475Smrg    scroll_update_one(&(screen->startRaw));
3375d522f475Smrg    scroll_update_one(&(screen->endRaw));
3376d522f475Smrg    scroll_update_one(&(screen->startSel));
3377d522f475Smrg    scroll_update_one(&(screen->endSel));
3378d522f475Smrg
3379d522f475Smrg    scroll_update_one(&(screen->rawPos));
3380d522f475Smrg
3381d522f475Smrg    /*
3382d522f475Smrg     * If we are told to scroll the selection but it lies outside the scrolling
3383d522f475Smrg     * margins, then that could cause the selection to move (bad).  It is not
3384d522f475Smrg     * simple to fix, because this function is called both for the scrollbar
3385d522f475Smrg     * actions as well as application scrolling.  The 'always' flag is set in
3386d522f475Smrg     * the former case.  The rest of the logic handles the latter.
3387d522f475Smrg     */
3388d522f475Smrg    if (ScrnHaveSelection(screen)) {
3389d522f475Smrg	int adjust;
3390d522f475Smrg
3391d522f475Smrg	adjust = ROW2INX(screen, screen->startH.row);
3392d522f475Smrg	if (always
33930bd37d32Smrg	    || !ScrnHaveRowMargins(screen)
33940bd37d32Smrg	    || ScrnIsRowInMargins(screen, adjust)) {
3395d522f475Smrg	    scroll_update_one(&screen->startH);
3396d522f475Smrg	}
3397d522f475Smrg	adjust = ROW2INX(screen, screen->endH.row);
3398d522f475Smrg	if (always
33990bd37d32Smrg	    || !ScrnHaveRowMargins(screen)
34000bd37d32Smrg	    || ScrnIsRowInMargins(screen, adjust)) {
3401d522f475Smrg	    scroll_update_one(&screen->endH);
3402d522f475Smrg	}
3403d522f475Smrg    }
3404d522f475Smrg
3405d522f475Smrg    screen->startHCoord = Coordinate(screen, &screen->startH);
3406d522f475Smrg    screen->endHCoord = Coordinate(screen, &screen->endH);
3407d522f475Smrg}
3408d522f475Smrg
3409d522f475Smrg/*ARGSUSED*/
3410d522f475Smrgvoid
3411f2e35a3aSmrgResizeSelection(TScreen *screen, int rows, int cols)
3412d522f475Smrg{
3413d522f475Smrg    rows--;			/* decr to get 0-max */
3414d522f475Smrg    cols--;
3415d522f475Smrg
3416d522f475Smrg    if (screen->startRaw.row > rows)
3417d522f475Smrg	screen->startRaw.row = rows;
3418d522f475Smrg    if (screen->startSel.row > rows)
3419d522f475Smrg	screen->startSel.row = rows;
3420d522f475Smrg    if (screen->endRaw.row > rows)
3421d522f475Smrg	screen->endRaw.row = rows;
3422d522f475Smrg    if (screen->endSel.row > rows)
3423d522f475Smrg	screen->endSel.row = rows;
3424d522f475Smrg    if (screen->rawPos.row > rows)
3425d522f475Smrg	screen->rawPos.row = rows;
3426d522f475Smrg
3427d522f475Smrg    if (screen->startRaw.col > cols)
3428d522f475Smrg	screen->startRaw.col = cols;
3429d522f475Smrg    if (screen->startSel.col > cols)
3430d522f475Smrg	screen->startSel.col = cols;
3431d522f475Smrg    if (screen->endRaw.col > cols)
3432d522f475Smrg	screen->endRaw.col = cols;
3433d522f475Smrg    if (screen->endSel.col > cols)
3434d522f475Smrg	screen->endSel.col = cols;
3435d522f475Smrg    if (screen->rawPos.col > cols)
3436d522f475Smrg	screen->rawPos.col = cols;
3437d522f475Smrg}
3438d522f475Smrg
3439d522f475Smrg#if OPT_WIDE_CHARS
3440f2e35a3aSmrg#define isWideCell(row, col) isWideFrg((int)XTERM_CELL(row, col))
3441d522f475Smrg#endif
3442d522f475Smrg
3443d522f475Smrgstatic void
3444e0a2b6dfSmrgPointToCELL(TScreen *screen,
3445d522f475Smrg	    int y,
3446d522f475Smrg	    int x,
3447e0a2b6dfSmrg	    CELL *cell)
3448d522f475Smrg/* Convert pixel coordinates to character coordinates.
3449d522f475Smrg   Rows are clipped between firstValidRow and lastValidRow.
3450d522f475Smrg   Columns are clipped between to be 0 or greater, but are not clipped to some
3451d522f475Smrg       maximum value. */
3452d522f475Smrg{
3453d522f475Smrg    cell->row = (y - screen->border) / FontHeight(screen);
3454d522f475Smrg    if (cell->row < screen->firstValidRow)
3455d522f475Smrg	cell->row = screen->firstValidRow;
3456d522f475Smrg    else if (cell->row > screen->lastValidRow)
3457d522f475Smrg	cell->row = screen->lastValidRow;
3458d522f475Smrg    cell->col = (x - OriginX(screen)) / FontWidth(screen);
3459d522f475Smrg    if (cell->col < 0)
3460d522f475Smrg	cell->col = 0;
3461d522f475Smrg    else if (cell->col > MaxCols(screen)) {
3462d522f475Smrg	cell->col = MaxCols(screen);
3463d522f475Smrg    }
3464d522f475Smrg#if OPT_WIDE_CHARS
3465d522f475Smrg    /*
3466d522f475Smrg     * If we got a click on the right half of a doublewidth character,
3467d522f475Smrg     * pretend it happened on the left half.
3468d522f475Smrg     */
3469d522f475Smrg    if (cell->col > 0
3470d522f475Smrg	&& isWideCell(cell->row, cell->col - 1)
3471d522f475Smrg	&& (XTERM_CELL(cell->row, cell->col) == HIDDEN_CHAR)) {
3472d522f475Smrg	cell->col -= 1;
3473d522f475Smrg    }
3474d522f475Smrg#endif
3475d522f475Smrg}
3476d522f475Smrg
3477d522f475Smrg/*
3478d522f475Smrg * Find the last column at which text was drawn on the given row.
3479d522f475Smrg */
3480d522f475Smrgstatic int
348101037d57SmrgLastTextCol(TScreen *screen, CLineData *ld, int row)
3482d522f475Smrg{
348320d2c4d2Smrg    int i = -1;
3484d522f475Smrg
34855104ee6eSmrg    if (ld != NULL) {
348620d2c4d2Smrg	if (okScrnRow(screen, row)) {
34872e4f8982Smrg	    const IAttr *ch;
348820d2c4d2Smrg	    for (i = screen->max_col,
348920d2c4d2Smrg		 ch = ld->attribs + i;
349020d2c4d2Smrg		 i >= 0 && !(*ch & CHARDRAWN);
349120d2c4d2Smrg		 ch--, i--) {
349220d2c4d2Smrg		;
349320d2c4d2Smrg	    }
3494d522f475Smrg#if OPT_DEC_CHRSET
349520d2c4d2Smrg	    if (CSET_DOUBLE(GetLineDblCS(ld))) {
349620d2c4d2Smrg		i *= 2;
349720d2c4d2Smrg	    }
3498d522f475Smrg#endif
349920d2c4d2Smrg	}
3500d522f475Smrg    }
3501d522f475Smrg    return (i);
3502d522f475Smrg}
3503d522f475Smrg
3504d522f475Smrg#if !OPT_WIDE_CHARS
3505d522f475Smrg/*
3506d522f475Smrg** double click table for cut and paste in 8 bits
3507d522f475Smrg**
3508d522f475Smrg** This table is divided in four parts :
3509d522f475Smrg**
3510d522f475Smrg**	- control characters	[0,0x1f] U [0x80,0x9f]
3511d522f475Smrg**	- separators		[0x20,0x3f] U [0xa0,0xb9]
3512d522f475Smrg**	- binding characters	[0x40,0x7f] U [0xc0,0xff]
3513d522f475Smrg**	- exceptions
3514d522f475Smrg*/
3515d522f475Smrg/* *INDENT-OFF* */
3516d522f475Smrgstatic int charClass[256] =
3517d522f475Smrg{
3518d522f475Smrg/* NUL  SOH  STX  ETX  EOT  ENQ  ACK  BEL */
3519d522f475Smrg    32,  1,    1,   1,   1,   1,   1,   1,
35200bd37d32Smrg/*  BS   HT   NL   VT   FF   CR   SO   SI */
3521d522f475Smrg     1,  32,   1,   1,   1,   1,   1,   1,
3522d522f475Smrg/* DLE  DC1  DC2  DC3  DC4  NAK  SYN  ETB */
3523d522f475Smrg     1,   1,   1,   1,   1,   1,   1,   1,
3524d522f475Smrg/* CAN   EM  SUB  ESC   FS   GS   RS   US */
3525d522f475Smrg     1,   1,   1,   1,   1,   1,   1,   1,
3526d522f475Smrg/*  SP    !    "    #    $    %    &    ' */
3527d522f475Smrg    32,  33,  34,  35,  36,  37,  38,  39,
3528d522f475Smrg/*   (    )    *    +    ,    -    .    / */
3529d522f475Smrg    40,  41,  42,  43,  44,  45,  46,  47,
3530d522f475Smrg/*   0    1    2    3    4    5    6    7 */
3531d522f475Smrg    48,  48,  48,  48,  48,  48,  48,  48,
3532d522f475Smrg/*   8    9    :    ;    <    =    >    ? */
3533d522f475Smrg    48,  48,  58,  59,  60,  61,  62,  63,
3534d522f475Smrg/*   @    A    B    C    D    E    F    G */
3535d522f475Smrg    64,  48,  48,  48,  48,  48,  48,  48,
3536d522f475Smrg/*   H    I    J    K    L    M    N    O */
3537d522f475Smrg    48,  48,  48,  48,  48,  48,  48,  48,
3538d522f475Smrg/*   P    Q    R    S    T    U    V    W */
3539d522f475Smrg    48,  48,  48,  48,  48,  48,  48,  48,
3540d522f475Smrg/*   X    Y    Z    [    \    ]    ^    _ */
3541d522f475Smrg    48,  48,  48,  91,  92,  93,  94,  48,
3542d522f475Smrg/*   `    a    b    c    d    e    f    g */
3543d522f475Smrg    96,  48,  48,  48,  48,  48,  48,  48,
3544d522f475Smrg/*   h    i    j    k    l    m    n    o */
3545d522f475Smrg    48,  48,  48,  48,  48,  48,  48,  48,
3546d522f475Smrg/*   p    q    r    s    t    u    v    w */
3547d522f475Smrg    48,  48,  48,  48,  48,  48,  48,  48,
3548d522f475Smrg/*   x    y    z    {    |    }    ~  DEL */
3549d522f475Smrg    48,  48,  48, 123, 124, 125, 126,   1,
3550d522f475Smrg/* x80  x81  x82  x83  IND  NEL  SSA  ESA */
3551d522f475Smrg    1,    1,   1,   1,   1,   1,   1,   1,
3552d522f475Smrg/* HTS  HTJ  VTS  PLD  PLU   RI  SS2  SS3 */
3553d522f475Smrg    1,    1,   1,   1,   1,   1,   1,   1,
3554d522f475Smrg/* DCS  PU1  PU2  STS  CCH   MW  SPA  EPA */
3555d522f475Smrg    1,    1,   1,   1,   1,   1,   1,   1,
3556d522f475Smrg/* x98  x99  x9A  CSI   ST  OSC   PM  APC */
3557d522f475Smrg    1,    1,   1,   1,   1,   1,   1,   1,
3558d522f475Smrg/*   -    i   c/    L   ox   Y-    |   So */
3559d522f475Smrg    160, 161, 162, 163, 164, 165, 166, 167,
3560d522f475Smrg/*  ..   c0   ip   <<    _        R0    - */
3561d522f475Smrg    168, 169, 170, 171, 172, 173, 174, 175,
3562d522f475Smrg/*   o   +-    2    3    '    u   q|    . */
3563d522f475Smrg    176, 177, 178, 179, 180, 181, 182, 183,
3564d522f475Smrg/*   ,    1    2   >>  1/4  1/2  3/4    ? */
3565d522f475Smrg    184, 185, 186, 187, 188, 189, 190, 191,
3566d522f475Smrg/*  A`   A'   A^   A~   A:   Ao   AE   C, */
3567d522f475Smrg     48,  48,  48,  48,  48,  48,  48,  48,
3568d522f475Smrg/*  E`   E'   E^   E:   I`   I'   I^   I: */
3569d522f475Smrg     48,  48,  48,  48,  48,  48,  48,  48,
3570d522f475Smrg/*  D-   N~   O`   O'   O^   O~   O:    X */
3571d522f475Smrg     48,  48,  48,  48,  48,  48,  48, 215,
3572d522f475Smrg/*  O/   U`   U'   U^   U:   Y'    P    B */
3573d522f475Smrg     48,  48,  48,  48,  48,  48,  48,  48,
3574d522f475Smrg/*  a`   a'   a^   a~   a:   ao   ae   c, */
3575d522f475Smrg     48,  48,  48,  48,  48,  48,  48,  48,
3576d522f475Smrg/*  e`   e'   e^   e:    i`  i'   i^   i: */
3577d522f475Smrg     48,  48,  48,  48,  48,  48,  48,  48,
3578d522f475Smrg/*   d   n~   o`   o'   o^   o~   o:   -: */
3579d522f475Smrg     48,  48,  48,  48,  48,  48,  48, 247,
3580d522f475Smrg/*  o/   u`   u'   u^   u:   y'    P   y: */
3581d522f475Smrg     48,  48,  48,  48,  48,  48,  48,  48};
3582d522f475Smrg/* *INDENT-ON* */
3583d522f475Smrg
3584d522f475Smrgint
3585d522f475SmrgSetCharacterClassRange(int low,	/* in range of [0..255] */
3586d522f475Smrg		       int high,
3587d522f475Smrg		       int value)	/* arbitrary */
3588d522f475Smrg{
3589d522f475Smrg
3590d522f475Smrg    if (low < 0 || high > 255 || high < low)
3591d522f475Smrg	return (-1);
3592d522f475Smrg
3593d522f475Smrg    for (; low <= high; low++)
3594d522f475Smrg	charClass[low] = value;
3595d522f475Smrg
3596d522f475Smrg    return (0);
3597d522f475Smrg}
3598d522f475Smrg#endif
3599d522f475Smrg
3600d522f475Smrgstatic int
36015104ee6eSmrgclass_of(LineData *ld, const CELL *cell)
3602d522f475Smrg{
3603d522f475Smrg    CELL temp = *cell;
36040bd37d32Smrg    int result = 0;
3605d522f475Smrg
3606d522f475Smrg#if OPT_DEC_CHRSET
3607956cc18dSsnj    if (CSET_DOUBLE(GetLineDblCS(ld))) {
3608d522f475Smrg	temp.col /= 2;
3609d522f475Smrg    }
3610d522f475Smrg#endif
36110bd37d32Smrg    if (temp.col < (int) ld->lineSize)
36120bd37d32Smrg	result = CharacterClass((int) (ld->charData[temp.col]));
36130bd37d32Smrg    return result;
3614d522f475Smrg}
3615956cc18dSsnj
3616956cc18dSsnj#if OPT_WIDE_CHARS
3617956cc18dSsnj#define CClassSelects(name, cclass) \
3618956cc18dSsnj	 (CClassOf(name) == cclass \
3619956cc18dSsnj	 || XTERM_CELL(screen->name.row, screen->name.col) == HIDDEN_CHAR)
3620d522f475Smrg#else
3621956cc18dSsnj#define CClassSelects(name, cclass) \
3622956cc18dSsnj	 (class_of(ld.name, &((screen->name))) == cclass)
3623d522f475Smrg#endif
3624d522f475Smrg
3625956cc18dSsnj#define CClassOf(name) class_of(ld.name, &((screen->name)))
3626956cc18dSsnj
3627913cc679Smrg#if OPT_REPORT_CCLASS
3628913cc679Smrgstatic int
3629913cc679Smrgshow_cclass_range(int lo, int hi)
3630913cc679Smrg{
3631913cc679Smrg    int cclass = CharacterClass(lo);
3632913cc679Smrg    int ident = (cclass == lo);
3633913cc679Smrg    int more = 0;
3634913cc679Smrg    if (ident) {
3635913cc679Smrg	int ch;
3636913cc679Smrg	for (ch = lo + 1; ch <= hi; ch++) {
3637913cc679Smrg	    if (CharacterClass(ch) != ch) {
3638913cc679Smrg		ident = 0;
3639913cc679Smrg		break;
3640913cc679Smrg	    }
3641913cc679Smrg	}
3642913cc679Smrg	if (ident && (hi < 255)) {
3643913cc679Smrg	    ch = hi + 1;
3644913cc679Smrg	    if (CharacterClass(ch) == ch) {
3645913cc679Smrg		if (ch >= 255 || CharacterClass(ch + 1) != ch) {
3646913cc679Smrg		    more = 1;
3647913cc679Smrg		}
3648913cc679Smrg	    }
3649913cc679Smrg	}
3650913cc679Smrg    }
3651913cc679Smrg    if (!more) {
3652913cc679Smrg	if (lo == hi) {
3653913cc679Smrg	    printf("\t%d", lo);
3654913cc679Smrg	} else {
3655913cc679Smrg	    printf("\t%d-%d", lo, hi);
3656913cc679Smrg	}
3657913cc679Smrg	if (!ident)
3658913cc679Smrg	    printf(":%d", cclass);
3659913cc679Smrg	if (hi < 255)
3660913cc679Smrg	    printf(", \\");
3661913cc679Smrg	printf("\n");
3662913cc679Smrg    }
3663913cc679Smrg    return !more;
3664913cc679Smrg}
3665913cc679Smrg
3666913cc679Smrgvoid
3667913cc679Smrgreport_char_class(XtermWidget xw)
3668913cc679Smrg{
3669913cc679Smrg    /* simple table, to match documentation */
3670913cc679Smrg    static const char charnames[] =
3671913cc679Smrg    "NUL\0" "SOH\0" "STX\0" "ETX\0" "EOT\0" "ENQ\0" "ACK\0" "BEL\0"
3672913cc679Smrg    " BS\0" " HT\0" " NL\0" " VT\0" " NP\0" " CR\0" " SO\0" " SI\0"
3673913cc679Smrg    "DLE\0" "DC1\0" "DC2\0" "DC3\0" "DC4\0" "NAK\0" "SYN\0" "ETB\0"
3674913cc679Smrg    "CAN\0" " EM\0" "SUB\0" "ESC\0" " FS\0" " GS\0" " RS\0" " US\0"
3675913cc679Smrg    " SP\0" "  !\0" "  \"\0" "  #\0" "  $\0" "  %\0" "  &\0" "  '\0"
3676913cc679Smrg    "  (\0" "  )\0" "  *\0" "  +\0" "  ,\0" "  -\0" "  .\0" "  /\0"
3677913cc679Smrg    "  0\0" "  1\0" "  2\0" "  3\0" "  4\0" "  5\0" "  6\0" "  7\0"
3678913cc679Smrg    "  8\0" "  9\0" "  :\0" "  ;\0" "  <\0" "  =\0" "  >\0" "  ?\0"
3679913cc679Smrg    "  @\0" "  A\0" "  B\0" "  C\0" "  D\0" "  E\0" "  F\0" "  G\0"
3680913cc679Smrg    "  H\0" "  I\0" "  J\0" "  K\0" "  L\0" "  M\0" "  N\0" "  O\0"
3681913cc679Smrg    "  P\0" "  Q\0" "  R\0" "  S\0" "  T\0" "  U\0" "  V\0" "  W\0"
3682913cc679Smrg    "  X\0" "  Y\0" "  Z\0" "  [\0" "  \\\0" "  ]\0" "  ^\0" "  _\0"
3683913cc679Smrg    "  `\0" "  a\0" "  b\0" "  c\0" "  d\0" "  e\0" "  f\0" "  g\0"
3684913cc679Smrg    "  h\0" "  i\0" "  j\0" "  k\0" "  l\0" "  m\0" "  n\0" "  o\0"
3685913cc679Smrg    "  p\0" "  q\0" "  r\0" "  s\0" "  t\0" "  u\0" "  v\0" "  w\0"
3686913cc679Smrg    "  x\0" "  y\0" "  z\0" "  {\0" "  |\0" "  }\0" "  ~\0" "DEL\0"
3687913cc679Smrg    "x80\0" "x81\0" "x82\0" "x83\0" "IND\0" "NEL\0" "SSA\0" "ESA\0"
3688913cc679Smrg    "HTS\0" "HTJ\0" "VTS\0" "PLD\0" "PLU\0" " RI\0" "SS2\0" "SS3\0"
3689913cc679Smrg    "DCS\0" "PU1\0" "PU2\0" "STS\0" "CCH\0" " MW\0" "SPA\0" "EPA\0"
3690913cc679Smrg    "x98\0" "x99\0" "x9A\0" "CSI\0" " ST\0" "OSC\0" " PM\0" "APC\0"
3691913cc679Smrg    "  -\0" "  i\0" " c/\0" "  L\0" " ox\0" " Y-\0" "  |\0" " So\0"
3692913cc679Smrg    " ..\0" " c0\0" " ip\0" " <<\0" "  _\0" "   \0" " R0\0" "  -\0"
3693913cc679Smrg    "  o\0" " +-\0" "  2\0" "  3\0" "  '\0" "  u\0" " q|\0" "  .\0"
3694913cc679Smrg    "  ,\0" "  1\0" "  2\0" " >>\0" "1/4\0" "1/2\0" "3/4\0" "  ?\0"
3695913cc679Smrg    " A`\0" " A'\0" " A^\0" " A~\0" " A:\0" " Ao\0" " AE\0" " C,\0"
3696913cc679Smrg    " E`\0" " E'\0" " E^\0" " E:\0" " I`\0" " I'\0" " I^\0" " I:\0"
3697913cc679Smrg    " D-\0" " N~\0" " O`\0" " O'\0" " O^\0" " O~\0" " O:\0" "  X\0"
3698913cc679Smrg    " O/\0" " U`\0" " U'\0" " U^\0" " U:\0" " Y'\0" "  P\0" "  B\0"
3699913cc679Smrg    " a`\0" " a'\0" " a^\0" " a~\0" " a:\0" " ao\0" " ae\0" " c,\0"
3700913cc679Smrg    " e`\0" " e'\0" " e^\0" " e:\0" " i`\0" " i'\0" " i^\0" " i:\0"
3701913cc679Smrg    "  d\0" " n~\0" " o`\0" " o'\0" " o^\0" " o~\0" " o:\0" " -:\0"
3702913cc679Smrg    " o/\0" " u`\0" " u'\0" " u^\0" " u:\0" " y'\0" "  P\0" " y:\0";
3703913cc679Smrg    int ch, dh;
3704913cc679Smrg    int class_p;
3705913cc679Smrg
3706913cc679Smrg    (void) xw;
3707913cc679Smrg
3708913cc679Smrg    printf("static int charClass[256] = {\n");
3709913cc679Smrg    for (ch = 0; ch < 256; ++ch) {
3710913cc679Smrg	const char *s = charnames + (ch * 4);
3711913cc679Smrg	if ((ch & 7) == 0)
3712913cc679Smrg	    printf("/*");
3713913cc679Smrg	printf(" %s ", s);
3714913cc679Smrg	if (((ch + 1) & 7) == 0) {
3715913cc679Smrg	    printf("*/\n  ");
3716913cc679Smrg	    for (dh = ch - 7; dh <= ch; ++dh) {
3717913cc679Smrg		printf(" %3d%s", CharacterClass(dh), dh == 255 ? "};" : ",");
3718913cc679Smrg	    }
3719913cc679Smrg	    printf("\n");
3720913cc679Smrg	}
3721913cc679Smrg    }
3722913cc679Smrg
3723913cc679Smrg    /* print the table as if it were the charClass resource */
3724913cc679Smrg    printf("\n");
3725913cc679Smrg    printf("The table is equivalent to this \"charClass\" resource:\n");
3726913cc679Smrg    class_p = CharacterClass(dh = 0);
3727913cc679Smrg    for (ch = 0; ch < 256; ++ch) {
3728913cc679Smrg	int class_c = CharacterClass(ch);
3729913cc679Smrg	if (class_c != class_p) {
3730913cc679Smrg	    if (show_cclass_range(dh, ch - 1)) {
3731913cc679Smrg		dh = ch;
3732913cc679Smrg		class_p = class_c;
3733913cc679Smrg	    }
3734913cc679Smrg	}
3735913cc679Smrg    }
3736913cc679Smrg    if (dh < 255) {
3737913cc679Smrg	show_cclass_range(dh, 255);
3738913cc679Smrg    }
3739913cc679Smrg
3740913cc679Smrg    if_OPT_WIDE_CHARS(TScreenOf(xw), {
3741913cc679Smrg	/* if this is a wide-character configuration, print all intervals */
3742913cc679Smrg	report_wide_char_class();
3743913cc679Smrg    });
3744913cc679Smrg}
3745913cc679Smrg#endif
3746913cc679Smrg
3747d522f475Smrg/*
3748d522f475Smrg * If the given column is past the end of text on the given row, bump to the
3749d522f475Smrg * beginning of the next line.
3750d522f475Smrg */
3751d522f475Smrgstatic Boolean
3752e0a2b6dfSmrgokPosition(TScreen *screen,
3753e0a2b6dfSmrg	   LineData **ld,
3754e0a2b6dfSmrg	   CELL *cell)
3755d522f475Smrg{
375620d2c4d2Smrg    Boolean result = True;
375720d2c4d2Smrg
37585104ee6eSmrg    assert(ld != NULL);
37595104ee6eSmrg    assert(*ld != NULL);
37605104ee6eSmrg
37615104ee6eSmrg    if (*ld == NULL) {
37625104ee6eSmrg	result = False;
37635104ee6eSmrg	TRACE(("okPosition LineData is null!\n"));
37645104ee6eSmrg    } else if (cell->row > screen->max_row) {
376520d2c4d2Smrg	result = False;
3766f2e35a3aSmrg	TRACE(("okPosition cell row %d > screen max %d\n", cell->row, screen->max_row));
376720d2c4d2Smrg    } else if (cell->col > (LastTextCol(screen, *ld, cell->row) + 1)) {
3768f2e35a3aSmrg	TRACE(("okPosition cell col %d > screen max %d\n", cell->col,
3769f2e35a3aSmrg	       (LastTextCol(screen, *ld, cell->row) + 1)));
377020d2c4d2Smrg	if (cell->row < screen->max_row) {
3771f2e35a3aSmrg	    TRACE(("okPosition cell row %d < screen max %d\n", cell->row, screen->max_row));
377220d2c4d2Smrg	    cell->col = 0;
377320d2c4d2Smrg	    *ld = GET_LINEDATA(screen, ++cell->row);
377420d2c4d2Smrg	    result = False;
377520d2c4d2Smrg	}
3776d522f475Smrg    }
377720d2c4d2Smrg    return result;
3778d522f475Smrg}
3779d522f475Smrg
3780d522f475Smrgstatic void
3781e0a2b6dfSmrgtrimLastLine(TScreen *screen,
3782e0a2b6dfSmrg	     LineData **ld,
3783e0a2b6dfSmrg	     CELL *last)
3784d522f475Smrg{
378520d2c4d2Smrg    if (screen->cutNewline && last->row < screen->max_row) {
3786d522f475Smrg	last->col = 0;
3787956cc18dSsnj	*ld = GET_LINEDATA(screen, ++last->row);
3788d522f475Smrg    } else {
3789956cc18dSsnj	last->col = LastTextCol(screen, *ld, last->row) + 1;
3790d522f475Smrg    }
3791d522f475Smrg}
3792d522f475Smrg
3793d522f475Smrg#if OPT_SELECT_REGEX
3794d522f475Smrg/*
3795d522f475Smrg * Returns the first row of a wrapped line.
3796d522f475Smrg */
3797d522f475Smrgstatic int
3798e0a2b6dfSmrgfirstRowOfLine(TScreen *screen, int row, Bool visible)
3799d522f475Smrg{
38005104ee6eSmrg    LineData *ld = NULL;
3801d522f475Smrg    int limit = visible ? 0 : -screen->savedlines;
3802d522f475Smrg
3803d522f475Smrg    while (row > limit &&
38045104ee6eSmrg	   (ld = GET_LINEDATA(screen, row - 1)) != NULL &&
3805956cc18dSsnj	   LineTstWrapped(ld)) {
3806d522f475Smrg	--row;
3807956cc18dSsnj    }
3808d522f475Smrg    return row;
3809d522f475Smrg}
3810d522f475Smrg
3811d522f475Smrg/*
3812d522f475Smrg * Returns the last row of a wrapped line.
3813d522f475Smrg */
3814d522f475Smrgstatic int
3815e0a2b6dfSmrglastRowOfLine(TScreen *screen, int row)
3816d522f475Smrg{
3817956cc18dSsnj    LineData *ld;
3818956cc18dSsnj
3819d522f475Smrg    while (row < screen->max_row &&
38205104ee6eSmrg	   (ld = GET_LINEDATA(screen, row)) != NULL &&
3821956cc18dSsnj	   LineTstWrapped(ld)) {
3822d522f475Smrg	++row;
3823956cc18dSsnj    }
3824d522f475Smrg    return row;
3825d522f475Smrg}
3826d522f475Smrg
3827d522f475Smrg/*
3828d522f475Smrg * Returns the number of cells on the range of rows.
3829d522f475Smrg */
3830d522f475Smrgstatic unsigned
3831e0a2b6dfSmrglengthOfLines(TScreen *screen, int firstRow, int lastRow)
3832d522f475Smrg{
3833d522f475Smrg    unsigned length = 0;
3834d522f475Smrg    int n;
3835d522f475Smrg
3836d522f475Smrg    for (n = firstRow; n <= lastRow; ++n) {
3837956cc18dSsnj	LineData *ld = GET_LINEDATA(screen, n);
3838956cc18dSsnj	int value = LastTextCol(screen, ld, n);
3839d522f475Smrg	if (value >= 0)
38402eaa94a1Schristos	    length += (unsigned) (value + 1);
3841d522f475Smrg    }
3842d522f475Smrg    return length;
3843d522f475Smrg}
3844d522f475Smrg
3845d522f475Smrg/*
3846d522f475Smrg * Make a copy of the wrapped-line which corresponds to the given row as a
3847d522f475Smrg * string of bytes.  Construct an index for the columns from the beginning of
3848d522f475Smrg * the line.
3849d522f475Smrg */
3850d522f475Smrgstatic char *
3851e0a2b6dfSmrgmake_indexed_text(TScreen *screen, int row, unsigned length, int *indexed)
3852d522f475Smrg{
38535104ee6eSmrg    Char *result = NULL;
385420d2c4d2Smrg    size_t need = (length + 1);
3855d522f475Smrg
3856d522f475Smrg    /*
3857d522f475Smrg     * Get a quick upper bound to the number of bytes needed, if the whole
3858d522f475Smrg     * string were UTF-8.
3859d522f475Smrg     */
3860d522f475Smrg    if_OPT_WIDE_CHARS(screen, {
3861956cc18dSsnj	need *= ((screen->lineExtra + 1) * 6);
3862d522f475Smrg    });
3863d522f475Smrg
38645104ee6eSmrg    if ((result = TypeCallocN(Char, need + 1)) != NULL) {
3865956cc18dSsnj	LineData *ld = GET_LINEDATA(screen, row);
3866d522f475Smrg	unsigned used = 0;
3867d522f475Smrg	Char *last = result;
3868d522f475Smrg
3869d522f475Smrg	do {
3870d522f475Smrg	    int col = 0;
3871956cc18dSsnj	    int limit = LastTextCol(screen, ld, row);
3872d522f475Smrg
3873d522f475Smrg	    while (col <= limit) {
3874d522f475Smrg		Char *next = last;
3875956cc18dSsnj		unsigned data = ld->charData[col];
3876d522f475Smrg
38770bd37d32Smrg		assert(col < (int) ld->lineSize);
3878d522f475Smrg		/* some internal points may not be drawn */
3879d522f475Smrg		if (data == 0)
3880d522f475Smrg		    data = ' ';
3881d522f475Smrg
3882d522f475Smrg		if_WIDE_OR_NARROW(screen, {
3883d522f475Smrg		    next = convertToUTF8(last, data);
3884d522f475Smrg		}
3885d522f475Smrg		, {
3886d522f475Smrg		    *next++ = CharOf(data);
3887d522f475Smrg		});
3888d522f475Smrg
3889d522f475Smrg		if_OPT_WIDE_CHARS(screen, {
3890956cc18dSsnj		    size_t off;
3891956cc18dSsnj		    for_each_combData(off, ld) {
3892956cc18dSsnj			data = ld->combData[off][col];
3893956cc18dSsnj			if (data == 0)
3894d522f475Smrg			    break;
3895d522f475Smrg			next = convertToUTF8(next, data);
3896d522f475Smrg		    }
3897d522f475Smrg		});
3898d522f475Smrg
389920d2c4d2Smrg		indexed[used] = (int) (last - result);
3900d522f475Smrg		*next = 0;
3901d522f475Smrg		/* TRACE(("index[%d.%d] %d:%s\n", row, used, indexed[used], last)); */
3902d522f475Smrg		last = next;
3903d522f475Smrg		++used;
3904d522f475Smrg		++col;
390520d2c4d2Smrg		indexed[used] = (int) (next - result);
3906d522f475Smrg	    }
3907d522f475Smrg	} while (used < length &&
3908956cc18dSsnj		 LineTstWrapped(ld) &&
39095104ee6eSmrg		 (ld = GET_LINEDATA(screen, ++row)) != NULL &&
3910956cc18dSsnj		 row < screen->max_row);
3911d522f475Smrg    }
3912d522f475Smrg    /* TRACE(("result:%s\n", result)); */
3913d522f475Smrg    return (char *) result;
3914d522f475Smrg}
3915d522f475Smrg
3916d522f475Smrg/*
3917d522f475Smrg * Find the column given an offset into the character string by using the
3918d522f475Smrg * index constructed in make_indexed_text().
3919d522f475Smrg */
3920d522f475Smrgstatic int
39215104ee6eSmrgindexToCol(const int *indexed, int len, int off)
3922d522f475Smrg{
3923d522f475Smrg    int col = 0;
3924d522f475Smrg    while (indexed[col] < len) {
3925d522f475Smrg	if (indexed[col] >= off)
3926d522f475Smrg	    break;
3927d522f475Smrg	++col;
3928d522f475Smrg    }
3929d522f475Smrg    return col;
3930d522f475Smrg}
3931d522f475Smrg
3932d522f475Smrg/*
3933d522f475Smrg * Given a row number, and a column offset from that (which may be wrapped),
3934d522f475Smrg * set the cell to the actual row/column values.
3935d522f475Smrg */
3936d522f475Smrgstatic void
3937e0a2b6dfSmrgcolumnToCell(TScreen *screen, int row, int col, CELL *cell)
3938d522f475Smrg{
3939d522f475Smrg    while (row < screen->max_row) {
394001037d57Smrg	CLineData *ld = GET_LINEDATA(screen, row);
3941956cc18dSsnj	int last = LastTextCol(screen, ld, row);
3942d522f475Smrg
3943d522f475Smrg	/* TRACE(("last(%d) = %d, have %d\n", row, last, col)); */
3944d522f475Smrg	if (col <= last) {
3945d522f475Smrg	    break;
3946d522f475Smrg	}
3947d522f475Smrg	/*
3948d522f475Smrg	 * Stop if the current row does not wrap (does not continue the current
3949d522f475Smrg	 * line).
3950d522f475Smrg	 */
3951956cc18dSsnj	if (!LineTstWrapped(ld)) {
3952d522f475Smrg	    col = last + 1;
3953d522f475Smrg	    break;
3954d522f475Smrg	}
3955d522f475Smrg	col -= (last + 1);
3956d522f475Smrg	++row;
3957d522f475Smrg    }
3958d522f475Smrg    if (col < 0)
3959d522f475Smrg	col = 0;
3960d522f475Smrg    cell->row = row;
3961d522f475Smrg    cell->col = col;
3962d522f475Smrg}
3963d522f475Smrg
3964d522f475Smrg/*
3965d522f475Smrg * Given a cell, find the corresponding column offset.
3966d522f475Smrg */
3967d522f475Smrgstatic int
3968e0a2b6dfSmrgcellToColumn(TScreen *screen, CELL *cell)
3969d522f475Smrg{
39705104ee6eSmrg    CLineData *ld = NULL;
3971d522f475Smrg    int col = cell->col;
3972d522f475Smrg    int row = firstRowOfLine(screen, cell->row, False);
3973d522f475Smrg    while (row < cell->row) {
3974956cc18dSsnj	ld = GET_LINEDATA(screen, row);
3975956cc18dSsnj	col += LastTextCol(screen, ld, row++);
3976d522f475Smrg    }
3977956cc18dSsnj#if OPT_DEC_CHRSET
39785104ee6eSmrg    if (ld == NULL)
3979956cc18dSsnj	ld = GET_LINEDATA(screen, row);
3980956cc18dSsnj    if (CSET_DOUBLE(GetLineDblCS(ld)))
3981956cc18dSsnj	col /= 2;
3982956cc18dSsnj#endif
3983d522f475Smrg    return col;
3984d522f475Smrg}
3985d522f475Smrg
3986d522f475Smrgstatic void
3987e0a2b6dfSmrgdo_select_regex(TScreen *screen, CELL *startc, CELL *endc)
3988d522f475Smrg{
3989956cc18dSsnj    LineData *ld = GET_LINEDATA(screen, startc->row);
3990d522f475Smrg    int inx = ((screen->numberOfClicks - 1) % screen->maxClicks);
3991d522f475Smrg    char *expr = screen->selectExpr[inx];
3992d522f475Smrg    regex_t preg;
3993d522f475Smrg    regmatch_t match;
3994d522f475Smrg
399501037d57Smrg    TRACE(("Select_REGEX[%d]:%s\n", inx, NonNull(expr)));
39965104ee6eSmrg    if (okPosition(screen, &ld, startc) && expr != NULL) {
3997d522f475Smrg	if (regcomp(&preg, expr, REG_EXTENDED) == 0) {
3998d522f475Smrg	    int firstRow = firstRowOfLine(screen, startc->row, True);
3999d522f475Smrg	    int lastRow = lastRowOfLine(screen, firstRow);
4000d522f475Smrg	    unsigned size = lengthOfLines(screen, firstRow, lastRow);
4001d522f475Smrg	    int actual = cellToColumn(screen, startc);
40022e4f8982Smrg	    int *indexed;
4003d522f475Smrg
4004d522f475Smrg	    TRACE(("regcomp ok rows %d..%d bytes %d\n",
4005d522f475Smrg		   firstRow, lastRow, size));
4006d522f475Smrg
40075104ee6eSmrg	    if ((indexed = TypeCallocN(int, size + 1)) != NULL) {
40082e4f8982Smrg		char *search;
4009d522f475Smrg		if ((search = make_indexed_text(screen,
4010d522f475Smrg						firstRow,
4011d522f475Smrg						size,
40125104ee6eSmrg						indexed)) != NULL) {
40132eaa94a1Schristos		    int len = (int) strlen(search);
4014d522f475Smrg		    int col;
401504b94745Smrg		    int offset;
4016d522f475Smrg		    int best_col = -1;
4017d522f475Smrg		    int best_len = -1;
4018d522f475Smrg
4019913cc679Smrg		    startc->row = 0;
4020913cc679Smrg		    startc->col = 0;
4021913cc679Smrg		    endc->row = 0;
4022913cc679Smrg		    endc->col = 0;
4023913cc679Smrg
402404b94745Smrg		    for (col = 0; (offset = indexed[col]) < len; ++col) {
4025d522f475Smrg			if (regexec(&preg,
402604b94745Smrg				    search + offset,
402704b94745Smrg				    (size_t) 1, &match,
402804b94745Smrg				    col ? REG_NOTBOL : 0) == 0) {
402904b94745Smrg			    int start_inx = (int) (match.rm_so + offset);
403004b94745Smrg			    int finis_inx = (int) (match.rm_eo + offset);
4031d522f475Smrg			    int start_col = indexToCol(indexed, len, start_inx);
4032d522f475Smrg			    int finis_col = indexToCol(indexed, len, finis_inx);
4033d522f475Smrg
4034d522f475Smrg			    if (start_col <= actual &&
4035913cc679Smrg				actual <= finis_col) {
4036d522f475Smrg				int test = finis_col - start_col;
4037d522f475Smrg				if (best_len < test) {
4038d522f475Smrg				    best_len = test;
4039d522f475Smrg				    best_col = start_col;
4040d522f475Smrg				    TRACE(("match column %d len %d\n",
4041d522f475Smrg					   best_col,
4042d522f475Smrg					   best_len));
4043d522f475Smrg				}
4044d522f475Smrg			    }
4045d522f475Smrg			}
4046d522f475Smrg		    }
4047d522f475Smrg		    if (best_col >= 0) {
4048d522f475Smrg			int best_nxt = best_col + best_len;
4049d522f475Smrg			columnToCell(screen, firstRow, best_col, startc);
4050d522f475Smrg			columnToCell(screen, firstRow, best_nxt, endc);
4051d522f475Smrg			TRACE(("search::%s\n", search));
4052d522f475Smrg			TRACE(("indexed:%d..%d -> %d..%d\n",
4053d522f475Smrg			       best_col, best_nxt,
4054d522f475Smrg			       indexed[best_col],
4055d522f475Smrg			       indexed[best_nxt]));
4056d522f475Smrg			TRACE(("matched:%d:%s\n",
405704b94745Smrg			       indexed[best_nxt] -
4058d522f475Smrg			       indexed[best_col],
4059956cc18dSsnj			       visibleChars((Char *) (search + indexed[best_col]),
406004b94745Smrg					    (unsigned) (indexed[best_nxt] -
4061d522f475Smrg							indexed[best_col]))));
4062d522f475Smrg		    }
4063d522f475Smrg		    free(search);
4064d522f475Smrg		}
4065d522f475Smrg		free(indexed);
4066956cc18dSsnj#if OPT_DEC_CHRSET
40675104ee6eSmrg		if ((ld = GET_LINEDATA(screen, startc->row)) != NULL) {
4068956cc18dSsnj		    if (CSET_DOUBLE(GetLineDblCS(ld)))
4069956cc18dSsnj			startc->col *= 2;
4070956cc18dSsnj		}
40715104ee6eSmrg		if ((ld = GET_LINEDATA(screen, endc->row)) != NULL) {
4072956cc18dSsnj		    if (CSET_DOUBLE(GetLineDblCS(ld)))
4073956cc18dSsnj			endc->col *= 2;
4074956cc18dSsnj		}
4075956cc18dSsnj#endif
4076d522f475Smrg	    }
4077d522f475Smrg	    regfree(&preg);
4078d522f475Smrg	}
4079d522f475Smrg    }
4080d522f475Smrg}
4081d522f475Smrg#endif /* OPT_SELECT_REGEX */
4082d522f475Smrg
4083956cc18dSsnj#define InitRow(name) \
4084956cc18dSsnj	ld.name = GET_LINEDATA(screen, screen->name.row)
4085956cc18dSsnj
4086956cc18dSsnj#define NextRow(name) \
4087956cc18dSsnj	ld.name = GET_LINEDATA(screen, ++screen->name.row)
4088956cc18dSsnj
4089956cc18dSsnj#define PrevRow(name) \
4090956cc18dSsnj	ld.name = GET_LINEDATA(screen, --screen->name.row)
4091956cc18dSsnj
409220d2c4d2Smrg#define MoreRows(name) \
409320d2c4d2Smrg	(screen->name.row < screen->max_row)
409420d2c4d2Smrg
4095956cc18dSsnj#define isPrevWrapped(name) \
4096956cc18dSsnj	(screen->name.row > 0 \
40975104ee6eSmrg	   && (ltmp = GET_LINEDATA(screen, screen->name.row - 1)) != NULL \
4098956cc18dSsnj	   && LineTstWrapped(ltmp))
4099956cc18dSsnj
4100d522f475Smrg/*
4101d522f475Smrg * sets startSel endSel
4102d522f475Smrg * ensuring that they have legal values
4103d522f475Smrg */
4104d522f475Smrgstatic void
4105d522f475SmrgComputeSelect(XtermWidget xw,
41065104ee6eSmrg	      const CELL *startc,
41075104ee6eSmrg	      const CELL *endc,
4108f2e35a3aSmrg	      Bool extend,
4109f2e35a3aSmrg	      Bool normal)
4110d522f475Smrg{
4111956cc18dSsnj    TScreen *screen = TScreenOf(xw);
4112956cc18dSsnj
4113d522f475Smrg    int cclass;
4114d522f475Smrg    CELL first = *startc;
4115d522f475Smrg    CELL last = *endc;
4116956cc18dSsnj    Boolean ignored = False;
4117956cc18dSsnj
4118956cc18dSsnj    struct {
4119956cc18dSsnj	LineData *startSel;
4120956cc18dSsnj	LineData *endSel;
4121956cc18dSsnj    } ld;
4122956cc18dSsnj    LineData *ltmp;
4123d522f475Smrg
4124d522f475Smrg    TRACE(("ComputeSelect(startRow=%d, startCol=%d, endRow=%d, endCol=%d, %sextend)\n",
4125d522f475Smrg	   first.row, first.col,
4126d522f475Smrg	   last.row, last.col,
4127d522f475Smrg	   extend ? "" : "no"));
4128d522f475Smrg
4129d522f475Smrg#if OPT_WIDE_CHARS
4130d522f475Smrg    if (first.col > 1
4131d522f475Smrg	&& isWideCell(first.row, first.col - 1)
4132d522f475Smrg	&& XTERM_CELL(first.row, first.col - 0) == HIDDEN_CHAR) {
413320d2c4d2Smrg	TRACE(("Adjusting start. Changing downwards from %i.\n", first.col));
4134d522f475Smrg	first.col -= 1;
4135d522f475Smrg	if (last.col == (first.col + 1))
4136d522f475Smrg	    last.col--;
4137d522f475Smrg    }
4138d522f475Smrg
4139d522f475Smrg    if (last.col > 1
4140d522f475Smrg	&& isWideCell(last.row, last.col - 1)
4141d522f475Smrg	&& XTERM_CELL(last.row, last.col) == HIDDEN_CHAR) {
4142d522f475Smrg	last.col += 1;
4143d522f475Smrg    }
4144d522f475Smrg#endif
4145d522f475Smrg
4146d522f475Smrg    if (Coordinate(screen, &first) <= Coordinate(screen, &last)) {
4147d522f475Smrg	screen->startSel = screen->startRaw = first;
4148d522f475Smrg	screen->endSel = screen->endRaw = last;
4149d522f475Smrg    } else {			/* Swap them */
4150d522f475Smrg	screen->startSel = screen->startRaw = last;
4151d522f475Smrg	screen->endSel = screen->endRaw = first;
4152d522f475Smrg    }
4153d522f475Smrg
4154956cc18dSsnj    InitRow(startSel);
4155956cc18dSsnj    InitRow(endSel);
4156956cc18dSsnj
4157d522f475Smrg    switch (screen->selectUnit) {
4158d522f475Smrg    case Select_CHAR:
41595104ee6eSmrg#if OPT_BLOCK_SELECT
41605104ee6eSmrg	/* Allow block selecting past EOL */
41615104ee6eSmrg	if (screen->blockSelecting)
41625104ee6eSmrg	    break;
41635104ee6eSmrg#endif
4164956cc18dSsnj	(void) okPosition(screen, &(ld.startSel), &(screen->startSel));
4165956cc18dSsnj	(void) okPosition(screen, &(ld.endSel), &(screen->endSel));
4166d522f475Smrg	break;
4167d522f475Smrg
4168d522f475Smrg    case Select_WORD:
4169d522f475Smrg	TRACE(("Select_WORD\n"));
4170956cc18dSsnj	if (okPosition(screen, &(ld.startSel), &(screen->startSel))) {
4171f2e35a3aSmrg	    CELL mark;
4172956cc18dSsnj	    cclass = CClassOf(startSel);
4173f2e35a3aSmrg	    TRACE(("...starting with class %d\n", cclass));
4174d522f475Smrg	    do {
4175f2e35a3aSmrg		mark = screen->startSel;
4176d522f475Smrg		--screen->startSel.col;
4177956cc18dSsnj		if (screen->startSel.col < 0
4178956cc18dSsnj		    && isPrevWrapped(startSel)) {
4179956cc18dSsnj		    PrevRow(startSel);
4180956cc18dSsnj		    screen->startSel.col = LastTextCol(screen, ld.startSel, screen->startSel.row);
4181d522f475Smrg		}
4182d522f475Smrg	    } while (screen->startSel.col >= 0
4183956cc18dSsnj		     && CClassSelects(startSel, cclass));
4184f2e35a3aSmrg	    if (normal)
4185f2e35a3aSmrg		++screen->startSel.col;
4186f2e35a3aSmrg	    else
4187f2e35a3aSmrg		screen->startSel = mark;
4188d522f475Smrg	}
4189d522f475Smrg#if OPT_WIDE_CHARS
4190f2e35a3aSmrg#define SkipHiddenCell(mark) \
4191f2e35a3aSmrg	if (mark.col && XTERM_CELL(mark.row, mark.col) == HIDDEN_CHAR) \
4192f2e35a3aSmrg	    mark.col++
4193f2e35a3aSmrg#else
4194f2e35a3aSmrg#define SkipHiddenCell(mark)	/* nothing */
4195d522f475Smrg#endif
4196f2e35a3aSmrg	SkipHiddenCell(screen->startSel);
4197f2e35a3aSmrg
4198f2e35a3aSmrg	if (!normal) {
4199f2e35a3aSmrg	    screen->endSel = screen->startSel;
4200f2e35a3aSmrg	    ld.endSel = ld.startSel;
4201f2e35a3aSmrg	}
4202d522f475Smrg
4203956cc18dSsnj	if (okPosition(screen, &(ld.endSel), &(screen->endSel))) {
42042e4f8982Smrg	    int length = LastTextCol(screen, ld.endSel, screen->endSel.row);
4205956cc18dSsnj	    cclass = CClassOf(endSel);
4206f2e35a3aSmrg	    TRACE(("...ending with class %d\n", cclass));
4207d522f475Smrg	    do {
4208d522f475Smrg		++screen->endSel.col;
4209d522f475Smrg		if (screen->endSel.col > length
4210956cc18dSsnj		    && LineTstWrapped(ld.endSel)) {
421120d2c4d2Smrg		    if (!MoreRows(endSel))
421220d2c4d2Smrg			break;
4213d522f475Smrg		    screen->endSel.col = 0;
4214956cc18dSsnj		    NextRow(endSel);
4215956cc18dSsnj		    length = LastTextCol(screen, ld.endSel, screen->endSel.row);
4216d522f475Smrg		}
4217d522f475Smrg	    } while (screen->endSel.col <= length
4218956cc18dSsnj		     && CClassSelects(endSel, cclass));
4219f2e35a3aSmrg	    if (normal
4220f2e35a3aSmrg		&& screen->endSel.col > length + 1
422120d2c4d2Smrg		&& MoreRows(endSel)) {
4222d522f475Smrg		screen->endSel.col = 0;
4223956cc18dSsnj		NextRow(endSel);
4224d522f475Smrg	    }
4225d522f475Smrg	}
4226f2e35a3aSmrg	SkipHiddenCell(screen->endSel);
4227d522f475Smrg
4228d522f475Smrg	screen->saveStartW = screen->startSel;
4229d522f475Smrg	break;
4230d522f475Smrg
4231d522f475Smrg    case Select_LINE:
4232d522f475Smrg	TRACE(("Select_LINE\n"));
423320d2c4d2Smrg	while (LineTstWrapped(ld.endSel)
423420d2c4d2Smrg	       && MoreRows(endSel)) {
4235956cc18dSsnj	    NextRow(endSel);
4236d522f475Smrg	}
4237d522f475Smrg	if (screen->cutToBeginningOfLine
4238d522f475Smrg	    || screen->startSel.row < screen->saveStartW.row) {
4239d522f475Smrg	    screen->startSel.col = 0;
4240956cc18dSsnj	    while (isPrevWrapped(startSel)) {
4241956cc18dSsnj		PrevRow(startSel);
4242d522f475Smrg	    }
4243d522f475Smrg	} else if (!extend) {
4244d522f475Smrg	    if ((first.row < screen->saveStartW.row)
4245d522f475Smrg		|| (isSameRow(&first, &(screen->saveStartW))
4246d522f475Smrg		    && first.col < screen->saveStartW.col)) {
4247d522f475Smrg		screen->startSel.col = 0;
4248956cc18dSsnj		while (isPrevWrapped(startSel)) {
4249956cc18dSsnj		    PrevRow(startSel);
4250d522f475Smrg		}
4251d522f475Smrg	    } else {
4252d522f475Smrg		screen->startSel = screen->saveStartW;
4253d522f475Smrg	    }
4254d522f475Smrg	}
4255956cc18dSsnj	trimLastLine(screen, &(ld.endSel), &(screen->endSel));
4256d522f475Smrg	break;
4257d522f475Smrg
4258d522f475Smrg    case Select_GROUP:		/* paragraph */
4259d522f475Smrg	TRACE(("Select_GROUP\n"));
4260956cc18dSsnj	if (okPosition(screen, &(ld.startSel), &(screen->startSel))) {
4261d522f475Smrg	    /* scan backward for beginning of group */
4262d522f475Smrg	    while (screen->startSel.row > 0 &&
4263956cc18dSsnj		   (LastTextCol(screen, ld.startSel, screen->startSel.row -
4264956cc18dSsnj				1) > 0 ||
4265956cc18dSsnj		    isPrevWrapped(startSel))) {
4266956cc18dSsnj		PrevRow(startSel);
4267d522f475Smrg	    }
4268d522f475Smrg	    screen->startSel.col = 0;
4269d522f475Smrg	    /* scan forward for end of group */
427020d2c4d2Smrg	    while (MoreRows(endSel) &&
4271956cc18dSsnj		   (LastTextCol(screen, ld.endSel, screen->endSel.row + 1) >
4272956cc18dSsnj		    0 ||
4273956cc18dSsnj		    LineTstWrapped(ld.endSel))) {
4274956cc18dSsnj		NextRow(endSel);
4275d522f475Smrg	    }
4276956cc18dSsnj	    trimLastLine(screen, &(ld.endSel), &(screen->endSel));
4277d522f475Smrg	}
4278d522f475Smrg	break;
4279d522f475Smrg
4280d522f475Smrg    case Select_PAGE:		/* everything one can see */
4281d522f475Smrg	TRACE(("Select_PAGE\n"));
4282d522f475Smrg	screen->startSel.row = 0;
4283d522f475Smrg	screen->startSel.col = 0;
428420d2c4d2Smrg	screen->endSel.row = MaxRows(screen);
4285d522f475Smrg	screen->endSel.col = 0;
4286d522f475Smrg	break;
4287d522f475Smrg
4288d522f475Smrg    case Select_ALL:		/* counts scrollback if in normal screen */
4289d522f475Smrg	TRACE(("Select_ALL\n"));
4290d522f475Smrg	screen->startSel.row = -screen->savedlines;
4291d522f475Smrg	screen->startSel.col = 0;
429220d2c4d2Smrg	screen->endSel.row = MaxRows(screen);
4293d522f475Smrg	screen->endSel.col = 0;
4294d522f475Smrg	break;
4295d522f475Smrg
4296d522f475Smrg#if OPT_SELECT_REGEX
4297d522f475Smrg    case Select_REGEX:
4298d522f475Smrg	do_select_regex(screen, &(screen->startSel), &(screen->endSel));
4299d522f475Smrg	break;
4300d522f475Smrg#endif
4301d522f475Smrg
4302d522f475Smrg    case NSELECTUNITS:		/* always ignore */
4303956cc18dSsnj	ignored = True;
4304956cc18dSsnj	break;
4305d522f475Smrg    }
4306d522f475Smrg
4307956cc18dSsnj    if (!ignored) {
4308956cc18dSsnj	/* check boundaries */
4309956cc18dSsnj	ScrollSelection(screen, 0, False);
4310956cc18dSsnj	TrackText(xw, &(screen->startSel), &(screen->endSel));
4311956cc18dSsnj    }
4312d522f475Smrg
4313d522f475Smrg    return;
4314d522f475Smrg}
4315d522f475Smrg
4316d522f475Smrg/* Guaranteed (first.row, first.col) <= (last.row, last.col) */
4317d522f475Smrgstatic void
4318d522f475SmrgTrackText(XtermWidget xw,
4319e0a2b6dfSmrg	  const CELL *firstp,
4320e0a2b6dfSmrg	  const CELL *lastp)
4321d522f475Smrg{
4322956cc18dSsnj    TScreen *screen = TScreenOf(xw);
4323d522f475Smrg    int from, to;
4324d522f475Smrg    CELL old_start, old_end;
4325d522f475Smrg    CELL first = *firstp;
4326d522f475Smrg    CELL last = *lastp;
4327d522f475Smrg
4328d522f475Smrg    TRACE(("TrackText(first=%d,%d, last=%d,%d)\n",
4329d522f475Smrg	   first.row, first.col, last.row, last.col));
4330d522f475Smrg
4331d522f475Smrg    old_start = screen->startH;
4332d522f475Smrg    old_end = screen->endH;
43330bd37d32Smrg    TRACE(("...previous(first=%d,%d, last=%d,%d)\n",
43340bd37d32Smrg	   old_start.row, old_start.col,
43350bd37d32Smrg	   old_end.row, old_end.col));
4336d522f475Smrg    if (isSameCELL(&first, &old_start) &&
43370bd37d32Smrg	isSameCELL(&last, &old_end)) {
4338d522f475Smrg	return;
43390bd37d32Smrg    }
43400bd37d32Smrg
4341d522f475Smrg    screen->startH = first;
4342d522f475Smrg    screen->endH = last;
4343d522f475Smrg    from = Coordinate(screen, &screen->startH);
4344d522f475Smrg    to = Coordinate(screen, &screen->endH);
43455104ee6eSmrg    if (to <= screen->startHCoord || from > screen->endHCoord
43465104ee6eSmrg#if OPT_BLOCK_SELECT
43475104ee6eSmrg	|| screen->blockSelecting
43485104ee6eSmrg	|| screen->blockSelecting != screen->lastSelectWasBlock
43495104ee6eSmrg#endif
43505104ee6eSmrg	) {
43515104ee6eSmrg#if OPT_BLOCK_SELECT
43525104ee6eSmrg	/* Either no overlap whatsoever between old and new hilite,
43535104ee6eSmrg	   or we're in block select mode (or just came from it),
43545104ee6eSmrg	   in which case there is no optimization possible. */
43555104ee6eSmrg	if (screen->lastSelectWasBlock) {
43565104ee6eSmrg	    /* If we just came from block select mode, we need to
43575104ee6eSmrg	       unhighlight more aggressively. */
43585104ee6eSmrg	    old_start.col = 0;
43595104ee6eSmrg	    old_end.col = MaxCols(screen);
43605104ee6eSmrg	}
43615104ee6eSmrg#endif
4362d522f475Smrg	ReHiliteText(xw, &old_start, &old_end);
4363d522f475Smrg	ReHiliteText(xw, &first, &last);
4364d522f475Smrg    } else {
4365d522f475Smrg	if (from < screen->startHCoord) {
4366d522f475Smrg	    /* Extend left end */
4367d522f475Smrg	    ReHiliteText(xw, &first, &old_start);
4368d522f475Smrg	} else if (from > screen->startHCoord) {
4369d522f475Smrg	    /* Shorten left end */
4370d522f475Smrg	    ReHiliteText(xw, &old_start, &first);
4371d522f475Smrg	}
4372d522f475Smrg	if (to > screen->endHCoord) {
4373d522f475Smrg	    /* Extend right end */
4374d522f475Smrg	    ReHiliteText(xw, &old_end, &last);
4375d522f475Smrg	} else if (to < screen->endHCoord) {
4376d522f475Smrg	    /* Shorten right end */
4377d522f475Smrg	    ReHiliteText(xw, &last, &old_end);
4378d522f475Smrg	}
4379d522f475Smrg    }
4380d522f475Smrg    screen->startHCoord = from;
4381d522f475Smrg    screen->endHCoord = to;
4382d522f475Smrg}
4383d522f475Smrg
4384f2e35a3aSmrgstatic void
4385f2e35a3aSmrgUnHiliteText(XtermWidget xw)
4386f2e35a3aSmrg{
4387f2e35a3aSmrg    TrackText(xw, &zeroCELL, &zeroCELL);
4388f2e35a3aSmrg}
4389f2e35a3aSmrg
4390d522f475Smrg/* Guaranteed that (first->row, first->col) <= (last->row, last->col) */
4391d522f475Smrgstatic void
4392d522f475SmrgReHiliteText(XtermWidget xw,
43935104ee6eSmrg	     const CELL *firstp,
43945104ee6eSmrg	     const CELL *lastp)
4395d522f475Smrg{
4396956cc18dSsnj    TScreen *screen = TScreenOf(xw);
4397d522f475Smrg    CELL first = *firstp;
4398d522f475Smrg    CELL last = *lastp;
4399d522f475Smrg
4400d522f475Smrg    TRACE(("ReHiliteText from %d.%d to %d.%d\n",
4401d522f475Smrg	   first.row, first.col, last.row, last.col));
4402d522f475Smrg
4403d522f475Smrg    if (first.row < 0)
4404d522f475Smrg	first.row = first.col = 0;
4405d522f475Smrg    else if (first.row > screen->max_row)
4406d522f475Smrg	return;			/* nothing to do, since last.row >= first.row */
4407d522f475Smrg
4408d522f475Smrg    if (last.row < 0)
4409d522f475Smrg	return;			/* nothing to do, since first.row <= last.row */
4410d522f475Smrg    else if (last.row > screen->max_row) {
4411d522f475Smrg	last.row = screen->max_row;
4412d522f475Smrg	last.col = MaxCols(screen);
4413d522f475Smrg    }
4414d522f475Smrg    if (isSameCELL(&first, &last))
4415d522f475Smrg	return;
4416d522f475Smrg
44175104ee6eSmrg#if OPT_BLOCK_SELECT
44185104ee6eSmrg    if (screen->blockSelecting) {
44195104ee6eSmrg	/* In block select mode, there is no special case for the first or
44205104ee6eSmrg	   last rows. Also, unlike normal selections, there can be
44215104ee6eSmrg	   unselected text on both sides of the selection per row, so we
44225104ee6eSmrg	   refresh all columns. */
44235104ee6eSmrg	int row;
44245104ee6eSmrg	for (row = first.row; row <= last.row; row++) {
44255104ee6eSmrg	    ScrnRefresh(xw, row, 0, 1, MaxCols(screen), True);
44265104ee6eSmrg	}
44275104ee6eSmrg    } else
44285104ee6eSmrg#endif
4429d522f475Smrg    if (!isSameRow(&first, &last)) {	/* do multiple rows */
44302e4f8982Smrg	int i;
4431d522f475Smrg	if ((i = screen->max_col - first.col + 1) > 0) {	/* first row */
4432d522f475Smrg	    ScrnRefresh(xw, first.row, first.col, 1, i, True);
4433d522f475Smrg	}
4434d522f475Smrg	if ((i = last.row - first.row - 1) > 0) {	/* middle rows */
4435d522f475Smrg	    ScrnRefresh(xw, first.row + 1, 0, i, MaxCols(screen), True);
4436d522f475Smrg	}
4437d522f475Smrg	if (last.col > 0 && last.row <= screen->max_row) {	/* last row */
4438d522f475Smrg	    ScrnRefresh(xw, last.row, 0, 1, last.col, True);
4439d522f475Smrg	}
4440d522f475Smrg    } else {			/* do single row */
4441d522f475Smrg	ScrnRefresh(xw, first.row, first.col, 1, last.col - first.col, True);
4442d522f475Smrg    }
4443d522f475Smrg}
4444d522f475Smrg
4445d522f475Smrg/*
4446913cc679Smrg * Guaranteed that (cellc->row, cellc->col) <= (cell->row, cell->col),
4447913cc679Smrg * and that both points are valid
4448d522f475Smrg * (may have cell->row = screen->max_row+1, cell->col = 0).
4449d522f475Smrg */
4450d522f475Smrgstatic void
4451d522f475SmrgSaltTextAway(XtermWidget xw,
4452f2e35a3aSmrg	     int which,
44535104ee6eSmrg	     const CELL *cellc,
44545104ee6eSmrg	     const CELL *cell)
4455d522f475Smrg{
4456956cc18dSsnj    TScreen *screen = TScreenOf(xw);
4457f2e35a3aSmrg    SelectedCells *scp;
4458f2e35a3aSmrg    int i;
4459d522f475Smrg    int eol;
4460f2e35a3aSmrg    int need = 0;
4461f2e35a3aSmrg    size_t have = 0;
4462d522f475Smrg    Char *line;
4463d522f475Smrg    Char *lp;
4464d522f475Smrg    CELL first = *cellc;
4465d522f475Smrg    CELL last = *cell;
4466d522f475Smrg
4467f2e35a3aSmrg    if (which < 0 || which >= MAX_SELECTIONS) {
4468f2e35a3aSmrg	TRACE(("SaltTextAway - which selection?\n"));
4469f2e35a3aSmrg	return;
4470f2e35a3aSmrg    }
4471f2e35a3aSmrg    scp = &(screen->selected_cells[which]);
4472f2e35a3aSmrg
4473f2e35a3aSmrg    TRACE(("SaltTextAway which=%d, first=%d,%d, last=%d,%d\n",
4474f2e35a3aSmrg	   which, first.row, first.col, last.row, last.col));
4475f2e35a3aSmrg
4476d522f475Smrg    if (isSameRow(&first, &last) && first.col > last.col) {
44772e4f8982Smrg	int tmp;
4478956cc18dSsnj	EXCHANGE(first.col, last.col, tmp);
4479d522f475Smrg    }
4480d522f475Smrg
4481d522f475Smrg    --last.col;
4482d522f475Smrg    /* first we need to know how long the string is before we can save it */
4483d522f475Smrg
4484d522f475Smrg    if (isSameRow(&last, &first)) {
4485f2e35a3aSmrg	need = Length(screen, first.row, first.col, last.col);
4486d522f475Smrg    } else {			/* two cases, cut is on same line, cut spans multiple lines */
4487f2e35a3aSmrg	need += Length(screen, first.row, first.col, screen->max_col) + 1;
4488d522f475Smrg	for (i = first.row + 1; i < last.row; i++)
4489f2e35a3aSmrg	    need += Length(screen, i, 0, screen->max_col) + 1;
4490d522f475Smrg	if (last.col >= 0)
4491f2e35a3aSmrg	    need += Length(screen, last.row, 0, last.col);
4492d522f475Smrg    }
4493d522f475Smrg
4494d522f475Smrg    /* UTF-8 may require more space */
4495d522f475Smrg    if_OPT_WIDE_CHARS(screen, {
4496f2e35a3aSmrg	if (need > 0) {
4497f2e35a3aSmrg	    if (screen->max_combining > 0)
4498f2e35a3aSmrg		need += screen->max_combining;
4499f2e35a3aSmrg	    need *= 6;
4500f2e35a3aSmrg	}
4501d522f475Smrg    });
4502d522f475Smrg
4503d522f475Smrg    /* now get some memory to save it in */
4504f2e35a3aSmrg    if (need < 0)
4505f2e35a3aSmrg	return;
4506d522f475Smrg
4507f2e35a3aSmrg    if (scp->data_limit <= (unsigned) need) {
45085104ee6eSmrg	if ((line = (Char *) malloc((size_t) need + 1)) == NULL)
4509d522f475Smrg	    SysError(ERROR_BMALLOC2);
4510f2e35a3aSmrg	free(scp->data_buffer);
4511f2e35a3aSmrg	scp->data_buffer = line;
4512f2e35a3aSmrg	scp->data_limit = (size_t) (need + 1);
4513d522f475Smrg    } else {
4514f2e35a3aSmrg	line = scp->data_buffer;
4515d522f475Smrg    }
4516d522f475Smrg
45175104ee6eSmrg    if (line == NULL)
4518d522f475Smrg	return;
4519d522f475Smrg
4520f2e35a3aSmrg    line[need] = '\0';		/* make sure it is null terminated */
4521d522f475Smrg    lp = line;			/* lp points to where to save the text */
4522d522f475Smrg    if (isSameRow(&last, &first)) {
4523d522f475Smrg	lp = SaveText(screen, last.row, first.col, last.col, lp, &eol);
45245104ee6eSmrg    }
45255104ee6eSmrg#if OPT_BLOCK_SELECT
45265104ee6eSmrg    else if (screen->blockSelecting) {
45275104ee6eSmrg	/* In block select mode, find the left most column of the block.
45285104ee6eSmrg	   This can be from either the start or the end of the selection. */
45295104ee6eSmrg	int blockFirst, blockLast;
45305104ee6eSmrg	if (first.col < last.col) {
45315104ee6eSmrg	    blockFirst = first.col;
45325104ee6eSmrg	    blockLast = last.col;
45335104ee6eSmrg	} else {
45345104ee6eSmrg	    blockFirst = last.col;
45355104ee6eSmrg	    blockLast = first.col;
45365104ee6eSmrg	}
45375104ee6eSmrg	for (i = first.row; i <= last.row; i++) {
45385104ee6eSmrg	    lp = SaveText(screen, i, blockFirst, blockLast, lp, &eol);
45395104ee6eSmrg	    if (i < last.row || eol)
45405104ee6eSmrg		*lp++ = '\n';
45415104ee6eSmrg	}
45425104ee6eSmrg    }
45435104ee6eSmrg#endif
45445104ee6eSmrg    else {
4545d522f475Smrg	lp = SaveText(screen, first.row, first.col, screen->max_col, lp, &eol);
4546d522f475Smrg	if (eol)
4547d522f475Smrg	    *lp++ = '\n';	/* put in newline at end of line */
4548d522f475Smrg	for (i = first.row + 1; i < last.row; i++) {
4549d522f475Smrg	    lp = SaveText(screen, i, 0, screen->max_col, lp, &eol);
4550d522f475Smrg	    if (eol)
4551d522f475Smrg		*lp++ = '\n';
4552d522f475Smrg	}
4553d522f475Smrg	if (last.col >= 0)
4554d522f475Smrg	    lp = SaveText(screen, last.row, 0, last.col, lp, &eol);
4555d522f475Smrg    }
4556d522f475Smrg    *lp = '\0';			/* make sure we have end marked */
4557d522f475Smrg
4558f2e35a3aSmrg    have = (size_t) (lp - line);
4559f2e35a3aSmrg    /*
4560f2e35a3aSmrg     * Scanning the buffer twice is unnecessary.  Discard unwanted memory if
4561f2e35a3aSmrg     * the estimate is too-far off.
4562f2e35a3aSmrg     */
4563f2e35a3aSmrg    if ((have * 2) < (size_t) need) {
4564f2e35a3aSmrg	Char *next;
4565f2e35a3aSmrg	scp->data_limit = have + 1;
4566f2e35a3aSmrg	next = realloc(line, scp->data_limit);
4567f2e35a3aSmrg	if (next == NULL) {
4568f2e35a3aSmrg	    free(line);
4569f2e35a3aSmrg	    scp->data_length = 0;
4570f2e35a3aSmrg	    scp->data_limit = 0;
4571f2e35a3aSmrg	}
4572f2e35a3aSmrg	scp->data_buffer = next;
4573f2e35a3aSmrg    }
4574f2e35a3aSmrg    scp->data_length = have;
4575d522f475Smrg
4576f2e35a3aSmrg    TRACE(("Salted TEXT:%u:%s\n", (unsigned) have,
4577f2e35a3aSmrg	   visibleChars(scp->data_buffer, (unsigned) have)));
4578d522f475Smrg}
4579d522f475Smrg
4580d522f475Smrg#if OPT_PASTE64
4581d522f475Smrgvoid
4582f2e35a3aSmrgClearSelectionBuffer(TScreen *screen, String selection)
4583d522f475Smrg{
4584f2e35a3aSmrg    int which = TargetToSelection(screen, selection);
4585f2e35a3aSmrg    SelectedCells *scp = &(screen->selected_cells[okSelectionCode(which)]);
4586f2e35a3aSmrg    FreeAndNull(scp->data_buffer);
4587f2e35a3aSmrg    scp->data_limit = 0;
4588f2e35a3aSmrg    scp->data_length = 0;
4589d522f475Smrg    screen->base64_count = 0;
4590d522f475Smrg}
4591d522f475Smrg
4592d522f475Smrgstatic void
4593f2e35a3aSmrgAppendStrToSelectionBuffer(SelectedCells * scp, Char *text, size_t len)
4594d522f475Smrg{
4595d522f475Smrg    if (len != 0) {
4596f2e35a3aSmrg	size_t j = (scp->data_length + len);
4597f2e35a3aSmrg	size_t k = j + (j >> 2) + 80;
4598f2e35a3aSmrg	if (j + 1 >= scp->data_limit) {
4599f2e35a3aSmrg	    Char *line;
4600f2e35a3aSmrg	    if (!scp->data_length) {
4601f2e35a3aSmrg		line = (Char *) malloc(k);
4602d522f475Smrg	    } else {
4603f2e35a3aSmrg		line = (Char *) realloc(scp->data_buffer, k);
4604d522f475Smrg	    }
46055104ee6eSmrg	    if (line == NULL)
4606f2e35a3aSmrg		SysError(ERROR_BMALLOC2);
4607f2e35a3aSmrg	    scp->data_buffer = line;
4608f2e35a3aSmrg	    scp->data_limit = k;
4609d522f475Smrg	}
46105104ee6eSmrg	if (scp->data_buffer != NULL) {
4611f2e35a3aSmrg	    memcpy(scp->data_buffer + scp->data_length, text, len);
4612f2e35a3aSmrg	    scp->data_length += len;
4613f2e35a3aSmrg	    scp->data_buffer[scp->data_length] = 0;
461420d2c4d2Smrg	}
4615d522f475Smrg    }
4616d522f475Smrg}
4617d522f475Smrg
4618d522f475Smrgvoid
4619f2e35a3aSmrgAppendToSelectionBuffer(TScreen *screen, unsigned c, String selection)
4620d522f475Smrg{
4621f2e35a3aSmrg    int which = TargetToSelection(screen, selection);
4622f2e35a3aSmrg    SelectedCells *scp = &(screen->selected_cells[okSelectionCode(which)]);
46232eaa94a1Schristos    unsigned six;
4624d522f475Smrg    Char ch;
4625d522f475Smrg
4626d522f475Smrg    /* Decode base64 character */
4627d522f475Smrg    if (c >= 'A' && c <= 'Z')
4628d522f475Smrg	six = c - 'A';
4629d522f475Smrg    else if (c >= 'a' && c <= 'z')
4630d522f475Smrg	six = c - 'a' + 26;
4631d522f475Smrg    else if (c >= '0' && c <= '9')
4632d522f475Smrg	six = c - '0' + 52;
4633d522f475Smrg    else if (c == '+')
4634d522f475Smrg	six = 62;
4635d522f475Smrg    else if (c == '/')
4636d522f475Smrg	six = 63;
4637d522f475Smrg    else
4638d522f475Smrg	return;
4639d522f475Smrg
4640d522f475Smrg    /* Accumulate bytes */
4641d522f475Smrg    switch (screen->base64_count) {
4642d522f475Smrg    case 0:
4643d522f475Smrg	screen->base64_accu = six;
4644d522f475Smrg	screen->base64_count = 6;
4645d522f475Smrg	break;
4646d522f475Smrg
4647d522f475Smrg    case 2:
46482eaa94a1Schristos	ch = CharOf((screen->base64_accu << 6) + six);
4649d522f475Smrg	screen->base64_count = 0;
4650f2e35a3aSmrg	AppendStrToSelectionBuffer(scp, &ch, (size_t) 1);
4651d522f475Smrg	break;
4652d522f475Smrg
4653d522f475Smrg    case 4:
46542eaa94a1Schristos	ch = CharOf((screen->base64_accu << 4) + (six >> 2));
4655d522f475Smrg	screen->base64_accu = (six & 0x3);
4656d522f475Smrg	screen->base64_count = 2;
4657f2e35a3aSmrg	AppendStrToSelectionBuffer(scp, &ch, (size_t) 1);
4658d522f475Smrg	break;
4659d522f475Smrg
4660d522f475Smrg    case 6:
46612eaa94a1Schristos	ch = CharOf((screen->base64_accu << 2) + (six >> 4));
4662d522f475Smrg	screen->base64_accu = (six & 0xF);
4663d522f475Smrg	screen->base64_count = 4;
4664f2e35a3aSmrg	AppendStrToSelectionBuffer(scp, &ch, (size_t) 1);
4665d522f475Smrg	break;
4666d522f475Smrg    }
4667d522f475Smrg}
4668d522f475Smrg
4669d522f475Smrgvoid
4670e0a2b6dfSmrgCompleteSelection(XtermWidget xw, String *args, Cardinal len)
4671d522f475Smrg{
4672956cc18dSsnj    TScreen *screen = TScreenOf(xw);
4673956cc18dSsnj
4674956cc18dSsnj    screen->base64_count = 0;
4675956cc18dSsnj    screen->base64_accu = 0;
4676d522f475Smrg    _OwnSelection(xw, args, len);
4677d522f475Smrg}
4678d522f475Smrg#endif /* OPT_PASTE64 */
4679d522f475Smrg
4680d522f475Smrgstatic Bool
4681d522f475Smrg_ConvertSelectionHelper(Widget w,
4682f2e35a3aSmrg			SelectedCells * scp,
4683894e0ac8Smrg			Atom *type,
4684d522f475Smrg			XtPointer *value,
4685d522f475Smrg			unsigned long *length,
4686d522f475Smrg			int *format,
4687d522f475Smrg			int (*conversion_function) (Display *,
4688d522f475Smrg						    char **, int,
4689d522f475Smrg						    XICCEncodingStyle,
4690d522f475Smrg						    XTextProperty *),
4691d522f475Smrg			XICCEncodingStyle conversion_style)
4692d522f475Smrg{
46935104ee6eSmrg    *value = NULL;
469401037d57Smrg    *length = 0;
469501037d57Smrg    *type = 0;
469601037d57Smrg    *format = 0;
469701037d57Smrg
46985104ee6eSmrg    if (getXtermWidget(w) != NULL) {
4699d522f475Smrg	Display *dpy = XtDisplay(w);
4700d522f475Smrg	XTextProperty textprop;
470101037d57Smrg	int out_n = 0;
47025104ee6eSmrg	char *result = NULL;
4703f2e35a3aSmrg	char *the_data = (char *) scp->data_buffer;
470401037d57Smrg	char *the_next;
4705f2e35a3aSmrg	unsigned long remaining = scp->data_length;
470601037d57Smrg
470701037d57Smrg	TRACE(("converting %ld:'%s'\n",
4708f2e35a3aSmrg	       (long) scp->data_length,
4709f2e35a3aSmrg	       visibleChars(scp->data_buffer, (unsigned) scp->data_length)));
471001037d57Smrg	/*
471101037d57Smrg	 * For most selections, we can convert in one pass.  It is possible
471201037d57Smrg	 * that some applications contain embedded nulls, e.g., using xterm's
471301037d57Smrg	 * paste64 feature.  For those cases, we will build up the result in
471401037d57Smrg	 * parts.
471501037d57Smrg	 */
47165104ee6eSmrg	if (memchr(the_data, 0, scp->data_length) != NULL) {
471701037d57Smrg	    TRACE(("selection contains embedded nulls\n"));
4718f2e35a3aSmrg	    result = calloc(scp->data_length + 1, sizeof(char));
471901037d57Smrg	}
4720d522f475Smrg
472101037d57Smrg      next_try:
472201037d57Smrg	memset(&textprop, 0, sizeof(textprop));
4723d522f475Smrg	if (conversion_function(dpy, &the_data, 1,
4724d522f475Smrg				conversion_style,
4725d522f475Smrg				&textprop) >= Success) {
47265104ee6eSmrg	    if ((result != NULL)
47275104ee6eSmrg		&& (textprop.value != NULL)
472801037d57Smrg		&& (textprop.format == 8)) {
472901037d57Smrg		char *text_values = (char *) textprop.value;
473001037d57Smrg		unsigned long in_n;
473101037d57Smrg
473201037d57Smrg		if (out_n == 0) {
473301037d57Smrg		    *value = result;
473401037d57Smrg		    *type = textprop.encoding;
473501037d57Smrg		    *format = textprop.format;
473601037d57Smrg		}
473701037d57Smrg		for (in_n = 0; in_n < textprop.nitems; ++in_n) {
473801037d57Smrg		    result[out_n++] = text_values[in_n];
473901037d57Smrg		}
474001037d57Smrg		*length += textprop.nitems;
47415104ee6eSmrg		if ((the_next = memchr(the_data, 0, remaining)) != NULL) {
474201037d57Smrg		    unsigned long this_was = (unsigned long) (the_next - the_data);
474301037d57Smrg		    this_was++;
474401037d57Smrg		    the_data += this_was;
474501037d57Smrg		    remaining -= this_was;
474601037d57Smrg		    result[out_n++] = 0;
474701037d57Smrg		    *length += 1;
474801037d57Smrg		    if (remaining)
474901037d57Smrg			goto next_try;
475001037d57Smrg		}
475101037d57Smrg		return True;
475201037d57Smrg	    } else {
475301037d57Smrg		free(result);
475401037d57Smrg		*value = (XtPointer) textprop.value;
475501037d57Smrg		*length = textprop.nitems;
475601037d57Smrg		*type = textprop.encoding;
475701037d57Smrg		*format = textprop.format;
475801037d57Smrg		return True;
475901037d57Smrg	    }
4760d522f475Smrg	}
476101037d57Smrg	free(result);
4762d522f475Smrg    }
4763d522f475Smrg    return False;
4764d522f475Smrg}
4765d522f475Smrg
47662eaa94a1Schristosstatic Boolean
47672eaa94a1SchristosSaveConvertedLength(XtPointer *target, unsigned long source)
47682eaa94a1Schristos{
47692eaa94a1Schristos    Boolean result = False;
47702eaa94a1Schristos
47712eaa94a1Schristos    *target = XtMalloc(4);
47725104ee6eSmrg    if (*target != NULL) {
47732eaa94a1Schristos	result = True;
47742eaa94a1Schristos	if (sizeof(unsigned long) == 4) {
47752eaa94a1Schristos	    *(unsigned long *) *target = source;
47762eaa94a1Schristos	} else if (sizeof(unsigned) == 4) {
477720d2c4d2Smrg	    *(unsigned *) *target = (unsigned) source;
47782eaa94a1Schristos	} else if (sizeof(unsigned short) == 4) {
47792eaa94a1Schristos	    *(unsigned short *) *target = (unsigned short) source;
47802eaa94a1Schristos	} else {
47812eaa94a1Schristos	    /* FIXME - does this depend on byte-order? */
47822eaa94a1Schristos	    unsigned long temp = source;
478320d2c4d2Smrg	    memcpy((char *) *target,
478420d2c4d2Smrg		   ((char *) &temp) + sizeof(temp) - 4,
478520d2c4d2Smrg		   (size_t) 4);
47862eaa94a1Schristos	}
47872eaa94a1Schristos    }
47882eaa94a1Schristos    return result;
47892eaa94a1Schristos}
47902eaa94a1Schristos
4791f2e35a3aSmrg#define keepClipboard(d,atom) ((screen->keepClipboard) && \
4792f2e35a3aSmrg	 (atom == XA_CLIPBOARD(d)))
47932e4f8982Smrg
4794d522f475Smrgstatic Boolean
4795d522f475SmrgConvertSelection(Widget w,
4796894e0ac8Smrg		 Atom *selection,
4797894e0ac8Smrg		 Atom *target,
4798894e0ac8Smrg		 Atom *type,
4799d522f475Smrg		 XtPointer *value,
4800d522f475Smrg		 unsigned long *length,
4801d522f475Smrg		 int *format)
4802d522f475Smrg{
4803d522f475Smrg    Display *dpy = XtDisplay(w);
4804d522f475Smrg    TScreen *screen;
4805f2e35a3aSmrg    SelectedCells *scp;
4806d522f475Smrg    Bool result = False;
4807d522f475Smrg
48082e4f8982Smrg    Char *data;
48092e4f8982Smrg    unsigned long data_length;
48102e4f8982Smrg
4811956cc18dSsnj    XtermWidget xw;
4812956cc18dSsnj
48135104ee6eSmrg    if ((xw = getXtermWidget(w)) == NULL)
4814d522f475Smrg	return False;
4815d522f475Smrg
4816956cc18dSsnj    screen = TScreenOf(xw);
4817d522f475Smrg
4818f2e35a3aSmrg    TRACE(("ConvertSelection %s -> %s\n",
4819f2e35a3aSmrg	   TraceAtomName(screen->display, *selection),
4820956cc18dSsnj	   visibleSelectionTarget(dpy, *target)));
4821956cc18dSsnj
4822f2e35a3aSmrg    if (keepClipboard(dpy, *selection)) {
48232e4f8982Smrg	TRACE(("asked for clipboard\n"));
4824f2e35a3aSmrg	scp = &(screen->clipboard_data);
48252e4f8982Smrg    } else {
48262e4f8982Smrg	TRACE(("asked for selection\n"));
4827f2e35a3aSmrg	scp = &(screen->selected_cells[AtomToSelection(dpy, *selection)]);
48282e4f8982Smrg    }
48292e4f8982Smrg
4830f2e35a3aSmrg    data = scp->data_buffer;
4831f2e35a3aSmrg    data_length = scp->data_length;
48322e4f8982Smrg    if (data == NULL) {
4833f2e35a3aSmrg	TRACE(("...no selection-data\n"));
4834f2e35a3aSmrg	return False;
483501037d57Smrg    }
483601037d57Smrg
4837d522f475Smrg    if (*target == XA_TARGETS(dpy)) {
4838d522f475Smrg	Atom *targetP;
48395104ee6eSmrg	XPointer std_return = NULL;
4840d522f475Smrg	unsigned long std_length;
4841d522f475Smrg
4842d522f475Smrg	if (XmuConvertStandardSelection(w, screen->selection_time, selection,
4843d522f475Smrg					target, type, &std_return,
4844d522f475Smrg					&std_length, format)) {
4845956cc18dSsnj	    Atom *my_targets = _SelectionTargets(w);
48462e4f8982Smrg	    Atom *allocP;
48472e4f8982Smrg	    Atom *std_targets;
4848956cc18dSsnj
4849956cc18dSsnj	    TRACE(("XmuConvertStandardSelection - success\n"));
4850a1f3da82Smrg	    std_targets = (Atom *) (void *) (std_return);
4851d522f475Smrg	    *length = std_length + 6;
4852d522f475Smrg
4853a1f3da82Smrg	    targetP = TypeXtMallocN(Atom, *length);
4854d522f475Smrg	    allocP = targetP;
4855d522f475Smrg
4856d522f475Smrg	    *value = (XtPointer) targetP;
4857d522f475Smrg
48585104ee6eSmrg	    if (my_targets != NULL) {
48590bd37d32Smrg		while (*my_targets != None) {
48600bd37d32Smrg		    *targetP++ = *my_targets++;
48610bd37d32Smrg		}
4862956cc18dSsnj	    }
4863d522f475Smrg	    *targetP++ = XA_LENGTH(dpy);
4864d522f475Smrg	    *targetP++ = XA_LIST_LENGTH(dpy);
4865d522f475Smrg
48662eaa94a1Schristos	    *length = std_length + (unsigned long) (targetP - allocP);
4867d522f475Smrg
4868d522f475Smrg	    memcpy(targetP, std_targets, sizeof(Atom) * std_length);
4869d522f475Smrg	    XtFree((char *) std_targets);
4870d522f475Smrg	    *type = XA_ATOM;
4871d522f475Smrg	    *format = 32;
4872d522f475Smrg	    result = True;
4873956cc18dSsnj	} else {
4874956cc18dSsnj	    TRACE(("XmuConvertStandardSelection - failed\n"));
4875d522f475Smrg	}
4876d522f475Smrg    }
4877d522f475Smrg#if OPT_WIDE_CHARS
4878d522f475Smrg    else if (screen->wide_chars && *target == XA_STRING) {
4879d522f475Smrg	result =
4880f2e35a3aSmrg	    _ConvertSelectionHelper(w, scp,
4881f2e35a3aSmrg				    type, value, length, format,
4882d522f475Smrg				    Xutf8TextListToTextProperty,
4883d522f475Smrg				    XStringStyle);
4884956cc18dSsnj	TRACE(("...Xutf8TextListToTextProperty:%d\n", result));
4885d522f475Smrg    } else if (screen->wide_chars && *target == XA_UTF8_STRING(dpy)) {
4886d522f475Smrg	result =
4887f2e35a3aSmrg	    _ConvertSelectionHelper(w, scp,
4888f2e35a3aSmrg				    type, value, length, format,
4889d522f475Smrg				    Xutf8TextListToTextProperty,
4890d522f475Smrg				    XUTF8StringStyle);
4891956cc18dSsnj	TRACE(("...Xutf8TextListToTextProperty:%d\n", result));
4892d522f475Smrg    } else if (screen->wide_chars && *target == XA_TEXT(dpy)) {
4893d522f475Smrg	result =
4894f2e35a3aSmrg	    _ConvertSelectionHelper(w, scp,
4895f2e35a3aSmrg				    type, value, length, format,
4896d522f475Smrg				    Xutf8TextListToTextProperty,
4897d522f475Smrg				    XStdICCTextStyle);
4898956cc18dSsnj	TRACE(("...Xutf8TextListToTextProperty:%d\n", result));
4899d522f475Smrg    } else if (screen->wide_chars && *target == XA_COMPOUND_TEXT(dpy)) {
4900d522f475Smrg	result =
4901f2e35a3aSmrg	    _ConvertSelectionHelper(w, scp,
4902f2e35a3aSmrg				    type, value, length, format,
4903d522f475Smrg				    Xutf8TextListToTextProperty,
4904d522f475Smrg				    XCompoundTextStyle);
4905956cc18dSsnj	TRACE(("...Xutf8TextListToTextProperty:%d\n", result));
4906d522f475Smrg    }
4907d522f475Smrg#endif
4908d522f475Smrg
4909d522f475Smrg    else if (*target == XA_STRING) {	/* not wide_chars */
4910d522f475Smrg	/* We can only reach this point if the selection requestor
4911d522f475Smrg	   requested STRING before any of TEXT, COMPOUND_TEXT or
4912d522f475Smrg	   UTF8_STRING.  We therefore assume that the requestor is not
4913d522f475Smrg	   properly internationalised, and dump raw eight-bit data
4914d522f475Smrg	   with no conversion into the selection.  Yes, this breaks
4915d522f475Smrg	   the ICCCM in non-Latin-1 locales. */
4916d522f475Smrg	*type = XA_STRING;
4917f2e35a3aSmrg	*value = (XtPointer) data;
4918f2e35a3aSmrg	*length = data_length;
4919d522f475Smrg	*format = 8;
4920d522f475Smrg	result = True;
4921956cc18dSsnj	TRACE(("...raw 8-bit data:%d\n", result));
4922d522f475Smrg    } else if (*target == XA_TEXT(dpy)) {	/* not wide_chars */
4923d522f475Smrg	result =
4924f2e35a3aSmrg	    _ConvertSelectionHelper(w, scp,
4925f2e35a3aSmrg				    type, value, length, format,
4926d522f475Smrg				    XmbTextListToTextProperty,
4927d522f475Smrg				    XStdICCTextStyle);
4928956cc18dSsnj	TRACE(("...XmbTextListToTextProperty(StdICC):%d\n", result));
4929d522f475Smrg    } else if (*target == XA_COMPOUND_TEXT(dpy)) {	/* not wide_chars */
4930d522f475Smrg	result =
4931f2e35a3aSmrg	    _ConvertSelectionHelper(w, scp,
4932f2e35a3aSmrg				    type, value, length, format,
4933d522f475Smrg				    XmbTextListToTextProperty,
4934d522f475Smrg				    XCompoundTextStyle);
4935956cc18dSsnj	TRACE(("...XmbTextListToTextProperty(Compound):%d\n", result));
4936d522f475Smrg    }
4937d522f475Smrg#ifdef X_HAVE_UTF8_STRING
4938d522f475Smrg    else if (*target == XA_UTF8_STRING(dpy)) {	/* not wide_chars */
4939d522f475Smrg	result =
4940f2e35a3aSmrg	    _ConvertSelectionHelper(w, scp,
4941f2e35a3aSmrg				    type, value, length, format,
4942d522f475Smrg				    XmbTextListToTextProperty,
4943d522f475Smrg				    XUTF8StringStyle);
4944956cc18dSsnj	TRACE(("...XmbTextListToTextProperty(UTF8):%d\n", result));
4945d522f475Smrg    }
4946d522f475Smrg#endif
4947d522f475Smrg    else if (*target == XA_LIST_LENGTH(dpy)) {
494820d2c4d2Smrg	result = SaveConvertedLength(value, (unsigned long) 1);
4949d522f475Smrg	*type = XA_INTEGER;
4950d522f475Smrg	*length = 1;
4951d522f475Smrg	*format = 32;
4952956cc18dSsnj	TRACE(("...list of values:%d\n", result));
4953d522f475Smrg    } else if (*target == XA_LENGTH(dpy)) {
4954d522f475Smrg	/* This value is wrong if we have UTF-8 text */
4955f2e35a3aSmrg	result = SaveConvertedLength(value, scp->data_length);
4956d522f475Smrg	*type = XA_INTEGER;
4957d522f475Smrg	*length = 1;
4958d522f475Smrg	*format = 32;
4959956cc18dSsnj	TRACE(("...list of values:%d\n", result));
4960d522f475Smrg    } else if (XmuConvertStandardSelection(w,
4961d522f475Smrg					   screen->selection_time, selection,
4962d522f475Smrg					   target, type, (XPointer *) value,
4963d522f475Smrg					   length, format)) {
4964d522f475Smrg	result = True;
4965956cc18dSsnj	TRACE(("...XmuConvertStandardSelection:%d\n", result));
4966d522f475Smrg    }
4967d522f475Smrg
4968d522f475Smrg    /* else */
49692eaa94a1Schristos    return (Boolean) result;
4970d522f475Smrg}
4971d522f475Smrg
4972d522f475Smrgstatic void
4973894e0ac8SmrgLoseSelection(Widget w, Atom *selection)
4974d522f475Smrg{
4975d522f475Smrg    TScreen *screen;
4976d522f475Smrg    Atom *atomP;
4977d522f475Smrg    Cardinal i;
4978d522f475Smrg
4979956cc18dSsnj    XtermWidget xw;
4980956cc18dSsnj
49815104ee6eSmrg    if ((xw = getXtermWidget(w)) == NULL)
4982d522f475Smrg	return;
4983d522f475Smrg
4984956cc18dSsnj    screen = TScreenOf(xw);
4985913cc679Smrg    TRACE(("LoseSelection %s\n", TraceAtomName(screen->display, *selection)));
498601037d57Smrg
4987d522f475Smrg    for (i = 0, atomP = screen->selection_atoms;
4988d522f475Smrg	 i < screen->selection_count; i++, atomP++) {
4989d522f475Smrg	if (*selection == *atomP)
4990d522f475Smrg	    *atomP = (Atom) 0;
4991d522f475Smrg	if (CutBuffer(*atomP) >= 0) {
4992d522f475Smrg	    *atomP = (Atom) 0;
4993d522f475Smrg	}
4994d522f475Smrg    }
4995d522f475Smrg
4996d522f475Smrg    for (i = screen->selection_count; i; i--) {
4997d522f475Smrg	if (screen->selection_atoms[i - 1] != 0)
4998d522f475Smrg	    break;
4999d522f475Smrg    }
5000d522f475Smrg    screen->selection_count = i;
5001d522f475Smrg
5002d522f475Smrg    for (i = 0, atomP = screen->selection_atoms;
5003d522f475Smrg	 i < screen->selection_count; i++, atomP++) {
5004d522f475Smrg	if (*atomP == (Atom) 0) {
5005d522f475Smrg	    *atomP = screen->selection_atoms[--screen->selection_count];
5006d522f475Smrg	}
5007d522f475Smrg    }
5008d522f475Smrg
5009d522f475Smrg    if (screen->selection_count == 0)
5010f2e35a3aSmrg	UnHiliteText(xw);
5011d522f475Smrg}
5012d522f475Smrg
5013d522f475Smrg/* ARGSUSED */
5014d522f475Smrgstatic void
5015d522f475SmrgSelectionDone(Widget w GCC_UNUSED,
5016894e0ac8Smrg	      Atom *selection GCC_UNUSED,
5017894e0ac8Smrg	      Atom *target GCC_UNUSED)
5018d522f475Smrg{
5019d522f475Smrg    /* empty proc so Intrinsics know we want to keep storage */
502001037d57Smrg    TRACE(("SelectionDone\n"));
5021d522f475Smrg}
5022d522f475Smrg
5023d522f475Smrgstatic void
5024d522f475Smrg_OwnSelection(XtermWidget xw,
5025e0a2b6dfSmrg	      String *selections,
5026d522f475Smrg	      Cardinal count)
5027d522f475Smrg{
5028956cc18dSsnj    TScreen *screen = TScreenOf(xw);
5029f2e35a3aSmrg    Display *dpy = screen->display;
5030d522f475Smrg    Atom *atoms = screen->selection_atoms;
5031d522f475Smrg    Cardinal i;
5032d522f475Smrg    Bool have_selection = False;
5033f2e35a3aSmrg    SelectedCells *scp;
5034d522f475Smrg
503520d2c4d2Smrg    if (count == 0)
503620d2c4d2Smrg	return;
5037d522f475Smrg
5038f2e35a3aSmrg    TRACE(("_OwnSelection count %d\n", count));
5039d522f475Smrg    selections = MapSelections(xw, selections, count);
5040d522f475Smrg
5041d522f475Smrg    if (count > screen->sel_atoms_size) {
5042d522f475Smrg	XtFree((char *) atoms);
5043a1f3da82Smrg	atoms = TypeXtMallocN(Atom, count);
5044d522f475Smrg	screen->selection_atoms = atoms;
5045d522f475Smrg	screen->sel_atoms_size = count;
5046d522f475Smrg    }
5047f2e35a3aSmrg    XmuInternStrings(dpy, selections, count, atoms);
5048d522f475Smrg    for (i = 0; i < count; i++) {
5049d522f475Smrg	int cutbuffer = CutBuffer(atoms[i]);
5050d522f475Smrg	if (cutbuffer >= 0) {
50512eaa94a1Schristos	    unsigned long limit =
5052f2e35a3aSmrg	    (unsigned long) (4 * XMaxRequestSize(dpy) - 32);
5053f2e35a3aSmrg	    scp = &(screen->selected_cells[CutBufferToCode(cutbuffer)]);
5054f2e35a3aSmrg	    if (scp->data_length > limit) {
505520d2c4d2Smrg		TRACE(("selection too big (%lu bytes), not storing in CUT_BUFFER%d\n",
5056f2e35a3aSmrg		       (unsigned long) scp->data_length, cutbuffer));
50570bd37d32Smrg		xtermWarning("selection too big (%lu bytes), not storing in CUT_BUFFER%d\n",
5058f2e35a3aSmrg			     (unsigned long) scp->data_length, cutbuffer);
5059d522f475Smrg	    } else {
5060d522f475Smrg		/* This used to just use the UTF-8 data, which was totally
5061894e0ac8Smrg		 * broken as not even the corresponding paste code in xterm
5062d522f475Smrg		 * understood this!  So now it converts to Latin1 first.
5063d522f475Smrg		 *   Robert Brady, 2000-09-05
5064d522f475Smrg		 */
5065f2e35a3aSmrg		unsigned long length = scp->data_length;
5066f2e35a3aSmrg		Char *data = scp->data_buffer;
5067d522f475Smrg		if_OPT_WIDE_CHARS((screen), {
5068956cc18dSsnj		    data = UTF8toLatin1(screen, data, length, &length);
5069d522f475Smrg		});
5070d522f475Smrg		TRACE(("XStoreBuffer(%d)\n", cutbuffer));
5071f2e35a3aSmrg		XStoreBuffer(dpy,
5072d522f475Smrg			     (char *) data,
5073d522f475Smrg			     (int) length,
5074d522f475Smrg			     cutbuffer);
5075d522f475Smrg	    }
5076f2e35a3aSmrg	} else {
5077f2e35a3aSmrg	    int which = AtomToSelection(dpy, atoms[i]);
5078f2e35a3aSmrg	    if (keepClipboard(dpy, atoms[i])) {
5079f2e35a3aSmrg		Char *buf;
5080f2e35a3aSmrg		SelectedCells *tcp = &(screen->clipboard_data);
5081f2e35a3aSmrg		TRACE(("saving selection to clipboard buffer\n"));
5082f2e35a3aSmrg		scp = &(screen->selected_cells[CLIPBOARD_CODE]);
50835104ee6eSmrg		if ((buf = (Char *) malloc((size_t) scp->data_length)) == NULL) {
5084f2e35a3aSmrg		    SysError(ERROR_BMALLOC2);
50855307cd1aSmrg		} else {
50865307cd1aSmrg		    free(tcp->data_buffer);
50875307cd1aSmrg		    memcpy(buf, scp->data_buffer, scp->data_length);
50885307cd1aSmrg		    tcp->data_buffer = buf;
50895307cd1aSmrg		    tcp->data_limit = scp->data_length;
50905307cd1aSmrg		    tcp->data_length = scp->data_length;
50915307cd1aSmrg		}
5092f2e35a3aSmrg	    }
5093f2e35a3aSmrg	    scp = &(screen->selected_cells[which]);
5094f2e35a3aSmrg	    if (scp->data_length == 0) {
5095f2e35a3aSmrg		TRACE(("XtDisownSelection(%s, @%ld)\n",
5096f2e35a3aSmrg		       TraceAtomName(screen->display, atoms[i]),
5097f2e35a3aSmrg		       (long) screen->selection_time));
5098f2e35a3aSmrg		XtDisownSelection((Widget) xw,
5099f2e35a3aSmrg				  atoms[i],
5100f2e35a3aSmrg				  screen->selection_time);
5101f2e35a3aSmrg	    } else if (!screen->replyToEmacs && atoms[i] != 0) {
5102f2e35a3aSmrg		TRACE(("XtOwnSelection(%s, @%ld)\n",
5103f2e35a3aSmrg		       TraceAtomName(screen->display, atoms[i]),
5104f2e35a3aSmrg		       (long) screen->selection_time));
5105f2e35a3aSmrg		have_selection |=
5106f2e35a3aSmrg		    XtOwnSelection((Widget) xw, atoms[i],
5107f2e35a3aSmrg				   screen->selection_time,
5108f2e35a3aSmrg				   ConvertSelection,
5109f2e35a3aSmrg				   LoseSelection,
5110f2e35a3aSmrg				   SelectionDone);
5111f2e35a3aSmrg	    }
5112d522f475Smrg	}
5113f2e35a3aSmrg	TRACE(("... _OwnSelection used length %lu value %s\n",
5114f2e35a3aSmrg	       (unsigned long) scp->data_length,
5115f2e35a3aSmrg	       visibleChars(scp->data_buffer,
5116f2e35a3aSmrg			    (unsigned) scp->data_length)));
5117d522f475Smrg    }
5118d522f475Smrg    if (!screen->replyToEmacs)
5119d522f475Smrg	screen->selection_count = count;
5120d522f475Smrg    if (!have_selection)
5121f2e35a3aSmrg	UnHiliteText(xw);
5122d522f475Smrg}
5123d522f475Smrg
5124d522f475Smrgstatic void
5125e0a2b6dfSmrgResetSelectionState(TScreen *screen)
5126d522f475Smrg{
5127d522f475Smrg    screen->selection_count = 0;
5128d522f475Smrg    screen->startH = zeroCELL;
5129d522f475Smrg    screen->endH = zeroCELL;
5130d522f475Smrg}
5131d522f475Smrg
5132d522f475Smrgvoid
5133d522f475SmrgDisownSelection(XtermWidget xw)
5134d522f475Smrg{
5135956cc18dSsnj    TScreen *screen = TScreenOf(xw);
5136d522f475Smrg    Atom *atoms = screen->selection_atoms;
5137d522f475Smrg    Cardinal count = screen->selection_count;
5138d522f475Smrg    Cardinal i;
5139d522f475Smrg
5140d522f475Smrg    TRACE(("DisownSelection count %d, start %d.%d, end %d.%d\n",
5141d522f475Smrg	   count,
5142d522f475Smrg	   screen->startH.row,
5143d522f475Smrg	   screen->startH.col,
5144d522f475Smrg	   screen->endH.row,
5145d522f475Smrg	   screen->endH.col));
5146d522f475Smrg
5147d522f475Smrg    for (i = 0; i < count; i++) {
5148d522f475Smrg	int cutbuffer = CutBuffer(atoms[i]);
5149d522f475Smrg	if (cutbuffer < 0) {
5150d522f475Smrg	    XtDisownSelection((Widget) xw, atoms[i],
5151d522f475Smrg			      screen->selection_time);
5152d522f475Smrg	}
5153d522f475Smrg    }
5154d522f475Smrg    /*
5155d522f475Smrg     * If none of the callbacks via XtDisownSelection() reset highlighting
5156d522f475Smrg     * do it now.
5157d522f475Smrg     */
5158d522f475Smrg    if (ScrnHaveSelection(screen)) {
5159d522f475Smrg	/* save data which will be reset */
5160d522f475Smrg	CELL first = screen->startH;
5161d522f475Smrg	CELL last = screen->endH;
5162d522f475Smrg
5163d522f475Smrg	ResetSelectionState(screen);
5164d522f475Smrg	ReHiliteText(xw, &first, &last);
5165d522f475Smrg    } else {
5166d522f475Smrg	ResetSelectionState(screen);
5167d522f475Smrg    }
5168d522f475Smrg}
5169d522f475Smrg
5170d522f475Smrgvoid
5171d522f475SmrgUnhiliteSelection(XtermWidget xw)
5172d522f475Smrg{
5173956cc18dSsnj    TScreen *screen = TScreenOf(xw);
5174d522f475Smrg
5175d522f475Smrg    if (ScrnHaveSelection(screen)) {
5176d522f475Smrg	CELL first = screen->startH;
5177d522f475Smrg	CELL last = screen->endH;
5178d522f475Smrg
5179d522f475Smrg	screen->startH = zeroCELL;
5180d522f475Smrg	screen->endH = zeroCELL;
5181d522f475Smrg	ReHiliteText(xw, &first, &last);
5182d522f475Smrg    }
5183d522f475Smrg}
5184d522f475Smrg
5185d522f475Smrg/* returns number of chars in line from scol to ecol out */
5186d522f475Smrg/* ARGSUSED */
5187d522f475Smrgstatic int
5188e0a2b6dfSmrgLength(TScreen *screen,
5189d522f475Smrg       int row,
5190d522f475Smrg       int scol,
5191d522f475Smrg       int ecol)
5192d522f475Smrg{
519301037d57Smrg    CLineData *ld = GET_LINEDATA(screen, row);
519401037d57Smrg    const int lastcol = LastTextCol(screen, ld, row);
5195d522f475Smrg
5196d522f475Smrg    if (ecol > lastcol)
5197d522f475Smrg	ecol = lastcol;
5198d522f475Smrg    return (ecol - scol + 1);
5199d522f475Smrg}
5200d522f475Smrg
5201d522f475Smrg/* copies text into line, preallocated */
5202d522f475Smrgstatic Char *
5203e0a2b6dfSmrgSaveText(TScreen *screen,
5204d522f475Smrg	 int row,
5205d522f475Smrg	 int scol,
5206d522f475Smrg	 int ecol,
5207e0a2b6dfSmrg	 Char *lp,		/* pointer to where to put the text */
5208d522f475Smrg	 int *eol)
5209d522f475Smrg{
5210956cc18dSsnj    LineData *ld;
5211d522f475Smrg    int i = 0;
5212d522f475Smrg    Char *result = lp;
5213d522f475Smrg#if OPT_WIDE_CHARS
52142eaa94a1Schristos    unsigned previous = 0;
5215d522f475Smrg#endif
5216d522f475Smrg
5217956cc18dSsnj    ld = GET_LINEDATA(screen, row);
5218d522f475Smrg    i = Length(screen, row, scol, ecol);
5219d522f475Smrg    ecol = scol + i;
5220d522f475Smrg#if OPT_DEC_CHRSET
5221956cc18dSsnj    if (CSET_DOUBLE(GetLineDblCS(ld))) {
5222d522f475Smrg	scol = (scol + 0) / 2;
5223d522f475Smrg	ecol = (ecol + 1) / 2;
5224d522f475Smrg    }
5225d522f475Smrg#endif
5226956cc18dSsnj    *eol = !LineTstWrapped(ld);
5227d522f475Smrg    for (i = scol; i < ecol; i++) {
52282e4f8982Smrg	unsigned c;
52295104ee6eSmrg	if (i >= (int) ld->lineSize) {
52305104ee6eSmrg	    /* The terminal was probably resized */
52315104ee6eSmrg	    *lp++ = CharOf(' ');
52325104ee6eSmrg	    continue;
52335104ee6eSmrg	}
52345104ee6eSmrg	c = ld->charData[i];
523504b94745Smrg	if (ld->attribs[i] & INVISIBLE)
523604b94745Smrg	    continue;
5237d522f475Smrg#if OPT_WIDE_CHARS
5238d522f475Smrg	/* We want to strip out every occurrence of HIDDEN_CHAR AFTER a
5239d522f475Smrg	 * wide character.
5240d522f475Smrg	 */
5241894e0ac8Smrg	if (c == HIDDEN_CHAR) {
5242894e0ac8Smrg	    if (isWide((int) previous)) {
5243894e0ac8Smrg		previous = c;
5244894e0ac8Smrg		/* Combining characters attached to double-width characters
5245894e0ac8Smrg		   are in memory attached to the HIDDEN_CHAR */
5246894e0ac8Smrg		if_OPT_WIDE_CHARS(screen, {
5247894e0ac8Smrg		    if ((screen->utf8_nrc_mode | screen->utf8_mode) != uFalse) {
5248894e0ac8Smrg			size_t off;
5249894e0ac8Smrg			for_each_combData(off, ld) {
52502e4f8982Smrg			    unsigned ch = ld->combData[off][i];
5251894e0ac8Smrg			    if (ch == 0)
5252894e0ac8Smrg				break;
5253894e0ac8Smrg			    lp = convertToUTF8(lp, ch);
5254894e0ac8Smrg			}
5255d522f475Smrg		    }
5256894e0ac8Smrg		});
5257894e0ac8Smrg		continue;
5258894e0ac8Smrg	    } else {
5259894e0ac8Smrg		c = ' ';	/* should not happen, but just in case... */
5260894e0ac8Smrg	    }
5261d522f475Smrg	}
5262d522f475Smrg	previous = c;
5263e0a2b6dfSmrg	if ((screen->utf8_nrc_mode | screen->utf8_mode) != uFalse) {
5264d522f475Smrg	    lp = convertToUTF8(lp, (c != 0) ? c : ' ');
5265d522f475Smrg	    if_OPT_WIDE_CHARS(screen, {
5266956cc18dSsnj		size_t off;
5267956cc18dSsnj		for_each_combData(off, ld) {
52682e4f8982Smrg		    unsigned ch = ld->combData[off][i];
5269956cc18dSsnj		    if (ch == 0)
5270d522f475Smrg			break;
5271d522f475Smrg		    lp = convertToUTF8(lp, ch);
5272d522f475Smrg		}
5273d522f475Smrg	    });
5274d522f475Smrg	} else
5275d522f475Smrg#endif
5276d522f475Smrg	{
5277d522f475Smrg	    if (c == 0) {
52785104ee6eSmrg		c = ' ';
52795104ee6eSmrg	    } else if (c < ' ') {
5280d522f475Smrg		c = DECtoASCII(c);
5281d522f475Smrg	    } else if (c == 0x7f) {
5282d522f475Smrg		c = 0x5f;
5283d522f475Smrg	    }
52845104ee6eSmrg	    *lp++ = CharOf(c);
5285d522f475Smrg	}
52865104ee6eSmrg	if (c != ' ')
5287d522f475Smrg	    result = lp;
5288d522f475Smrg    }
5289d522f475Smrg
5290d522f475Smrg    /*
5291d522f475Smrg     * If requested, trim trailing blanks from selected lines.  Do not do this
5292d522f475Smrg     * if the line is wrapped.
5293d522f475Smrg     */
5294d522f475Smrg    if (!*eol || !screen->trim_selection)
5295d522f475Smrg	result = lp;
5296d522f475Smrg
5297d522f475Smrg    return (result);
5298d522f475Smrg}
5299d522f475Smrg
5300f2e35a3aSmrg/*
5301f2e35a3aSmrg * This adds together the bits:
5302f2e35a3aSmrg *   shift key   -> 1
5303f2e35a3aSmrg *   meta key    -> 2
5304f2e35a3aSmrg *   control key -> 4
5305f2e35a3aSmrg */
5306f2e35a3aSmrgstatic unsigned
5307f2e35a3aSmrgKeyState(XtermWidget xw, unsigned x)
5308f2e35a3aSmrg{
5309f2e35a3aSmrg    return ((((x) & (ShiftMask | ControlMask)))
5310f2e35a3aSmrg	    + (((x) & MetaMask(xw)) ? 2 : 0));
5311f2e35a3aSmrg}
5312f2e35a3aSmrg
5313f2e35a3aSmrg/* 32 + following 8-bit word:
5314d522f475Smrg
5315d522f475Smrg   1:0  Button no: 0, 1, 2.  3=release.
5316d522f475Smrg     2  shift
5317d522f475Smrg     3  meta
5318d522f475Smrg     4  ctrl
5319d522f475Smrg     5  set for motion notify
5320f2e35a3aSmrg     6  set for wheel (and button 6 and 7)
5321f2e35a3aSmrg     7  set for buttons 8 to 11
5322d522f475Smrg*/
5323d522f475Smrg
5324d522f475Smrg/* Position: 32 - 255. */
5325a1f3da82Smrgstatic int
5326f2e35a3aSmrgBtnCode(XtermWidget xw, XButtonEvent *event, int button)
5327d522f475Smrg{
5328f2e35a3aSmrg    int result = (int) (32 + (KeyState(xw, event->state) << 2));
5329d522f475Smrg
53300bd37d32Smrg    if (event->type == MotionNotify)
53310bd37d32Smrg	result += 32;
53320bd37d32Smrg
5333f2e35a3aSmrg    if (button < 0) {
5334d522f475Smrg	result += 3;
5335d522f475Smrg    } else {
5336f2e35a3aSmrg	result += button & 3;
5337f2e35a3aSmrg	if (button & 4)
5338f2e35a3aSmrg	    result += 64;
5339f2e35a3aSmrg	if (button & 8)
5340f2e35a3aSmrg	    result += 128;
5341d522f475Smrg    }
53420bd37d32Smrg    TRACE(("BtnCode button %d, %s state " FMT_MODIFIER_NAMES " ->%#x\n",
53430bd37d32Smrg	   button,
53440bd37d32Smrg	   visibleEventType(event->type),
53450bd37d32Smrg	   ARG_MODIFIER_NAMES(event->state),
53460bd37d32Smrg	   result));
5347a1f3da82Smrg    return result;
5348a1f3da82Smrg}
5349a1f3da82Smrg
5350a1f3da82Smrgstatic unsigned
5351913cc679SmrgEmitButtonCode(XtermWidget xw,
5352e0a2b6dfSmrg	       Char *line,
53530bd37d32Smrg	       unsigned count,
5354894e0ac8Smrg	       XButtonEvent *event,
53550bd37d32Smrg	       int button)
5356a1f3da82Smrg{
5357913cc679Smrg    TScreen *screen = TScreenOf(xw);
53580bd37d32Smrg    int value;
5359a1f3da82Smrg
5360913cc679Smrg    if (okSendMousePos(xw) == X10_MOUSE) {
53610bd37d32Smrg	value = CharOf(' ' + button);
5362a1f3da82Smrg    } else {
5363f2e35a3aSmrg	value = BtnCode(xw, event, button);
53640bd37d32Smrg    }
53650bd37d32Smrg
53660bd37d32Smrg    switch (screen->extend_coords) {
53670bd37d32Smrg    default:
53680bd37d32Smrg	line[count++] = CharOf(value);
53690bd37d32Smrg	break;
53700bd37d32Smrg    case SET_SGR_EXT_MODE_MOUSE:
5371f2e35a3aSmrg    case SET_PIXEL_POSITION_MOUSE:
53720bd37d32Smrg	value -= 32;		/* encoding starts at zero */
53730bd37d32Smrg	/* FALLTHRU */
53740bd37d32Smrg    case SET_URXVT_EXT_MODE_MOUSE:
53750bd37d32Smrg	count += (unsigned) sprintf((char *) line + count, "%d", value);
53760bd37d32Smrg	break;
53770bd37d32Smrg    case SET_EXT_MODE_MOUSE:
53780bd37d32Smrg	if (value < 128) {
53790bd37d32Smrg	    line[count++] = CharOf(value);
53800bd37d32Smrg	} else {
53810bd37d32Smrg	    line[count++] = CharOf(0xC0 + (value >> 6));
53820bd37d32Smrg	    line[count++] = CharOf(0x80 + (value & 0x3F));
53830bd37d32Smrg	}
53840bd37d32Smrg	break;
5385a1f3da82Smrg    }
5386a1f3da82Smrg    return count;
5387d522f475Smrg}
5388d522f475Smrg
53890bd37d32Smrgstatic int
53900bd37d32SmrgFirstBitN(int bits)
53910bd37d32Smrg{
53920bd37d32Smrg    int result = -1;
53930bd37d32Smrg    if (bits > 0) {
53940bd37d32Smrg	result = 0;
53950bd37d32Smrg	while (!(bits & 1)) {
53960bd37d32Smrg	    bits /= 2;
53970bd37d32Smrg	    ++result;
53980bd37d32Smrg	}
53990bd37d32Smrg    }
54000bd37d32Smrg    return result;
54010bd37d32Smrg}
54020bd37d32Smrg
54030bd37d32Smrg#define ButtonBit(button) ((button >= 0) ? (1 << (button)) : 0)
54040bd37d32Smrg
5405913cc679Smrg#define EMIT_BUTTON(button) EmitButtonCode(xw, line, count, event, button)
54060bd37d32Smrg
5407d522f475Smrgstatic void
5408894e0ac8SmrgEditorButton(XtermWidget xw, XButtonEvent *event)
5409d522f475Smrg{
5410956cc18dSsnj    TScreen *screen = TScreenOf(xw);
5411d522f475Smrg    int pty = screen->respond;
54120bd37d32Smrg    int mouse_limit = MouseLimit(screen);
54130bd37d32Smrg    Char line[32];
54140bd37d32Smrg    Char final = 'M';
5415d522f475Smrg    int row, col;
5416d522f475Smrg    int button;
5417d522f475Smrg    unsigned count = 0;
5418d522f475Smrg    Boolean changed = True;
5419d522f475Smrg
5420d522f475Smrg    /* If button event, get button # adjusted for DEC compatibility */
54212eaa94a1Schristos    button = (int) (event->button - 1);
5422d522f475Smrg    if (button >= 3)
5423d522f475Smrg	button++;
5424d522f475Smrg
5425f2e35a3aSmrg    /* Ignore buttons that cannot be encoded */
5426f2e35a3aSmrg    if (screen->send_mouse_pos == X10_MOUSE) {
5427f2e35a3aSmrg	if (button > 3)
5428f2e35a3aSmrg	    return;
5429f2e35a3aSmrg    } else if (screen->extend_coords == SET_SGR_EXT_MODE_MOUSE
5430f2e35a3aSmrg	       || screen->extend_coords == SET_URXVT_EXT_MODE_MOUSE
5431f2e35a3aSmrg	       || screen->extend_coords == SET_PIXEL_POSITION_MOUSE) {
5432f2e35a3aSmrg	if (button > 15) {
5433f2e35a3aSmrg	    return;
5434f2e35a3aSmrg	}
5435f2e35a3aSmrg    } else {
5436f2e35a3aSmrg	if (button > 11) {
5437f2e35a3aSmrg	    return;
5438f2e35a3aSmrg	}
5439f2e35a3aSmrg    }
5440d522f475Smrg
5441f2e35a3aSmrg    if (screen->extend_coords == SET_PIXEL_POSITION_MOUSE) {
5442f2e35a3aSmrg	row = event->y - OriginY(screen);
5443f2e35a3aSmrg	col = event->x - OriginX(screen);
5444f2e35a3aSmrg    } else {
5445f2e35a3aSmrg	/* Compute character position of mouse pointer */
5446f2e35a3aSmrg	row = (event->y - screen->border) / FontHeight(screen);
5447f2e35a3aSmrg	col = (event->x - OriginX(screen)) / FontWidth(screen);
5448d522f475Smrg
5449f2e35a3aSmrg	/* Limit to screen dimensions */
5450f2e35a3aSmrg	if (row < 0)
5451f2e35a3aSmrg	    row = 0;
5452f2e35a3aSmrg	else if (row > screen->max_row)
5453f2e35a3aSmrg	    row = screen->max_row;
5454492d43a5Smrg
5455f2e35a3aSmrg	if (col < 0)
5456f2e35a3aSmrg	    col = 0;
5457f2e35a3aSmrg	else if (col > screen->max_col)
5458f2e35a3aSmrg	    col = screen->max_col;
5459f2e35a3aSmrg
5460f2e35a3aSmrg	if (mouse_limit > 0) {
5461f2e35a3aSmrg	    /* Limit to representable mouse dimensions */
5462f2e35a3aSmrg	    if (row > mouse_limit)
5463f2e35a3aSmrg		row = mouse_limit;
5464f2e35a3aSmrg	    if (col > mouse_limit)
5465f2e35a3aSmrg		col = mouse_limit;
5466f2e35a3aSmrg	}
54670bd37d32Smrg    }
5468d522f475Smrg
5469d522f475Smrg    /* Build key sequence starting with \E[M */
5470d522f475Smrg    if (screen->control_eight_bits) {
5471d522f475Smrg	line[count++] = ANSI_CSI;
5472d522f475Smrg    } else {
5473d522f475Smrg	line[count++] = ANSI_ESC;
5474d522f475Smrg	line[count++] = '[';
5475d522f475Smrg    }
54760bd37d32Smrg    switch (screen->extend_coords) {
54770bd37d32Smrg    case 0:
54780bd37d32Smrg    case SET_EXT_MODE_MOUSE:
5479d522f475Smrg#if OPT_SCO_FUNC_KEYS
54800bd37d32Smrg	if (xw->keyboard.type == keyboardIsSCO) {
54810bd37d32Smrg	    /*
54820bd37d32Smrg	     * SCO function key F1 is \E[M, which would conflict with xterm's
54830bd37d32Smrg	     * normal kmous.
54840bd37d32Smrg	     */
54850bd37d32Smrg	    line[count++] = '>';
54860bd37d32Smrg	}
5487d522f475Smrg#endif
54880bd37d32Smrg	line[count++] = final;
54890bd37d32Smrg	break;
54900bd37d32Smrg    case SET_SGR_EXT_MODE_MOUSE:
5491f2e35a3aSmrg    case SET_PIXEL_POSITION_MOUSE:
54920bd37d32Smrg	line[count++] = '<';
54930bd37d32Smrg	break;
54940bd37d32Smrg    }
5495d522f475Smrg
5496d522f475Smrg    /* Add event code to key sequence */
5497913cc679Smrg    if (okSendMousePos(xw) == X10_MOUSE) {
54980bd37d32Smrg	count = EMIT_BUTTON(button);
5499d522f475Smrg    } else {
5500d522f475Smrg	/* Button-Motion events */
5501d522f475Smrg	switch (event->type) {
5502d522f475Smrg	case ButtonPress:
55030bd37d32Smrg	    screen->mouse_button |= ButtonBit(button);
55040bd37d32Smrg	    count = EMIT_BUTTON(button);
5505d522f475Smrg	    break;
5506d522f475Smrg	case ButtonRelease:
5507d522f475Smrg	    /*
5508f2e35a3aSmrg	     * The (vertical) wheel mouse interface generates release-events
5509f2e35a3aSmrg	     * for buttons 4 and 5.
5510f2e35a3aSmrg	     *
5511f2e35a3aSmrg	     * The X10/X11 xterm protocol maps the release for buttons 1..3 to
5512f2e35a3aSmrg	     * a -1, which will be later mapped into a "0" (some button was
5513f2e35a3aSmrg	     * released),  At this point, buttons 1..3 are encoded 0..2 (the
5514f2e35a3aSmrg	     * code 3 is unused).
5515f2e35a3aSmrg	     *
5516f2e35a3aSmrg	     * The SGR (extended) xterm mouse protocol keeps the button number
5517f2e35a3aSmrg	     * and uses a "m" to indicate button release.
5518f2e35a3aSmrg	     *
5519f2e35a3aSmrg	     * The behavior for mice with more buttons is unclear, and may be
5520f2e35a3aSmrg	     * revised -TD
5521d522f475Smrg	     */
55220bd37d32Smrg	    screen->mouse_button &= ~ButtonBit(button);
5523f2e35a3aSmrg	    if (button < 3 || button > 5) {
55240bd37d32Smrg		switch (screen->extend_coords) {
55250bd37d32Smrg		case SET_SGR_EXT_MODE_MOUSE:
5526f2e35a3aSmrg		case SET_PIXEL_POSITION_MOUSE:
55270bd37d32Smrg		    final = 'm';
55280bd37d32Smrg		    break;
55290bd37d32Smrg		default:
55300bd37d32Smrg		    button = -1;
55310bd37d32Smrg		    break;
55320bd37d32Smrg		}
55330bd37d32Smrg	    }
55340bd37d32Smrg	    count = EMIT_BUTTON(button);
5535d522f475Smrg	    break;
5536d522f475Smrg	case MotionNotify:
5537d522f475Smrg	    /* BTN_EVENT_MOUSE and ANY_EVENT_MOUSE modes send motion
5538d522f475Smrg	     * events only if character cell has changed.
5539d522f475Smrg	     */
5540d522f475Smrg	    if ((row == screen->mouse_row)
5541d522f475Smrg		&& (col == screen->mouse_col)) {
5542d522f475Smrg		changed = False;
5543d522f475Smrg	    } else {
55440bd37d32Smrg		count = EMIT_BUTTON(FirstBitN(screen->mouse_button));
5545d522f475Smrg	    }
5546d522f475Smrg	    break;
5547d522f475Smrg	default:
5548d522f475Smrg	    changed = False;
5549d522f475Smrg	    break;
5550d522f475Smrg	}
5551d522f475Smrg    }
5552d522f475Smrg
5553d522f475Smrg    if (changed) {
5554d522f475Smrg	screen->mouse_row = row;
5555d522f475Smrg	screen->mouse_col = col;
5556d522f475Smrg
5557492d43a5Smrg	TRACE(("mouse at %d,%d button+mask = %#x\n", row, col, line[count - 1]));
5558d522f475Smrg
5559492d43a5Smrg	/* Add pointer position to key sequence */
55600bd37d32Smrg	count = EmitMousePositionSeparator(screen, line, count);
5561492d43a5Smrg	count = EmitMousePosition(screen, line, count, col);
55620bd37d32Smrg	count = EmitMousePositionSeparator(screen, line, count);
5563492d43a5Smrg	count = EmitMousePosition(screen, line, count, row);
5564d522f475Smrg
55650bd37d32Smrg	switch (screen->extend_coords) {
55660bd37d32Smrg	case SET_SGR_EXT_MODE_MOUSE:
55670bd37d32Smrg	case SET_URXVT_EXT_MODE_MOUSE:
5568f2e35a3aSmrg	case SET_PIXEL_POSITION_MOUSE:
55690bd37d32Smrg	    line[count++] = final;
55700bd37d32Smrg	    break;
55710bd37d32Smrg	}
55720bd37d32Smrg
5573d522f475Smrg	/* Transmit key sequence to process running under xterm */
5574f2e35a3aSmrg	TRACE(("EditorButton -> %s\n", visibleChars(line, count)));
55755307cd1aSmrg	v_write(pty, line, (size_t) count);
5576d522f475Smrg    }
5577d522f475Smrg    return;
5578d522f475Smrg}
5579d522f475Smrg
5580913cc679Smrg/*
5581913cc679Smrg * Check the current send_mouse_pos against allowed mouse-operations, returning
5582913cc679Smrg * none if it is disallowed.
5583913cc679Smrg */
5584913cc679SmrgXtermMouseModes
5585913cc679SmrgokSendMousePos(XtermWidget xw)
5586913cc679Smrg{
5587913cc679Smrg    TScreen *screen = TScreenOf(xw);
5588f2e35a3aSmrg    XtermMouseModes result = (XtermMouseModes) screen->send_mouse_pos;
5589913cc679Smrg
5590f2e35a3aSmrg    switch ((int) result) {
5591913cc679Smrg    case MOUSE_OFF:
5592913cc679Smrg	break;
5593913cc679Smrg    case X10_MOUSE:
5594913cc679Smrg	if (!AllowMouseOps(xw, emX10))
5595913cc679Smrg	    result = MOUSE_OFF;
5596913cc679Smrg	break;
5597913cc679Smrg    case VT200_MOUSE:
5598913cc679Smrg	if (!AllowMouseOps(xw, emVT200Click))
5599913cc679Smrg	    result = MOUSE_OFF;
5600913cc679Smrg	break;
5601913cc679Smrg    case VT200_HIGHLIGHT_MOUSE:
5602913cc679Smrg	if (!AllowMouseOps(xw, emVT200Hilite))
5603913cc679Smrg	    result = MOUSE_OFF;
5604913cc679Smrg	break;
5605913cc679Smrg    case BTN_EVENT_MOUSE:
5606913cc679Smrg	if (!AllowMouseOps(xw, emAnyButton))
5607913cc679Smrg	    result = MOUSE_OFF;
5608913cc679Smrg	break;
5609913cc679Smrg    case ANY_EVENT_MOUSE:
5610913cc679Smrg	if (!AllowMouseOps(xw, emAnyEvent))
5611913cc679Smrg	    result = MOUSE_OFF;
5612913cc679Smrg	break;
5613913cc679Smrg    case DEC_LOCATOR:
5614913cc679Smrg	if (!AllowMouseOps(xw, emLocator))
5615913cc679Smrg	    result = MOUSE_OFF;
5616913cc679Smrg	break;
5617913cc679Smrg    }
5618913cc679Smrg    return result;
5619913cc679Smrg}
5620913cc679Smrg
5621d522f475Smrg#if OPT_FOCUS_EVENT
5622913cc679Smrg/*
5623913cc679Smrg * Check the current send_focus_pos against allowed mouse-operations, returning
5624913cc679Smrg * none if it is disallowed.
5625913cc679Smrg */
5626913cc679Smrgstatic int
5627913cc679SmrgokSendFocusPos(XtermWidget xw)
5628d522f475Smrg{
5629956cc18dSsnj    TScreen *screen = TScreenOf(xw);
5630913cc679Smrg    int result = screen->send_focus_pos;
5631913cc679Smrg
5632913cc679Smrg    if (!AllowMouseOps(xw, emFocusEvent)) {
5633913cc679Smrg	result = False;
5634913cc679Smrg    }
5635913cc679Smrg    return result;
5636913cc679Smrg}
5637d522f475Smrg
5638913cc679Smrgvoid
5639913cc679SmrgSendFocusButton(XtermWidget xw, XFocusChangeEvent *event)
5640913cc679Smrg{
5641913cc679Smrg    if (okSendFocusPos(xw)) {
5642d522f475Smrg	ANSI reply;
5643d522f475Smrg
5644d522f475Smrg	memset(&reply, 0, sizeof(reply));
5645d522f475Smrg	reply.a_type = ANSI_CSI;
5646d522f475Smrg
5647d522f475Smrg#if OPT_SCO_FUNC_KEYS
5648d522f475Smrg	if (xw->keyboard.type == keyboardIsSCO) {
5649d522f475Smrg	    reply.a_pintro = '>';
5650d522f475Smrg	}
5651d522f475Smrg#endif
56522eaa94a1Schristos	reply.a_final = CharOf((event->type == FocusIn) ? 'I' : 'O');
5653d522f475Smrg	unparseseq(xw, &reply);
5654d522f475Smrg    }
5655d522f475Smrg    return;
5656d522f475Smrg}
5657d522f475Smrg#endif /* OPT_FOCUS_EVENT */
56580bd37d32Smrg
56590bd37d32Smrg#if OPT_SELECTION_OPS
56600bd37d32Smrg/*
56610bd37d32Smrg * Get the event-time, needed to process selections.
56620bd37d32Smrg */
56630bd37d32Smrgstatic Time
5664894e0ac8SmrggetEventTime(XEvent *event)
56650bd37d32Smrg{
56660bd37d32Smrg    Time result;
56670bd37d32Smrg
56680bd37d32Smrg    if (IsBtnEvent(event)) {
56690bd37d32Smrg	result = ((XButtonEvent *) event)->time;
56700bd37d32Smrg    } else if (IsKeyEvent(event)) {
56710bd37d32Smrg	result = ((XKeyEvent *) event)->time;
56720bd37d32Smrg    } else {
56730bd37d32Smrg	result = 0;
56740bd37d32Smrg    }
56750bd37d32Smrg
56760bd37d32Smrg    return result;
56770bd37d32Smrg}
56780bd37d32Smrg
56790bd37d32Smrg/* obtain the selection string, passing the endpoints to caller's parameters */
568001037d57Smrgstatic void
568101037d57SmrgdoSelectionFormat(XtermWidget xw,
568201037d57Smrg		  Widget w,
568301037d57Smrg		  XEvent *event,
568401037d57Smrg		  String *params,
56855104ee6eSmrg		  const Cardinal *num_params,
568601037d57Smrg		  FormatSelect format_select)
56870bd37d32Smrg{
56880bd37d32Smrg    TScreen *screen = TScreenOf(xw);
568901037d57Smrg    InternalSelect *mydata = &(screen->internal_select);
569001037d57Smrg
569101037d57Smrg    memset(mydata, 0, sizeof(*mydata));
569201037d57Smrg    mydata->format = x_strdup(params[0]);
569301037d57Smrg    mydata->format_select = format_select;
56940bd37d32Smrg
56950bd37d32Smrg    screen->selectToBuffer = True;
5696f2e35a3aSmrg    beginInternalSelect(xw);
5697f2e35a3aSmrg
56980bd37d32Smrg    xtermGetSelection(w, getEventTime(event), params + 1, *num_params - 1, NULL);
5699f2e35a3aSmrg
5700f2e35a3aSmrg    if (screen->selectToBuffer)
5701f2e35a3aSmrg	finishInternalSelect(xw);
57020bd37d32Smrg}
57030bd37d32Smrg
57040bd37d32Smrg/* obtain data from the screen, passing the endpoints to caller's parameters */
57050bd37d32Smrgstatic char *
5706913cc679SmrggetDataFromScreen(XtermWidget xw, XEvent *event, String method, CELL *start, CELL *finish)
57070bd37d32Smrg{
57080bd37d32Smrg    TScreen *screen = TScreenOf(xw);
57090bd37d32Smrg
57100bd37d32Smrg    CELL save_old_start = screen->startH;
57110bd37d32Smrg    CELL save_old_end = screen->endH;
57120bd37d32Smrg
57130bd37d32Smrg    CELL save_startSel = screen->startSel;
57140bd37d32Smrg    CELL save_startRaw = screen->startRaw;
57150bd37d32Smrg    CELL save_finishSel = screen->endSel;
57160bd37d32Smrg    CELL save_finishRaw = screen->endRaw;
57170bd37d32Smrg
57180bd37d32Smrg    int save_firstValidRow = screen->firstValidRow;
57190bd37d32Smrg    int save_lastValidRow = screen->lastValidRow;
57200bd37d32Smrg
572101037d57Smrg    const Cardinal noClick = 0;
572201037d57Smrg    int save_numberOfClicks = screen->numberOfClicks;
572301037d57Smrg
57240bd37d32Smrg    SelectUnit saveUnits = screen->selectUnit;
572501037d57Smrg    SelectUnit saveMap = screen->selectMap[noClick];
57260bd37d32Smrg#if OPT_SELECT_REGEX
572701037d57Smrg    char *saveExpr = screen->selectExpr[noClick];
57280bd37d32Smrg#endif
5729f2e35a3aSmrg    SelectedCells *scp = &(screen->selected_cells[PRIMARY_CODE]);
5730f2e35a3aSmrg    SelectedCells save_selection = *scp;
57310bd37d32Smrg
57325104ee6eSmrg    char *result = NULL;
57330bd37d32Smrg
57340bd37d32Smrg    TRACE(("getDataFromScreen %s\n", method));
57350bd37d32Smrg
5736f2e35a3aSmrg    memset(scp, 0, sizeof(*scp));
57370bd37d32Smrg
573801037d57Smrg    screen->numberOfClicks = 1;
573901037d57Smrg    lookupSelectUnit(xw, noClick, method);
574001037d57Smrg    screen->selectUnit = screen->selectMap[noClick];
57410bd37d32Smrg
57420bd37d32Smrg    memset(start, 0, sizeof(*start));
5743913cc679Smrg    if (IsBtnEvent(event)) {
5744913cc679Smrg	XButtonEvent *btn_event = (XButtonEvent *) event;
5745913cc679Smrg	CELL cell;
5746913cc679Smrg	screen->firstValidRow = 0;
5747913cc679Smrg	screen->lastValidRow = screen->max_row;
5748913cc679Smrg	PointToCELL(screen, btn_event->y, btn_event->x, &cell);
5749913cc679Smrg	start->row = cell.row;
5750913cc679Smrg	start->col = cell.col;
5751913cc679Smrg	finish->row = cell.row;
5752913cc679Smrg	finish->col = screen->max_col;
5753913cc679Smrg    } else {
5754913cc679Smrg	start->row = screen->cur_row;
5755913cc679Smrg	start->col = screen->cur_col;
5756913cc679Smrg	finish->row = screen->cur_row;
5757913cc679Smrg	finish->col = screen->max_col;
5758913cc679Smrg    }
57590bd37d32Smrg
5760f2e35a3aSmrg    ComputeSelect(xw, start, finish, False, False);
5761f2e35a3aSmrg    SaltTextAway(xw,
5762f2e35a3aSmrg		 TargetToSelection(screen, PRIMARY_NAME),
5763f2e35a3aSmrg		 &(screen->startSel), &(screen->endSel));
57640bd37d32Smrg
5765f2e35a3aSmrg    if (scp->data_limit && scp->data_buffer) {
5766f2e35a3aSmrg	TRACE(("...getDataFromScreen selection-data %.*s\n",
5767f2e35a3aSmrg	       (int) scp->data_limit,
5768f2e35a3aSmrg	       scp->data_buffer));
5769f2e35a3aSmrg	result = malloc(scp->data_limit + 1);
57700bd37d32Smrg	if (result) {
5771f2e35a3aSmrg	    memcpy(result, scp->data_buffer, scp->data_limit);
5772f2e35a3aSmrg	    result[scp->data_limit] = 0;
57730bd37d32Smrg	}
5774f2e35a3aSmrg	free(scp->data_buffer);
5775f2e35a3aSmrg	scp->data_limit = 0;
57760bd37d32Smrg    }
57770bd37d32Smrg
57780bd37d32Smrg    TRACE(("...getDataFromScreen restoring previous selection\n"));
57790bd37d32Smrg
57800bd37d32Smrg    screen->startSel = save_startSel;
57810bd37d32Smrg    screen->startRaw = save_startRaw;
57820bd37d32Smrg    screen->endSel = save_finishSel;
57830bd37d32Smrg    screen->endRaw = save_finishRaw;
57840bd37d32Smrg
57850bd37d32Smrg    screen->firstValidRow = save_firstValidRow;
57860bd37d32Smrg    screen->lastValidRow = save_lastValidRow;
57870bd37d32Smrg
578801037d57Smrg    screen->numberOfClicks = save_numberOfClicks;
57890bd37d32Smrg    screen->selectUnit = saveUnits;
579001037d57Smrg    screen->selectMap[noClick] = saveMap;
57910bd37d32Smrg#if OPT_SELECT_REGEX
579201037d57Smrg    screen->selectExpr[noClick] = saveExpr;
57930bd37d32Smrg#endif
57940bd37d32Smrg
5795f2e35a3aSmrg    screen->selected_cells[0] = save_selection;
57960bd37d32Smrg
57970bd37d32Smrg    TrackText(xw, &save_old_start, &save_old_end);
57980bd37d32Smrg
57990bd37d32Smrg    TRACE(("...getDataFromScreen done\n"));
58000bd37d32Smrg    return result;
58010bd37d32Smrg}
58020bd37d32Smrg
58035307cd1aSmrg#if OPT_EXEC_SELECTION
58040bd37d32Smrg/*
58050bd37d32Smrg * Split-up the format before substituting data, to avoid quoting issues.
58060bd37d32Smrg * The resource mechanism has a limited ability to handle escapes.  We take
58070bd37d32Smrg * the result as if it were an sh-type string and parse it into a regular
58080bd37d32Smrg * argv array.
58090bd37d32Smrg */
58100bd37d32Smrgstatic char **
58110bd37d32SmrgtokenizeFormat(String format)
58120bd37d32Smrg{
58135104ee6eSmrg    char **result = NULL;
58140bd37d32Smrg
58150bd37d32Smrg    format = x_skip_blanks(format);
58160bd37d32Smrg    if (*format != '\0') {
58170bd37d32Smrg	char *blob = x_strdup(format);
58182e4f8982Smrg	int pass;
58190bd37d32Smrg
58200bd37d32Smrg	for (pass = 0; pass < 2; ++pass) {
58210bd37d32Smrg	    int used = 0;
58220bd37d32Smrg	    int first = 1;
58230bd37d32Smrg	    int escaped = 0;
58240bd37d32Smrg	    int squoted = 0;
58250bd37d32Smrg	    int dquoted = 0;
58262e4f8982Smrg	    int n;
5827f2e35a3aSmrg	    int argc = 0;
58280bd37d32Smrg
58290bd37d32Smrg	    for (n = 0; format[n] != '\0'; ++n) {
58300bd37d32Smrg		if (escaped) {
58310bd37d32Smrg		    blob[used++] = format[n];
58320bd37d32Smrg		    escaped = 0;
58330bd37d32Smrg		} else if (format[n] == '"') {
58340bd37d32Smrg		    if (!squoted) {
58350bd37d32Smrg			if (!dquoted)
58360bd37d32Smrg			    blob[used++] = format[n];
58370bd37d32Smrg			dquoted = !dquoted;
58380bd37d32Smrg		    }
58390bd37d32Smrg		} else if (format[n] == '\'') {
58400bd37d32Smrg		    if (!dquoted) {
58410bd37d32Smrg			if (!squoted)
58420bd37d32Smrg			    blob[used++] = format[n];
58430bd37d32Smrg			squoted = !squoted;
58440bd37d32Smrg		    }
58450bd37d32Smrg		} else if (format[n] == '\\') {
58460bd37d32Smrg		    blob[used++] = format[n];
58470bd37d32Smrg		    escaped = 1;
58480bd37d32Smrg		} else {
58490bd37d32Smrg		    if (first) {
58500bd37d32Smrg			first = 0;
58510bd37d32Smrg			if (pass) {
58520bd37d32Smrg			    result[argc] = &blob[n];
58530bd37d32Smrg			}
58540bd37d32Smrg			++argc;
58550bd37d32Smrg		    }
58560bd37d32Smrg		    if (isspace((Char) format[n])) {
58570bd37d32Smrg			first = !isspace((Char) format[n + 1]);
58580bd37d32Smrg			if (squoted || dquoted) {
58590bd37d32Smrg			    blob[used++] = format[n];
58600bd37d32Smrg			} else if (first) {
58610bd37d32Smrg			    blob[used++] = '\0';
58620bd37d32Smrg			}
58630bd37d32Smrg		    } else {
58640bd37d32Smrg			blob[used++] = format[n];
58650bd37d32Smrg		    }
58660bd37d32Smrg		}
58670bd37d32Smrg	    }
58680bd37d32Smrg	    blob[used] = '\0';
58690bd37d32Smrg	    assert(strlen(blob) <= strlen(format));
58700bd37d32Smrg	    if (!pass) {
58710bd37d32Smrg		result = TypeCallocN(char *, argc + 1);
58725104ee6eSmrg		if (result == NULL) {
58730bd37d32Smrg		    free(blob);
58740bd37d32Smrg		    break;
58750bd37d32Smrg		}
58760bd37d32Smrg	    }
58770bd37d32Smrg	}
58780bd37d32Smrg    }
58790bd37d32Smrg#if OPT_TRACE
58800bd37d32Smrg    if (result) {
5881f2e35a3aSmrg	int n;
58820bd37d32Smrg	TRACE(("tokenizeFormat %s\n", format));
5883f2e35a3aSmrg	for (n = 0; result[n]; ++n) {
5884f2e35a3aSmrg	    TRACE(("argv[%d] = %s\n", n, result[n]));
58850bd37d32Smrg	}
58860bd37d32Smrg    }
58870bd37d32Smrg#endif
58880bd37d32Smrg
58890bd37d32Smrg    return result;
58900bd37d32Smrg}
58915307cd1aSmrg#endif /* OPT_EXEC_SELECTION */
58920bd37d32Smrg
58930bd37d32Smrgstatic void
5894e0a2b6dfSmrgformatVideoAttrs(XtermWidget xw, char *buffer, CELL *cell)
58950bd37d32Smrg{
58960bd37d32Smrg    TScreen *screen = TScreenOf(xw);
58970bd37d32Smrg    LineData *ld = GET_LINEDATA(screen, cell->row);
58980bd37d32Smrg
58990bd37d32Smrg    *buffer = '\0';
59005104ee6eSmrg    if (ld != NULL && cell->col < (int) ld->lineSize) {
5901894e0ac8Smrg	IAttr attribs = ld->attribs[cell->col];
59020bd37d32Smrg	const char *delim = "";
59030bd37d32Smrg
59040bd37d32Smrg	if (attribs & INVERSE) {
59050bd37d32Smrg	    buffer += sprintf(buffer, "7");
59060bd37d32Smrg	    delim = ";";
59070bd37d32Smrg	}
59080bd37d32Smrg	if (attribs & UNDERLINE) {
59090bd37d32Smrg	    buffer += sprintf(buffer, "%s4", delim);
59100bd37d32Smrg	    delim = ";";
59110bd37d32Smrg	}
59120bd37d32Smrg	if (attribs & BOLD) {
59130bd37d32Smrg	    buffer += sprintf(buffer, "%s1", delim);
59140bd37d32Smrg	    delim = ";";
59150bd37d32Smrg	}
59160bd37d32Smrg	if (attribs & BLINK) {
59170bd37d32Smrg	    buffer += sprintf(buffer, "%s5", delim);
59180bd37d32Smrg	    delim = ";";
59190bd37d32Smrg	}
59200bd37d32Smrg#if OPT_ISO_COLORS
59210bd37d32Smrg	if (attribs & FG_COLOR) {
5922f2e35a3aSmrg	    Pixel fg = extract_fg(xw, ld->color[cell->col], attribs);
59230bd37d32Smrg	    if (fg < 8) {
59240bd37d32Smrg		fg += 30;
59250bd37d32Smrg	    } else if (fg < 16) {
59260bd37d32Smrg		fg += 90;
59270bd37d32Smrg	    } else {
59280bd37d32Smrg		buffer += sprintf(buffer, "%s38;5", delim);
59290bd37d32Smrg		delim = ";";
59300bd37d32Smrg	    }
5931f2e35a3aSmrg	    buffer += sprintf(buffer, "%s%lu", delim, fg);
59320bd37d32Smrg	    delim = ";";
59330bd37d32Smrg	}
59340bd37d32Smrg	if (attribs & BG_COLOR) {
5935f2e35a3aSmrg	    Pixel bg = extract_bg(xw, ld->color[cell->col], attribs);
59360bd37d32Smrg	    if (bg < 8) {
59370bd37d32Smrg		bg += 40;
59380bd37d32Smrg	    } else if (bg < 16) {
59390bd37d32Smrg		bg += 100;
59400bd37d32Smrg	    } else {
59410bd37d32Smrg		buffer += sprintf(buffer, "%s48;5", delim);
59420bd37d32Smrg		delim = ";";
59430bd37d32Smrg	    }
5944f2e35a3aSmrg	    (void) sprintf(buffer, "%s%lu", delim, bg);
59450bd37d32Smrg	}
59460bd37d32Smrg#endif
59470bd37d32Smrg    }
59480bd37d32Smrg}
59490bd37d32Smrg
59502e4f8982Smrgstatic char *
59512e4f8982SmrgformatStrlen(char *target, char *source, int freeit)
59522e4f8982Smrg{
59535104ee6eSmrg    if (source != NULL) {
59542e4f8982Smrg	sprintf(target, "%u", (unsigned) strlen(source));
59552e4f8982Smrg	if (freeit) {
59562e4f8982Smrg	    free(source);
59572e4f8982Smrg	}
59582e4f8982Smrg    } else {
59592e4f8982Smrg	strcpy(target, "0");
59602e4f8982Smrg    }
59612e4f8982Smrg    return target;
59622e4f8982Smrg}
59632e4f8982Smrg
59640bd37d32Smrg/* substitute data into format, reallocating the result */
59650bd37d32Smrgstatic char *
59660bd37d32SmrgexpandFormat(XtermWidget xw,
59670bd37d32Smrg	     const char *format,
59680bd37d32Smrg	     char *data,
5969e0a2b6dfSmrg	     CELL *start,
5970e0a2b6dfSmrg	     CELL *finish)
59710bd37d32Smrg{
59725104ee6eSmrg    char *result = NULL;
59730bd37d32Smrg    if (!IsEmpty(format)) {
59740bd37d32Smrg	static char empty[1];
59750bd37d32Smrg	int pass;
59760bd37d32Smrg	int n;
59770bd37d32Smrg	char numbers[80];
59780bd37d32Smrg
59795104ee6eSmrg	if (data == NULL)
59800bd37d32Smrg	    data = empty;
59810bd37d32Smrg
59820bd37d32Smrg	for (pass = 0; pass < 2; ++pass) {
59830bd37d32Smrg	    size_t need = 0;
59840bd37d32Smrg
59850bd37d32Smrg	    for (n = 0; format[n] != '\0'; ++n) {
59860bd37d32Smrg
59870bd37d32Smrg		if (format[n] == '%') {
59885104ee6eSmrg		    char *value = NULL;
59892e4f8982Smrg
59900bd37d32Smrg		    switch (format[++n]) {
59910bd37d32Smrg		    case '%':
59920bd37d32Smrg			if (pass) {
59930bd37d32Smrg			    result[need] = format[n];
59940bd37d32Smrg			}
59950bd37d32Smrg			++need;
59960bd37d32Smrg			break;
59970bd37d32Smrg		    case 'P':
59980bd37d32Smrg			sprintf(numbers, "%d;%d",
59990bd37d32Smrg				TScreenOf(xw)->topline + start->row + 1,
60000bd37d32Smrg				start->col + 1);
60010bd37d32Smrg			value = numbers;
60020bd37d32Smrg			break;
60030bd37d32Smrg		    case 'p':
60040bd37d32Smrg			sprintf(numbers, "%d;%d",
60050bd37d32Smrg				TScreenOf(xw)->topline + finish->row + 1,
60060bd37d32Smrg				finish->col + 1);
60070bd37d32Smrg			value = numbers;
60080bd37d32Smrg			break;
60092e4f8982Smrg		    case 'R':
60102e4f8982Smrg			value = formatStrlen(numbers, x_strrtrim(data), 1);
60112e4f8982Smrg			break;
60122e4f8982Smrg		    case 'r':
60132e4f8982Smrg			value = x_strrtrim(data);
60142e4f8982Smrg			break;
60150bd37d32Smrg		    case 'S':
60162e4f8982Smrg			value = formatStrlen(numbers, data, 0);
60170bd37d32Smrg			break;
60180bd37d32Smrg		    case 's':
60190bd37d32Smrg			value = data;
60200bd37d32Smrg			break;
60210bd37d32Smrg		    case 'T':
60222e4f8982Smrg			value = formatStrlen(numbers, x_strtrim(data), 1);
60230bd37d32Smrg			break;
60240bd37d32Smrg		    case 't':
60250bd37d32Smrg			value = x_strtrim(data);
60260bd37d32Smrg			break;
60270bd37d32Smrg		    case 'V':
60280bd37d32Smrg			formatVideoAttrs(xw, numbers, start);
60290bd37d32Smrg			value = numbers;
60300bd37d32Smrg			break;
60310bd37d32Smrg		    case 'v':
60320bd37d32Smrg			formatVideoAttrs(xw, numbers, finish);
60330bd37d32Smrg			value = numbers;
60340bd37d32Smrg			break;
60350bd37d32Smrg		    default:
60360bd37d32Smrg			if (pass) {
60370bd37d32Smrg			    result[need] = format[n];
60380bd37d32Smrg			}
60390bd37d32Smrg			--n;
60400bd37d32Smrg			++need;
60410bd37d32Smrg			break;
60420bd37d32Smrg		    }
60435104ee6eSmrg		    if (value != NULL) {
60440bd37d32Smrg			if (pass) {
60450bd37d32Smrg			    strcpy(result + need, value);
60460bd37d32Smrg			}
60470bd37d32Smrg			need += strlen(value);
60480bd37d32Smrg			if (value != numbers && value != data) {
60490bd37d32Smrg			    free(value);
60500bd37d32Smrg			}
60510bd37d32Smrg		    }
60520bd37d32Smrg		} else {
60530bd37d32Smrg		    if (pass) {
60540bd37d32Smrg			result[need] = format[n];
60550bd37d32Smrg		    }
60560bd37d32Smrg		    ++need;
60570bd37d32Smrg		}
60580bd37d32Smrg	    }
60590bd37d32Smrg	    if (pass) {
60600bd37d32Smrg		result[need] = '\0';
60610bd37d32Smrg	    } else {
60620bd37d32Smrg		++need;
60630bd37d32Smrg		result = malloc(need);
60645104ee6eSmrg		if (result == NULL) {
60650bd37d32Smrg		    break;
60660bd37d32Smrg		}
60670bd37d32Smrg	    }
60680bd37d32Smrg	}
60690bd37d32Smrg    }
60700bd37d32Smrg    TRACE(("expandFormat(%s) = %s\n", NonNull(format), NonNull(result)));
60710bd37d32Smrg    return result;
60720bd37d32Smrg}
60730bd37d32Smrg
60745307cd1aSmrg#if OPT_EXEC_SELECTION
60750bd37d32Smrg/* execute the command after forking.  The main process frees its data */
60760bd37d32Smrgstatic void
60772e4f8982SmrgexecuteCommand(pid_t pid, char **argv)
60780bd37d32Smrg{
60792e4f8982Smrg    (void) pid;
60805104ee6eSmrg    if (argv != NULL && argv[0] != NULL) {
60812e4f8982Smrg	char *child_cwd = ProcGetCWD(pid);
60822e4f8982Smrg
60830bd37d32Smrg	if (fork() == 0) {
60842e4f8982Smrg	    if (child_cwd) {
60852e4f8982Smrg		IGNORE_RC(chdir(child_cwd));	/* We don't care if this fails */
60862e4f8982Smrg	    }
60870bd37d32Smrg	    execvp(argv[0], argv);
60880bd37d32Smrg	    exit(EXIT_FAILURE);
60890bd37d32Smrg	}
6090913cc679Smrg	free(child_cwd);
60910bd37d32Smrg    }
60920bd37d32Smrg}
60930bd37d32Smrg
60940bd37d32Smrgstatic void
60950bd37d32SmrgfreeArgv(char *blob, char **argv)
60960bd37d32Smrg{
60970bd37d32Smrg    if (blob) {
60980bd37d32Smrg	free(blob);
60990bd37d32Smrg	if (argv) {
61002e4f8982Smrg	    int n;
61010bd37d32Smrg	    for (n = 0; argv[n]; ++n)
61020bd37d32Smrg		free(argv[n]);
61030bd37d32Smrg	    free(argv);
61040bd37d32Smrg	}
61050bd37d32Smrg    }
61060bd37d32Smrg}
61070bd37d32Smrg
610801037d57Smrgstatic void
610901037d57SmrgreallyExecFormatted(Widget w, char *format, char *data, CELL *start, CELL *finish)
611001037d57Smrg{
611101037d57Smrg    XtermWidget xw;
611201037d57Smrg
61135104ee6eSmrg    if ((xw = getXtermWidget(w)) != NULL) {
611401037d57Smrg	char **argv;
611501037d57Smrg
61165104ee6eSmrg	if ((argv = tokenizeFormat(format)) != NULL) {
61172e4f8982Smrg	    char *blob = argv[0];
61182e4f8982Smrg	    int argc;
61192e4f8982Smrg
61205104ee6eSmrg	    for (argc = 0; argv[argc] != NULL; ++argc) {
612101037d57Smrg		argv[argc] = expandFormat(xw, argv[argc], data, start, finish);
612201037d57Smrg	    }
61232e4f8982Smrg	    executeCommand(TScreenOf(xw)->pid, argv);
612401037d57Smrg	    freeArgv(blob, argv);
612501037d57Smrg	}
612601037d57Smrg    }
612701037d57Smrg}
612801037d57Smrg
61290bd37d32Smrgvoid
61300bd37d32SmrgHandleExecFormatted(Widget w,
613101037d57Smrg		    XEvent *event,
6132e0a2b6dfSmrg		    String *params,	/* selections */
61330bd37d32Smrg		    Cardinal *num_params)
61340bd37d32Smrg{
61350bd37d32Smrg    XtermWidget xw;
61360bd37d32Smrg
6137f2e35a3aSmrg    TRACE_EVENT("HandleExecFormatted", event, params, num_params);
61385104ee6eSmrg    if ((xw = getXtermWidget(w)) != NULL &&
613901037d57Smrg	(*num_params > 1)) {
614001037d57Smrg	doSelectionFormat(xw, w, event, params, num_params, reallyExecFormatted);
61410bd37d32Smrg    }
61420bd37d32Smrg}
61430bd37d32Smrg
61440bd37d32Smrgvoid
61450bd37d32SmrgHandleExecSelectable(Widget w,
6146913cc679Smrg		     XEvent *event,
6147e0a2b6dfSmrg		     String *params,	/* selections */
61480bd37d32Smrg		     Cardinal *num_params)
61490bd37d32Smrg{
61500bd37d32Smrg    XtermWidget xw;
61510bd37d32Smrg
61525104ee6eSmrg    if ((xw = getXtermWidget(w)) != NULL) {
6153f2e35a3aSmrg	TRACE_EVENT("HandleExecSelectable", event, params, num_params);
61540bd37d32Smrg
61550bd37d32Smrg	if (*num_params == 2) {
61560bd37d32Smrg	    CELL start, finish;
61570bd37d32Smrg	    char *data;
61580bd37d32Smrg	    char **argv;
61590bd37d32Smrg
6160913cc679Smrg	    data = getDataFromScreen(xw, event, params[1], &start, &finish);
61615104ee6eSmrg	    if (data != NULL) {
61625104ee6eSmrg		if ((argv = tokenizeFormat(params[0])) != NULL) {
61632e4f8982Smrg		    char *blob = argv[0];
61642e4f8982Smrg		    int argc;
61652e4f8982Smrg
61665104ee6eSmrg		    for (argc = 0; argv[argc] != NULL; ++argc) {
61670bd37d32Smrg			argv[argc] = expandFormat(xw, argv[argc], data,
61680bd37d32Smrg						  &start, &finish);
61690bd37d32Smrg		    }
61702e4f8982Smrg		    executeCommand(TScreenOf(xw)->pid, argv);
61710bd37d32Smrg		    freeArgv(blob, argv);
61720bd37d32Smrg		}
6173894e0ac8Smrg		free(data);
61740bd37d32Smrg	    }
61750bd37d32Smrg	}
61760bd37d32Smrg    }
61770bd37d32Smrg}
61785307cd1aSmrg#endif /* OPT_EXEC_SELECTION */
61790bd37d32Smrg
618001037d57Smrgstatic void
618101037d57SmrgreallyInsertFormatted(Widget w, char *format, char *data, CELL *start, CELL *finish)
618201037d57Smrg{
618301037d57Smrg    XtermWidget xw;
618401037d57Smrg
61855104ee6eSmrg    if ((xw = getXtermWidget(w)) != NULL) {
618601037d57Smrg	char *exps;
618701037d57Smrg
61885104ee6eSmrg	if ((exps = expandFormat(xw, format, data, start, finish)) != NULL) {
618901037d57Smrg	    unparseputs(xw, exps);
619001037d57Smrg	    unparse_end(xw);
619101037d57Smrg	    free(exps);
619201037d57Smrg	}
619301037d57Smrg    }
619401037d57Smrg}
619501037d57Smrg
61960bd37d32Smrgvoid
61970bd37d32SmrgHandleInsertFormatted(Widget w,
619801037d57Smrg		      XEvent *event,
6199e0a2b6dfSmrg		      String *params,	/* selections */
62000bd37d32Smrg		      Cardinal *num_params)
62010bd37d32Smrg{
62020bd37d32Smrg    XtermWidget xw;
62030bd37d32Smrg
6204f2e35a3aSmrg    TRACE_EVENT("HandleInsertFormatted", event, params, num_params);
62055104ee6eSmrg    if ((xw = getXtermWidget(w)) != NULL &&
620601037d57Smrg	(*num_params > 1)) {
620701037d57Smrg	doSelectionFormat(xw, w, event, params, num_params, reallyInsertFormatted);
62080bd37d32Smrg    }
62090bd37d32Smrg}
62100bd37d32Smrg
62110bd37d32Smrgvoid
62120bd37d32SmrgHandleInsertSelectable(Widget w,
6213913cc679Smrg		       XEvent *event,
6214e0a2b6dfSmrg		       String *params,	/* selections */
62150bd37d32Smrg		       Cardinal *num_params)
62160bd37d32Smrg{
62170bd37d32Smrg    XtermWidget xw;
62180bd37d32Smrg
62195104ee6eSmrg    if ((xw = getXtermWidget(w)) != NULL) {
6220f2e35a3aSmrg	TRACE_EVENT("HandleInsertSelectable", event, params, num_params);
62210bd37d32Smrg
62220bd37d32Smrg	if (*num_params == 2) {
62230bd37d32Smrg	    CELL start, finish;
62240bd37d32Smrg	    char *data;
62250bd37d32Smrg	    char *temp = x_strdup(params[0]);
62260bd37d32Smrg
6227913cc679Smrg	    data = getDataFromScreen(xw, event, params[1], &start, &finish);
62285104ee6eSmrg	    if (data != NULL) {
62292e4f8982Smrg		char *exps = expandFormat(xw, temp, data, &start, &finish);
62305104ee6eSmrg		if (exps != NULL) {
62310bd37d32Smrg		    unparseputs(xw, exps);
623201037d57Smrg		    unparse_end(xw);
62330bd37d32Smrg		    free(exps);
62340bd37d32Smrg		}
62350bd37d32Smrg		free(data);
62360bd37d32Smrg	    }
62370bd37d32Smrg	    free(temp);
62380bd37d32Smrg	}
62390bd37d32Smrg    }
62400bd37d32Smrg}
62410bd37d32Smrg#endif /* OPT_SELECTION_OPS */
6242